← ST-Computer 06 / 1988

Auf der Schwelle zum Licht: Die obere Ebene der Dateiverwaltung

Grundlagen

Im MĂ€rz-Heft hatten wir uns schon um die Dateiverwaltung des GEMDOS gekĂŒmmert. Damals ging es darum, wie Daten von bzw. zu Massenspeichern wie Floppy, Harddisk und RAM-Disk ĂŒbertragen werden. Darauf baut nun die obere Ebene der Dateiverwaltung auf. Hier sind die Dateioperationen vom Erzeugen bis zum Löschen zu finden.

Der “File Descriptor”

FĂŒr jede geöffnete Datei legt GEM-DOS eine Struktur an, die ich “File Descriptor” (FD) genannt habe (Abb. 1). Die ErklĂ€rungen beziehen sich zunĂ€chst auf Anwenderdateien. Di-rectories und FATs haben auch ihre FDs, die sich allerdings in einigen Einzelheiten unterscheiden, was im Anschluß erlĂ€utert wird.

Die FDs enthalten zum einen Angaben ĂŒber die Datei, die dem Directory entnommen werden. Dazu zĂ€hlen der “timestamp”, bestehend aus Erstellungszeit (‘fd_time’) und Erstellungsdatum (‘fd_date’), der Start-Cluster (‘fd_stcl’) und die DateilĂ€nge in Bytes (‘fd_len’). Interessanterweise fehlt hier das Dateiattribut, was von GEMDOS sowieso recht stiefmĂŒtterlich behandelt wird.

Im Directory liegen diese Werte im Intel-Format vor, bei dem die Reihenfolge von nieder- und höherwertigen Bytes gegenĂŒber dem Motorola-Format vertauscht ist. Dadurch ist das Directory PC-DOS-kompatibel. ‘fd_stcl’ und ‘fd_len’ liegen im FD im Motorola-Format, ‘fd_time’ und ‘fd_date’ dagegen im Intel-Format vor.

‘fd_dirch’ enthĂ€lt zwei Flags mit sehr unterschiedlichen Aufgaben.

Bit 0 wird gesetzt, wenn ‘fd_stcl’ oder lfd_len' im FD geĂ€ndert wurden (bei dem timestamp funktioniert dies nicht, s.u. bei ‘Fdatime’). Beim Schließen der Datei werden dann die Änderungen im FD ins Directory ĂŒbertragen.

Bit 1 wird von der Directory-Verwaltung benutzt, worauf wir in der nĂ€chsten Folge noch zurĂŒckkommen. Es wird gesetzt, wenn ein Directory einmal ganz gelesen wurde, d.h., wenn bei einer Suchoperation im Directory die gesuchte Datei einmal nicht gefunden wurde.

Beide Bits werden nie zurĂŒckgesetzt (außer natĂŒrlich beim Anlegen des FD), was zwar unsauber ist, aber funktioniert (s. Beschreibung von ‘Fclose’).

Die anderen Bits werden nicht benutzt.

‘fd_dmd’ zeigt auf den Drive Media Descriptor (DMD) des Laufwerks, auf dem sich die Datei befindet. Der DMD wurde in der letzten Folge erklĂ€rt.

Directories werden nicht nur mit den FDs verwaltet, sondern benötigen noch von mir ‘Directory Descripto-ren’ “getaufte” Strukturen. Sie stellen die Directory-Hierarchie dar und werden ebenfalls nĂ€chsten Monat abgehandelt. ‘fd_dirdd’ zeigt auf den Directory Descriptor des Directories, das zur Datei gehört.

typedef struct { FD *fd_link; /* Zeiger auf nĂ€chsten Daten-FD des gleichen Directories */ int fd_dirch; /* Bit 0=1: Directory-Änderung erfolgt */ /* Bit 1=1: Directory einmal ganz durchsucht */ unsigned int fd_time; /* Erstellungs-Zeit */ unsigned int fd_date; /* Erstellungs-Datum */ int fd_stcl: /* Start-Cluster */ long fd_len; /* DateilĂ€nge in Bytes */ DMD *fd_dmd; /* Zeiger auf zu Datei gehörenden DMD */ DD *fd_dirdd; /* Zeiger auf DD des zugehörigen Directories */ FD *fd_dirfd; /* Zeiger auf FD des zugehörigen Directories */ long fd_dirpos; /* Position des eigenen Eintrags im Directory */ long fd_fpos; /* akt. Zugriffs-Position in Datei */ int fd_ccl; /* Cluster zu 'fd_fpos' */ int fd_csec; /* Sektor zu 'fd_fpos' */ int fd_clpos; /* akt. Zugriffs-Position in Cluster */ int fd_unused; /* unbenutzt */ FD *fd_multi; /* Zeiger auf anderen FD der gleichen Datei */ int fd_mode; /* Zugriffsmodus beim Öffnen der Datei */ } FD;

Abb. 1: Der File Descriptor (FD)

'fcLdirfd' zeigt auf den FD des zugehörigen Directories. ‘fd_dirpos’ ist die Dateiposition des Eintrages der Datei im eigenen Directory. Alle Zeichen einer Datei sind von Null an aufwĂ€rts durchnumeriert. Null bezeichnet somit den ersten Eintrag eines Directories, 32 den zweiten usw. (Directory-EintrĂ€ge sind 32 Byte lang).

Als nĂ€chstes kommen einige Angaben ĂŒber die aktuelle Dateiposition in der Datei selbst. Bekanntlich operieren Dateizugriffe mit ‘Fread’ und ‘Fwrite’ immer ab der aktuellen Dateiposition, die mit ‘Fseek’ neu festgelegt werden kann. ‘fd_fpos’ ist nun direkt diese Dateiposition (Null = Dateianfang). ‘fd_ccl’ ist der Cluster, ‘fd_csec’ ist der Sektor (in GEM-DOS-ZĂ€hlweise), der zur Dateiposition gehört. ‘fd_clpos’ bezeichnet die Dateiposition relativ zum Beginn des Clusters.

Ein Beispiel: Auf einer Standard-Diskette (2 Sektoren/Cluster, 512 Byte/Sektor) belegt eine Datei die Cluster 2 und 3, also die Sektoren 4 bis 7 (GEMDOS-ZĂ€hlung!). Die Dateiposition (‘fd_fpos’) 1538 verweist auf das Zeichen Nr. 2 im letzten Sektor. ‘fd_ccl’ hat demnach den Wert 3, ‘fd_csec’ ist 7 und ‘fd_clpos’ autet 514.

Der beim Öffnen einer Datei angegebene Zugriffsmodus wird in fd_mode’ festgehalten. fd_unused’ wird nicht benutzt.

Mit ‘fd_link’ und ‘fd_multi’ sind FDs miteinander verkettet (s.u.).

Auch das Root Directory (RD) hat -einen eigenen FD, der beim erstmaligen Ansprechen eines Laufwerks automatisch eingerichtet wird. Die DateilĂ€nge ist dabei durch die Anzahl der RD-Sektoren festgelegt. Der erste Cluster ist negativ entsprechend der GEMDOS-Cluster-ZĂ€hlung (siehe letzte Folge: ‘rdst’-Wert). Datum und Zeit sind Null (nicht definiert). ‘fd_multi’ und ‘fd_link’ sind ebenfalls NIL. Da das Root Directory in keinem anderen Directory verankert ist, sind ‘fd_dirdd’, ‘fd_dirfd’ und ‘fd_dirpos’ stets NIL. Auch ‘fd_mode’ ist unbenutzt und daher immer Null.

Subdirectories haben keine LĂ€nge (in ihrem Parent Directory ist Null als “DateilĂ€nge” eingetragen). Da Subdirectories in ihrer GrĂ¶ĂŸe nicht begrenzt sind (außer durch die GesamtkapazitĂ€t des Mediums), wird ‘fd_len’ auf $7FFFFFFF gesetzt. Dies ist die grĂ¶ĂŸte positive 32-Bit-Zahl und damit die maximale von GEMDOS verwaltbare DateigrĂ¶ĂŸe. Dadurch wird erreicht, daß innerhalb von Subdirectories beliebig gelesen und geschrieben werden kann (bei einer zu kleinen LĂ€nge wĂŒrde ‘f_seek’ einen Fehler melden). Das eigentliche VerlĂ€ngern eines Subdirectories (durch AnfĂŒgen eines weiteren Clusters) klappt trotzdem, da dieser Mechanismus hier nicht von der DateilĂ€nge abhĂ€ngt.

Wie beim Root Directory sind ‘fd_link', ‘fd_multi’ und ‘fd_mode’ unbenutzt und Null.

Auch der FD der FAT wird beim erstmaligen Zugriff auf ein Laufwerk automatisch angelegt.

Bei ihm sind ĂŒberhaupt nur ‘fd_stcl’ (negative GEMDOS-Sektornummer), ‘fd_dmd’, ‘fd_fpos’, ‘fd_clpos’ und 'fd_len’ (FAT-LĂ€nge in Bytes, errechnet aus ‘fsiz’ des BPB) benutzt. Alle anderen Komponenten des FD werden verstĂ€ndlicherweise nicht verwendet und sind Null.

Die FD sind in ein großes Netz der verschiedensten Strukturen eingebunden, worauf wir erst nach Behandlung der Directory Descriptoren zurĂŒckkommen werden.

Die FDs werden dynamisch verwaltet. Mit der “internen Speicherverwaltung” wird fĂŒr sie Platz reserviert, wenn sie gebraucht werden. Beim Schließen der Dateien, Mediumwechsel, usw. wird der Speicherplatz wieder frei.

Dazu noch eine ErgĂ€nzung fĂŒr diejenigen unter Ihnen, die den Artikel ĂŒber die Speicherverwaltung kennen: Ein FD benötigt 50 Byte; aufgerundet ergibt dies vier 8-Wort-Einheiten (482 = 64). Daher finden sich freigegebene FDs in der ‘mifl’-Liste 4. In dieser Liste finden sich auch die oben erwĂ€hnten Directory Descriptoren. Die 4er-Liste wird somit am meisten beansprucht und sorgt am ehesten dafĂŒr, daß der GEMDOS-Speicher knapp wird. Daher wird diese Liste von den “Mehr-Ordner-Programmen” oder dem Atari Harddisk-Treiber erweitert (manchmal auch die Liste 3 fĂŒr die DMDs, obwohl dies nichts bringt), um den Exitus des GEMDOS hinauszuzögern.

Eine kleine Bemerkung am Rande: Wenn das nicht benutzte ‘fd_unused’ weggelassen wĂŒrde, brĂ€uchte der FD nur 48 Bytes und somit nur drei 8-Wort-Einheiten. Dies brĂ€chte 16 Bytes Ersparnis pro FD, was den sowieso knappen internen Speicher entlasten wĂŒrde...

Datei-Handles

Die Anwenderdateien, also die Dateien, die von Programmen aus angesprochen werden (im Gegensatz zu Directory- und FAT-Dateien), erhalten beim Eröffnen mit ‘Fcreate’/ ’Fopen’ ein ‘Handle’ zugewiesen. Diese Handles (zu deutsch “Griff’, also bleiben wir lieber bei Handle) sind aus der Sicht von GEMDOS nur fĂŒr die Kommunikation mit der “Außenwelt”, sprich den Anwender-Programmen, zustĂ€ndig. Bei der ersten sich bietenden Gelegenheit wird der sich hinter dem Handle verbergende FD ermittelt. Alle internen Dateifunktionen operieren auf FDs, so daß sie auch fĂŒr die Directory- und FAT-Dateien benutzt werden können.

FĂŒr Dateien werden die sogenannten “Non-standard Handles” vergeben. Dies sind Zahlen zwischen 6 und 80; somit kann GEMDOS theoretisch bis zu 75 (!) Dateien gleichzeitig geöffnet haben. In diversen Atari-Dokumentationen steht etwas von 40 Dateien (Handles bis 45), im Januar-Heft hatte ich noch 69 geschrieben (rechnen mĂŒĂŸte man können). Vielleicht hat Digital Research hier GEMDOS selbst nicht so ganz getraut, oder es liegt eine Verwechslung mit den “Pfad-Handles” (nĂ€chste Folge) vor, von denen es nĂ€mlich 40 (eigentlich nur 39) geben kann.

Daneben gibt es noch die “Standard Handles” (0 bis 5) und die “Device Handles” (-1 bis -3), mit denen wir uns in den Folgen ĂŒber die zeichenorientierten GerĂ€te und der I/O-Umleitung beschĂ€ftigen werden.

FĂŒr’s erste bleiben wir also bei den Non-standard Handles, genauer sogar bei den “Datei-Handles”. Das sind die Handles, die letztendlich (nach BerĂŒcksichtigung aller Möglichkeiten der I/O-Umleitung) eine Datei auf einem blockorientierten Massenspeicher bezeichnen.

Der “File Control Block”

FĂŒr jedes Non-standard Handle gibt es eine kleine Struktur, die ich “File Control Block” (FCB) genannt habe (Abb. 2).

typedef struct { FD *f_fd; /* Zeiger auf FD der Datei */ PD *f_pd; /* Zeiger auf Prozeßdescriptor des besitzenden Prozesses */ int f_cnt; /* Anzahl der vergebenen Handles */ } FCB;

Abb. 2: Der File Control Block (FCB)

‘f_fd’ ist ein Zeiger auf den zugehörigen FD (negative Werte werden fĂŒr Devices benutzt). NIL zeigt an, daß das Handle nicht belegt ist. Mit ‘f_pd’ wird auf den Prozeßdescriptor des Prozesses verwiesen, der die Datei eröffnet hat. Auch hier steht NIL, wenn das Handle nicht belegt ist. ‘f_cnt’ zĂ€hlt mit, an wieviele Aufrufer dieses Handle vergeben wurde. Ein Handle kann in Zusammenhang mit der I/O-Umleitung (kommt auch noch irgendwann einmal) mehr als einmal vergeben werden. Beim Schließen von Dateien soll hiermit bemerkt werden, wann die Datei von allen Handle Besitzern geschlossen wurde und FCB und FD somit freigegeben werden mĂŒssen.Die FCBs sind statisch in einer Tabelle (‘fcbx’) organisiert, so daß mit einem Handle direkt auf den FCB zugegriffen werden kann. Das heißt, ‘fcbx[handle-6]’ ist der FCB fĂŒr ‘handle’ (die Subtraktion von 6 erfolgt, da Non-standard Handles erst bei 6 anfangen).

‘fcbx’ ist die einzige globale GEM-DOS-Variable fĂŒr die Dateiverwaltung, die noch nicht in einer der letzten Folgen vorkam, trotzdem bekommt sie eine eigene Abbildung spendiert (Abb. 3). Die nicht legal benutzbaren Adressen beziehen sich wie ĂŒblich auf das “alte TOS” vom 6.2.1986 bzw. auf das "Blitter-TOS” vom 22.4.1987.

FCB fcbx[75]; /* $58f0/$8092: FCB-Tabelle fĂŒr alle Dateien */

Abb. 3: Globale GEMDOS-Variablen fĂŒr Dateiverwaltung

GEMDOS kontrolliert ĂŒbrigens nicht, ob die Handles ĂŒberhaupt im zulĂ€ssigen Bereich von -3 bis 80 liegen. Die Fehlermeldung EIHNDL besagt nur, daß unter einem gĂŒltigen Handle keine geöffnete Datei registriert ist. Bei anderen Handles greift GEMDOS munter auf den nach ‘fcbx’ liegenden Speicherbereich zu und interpretiert das dort gefundene “Irgendetwas” als FCB... womit die Chancen auf einen Bus oder Address Error nicht schlecht stehen.

Multitasking with GEMDOS? No!

Nicht nur in der Prozeßverwaltung des GEMDOS finden sich einige AnsĂ€tze zum Multitasking, sondern auch in der Dateiverwaltung sind einige Strukturen, die aber so unausgereift sind, daß sie wohl nur als Basis fĂŒr zukĂŒnftige Weiterentwicklungen (die es wohl aller Voraussicht nach nie geben wird) gedacht sein können. GEMDOS erlaubt das mehrfache Öffnen von (Anwender-)Dateien durch einen oder mehrere Prozesse. Außerdem gibt es noch die I/O-Umleitung, die hier nicht berĂŒcksichtigt wird. Bei jedem neuen Öffnen wird ein eigenes Handle mit FCB sowie ein eigener FD vergeben.

Alle FDs, deren Dateien im gleichen Directory liegen, sind in einer einfach verketteten Liste verknĂŒpft (Abb. 4). Die FDs werden durch ‘fd_link’ verbunden (Ende durch NIL markiert), wobei die zuletzt eröffnete Datei am Anfang der Liste zu finden ist. Ein Zeiger auf den Beginn der Liste steht im Directory Descriptor (‘dd_fdl’), bei dem ich Sie erneut auf das nĂ€chste Mal vertrösten muß.

Abb. 4: Verkettung der FDs untereinander

Es existiert noch eine zweite Art von Listen mit FDs, in der alle FDs, die zur gleichen Datei gehören, aufgefĂŒhrt sind (ebenfalls Abb. 4). Sie wird mit dem ‘fd_multi’-Zeiger realisiert .Ende wieder durch NIL markiert). Hier beginnt die Liste mit der zuerst eröffneten Datei, d.h. bei jedem Öffnen wird der FD hinten angehĂ€ngt. Der Beginn dieser Liste ist jedoch nirgendwo vermerkt, so daß ein Arbeiten mit dieser Liste sich recht schwierig gestalten könnte. Kein Wunder, daß GEMDOS sich nicht groß darum kĂŒmmert.

In dem abgebildeten Beispiel wurde nun zuerst eine Datei mit dem Handle 6. dann eine zweite in einem anderen Directory mit dem Handle 7 eröffnet. Eine weitere Datei dieses Directories bekam nun Handle 8 und wurde an den Anfang der FD-Liste gehĂ€ngt. Zum Schluß wurde die Datei mit Handle 7 ein zweites Mal eröffnet, so daß der zu Handle 9 gehörende FD nicht nur mit 'fd_link' sondern auch mit ‘fd_multi’ (von Datei mit Handle 7 aus) verkettet wurde.

Bei Dateioperationen könnte nun mit diesen Listen festgestellt werden, ob eine Datei von mehreren Prozessen oenutzt wird, um Zugriffsrechte zu regeln und Kollisionen zu vermeiden. Davon wird aber nur bei ‘Fdelete’ und Fopen’ - und das auch noch in mangelhafter Form - Gebrauch gemacht (s.u.). Auch Änderungen in den FDs (z.B. DateilĂ€nge) mĂŒĂŸten eventuell in die anderen FDs der selben Datei ĂŒbertragen werden, was aber ebenfalls nicht gemacht wird. Daher ist es nicht ratsam, ernsthaft mit mehrfach geöffneten Dateien zu arbeiten, außer vielleicht bei reinen Lesezugriffen. Die ‘Multi-Liste’ wird gar noch nicht einmal korrekt verwaltet. Beim Schließen von Dateien werden FDs nicht ausgehĂ€ngt, so daß sie im Prinzip nicht zu gebrauchen ist.

Im FCB ist zwar auch der besitzende Prozeß registriert, aber trotzdem erfolgen i.a. (außer bei ‘Fdelete’) keine Kontrollen. Dies bedeutet, daß man -Kenntnis der Handles vorausgesetzt - ohne weiteres Dateien fremder Prozesse bearbeiten kann.

GEMDOS-Funktionen (TRAP #1)

Es folgt die Beschreibung der GEMDOS-Funktionen, die diesmal den meisten Raum in Anspruch nimmt. Hier wird nur der direkte Zugriff auf Disk-Dateien behandelt. Die Dateifunktionen lassen sich auch fĂŒr die zeichenorientierten GerĂ€te (Bildschirm usw.) benutzen, worauf wir in einer spĂ€teren Folge zurĂŒckkommen.

Die Directory-Funktionen sowie ‘Frename’ werden ebenfalls auf ein anderes Mal verschoben.

Wenn bei der Beschreibung der Arbeitsweise vom Lesen oder Schreiben von FAT oder Directories die Rede ist, so geschieht dies meist mit den internen Versionen von ‘Fread’, ‘Fwrite’ und ‘Fseek’ (s. dort). Bei Directory-Operationen wird manchmal auch direkt auf den Sektorpuffer zugegriffen (siehe MĂ€rz-Folge).

Das Dateiattribut kann hier leider nicht mehr erklĂ€rt werden. Ein Bit dieses Attributs ist fĂŒr den ‘read only’-Status zustĂ€ndig, ‘readonly’ ist mit dem Desktop ĂŒber die “zeige-Info”-Funktion zugĂ€nglich und wird dort als ‘nur lesen’ bezeichnet. Dateien mit diesem Status sollten nicht verĂ€ndert oder gar gelöscht werden dĂŒrfen.

Funktion $3c Fcreate

int Fcreate(char *path, int attr)

Mit ‘Fcreate’ wird eine Datei ‘path’ neu angelegt. Wenn die Datei schon vorhanden war, wird sie vorher gelöscht. Die Datei hat hinterher die LĂ€nge Null.

‘attr’ ist das Dateiattribut, das die Datei beim Anlegen erhĂ€lt. Mit ‘Fcreate’ können keine Subdirectories erzeugt werden. Das entsprechende Attribut-Bit wird ignoriert. Im Gegensatz zu anderen Funktionen erfolgt dabei keine Fehlermeldung. Normalerweise wird die Datei zum Lesen und Schreiben geöffnet. Wenn das ‘read only’-Bit des Attributs gesetzt ist, wird die Datei nur zum Lesen geöffnet. Da sie nach einem ‘Fcreate’ noch leer ist, kann man nicht mehr sehr viel machen, so daß man sie am besten gleich wieder schließt. Dies ist ĂŒbrigens kein Bug, sondern mit voller Absicht so programmiert (“it’s a feature, not a bug”).

Die Datei lĂ€ĂŸt sich aber trotzdem beschreiben; der ‘read only’-Modus wird nĂ€mlich nicht immer richtig erkannt... (s. ‘Fwrite’).

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-34L (EPTHNF) Pfad nicht gefunden, unbekanntes Laufwerk, illegaler Dateiname, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-35L (ENHNDL) Kein Datei-Handle mehr frei
-36L (EACCDN) Datei existiert schon als Subdirectory oder ‘read only’-Datei, Medium oder Root Directory voll.
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
-3..80 Handle, unter dem die Datei angesprochen werden kann (>= 6 bei Disk-Dateien).

Arbeitsweise

Das Subdirectory-Bit im Dateiattribut wird gelöscht und damit ignoriert. Wenn der Pfad nicht gefunden werden kann oder der Dateiname ungĂŒltig ist (‘. ’ und ‘..’ werden abgewiesen) wird abgebrochen. Die Directory-Verwaltung wird vorbereitet (im Vorgriff: fĂŒr das Directory wird ein FD bereitgestellt, falls noch keiner existiert). Falls die Datei schon existiert, Wird sie erst einmal gelöscht, außer wenn es sich um eine ‘read only’-Datei oder ein Subdirectory handelt (Abbruch).

Nun wird im Directory ein freier Eintrag gesucht. Dabei wird, wenn die Datei schon existierte, auf jeden Fall der gerade eben gelöschte Eintrag wiederverwendet. Dies ist notwendig, da Referenzen in FDs auf den Directory Eintrag (‘fd_dirpos’) sonst nicht mehr stimmen wĂŒrden. Außerdem entfĂ€llt langes Suchen nach einem freien Eintrag.

Der Directory-Eintrag wird initialisiert: Dateiname und Attribut werden ĂŒbertragen, die Systemzeit wird als timestamp genommen, die DateilĂ€nge ist Null und der Start-Cluster wird auf Null gesetzt. An dieser Null, die keine gĂŒltige Clustemummer darstellt, wird bei spĂ€teren Schreibzugriffen erkannt, daß noch gar kein Cluster vergeben wurde.

Nun wird dafĂŒr gesorgt, daß alle GEMDOS-Sektorpuffer zurĂŒckgeschrieben werden, damit die Änderung des Directories auch auf dem Speichermedium wirksam wird.

Jetzt wird die Datei erst richtig eröffnet. Dazu wird eine der internen Open-Routinen, die auch das Handle generiert, benutzt (s. ‘Fopen’). Der Zugriffsmodus hierbei ist bei ‘read only’ 0 (“nur lesen”), sonst 2 (“lesen und schreiben”).

Wenn beim Öffnen kein Fehler auf-tritt, wird das ‘fd_dirch’-Flag fĂŒr Directory-Änderung (Bit 0) gesetzt. Diese Maßnahme scheint mir ĂŒberflĂŒssig zu sein, da der neue Directory-Eintrag schon gesichert ist und nachfolgende Schreibzugriffe, da sie die Datei vergrĂ¶ĂŸern, das Flag sowieso setzen. Vielleicht wurde hier nach dem Motto “doppelt hĂ€lt besser” verfahren.

Der relativ bekannte Fehler, daß bei einem ‘Fcreate’ manchmal nicht bemerkt wird, daß die Datei schon vorhanden ist, der wiederum dazu fĂŒhrt, daß dann zwei Dateien unter gleichem Namen im Directory stehen, wird durch die Directory-Verwaltung verschuldet.

Funktion $3d Fopen

int Fopen(char *path, int mode)

Eine schon existierende Datei ‘path’ wird eröffnet, ‘mode’ bestimmt die erlaubten Zugriffsarten:

0 nur lesen
1 nur schreiben
2 lesen und schreiben

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-33L (EFILNF) Pfad oder Datei nicht gefunden, unbekanntes Laufwerk, illegaler Dateiname, Datei ist Subdirectory, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-35L (ENHNDL) Kein Datei-Handle mehr frei
-36L (EACCDN) Datei ist ‘read only’ und ‘mode’ ist nicht 0
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
-3..80 Handle, unter dem die Datei angesprochen werden kann (>= 6 bei Disk-Dateien).

Arbeitsweise

Nachdem GEMDOS sich vergewissert hat, daß die Datei auch tatsĂ€chlich existiert, werden der Zugriffsmodus und das ‘read only ’-Bit des Dateiattributs ĂŒberprĂŒft. Andere Werte fĂŒr ‘mode’ als 0,1 und 2 werden kommentarlos hingenommen.

Die eigentliche Arbeit macht die nachfolgende Open-Prozedur, die auch von ‘Fcreate’ aufgerufen wird. Es wird nach einem freien Handle in ‘fcbx’ gesucht. Dabei wird immer das kleinste, freie Handle gefunden (bei eigenen Experimenten lassen sich somit “Handle-Verluste” leicht erkennen). Ein freies Handle wird hier an einem NIL des ‘f_pd’-Zeigers (nicht von ‘f_fd’!) erkannt. ‘f_pd’ und ‘f_cnt’ werden gesetzt (letzteres auf 1). Ein FD wird von einer eigenen Routine bereitgestellt, von der auch ‘f_fd’ des FCB definiert wird (merkwĂŒrdig genug). Wenn hierbei ein Fehler auftritt, wird er zwar korrekt an den Aufrufer zurĂŒckgegeben, aber der FCB bleibt halb ausgefĂŒllt! Dies hat zur Folge, daß das Handle belegt bleibt. Bei der Prozeßterminierung wird sogar versucht, diese Datei zu schließen. Dies wird zwar von ‘f_close’ verweigert, aber selbst dann bleibt das Handle “verbraucht”. Welch ein GlĂŒck, daß der einzige Fehler, der hier auftreten kann, ein Mangel an “internem Speicher” ist, der sowieso mehr oder weniger tödlich ist. Trotzdem wird hier deutlich, welche Seiteneffekte in GEMDOS hineinprogrammiert wurden.

Viel eleganter wĂ€re es gewesen, wenn die Funktion, die das Handle ermittelt, erst nach geglĂŒcktem Einrichten des FDs den FCB initialisiert, und zwar alle drei Werte auf einmal. Die untergeordnete Funktion brĂ€uchte dann nur noch mit dem FD zu hantieren und sich nicht mehr um den FCB zu kĂŒmmern.

Doch nun zum Einrichten des Daten-FDs. Die meisten Komponenten werden dem Directory und dessen Strukturen (FD und Directory Descriptor) entnommen. Der Zugriffsmodus wird in ‘fd_mode’ gespeichert. Die Dateiposition wird auf den Dateianfang gesetzt.

Der FD wird in die Anwenderdatei-FD-Liste des Directories vorne eingehĂ€ngt. Zuvor wird die Liste jedoch durchsucht, ob die Datei schon einmal eröffnet wurde. Wenn dies nicht der Fall ist, werden timestamp, DateilĂ€nge und Start-Cluster ebenfalls aus dem Directory ĂŒbernommen, ansonsten aus dem schon bestehenden FD kopiert.

Dabei werden ĂŒbrigens zwei Byte zuviel kopiert, was aber keinen Schaden anrichtet. Dies ist auch eine typische GEMDOS-Macke, die sich auch an anderen Stellen findet.

Bei mehrfacher Eröffnung wird der ‘fd_multi’-Zeiger des “alten” FD auf den “neuen” gesetzt. Da neue FDs immer vorne in die FD-Liste eingehĂ€ngt werden, ist sichergestellt, daß alle zu einer Datei gehörenden FDs durch ‘fd_multi’ miteinander verbunden werden. ‘fd_multi’ wird sonst nirgendwo im GEMDOS benutzt; auch nicht beim Schließen von Dateien, wo FDs aus der Liste ausgehĂ€ngt werden mĂŒĂŸten.

Funktion $3e Fclose

int Fclose(int handle)

Eine zuvor mit ‘Fcreate’/’Fopen’ eröffnete Datei wird geschlossen. Dabei werden alle noch gepufferten Änderungen der Datei selbst und seines Directory-Eintrags auf das Medium geschrieben.

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-37L (EIHNDL) Handle ungĂŒltig
-65L (EINTRN) Interner Fehler (es existiert kein FD zum Handle)
-0L (EOK) alles ok

Arbeitsweise

Das eigentliche Schließen einer Datei (auch Directory-Datei) ĂŒbernimmt eine interne Close-Routine (‘f_fclose’). Danach wird die Datei auf FCB-Ebene geschlossen. Dazu wird ‘f_cnt’ um eins erniedrigt. Wenn das Datei-Handle nur einmal vergeben war (‘f_cnt’ jetzt Null), wird der FD der internen Speicherverwaltung zurĂŒckgegeben und ‘f_fd’ und ‘f_pd’ werden auf NIL gesetzt, so daß das Handle frei wird.

Die von ‘f_fclose’ gelieferte Fehlermeldung wird nun zurĂŒckgegeben. Hier soll schon verraten werden, daß ‘Fclose’ nur richtig funktioniert, wenn ein FD nur unter einem Handle ansprechbar ist, was aber bei Verwendung der I/O-Umleitung nicht der Fall sein muß. Auch mehrfach vergebene Handles (‘f_cnt’ > 1) fĂŒhren zu Fehlfunktionen. Darauf kommen wir bei der Folge ĂŒber die I/O-Umleitung noch zurĂŒck.

‘f_fclose’ bekommt einen FD und ein besonderes Flag ĂŒbergeben, welches die auszufĂŒhrenden Aktionen steuert. Bei Directory-Änderungen, die am gesetzten Bit 0 von ‘fd_dirch’ erkannt werden, werden der timestamp, die DateilĂ€nge und der Start-Cluster vom FD ins Directory ĂŒbertragen. Falls der FD zu einer Directory-Datei gehört, wird an Stelle der intern verwendeten Pseudo-DateilĂ€nge $7FFFFFFF eine Null geschrieben (PC-DOS-KompatibilitĂ€t).

Bit 0 von ‘fd_dirch’ mĂŒĂŸte jetzt eigentlich zurĂŒckgesetzt werden, um zu verhindern, daß bei nachfolgenden ‘f_fclose’-Aufrufen erneut gesichert wird. Da ‘f_fclose’ zur Zeit bei Daten-FDs nur zum endgĂŒltigen Schließen benutzt wird, fĂ€llt dies nicht auf. Bei Directory- und FAT-FDs treten Änderungen des eigenen Directory-Eintrages i.a. nicht auf.

Je nach Steuer-Flag, insbesondere bei Daten-FDs, wird der FD aus der FD-Liste des Directories entfernt. Wenn er dort nicht aufzufinden ist, wird mit der Fehlermeldung EINTRN abgebrochen.

Geht alles gut, werden alle Sektoren der Sektorpufferung mit ‘f_swrite’ herausgeschrieben. ‘f_swrite’ schreibt zwar nur die verĂ€nderten Sektoren zurĂŒck, sabotiert aber faktisch dabei die Sektorpufferung (siehe MĂ€rz-Ausgabe).

Funktion $3f Fread

int Fread(int handle, long count, char *buf)

Es werden ‘count’ Zeichen aus der Datei ‘handle’ in einen bei ‘buf’ beginnenden Speicherbereich geladen.

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-37L (EIHNDL) Handle ungĂŒltig >= 0 Zahl der tatsĂ€chlich gelesenen Zeichen, Vergleich mit ‘count’ gibt Auskunft, ob Fehler auf getreten ist (z.B. Dateiende).

Arbeitsweise

Mit dem durch das handle bestimmten FD wird eine interne Lese-Routine (‘f_fread’) aufgerufen, die auch bei FAT- und Directory-Dateien Verwendung findet.

‘f_fread’ begrenzt nun die Zahl der zu lesenden Zeichen (‘count’) so, daß das Dateiende nicht ĂŒberschritten werden kann. Wenn nun gar kein Zeichen mehr zu lesen ist, da das Dateiende schon erreicht ist, wird sofort mit OL abgebrochen. Ansonsten kommt die zentrale Lese-/ Schreibroutine ‘f_frw’ zum Einsatz (siehe MĂ€rz-Ausgabe).

‘Fread’ kontrolliert nicht den mit ‘Fcreate’/’Fopen’ festgelegten Zugriffsmodus. Lesen aus einer nur zum Schreiben geöffneten Datei wird nicht verhindert!

Funktion $40 Fwrite

*int Fwrite(int handle, long count, char buf)

Es werden ‘count’ Zeichen aus einem bei ‘buf’ beginnenden Speicherbereich in die Datei ‘handle’ geschrieben.

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-37L (EIHNDL) Handle ungĂŒltig
>= 0 Zahl der tatsĂ€chlich geschriebenen Zeichen, Vergleich mit ‘count’ gibt Auskunft, ob Fehler aufgetreten ist (z.B. Speichermedium voll).

Arbeitsweise

Ähnlich wie bei ‘Fread’ wird mit dem FD eine interne Version ‘f_fwrite’ aufgerufen. Diese macht allerdings nichts weiter, als unmittelbar ‘f_frw’ aufzurufen.

Wie ‘Fread’ kontrolliert ‘Fwrite’ nicht den Zugriffsmodus. Daher ist es ohne weiteres möglich, eine nur zum Lesen geöffnete Datei (auch bei ‘read only’!) zu beschreiben! Dies ist auch der Grund dafĂŒr, warum eine mit ‘Fcreate’ als ‘read only’ erzeugte Datei ĂŒberhaupt noch beschrieben werden kann.

Funktion $41 Fdelete

int Fdelete(char *path)

Die Datei ‘path’ wird gelöscht. Sie sollte dabei nicht geöffnet sein. RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-33L (EFILNF) Pfad oder Datei nicht gefunden, unbekanntes Laufwerk, illegaler Dateiname, Datei ist Subdirectory, interner Fehler (keine Pfad Handles, zu wenig interner Speicher)
-35L (ENHNDL) Kein Datei Handle mehr frei
-36L (EACCDN) Datei ist ‘read only’, Datei von anderem Prozeß geöffnet -0L alles ok Arbeitsweise

Nach der Ermittlung des Directories, zu dem die Datei gehört, und der ÜberprĂŒfung des ‘read only’-Bits wird eine interne Routine ‘f_fdelete’ aufgerufen, die auch beim Löschen von Directories verwendet wird.

In ‘f_fdelete’ finden sich wieder einige Teile, die zu einem MultitaskingfĂ€higen GEMDOS gehören wĂŒrden.

In der Liste des Directories aller offenen Dateien wird die Datei gesucht. Wenn sie gefunden wird und dem eigenen Prozeß gehört, wird sie geschlossen und es wird weiter gesucht (sie könnte ja mehrmals geöffnet sein). Wenn die Datei von einem anderen Prozeß geöffnet wurde, wird sofort mit EACCDN abgebrochen. Je nach Reihenfolge der FDs in der Liste, kann die Datei, wenn sie auch noch vom eigenen Prozeß geöffnet war, schon geschlossen worden sein oder auch nicht. Nach einem EACCDN weiß man also nicht, wie es um die Datei steht und ist ratlos. Hinzu kommt, daß die Datei, wenn sie dem eigenen Prozeß gehört, nur mit ‘f_fclose’ und nicht mit ‘Fclose’ geschlossen wird. Daher ist der FCB noch gĂŒltig und das Datei-Handle noch belegt.Da das Handle noch gĂŒltig ist, werden Zugriffe auf die schon gelöschte Datei nicht verhindert, was zu einer mittleren Katastrophe fĂŒhrt (ausprobiert habe ich es lieber noch nicht, da ich tĂ€glich schon mehr Bomben sehe, als mir lieb ist).

Beim (spĂ€teren) Schließen mit 'Fclose’ ist daher der FD schon freigegeben und man erhĂ€lt ein EINTRN (s. ‘Fclose’), wobei das Handle nun frei wird. Es ist also möglich, eine geöffnete Datei zu löschen, nur darf man sich dann nicht ĂŒber Fehlermeldungen von ‘Fclose’ wundem.

Es wĂ€re also besser, wenn die FD-Liste zweimal durchsucht wĂŒrde; einmal, um Zugehörigkeiten zu fremden Prozessen festzustellen, das andere Mal, um die eigenen Dateien zu schließen. Das korrekte Schließen ist jedoch gar nicht so einfach, da ein Aufmf von ‘Fclose’ an Stelle von ‘f_fclose’ auch den FD freigeben wĂŒrde; was zu Komplikationen fĂŒhrt, die sich aber auch beseitigen lassen. Dann werden die FAT-EintrĂ€ge aller Cluster der Datei auf Null gesetzt, wodurch die Cluster als frei markiert werden. Das erste Zeichen des Namens im Directory wird auf $E5 gesetzt, woran GEMDOS bei Directory-Operationen gelöschte Dateien erkennt. Anschließend werden mittels ‘f_fclose’ alle Sektorpuffer auf das Speichermedium zurĂŒckgeschrieben.

Funktion $42 Fseek

int Fseek(long offset, int handle, int mode)

Die aktuelle Dateiposition der Datei ‘handle’ wird neu gesetzt, ‘mode’ legt fest, worauf ‘offset’ bezogen ist.

0 position = offset
1 position = position + offset
2 position = DateilÀnge + offset

Da die neue Position innerhalb der Datei liegen muß, ist ‘offset’ bei ‘mode'=0 immer positiv und bei ‘mode’=2 immer negativ zu wĂ€hlen. Mit dieser Funktion kann auch die DateilĂ€nge ermittelt werden:

file_len = Fseek(0L,handle,2) RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-1L (ERROR) interner Fehler in FAT (Datei ist kĂŒrzer als erwartet)
-32L (EINVFN) ungĂŒltiger Modus (nicht 0,1,2)
-37L (EIHNDL) Handle ungĂŒltig
-64L (ERANGE) gewĂŒnschte Dateiposition nicht innerhalb der Datei
>= 0 neue Dateiposition (= gewĂŒnschte Dateiposition)

Arbeitsweise

Mit ‘mode’ und ‘offset’ wird die gewĂŒnschte neue Dateiposition nach obigen Formeln errechnet. Zusammen mit dem aus dem Handle ermittelten FD wird die interne Seek-Funk-tion ‘f_fseek’ aufgerufen, die auch bei Directory- und FAT-Dateien benutzt wird.

‘f_fseek’ prĂŒft die gewĂŒnschte Dateiposition und bricht gegebenenfalls mit ERANGE ab. Diese Fehlermeldung wird bei den GEMDOS-eigenen Aufrufen jedoch nie abgefragt. GEMDOS hat offenbar grenzenloses Vertrauen in seine Directory- und FAT-Verwaltung.

Bei einer Position von Null werden einfach die Werte im FD, die die Dateiposition festlegen, auf Null gesetzt.

Ansonsten wird die FAT solange gelesen, bis der zur Position gehörende Cluster gefunden wurde. Bei Directory- und FAT-Dateien berechnet die FAT-Lese-Routine die GEM-DOS-Clusternummern anstatt sie aus der FAT zu holen.

Bei Zielpositionen kleiner als der aktuellen Position werden die Cluster vom Dateianfang aus verfolgt, bei grĂ¶ĂŸeren Zielpositionen vom aktuellen Cluster aus. Wenn die FAT-Verwaltung keinen Cluster mehr liefern kann, wird mit ERROR abgebrochen. Dies darf eigentlich aber nicht passieren, da ja vorher sichergestellt wurde, daß der Dateibereich nicht verlassen wird.

Funktion $43 Fattrib

int Fattrib(char *path, int set, char attr)

Das Dateiattribut der Datei ‘path’ wird ermittelt (set = 0) bzw. auf ‘attr’ gesetzt (set =1).

In einigen Dokumentationen ist die Bedeutung von ‘set’ gerade verkehrt herum angegeben.

Auf die Zugriffsberechtigung haben Attribut-Änderungen keinen Einfluß, da das ‘read only’-Bit nur beim Öffnen der Datei ĂŒberprĂŒft wird.

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-33L (EFILNF) Datei nicht gefunden, illegaler Dateiname, Datei ist Subdirectory
-34L (EPTHNF) Pfad nicht gefunden, unbekanntes Laufwerk, interner Fehler (keine Pfad Handles, zu wenig interner Speicher)
>= 0 set=0: ‘attr’ wird unverĂ€ndert zurĂŒckgegeben set= 1: Dateiattribut der Datei ‘path’

Arbeitsweise

Die Directory-Verwaltung ermittelt das zugehörige Directory. Das Attribut wird mit ‘f_fseek’ und ‘f_fread’ bzw. ‘f_fwrite’ gelesen bzw. geschrieben. Beim Schreiben werden mit ‘f_fclose’ alle Sektorpuffer zurĂŒckgeschrieben, so daß das neue Attribut auf jeden Fall auf dem Medium gesichert wird.

Da das Attribut nicht im FD gespeichert wird, gibt es hier keine Konflikte zwischen Directory und FD. Daher kann ‘Fattrib’ auch bei geöffneten Dateien angewendet werden.

Funktion $57 Fdatime

int Fdatime(int *buf, int handle,int set)

Der timestamp der Datei ‘handle’ wird gelesen (set = 0) bzw. geschrieben (set =1).

In buf[0] steht dabei die zu schreibende bzw. die gelesene Zeit, in buf[1] findet sich das Datum (beide im GEMDOS-Format).

Das Ändern des timestamps ist auch bei ‘read only’-Status möglich. Auch der Zugriffsmodus beim Öffnen spielt keine Rolle.

Hier hat der Fehlerteufel mal wieder gewaltig zugeschlagen:

Das Setzen funktioniert nicht nach einem ‘Fcreate’ oder einem ‘Fopen’, wenn die Datei verlĂ€ngert wurde (der alte timestamp ĂŒberschreibt beim ‘Fclose’ den neu gesetzten).

Beim Schreiben liegen Zeit und Datum in ‘buf’ nach dem Aufruf im Intel-Format vor. Da dies wohl eher ein Bug als Absicht ist, sollte der Inhalt von ‘buf’ nachher nicht weiterverwendet werden, da dieser Fehler vielleicht noch einmal behoben wird. Die GĂŒltigkeitskontrolle beim Handle wurde vergessen. Bei einem ungĂŒltigen Handle gibt es einen Bus Error (2 niedliche Bömbchen).

Arbeitsweise

Hier glaubten die Programmierer wohl, es sich besonders einfach machen zu können. Die Funktion ist nĂ€mlich genauso wie ‘Fattrib’ aufgebaut. Hinzu kommt natĂŒrlich die Konvertierung vom Motorola- ins Intel-Format, die direkt in ‘buf' durchgefĂŒhrt wird, wodurch sich der zweite Fehler erklĂ€rt.

‘Fdatime’ operiert auf einer schon offenen Datei, der timestamp wird aber nur direkt im Directory geĂ€ndert. Daher steht im FD noch der alte timestamp. Beim ‘Fclose’ wird er nun vom FD ins Directory “gesichert” und macht somit die Änderung durch ‘Fdatime’ rĂŒckgĂ€ngig, wenn ‘fd_dirch’-Bit 0 gesetzt ist, was nach einem ‘Fcreate’ immer der Fall ist (siehe ‘Fclose’ und ‘Fcreate’).

Wenn man den timestamp mit ‘Fdatime’ liest, bevor die Datei geschlossen wird, erhĂ€lt man noch den richtigen Wert, da auch das Lesen direkt aus dem Directory erfolgt.

Bei einer Datei, bei der keine Änderungen des Directory-Eintrags vorgenommen werden, geht alles gut, da hier ‘fd_dirch’-Bit 0 nicht gesetzt wird. Zur Demonstration gibt’s zur Abwechslung mal wieder ein kleines Demo-Programm (siehe Listing), mit dem Sie diesen Fehler nachvollziehen können. Sehen Sie sich auch den timestamp von ‘test’ nach dem Programmlauf an (mit “zeige Info” des Desktop).

‘Fdatime’ gibt gleich Anlaß zu einem Patch des TOS, der alle drei Fehler auf einen Schlag beseitigt (Abb. 5).

Adresse RAM-TOS ROM-TOS ROM-TOS Bytes (in Hex) 6.2.86 6.2.86 22.4.87 00d372 fc74e4 fc779a 4a 80 66 04 70 db 60 60 20 6e 00 08 4a 6e 00 Oe 66 12 10 ed 00 07 10 ed 00 06 10 ed 00 09 10 ad 00 08 60 16 1b 58 00 07 1b 58 00 06 1b 58 00 09 1b 50 00 08 00 6d 00 01 00 04 70 00 60 2a

Abb. 5: Patch fĂŒr TOS-Fehler in "Fdatime"

Das neue ‘Fdatime’ liefert EIHNDL bei einem ungĂŒltigen Handle und verĂ€ndert ‘buf nicht mehr. Der time-stamp wird nur in den FD geschrieben bzw. aus ihm gelesen und das Bit 0 von ‘fd_dirch’ wird gesetzt, so daß er beim ‘Fclose’ ins Directory ĂŒbernommen wird.

Ein sofortiges Sichern der Directory-Sektoren ist hier nicht möglich, da ‘f_fclose’ nicht dafĂŒr ausgelegt ist, einen Datei-FD zu sichern, ohne den FD aus der FD-Liste zu streichen. Durch einen kleinen Kniff ließe sich dies zwar doch erreichen, aber so eine kritische Operation ist ‘Fdatime’ nun auch wieder nicht, daß sich “schmutzige Tricks” rechtfertigen wĂŒrden. Beim einer mehrfach geöffneten Datei mĂŒĂŸte der timestamp eigentlich in allen FDs geĂ€ndert werden, was der Patch in Anlehnung an die Praktiken des GEMDOS nicht macht (Änderungen von ‘fd_stcl’ und ‘fd_len’ werden auch nur in einem FD berĂŒcksichtigt).

Ausblick

Eng verknĂŒpft mit der Dateiverwal-tung ist die Handhabung der Directories und ihrer Pfade, mit der wir uns nĂ€chstes Mal eingehend beschĂ€ftigen werden.

/* Demonstration der Fehler von Fdatime' */ #include <osbind.h> test() { int fh1; unsigned int buf[2]; /* Datei kreieren */ fh1 = Fcreate("test",0); if (fh1 < 0) /* Abbruch bei Fehler */ return; /* von Fcreate generiertes Time stamp lesen */ Fdatime(fh1,buf,0); /* Time stamp lesen */ printf("alte Zeit :%04% altes Datum : %04%\n",buf[0],buf[1]); buf[0] = 2; /* Uhrzeit 0:00:02 */ buf[1] = Tgetdate(); /* akt. Datum behalten */ printf("gesetzte Zeit:%04% gesetztes Datum: %04%\n",buf[0],buf[1]); Fdatime(fh1,buf,1); /* Time stamp setzen */ /* Fdatime hat Zeit und Datum ins Intel-Format konvertiert */ printf("gesetzte Zeit:%04% gesetztes Datum: %04%\n"buf[0],buf[1]): /* Time stamp wurde korrekt geschrieben wie Kontrolle zeigt */ Fdatime(fh1,buf,0); /* Time stamp lesen */ printf("gelesene Zeit:%04% gelesenes Datum: %04%\n",buf[0],buf[1]); /* beim Schließen der Datei wird alter Time stamp zurĂŒckgeschrieben */ Fclose(fh1); } main() { test(); Cnecin(); }
Alex Esser