Auf der Schwelle zum Licht: Directory-Verwaltung Teil 2

Heute geht es um die Anwendung der im ersten Teil besprochenen Grundlagen der Directory-Verwaltung. Hauptsächlich handelt es sich dabei um das Suchen (und hoffentlich auch Finden) von Dateien, einem der Kernstücke von GEMDOS. Für diese Aufgabe werden die wichtigen Routinen ‘d srcname’ und ‘d getdir' (Namen sind wie üblich frei erfunden, Ähnlichkeiten mit lebenden Routinen wären rein zufällig) benötigt, die schon im ersten Teil erwähnt wurden. Bevor wir ans Eingemachte gehen, möchte ich aber noch einige allgemeine Dinge vorausschicken.

Ein Name - zwei Teile

GEMDOS-Dateinamen bestehen aus zwei Teilen: Der Hauptname besteht aus bis zu 8 Zeichen, die Namenserweiterung (Extender) aus bis zu 3 Zeichen. Bei dem dem Benutzer bekannten Namensformat sind beide Teile durch einen V getrennt. Fehlt der Extender, so kann der ‘. ’ weggelassen werden (GEMDOS selbst macht dies bei “Rückgabe” von Dateinamen wie bei ‘Dgetpath' und ‘Fsfirst’/’Fsnext’ immer).

Das GEMDOS-interne Format kennt nur genau 11 Zeichen lange Dateinamen. Zu kurze Namensteile werden mit Leerzeichen auf die 8 bzw. 3 Zeichen aufgefüllt. Daher ist auch kein Trennzeichen zwischen den beiden Teilen notwendig. Ein Beispiel: Die Datei ‘DATEI.C’ wird im internen Format durch ‘DATEI C ‘ repräsentiert.

Ersetzungszeichen

Bei der Spezifikation von Dateinamen, nach denen gesucht werden soll, können die Ersetzungszeichen (Wildcards) '*’ und '?’ verwendet werden.

Ein '?' zeigt an, daß an seiner Stelle ein beliebiges Zeichen stehen darf. So wird bei einem Suchnamen ‘H?LLO' sowohl die Datei ‘HALLO' als auch ‘HELLO’ gefunden.

Bei einem '' sind alle weiteren Zeichen bis zum Ende des Namensteils, in dem das '' vorkommt, beliebig. Der Suchname ‘H*.C’ läßt GEMDOS z.B. 'HALLO.C’ oder ‘HXXX.C’, nicht aber ‘HALLO.S’ finden.

Weitere Zeichen nach dem '*' (bis zum Ende des Namensteils) werden ignoriert.

Intern wird übrigens das '*' in '?' umgewandelt, indem der betreffende Namensteil bis zu seiner maximalen Länge von 8 bzw. 3 Zeichen mit ‘?' aufgefüllt wird.

Die Ersetzungszeichen können immer dann verwendet werden, wenn nach einer schon vorhandenen Datei gesucht werden soll (z.B. bei ‘Fopen’ oder dem Quellnamen von ‘Frename’). Alle GEMDOS-Funktionen außer ‘Fsfirst’/’Fsnext’ arbeiten jedoch immer nur auf einer Datei. Dies ist stets die im Directory am “weitesten vorne”, also bei niedriger Dateiposition stehende.

Wenn alle Dateien, die das Suchkriterium erfüllen, angesprochen werden sollen, so muß dies entweder durch wiederholte Anwendung (z.B. bei ‘Fdelete’) oder durch ‘Fsfirst’/’Fsnext’ (s.u.) geschehen. Bei Dateipfaden darf nur der eigentliche Dateiname Ersetzungszeichen enthalten. Die Directories müssen immer vollständig angegeben werden.

Dateiattribut

Bisher habe ich mich immer um das Dateiattribut gedrückt, das schon des öfteren erwähnt wurde. Aber das dürfte wohl ganz im Sinne des GEMDOS liegen, da es sich auch nicht sonderlich darum kümmert. Das Attribut ist nämlich zu weit weniger zu gebrauchen, als es im ersten Moment scheint. Jede in einem Directory verzeichnete Datei hat also ein sogenanntes Dateiattribut, durch das ihre Verwendung und bestimmte Eigenschaften charakterisiert werden. Das Attribut wird durch einen Byte-Wert repräsentiert, wobei bestimmten Bits eine Eigenschaft zugewiesen wird. In einigen Fällen ist ihre Kombination möglich. Die Bedeutung dieser Bits ist in Tab. 1 zusammengestellt.

Der ‘read only’-Modus soll ein softwaremäßiger Schreibschutz einzelner Dateien sein. Er verhindert das Öffnen einer Datei mit ‘Fopen’ im Modus “schreiben” oder "lesen und schreiben”. Weiterhin wird er bei ‘Fdelete’ und ‘Fcreate’ berücksichtigt.

'Fwrite’ kümmert sich jedoch herzlich wenig um den Zugriffsmodus, so daß nur zum Lesen geöffnete Dateien trotzdem beschrieben werden können (s. Juni-Ausgabe).

Versteckte Dateien (‘hidden’ oder ‘System’) sollten normalerweise bei der Anzeige von Directories nicht erscheinen. Der Desktop berücksichtigt dies sogar, d.h. versteckte Dateien werden einfach nicht angezeigt (s. auch ‘Suchattribut’). Der Begriff “System-Datei” hat weiter keine tiefschürfende Bedeutung, er klingt geheimnisvoller als er ist.

Das Diskettennamen-Bit kennzeichnet einen besonderen Eintrag, der für GEMDOS keine spezielle Bedeutung hat und a1s Diskettenname verwendet werden kann. Er ist in Directories normalerweise unsichtbar. Er wird im allgemeinen beim Formatieren festgelegt und vom Desktop beim “Disk-Info” angezeigt.

Das Ordner-Bit zeigt an, daß es sich bei er Datei um ein Directory handelt. Da Directories in einigen Punkten anders behandelt werden, sollte man es tunlichst unterlassen, mit ‘Fattrib’ eine “normale” Datei in einen Ordner umzuwandeln oder umgekehrt (GEMDOS fängt dies nicht ab!). Gleiches gilt auch für den Diskettennamen.

Das Archiv-Bit hat unter GEMDOS keinerlei Funktion. Normalerweise sollte es dazu dienen, veränderte Dateien zu erkennen. Backup-Programme oder ähnliches würden bei jedem Backup das Bit (bei den Quelldateien) löschen. Wenn die Datei verändert wird, müßte es gesetzt werden. Beim nächsten Backup könnte man erkennen, ob die Datei inzwischen verändert oder neu angelegt wurde.

Ein paar Worte zu den Kombinationsmöglichkeiten der einzelnen Bits sind vielleicht auch ganz angebracht. Bei Diskettennamen und Ordnern werden alle anderen Bits ignoriert, was ihre Funktion angeht. Allerdings dürfen Schreibschutz-und Archiv-Bit trotzdem nicht gesetzt sein, da Ordner sonst nicht nur als Ordner sondern auch als normale Dateien gefunden werden, was zu Konflikten führen kann.

Atari-Dokumentationen verlangen sogar, daß bei Directories und Diskettennamen keinerlei weitere Bits gesetzt werden. Bei versteckten Dateien dürfen zwar das Schreibschutz- und das Archiv-Bit gesetzt sein, doch sind dann die Dateien nicht mehr unsichtbar! System- und Ver-steckt-Bit dürfen miteinander kombiniert werden. Dann wird die Datei sowohl als System- als auch als versteckte Datei gefunden. Sie sehen also, daß das Dateiattribut keine allzu aufregende Sache ist.

Bit Bedeutung, wenn gesetzt engl. Kurzbezeichnung
0 Datei ist schreibgeschützt read only
1 Datei ist versteckt hidden
2 Datei ist (versteckte) Systemdatei System
3 Dateieintrag ist der Diskettenname volume label
4 Datei ist ein Ordner subdirectory
5 Datei ist “archiviert” archive
6,7 reserviert (sollten gelöscht sein)

Tab. 1: Das Dateiattribut

Suchattribute

Die Directory-Suchfunktionen bekommen ein “Suchattribut” übergeben, das festlegt, unter welchen Bedingungen Dateien als gefunden gemeldet werden. Intern verwendet GEMDOS allerdings ein Suchattribut etwas anderer Form. Es wird von der Suchroutine "d_srcname" wie folgt ausgewertet:

Der Programmierer hat mit Suchattributen nur bei ‘Fsfirst’ zu tun. Das dort übergebene Suchattribut wird in das interne Suchattribut umgewandelt, indem die Bits 0 und 5 gesetzt werden, außer wenn das Suchattribut 8 ist. In diesem Fall ist das interne Suchattribut ebenfalls 8. Dies führt zu folgender Ergänzung:

Diese Suchregeln sind allerdings nicht immer die vernünftigsten. Ein paar Beispiele für das, was so alles gefunden wird.

Suchattribut 0:

Suchattribut $08:

Suchattribut $10:

Suchattribut $11:

Suchattribut $02:

Ungünstig ist vor allem die Mixtur aus verschiedenen Arten von Dateien (normal, Directory, Diskettenname, Systemdatei) und ihren Eigenschaften (Schreibschutz, versteckt, archiviert). Hier gäbe es sinnvollere Regeln, die es erlauben würden, gezielt nach einzelnen Dateitypen mit bestimmten Eigenschaften zu suchen. Im allgemeinen wird man daher mit Suchattribut 0 oder $ 10 arbeiten.

Interessant ist es noch zu wissen, mit welchen Suchattributen die verschiedenen Teile des TOS arbeiten. GEMDOS verwendet bei allen Dateifunktionen, die ‘d_srcname’ benutzen (z.B. ‘Fopen’ usw.), das Suchattribut $27. Damit werden sämtliche “normalen’' Dateien gefunden. Es ist daher meistens möglich, lästige Zusatzdateien zu “verstecken”. Das räumt so manches Directory auf. Ein gutes Beispiel ist der Auto-Boot-Harddisk-Treiber “SH204DVR.SYS”, den man so elegant "loswerden” kann. Bei der Suche nach Directories benutzt GEMDOS Suchattribut $10, wie nicht anders zu erwarten.

‘Frename’ überprüft mit ‘Fsfirst’, ob der gewünschte Zielname schon existiert. Dabei wird Suchattribut 0 verwendet, was zu Problemen führt (s. ‘Frename’ in Teil 1).

Das AES sucht Accessories mit ‘Fsfirst’ und Suchattribut 0. “Versteckte” Accessories werden daher nicht gefunden.

Das DESKTOP.INF dagegen wird mit ‘Fopen’ angesprochen und kann daher “versteckt” werden (und wieder wird ein Directory etwas übersichtlicher).

Beim Suchen von Resource Dateien wird Suchattribut 5 genommen (findet auch “Systen""-Dateien). Also können auch die oft lästigen Resources unsichtbar gemacht werden.

Beim Arbeiten mit Command Line-Interpretern gibt es manchmal eine Option, wirklich alle (also auch die versteckten) Dateien aufzulisten. Der Desktop kann dies nicht, daher ist es zu empfehlen, das Verstecken auf wenige Directories zu beschränken (z.B. auf die Root-Directo-ries der Harddisk), damit man nicht den Überblick verliert.

Ermitteln eines Directories mit d_getdir

Kommen wir nun zur Routine ‘d_getdir’, die von den meisten GEMDOS-TRAP1-Funktionen benutzt wird. Sie ermittelt aus einem Dateipfad den DD des zugehörigen Directories und eventuell den aus dem Pfad isolierten eigentlichen Dateinamen.

Der grobe Ablauf ist wie folgt: Der Pfadname wird in einzelne Directory-Namen zerlegt, wobei sich GEMDOS im DD-Baum von den oberen zu den unteren Ebenen hangelt. Doch nun zu den Details.

Zuerst wird das Start-Directory bestimmt, auf das sich der restliche Pfad bezieht. Dazu wird als allererstes das Laufwerk festgelegt. Wenn es im Dateipfad nicht explizit angegeben ist, wird das aktuelle Laufwerk (‘pdefdrv’ im PD) ausgewählt. Wie im Teil 1 beschrieben, übernimmt ‘d_chkdrv’ die Überprüfung des Laufwerks und sorgt für die Existenz eines Standardpfades. Beginnt der Pfad mit einem ‘', so ist das Root-Directory das Start-Directory, ansonsten das Ende des Standardpfads. Das so festgelegte Start-Directory ist ersteinmal das “aktuelle Directory”.

Nun wird die nächste Komponente des Pfades extrahiert und in das GEMDOS-interne Format konvertiert. Die Spezial-Dateinamen '.' und '..' werden hier separat ausgewertet. ‘.’ wird ignoriert, bei '..' wird das Parent-Directory des aktuellen Directories über den ‘dd_pardd’-Zeiger ermittelt. Unschönerweise erfolgt hier keine Sicherheitsabfrage ob man sich nicht schon im Root-Directory befindet. Da dessen ‘dd_pardd’ NIL ist, gibt es gleich darauf einen Bus-Error.

Ansonsten muß das gewünschte Subdirectory im aktuellen Directory gesucht werden. Wenn GEMDOS in seinem “DD-Baum” (s. Teil 1) hier noch überhaupt kein Child kennt, muß auf jeden Fall im Directory selbst gesucht werden. Dies erledigt ‘d_srcname’ (s.u.). Wenn im aktuellen Directory kein weiteres Directory ist, ist die Suche auf jeden Fall fehlgeschlagen und wird abgebrochen.

Wenn schon mindestens ein Child bekannt ist oder von ‘d_srcname’ eins gefunden wurde, wird die Child-Liste des DD durchsucht. Falls das gewünschte Directory hier nicht gefunden werden kann, besteht noch die Möglichkeit, daß noch nicht alle Child-Directories in der Child-Liste eingetragen sind. Dies wird am ‘fd_dirch’-Flag des Directory-FD erkannt (s.u.). In diesem Fall wird nochmals ‘d srcname’ aufgerufen. Schlägt dies auch fehl, so existiert das Directory nicht und es wird abgebrochen.

Nun wird das ermittelte Child zum aktuellen Directory und das Spiel beginnt von vorne, bis der ganze Pfad durchgearbeitet ist.

Je nach Aufruf (bestimmt durch ein Flag) wird dabei der ganze String (‘Dsetpath’) oder alles bis auf den letzten Namen (‘Fopen’) als Pfadname angesehen. Im zweiten Fall wird der letzte Name als Dateiname interpretiert und der übergeordneten Routine zur Verfügung gestellt.

Letztendlich erhält man also den DD des gewünschten Directories am Pfadende, wobei alle Directories auf dem Weg dorthin automatisch im DD-Baum angelegt werden.

Suchen von Dateien mit d_srcname

Doch nun zur internen Funktion ‘d_srcname’, die die Drecksarbeit, sprich das Suchen im Directory, erledigt und so nebenbei den DD-Baum aufbaut. ‘d_srcname’ wird für alle Sucharbeiten (Dateinamen, Directories oder freie Einträge) verwendet und bekommt daher auch das “interne Suchattribut” übergeben.

Da hier mit den üblichen Dateioperationen auf das Directory zugegriffen wird, muß ersteinmal ein FD für das Directory erzeugt werden, falls noch keiner vorhanden ist.

‘d_srcname’ kann eine Anfangs-Dateiposition übergeben werden, ab der gesucht werden soll. Dies ist wichtig, wenn erst ab einer bestimmten Stelle gesucht werden soll (wie bei ‘Fsnext’).

‘d_getdir’ läßt dagegen immer ab der letzten Suchposition (‘dd_lpos’) suchen, da hier ja nur die Child-Liste verlängert werden soll, und ‘dd_lpos’ gerade angibt, bis wohin die Subdirectories schon erfaßt wurden.

Das Directory wird nun ab dieser Anfangsposition Eintrag für Eintrag gelesen, bis das Ende erreicht wird oder bis die gesuchte Datei gefunden wurde. Das Ende eines Directories wird von GEMDOS nicht nur an seiner tatsächlichen Länge in Sektoren bzw. Clustern erkannt. Schon der erste Eintrag, der mit einem Nullbyte beginnt, führt bei Directory-Suchoperationen zum Abbruch. Dies spart vor allem beim Root-Directory erheblich an Zeit.

Zuerst wird überprüft, ob der gerade untersuchte Eintrag ein geeigneter Kandidat für die automatische Aufnahme in den DD-Baum ist. Dazu muß es sich natürlich um einen Directory-Namen (außer '.' und '..’) handeln. Außerdem muß die aktuelle Dateiposition hinter ‘dd_lpos’ liegen, da das Directory sonst schon im Baum vorhanden ist. Desweiteren wird überprüft, ob nicht überhaupt schon früher einmal das gesamte Directory durchsucht wurde (‘fd_dirch’-Flag gesetzt, s.u.). Wenn nach einem Directory (nicht nach einer normalen Datei) gesucht wird, soll eine letzte Abfrage nochmals sicherstellen, daß der Ordner nicht schon in der Child-Liste vorhanden ist.

Nachdem alle Klippen überwunden sind und ‘d_srcname’ der Meinung ist, das Directory müsse in die Child-Liste des durchsuchten Directories aufgenommen werden, wird ein DD eingerichtet. Die benötigten Daten werden dem Directory-Eintrag bzw. dem DD des Parents entnommen. Der neue DD wird immer vorne in die Child-Liste eingehängt.

Nun erfolgt der Vergleich des Directory-Eintrags mit den Suchkriterien. Eine Datei gilt als gefunden, wenn ihr Name unter Berücksichtigung der Ersetzungszeichen und ihr Attribut im Rahmen der Suchregeln übereinstimmt.

Bei der Suche nach einem freien Eintrag (z.B. von ‘Fcreate’ aus) wird ‘d_srcname’ fündig, wenn es auf $00 oder $E5 im ersten Byte des Dateinamens stößt.

Nach Abbruch der Suche (egal ob erfolgreich oder nicht) wird ‘dd_lpos’ auf den neuesten Stand gebracht, aber nur, wenn ‘d_srcname’ von 'd_ getdir' aus aufgerufen wurde. Dies führt zu einem fatalen Fehler, wie unten noch beschrieben wird.

Wenn die gewünschte Datei nicht gefunden werden konnte und auch nicht gerade nach einem freien Eintrag gesucht wurde, so wird das Bit 1 von ‘fd_dirch’ im FD des Suchdirectories gesetzt. Es zeigt somit an, daß das Directory einmal komplett gelesen wurde und dieser Zweig des DD-Baums vollständig aufgebaut ist.

Je nach Aufruf von ‘d_srcname’ wird ein Zeiger auf den Directory-Eintrag der gefundenen Datei oder einer auf den DD des gefundenen oder in die DD-Liste aufgenommenen Directories (NIL zeigt Fehlschlag an) zurückgegeben.

Fehler in der Directory-Verwaltung

Diese Routine beinhaltet einen der schwerwiegendsten Fehler des ganzen GEMDOS. Er kann dazu führen, daß ein Directory mehrmals in die Child-Liste seines Parents aufgenommen wird.

Zum einen führt dies dazu, daß zuviel “interner Speicher” für die DDs und eventuell deren FDs verbraucht wird. Dadurch findet eine zusätzliche Begrenzung der ansprechbaren Ordner statt (das berühmtberüchtigte “40-Ordner-Problem”). In vielen Fällen tritt dieses Problem überhaupt nur auf Grund dieses Fehlers auf und nicht, weil zu viele Ordner geöffnet wurden o.ä!

Es sind aber noch weitere merkwürdige Effekte möglich. Es kann passieren, daß sich ein Directory nicht mehr löschen läßt, obwohl es keine Dateien mehr enthält, denn beim ‘Ddelete’ wird überprüft, ob die Child-Liste des zu löschenden Directories auch wirklich leer ist (s. Teil 1).

Doch nun zur Ursache des Fehlers. Beim Aufruf von ‘d_srcname’ für “normale” Suchoperationen (wie etwa von ‘Fopen’, ‘Fcreate’ usw.) werden ebenfalls “zufällig” entdeckte Directories in den DD-Baum integriert. Im Gegensatz zu der von ‘d_getdir’ initiierten Suche wird aber ‘dd_lpos’ nicht auf die Endposition der Suche gesetzt, so daß GEMDOS bei der nächsten Suchoperation nicht merkt, daß das Directory schon in der Child-Liste hängt und es erneut einfügt.

Warum, so werden Sie sich vielleicht fragen, merkt man dann normalerweise nicht viel von diesem Fehler? Er müßte sich doch eigentlich ständig bemerkbar machen.

Dies liegt daran, daß die Child-Liste nicht mehr erweitert wird, wenn das ‘fd_dirch’-Bit 1 gesetzt ist. Das ist aber der Fall, sobald das Directory einmal komplett durchsucht wurde, weil eine Suchoperation fehlgeschlagen ist. Das ist aber genau das, was beim Arbeiten mit dem Desktop ständig passiert. Meistens macht man einen Ordner auf, bevor man mit ihm arbeitet. Dabei liest der Desktop das Directory komplett, um es im Fenster darzustellen.

Auch bei Command Line-Interpretem listet man die meisten Directories irgendwann einmal, oft ebenfalls bevor man sie anspricht.

Gefährlich wird es bei Programmen, die in einem Directory ständig ganz bestimmte Dateien bearbeiten. Da dabei i.allg. keine Suchoperation fehlschlägt, weil das Programm ja “gezielt” sucht, geht bei jedem Datei-Offnen interner Speicher verloren. Da dieser Effekt keine Begrenzung kennt, kommt es irgendwann zum Zusammenbruch der internen Speicherverwaltung, da hilft dann kein noch so großzügiges “Mehr-Ordner-Programm”.

Ein schönes Beispiel findet sich in Listing 1. Vor Ablauf des Programms sind allerdings einige Vorbereitungen notwendig. Legen Sie in dem Directory, in dem sich das Programm befindet, einen Ordner “TEST.ORD” an. Öffnen Sie ihn nun und erzeugen Sie zuerst einen weiteren Ordner mit beliebigem Namen. Außerdem muß danach irgendeine Datei in den Ordner "TEST.ORD" kopiert und in “TEST” umbenannt werden. Initialisieren Sie das System mittels RESET-Knopf neu und starten Sie das Demo-Programm, ohne vorher "TEST.ORD” geöffnet zu haben. Nach einiger Zeit meldet das Programm einen Fehler und gibt an, wie oft es die Datei “TEST” öffnen und wieder schließen konnte. Dieser Wert hängt vom verwendeten TOS (“altes TOS” oder “Blitter-TOS”) und eventuell vorhandenen "Mehr-Ordner-Programmen” ab.

Nun ist der GEMDOS-Speicher fast restlos aufgebraucht, und Sie werden dies am merkwürdigen Verhalten des Desktops bemerken. Ordner sind scheinbar leer, es gibt ungewohnte Fehlermeldungen bei harmlosen Operationen, Abstürze oder einen selbstausgelösten RESET.

Warnung: Führen Sie keinesfalls irgendwelche Schreibzugriffe durch (wie Kopieren), da sonst die Gefahr von ungewollten Datenverlusten besteht.

Am besten machen Sie einen RESET, dann normalisiert sich wieder alles. Wenn “TEST.ORD" vor dem Start des Demos einmal geöffnet wurde, geht alles gut und das Programm bricht ohne Fehler nach 1000 ‘Fopen’-’Fclose’-Sequenzen ab.

Die “Disk Transfer Area” (DTA)

GEMDOS stellt dem Programmierer zwei Funktionen zur Verfügung, mit denen ein Directory nach bestimmten Einträgen durchsucht werden kann. Im einfachsten Fall dienen sie dem Einlesen des kompletten Directories, was z.B. der Desktop macht, um es dann in einem Fenster darzustellen.

‘Fsfirst’ führt dabei die erste Suche aus, die am Anfang des Directories beginnt, und hinterlegt die im Directory gespeicherten Informationen über die gefundene Datei in der DTA.

‘Fsnext’ ist in der Lage, ab der Stelle weiterzusuchen, wo ‘Fsfirst’ bzw. ein vorheriges ‘Fsnext’ aufgehört hat. Durch wiederholte Aufrufe ist es damit möglich, ein Directory komplett nach Dateien, die bestimmten Kriterien genügen, zu durchsuchen.

Die DTA enthält daher auch alle Daten, die GEMDOS braucht, um die Suche am alten Abbruch-Punkt aufsetzen zu lassen. Speicherplatz für die DTA kann vom Anwender selbst bereitgestellt werden (s. ‘Fsetdta’ und ‘Fgetdta’). Eine genaue Beschreibung dieser Funktionen findet sich im Abschnitt “GEMDOS-Funktionen”.

Hier geht es in erster Linie um den Aufbau der DTA (Abb. 1). Beginnen wir hinten, ja dort die für den Programmierer wichtigeren Daten abgelegt sind.

tvpedef struct
{ char dta_sname[12];	/* Suchname (von Fsfirst) */ 
	char dta_sattr;		/* Suchattribut (von Fsfirst) */
	char dta_dpos[4];	/* letzte Suchposition im Suchdirectory */
	char dta_dd[4];		/* Zeiger auf DD des Suchdirectories */
	char dta_attr;		/* gefundenes Attribut */
	unsigned int dta_time; /* gefundene Zeit */ 
	unsigned int dta_date; /* gefundenes Datum */ 
	long dta len;		/* gefundene Laenge */
	char dta_name[14];	/* gefundener Name */
} DTA;

Abb. 1: Struktur der “Disk Transfer Area” (DTA)

'dta_name’ ist der Name der gefundenen Datei in der dem Anwender bekannten Darstellung (nicht im internen Format). dta_name’ ist mit 14 Zeichen großzügig dimensioniert (13 hätten auch gelangt). Desweiteren findet man hier die Dateilänge in Bytes (‘dta_len’), Erstellungszeit dta_time’) und -datum (‘dta_date’) sowie das Dateiattribut (‘dta_attr') der gefundenen Datei. Diese Komponenten sind alle von Atari dokumentiert, anders wäre die Nutzung von 'Fsfirst’/’Fsnext’ wohl auch kaum möglich.

Der Rest der DTA ist offiziell als “reserviert für interne Zwecke” ausgewiesen, daher kann (und nach neuesten Informationen soll) sich die Belegung in späteren TOS-Versionen durchaus ändern. Man sollte daher die Kenntnis hiervon nicht beim Programmieren ausnutzen.

Der bei ‘Fsfirst’ übergebene Suchname (abgetrennt vom zugehörigen Suchpfad), wird in ‘dta_sname’ abgelegt. Dieser Name liegt im GEMDOS-internen Format vor. Das bei ‘Fsfirst’ festgelegte Suchattribut wird in ‘dta_attr’ aufbewahrt. ‘dta_dd’ zeigt auf den DD des Directories, in dem die Suche stattfindet.

Die aktuelle Dateiposition der Suche ist in dta_dpos’ zu finden. ‘dta_dpos’ zeigt dabei auf den Eintrag nach dem zuletzt gefundenen, also auf den, bei dem die nächste Suche beginnen soll.

Bei ‘dta_dd’ und ‘dta_dpos’ fällt die merkwürdige Definition als “char-Array” in der Struktur auf. Etwas besseres fiel mir nicht ein, da es sich offensichtlich um 32-Bit-Werte auf ungeraden Adressen handelt. So etwas erzeugt kein C-Compiler, daher muß die Original-Definition von Digital Research wohl ähnlich merkwürdig aussehen. Der Zugriff auf diesen Teil der DTA geschieht ohnehin nur mit Byte-Kopierroutinen.

Das Problem wäre bei anderer Anordnung der Komponenten oder Verkürzung von ‘dta_sname’ (11 Zeichen reichen auch) nicht aufgetreten.

Mit Hilfe von ‘dta_dd’ kommt man übrigens an die internen Datenstrukturen der Dateiverwaltung heran. Hier findet sich nämlich unter anderem ein Zeiger auf den DMD, von dem aus alle DDs und FDs erreichbar sind. Dies kann als eine “halblegale” Methode angesehen werden, da zwar nicht die Kenntnis von veränderlichen Systemadressen, aber die der internen Strukturen vorausgesetzt wird.

Der DMD ist auch über die Sektorpuffer-Listen (‘bufT) erreichbar, doch kann man oft nicht wissen, ob ein solcher Puffer für das gewünschte Laufwerk existiert.

GEMDOS-Funktionen (TRAP #1)

Hier sind die vier GEMDOS-Funktionen zusammengestellt, die mit Suchoperationen zu tun haben.

Funktion $1A Fsetdta

void Fsetdta(DTA *new_dta)

Die Adresse der aktiven DTA wird auf ‘new_dta’ gesetzt. Die DTA wird von ‘Fsfirst’/’Fsnext’ benutzt. Vor dem Start jedes Prozesses wird eine Default-DTA definiert. Sie liegt bei der aktuellen GEMDOS-Version im Prozeßdescriptor unmittelbar am Beginn der Kommandozeile.

In vielen Fällen muß man daher keinen eigenen Speicherbereich mit ‘Fsetdta’ zur Verfügung stellen. Man muß aber Konflikte mit der Kommandozeile berücksichtigen. Das bedeutet, die Kommandozeile muß vordem ersten ‘Fsfirst’ ausgewertet sein, wenn keine eigene DTA verwendet wird.

Arbeitsweise

Hierbei handelt es sich um einen ‘C’-Einzeiler. ‘new_dta’ wird direkt in den PD des aktiven Prozesses nach ‘p_dta’ übertragen.

Funktion $2F Fgetdta

DTA *Fgetdta()

Die Adresse der aktiven DTA wird zurückgegeben. Es ist immer eine DTA definiert (s. ‘Fsetdta’).

Arbeitsweise
Noch ein Einzeiler: Der Rückgabewert ist einfach ‘p_dta’ des aktiven Prozesses.

Funktion $4E Fsfirst

long Fsfirst(char *path, int attr)

Das durch den Directory-Anteil von ‘path’ ausgewählte Directory wird nach Dateien durchsucht, deren Namen durch den von ‘path’ abgetrennten Suchnamen und das Suchattribut ‘attr’ bestimmt werden.

Beispiel: Bei einem ‘path’ von ‘ORDNERVLDOC’ wird im Directory ‘ORDNER’, das sich im aktuellen Directory des Standardlaufwerks befindet, nach allen Dateien gesucht, die die Endung ‘.DOC’ haben, und mit dem Suchattribut vereinbar sind.

Die Suche wird beim Anfang des Directories begonnen und bricht beim ersten die Suchanfrage erfüllenden Dateinamen ab. Sie kann mit ‘Fsnext’ fortgeführt werden. Der Suchname kann die üblichen Ersetzungszeichen ‘?’ und '' enthalten, insbesondere sucht ‘.*’ nach allem. Das Suchattribut ist oben schon erklärt worden.

Rückgabewerte:

-1 ..-31 BIOS-Fehlermeldung bei Diskzugriff

-33L (EFILNF) Pfad oder keine Datei gefunden, Laufwerk existiert nicht, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)

0L alles ok.

Arbeitsweise

Das Suchattribut wird in das interne Suchattribut konvertiert, wie oben beschrieben.

‘d_getdir’ ermittelt das Suchdirectory und mit ‘d_srcname’ wird ab dem Directory-Anfang wie gewünscht gesucht. Wenn eine Datei gefundenen wurde, die die Suchanfrage erfüllt, wird die DTA komplett gefüllt (Teile für Anwender und interne Zwecke).

Funktion $4F Fsnext

lang Fsnext()

Eine vorher mit ‘Fsfirst’ begonnene Suche wird fortgesetzt. Die Suche wird ab dem Eintrag fortgeführt, der dem letzten gefundenen folgt. ‘Fsnext’ ist mehrmals hintereinander anwendbar, bis das ganze Directory durchsucht ist.

Andere Dateioperationen “zwischendurch” sind durchaus erlaubt. Fatal ist dagegen ein Mediumwechsel. Er sorgt für die Freigabe aller internen Strukturen. Bei ‘Fsnext’ wird auf diese zugegriffen, obwohl sie nicht mehr definiert sind. Dies kann zur Suche in einem ganz anderen Directory oder zum Absturz führen. Ebenfalls tödlich ist ein ‘Fsnext’ ohne vorhergehendes ‘Fsfirst’, da die DTA dann Undefiniert ist.

Rückgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff

-49L (ENMFIL) keine Datei gefunden, interner Fehler (zu wenig interner Speicher)

0L alles ok.

Arbeitsweise

Mit den in der DTA gespeicherten Daten wird ‘d_srcname’ aufgerufen. Wenn die Suche erfolgreich war, wird der Anwender-Teil der DTA und ‘dta_dpos’ neu gesetzt.

Anwendungsbespiel

Ein Beispiel für die Verwendung dieser vier GEMDOS-Funktionen ist in Listing 2 gegeben.

Die Funktion ‘search’ sucht in einem bestimmten Directory nach sämtlichen Dateien (außer dem Diskettennamen). Dabei werden auch alle Subdirectories sowie deren Subdirectories usw. durchsucht. Bei jeder gefundenen Datei wird ‘found’ aufgerufen, das in diesem Beispiel nichts weiter macht, als den Pfad und das Dateiattribut der gewünschten Datei auszugeben. Aufgerufen wird ‘search’ von ‘main’, wobei das aktuelle Directory als zu durchsuchendes festgelegt wird. Die Einzelheiten können dem Listing entnommen werden.

Mit Vorsicht ist dieses Programm im Root-Directory einer Harddisk anzuwenden. Da dabei sämtliche Ordner geöffnet werden, tritt ein erheblicher Verbrauch an “internem Speicher” auf, womit wir wieder beim “40-Ordner-Problem” angelangt wären.

Ausblick

Das Gröbste hätten wir nun hinter uns. Zur Datei- und Directory-Verwaltung wissen Sie nun alles wesentliche. Was noch bleibt, sind vor allem die I/O-Umlei-tung und die I/O-Funktionen für die zeichenorientierten Geräte, womit wir uns als nächstes beschäftigen werden.

/* Demo-Programm f .TOS-Fehler d. Directory-Verwaltung */

#include <osbind.h>

main ()
{	int err; 
	int i;

/* maximal 1000 Durchläufe oder Abbruch durch Fehler */ 
	for (i=0,err=0; err==0 && i<1000; i++)
	{
		err = Fopen("test.ord\\test", 0); 
		if (err > 0)
			err = Fclose(err);
	}
	printf("Abbruch nach %d Durchläufen mit Fehler %d.\n",i,err);

	Cnecin();
}

Listing 1: Demo-Programm für TOS-Fehler

/* Rekursives Durchsuchen eines Directories 
   by A.Esser 
   13.6.1988
*/

#include <osbind.h>

#define E_OK	0
#define ERROR	1
#define MAX_RECUR	10	/* max. Rekursionstiefe */

typedef struct 
{	char dta_reserved[21]; 
	char dta_attr; 
	unsigned int dta_time; 
	unsigned int dta_date; 
	long dta_len; 
	char dta_name [14];
} DTA;

/* Rekursiv durch Directory-Hierarchie durcharbeiten */ 
/* 'path' ist Start-Pfad (ohne abschliePendes '\') */
/* Erst-Aufruf erfolgt mit 'level'=0 */ 
int search(level,path) 
int level;	/* Rekursionstiefe */
char *path;
{	DTA dta;	/* lokale DTA */
	DTA *olddta; /* DTA-Adresse der höheren Ebene */ 
	int err;
	char *name; /* Zeiger auf Dateinamen in 'path' */

	olddta = (DTA *)Fgetdta(); /* alte DTA-Adresse merken */
	Fsetdta(&dta); /* lokale DTA definieren */
	name = path + strlen (path) + 1;
	strcpy(name-1,"\\*.*") ; /* alles suchen */
	err = Fsfirst(path,0x37);
	while (err == E_OK) /* suchen bis nichts mehr da oder Fehler */
	{
		strcpy(name,dta.dta_name); /* gefundenen Namen an Pfad anhängen */ 
		found(path,&dta); /* gewünschte Aktion ausführen */
		if (dta.dta_attr & 0x10 && *name != '.')
			if (level < MAX_RECUR) /* ab in die nächste Ebene */ 
				err = search(level+1,path); 
			else
				err = ERROR; /* Rekursion zu tief:Abbruch */ 
			if (err == E_OK)
				err = Fsnext(); /* weiter suchen wenn kein Abbruch */
	}
	Fsetdta(olddta); /* DTA der letzten Ebene zurück */ 
	if (err < 0) /* keine Datei mehr gefunden */
		err =0; /* weiter in letzter Ebene */
	return err; /* sonst Abbruch */
}

/* Aufruf bei jeder gefundenen Datei einschl. Ordner */ 
/* Beispiel: Ausgabe des Pfades und des Attributs */ 
found(path,dta) 
char *path;
DTA *dta;
{
	printf("$%02x %s\n",dta->dta_attr,path);
}

/* Beispiel für Aufruf */ 
main()
{
	char cpath[128];

	Dgetpath(cpath,0); /* los geht's ab akt. Directory */ 
	search(0,cpath);
}

Listing 2: Anwendung der Datei-Suchfunktionen


Alex Esser
Aus: ST-Computer 08 / 1988, Seite 52

Links

Copyright-Bestimmungen: siehe Über diese Seite