Im Katalog suchen

Linux - Wegweiser zur Installation & Konfiguration, 3. Auflage

Online-Version

Copyright © 2000 by O'Reilly Verlag GmbH & Co.KG

Bitte denken Sie daran: Sie dürfen zwar die Online-Version ausdrucken, aber diesen Druck nicht fotokopieren oder verkaufen. Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

Wünschen Sie mehr Informationen zu der gedruckten Version des Buches Linux - Wegweiser zur Installation & Konfiguration oder wollen Sie es bestellen, dann klicken Sie bitte hier.


vorheriges Kapitel Inhaltsverzeichnis Stichwortverzeichnis nächstes Kapitel

Werkzeuge für die Programmierung

Neben den Programmiersprachen und Compilern gibt es noch eine Fülle von Programmierwerkzeugen, u.a. Libraries, Programme zum Erstellen von Bedienoberflächen, Debugger und andere Tools, die Sie bei der Programmierung unterstützen sollen. Wir werden in diesem Abschnitt einige der interessanteren Werkzeuge besprechen, damit Sie einen Eindruck davon bekommen, was überhaupt verfügbar ist.

Debugger

Es gibt etliche interaktive Debugger für Linux. Der De-facto-Standard ist gdb, den wir ausführlich besprochen haben.

Neben gdb existieren einige andere Debugger, die gdb sehr ähnlich sind. xxgdb ist eine Version von gdb mit einer Schnittstelle zum X Window System, die dem Debugger xdbx ähnelt, den man auf anderen Unix-Systemen findet. Das Fenster des xxgdb ist in mehrere Bereiche unterteilt. Ein Ausschnitt stellt die normale Textschnittstelle von gdb dar, so daß Sie von Hand Befehle eingeben können, um das System zu bedienen. Ein anderer Ausschnitt zeigt automatisch die aktuelle Quelldatei an und hebt die aktuelle Textzeile hervor. Im Hauptfenster können Sie Breakpoints setzen und auswählen, durch den Quelltext blättern usw., während Sie Ihre Befehle direkt an den gdb schicken. Im xxgdb-Fenster erscheinen auch einige Schaltflächen, hinter denen sich häufig benutzte Befehle wie step, next usw. verbergen. Mit diesen Schaltflächen können Sie mit Maus und Tastatur innerhalb einer einfach zu bedienenden X-Oberfläche Ihr Programm debuggen.

Ein anderer, dem xxgdb ähnlicher Debugger ist UPS - ein X-basierter Debugger, der auf einige Unix-Plattformen portiert wurde. UPS ist wesentlich einfacher als xxgdb und bietet weniger Möglichkeiten, ist aber trotzdem ein guter Debugger mit einer weniger anspruchsvollen Lernkurve als gdb. UPS ist ausreichend für die meisten Anwendungen und einfaches Debugging.

Zwei weitere graphische Frontends für gdb verdienen es, hier erwähnt zu werden. DDD, der »Data Display Debugger«, hat die gleichen Features wie xxgdb, verfügt aber über ein sehr viel schöneres M-GUI. Außerdem kann er Strukturen und Klassen graphisch anzeigen, was besonders nützlich ist, wenn Sie die Datenstrukturen in einem unbekannten Programm analysieren wollen. kdbg stammt aus dem KDE-Projekt und bietet neben den auch in xxgdb vorhandenen Features eine vollständige Integration in den KDE-Desktop.

Werkzeuge für Profiling und Leistungsmessung

Es gibt mehrere Utilities, mit deren Hilfe Sie die Leistungen Ihres Programms messen und beurteilen können. Diese Werkzeuge helfen beim Aufspüren von Engpässen in Ihrem Code - Stellen, an denen die Leistung einbricht. Mit diesen Tools erhalten Sie auch einen Überblick über die Aufrufstruktur Ihres Programms und können feststellen, welche Funktionen woher und wie oft aufgerufen werden. (Alles, was Sie schon immer über Ihr Programm wissen wollten, aber nicht zu fragen wagten.)

gprof ist ein Profiling-Utility, das Ihnen ausführliche Statistiken zum Ablauf Ihres Programms liefert - beispielsweise, wie oft eine Funktion aufgerufen wird, von wo, die Laufzeit für jede einzelne Funktion usw.

Wenn Sie gprof auf ein Programm anwenden möchten, müssen Sie beim Kompilieren die Option -pg von gcc benutzen. Damit werden Profiling-Informationen in die Objektdatei geschrieben und Standard-Libraries in die ausführbare Datei eingebunden, die das Profiling aktiviert haben.

Nachdem Sie das Programm mit der Option -pg kompiliert haben, können Sie es ganz normal starten. Wenn es ohne Fehler beendet wird, finden Sie im Arbeitsverzeichnis des Programms die Datei gmon.out vor. Sie enthält die Profiling-Informationen für diesen Programmlauf und kann mit gprof benutzt werden, um die Statistiken darzustellen.

Als Beispiel wollen wir ein Programm namens getstat benutzen, das statistische Informationen zu einer Bilddatei erstellt. Wir kompilieren also getstat mit der Option -pg und rufen es auf:

papaya$ getstat image11.pgm > stats.dat papaya$ ls -l gmon.out -rw------- 1 mdw mdw 54448 Feb 5 17:00 gmon.out papaya$

Die Profiling-Informationen wurden tatsächlich in gmon.out abgelegt.

Wir rufen anschließend gprof mit dem Namen der ausführbaren Datei und der Datei gmon.out auf:

papaya$ gprof getstat gmon.out

Wenn Sie den Namen der Profiling-Datei nicht angeben, wird gprof nach dem Namen gmon.out suchen; wenn Sie auch den Namen der ausführbaren Datei nicht angeben, nimmt gprof an, daß a.out gemeint ist.

gprof gibt ziemlich ausführliche Meldungen aus; vielleicht sollten Sie die Ausgabe in eine Datei schreiben lassen oder durch einen Pager leiten. Die Ausgabe besteht aus zwei Teilen. Der erste Teil ist ein »flaches Profil« mit einem einzeiligen Eintrag zu jeder Funktion. Dieser Eintrag enthält die Zeit, die in der Funktion verbracht wurde, als Prozentwert, die absolute Zeit (in Sekunden) für die Ausführung dieser Funktion, die Anzahl der Aufrufe dieser Funktion und weitere Informationen. Ein Beispiel:

Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 45.11 27.49 27.49 41 670.51 903.13 GetComponent 16.25 37.40 9.91 mcount 10.72 43.93 6.54 1811863 0.00 0.00 Push 10.33 50.23 6.30 1811863 0.00 0.00 Pop 5.87 53.81 3.58 40 89.50 247.06 stackstats 4.92 56.81 3.00 1811863 0.00 0.00 TrimNeighbors

Falls in der Ausgabe einige der Felder leer bleiben, war gprof nicht in der Lage, genauere Werte zu einer Funktion zu ermitteln. Das liegt meistens daran, daß Teile des Programmcodes nicht mit der Option -pg kompiliert wurden - wenn Sie beispielsweise Routinen außerhalb der Standard-Libraries aufrufen, die nicht mit -pg kompiliert wurden, kann gprof nicht allzuviel über solche Routinen in Erfahrung bringen. Anhand der obenstehenden Ausgabe läßt sich erkennen, daß mcount wahrscheinlich nicht für das Profiling kompiliert wurde.

Wir können auch erkennen, daß insgesamt 45,11% der Zeit in der Funktion GetComponent verbracht wurden - immerhin 27,49 Sekunden. Liegt das daran, daß GetComponent schrecklich ineffizient arbeitet, oder hat GetComponent viele andere langsame Funktionen aufgerufen? Die Funktionen Push und Pop sind während der Programmausführung sehr, sehr oft aufgerufen worden - haben wir damit die Schuldigen gefunden? Fußnoten 1

Bei dieser Frage kann uns der zweite Teil der gprof-Ausgabe weiterhelfen. Er enthält einen detaillierten »Aufruf-Graphen«, aus dem Sie ablesen können, welche Funktionen andere Funktionen aufgerufen haben und wie oft sie selbst aufgerufen wurden. Ein Beispiel:

index % time self children called name <spontaneous> [1] 92.7 0.00 47.30 start [1] 0.01 47.29 1/1 main [2] 0.00 0.00 1/2 on_exit [53] 0.00 0.00 1/1 exit [172]

In der ersten Spalte des Aufruf-Graphen finden wir den Index - eine eindeutige Nummer, die jeder Funktion zugeordnet wird; mit Hilfe des Index können Sie andere Funktionen im Graphen finden. In unserem Beispiel wird die erste Funktion, start, beim Programmstart implizit aufgerufen. start hat einschließlich seiner Kindprozesse 92,7% der Gesamtlaufzeit in Anspruch genommen (47,3 Sekunden), aber selbst nur sehr wenig Laufzeit verbraucht. Das liegt daran, daß start allen anderen Funktionen im Programm, einschließlich main, übergeordnet ist - deshalb ist es nicht ungewöhnlich, daß start mit seinen Ablegern soviel Programmlaufzeit beansprucht.

Der Aufruf-Graph stellt in der Regel zu jeder Funktion die Eltern- ebenso wie die Kindfunktionen dar. In unserem Beispiel können wir erkennen, daß start die Funktionen main, on_exit und exit aufgerufen hat (unter der Zeile für start). Allerdings gibt es keine Elternfunktion, die normalerweiser über start erscheinen würde - statt dessen finden wir dort das ominöse <spontaneous>. Das bedeutet, daß gprof nicht in der Lage war, die Elternfunktion zu start zu ermitteln - wahrscheinlich, weil start nicht aus dem Programm heraus aufgerufen, sondern vom Betriebssystem angestoßen wurde.

Gehen wir weiter zu der Funktion, die wir im Verdacht haben: GetComponent. Dort sehen wir:

index % time self children called name 0.67 0.23 1/41 GetFirstComponent [12] 26.82 9.30 40/41 GetNextComponent [5] [4] 72.6 27.49 9.54 41 GetComponent [4] 6.54 0.00 1811863/1811863 Push [7] 3.00 0.00 1811863/1811863 TrimNeighbors [9] 0.00 0.00 1/1 InitStack [54]

Die Elternfunktionen von GetComponent sind GetFirstComponent und GetNextComponent, und seine Kindfunktionen sind Push, TrimNeighbors und InitStack. Wie wir sehen, wurde GetComponent 41mal aufgerufen - einmal von GetFirstComponent und 40mal von GetNextComponent. In der Ausgabe von gprof finden Sie Hinweise, die weitere Details zum Bericht liefern.

GetComponent selbst läuft für mehr als 27,49 Sekunden - nur 9,54 Sekunden werden gebraucht, um die Kinder von GetComponent auszuführen (einschließlich der vielen Aufrufe von Push und TrimNeighbors!). Es sieht also so aus, als ob GetComponent und eventuell seine Elternfunktion GetNextComponent überarbeitet werden sollten. Die häufig aufgerufene Funktion Push ist nicht die einzige Ursache des Problems.

gprof ist auch in der Lage, rekursive Aufrufe sowie sich immer wieder gegenseitig aufrufende Funktionen zu verfolgen und die Zeit für jeden Aufruf zu registrieren. Natürlich müssen alle Bestandteile des Programmcodes, die einem Profiling unterworfen werden sollen, mit der Option -pg kompiliert werden, damit gprof erfolgreich angewendet werden kann. Außerdem sollten Sie das Programm kennen, das Sie untersuchen möchten - gprof kann nur eine gewisse Menge an Informationen ermitteln. Es liegt am Programmierer, ineffizienten Code zu verbessern.

Icon

Eine abschließende Bemerkung zu gprof: Wenn Sie es auf ein Programm ansetzen, das nur wenige Funktionen aufruft und sehr schnell läuft, erhalten Sie eventuell keine aussagekräftigen Ergebnisse. Die Laufzeiten werden meist in recht großen Zeiteinheiten gemessen - etwa in Hundertstelsekunden -, und wenn viele Funktionen in Ihrem Programm schneller sind, kann gprof die Laufzeiten der einzelnen Funktionen nicht mehr unterscheiden (und rundet dann auf die nächste Hundertstelsekunde auf oder ab). Um brauchbare Profiling-Informationen zu erhalten, müssen Sie Ihr Programm vielleicht unter ungewöhnlichen Umständen laufen lassen - etwa indem Sie es mit besonders vielen Daten füttern, wie wir das in unserem Beispiel getan haben.

Falls gprof mehr leistet, als Sie benötigen, können Sie mit dem Programm calls eine Baumstruktur aller Funktionsaufrufe in Ihrem C-Quellcode erstellen. Diese Struktur können Sie entweder zu einem Index aller aufgerufenen Funktionen oder zu einer hierarchischen Darstellung der Programmstruktur weiterverarbeiten.

Die Benutzung von calls ist sehr einfach. Sie geben dem Programm einfach die Namen der Quelldateien mit, die es analysieren soll, und schon wird die Aufrufstruktur angezeigt. Ein Beispiel:

papaya$ calls scan.c 1 level1 [scan.c] 2 getid [scan.c] 3 getc 4 eatwhite [scan.c] 5 getc 6 ungetc 7 strcmp 8 eatwhite [see line 4] 9 balance [scan.c] 10 eatwhite [see line 4]

calls ist so voreingestellt, daß es auf jeder Ebene der Baumstruktur nur ein Vorkommen jeder aufgerufenen Funktion auflistet (wenn also printf innerhalb einer bestimmten Funktion fünfmal aufgerufen wird, erscheint es nur einmal). Mit dem Schalter -a werden alle Vorkommen aufgelistet. Es gibt weitere Optionen zu calls; mit calls -h erhalten Sie eine Zusammenfassung.

strace

strace ist ein Werkzeug, das die Systemaufrufe anzeigt, die ein laufendes Programm durchführt. Fußnoten 2 Das kann bei der Analyse eines Programms zur Laufzeit eine große Hilfe sein - allerdings erfordert es einige Kenntnisse der Programmierung auf der Ebene der Systemaufrufe. Wenn Sie beispielsweise die Library-Routine printf in einem Programm benutzen, wird strace nur Informationen zum darunterliegenden Systemaufruf write anzeigen, wenn dieser ausgeführt wird. Die Ausgaben von strace können ziemlich umfangreich werden - d.h., in einem Programm finden viele Systemaufrufe statt, deren sich der Programmierer vielleicht nicht bewußt ist. Trotzdem ist strace ein nützliches Tool, um schnell die Ursache eines Programmabsturzes oder anderer Merkwürdigkeiten zu ermitteln.

Wir benutzen noch einmal das »Hello, World!«-Programm vom Anfang des Kapitels. Wenn wir strace auf die ausführbare Datei ansetzen, erhalten wir:

papaya$ strace hello execve("./hello", ["hello"], [/* 49 vars */]) = 0 mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,\ -1, 0) = 0x40007000 mprotect(0x40000000, 20881, PROT_READ|PROT_WRITE|PROT_EXEC) = 0 mprotect(0x8048000, 4922, PROT_READ|PROT_WRITE|PROT_EXEC) = 0 stat("/etc/ld.so.cache", {st_mode=S_IFREG|0644, st_size=18612,\ ...}) = 0 open("/etc/ld.so.cache", O_RDONLY) = 3 mmap(0, 18612, PROT_READ, MAP_SHARED, 3, 0) = 0x40008000 close(3) = 0 stat("/etc/ld.so.preload", 0xbffff52c) = -1 ENOENT (No such\ file or directory) open("/usr/local/KDE/lib/libc.so.5", O_RDONLY) = -1 ENOENT (No\ such file or directory) open("/usr/local/qt/lib/libc.so.5", O_RDONLY) = -1 ENOENT (No\ such file or directory) open("/lib/libc.so.5", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3"..., 4096) = 4096 mmap(0, 770048, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = \ 0x4000d000 mmap(0x4000d000, 538959, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_\ FIXED, 3, 0) = 0x4000d000 mmap(0x40091000, 21564, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_\ FIXED, 3, 0x83000) = 0x40091000 mmap(0x40097000, 204584, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_\ FIXED|MAP_ANONYMOUS, -1, 0) = 0x40097000 close(3) = 0 mprotect(0x4000d000, 538959, PROT_READ|PROT_WRITE|PROT_EXEC) = 0 munmap(0x40008000, 18612) = 0 mprotect(0x8048000, 4922, PROT_READ|PROT_EXEC) = 0 mprotect(0x4000d000, 538959, PROT_READ|PROT_EXEC) = 0 mprotect(0x40000000, 20881, PROT_READ|PROT_EXEC) = 0 personality(PER_LINUX) = 0 geteuid() = 501 getuid() = 501 getgid() = 100 getegid() = 100 fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(3, 10), ...}) = 0 mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,\ -1, 0) = 0x40008000 ioctl(1, TCGETS, {B9600 opost isig icanon echo ...}) = 0 write(1, "Hello World!\n", 13Hello World! ) = 13 _exit(0) = ? papaya$

Das ist wahrscheinlich viel mehr, als Sie von einem kleinen Programm erwartet hatten. Wir wollen die Ausgabe Schritt für Schritt durchgehen und dabei erklären, was passiert.

Der erste execve-Aufruf startet das Programm selbst. Alle mmap-, mprotect- und munmap-Aufrufe stammen aus der Speicherverwaltung des Kernels und sind hier nicht weiter interessant. In den drei folgenden open-Aufrufen sucht der Loader nach der C-Bibliothek und findet sie im dritten Anlauf. Der Header der Bibliothek wird dann eingelesen und die Bibliothek in den Speicher eingeblendet. Nach einigen weiteren Speicherverwaltungsoperationen und den Aufrufen von getuid, geteuid, getgid und getegid, die die Berechtigungen des Prozesses bestimmen, sehen Sie einen ioctl-Aufruf. Dieser stammt aus der Bibliotheksfunktion tcgetattr, die das Programm verwendet, um die Terminal-Attribute herauszubekommen, bevor es auf das Terminal schreibt. Schließlich gibt der write-Aufruf unsere freundliche Meldung auf das Terminal aus, und exit beendet das Programm.

strace schickt seine Ausgaben an die Standardfehlerausgabe. Sie haben also die Möglichkeit, diese Meldungen getrennt von den Ausgaben des Programms selbst (die meist an die Standardausgabe gehen) in eine Datei zu schreiben. Wie Sie sehen, gibt strace nicht nur die Namen der Systemaufrufe aus, sondern auch ihre Parameter (wenn möglich in Form von wohlbekannten Namen von Konstanten statt in Form von Zahlenwerten) und die Rückgabewerte.

make und imake

Wir haben bereits make vorgestellt, den Projektmanager, mit dem u.a. ganze Programmprojekte kompiliert werden. Ein Problem mit make ist, daß die Makefiles nicht ganz einfach zu erstellen sind. Wenn große Projekte anstehen, kann es langweilig werden, ein Makefile zu schreiben, das alle möglichen Quelldateien berücksichtigt. Selbst mit den internen Voreinstellungen von make bleibt häufig mehr Arbeit zu tun, als das der Fall sein sollte.

Die Benutzung von imake, einer Erweiterung zu make, die den C-Präprozessor verwendet, ist eine mögliche Lösung des Problems. imake erzeugt einfach Makefiles - Sie schreiben ein Imakefile, das imake in ein brauchbares Makefile umwandelt. imake wird von Programmen in der »X Window System«-Distribution benutzt, ist aber nicht nur auf X-Anwendungen beschränkt.

Wir wollen gleich an dieser Stelle darauf hinweisen, daß imake das Erstellen von Makefiles vereinfachen kann; insbesondere, wenn C-Programme kompiliert werden. Allerdings ist im allgemeinen make für diese Aufgabe viel eher geeignet als imake. So können Sie make beispielsweise zusammen mit groff oder dazu bringen, Schriftstücke automatisch zu formatieren. In diesem Fall sind Sie auf die Flexibilität von make angewiesen, und imake ist vielleicht nicht die beste Lösung.

Wir zeigen Ihnen hier ein Beispiel für ein Imakefile, das die beiden Programme laplacian und getstat kompiliert. Am Anfang des Imakefiles stehen Optionen, die die gesamte Kompilierung betreffen (imake hat seine eigenen Voreinstellungen hierfür, aber die sind nicht immer hilfreich). Anschließend werden für jedes zu kompilierende Programm Variablen definiert, und mit den imake-Makros AllTarget und NormalProgramTarget werden Makefile-Regeln für die Kompilierung dieser Programme aufgestellt.

# Linker-Optionen: LDOPTIONS = -L/usr/local/lib -L../lib # Benutze diesen C-Compiler: CC = gcc # Benutze diese Flags mit gcc: CFLAGS = -I. -I$(HOME)/include -g # Binde mit diesen lokalen und System-Libraries: LOCAL_LIBRARIES = -lvistuff SYS_LIBRARIES = -lm # Bestimme die Quelldateien in der Variablen SRCS und die entsprechenden # Objektdateien in der Variablen LAP_OBJS: SRCS = laplacian.c laplacian-main.c LAP_OBJS = laplacian.o laplacian-main.o # Stelle Regeln für laplacian auf: AllTarget(laplacian) NormalProgramTarget(laplacian,$(LAP_OBJS),,$(LOCAL_LIBRARIES),$(SYS_LIBRARIES)) # Dasselbe für getstat. Beachten Sie, daß SRCS für jedes Ziel neu # definiert werden kann, aber LAP_OBJS nicht. Wir benutzen deshalb einen # eindeutigen Namen für jedes Ziel. SRCS = getstat.c getstat-main.c component.c GS_OBJS = getstat.o getstat-main.o component.o AllTarget(getstat) NormalProgramTarget(getstat,$(GS_OBJS),,$(LOCAL_LIBRARIES),$(SYS_LIBRARIES))

Beachten Sie, daß wir für jedes Ziel eine andere Variable für die Objektdatei benutzen müssen, während SRCS immer wieder neu definiert werden kann.

Geben Sie den Befehl xmkmf ein, um aus dem Imakefile ein Makefile zu erstellen. xmkmf ruft einfach imake mit den für die Übersetzung geeigneten Optionen auf und benutzt dabei die Standardmakros von imake (wie AllTarget und NormalProgramTarget). Anschließend können Sie mit make das Programm kompilieren.

papaya$ xmkmf mv Makefile Makefile.bak imake -DUseInstalled -I/usr/X386/lib/X11/config papaya$

Icon

imake(1)
xmkmf(1)
[57] imake

Falls Sie Ihre eigenen imake-Makros einsetzen möchten, können Sie imake von Hand mit den entsprechenden Optionen aufrufen. In den Manpages zu imake und xmkmf finden Sie weitere Informationen. Das Buch Software Portability with imake von Paul DuBois bietet ebenfalls eine Beschreibung dieses Systems.

Für den Fall, daß Ihnen imake zu kompliziert vorkommt, gibt es andere »Makefile-Generatoren« - etwa ICmake, das Makefiles mit Hilfe einer Makrosprache erstellt, die sehr an C erinnert.

Wenn Sie selbst Softwarepakete kompiliert haben, sind Sie sicherlich oft auf Kompilieranweisungen gestoßen, nach denen Sie ein mitgeliefertes Skript namens configure ausführen sollten. Dieses wird von einem Makefile-Generator namens autoconf erzeugt, der oft zusammen mit einem anderen Programm namens automake verwendet wird. autoconf und automake sind nicht einfach zu benutzen, bringen aber mehr Flexibilität als imake, ICmake und alle anderen Makefile-Generatoren. Leider würde es den Rahmen dieses Buches weit sprengen, die Verwendung von autoconf hier zu beschreiben. Wenn Sie das interessiert, dann laden Sie sich die Pakete aus den Archiven herunter und lesen Sie die Dokumentation.

Checker

Checker ersetzt die verschiedenen Routinen, die von C-Programmen für die Speicherverwaltung verwendet werden (wie zum Beispiel malloc, realloc und free). Checker hat die besseren Prozeduren zur Verwaltung des Speichers sowie Code, der unzulässige Speicherzugriffe und häufig gemachte Fehler entdeckt (wie zum Beispiel den Versuch, einen Speicherbereich mehr als einmal freizugeben). Wenn Ihr Programm kritische Speicherzugriffe durchführen möchte, wird Checker ausführliche Fehlermeldungen ausgeben und Ihnen damit helfen, Segmentierungsfehler in Ihrem Programm abzufangen, bevor sie auftreten. Checker ist außerdem in der Lage, Speicherverluste zu verhindern - beispielsweise solche Stellen im Code, an denen Speicher mit malloc zugewiesen und anschließend nicht mit free wieder freigegeben wird.

Checker ist mehr als nur ein Ersatz für malloc und Co. Es fügt auch Code in Ihr Programm ein, der alle Lese- und Schreibzugriffe auf den Speicher verifiziert. Es ist äußerst robust und deshalb etwas langsamer als die üblichen malloc-Routinen. Checker ist für den Einsatz während der Programmentwicklung und -tests gedacht - sobald alle potentiellen Speicherfehler beseitigt sind, können Sie Ihr Programm mit den Standardbibliotheken binden.

Lassen Sie uns folgendes Beispielprogramm betrachten, das Speicher zuordnet und dann verschiedene haarige Dinge damit anzustellen versucht:

#include <malloc.h> int main() { char *thememory, ch; thememory=(char *)malloc(10*sizeof(char)); ch=thememory[1]; /* Attempt to read uninitialized memory */ thememory[12]=' '; /* Attempt to write after the block */ ch=thememory[-2]; /* Attempt to read before the block */ }

Wir kompilieren dieses Programm einfach mit der Option -lchecker, so daß es mit den Checker-Libraries gebunden wird. Nach dem Aufruf erhalten wir (unter anderem) folgende Fehlermeldungen:

From Checker: Memory access error When Reading at address 0x10033 inside the heap 1 bytes after the begin of the block From Checker: Memory access error When Writing at address 0x1003e inside the heap 2 bytes after the end of the block From Checker: Memory access error When Reading at address 0x10030 inside the heap 2 bytes before the begin of the block

Zu jedem Speicherfehler gibt Checker eine Meldung aus und teilt uns mit, was passiert ist. Die vollständigen Fehlermeldungen von Checker enthalten auch Informationen darüber, wo das Programm ausgeführt wird und wo der Speicherblock vergeben wurde. Auf Wunsch können Sie noch mehr Informationen aus Checker herauskitzeln - zusammen mit einem Debugger wie gdb lassen sich Problemstellen dann leicht finden. Fußnoten 3

Checker enthält auch einen Garbage-Collector und -Detector, die Sie aus Ihrem Programm heraus aufrufen können. Kurz gesagt: Der Garbage-Detector informiert Sie über alle Speicherverluste - Stellen, an denen eine Funktion mit malloc Speicher zugewiesen hat, den sie vor der Rückkehr nicht mit free wieder freigegeben hat. Der Garbage-Collector untersucht den Stapel und bereinigt die Folgen solcher Speicherverluste. Sie können Garbage-Collector und -Detector auch von Hand aufrufen, während Sie Ihr Programm innerhalb von gdb laufen lassen (weil gdb den direkten Aufruf von Funktionen zur Laufzeit unterstützt).

Werkzeuge für die Erstellung von Benutzerschnittstellen

Es gibt eine Reihe von Anwendungen und Bibliotheken, die auf einfache Weise die Erstellung von Benutzerschnittstellen für Ihre Programme unter dem X Window System unterstützen. Wenn Sie sich nicht mit der Komplexität der X-Programmierschnittstelle auseinandersetzen möchten, ist eines dieser einfachen Tools zum Erstellen einer Benutzerschnittstelle vielleicht genau das, was Sie suchen. Es gibt auch Tools, die die Erstellung von textbasierten Schnittstellen für solche Programme unterstützen, die nicht unter X laufen.

Die klassische X-Programmierung wollte so allgemein wie möglich bleiben und nur ein absolutes Minimum an Beschränkungen und Voraussetzungen für das Interface vorschreiben. Diese Allgemeinheit gibt den Programmierern die Möglichkeit, ihre eigenen Schnittstellen »von Grund auf« zu entwerfen, weil die zugrundeliegenden X-Bibliotheken vorab keinerlei Voraussetzungen an das Interface stellen. Das X-Toolkit Intrinsics (Xt) stellt einige wenige Interface-Widgets (etwa einfache Schaltflächen, Laufleisten und ähnliches) zur Verfügung; außerdem ist ein generelles Interface zum Schreiben eigener Widgets vorhanden. Unglücklicherweise steht den Programmierern, die lieber einen Satz vorgefertigter Interface-Routinen benutzen würden, eventuell eine Menge Arbeit ins Haus. Eine Reihe von Xt-Widget-Sets und Bibliotheken stehen unter Linux zur Verfügung, die alle dazu beitragen, daß die Benutzerschnittstelle einfacher zu erstellen ist.

Zusätzlich gibt es von verschiedenen Quellen die kommerzielle M-Library und das Widget-Set in einer Einzelplatzversion zu einem günstigen Preis. Außerdem existiert die XView-Library mit Widget-Interface als eine weitere Alternative zur Erstellung von Schnittstellen mit Xt. XView und Motif sind zwei Sammlungen von X-basierten Programmbibliotheken, die in mancher Hinsicht einfacher zu programmieren sind als das X-Toolkit Intrinsics. Es gibt viele Anwendungsprogramme, die Motif und XView nutzen - etwa XVhelp (ein System, mit dem Sie eine interaktive Hypertext-Hilfefunktion für Ihr Programm erzeugen). Binärcode, der mit Motif statisch gebunden wurde, darf ohne Einschränkungen weitergegeben werden und kann so auch von Leuten benutzt werden, die kein eigenes Motif haben.

Bevor Sie jetzt aber anfangen, mit XView oder M zu entwickeln, noch ein paar Worte der Warnung. XView war ursprünglich ein kommerzielles Produkt von Sun Microsystems, wird jetzt aber nicht mehr weiterentwickelt. Außerdem sehen mit XView geschriebene Programme ziemlich ungewohnt aus, auch wenn manche Leute das gern mögen. Motif wird dagegen weiterhin entwickelt (wenn auch in einem ziemlich langsamen Takt), hat aber einige Probleme. Zunächst kann die Programmierung mit Motif frustrierend - weil schwierig -, fehlerträchtig und umständlich sein, denn die Programmierschnittstelle von Motif (API) wurde nicht entsprechend moderner Entwurfsprinzipien entwickelt. Außerdem sind Motif-Programme üblicherweise sehr langsam.

Es gibt aber noch andere Widget-Sets und Interface-Libraries für X:

Xaw3D
eine angepaßte Version der Athena-Widgets, die ein dreidimensionales, M-ähnliches Look-and-Feel vermittelt.
Qt
ein C++-GUI-Toolkit, das von der norwegischen Firma Troll Tech entwickelt wird.
GTK
ein C-GUI-Toolkit, das ursprünglich für das Bildbearbeitungsprogramm GIMP entwickelt wurde

Viele Leute sind der Meinung, daß die Athena-Widgets zu schlicht aussehen. Xaw3D ist vollständig kompatibel zu den Standard-Athena-Widgets und kann sogar benutzt werden, um die Athena-Libraries auf Ihrem System zu ersetzen. Dadurch bekommen alle Programme, die Athena benutzen, ein modernes Aussehen. Xaw3D enthält zusätzlich ein paar Widgets, die in der Athena-Sammlung nicht enthalten sind; etwa ein Layout-Widget mit einer TEX-ähnlichen Schnittstelle zur Bestimmung der Position eines Kind-Widgets.

Icon

Kapitel 11

Qt ist ein exzellentes Paket zur GUI-Entwicklung in C++, das sich unter anderem durch einen genialen Mechanismus zur Verbindung von Benutzerinteraktionen mit Programmcode, schnellen Zeichencode und eine umfassende, aber gleichwohl einfach zu verwendende API auszeichnet. Qt wird von vielen als der Nachfolger von M als De-facto-Standard für die GUI-Programmierung angesehen, weil es die Grundlage des Desktops KDE (siehe »Das K Desktop Environment« in Kapitel 11, Die X Arbeitsoberfläche anpassen) bildet, der derzeit sehr viel Aufmerksamkeit erfährt.

Icon

[54]

Qt ist ein kommerzielles Produkt, aber Sie können es kostenlos verwenden, wenn Sie damit freie Software für Unix (und damit auch Linux) entwickeln. Es gibt auch eine (kommerzielle) Windows-Version von Qt, was es möglich macht, für Linux und Windows gleichzeitig zu entwickeln und das Programm im besten Fall durch einfaches Neukompilieren auf die andere Plattform zu übertragen. Stellen Sie sich das einmal vor: Sie können auf Ihrem Lieblings-Betriebssystem Linux entwickeln und trotzdem den noch sehr viel größeren Windows-Markt bedienen! Einer der Autoren, Kalle, verwendet Qt, um sowohl Freie Software (das gerade erwähnte KDE) als auch kommerzielle Software (oft Cross-Plattform-Produkte, die für Linux und Windows entwickelt werden) zu schreiben. Qt wird äußerst aktiv weiterentwickelt; nähere Informationen finden Sie in Programming with Qt von Kalle Dalheimer.

Für diejenigen, die C++ nicht mögen, kann GTK eine Alternative sein. GTK-Programme sind normalerweise genauso schnell wie Qt-Programme, aber das Toolkit selbst ist nicht so vollständig. GTK kann eine gute Alternative sein, wenn Sie nicht darauf angewiesen sind, Ihr Programm auch unter Windows zum Laufen zu bringen.

Viele Programmierer sind der Meinung, daß die Erzeugung von Benutzerschnittstellen, selbst mit einer vollständigen Sammlung von Widgets und C-Routinen, einigen Aufwand verursacht und ziemlich schwierig sein kann. Hier muß man Flexibilität gegen einfache Programmierung abwägen - je einfacher die Schnittstelle erzeugt werden kann, desto weniger Einfluß kann der Programmierer darauf nehmen. Viele Programmierer begnügen sich mit vorgefertigten Widgets, so daß die verlorengegangene Flexibilität keine Rolle mehr spielt.

Eines der Probleme bei der Erzeugung von Schnittstellen und der Programmierung unter X ist die Tatsache, daß es sehr schwierig ist, für die am häufigsten benutzten Elemente einer Benutzerschnittstelle ein allgemeingültiges, einfaches Programmiermodell zu finden. Ein Beispiel: Viele Programme benutzen solche Elemente wie Schaltflächen, Dialogboxen, Pull-down-Menüs usw. - aber fast jedes Programm benutzt diese Widgets in einem anderen Kontext. Indem die Tools das Erstellen von graphischen Schnittstellen vereinfachen, machen sie in der Regel auch Annahmen dazu, was Sie haben wollen. Auch dazu ein Beispiel: Es ist kein Problem zu bestimmen, daß ein Klick auf eine Schaltfläche innerhalb Ihres Programms eine bestimmte Prozedur ausführen soll. Was aber passiert, wenn Sie dieser Schaltfläche eine spezielle Aktion zuordnen wollen, die in der Programmierschnittstelle nicht vorgesehen ist? Beispielsweise könnte ein Klick auf diese Schaltfläche unterschiedliche Reaktionen hervorrufen - je nachdem, ob mit der rechten oder der linken Maustaste geklickt wird. Wenn das System zum Erzeugen der Schnittstelle ein solches Maß an Flexibilität nicht vorgesehen hat, ist es für die Programmierer kaum zu gebrauchen, die ein vielseitiges, speziell angepaßtes Interface benötigen.

Die Programmierschnittstelle Tcl/Tk, die wir im letzten Kapitel beschrieben haben, wird immer beliebter. Das liegt zum Teil daran, daß sie so einfach zu benutzen ist und ein hohes Maß an Flexibilität bietet. Weil Tcl- und Tk-Routinen sowohl von interpretierten »Skripten« als auch aus einem C-Programm heraus aufgerufen werden können, ist es nicht schwierig, die Schnittstellenfunktionen dieser Sprache und dieses Toolkits mit Funktionen im Programm zu verknüpfen. Die Arbeit mit Tcl und Tk ist insgesamt weniger anspruchsvoll, als die direkte Programmierung mit Xlib und Xt zu erlernen (zusammen mit den unzähligen Widget-Sets). Es sollte hier aber nicht unerwähnt bleiben, daß Sie bei größeren Projekten eher eine Programmiersprache wie C++ verwenden sollten, die mehr für Entwicklung in größerem Rahmen geeignet ist. Größere Projekte lassen sich aus mehreren Gründen mit Tcl schlecht hantieren: Die Verwendung einer interpretierten Programmiersprache verringert die Ausführungsgeschwindigkeit des Programms, das Design von Tcl/Tk steht einer Anwendung von Tcl/Tk bei größeren Projekten im Weg, und wichtige Features zum Schreiben fehlerarmer Programme - wie die Typüberprüfung zur Kompilier- und Link-Zeit - fehlen ganz. Das Problem der schlechten Skalierung wird durch die Verwendung von Namensräumen (einer Möglichkeit, Namenskonflikte zwischen verschiedenen Teilen eines Programms zu vermeiden) und einer objektorientierten Erweiterung namens [incr Tcl] etwas beseitigt.

TclM, eine Version von Tcl, die mit dem beliebten Widget-Set von Motif gebunden wurde, ist ebenfalls für Linux erhältlich. Man sagt den Motif-Widgets nach, daß sie einfach zu programmieren und angenehm zu benutzen sind. Der Vorteil von TclMotif ist, daß die ausführbare Datei frei verteilt werden darf, obwohl Motif selbst ein kommerzielles Produkt ist. Sie müssen also nicht Motif besitzen, um TclMotif zu benutzen. TclMotif gestattet es, daß Sie Programme schreiben, die über die Tcl-Schnittstelle die Widgets und Routinen von Motif benutzen. Eine statisch gebundene ausführbare Datei finden Sie auf einer Reihe von Linux-FTP-Rechnern und bei anderen Quellen. Falls Sie aus irgendeinem Grund vorhaben, TclMotif selbst neu zu kompilieren, müßten Sie dazu erst Motif besitzen.

Wafe ist eine andere Version von Tcl/Tk, die auch die Athena-Widgets sowie diverse andere Tools enthält, die die Programmierung vereinfachen. Wenn Sie bisher Xt mit den Athena-Widgets programmiert haben, aber jetzt auf Tcl und Tk umsteigen möchten, bietet Ihnen Wafe einen guten Ausgangspunkt.

Icon

Kapitel 13

Mit Tcl und Tk können Sie um ein existierendes Programm herum eine komplette X-basierte Schnittstelle mit Fenstern, Schaltflächen, Menüs, Laufleisten und anderen Dingen erzeugen. Sie können entweder von einem Tcl-Skript aus (wie im Abschnitt »Programmieren mit Tcl und Tk« in Kapitel 13, Programmiersprachen, beschrieben) oder aus einem C-Programm heraus auf die Schnittstelle zugreifen.

Ein anderes Werkzeug zum Erstellen von Schnittstellen, das große Ähnlichkeiten mit Tcl und Tk aufweist, ist xtpanel. xtpanel soll in erster Linie einen X-Wrapper (Hülle, Umschlag) um eine existierende textbasierte Anwendung herumlegen. Mit xtpanel können Sie ein Fenster mit verschiedenen Bereichen - Regionen zum Editieren von Texten, Schaltflächen, Laufleisten usw. - definieren; die Aktionen dieser Widgets verknüpfen Sie dann mit bestimmten Fähigkeiten des Programms. Man könnte beispielsweise mit xtpanel nach dem Vorbild von xxgdb eine X-basierte Schnittstelle zum Debugger gdb erzeugen. Sie könnten darin einen »step«-Button definieren, der nach dem Anklicken den Befehl step an die eigentliche gdb-Schnittstelle schickt. Man könnte ein Textfenster definieren, in dem der gdb in der üblichen Weise benutzt würde. Natürlich wäre es schwierig, mit einem wenig spezialisierten Programm wie xtpanel eine komplexere Aufgabe wie die Erzeugung eines Quellcodefensters zu bewerkstelligen.

Wenn Sie das Toolkit Tk, aber nicht die Programmiersprache Tcl mögen, dann wird es Sie freuen zu hören, daß man Tk auch mit anderen Programmiersprachen verwenden kann. So ist es das GUI-Toolkit der Wahl für Skriptsprachen wie Python und Perl geworden.

Wenn Sie ein nettes textbasiertes Interface zu einem Programm suchen, haben Sie mehrere Möglichkeiten. Die getline-Library von GNU besteht aus einer Sammlung von Routinen, die weitergehende Möglichkeiten zum Editieren der Befehlszeile und zum Anzeigen von Aufforderungen an den Benutzer enthält; auch das Wiederaufrufen bereits abgeschickter Befehle (command history) und andere Fähigkeiten, die von vielen Programmen genutzt werden, sind enthalten. So lesen zum Beispiel sowohl bash als auch gdb die Eingaben des Benutzers mit Hilfe der getline-Bibliothek. getline bietet auch die Möglichkeiten zum Editieren der Befehlszeile mit vi- und Emacs-ähnlichen Befehlen, die man in bash und ähnlichen Programmen findet. (Wir beschreiben das Editieren der Befehlszeile unter bash im Abschnitt »Tips für schnelle Tipper« in Kapitel 4, Grundlegende Unix-Befehle und -Konzepte.)

Eine andere Möglichkeit besteht darin, eine Sammlung von Emacs-Interface-Routinen für Ihr Programm zu schreiben. Ein Beispiel dafür ist die Schnittstelle zwischen gdb und Emacs, die innerhalb von Emacs mehrere Fenster öffnet, Sondertasten definiert usw. (Wir besprechen dieses Interface im Abschnitt »Emacs und gdb«.) Im gdb-Code mußten keinerlei Änderungen vorgenommen werden, um diese Schnittstelle zu implementieren - in der Emacs-Library-Datei gdb.el können Sie nachsehen, wie das bewerkstelligt wurde. Emacs erlaubt den Aufruf eines Unterprogramms innerhalb eines Textpuffers, und es kennt viele Routinen für die Analyse und Bearbeitung von Text in diesem Puffer. Ein Beispiel: Innerhalb seiner gdb-Schnittstelle fängt Emacs die Ausgaben eines Quellcode-Listings von gdb ab und übergibt das Listing an einen Befehl, der die aktuelle Codezeile in einem anderen Fenster anzeigt. Routinen in Emacs-LISP verarbeiten die Ausgabe von gdb und führen anschließend - abhängig von der Ausgabe - bestimmte Aktionen durch.

Der Vorteil eines Zusammenspiels zwischen Emacs und textbasierten Programmen ist, daß Emacs selbst eine ausgefeilte und anpassungsfähige Benutzerschnittstelle darstellt. Der Benutzer kann problemlos Tasten und Befehle nach seinen Wünschen neu belegen - Sie müssen diese Möglichkeiten zur Konfiguration nicht selbst schaffen. Solange die Textschnittstelle des Programms in der Lage ist, mit Emacs zusammenzuarbeiten, läßt sich das auf einfache Weise einrichten. Außerdem ziehen viele Benutzer es vor, beinahe alles innerhalb von Emacs zu tun - das Lesen von E-Mail und News ebenso wie das Kompilieren und Debuggen von Programmen. Wenn Sie Ihr Programm mit einem Emacs-Frontend versehen, erleichtern Sie diesen Leuten die Benutzung des Programms. Ihr Programm wird auch in der Lage sein, mit anderen Programmen zu kooperieren, die ebenfalls unter Emacs laufen - beispielsweise kann Text zwischen verschiedenen Emacs-Textpuffern problemlos hin- und herkopiert werden. Wenn Sie wollen, können Sie auch komplette Programme in Emacs-LISP schreiben.

Werkzeuge für die Versionskontrolle - RCS

Das »Revision Control System« (RCS; System zur Versionskontrolle) ist nach Linux portiert worden. Es handelt sich dabei um eine Sammlung von Programmen, die eine »Bibliothek« von Dateien unterhalten, in denen Versionswechsel vermerkt werden. RCS gestattet außerdem das Sperren von Quellcodedateien (wenn mehrere Leute an einem Projekt arbeiten) und merkt sich automatisch die Versionsnummern dieser Dateien. Das RCS wird meistens mit Quellcodedateien benutzt, ist aber so flexibel, daß es mit beliebigen Dateiarten verwendet werden kann, von denen mehrere Versionen gepflegt werden müssen.

Wozu braucht man eine Versionskontrolle? Für viele große Projekte ist die Versionskontrolle notwendig, um den Überblick über zahlreiche kleine, komplexe Änderungen am System zu behalten. Wenn Sie beispielsweise versuchen, ein Programm mit 1000 Quelldateien und einigen Dutzend Programmierern zu pflegen, haben Sie ohne so etwas wie RCS kaum eine Chance. Mit RCS können Sie sicherstellen, daß zu einem beliebigen Zeitpunkt nur eine Person eine bestimmte Datei ändern kann. Alle Änderungen werden, zusammen mit einem Hinweis auf die Art derselben, schriftlich festgehalten.

RCS basiert auf dem Konzept einer RCS-Datei - das ist eine Datei, die wie eine »Bibliothek« funktioniert, in der Quelldateien »eingecheckt« und »ausgecheckt« werden. Nehmen wir an, daß Sie die Quellcodedatei importrtf.c mit RCS verwalten möchten. Per Voreinstellung heißt die RCS-Datei dann importrtf.c,v. Die RCS-Datei enthält die Geschichte aller Versionen dieser Datei, so daß Sie die Möglichkeit haben, beliebige frühere Versionen der Datei zu extrahieren. Zu jeder Version können Sie selbst eine Notiz (log) hinzufügen.

Wenn Sie mit dem RCS eine Datei einchecken, werden die Änderungen in die RCS-Datei aufgenommen, und die Originaldatei wird (in der Voreinstellung) gelöscht. Wenn Sie auf die Originaldatei zugreifen möchten, müssen Sie sie aus der RCS-Datei auschecken. Wenn Sie gerade eine Datei bearbeiten, möchten Sie in der Regel vermeiden, daß jemand anderes gleichzeitig daran arbeitet. RCS wird die Datei deshalb sperren, wenn sie zum Editieren ausgecheckt wurde. Eine gesperrte Datei kann nur von dem Benutzer verändert werden, der sie ausgecheckt hat (dies wird mit Hilfe der Dateiberechtigungen erreicht). Sobald Sie mit Ihren Änderungen am Quellcode fertig sind, checken Sie die Datei wieder ein; anschließend kann jeder andere Mitarbeiter diese Datei wieder auschecken, um weiter daran zu arbeiten. Wenn Sie eine Datei im ungesperrten Zustand auschecken, gelten diese Einschränkungen nicht. Im allgemeinen werden Dateien dann als gesperrt ausgecheckt, wenn sie editiert werden sollen, und sie werden ungesperrt ausgecheckt, wenn sie nur gelesen werden (um den Quellcode beispielsweise beim Debuggen vor Augen zu haben).

Icon

Kapitel 4

RCS vermerkt automatisch alle früheren Änderungen in der RCS-Datei und vergibt aufsteigende Versionsnummern an jede neu eingecheckte Version. Sie haben auch die Möglichkeit, beim Einchecken einer Datei mit RCS selbst eine Versionsnummer zu vergeben. Damit können Sie einen neuen »Versionszweig« anlegen, so daß mehrfach vorhandene Projekte von verschiedenen Versionen derselben Datei abstammen können. Auf diese Weise kann Programmcode in verschiedenen Projekten benutzt werden, ohne daß die Änderungen aus einem Projekt auch in den anderen wirksam werden.

Dazu ein Beispiel: Wir gehen von der Quelldatei importrtf.c aus, die unser freundliches Programm enthält:

#include <stdio.h> int main(void) { printf("Hello, world!"); }

Als erstes müssen wir die Datei mit dem Befehl ci in das RCS einchecken:

papaya$ ci importrtf.c importrtf.c,v <-- importrtf.c enter description, terminated with single '.' or end of file: NOTE: This is NOT the log message! >> Hello world source code >> . initial revision: 1.1 done papaya$

Die RCS-Datei importrtf.c,v wird angelegt, und importrtf.c wird entfernt.

Wenn Sie die Quelldatei weiter bearbeiten möchten, checken Sie sie mit dem Befehl co wieder aus. Mit

papaya$ co -l importrtf.c importrtf.c,v --> importrtf.c revision 1.1 (locked) done papaya$

checken Sie importrtf.c (aus importrtf.c,v) aus und sperren die Datei. Sie können dann die gesperrte Datei editieren und wieder einchecken. Wenn Sie eine Datei nur zum Lesen auschekken möchten (um zum Beispiel make aufzurufen), können Sie den Schalter -l hinter dem Befehl co weglassen und die Datei ungesperrt auschecken. Sie können eine Datei nur einchecken, wenn sie zuvor gesperrt wurde (oder wenn sie noch nie eingecheckt war, wie in unserem Beispiel).

Jetzt haben Sie Gelegenheit, den Quellcode zu ändern und die Datei anschließend wieder einzuchecken. Oft werden Sie die Datei permanent ausgecheckt lassen und ci nur benutzen, um die letzten Änderungen in der RCS-Datei zu vermerken und die Versionsnummer zu erhöhen. Benutzen Sie für diesen Zweck den Schalter -l mit ci, etwa so:

papaya$ ci -l importrtf.c importrtf.c,v <-- importrtf.c new revision: 1.2; previous revision: 1.1 enter log message, terminated with single '.' or end of file: >> Changed printf call >> . done papaya$

Damit wird die Datei nach dem Einchecken automatisch in gesperrtem Zustand wieder ausgecheckt. Auf diese Weise können Sie Änderungen dokumentieren, wenn Sie der einzige Mitarbeiter an einem Projekt sind.

Wenn Sie RCS häufig benutzen, möchten Sie vielleicht nicht die ganzen unschönen importrtf.c,v-Dateien in Ihrem Verzeichnis stehen haben. Legen Sie deshalb in Ihrem Projektverzeichnis das Unterverzeichnis RCS an, und ci sowie co werden die RCS-Dateien dort ablegen - getrennt vom Rest der Quellcodedateien.

RCS hebt außerdem alle vorherigen Versionen Ihrer Datei auf. Wenn Sie beispielsweise eine Datei ändern, und diese Änderung führt zu einem Programmabbruch, möchten Sie vielleicht die letzte Änderung rückgängig machen und mit der vorherigen Version noch einmal beginnen. Sie können zu diesem Zweck mit co die Datei mit einer bestimmten Versionsnummer auschecken. Ein Beispiel:

papaya$ co -l1.1 importrtf.c importrtf.c,v --> importrtf.c revision 1.1 (locked) writable importrtf.c exists; remove it? [ny](n): y done papaya$

Damit checken Sie die Version 1.1 der Datei importrtf.c aus. Mit dem Programm rlog können Sie die Versionsgeschichte einer bestimmten Datei einsehen. Sie bekommen die Notizen angezeigt, die Sie mit ci eingegeben haben, sowie weitere Informationen wie das Datum, den Benutzer, der die Version eingecheckt hat, usw.

Beim Auschecken wird RCS automatisch die Informationen aktualisieren, die sich hinter bestimmten »Schlüsselwörtern« verbergen. Wenn Sie beispielsweise den String

/* $Header$ */

in die Quellcodedatei schreiben, ersetzt co ihn durch einen ausführlichen Text mit dem Versionsdatum, der Versionsnummer usw. - etwa so:

/* $Header: /work/linux/hitch/programming/tools/RCS/rcs.tex 1.2 1994/12/04 15:19:31 mdw Exp mdw $ */

(Wir haben die Zeile umbrochen, damit sie auf die Seite paßt; in Wirklichkeit finden Sie alle Informationen in einer Zeile vor.)

Es gibt weitere Schlüsselwörter wie $Author$, $Date$ und $Log$ (letzteres enthält die Notizen zu allen Versionen, die in der Quelldatei enthalten sind).

Viele Programmierer schreiben eine feste Zeichenfolge in jede Quellcodedatei, die nach dem Kompilieren benutzt werden kann, um diese Programmversion zu ideizieren. Sie könnten zum Beispiel folgendes in jede Quelltextdatei Ihres Programms schreiben:

static char rcsid[] = "\@(#)$Header$";

co ersetzt das Schlüsselwort $Header$ durch einen Text, wie wir ihn etwas weiter oben gezeigt haben. Diese statische Zeichenfolge wird in der ausführbaren Datei erhalten bleiben, und mit dem Befehl what können Sie solche Strings anzeigen lassen. Beispielsweise könnten wir nach dem Kompilieren von importrtf.c zu der ausführbaren Datei importrtf folgenden Befehl eingeben:

papaya$ what importrtf foo: $Header: /work/linux/hitch/programming/tools/RCS/rcs.tex 1.2 1994/12/04 15:19:31 mdw Exp mdw $ papaya$

what sucht die Zeichenfolgen heraus, die mit den Zeichen @(#) beginnen, und zeigt sie an. Wenn Sie ein Programm vor sich haben, das aus vielen Quelldateien und Libraries kompiliert wurde, und Sie wissen nicht mehr, wie aktuell die einzelnen Bestandteile sind, können Sie mit what Versionsinformationen zu allen Quelldateien anzeigen, aus denen die ausführbare Datei kompiliert wurde.

Icon

ci(1)
co(1)
rcs(1)

Zu RCS gehören einige weitere Programme, darunter auch rcs, das für die Verwaltung von RCS-Dateien benutzt wird. rcs kann zum Beispiel anderen Benutzern die Berechtigung erteilen, Quelldateien aus einer RCS-Datei auszuchecken. Sie finden weitere Informationen in den Manpages zu ci, co und rcs.

Werkzeuge für die Versionskontrolle - CVS

CVS, das »Concurrent Version System«, ist komplexer als RCS und daher vielleicht etwas überqualifiziert für Ein-Mann-Projekte. Aber immer wenn mehr als ein oder zwei Programmierer an einem Projekt arbeiten oder der Quellcode auf mehrere Verzeichnisse verteilt ist, ist CVS die bessere Wahl. CVS verwendet das Dateiformat von RCS zum Speichern der Änderungen, hat aber eine eigene Verwaltungsstruktur.

Per Voreinstellung arbeitet CVS auf ganzen Verzeichnisbäumen. Jeder CVS-Befehl, den Sie verwenden, betrifft also das aktuelle Verzeichnis und alle darin enthaltenen Unterverzeichnisse, wiederum alle darin enthaltenen Unterverzeichnisse und so weiter. Dieses rekursive Durchlaufen kann mit einer Kommandozeilenoption abgeschaltet werden; Sie können aber auch einfach eine einzige Datei zur Bearbeitung angeben.

CVS hat das Sandkasten-Konzept formalisiert, das in vielen Softwarefirmen verwendet wird. In diesem Konzept gibt es ein sogenanntes Repository, das die »offiziellen« Quellen enthält, von denen man weiß, daß sie kompilieren und funktionieren (zumindest teilweise). Kein Entwickler darf jemals Dateien direkt im Repository bearbeiten. Statt dessen checkt jeder Entwickler einen eigenen Verzeichnisbaum aus, den sogenannten Sandkasten. Hier kann er die Quellen nach Belieben editieren, Änderungen vornehmen, Dateien hinzufügen oder entfernen und all die Dinge tun, die Entwickler üblicherweise so tun (nein, wir meinen nicht Quake spielen und Marshmallows essen). Wenn der Entwickler sicher ist, daß die Änderungen kompilieren und funktionieren, überträgt er diese wieder in das Repository und stellt sie damit anderen Entwicklern zur Verfügung.

Wenn Sie als Entwickler einen lokalen Verzeichnisbaum ausgecheckt haben, sind alle Dateien schreibbar. Sie können in Ihrem persönlichen Arbeitsbereich alle notwendigen Änderungen vornehmen. Wenn Sie mit dem Testen fertig sind und von Ihrer Arbeit so überzeugt sind, daß Sie sie dem Rest Ihres Teams zur Verfügung stellen wollen, dann schreiben Sie alle geänderten Dateien mit einem CVS-commit-Befehl wieder in das zentrale Repository zurück. CVS überprüft dann, ob ein anderer Entwickler Änderungen vorgenommen hat, seit Sie den Verzeichnisbaum ausgecheckt haben. Wenn das der Fall ist, läßt CVS Sie nicht Ihre Änderungen einchecken, sondern bittet Sie, zunächst die Änderungen der anderen Entwickler in Ihren lokalen Verzeichnisbaum zu übernehmen. Während dieser Update-Operation verwendet CVS einen raffinierten Algorithmus, um Ihre Änderungen mit denen der anderen Entwickler zusammenzuführen. Es gibt Fälle, in denen das nicht automatisch möglich ist. In diesem Fall informiert Sie CVS darüber, daß es Konflikte gegeben hat, und fordert Sie auf, diese aufzulösen. Die betroffene Datei wird mit speziellen Zeichen versehen, so daß Sie gut erkennen können, wo ein Konflikt eingetreten ist, und sich überlegen können, welche Version verwendet werden soll. Beachten Sie, wie CVS so sicherstellt, daß es Konflikte immer nur im lokalen Baum des Entwicklers geben kann. Das Repository enthält grundsätzlich eine konsistente Version.

Ein CVS-Repository einrichten

Wenn Sie an einem größeren Projekt arbeiten, dann ist es nicht unwahrscheinlich, daß jemand anderes bereits alles Notwendige installiert und eingerichtet hat, um CVS zu verwenden. Aber wenn Sie der Administrator Ihres Projekts sind oder einfach nur mit CVS auf Ihrem lokalen Rechner herumspielen wollen, dann müssen Sie selbst ein Repository einrichten.

Weisen Sie zunächst der Umgebungsvariablen CVSROOT das Verzeichnis zu, in dem das CVS-Repository liegen soll. CVS kann beliebig viele Projekte in einem Repository verwalten und stellt sicher, daß diese nicht miteinander in Konflikt geraten. Sie müssen also nur einmal ein Verzeichnis auswählen, in dem alle Ihre mit CVS verwalteten Projekte untergebracht werden sollen, und müssen dieses nicht ändern, wenn Sie an einem anderen Projekt arbeiten. Anstelle der Variablen CVSROOT können Sie bei allen CVS-Befehlen immer auch den Kommandozeilenschalter -d verwenden, aber es ist mühsam, das immer von Hand einzugeben, weswegen wir hier annehmen werden, daß Sie CVSROOT gesetzt haben.

Wenn das Verzeichnis für das Repository einmal existiert, können Sie das Repository selbst mit dem folgenden Befehl erzeugen (wir gehen hier davon aus, daß CVS auf Ihrem Rechner installiert ist):

tigger$ cvs init

Es gibt mehrere verschiedene Möglichkeiten, einen Projektbaum im CVS-Repository anzulegen. Wenn Sie bereits einen Verzeichnisbaum haben, der noch nicht von RCS verwaltet wird, dann können Sie diesen einfach mit folgendem Befehl in das Repository importieren:

tigger$ cvs import verzeichnis hersteller marke

Dabei ist verzeichnis der Name des Toplevel-Verzeichnisses des Projekts, hersteller der Name des Autors dieses Codes (Sie können hier einen beliebigen Namen verwenden) und marke eine sogenannte Release-Marke (Tag), die ebenfalls beliebig gewählt werden kann. Ein Beispiel:

tigger$ cvs import dataimport acmeinc initial ... eine Menge Ausgaben ...

Wenn Sie ein völlig neues Projekt anfangen wollen, können Sie einfach den Verzeichnisbaum mit mkdir-Aufrufen aufbauen und dann diesen leeren Baum wie eben gezeigt importieren.

Wollen Sie dagegen ein Projekt importieren, das bereits von RCS verwaltet wird, wird es etwas komplizierter, weil Sie cvs import nicht verwenden können. In diesem Fall müssen Sie die benötigten Verzeichnisse direkt im Repository anlegen und dann alle RCS-Dateien (also die Dateien, deren Namen auf ,v enden) in diese Verzeichnisse kopieren. Verwenden Sie hier keine RCS-Unterverzeichnisse!

Jedes Repository enthält eine Datei namens CVSROOT/modules, die die Namen der Projekte im Repository enthält. Es lohnt sich, diese Datei mitzupflegen, wenn Sie ein neues Modul hinzufügen. Sie können diese Datei auschecken, editieren und einchecken wie jede andere Datei. Um also Ihr Modul zur Liste hinzuzufügen, verwenden Sie folgende Befehlsfolge (wir werden die einzelnen Befehle in Kürze besprechen):

tigger$ cvs checkout CVSROOT/modules tigger$ cd CVSROOT tigger$ emacs modules ... oder jeder andere Editor Ihrer Wahl; wir besprechen noch, was hier einzugeben ist... tigger$ cvs commit modules tigger$ cd.. tigger$ cvs release -d CVSROOT

Wenn Sie nichts Besonderes machen wollen, dann ist das Format der Datei modules sehr einfach: Jede Zeile beginnt mit dem Namen des Moduls, gefolgt von einem Leer- oder Tabulatorzeichen und dem Pfad im Repository. Sie können noch viele andere Dinge mit dieser Datei machen. Näheres finden Sie in der CVS-Dokumentation, insbesondere in den Info-Seiten oder unter http://www.loria.fr/~molli/cvs-index.html .

Mit CVS arbeiten

In diesem Abschnitt gehen wir davon aus, daß entweder Sie oder Ihr Systemverwalter ein Modul namens dataimport eingerichtet haben. Sie können jetzt einen lokalen Baum dieses Moduls mit folgendem Befehl auschecken:

tigger$ cvs checkout dataimport

Wenn für das Projekt, mit dem Sie arbeiten wollen, kein Modul definiert ist, dann müssen Sie den Pfad im Repository kennen. Beispielsweise könnte folgender Befehl funktionieren:

tigger$ cvs checkout kunden/acmeinc/dataimport

Aber egal, welche Version des checkout-Befehls Sie verwenden, CVS wird ein Verzeichnis namens dataimport in Ihrem aktuellen Arbeitsverzeichnis erzeugen und alle Dateien und Verzeichnisse, die zu diesem Modul gehören, dorthinein auschekken. Alle Dateien sind schreibbar, Sie können also unmittelbar mit dem Editieren loslegen.

Nachdem Sie einige Änderungen gemacht haben, können Sie die geänderten Dateien mit einem einzigen Befehl in das Repository zurückschreiben:

tigger$ cvs commit

Natürlich können Sie auch einzelne Dateien einchecken:

tigger$ cvs commit importrtf.c

CVS fragt Sie in jedem Fall - genau wie RCS - nach einem Kommentar, der zusammen mit Ihren Änderungen abgespeichert werden soll. Aber CVS geht noch einen Schritt weiter als RCS, was Ihre Bequemlichkeit angeht. Anstelle der rudimentären Eingabeaufforderung von RCS öffnet CVS einen Bildschirmeditor. Sie können diesen Editor durch Angeben in der Umgebungsvariablen CVSEDITOR festlegen; wenn diese Umgebungsvariable nicht gesetzt ist, schaut CVS in EDITOR nach; ist auch diese nicht gesetzt, dann wird vi verwendet. Wenn Sie ein ganzes Projekt einchekken, verwendet CVS den eingegebenen Kommentar für jedes Verzeichnis, in dem Sie Änderungen vorgenommen haben, startet aber jedesmal einen neuen Editor, damit Sie gegebenenfalls Änderungen vornehmen können.

Wie bereits erwähnt, ist es nicht notwendig, CVSROOT korrekt zu setzen, wenn Sie Dateien einchecken wollen, weil CVS beim Auschecken des Baums ein Verzeichnis namens CVS in jedem Arbeitsverzeichnis angelegt hat. Dieses enthält alle Informationen, die CVS für seine Arbeit braucht, darunter auch, wo das Repository steht.

Während Sie an Ihren Dateien gearbeitet haben, kann es ja durchaus sein, daß ein Kollege inzwischen einige der Dateien eingecheckt hat, an denen Sie selbst auch gerade arbeiten. In diesem Fall läßt CVS Sie Ihre Dateien nicht einchecken, sondern fordert Sie auf, zunächst Ihren lokalen Baum zu aktualisieren. Dies geschieht mit folgendem Befehl:

tigger$ cvs update M importrtf.c A exportrtf.c ? importrtf U importword.c

(Sie können auch hier nur eine einzelne Datei angeben.) Sie sollten sich die Ausgabe dieses Befehls sorgfältig anschauen: CVS gibt die Namen aller Dateien aus, die es untersucht, und stellt jedem Namen ein Zeichen voran. Dieses Zeichen teilt Ihnen mit, was während der Aktualisierung passiert ist. Die wichtigsten Zeichen sind in Tabelle 14-1 aufgeführt.
Zeichen Erklärung
P Diese Datei ist aktualisiert worden. P wird verwendet, wenn die Datei in der Zwischenzeit zum Repository hinzugefügt oder verändert worden ist, aber Sie selbst an dieser Datei keine Änderungen vorgenommen haben.
U Sie haben diese Datei in der Zwischenzeit geändert, aber niemand sonst.
M Sie haben diese Datei in der Zwischenzeit geändert, und jemand anders hat eine neue Version eingecheckt. Alle Änderungen sind erfolgreich zusammengeführt worden.
C Sie haben diese Datei in der Zwischenzeit geändert, und jemand anders hat eine neue Version eingecheckt. Während des Zusammenführungsversuchs sind Konflikte aufgetreten.
? CVS weiß nichts über diese Datei, sie steht also nicht unter CVS-Kontrolle.

Das C ist das wichtigste dieser Zeichen. CVS konnte nicht alle Änderungen zusammenführen und benötigt Ihre Hilfe. Laden Sie diese Dateien in Ihren Editor, und suchen Sie nach dem String <<<<<<<. Nach diesem String steht noch einmal der Name der Datei, gefolgt von Ihrer Versionsnummer, die durch eine Zeile mit ======= abgeschlossen wird. Anschließend kommt die Version aus dem Repository, die wiederum durch eine Zeile mit >>>>>>> abgeschlossen wird. Sie müssen jetzt - eventuell in Absprache mit Ihrem Kollegen - herausfinden, welche Version besser ist oder ob es möglich ist, die beiden Versionen von Hand zusammenzuführen. Ändern Sie die Datei entsprechend, und entfernen Sie die CVS-Markierungen <<<<<<<, ======= und >>>>>>>. Speichern Sie die Datei, und versuchen Sie den commit-Befehl noch einmal.

Wenn Sie sich entschließen, daß Sie an einem Projekt einige Zeit nicht mehr weiterarbeiten wollen, sollten Sie überprüfen, ob Sie wirklich alle Änderungen übertragen haben. Gehen Sie dazu in das Verzeichnis oberhalb des Wurzelverzeichnisses Ihres Projekts, und geben Sie folgenden Befehl ein:

tigger$ cvs release dataimport

CVS überprüft dann, ob Sie alle Änderungen in das Repository übertragen haben, und warnt Sie, wenn nötig. Eine nützliche Option dieses Befehls ist -d, die den lokalen Baum löscht, wenn alle Änderungen übertragen worden sind.

CVS über das Internet

CVS ist auch dann sehr nützlich, wenn verteilte Entwicklerteams im Spiel sind, Fußnoten 4 weil es mehrere Möglichkeiten bereitstellt, um auf ein Repository auf einem entfernten Rechner zuzugreifen.

Wenn Sie sich in den Rechner, auf dem sich das Repository befindet, mit rsh einloggen können, dann können Sie auch mit CVS auf das Repository zugreifen. Um ein Modul auszuchekken, verwenden Sie folgenden Befehl:

cvs -d :ext:benutzer@domaene.com:/pfad/zum/repository checkout dataimport

Wenn Sie rsh aus Sicherheitsgründen nicht verwenden können oder wollen, können Sie auch die verschlüsselte Variante ssh benutzen. Sie teilen CVS mit, daß Sie ssh benutzen wollen, indem Sie die Umgebungsvariable CVS_RSH auf ssh setzen.

Autheizierung und Zugriff auf das Repository kann auch über ein Client/Server-Protokoll geschehen. Dazu muß auf dem entfernten Rechner ein CVS-Server laufen. In der CVS-Dokumentation können Sie nachlesen, wie ein solcher eingerichtet wird. Wenn der Server läuft, können Sie sich folgendermaßen einloggen:

cvs -d :pserver:benutzer@domaene.com:/pfad/zum/repository login CVS password:

Wie Sie sehen, fragt Sie der CVS-Server nach Ihrem CVS-Paßwort, das Ihnen vom Administrator des CVS-Servers zugewiesen worden ist. Diese Login-Prozedur ist nur einmal pro Repository notwendig; wie bei lokalen Repositories wird diese Information in Ihrem lokalen Baum gespeichert. Weil das Paßwort nur gering verschlüsselt in der Datei .cvspass in Ihrem Heimatverzeichnis gespeichert wird, liegt hier ein potentielles Sicherheitsrisiko vor. Die CVS-Dokumentation sagt Ihnen mehr.

Wenn Sie CVS über das Internet verwenden und große Module auschecken oder aktualisieren, dann kann es auch sinnvoll sein, die Option -z zu verwenden, die einen zusätzlichen numerischen Parameter erwartet und die Daten in komprimierter Form überträgt.

Dateien patchen

Nehmen wir an, daß Sie versuchen, ein Programm zu pflegen, das regelmäßig aktualisiert wird. Das Programm besteht aber aus vielen Quelldateien, und es ist einfach nicht machbar, mit jedem Update die kompletten Quelldateien herauszugeben. Die beste Methode, Quellen nach und nach zu aktualisieren, ist patch, ein Programm von Larry Wall, dem Autor von Perl.

patch ist ein Programm, mit dem Sie kontextabhängige Änderungen in einer Datei vornehmen können, um ein Update auf die nächste Version durchzuführen. Sie geben also bei Änderungen im Programm einfach eine Patch-Datei (patch = flicken) zum Quellcode heraus, die der Benutzer dann mit patch einspielen kann, um die neueste Programmversion zu erhalten. Linus Torvalds gibt neue Versionen des Linux-Kernels normalerweise in Form von Patch-Dateien und vollständigen Quell-Distributionen heraus.

patch ist netterweise in der Lage, Updates im richtigen Kontext durchzuführen. Wenn Sie also den Quellcode selbst geändert haben, aber trotzdem noch die Änderungen aus der Patch-Datei einspielen möchten, kann patch in der Regel herausfinden, an welcher Stelle der Originaldatei die Änderungen eingespielt werden müssen. Deshalb muß Ihre Version der Originalquellen nicht genau mit der Version übereinstimmen, für die die Patch-Datei erstellt wurde.

Mit dem Programm diff, das »Kontext-Diffs« (eine kontextsensitive Liste der Unterschiede) zwischen zwei Dateien erzeugen kann, werden die Patch-Dateien erstellt. Wir benutzen wieder den hinreichend bekannten »Hello, World!«-Quelltext als Beispiel:

/* hello.c version 1.0 by Norbert Ebersol */ #include <stdio.h> int main() { printf("Hello, World!"); exit(0); }

Nehmen wir an, Sie wollen diesen Quelltext folgendermaßen ändern:

/* hello.c version 2.0 */ /* (c)1994 Norbert Ebersol */ #include <stdio.h> int main() { printf("Hello, Mother Earth!\n"); return 0; }

Wenn Sie eine Patch-Datei erzeugen möchten, die die Originaldatei hello.c auf den aktuellen Stand bringt, geben Sie diff mit der Option -c ein:

papaya$ diff -c hello.c.old hello.c > hello.patch

Damit erzeugen Sie die Patch-Datei hello.patch, in der beschrieben wird, wie die Originaldatei hello.c (in diesem Fall unter hello.c.old abgelegt) in die neue Version konvertiert wird. Sie können diese Patch-Datei an alle Interessenten verteilen, die die Originalversion von »Hello, World!« haben; diese können dann mit patch das Update durchführen.

Die Benutzung von patch ist recht einfach; in den meisten Fällen rufen Sie es mit der Patch-Datei als Eingabedatei auf: Fußnoten 5

papaya$ patch < hello.patch Hmm... Looks like a new-style context diff to me... The text leading up to this was: -------------------------- |*** hello.c.old Sun Feb 6 15:30:52 1994 |--- hello.c Sun Feb 6 15:32:21 1994 -------------------------- Patching file hello.c using Plan A... Hunk #1 succeeded at 1. done papaya$

patch gibt eine Warnung aus, wenn der Patch anscheinend schon einmal eingespielt wurde. Wenn wir versuchen, diese Patch-Datei noch einmal anzuwenden, wird patch fragen, ob es so tun soll, als ob der Schalter -R gesetzt sei - mit diesem Schalter wird ein Patch rückgängig gemacht. Auf diese Weise lassen sich Patches wieder entfernen, die versehentlich eingespielt wurden. patch hebt die Originalversionen aller aktualisierten Dateien in einer Backup-Datei auf, die in der Regel dateiname~ genannt wird (der Dateiname mit angehängter Tilde).

Oft werden Sie nicht nur eine einzelne Quelltextdatei patchen wollen, sondern einen ganzen Verzeichnisbaum mit Quellen. Bei patch können Sie mit einem einzigen diff mehrere Dateien aktualisieren. Wir gehen von den beiden Verzeichnisbäumen hello.old und hello aus, in denen der Quellcode der alten bzw. neuen Version des Programms steht. Benutzen Sie diff mit dem Schalter -r, um eine Patch-Datei für den ganzen Verzeichnisbaum zu erzeugen:

papaya$ diff -cr hello.old hello > hello.patch

Wir begeben uns jetzt auf das System, auf dem die Software aktualisiert werden soll. Wenn wir davon ausgehen, daß die Originalquelltexte im Verzeichnis hello stehen, können Sie das Update folgendermaßen einspielen:

papaya$ patch -p0 < hello.patch

Icon

patch(1)

Der Schalter -p0 weist patch an, die Pfadnamen der Dateien beizubehalten, die aktualisiert werden sollen (patch weiß also, daß es im Verzeichnis hello nach dem Quelltext suchen muß). Wenn die Quelltextdatei, die aktualisiert werden soll, in einem anderen Verzeichnis steht als dem, das in der Patch-Datei angegeben wurde, müssen Sie eventuell den Schalter -p benutzen. In der Manpage zu patch finden Sie die Details hierzu.

Code einrücken

Falls das Einrücken von Codezeilen nicht Ihre Stärke ist und Sie sich auch nicht mit einem Editor anfreunden können, der den Code während der Eingabe einrückt, sollten Sie das Programm indent benutzen, um nach dem Eingeben des Programmcodes denselben wohlformatiert auszudrucken. indent ist ein ziemlich schlauer C-Code-Formatierer, dem Sie mit Hilfe einiger Optionen mitteilen, wie Sie Ihren Quelltext eingerückt haben möchten.

Wir beginnen mit diesem fürchterlich formatierten Quelltext:

double fact (double n) { if (n==1) return 1; else return (n*fact(n-1)); } int main () { printf("Factorial 5 is %f.\n",fact(5)); printf("Factorial 10 is %f.\n",fact(10)); exit (0); }

Wenn wir indent auf diese Quelle ansetzen, erhalten wir das deutlich schönere:

#include <math.h> double fact (double n) { if (n == 1) return 1; else return (n * fact (n - 1)); } void main () { printf ("Factorial 5 is %f.\n", fact (5)); printf ("Factorial 10 is %f.\n", fact (10)); exit (0); }

Hier sind nicht nur die Zeilen übersichtlich eingerückt, sondern es sind außerdem Leerstellen um die Operatoren und Funktionsparameter herum eingefügt worden, um die Lesbarkeit zu erhöhen. Sie können auf vielerlei Weise bestimmen, wie die Ausgabe von indent aussehen soll - für den Fall, daß Ihnen diese Art der Einrükkung nicht gefällt, bietet indent noch andere Möglichkeiten.

indent ist außerdem in der Lage, aus einer Quelldatei troff-Code zu erzeugen, den Sie dann ausdrucken oder in ein technisches Dokument einfügen können. Dieser Code wird solche Textauszeichnungen wie kursive Kommentare, fett gesetzte Schlüsselwörter usw. enthalten. Ein Befehl wie

papaya$ indent -troff importrtf.c | groff -mindent

erzeugt troff-Code und formatiert diesen mit groff.

 Fußnoten 1
Das ist in den Programmen des Autors jederzeit möglich!
 Fußnoten 2
Für Debian-Benutzer kann auch das Paket ltrace nützlich sein. Es handelt sich dabei um eine Protokollfunktion für Bibliotheksaufrufe, die nicht nur Aufrufe von Kernel-Funktionen, sondern von allen Bibliotheksfunktionen protokolliert. Benutzer anderer Distributionen können die neueste Version des Quellcodes von ftp://ftp.debian.org/debian/dists/unstable/main/source/utils/ herunterladen.
 Fußnoten 3
Wir haben die Ausgabe editiert, um überflüssige Informationen zu entfernen und das Beispiel überschaubarer zu machen.
 Fußnoten 4
Die Verwendung von CVS ist mit der Anzahl der freien Softwareprojekte gestiegen, die über das Internet von Menschen aus verschiedenen Kontinenten entwickelt werden.
 Fußnoten 5
Die hier gezeigte Ausgabe stammt aus der letzten von Larry Wall veröffentlichten Version, Version 2.1. Wenn Sie eine neuere Version von patch haben, dann bekommen Sie die gleiche Ausgabe mit dem Schalter --verbose.


vorheriges Kapitel Inhaltsverzeichnis Stichwortverzeichnis nächstes Kapitel


Weitere Informationen zum Linux - Wegweiser zur Installation & Konfiguration

Weitere Online-Bücher & Probekapitel finden Sie in unserem Online Book Center


O'Reilly Home | O'Reilly-Partnerbuchhandlungen | Bestellinformationen | Kontaktieren Sie uns
International | Über O'Reilly | Tochterfirmen

© 2000, O'Reilly Verlag