Heute geht es um die Directory-Verwaltung. Da dies wieder ein sehr umfangreiches Kapitel des GEMDOS ist, haben wir es in zwei Teile aufgeteilt. Im ersten Teil beschÀftigen wir uns mit der internen Verwaltung von Directories (Dateiverzeichnissen), Pfaden und den zugehörigen GEMDOS-Funktionen. Im zweiten Teil, der in der nÀchsten Ausgabe erscheint, geht es dann um Suchoperationen in Directories. Diesmal kommen die Freunde der allseits beliebten GEMDOS-Fehler wieder voll auf ihre Kosten.
Allgemeines
ZunĂ€chst ein paar allgemeine Bemerkungen zur Struktur der Directories (Dateiverzeichnisse) unter GEMDOS. Jedes Laufwerk verfĂŒgt ĂŒber ein Root Directory (Wurzelverzeichnis), welches mit â' angesprochen wird. Das Root Directory kann neben "normalenâ Anwenderdateien wie Programmen, Texten usw. weitere (Sub-)Directories (Ordner) enthalten, die eigene Namen haben. Jeder Ordner kann selbst wiederum weitere Ordner enthalten, so daĂ sich eine hierarchische Struktur ergibt.
Ein ĂŒbergeordnetes Directory wird âParentâ genannt, ein Subdirectory ist ein âChildâ. Diese Bezeichnungen sind jeweils relativ zu einem anderen Directory gemeint, d.h., ein Child kann selbst wiederum Parent eines anderen Directories sein.
Dateien werden durch ihren Zugriffspfad bezeichnet, der sich aus dem Laufwerk und der Abfolge von durchlaufenen Ordnern vom Root Directory bis zum Directory, in dem sich die Datei befindet, ergibt (z.B. âA:\ MEGAMAX\HEADERS\STDIO.H'). Dies ist jedem Anwender vom Arbeiten mit dem Desktop her bekannt.
Bei den dem Programmierer zur VerfĂŒgung stehenden GEMDOS-Funktionen können auĂer diesen vollstĂ€ndigen Dateipfaden auch nur bestimmte Teile davon angegeben werden. Beim Fehlen der Laufwerkskennung bezieht sich der Pfad auf das sogenannte âStandardlaufwerkâ, welches mit âDsetdrvâ festgelegt werden kann (z.B. â\MEGAMAX\HEADERS\STDIO.H').
Wenn der erste Backslash (â\â) fehlt, bezieht sich die Ordnerfolge nicht auf das Root Directory, sondern wird an den âStandardpfadâ, der mit âDsetpathâ eingestellt wird, angehĂ€ngt (relative Pfadangabe). Z.B. meint bei einem Standardpfad â\MEGAMAX\â fĂŒr Laufwerk A: der Name âA:HEADERS\STDIO.Hâ die vollstĂ€ndige Bezeichnung âA:\MEGAMAX\HEADERS\ STDIO.Hâ. Diese beiden AbkĂŒrzungsmöglichkeiten können auch miteinander kombiniert werden, z.B. könnte beim letzten Beispiel auch nur "HEADERS\ STDIO.Hâ angegeben werden, wenn A: Standardlaufwerk ist. Diese letzte Möglichkeit ist in den meisten FĂ€llen zu bevorzugen, da hierbei die absolute Position einer Datei im Dateisystem nicht bekannt zu sein braucht.
Bei der kompletten Angabe von Dateinamen, wie in vielen Programmen leider der Fall, besteht die Gefahr, daĂ eine Datei z.B. immer nur auf Laufwerk A: gefunden wird, obwohl es sich auf C: oder in einem anderen Ordner befindet.
Weiterhin können die speziellen Directories '.' und '..' verwendet werden. '.' bezeichnet das aktuelle Directory selbst und '..' fĂŒhrt eine Directory-Ebene höher. So ist z.B. âHEADERS.\ STDIO.Hâ identisch mit âHEADERS\STDIO.Hâ und â..\ERRORS.OUTâ bezeichnet '\MEGAMAX\ERRORS.OUTâ, wenn der Standardpfad wieder â\MEGAMAX\HEADERS' ist.
GEMDOS verwaltet fĂŒr jedes Laufwerk einen eigenen Standardpfad. Dabei hat sogar jeder ProzeĂ seine eigenen Standardpfade und Standardlaufwerke. Sie werden an nachgeladene Child-Prozesse "weitervererbtâ. Die Pfade des Parent-Prozesses können vom Child-ProzeĂ nicht verĂ€ndert werden.
Bei der maximalen Schachtelungstiefe von Ordnern gibt es keine prinzipielle Begrenzung von Seiten des GEMDOS aus. In der Praxis gibt es EinschrĂ€nkungen bei der maximalen PfadlĂ€nge (s. 'Dgetpathâ) und bei der âinternen Speicherverwaltung''.
Aufbau der Directories
Subdirectories sind ganz Ă€hnlich wie "normaleâ Dateien aufgebaut. Ihr Directory-Eintrag in ihrem Parent Directory unterscheidet sich hauptsĂ€chlich im Dateiattribut. Bei Directories ist hier Bit 4 gesetzt. AuĂerdem ist als âDateilĂ€ngeâ immer Null eingetragen. Die Directory-Datei besteht aus lauter 32 Byte groĂen Directory-EintrĂ€gen. Ihre Struktur ist in Abb. 1 dargestellt.
'dir_name' ist der Dateiname im GEM-DOS-Format. Er ist immer genau 11 Zeichen lang, 8 Zeichen fĂŒr den Hauptteil des Namens, 3 Zeichen fĂŒr die Namenserweiterung (Extender). Zu kurze Namensteile werden mit SPACE aufgefĂŒllt (âDATEI.Xâ wird hier als âDATEI X â dargestellt). Jeder Dateiname darf pro Directory nur einmal Vorkommen. Es darf also keine Anwenderdatei und einen Ordner gleichen Namens geben. âdir_attrâ ist das "Dateiattributâ, das Auskunft ĂŒber Typ und einige Eigenschaften der Datei gibt. Es wird nĂ€chsten Monat noch diskutiert.
Der timestamp (Zeit und Datum der Erstellung der Datei) findet sich in âdir_dateâ und âdir_timeâ (zur ReprĂ€sentation dieser Daten siehe spĂ€tere Folge ĂŒber die âTimer-Funktionenâ).
Die Nummer des ersten Clusters der Datei ist âdir_stclâ. Die weiteren Clusternummern werden mit der FAT ermittelt (s. MĂ€rz-Ausgabe). âdir_flenâ schlieĂlich ist die LĂ€nge der Datei in Bytes.
Timestamp, Startcluster und DateilĂ€nge sind ĂŒbrigens im Intel-Format gespeichert, d.h. die Reihenfolge von höher- und niederwertigen Bytes ist gegenĂŒber dem Motorola-Format gerade vertauscht. Beim Löschen von Dateien wird im Directory nur das erste Zeichen des Dateinamens auf $E5 gesetzt, der Rest bleibt erhalten. Da die zugehörigen Cluster in der FAT aber freigegeben werden, ist ein Rekonstruieren gelöschter Dateien nicht so ohne weiteres möglich.
typedef struct
{ char dir_name[11]; /* Dateiname */
char dir_attr; /* Attribut */
char dir_dummy [10] ; /* unbenutzt */
unsigned int dir_time; /* Erstellungszeit */
unsigned int dir_date; /* Erstellungsdatum */
int dir_stcl; /* erster Cluster */
long dir_flen; /* DateilÀnge */
} DIR;
Abb. 1: Directory-Eintrag
Ein neu angelegtes Directory besteht zuerst aus einem Cluster, der anfangs gelöscht ist (nur Nullbytes enthÀlt). Sobald mehr Dateien erzeugt werden, als hier Platz haben, wird das Directory automatisch um einen Cluster verlÀngert, der ebenfalls zuerst gelöscht wird.
Eine Ausnahme bildet wiederum das Root Directory. Es hat eine fest vorgegebene LĂ€nge, die nicht ĂŒberschritten werden kann. Das Root Directory wird beim Formatieren komplett mit Nullen gefĂŒllt. Ein Directory wird jedoch nie verkĂŒrzt, auch dann nicht, wenn der letzte Cluster ĂŒberhaupt nicht mehr benutzt wird.
Neue EintrĂ€ge werden immer an der ersten freien Position gemacht. Die EintrĂ€ge haben somit keine bestimmte Reihenfolge. FĂŒr die sortierte Anzeige in den Fenstern ist der Desktop verantwortlich. Jedes Subdirectory hat noch zwei spezielle EintrĂ€ge, die die Dateinamen â.â und ââ haben. Sie werden bei Anlegen eines Subdirectory automatisch erzeugt und erscheinen im allgemeinen bei der Anzeige von Directories nicht. '.' verweist auf das Directory selbst (âdir_stclâ ist der eigene Start-Cluster), fĂŒhrt zum Parent Directory (âdir_stclâ ist der Start-Cluster des Parent Directories). Wenn das Root Directory der Parent ist, enthĂ€lt âdir_stclâ hier Null.
Directory-Dateien
GEMDOS verwaltet Directories ganz Ă€hnlich wie Anwenderdateien. Das bedeutet, jedes Directory hat einen eigenen âFile Descriptorâ (FD), ĂŒber den die eigentlichen Datei-Zugriffe wie Lesen, Schreiben und Positionieren laufen. Dabei finden die allgemeinen internen Routinen Verwendung, wie sie in der MĂ€rz-Folge beschrieben wurden. FĂŒr die von GEMDOS intern verwalteten Directory-Dateien gibt es keine Datei-Handles und keine âFile Control Blocksâ (FCB).
Da auf Directories wesentlich hĂ€ufiger zugegriffen werden muĂ als auf andere Dateien, wird hier ein erhöhter Verwaltungsaufwand betrieben. Dies hat zur Folge, daĂ '.' und '..' von GEMDOS ĂŒberhaupt nicht benutzt werden. Sie existieren nur aus KompatibilitĂ€tsgrĂŒnden zu PC-DOS.
GEMDOS merkt sich alle Verzeichnisse, auf die irgendwann einmal zugegriffen wurde, um bei weiteren Suchoperationen sofort zu wissen, was mit dem Directory los ist. Dies spart erheblich an Zugriffen auf die ĂŒbergeordneten Directories. Zu jedem GEMDOS bekannten Directory existiert dazu eine Datenstruktur, die ich âDirectory Descriptorâ (DD) getauft habe. Die DDs sind in einer Baum-Struktur entsprechend der Directory-Hierarchie angeordnet. Jedesmal, wenn eine der GEMDOS-Dateifunktionen ein Directory anspricht, fĂŒr das es noch keinen DD gibt, wird dieser automatisch angelegt und in den Baum einsortiert. GEMDOS geht sogar noch weiter, denn das Bekanntmachen neuer Directories geschieht sozusagen im Vorbeigehen. Bei jeder Suchoperation (also auch beim Suchen gewöhnlicher Dateien) werden nĂ€mlich alle Subdirectories, deren EintrĂ€ge untersucht werden, bekannt gemacht. Da sich GEMDOS nicht fĂŒr V und interessiert, gibt es dafĂŒr auch keine DDs.
âDirectory Descriptorenâ (DD)
DDs sind nun folgendermaĂen aufgebaut (s. Abb. 2). Ăhnlich wie die FDs beinhalten sie einige Informationen zum Eintrag des Directories in dessen Parent Directory. Dazu gehören der eigene Name im GEMDOS-Format (âdd_nameâ), der erste Cluster (âdd_stclâ) sowie Erstellungszeit und -datum
(âdd_timeâ und âdd_dateâ). Der Startcluster liegt in GEMDOS-ZĂ€hlung und der timestamp im Intel-Format vor. FĂŒr weitere ErlĂ€uterungen vergleichen Sie bitte mit den ErklĂ€rungen zum FD aus der letzten Folge.
âdd_dfdâ zeigt auf den FD des Directories und bedarf wohl keiner weiteren ErklĂ€rung. NIL zeigt an, daĂ das Directory zwar schon erkannt, aber noch kein FD benötigt wurde. Der FD wird erst dann eingerichtet, wenn auch tatsĂ€chlich ein Zugriff auf das Directory stattfindet. Auch eine Referenz auf das zugehörige Laufwerk muĂ sein: 'dd_dmdâ verweist auf den "Drive Media Descriptorâ, der ja auch schon frĂŒher behandelt wurde.
Im letzten Artikel wurde erwĂ€hnt, daĂ die FDs aller geöffneten Dateien eines Directories in einer Liste verknĂŒpft sind.
â dd_fdl â zeigt auf den Beginn dieser Liste. Dabei bedeutet NIL, daĂ keine Dateien geöffnet sind.
Doch kommen wir nun zum eigentlich interessanten Teil, der Verkettung der einzelnen DDs untereinander (Abb. 3). Der RĂŒckverweis auf das eigene Parent Directory geschieht mit âdd_parddâ. Das Root Directory hat als einziges keinen Parent; daher steht hier NIL. âdd_chddâ bezeichnet ein Child Directory; wenn noch keins bekannt ist, steht hier NIL. Die einzelnen Directories einer Hierarchie-Ebene sind in einer âChild-Listeâ mit âdd_linkâ verbunden, das Ende der Liste wird durch NIL markiert. Die Reihenfolge der Directories in der Liste ist im allgemeinen umgekehrt wie in ihrem Parent. Das ist aber nicht immer so, daher ist diese Reihenfolge als Undefiniert anzusehen. Mit Hilfe dieser drei Zeiger kann sich GEMDOS also relativ schnell innerhalb der Directory Hierarchie bewegen, ohne daĂ Zugriffe auf den Massenspeicher notwendig sind.
Da Zugriffe auf das Parent Directory ziemlich hĂ€ufig sind, gibt es noch weitere VerknĂŒpfungen zwischen Parent und Child. In der Abb. 4 sind alle Beziehungen eines Child Directories zu seinem Parent dargestellt. Der FD des Parents kann direkt ĂŒber âdd_parfdâ angesprochen werden, ohne Umweg ĂŒber den DD des Parents.
Jedes Directory hat ja einen ganz normalen Eintrag in seinem Parent. âdd_parposâ bezeichnet nun die Dateiposition dieses eigenen Eintrags im Parent. Ăber âdd_pardfd' und âdd_parposâ kann also mit Hilfe von Fseek und Fwrite direkt der eigene Eintrag angesprochen und verĂ€ndert werden. âdd_lposâ zeigt auf die höchste Dateiposition, die bisher bei Suchoperationen in diesem Directory erreicht wurde. Dies ist fĂŒr das automatische Erweitern des DD-Baumes wichtig (s. Teil 2).
Damit können wir auch noch einmal auf die FDs von Anwenderdateien der letzten Folge zurĂŒckkommen. Die Verbindungen mit dem zugehörigen Directory-Descriptor sind in Abb. 5 dargestellt. Es ist praktisch eine vereinfachte Version von Abb. 4.
Hier noch ein Hinweis fĂŒr diejenigen, die sich mit der internen Speicherverwaltung des GEMDOS auskennen. Die DDs benötigen vier 8-Wort-Einheiten. Freigegebene DDs werden daher ebenso wie die FDs in der âmiflâ-Liste 4 aufbewahrt.
typedef struct
{ char dd__name [11] ; /* Directory-Name */
int dd_stcl; /* Start-Cluster */
unsigned int dd_time; /* Erstellungs-Zeit */
unsigned int dd_date; /* Erstellungs-Datum */
FD *dd_dfd; /* Zeiger auf eigenen FD */
DD *dd_pardd; /* Zeiger auf DD des parent directory */
DD *dd_chdd; /* Zeiger auf DD eines child directory */
DD *dd_link; /* Zeiger auf DD des nÀchsten childs */
DMD *dd_dmd; /* Zeiger auf zugehörigen DMD */
FD *dd_parfd; /* Zeiger auf FD des parent directory */
long dd_parpos; /* Position des eigenen Eintrags im parent */
long dd_lpos; /* letzte Directory-Suchposition */
FD *dd_fdl; /* Zeiger auf ersten User-Datei-FDi */
} DD;
Abb. 2: Directory Descriptor
Initialisierung des DD-Baumes
Wenn ein Laufwerk zum ersten Mal angeprochen wird, richtet GEMDOS, wie Sie schon wissen, einen âDrive Media Descriptorâ (DMD) ein. Dabei wird automatisch der DD fĂŒr das Root Directory erzeugt. Damit ist ein Minimal-Baum vorhanden, der bei nachfolgenden Operationen erweitert wird.
Der Baum wird dabei im Laufe der Zeit immer nur vergröĂert. GEMDOS hat quasi ein perfektes GedĂ€chtnis, was die Existenz von Directories angeht. Eine Ausnahme bildet der Mediumwechsel, bei dem der gesamte DD-Baum aufgelöst wird. Hieraus ergeben sich auch Probleme mit der Harddisk. Daeshieri.allg. sehr viele Ordner und nie Medienwechsel gibt, sammeln sich recht viele DDs an, die leicht zu einer Ăberlastung der internen Speicherverwaltung fĂŒhren können.
Pfad-Handles - schon wieder neue Handles
Auf den DDs baut die Verwaltung der Standardpfade auf. Intern wird jeder Standardpfad ĂŒber ein âPfad-Handleâ angesprochen. Diese Pfad-Handles sind fĂŒr den Programmierer legal nicht zugĂ€nglich. Pfad-Handles liegen zwischen 1 und 39, hinzu kommt noch die Null als Dummy-Handle, das verwendet wird, wenn kein Pfad festgelegt ist.
Der Pfad selbst ist durch einen Zeiger auf den DD des Pfadendes, also des letzten Ordners des Pfades, eindeutig charakterisiert. Die Zuordnung dieser Zeiger zu den Pfad-Handles erfolgt ĂŒber eine Tabelle Câpathx[]â), die zu den globalen GEMDOS-Variablen gehört (Tab. 1). âpathx[5]â liefert also z.B. den Pfad fĂŒr Pfad-Handle 5. Die GĂŒltigkeit eines Elements von âpathx[]â wird durch âpthcntx[]â angezeigt.
Beim Starten von Tochterprozessen werden die Standardpfade mit ihren Handles âvererbtâ. Daher kann ein Handle mehrmals vergeben sein. DarĂŒber wird mit 'pthcntx[]â (Tab. 1) Buch gefĂŒhrt. FĂŒr jedes Pfad-Handle steht hier die Anzahl der Prozesse, denen der jeweilige Standardpfad bekannt ist. Null zeigt an, daĂ das Pfad-Handle unbenutzt ist. Dann ist das zugehörige âpathx[]â Undefiniert (es muĂ nicht unbedingt NIL sein).
Nun muĂ GEMDOS noch wissen, welche Standardpfade jeder ProzeĂ hat. Dazu findet sich im ProzeĂdescriptor (PD) das Feld âchar p_drvx[16]â, in dem fĂŒr jedes der möglichen 16 Laufwerke das Handle des Standardpfades steht (Null falls noch kein Standardpfad vergeben wurde, d.h. das Laufwerk noch nicht angesprochen wurde).
Abb. 3: Baum der Directory Descriptoren (Beispiel)
Vererbung von Pfaden
Bei âPexecâ werden alle Pfad-Handles aus âp_drvx[]â des Parent-PDs in den Child-PD kopiert, wobei âpthcntx[jâ inkrementiert wird. Kurioserweise geschieht dies auch bei Null-Handles, die eigentlich anzeigen, daĂ kein Standardpfad existiert. Dies fĂŒhrt dazu, daĂ âpthcntx[0]â unsinnige Werte enthĂ€lt. Dadurch wird erreicht, daĂ der Child-ProzeĂ die Pfade des Parents erhĂ€lt, ohne daĂ fĂŒr jeden Pfad ein neues Handle vergeben werden muĂ. Erst wenn das Child einen Pfad umsetzt, wird ein neues Handle fĂ€llig. Da sich an den Pfad-Handles des Parents nichts Ă€ndert, bleiben dessen Pfade von allen Aktionen des Childs unberĂŒhrt.
Trotzdem sind 39 Pfad-Handles ein wenig knapp kalkuliert. Bei Verwendung wirklich aller 16 Laufwerke und Programme, die Pfade vieler Laufwerke neu setzen (z.B. Command Line Interpreter, Desktop) kann die Zahl gleichzeitig im Speicher haltbarer Prozesse schon auf 2-3 beschrÀnkt sein.
Beim Terminieren eines Prozesses mit einer der âPtermâ-Funktionen wird 'pthcntx[]' bei allen gĂŒltigen Handles dekrementiert. Hier werden Null-Handles extra abgefangen. Es wird jedoch nicht berĂŒcksichtigt, ob âpthcntx[]â auch wirklich echt gröĂer als Null ist. Dies dĂŒrfte eigentlich auch nicht Vorkommen, aber auf Grund zweier Fehler (siehe weiter unten) kann dieser Fall doch eintreten, und schon hat man negative Werte.
Mit Abb. 6 ist ein Beispiel fĂŒr die Pfadverwaltung mit zwei Prozessen gegeben. ProzeĂ 1 ist der Parent und hat Pfade fĂŒr die Laufwerke A: und B: definiert. ProzeĂ 2 ist das Child und hat zunĂ€chst beide Pfade geerbt. Dann hat er einen Pfad fĂŒr C: festgelegt und den Pfad fĂŒr B: umdefiniert. Vergleichen Sie auch mit "Dset-pathâ.
So weit, so gut. Probleme tauchen aber auf, wenn man Programme mit âPexecâ einmal laden, resident im Speicher halten und mehrmals starten möchte. Denn das Vererben der Pfade geschieht bei allen Pexecâ-Modi auĂer 4 (ânur startenâ). Die Ăbertragung der Pfade geschieht also nur ?eim ersten Start eines Programms und das auch noch zum Zeitpunkt des Ladens. Deshalb wĂ€re es sinnvoller, dies mit den Modi 0 und 4 (âladen und startenâ und ânur startenâ) zu verknĂŒpfen.
Abb. 4: Child und Parent Directory
Ăhnliches gilt ĂŒbrigens auch fĂŒr die I/O-Umleitung. Beim âPtermâ wĂ€re es besser, wenn der PD wieder in den Zustand vor dem âPexecâ gebracht, d.h. âp_drvx[]â gelöscht wĂŒrde. WĂ€hrend letzteres noch âvon Handâ gemacht werden könnte, ist ersteres Problem nicht lösbar - halbwegs saubere Programmierung vorausgesetzt. Hier sei an die Atari-Programmierer appelliert, in Zukunft fĂŒr Besserung zu sorgen.
Anmeldung von Laufwerken
Nach der Initialisierung des GEMDOS beim Systemstart sind keinerlei Pfad-Handles belegt (alle âp_drvx[]â sind gelöscht), d.h. es gibt fĂŒr GEMDOS noch keine Standardpfade. FĂŒr den Anwender gilt jedoch das Root Directory als Standardpfad, solange kein anderer mit âDsetpathâ ausgewĂ€hlt wird. Daher sorgt GEMDOS dafĂŒr, daĂ bei einem Zugriff auf ein Laufwerk, das noch keinen Standardpfad hat, automatisch das Root Directory als Standardpfad gesetzt wird. Dies geschieht in der Routine âd_chkdrvâ, die bei allen Laufwerks- und Pfadoperationen aufgerufen wird, bevor mit Pfad-Handies usw. gearbeitet wird.
âd_chkdrvâ wurde schon in der April-Ausgabe unter âAnmeldung von Laufwerkenâ beschrieben, jedoch folgt hier noch einmal das wesentliche, um den Zusammenhang zur Pfadverwaltung besser herauszustellen.
Abb. 5: Anwenderdatei und Directory
GEMDOS merkt sich, welche Laufwerke ihm bekannt sind, d.h. schon einmal angesprochen wurden. Bei bisher noch nicht bekanntem Laufwerk wird ĂŒberprĂŒft, ob es ĂŒberhaupt ansprechbar ist (mit âGetbpbâ), wobei alle elementaren Strukturen fĂŒr das Laufwerk (DMD, FD fĂŒr FAT und Root Directory, DD des Root Directory) initialisiert werden. Existiert das Laufwerk nicht, bricht âd_chkdrvâ hier ab. Ansonsten ist das Laufwerk auf jeden Fall bekannt.
Ein Null-Pfad-Handle in 'p_drvx[]' zeigt an, daĂ noch kein Standardpfad festgelegt wurde. In diesem Fall wird ein freies Pfad-Handle ermittelt, der DD des Root Directory, der zu diesem Zeitpunkt auf jeden Fall vorhanden ist, in âpathx[]' eingetragen und âpthcntx[]â auf Eins gesetzt. Damit ist das Root Directory als Standardpfad definiert. Das gleiche passiert auch, wenn das Pfad-Handle in âp_drvx[]â zwar gĂŒltig ist, aber âpathx[] ein NIL-Zeiger ist. Dieser Fall soll der besonderen Behandlung von Medienwechseln dienen.
DD *pathx[40]; /* $564C/ $564c/ $7dee: Standardpfade */
char pthcntx[40]; /* $58c4/ $58c4/ $8066: Zahl der Prozesse */
long inv_pos /* $17b06/$fdlcl6/$fd3042: Konstante */
=-1L;
char dirown[] /* $17b0a/$fdlcla/$fd3046: Directory-Eintrag */
= [ '.',0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,
0,0,0,0,'0,0,0,0,0,0,0];
char dirpar[] /* $17b20/$fdlc30/$fd305c: Directory-Eintrag */
= [ '.','.',0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20 ,
0,0,0,0,0,0,0,0,0,0,0];
Tabelle 1: Globale GEMDOS-Variablen zur Directory-Verwaltung
Diskettenwechsel - lieber nicht
Wie innerhalb dieser Serie schon des öfteren erwĂ€hnt, mĂŒssen bei Medienwechseln, die beim ST i.allg. auf Disketten beschrĂ€nkt sind, alle das Laufwerk betreffenden internen Datenstrukturen freigegeben und gegebenenfalls neu initialisiert werden. Dies gilt selbstverstĂ€ndlich auch fĂŒr die Pfadverwaltung. Deshalb gibt der âMedia changeâ-Handler sĂ€mtliche DDs und FDs frei. AuĂerdem werden alle Pfade des betroffenen Laufwerks ungĂŒltig gemacht, indem âpthcntx[]â auf Null gesetzt wird. AuĂerdem wird âpathx[]â auf NIL gesetzt, um die oben erwĂ€hnte Sonderbehandlung durch âd_chkdrv' zu erzwingen, die aber nicht die rechte Wirkung zeigt. Das Problem besteht darin, daĂ die in den PDs gespeicherten Pfad-Handles von nun an ungĂŒltig gemachte Pfade bezeichnen.
Im Falle, daĂ der Pfad vorher nur vom eigenen ProzeĂ benutzt wurde, wird das Pfad-Handle bei der nĂ€chsten Gelegenheit neu vergeben, womöglich fĂŒr den Standardpfad eines anderen Laufwerks. Dies hat dann zur Folge, daĂ dieser Pfad auch fĂŒr das andere Laufwerk gilt. Und schon herrscht Chaos und der Programmierer wundert sich.
Abgesehen davon ist natĂŒrlich die ZĂ€hlung mit âpthcntx[]- durcheinandergeraten, was weitere âSpĂ€tfolgenâ (z.B. negative Werte in âpthcntx [] â nach der ProzeĂ-Terminierung!) nach sich ziehen kann. Der Fehler lieĂe sich beseitigen, indem nur âpathx[]â gelöscht wĂŒrde, âpthcntxf]â aber unverĂ€ndert bliebe. Die Handles blieben dann belegt und die âp_drvx[] -Felder wĂŒrden weiterhin gĂŒltige Handles enthalten.
In den meisten FĂ€llen wird das Laufwerk mit einem neuen Medium ansprechbar sein, so daĂ dann die Pfad-Handles wieder gĂŒltig werden sollten. Die Routine âd_chkdrv\ die vor jeder Benutzung der Pfade zum Einsatz kommt, fragt diesen Fall (Laufwerk bekannt, handle gĂŒltig, aber âpathx[]â NIL) sogar schon extra ab, reagiert aber falsch darauf. Bisher vergibt sie einfach ein neues Handle, dem das Root Directory als Pfad zugeordnet wird, und setzt es auch noch in âp_drvx[]\ Dies wĂŒrde dann funktionieren, wenn dabei â pthcntx []â des alten Handles dekrementiert wĂŒrde, da es nun einem ProzeĂ weniger bekannt ist. Dies fehlt vermutlich deshalb, weil auf Grund des ersten Fehlers âpthcntx[]â schon vom âMedia changeâ-Handler gelöscht wird.
GĂŒnstiger wĂ€re es noch, gar kein neues Handle zu vergeben, sondern einfach âpathx[]' auf das Root Directory zu setzen. Damit wĂ€re nach einem Mediumwechsel der alte Pfad einfach gegen das Root Directory ausgetauscht worden.
Directory-Suchoperationen
Alle GEMDOS-Funktionen, die einen beliebigen GEMDOS-Dateinamen (möglicher Pfad + Name) als Parameter haben, rufen eine wichtige Routine auf, die aus dem Dateipfad den DD des zugehörigen Directories und den ĂŒbrig bleibenden eigentlichen Dateinamen ermittelt. Diese Routine heiĂt bei mir âd_getdirâ.
Eine weitere wichtige Funktion ist â d_srcname â, die in einem schon ermittelten Directory nach bestimmten Dateien sucht. Beide Routinen werden in Teil 2 ausfĂŒhrlich erlĂ€utert. Entscheidend ist hier nur, daĂ bei beiden Funktionen alle Directories, die "auf dem Wegâ liegen, automatisch in den DD-Baum eingefĂŒgt werden. Wenn ĂŒbergeordnete Routinen mit Directories und Pfaden arbeiten, können sie sich darauf verlassen, daĂ alle benötigten Strukturen hiernach definiert sind.
Globale GEMDOS-Variablen
Eine Zusammenstellung findet sich in Tab. 1. Die Adressen sind wie ĂŒblich TOS-abhĂ€ngig und beziehen sich auf das RAM-TOS vom 6.2.1986, das ROMTOS vom 6.2.1986 und das ROM-TOS vom 22.4.1987 (von links nach rechts), âdirownâ und âdirparâ werden bei âDcreateâ erlĂ€utert.
GEMDOS-Funktionen (TRAP #1)
Funktion $47 Dgetpath
long Dgetpath(char *buf, int drive)
Der aktuelle Standardpfad vom Laufwerk âdriveâ (0 fĂŒr Standard-Laufwerk, 1 fĂŒr A:, 2 fĂŒr B: usw.) wird ermittelt und in âbufâ abgelegt. Der String beinhaltet nicht die Laufwerkskennung, beginnt und endet nicht mit â' und ist nullterminiert. Der Standardpfad âA:\ORDNERl\ORDNER2' wird also als âORDNER1\ORDNER2' geliefert.
Es gibt keine GröĂenbeschrĂ€nkung von Seiten des GEMDOS; âbuf muĂ groĂ genug bereitgestellt werden. Eine GröĂe von 128 Zeichen dĂŒrfte im allgemeinen ausreichen.
RĂŒckgabewerte:
-46L (EDRIVE) Laufwerk existiert nicht, interner Fehler (kein interner Speicher, keine Pfad-Handles mehr)
0L alles ok
Arbeitsweise
Zuerst wird eine e ventuelle Angabe des Standardlaufwerks ausgewertet, d.h.. das gewĂŒnschte Laufwerk wird ermittelt. Wie ĂŒblich erfolgt der Aufruf von âd_chkdrv' Beginnend beim DD des Directories am Pfadende (erreicht durch âp_drvx[]â und âpathx[]' wird sich mittels der âdd_parddâ-Zeiger rekursiv bis zum Root Directory hochgearbeitet. Beim RĂŒcklauf der Rekursion werden die Directory-Namen (âdd_nameâ) zum Pfad zusammengesetzt.
Stack-Ăberlauf-Probleme dĂŒrfte es hier nicht geben, da der fĂŒr die AusfĂŒhrung von GEMDOS-Funktionen eigens bereitgestellte Stack 3 kB groĂ ist. Dies ist eine richtig hĂŒbsche Routine; bei aller Kritik muĂ man die GEM-DOS-Programmierer auch einmal loben.
Funktion $3B Dsetpath
long Dsetpath(char *path)
Der Standardpfad fĂŒr das in âpath' mit angegebene Laufwerk wird neu gesetzt. Er gilt nur fĂŒr den eigenen ProzeĂ und wird von diesem an Tochterprozesse vererbt, âpathâ endet nicht mit âV. Da Accessories immer unter dem PD der Haupt-Applikation laufen, können sich hier Konflikte ergeben. Wenn Accessories Pfade umsetzen. findet die Applikation eventuell ihre Dateien nicht mehr. Daher sollten Accessories âDsetpathâ (und auch âDsetdrvâ) nicht benutzen, sondern jeweils komplette Pfade angeben.
RĂŒckgabewerte:
-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-1L (ERROR) Laufwerk existiert nicht, interner Fehler (keine Pfad-Handles)
-34L (EPTHNF) Pfad nicht gefunden, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
OL alles ok.
Arbeitsweise
Zuerst wird das Laufwerk, bei dem der Standardpfad geÀndert werden soll, ermittelt. Wenn kein Laufwerk explizit im Pfad angegeben ist, wird das aktuelle genommen.
Auch hier wird zunĂ€chst âd_chkdrvâ aufgerufen. AnschlieĂend wird der alte Pfad ungĂŒltig gemacht, indem âpthcntx[]â um eins erniedrigt wird. Wenn der Pfad von mehreren Prozessen benutzt wird, bleibt das Pfad-Handle also noch belegt.
Nun wird ein freies Pfad-Handle gesucht (erkannt an einer Null in âpthcntx[]â), woraufhin mit âd_getdirâ der DD des gewĂŒnschten Directories (also des neuen Pfadendes) ermittelt wird.
Zuletzt wird ein Zeiger auf diesen DD in âpathx[]â mit dem vorher gefundenen Pfad-Handle gespeichert, das zugehörige âpthcntx[]â inkrementiert (hier also auf Eins gesetzt) und das Pfad-Handle in âp_drvx[]â gemerkt.
Hier hat der Fehlerteufel mal wieder zugeschlagen:
Die Angabe des Laufwerks im Pfadnamen wird so gut wie ignoriert. Der Pfad bezieht sich immer auf das Standardlaufwerk. Beim zuerst ausgefĂŒhrten âd_chkdrvâ wird noch das korrekte Laufwerk benutzt. âd_getdirâ wird aber ein Pfad ohne Kennung ĂŒbergeben, so daĂ es auf dem Standardlaufwerk operiert. Dagegen verblaĂt der andere Fehler geradezu.
Abb. 6: Verwaltung der Standardpfade (Beispiel)
Wenn kein Pfad-Handle mehr verfĂŒgbar ist (tritt praktisch nicht auf) oder der Pfad nicht gefunden wurde (durchaus möglich!), wird mit EPTHNF abgebrochen. Dabei ist aber der alte Pfad schon freigegeben (âpthcntx[]â dekrementiert), und die Referenz auf ihn ĂŒber das Handle aus âp_drvx[]â ist noch gĂŒltig. Dieser Fehler wirkt sich ganz Ă€hnlich wie der unter âDiskettenwechselâ beschriebene aus.
Funktion $39 Dcreate
long Dcreate(char *path)
Ein neues Directory âpathâ wird geschaffen. Eine Datei oder ein Directory gleichen Namens darf nicht schon existieren.
RĂŒckgabewerte:
-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-34L (EPTHNF) Pfad nicht gefunden, Laufwerk existiert nicht, illegaler Dateiname (â.â oder â..â), interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-35L (ENHNDL) keine Datei-Handies mehr (intern wird ein Datei-Handle benötigt)
-36L (EACCDN) Diskette voll, Root Directory voll, Name existiert schon
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
0L alles ok.
Arbeitsweise
âDcreateâ ist im Prinzip ein erweitertes â Fcreate'. Daher wird zuerst das interne âFcreateâ mit dem Dateiattribut $10 fĂŒr Subdirectories aufgerufen. Zur Erinnerung sei hier noch einmal ein Teil der Beschreibung von âFcreateâ wiederholt (s. letzte Folge).
Falls noch nicht vorhanden, wird fĂŒr das Parent Directory des neu zu erzeugenden Directories ein FD angelegt. Die meisten Daten des FD korrespondieren mit entsprechenden des DD und werden direkt ĂŒbernommen. Die DateilĂ€nge (âfd_lenâ) wird auf $7FFFFFFF gesetzt, da die LĂ€nge von Subdirectories nur durch die KapazitĂ€t des Mediums begrenzt ist. âfd_linkâ und âfd_modeâ werden nicht benutzt.
Falls das Subdirectory schon existiert, wird abgebrochen. Nun wird im Parent Directory ein freier Eintrag gesucht und mit Dateiname, Attribut und timestamp initialisiert. Die DateilĂ€nge und der Start-Cluster werden auf Null (letzteres zum Zeichen dafĂŒr, daĂ noch kein Cluster vergeben wurde) gesetzt. AuĂerdem wird dafĂŒr gesorgt, daĂ alle GEMDOS-Sektorpuffer zurĂŒckgeschrieben werden.
Jetzt wird die Datei mit einer der internen Open-Routinen, die auch das Handle generiert, richtig eröffnet (s. âFopenâ, letzte Folge). Hier werden Subdirectories wie jede andere Datei auch behandelt, d.h. das Subdirectory bekommt einen ganz normalen Eintrag im Parent Directory.
Nach dem âFcreate' wird fĂŒr das neue Subdirectory ein DD eingerichtet und in die Child-Liste des Parent Directories eingehĂ€ngt. AuĂerdem wird auch gleich ein FD fĂŒr das Subdirectory angelegt. Wenn fĂŒr eine dieser Operationen nicht genĂŒgend interner Speicher vorhanden ist, wird zwar mit ENSMEM abgebrochen, doch die Subdirectory-Datei bleibt bestehen und geöffnet, insbesondere bleibt das Handle belegt und der Eintrag im Parent Directory bleibt erhalten.
Als nÀchstes ermittelt die FAT-Verwaltung einen freien Cluster und macht ihn zum ersten Cluster des Subdirectories. Bei vollem Speichennedium wird die Directory-Datei korrekt geschlossen, das Subdirectory im Parent Directory gelöscht und es erfolgt eine Fehlermeldung (EACCDN).
Nun werden die ersten zwei EintrĂ€ge im Subdirectory (â.â und generiert. Die RoheintrĂ€ge sind in den globalen GEM-DOS-Variablen zu finden (Tab. 1). Einziger Fehler hierbei: Der timestamp in diesen EintrĂ€gen wird nicht ins Intel-Format konvertiert. Wenn das Parent Directory das Root Directory ist, wird als Start-Cluster von Null eingesetzt (an Stelle der GEMDOS-eigenen negativen Cluster-Nummer).
Zuletzt wird die Subdirectory-Datei korrekt geschlossen, wobei alle Sektorpuffer zurĂŒckgeschrieben werden.
Funktion $3A Ddelete
long Ddelete(char *path)
Das Subdirectory 'path' wird gelöscht. Es muĂ dabei leer sein (bis auf die '.' EintrĂ€ge und â..â).
RĂŒckgabewerte:
-l..-31BIOS-Fehlermeldung bei Diskzugriff
-34L (EPTHNF) Pfad nicht gefunden, Laufwerk existiert nicht, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-36L (EACCDN) Directory nicht leer
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
-65L (EINTRN) interner Fehler (s. âArbeitsweiseâ)
0L alles ok
Arbeitsweise
Zuerst wird mit âd_getdirâ der DD des zu löschenden Subdirectories ermittelt und gegebenenfalls ein FD angelegt. Nun wird ĂŒberprĂŒft, ob das Subdirectory auch tatsĂ€chlich leer ist. Ab dem dritten Eintrag des Subdirectories werden alle EintrĂ€ge, die mit $E5 beginnen (gelöschte Datei), ĂŒberlesen. Wenn danach das Directory-Ende, erkannt an einem mit $00 beginnenden Eintrag oder am Ende der Directory-Datei, erreicht wird, ist das Subdirectory leer. Als nĂ€chstes wird das Subdirectory in der Child-Liste seines Parents gesucht. Dies muĂ eigentlich immer klappen. Es gibt aber eine Sicherheitsabfrage, die EINTRN zurĂŒckgeben soll, wenn der DD des Subdirectory nicht auffindbar ist. Leider ist diese Abfrage falsch programmiert, so daĂ es stattdessen einen Bus Error (2 Bomben) gibt.
Noch zwei weitere FĂ€lle, die eigentlich nicht auftreten dĂŒrfen, werden abgefangen und als EINTRN gemeldet: Es gibt noch offene Dateien im leeren Subdirectory (âdd_fdlâ ungleich NIL), oder das Subdirectory hat selbst noch Subdirectories (âdd_chddâ ungleich NIL). Jetzt wird der DD aus dem DD-Baum ausgehĂ€ngt, DD und FD werden der internen Speicherverwaltung zurĂŒckgegeben und das Subdirectory wird mit einer internen Version von âFdeleteâ wie eine normale Datei gelöscht. Haben Sie schon einmal versucht, das Root Directory zu löschen? Eigentlich ĂŒberflĂŒssig zu erwĂ€hnen, daĂ dies nicht abgefangen wird. âDdeleteâ bedankt sich beim Zugriff auf den Parent-DD mit zwei netten Bömbchen.
Funktion $56 Frename
**long Frename(int dummy, char oldpth, char newpth)
Die Datei âoldpthâ wird in ânewpthâ umbenannt, ânewpthâ muĂ das gleiche Laufwerk bezeichnen, darf jedoch ein anderes Directory spezifizieren. Die Datei kann also nicht zwischen zwei Laufwerken kopiert, aber innerhalb eines Laufwerks verschoben werden. Dabei werden nur Directory-Verweise geĂ€ndert, die Datensektoren der Datei bleiben an ihrem Platz, sodaĂ âFrename â wesentlich schneller als Kopieren und anschlieĂendes Löschen der Datei ist. Darum ist es besonders schade, daĂ man dies vom Desktop aus nicht ausnutzen kann.
âdummyâ ist unbenutzt und sollte Null sein, um KompatibilitĂ€t zu spĂ€teren GEMDOS-Versionen zu wahren.
RĂŒckgabewerte:
-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-33L (EFILNF) Datei nicht gefunden, interner Fehler (zu wenig interner Speicher), illegaler Quelldateiname, Datei ist Subdirectory
-34L (EPTHNF) Quell- oder Zielpfad nicht gefunden, Laufwerk existiert nicht, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-35L (ENHNDL) kein Datei-Handle fĂŒr Datei im Quellpfad
-36L (EACCDN) Zieldatei existiert schon, Datei ist âread onlyâ
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
-48L (ENSAME) unterschiedliche Laufwerke in Quell- und Zielpfad
-65L (EINTRN) interner Fehler (s. âArbeitsweiseâ)
OL alles ok.
Arbeitsweise
Zuerst wird mit âFsfirstâ/âFsnextâ (s. Teil 2) ĂŒberprĂŒft, ob es eine Datei gleichen Namens wie ânewpthâ schon gibt. Dann werden mit âd__getdirâ die DDs von Quell- und Zielpfad bestimmt. Daraufhin werden Quell- und Ziellaufwerk miteinander verglichen, um evtl, mit ENSAME abzubrechen. Die Datei wird mit âFopenâ und Modus 2 (âlesen und schreibenâ) eröffnet, daher erfolgt hier ein Abbruch bei âread only'-Dateien. Wenn Quell- und Zieldirectory gleich sind, wird direkt mit internem âFwriteâ der alte Dateiname im Directory durch den neuen ersetzt.
Bei verschiedenen Directories wird zuerst die Datei im Quelldi-rectory gelöscht, indem $E5 ins erste Zeichen des Dateinamens geschrieben wird. Dann werden Dateiattribut, Start-Cluster, DateilĂ€nge und timestamp gemerkt. Jetzt erst wird mit âFcreateâ und dem alten Dateiattribut ein neuer Dateieintrag im Zieldirectory geschaffen. Die gemerkten Daten werden ins Zieldirectory ĂŒbertragen und Zieldatei und Zieldirectory geschlossen, so daĂ alle Ănderungen hier auf das Speichermedium ĂŒbertragen werden.
In beiden FÀllen wird zum Schluà die Quelldatei geschlossen. Nur wenn hierbei kein Fehler auftritt, wird die Quell-directory-Datei ebenfalls geschlossen. Beim Umbenennen zwischen Ordnern ist vor allem die Reihenfolge der Aktionen zu beachten. Wenn beim Anlegen des neuen Eintrags etwas schiefgeht (z.B. BIOS-Fehler beim Zugriff auf Diskette), ist die Datei im Quelldirectory schon gelöscht!
Schlimmer noch, daĂ beim âFcreateâ fĂŒr die Zieldatei Fehler nicht erkannt werden, was zu Bombenhagel fĂŒhren muĂ. Und zu diesem Zeitpunkt ist die Datei ebenfalls schon gelöscht... Mögliche Fehler sind z.B. âDiskette voll", âRoot Directory vollâ, âkeine Datei-Handles mehrâ. Auch die Abfrage zu Beginn, ob der Zielname schon existiert, greift nicht immer. âFsfirstâ wird nĂ€mlich mit dem Suchattribut Null aufgerufen. Deshalb werden âversteckte Dateienâ (âhidden-Bit' oder âsystem-Bitâ im Dateiattribut gesetzt) nicht bemerkt. Das spĂ€tere âFcreateâ löscht nun rĂŒcksichtslos die versteckte Datei, um den neuen Eintrag fĂŒr die umzubenennende Datei zu schaffen.
NĂ€chsten Monat geht es dann weiter mit Teil 2 der Directory-Verwaltung.