Nach dem umfangreichen Ausflug ins Betriebssystem in der letzten Folge, sollen nun einige von uns benötigte Hilfsfunktionen programmiert werden. Damit sollen Detailprobleme bei der nachfolgenden Behandlung der Kommandos möglichst ausgeklammert werden. Im Zentrum dieses Serienteils befindet sich Listing 1.3 - die Implementierung der Hilfsfunktionen.
Listing 1.3 enthält dabei Funktionen aus den drei folgenden Bereichen:
Verwaltung der Systemuhr: getdate putdate gettime puttime
Dateiverwaltung: fgetdattime fputdattime ftouch fexist fisdir confirm acp arm amv apwd acd
Stringbehandlung: pathsplit convupper ctop ptoc onlyalpha
Die Funktionen zur Verwaltung der Systemuhr stützen sich weitgehend auf die vier GEMDOS-Funktionen zum Setzen und Auslesen der Uhr ab. Die diesbezüglichen Funktionen innerhalb von Listing 1.3 besitzen mithin lediglich die Aufgabe, dem Anwender der Systemuhr die Kodierung von Systemdatum und Zeit im GEMDOS-Zeitformat abzunehmen. Zwischen den GEMDOS-Funktionen und den Funktionen aus Listing 1.3 besteht dabei folgender funktionaler Zusammenhang:
List.1.3 GEMDOS Funktion
getdate (Zeilen 40-49) Tgetdate Abfrage des Systemdatums
putdate (Zeilen 51-61) Tsetdate Setzen des Systemdatums
gettime (Zeilen 63-72) Tgettime Abfrage der Systemzeit
puttime (Zeilen 74-84) Tsettime Setzen der Systemzeit
Die Parameter von getdate, putdate, gettime und puttime sind entsprechend der Aufgabenstellung allesamt vom Typ short bzw. vom Typ Zeiger auf short.
Etwas umfangreicher ist die Aufgabenstellung der insgesamt elf Funktionen des zweiten Bereichs von Listing 1.3. Hier sind sämtliche Funktionen zusammengefaßt, die den Umgang mit Dateien erleichtern. Obwohl GEMDOS bereits eine ganze Reihe von Funktionen zum Umgang mit Dateien bereit stellt, fehlen doch noch relativ häufig benötigte Funktionen, wie etwa das Kopieren oder das Bewegen einzelner Dateien. Darüberhinaus besteht auch im Bereich der Dateiverwaltung die Notwendigkeit einzelne GEMDOS-Funktionen mit einer aufgesetzten Funktionsschale zu versehen, wenn man komfortabel mit ihnen arbeiten will. Auch hier möchte ich wieder mit drei Funktionen beginnen, die im Zusammenhang mit dem GEMDOS-Zeitformat stehen: fgetdattime (Zeilen 114-138), fputdattime (Zeilen 140-164) und ftouch (Zeilen 166-179). Mit diesen Funktionen soll der Zeitpunkt der letzten Modifikation von Dateien bearbeitet werden. Der Zeitpunkt der letzten Modifikation, ist die Zeit, die bei der Ausgabe von Verzeichnissen im Desk-Top angegeben wird, wenn die Verzeichnisausgabe in Form von "Text" geschieht. Auch hier verwendet GEMDOS das in der letzten Folge beschriebene Zeitformat. Allerdings existiert lediglich eine Funktion zum Setzen und Auslesen dieses Zeitwertes: Fdatime. Die Funktionen fgetdattime und fputdattime sind entsprechend beide auf Fdatime abzubilden. Dabei sind die gleichen Kodierungen bzw. Dekodierungen, wie für das Setzen der Systemuhr vorzunehmen. Zusätzlich gilt es die gewünschte Datei zunächst zu eröffnen, da Fdatime nicht direkt mit dem Dateinamen arbeitet, sondern stattdessen eine Kanalkennung benötigt. ftouch ist eine Variante von fputdattime. ftouch setzt das Datum der letzten Modifikation gerade auf die aktuelle Systemzeit. Damit ist es etwa möglich, nach Bearbeitung einer Datei, das Modifikationsdatum zu aktualisieren. Zwei weitere Funktionen werden benötigt, um Dateien darauf zu überprüfen, ob es sich um "normale" Dateien (fexist, Zeilen 196-206) oder um Verzeichnisse (fisdir, Zeilen 208-219) handelt. Das Existenzkriterium für eine normale Datei besteht dabei darin, daß sich die Datei mit Fopen zum lesenden Zugriff öffnen läßt. Das Kriterium für ein Verzeichnis besteht darin, daß sich das aktuelle Verzeichnis mit acd (siehe unten) auf den gewünschten Pfad setzen läßt.
Die nächste Funktion im Listing, confirm (Zeilen 239-245), ist eine simple Hilfsfunktion. Sie führt einen kurzen Benutzerdialog der Form:
printf(text, name);
Erwartet wird entweder eine Bestätigung ('y') oder eine andere Eingabe. Das Ergebnis von confirm gibt dabei an, ob die Bestätigung positiv oder negativ ausgefallen ist. confirm wird von den drei nachfolgend beschriebenen Funktionen genutzt.
Eine etwas umfangreichere Funktion ist acp. Mit acp wird eine Datei mit dem Pfad src auf eine Datei mit dem Pfad dest kopiert. Existiert dest bereits, dann wird es dabei überschrieben. Um dies zu vermeiden kann acp mit dem Wert TRUE für das Flag interactive aufgerufen werden. In diesem Fall führt acp eine Rückfrage durch, ob dest auch tatsächlich überschrieben werden soll. Weiterhin ist zu berücksichtigen, daß acp eine Datei nicht auf sich selbst kopieren darf (src = dest). In der Implementierung von acp (Listing 1.3, Zeilen 270-318) werden eine ganze Reihe von Betriebssystemaufrufen benutzt. Zunächst sind hier die beiden Aufrufe zur dynamischen Speicherverwaltng, Malloc und Mfree, zu nennen. Mit ihnen wird ein Schreib-/Lesepuffer zur Aufnahme der Kopierinformationen verwaltet. Die Verwendung des dynamischen Speichers erscheint dabei sinnvoll, da der benötigte Puffer nicht gerade klein ist. Die Konstante BSIZE gibt für ihn eine voreingestellte Größe von 32 kByte an. Der Kopiervorgang vollzieht sich in vier Phasen:
Die beiden Pfade werden auf Gleichheit überprüft und es wird im gegebenen Fall (interactive-Flag ist gesetzt und die Kopierung erfolgt auf eine existierende Datei) ein Benutzerdialog durchgeführt.
Der Schreib-/Lesepuffer wird eingerichtet und die Ein- und Ausgabekanäle werden geöffnet.
Der eigentliche Kern der Kopierroutine ist recht klein. Durch wiederholten, aufeinanderfolgenden Aufruf der beiden Betriebssystemaufrufe Fread und Fwrite wird die Quelldatei stückweise in die Zieldatei kopiert. Die Maximalgröße der Stücke ist dabei bereits durch die Puffergröße (BSIZE) vorgegeben.
Der Schreib-/Lesepuffer wird freigegeben und die Datenkanäle werden geschlossen.
Auf diese etwas umfangreichere Funktion folgen nun wieder einige einfachere: Zunächst ist dies die Funktion arm (Zeilen 319-334). Sie realisiert das Löschen einer Datei. Unterschieden wird dabei zwischen dem Löschen von "normalen" Dateien und dem Löschen von schreibgeschützten Dateien. Bei den letztgenannten Dateien wird zusätzlich eine Sicherheitsabfrage durchgeführt, da hier im gegebenen Fall zunächst der Schreibschutz entfernt werden muß (Fattrib), weil sonst das Löschen (unlink) nicht möglich ist. Außerdem besteht wie bereits bei acp die Möglichkeit Interaktion vorzuschreiben (interactive-Flag).
Die nächste Funktion - amv (Zeilen 336-344) - besitzt zwar eine ähnlich hohe Komplexität wie acp: amv realisiert das Bewegen einer Datei auf einen anderen Pfad. Allerdings ist die Implementierung weitaus einfacher, da amv sich leicht durch Hintereinanderausführung von acp und arm durchführen läßt.
Zwei weitere Funktionen - apwd (Zeilen 362-373) und acd (Zeilen 375-384) - sind wieder Abstraktionen von Funktionen auf Betriebssystemebene. Sie abstrahieren Dgetpath und Dsetpath, die das Lesen und Setzen des aktuellen Verzeichnisses ermöglichen. Die Funktionalität dieser beiden GEMDOS-Funktionen wird durch apwd und acd dahingehend erweitert, daß auch Pfade mit Gerätekennung beim Lesen und Setzen des Arbeitsverzeichnisses verwendet werden dürfen. Zur Anwendung kommen zwei weitere Betriebssystemaufrufe: Dgetdrv und Dsetdrv.
Der dritte Bereich des Listings 1.3, die Funktionen zur Stringbearbeitung, beginnt mit der Funktion pathsplit (Zeilen 401-447). pathsplit zerlegt den Pfad path in seinen Verzeichnisanteil (directory) und seinen Dateianteil (filename). Da dabei auch einige Grenzfälle berücksichtigt werden müssen, ergibt sich eine etwas umfangreichere aber nicht besonders schwierige Funktion. Zur Anwendung gelangen einige der Stringfunktionen aus der C-Bibliothek. Entsprechend macht die Funktion pathsplit den Header "STRING.H" erforderlich. Die Details sollen an dieser Stelle jedoch nicht diskutiert werden, da sie nicht weiter interessieren. Auch die weiteren Funktionen des Bereichs Stringbearbeitung sind alle nicht besonders schwierig, sie sollen nur kurz in ihrer Funktionalität dargestellt werden:
Die Funktion convupper (Zeilen 461-468) konvertiert alle Kleinbuchstaben des Strings str in Großbuchstaben.
Die Funktion ctop (Zeilen 487-495) konvertiert den nullterminierten C-String cstring in den Pascal-String pstring. Die Funktion ptoc (Zeilen 496-503) erledigt die umgekehrte Arbeit.
Anmerkung: Im Gegensatz zu den nur durch die Nullterminierung begrenzten C-Strings, kann bei Pascal-Strings die Nullterminierung fehlen. Dafür besitzen sie anstelle des ersten Zeichens eine Stringlängenangabe. Ein Nachteil dieses Datenformats besteht darin, daß Pascal-Strings maximal 256 Zeichen lang sein können. Dies ist gerade der maximale Wertebereich eines Zeichens. Pascal-Strings werden bei einigen Betriebssystemaufrufen benötigt, so z.B. bei Pexec.
Die letzte Funktion des Listings 1.3, onlyalpha (Zeilen 519-527), überprüft die Zeichenkette str darauf, ob sie nur aus Buchstaben besteht.
Wie eingangs erwähnt, sollen die Funktionen des Listings 1.3 im weiteren Ablauf der Programmierung als Hilfsfunktionen zur Verfügung stehen. Entsprechend ist die Datei "ATOM.C" als Modul zu kompilieren und es ist eine Header-Datei zu formulieren, die die Funktionsdeklarationen aus "ATOM.C" aufnimmt. Sinnvollerweise nennt man diesen Header dann auch "ATOM.H". "ATOM.H" finden Sie als Listing 1.4 abgedruckt.
Apropos Kompilierung von Moduln. Ich denke nach der Implementierung des ersten Moduls ist es an der Zeit das Makefile für den kompletten ersten Block der Toolbox abzudrucken. Es dürfte sich irgendwo hinter den heutigen Listings befinden. Das Makefile ist dabei auf die Belange des Laser C Systems zugeschnitten. Die entsprechenden Kommandonamen und Parameter für Compiler und Linker sind jedoch als Makros definiert, so daß es kein Problem darstellen dürfte, das Makefile durch Umdefinition der Makros auf andere C-Systeme anzupassen. Voraussetzung für eine Anpassung ist natürlich ein C-System mit MAKE-Utility.
Das war's dann auch mal wieder für heute. In der nächsten Folge der "Programmer's Toolbox" gelangen wir (endlich) an den Punkt, wo erste Kommandos programmiert werden. Es handelt sich dabei um:
CD - Ändern des Arbeitsverzeichnisses
PWD - Ausgabe des Arbeitsverzeichnisses
DATE - Auslesen/Setzen des Systemdatums
TIME - Auslesen/Setzen der Systemzeit
DF - Auslastungsstatistik für Dateisysteme
Listings zur Programmer's Toolbox (3)