Bereits nach einer sehr kurzen Bekanntschaft mit meinem ST war mir klar - eins gefällt mir an dieser Kiste überhaupt nicht: Die unumgängliche Unordnung, die sich bereits bei dem Besitz von mehr als zwei Disketten in jeden Dateibestand einschleicht, läßt sich leider nur sehr unzureichend dokumentieren. So ist es, zur Dokumentation einer Diskette, die über mehrere Unterverzeichnisse verfügt, durchaus nicht befriedigend, eine Hardcopy jedes Unterverzeichnisses anlegen zu müssen, um ein wenig Übersicht über das Ganze zu erhalten. Mit der Zeit - und der Erwerbung einer Harddisk - stieg diese Unzufriedenheit ganz gewaltig, weshalb ich mich genötigt sah, ein entsprechendes Utility zu formulieren. Um im Dschungel der mittlerweile ungezählten Accessories gleicher Thematik nicht völlig unterzugehen, hält dieses Utility, eingebaut in unseren Lovely Helper, einige Leckerbissen für Sie bereit:
Punkt 1 - der Ausdruck aller Unterverzeichnisse - enthält dabei den interessantesten Algorithmus. Zu seinem Verständnis möchte ich etwas weiter ausholen. Stichwort ist die XENIX-Struktur des TOS. Gemeint ist damit die “Art und Weise”, wie unter TOS Dateien verwaltet werden, also Strukturierung von Dateien durch Unterverzeichnisse, die hierarchische Schachtelung derselben etc. Wie Sie vielleicht wissen, ist diese Struktur keine ATARI-spezifische Entwicklung, sondern fand bereits in vielen anderen erfolgreichen Betriebssystemen, wie etwa MS-DOS oder UNIX, Anwendung. Der Ursprung ist dabei ein XENIX genanntes Betriebssystem, wodurch sich die Bezeichnung erklärt. In einem derartigen Betriebssystem kann man sich nun alle Dateien einer Station in einer baumartigen Struktur organisiert denken (Abbildung 21). Die Wurzel des Baumes ist dabei das Hauptverzeichnis der Station und wird mit dem Zeichen ‘\‘ benannt. Anmerkung: Komischerweise stehen Bäume in der Informatik immer auf dem Kopf. Ich schließe mich dieser Konvention an. Wenn Sie die Wurzel suchen: Sie ist oben.
Das Hauptverzeichnis kann nun sowohl Dateien, als auch Unterverzeichnisse, im ST-Jargon auch Ordner genannt, enthalten. Mit den Ordnern verhält es sich genauso. Auch sie dürfen sowohl Dateien, als auch ihrerseits wieder Ordner enthalten. Auf einzelne Elemente des Dateibaumes, egal ob es sich dabei um Ordner oder Dateien handelt, greift man über die zugehörigen Pfadnamen zu. Vergleichen Sie dazu bitte auch Abbildung 21. Dargestellt ist ein Dateibaum mit sämtlichen zugehörigen Pfadnamen. Da diese Pfadnamen bei größeren Baumstrukturen, wie etwa auf einer Hard-Disk, mitunter recht lang werden, führt man, zwecks einfacherer Handhabung, noch den Begriff des aktuellen Ordners ein. Dieser Ordner ist unter sämtlichen Ordnern des Dateibaumes frei wählbar und kann jederzeit geändert werden. Zum Zugriff auf Dateien innerhalb des aktuellen Ordners braucht man nicht mehr den kompletten Pfadnamen anzugeben. Es genügt der einfache Dateiname. Auch Dateien “in der Nähe” des aktuellen Ordners lassen sich einfacher beschreiben als durch ihren kompletten Pfad. Gehen wir z.B. davon aus, daß wir uns im Ordner\TEIL.6\RSC innerhalb der Abbildung 21 befinden. Untenstehende Tabelle enthält dann jeweils zwei Dateinamen, die ein und dieselbe Datei innerhalb der Baumstruktur beschreiben.
\TEIL.6\RSC\DIRECT.I DIRECT.I
\TEIL.6
\TEIL.6\RUN ..\RUN
\TEIL6\S0URCE\DIRECT.I ..\SOURCE\DIRECT.I
\TEIL.5 ..\..\TEIL.5
\ ..\..
Die Verwendung der beiden Punkte innerhalb der Pfadnamen bedeutet dabei soviel wie “Gehe einen Ordner im Dateibaum zurück!”. Damit ist es relativ bequem, auch an den äußeren Enden des Dateibaumes Dateien in unmittelbarer Nähe anzusprechen, ohne immer über den ganzen Pfad steigen zu müssen. Für unsere heutige Anwendung benötigen wir nun einen Algorithmus, der in einer “gewissen” Reihenfolge nacheinander jeweils alle Ordnereines Dateibaumes zu aktuellen Ordnern macht, um dann den jeweiligen Ordner auszudrucken. Die Arbeit läßt sich leicht in zwei Teile zerlegen:
Beginnen wir mit Punkt 1. Dazu ist es zunächst erforderlich, irgendwie an die Informationen, sprich Dateieinträge, des aktuellen Ordners heranzukommen. TOS stellt für diese Aufgabe die Disk-Transfer-Adress, kurz DTA, zur Verfügung. An diese Adresse schreibt TOS einen 44 Bytes großen Informationsblock, der sämtliche notwendigen Daten über eine Datei enthält. Das Format dieses DTA-Puffer können Sie der Abbildung 20 entnehmen (Innerhalb unserer Dateien, wurde der DTA-Puffer bereits in der Datei HILF.PAS - in der ersten Folge des Lovely Helper - deklariert.). Erster Bestandteil des Puffers ist ein Array aus 22 Bytes. Es enthält einige Organisations-Informationen für das Betriebssystem. Für uns wichtig ist nur das letzte Element dieses Arrays - reserviert[21]. Es enthält ein Bitmuster, das Aufschluß über den Dateityp der betrachteten Datei gibt. Alle sechs möglichen Belegungen dieses Bytes können Sie der Abbildung 20 entnehmen. Die nächsten beiden Informationen beziehen sich auf das Erstell- bzw. Modifikationsdatum der betrachteten Datei. Die beiden Variablen zeit und datum nehmen die entsprechende Information auf.
Da zur Speicherung beider Größen hier aber nur zwei Integer-Werte (2x16 Bit) verwendet werden, wird eine gewisse Interpretation der Größen notwendig. Wie Sie ebenfalls der Abbildung entnehmen können, sind die sechs Größen Tag, Monat, Jahr, Stunde, Minute und Sekunde auf folgende Weise gepackt:
Variable zeit: | Bits | |
---|---|---|
Stunde | 11-15 | |
Minute | 5-10 | |
Sekunde | 0-4 |
Variable datum: | Bits | |
---|---|---|
Jahr | 9-15 | |
Monat | 5-8 | |
Tag | 0-4 |
Einer Erklärung bedürfen dabei die Werte für das Jahr und die Sekunde. Hier reicht nämlich der darstellende Bereich nicht für die Aufnahme sämtlicher möglicher Zustände aus. Der Bereich für Sekunde kann maximal 32 (25) Zustände annehmen, wodurch es nur möglich ist, jede zweite Sekunde zu zählen. Zu den Jahren ist immer der Offset 1980 hinzuzuaddieren, um die korrekte Jahreszahl zu erhalten. Auf diese Weise kann unsere ST Systemuhr maximal bis zum Jahre 2107 (1980 + 27</sup - 1) Daten korrekt darstellen. Wohl mehr als ausreichend, selbst für den bis jetzt ziemlich zähen und langlebigen ST. Vierter Bestandteil des DTA-Puffers ist der Long_integer groesse. Er enthält die Länge der betrachteten Datei in der Einheit Byte. Letzter Bestandteil, der String name (dta_name = PACKED ARRAY [ 1.. 14] OF char), enthält den Namen der Datei. Er wird, wie im Betriebssystem üblich, mit CHR$(0) abgeschlossen. Die Adresse des DTA-Puffers verrät uns TOS mit dem Betriebssystemaufruf fgetdta (GEMDOS($2f)). Anmerkung: In der Regel liegt die DTA außerhalb des Bereichs der Pascal-Variablen. Deshalb ist p- als Compileroption unverzichtbar, sobald mit fgetdta gearbeitet wird. Bleibt die Frage, mit welchen Befehlen wir Daten in diesen DTA-Puffer bekommen. Dazu stellt TOS das Operationspaar fsfirst (GEMDOS($4e)) und fsnext (GEMDOS($4f)) zur Verfügung. fsfirst erhält als ersten Parameter einen Pfadnamen, im Betriebssystemformat, also cstring. Er darf auch Wildcards (’*’ und ’?’) beinhalten. Als weiterer Parameter wird ein Integer übergeben, der die Art der gesuchten Datei beschreibt. Das Format entspricht dabei dem des 22. Byte des DTA-Puffers (reserviert[21]).
Zusätzlich können hier jedoch Kombinationen der sechs möglichen Zustände angegeben werden, indem man die entsprechenden Werte “verodert”. So entspricht ein Wert von $17 (folder | disk_label | hidden_sys | hidden | read_only | normal_file) für diesen Parameter, der Suche nach einem beliebigen Ordnereintrag. Berechnet wird von fsfirst ein Bool'scher Parameter, entsprechend dem Erfolg der durchgeführten Suche. War die Suche mit fsfirst erfolgreich, so stehen die genauen Informationen der aufgefundenen Datei automatisch im DTA-Puffer. Mit fsnext kann dann nach weiteren, zutreffenden Einträgen gesucht werden. Wird für fsfirst als Dateiname ’.’ angegeben und als Attribut $11 (normal_file | read_only | folder), so kann mit den nachfolgenden fsnext-Aufrufen ein komplettes Inhaltsverzeichnis erfragt werden. Dabei werden alle Dateien gefunden, die auch in den GEM-Windows angezeigt würden. Im Prinzip also sehr einfach, nämlich:
IF fsfirst(...) THEN
BEGIN
ausgabe(...);
WHILE fsnext DO
ausgabe(...);
END;
Umgesetzt in die Praxis benötigen wir nun jedoch noch einen ganzen Satz von Operationen, um eine maßgeschneiderte Ausgabe eines Ordners vorzunehmen (Listing 12, Zeilen 10-276). Die ersten beiden Prozeduren - inter_date und inter_time (Zeilen 10-30) - wandeln einen Integerwert, in einem der zwei Systemzeitformate, in jeweils drei Integerwerte, in normalem Format, um. Die Prozedur ausgabe_dta (Zeilen 32-82) erhält eine Variable dta im Format des DTA-Puffers. Sie druckt eine Zeile mit den entsprechenden Informationen aus. Benutzt werden dazu unsere Spoolerroutinen send_signal und send_string, sowie einige Routinen zuin Formatieren der beteiligten Variablen. Die eigentliche Arbeit aber erledigt Prozedur directory (Zeilen 99-276). Wir wenden uns zunächst ihrem Anweisungsteil (Zeilen 240-276) zu. Im ersten Teil (Zeilen 241-249) wird die DTA-Adresse und der Name des aktuellen Ordners ermittelt. Der Name des Ordners wird daraufhin ausgegeben. In den Zeilen 250-261 wird nun - mit dem Dateinamen - das komplette Ordnerverzeichnis zunächst von der Station gelesen und in einem Puffer (dta_puffer) abgelegt. Grund für dieses Vorgehen ist, daß wir fsfirst keine Sortierkriterien für die gesuchten Dateien mitgeben können. Wir haben also alle Informationen zuerst zu puffern und dann selber zu sortieren, bevor an die Ausgabe gedacht werden kann. Die Sortierung (Zeile 264) erfolgt mit dem bereits früher vorgestellten Quicksort (Zeilen 205-238). Es ist diesmal lediglich unserer Problematik angepaßt worden.
Größte Schwierigkeit war dabei die Wahl einer zutreffenden Relation zur Anordnung der Dateien. Relation rel (Zeilen 115-203) leistet das Gewünschte. Hier wird zunächst berücksichtigt, welcher Art das Sortierkriterium ist. Zur Verfügung stehen Name, Zeit, Größe und Typ. Daraufhin erfolgt die prompte Auswertung der entsprechenden (Teil-)Relation. Nach diesem Sortiervorgang wird nun nur noch der Puffer, durch mehrfachen Aufruf von ausgabe_dta, ausgegeben.
PROCEDURE where(compare : string);
VAR pfad : string;
cpfad : cstring;
name : string;
dta_ptr : dta_ptr_type;
BEGIN
dta_ptr:=fgetdta;
IF dgetpath(cpfad,0) THEN
BEGIN
ctopstr(cpfad,pfad);
ptocstr(compare,cpfad);
IF fsfirst(cpfad,normal_file | read_only | folder)>=0 THEN
BEGIN
dtopstr(dta_ptr^.name,name);
writeln(concat(pfad,'\',name));
WHILE fsnext DO
BEGIN
dtopstr(dta_ptr^.name,name);
writeln(concat(pfad,'\',name);
END;
END;
END;
END;
Kommen wir nun zu der Prozedur, die ein komplettes Stationsverzeichnis erstellt - x_directory (Zeilen 278-348). Bis auf die Ausgabe der eingeschachtelten Ordner, sowie ein wenig Randinformation - Stationskennung und Stationsbelegungsgrad ermitteln und ausgeben -, hat x_directory nur einen Durchlauf durch den Dateibaum durchzuführen. Dies geschieht in der rekursiven Prozedur dfs, Zeilen 285-311. Benutzt wird hier im Wesentlichen der Betriebssystemaufruf dsetpath (GEMDOS($3b)) im Zusammenwirken mit fsfirst und fsnext. Begonnen wird der Durchlauf im Hauptverzeichnis ‘\‘ (Die Initialisierung dazu erfolgt noch in x_directory selber (Zeilen 315-317)). dfs gibt nun zunächst das komplette Hauptverzeichnis aus und ermittelt dann mit fsfirst und fsnext sämtliche verzeichneten Ordner im aktuellen Verzeichnis. Dabei erfolgt jeweils ein rekursiver Aufruf mit dem entsprechenden Ordnernamen, sowie ein Rücksprung mit dem Pfadnamen ’..’ (Ordner zurück) nach erfolgter Rekursion. Besondere Bedeutung kommt dabei den reservierten Bytes des DTA-Puffers zu. In Ihnen wird nämlich vermerkt, inwieweit mit fsfirst und fsnext ein Ordner bereits durchwandert ist. Durch die rekursiven Aufrufe - und damit andere Belegungen des DTA-Puffers - wird diese Information überschrieben. Beim Durchlauf würde dies bei der Unterlassung von Gegenmaßnahmen dazu führen, daß immer wieder in den ersten Ordner hineingestiegen wird: Eine Endlosschleife, über die sich besonders der Drucker freut. Eine einfache Gegenmaßnahme, die keinerlei Information über die genaue Struktur der reservierten Bytes erfordert, ist die komplette Sicherung des DTA-Puffers in einer Variablen copy (Zeile 303). Nach dem rekursiven Aufruf, kann dann auf sehr einfache Weise - Zuweisung von copy an den DTA-Puffer - der ursprüngliche Zustand wieder hergestellt werden. Für Insider: Das, was wir gerade gemacht haben, ist an sich nichts weiter, als ein Preorder-Durchlauf durch den Dateibaum, mit der Knotenoperation “directory”.
Denkbar sind durchaus auch andere Knotenoperationen: Ersetzen wir etwa directory, sowie Ihre Aufrufe durch die Operation, die im Listing auf der vorigen Seite zu sehen ist, so erhalten wir - wird das Ganze mit dfs in ein separates Programm eingebunden - eine Routine, die in größeren Dateibeständen bestimmte Dateien sucht. Enthält compare beispielsweise den Wert ‘*.PAS‘ so werden sämtliche Pascal-Dateien mit ihrem kompletten Pfadnamen ausgegeben. Zumindestens in UNIX, mittlerweile aber auch auf dem ST, habe ich das immer sehr nützlich gefunden.
Objekt: | Objektart: | Länge: | Diverses: |
---|---|---|---|
TDIRECT | TEXT | 5 | |
BNAME | BUTTON | Flags: Selectable & Exit | |
BGROESSE | BUTTON | Flags: Selectable & Exit | |
BDATUM | BUTTON | Flags: Selectable & Exit | |
BTYP | BUTTON | Flags: Selectable & Exit | |
BNORMAL | BUTTON | Flags: Selectable & Exit | |
BABBDIRE | BUTTON | Flags: Selectable & Exit | |
BREKURSI | BUTTON | Flags: Selectable & Exit |
Tab. 1: Objekte der Abb. 22
Nach diesem Ausflug in die Welt der Betriebssysteme, ist nun noch das Bindeglied zwischen den bis jetzt vorgestellten Operationen und unserem Lovely Helper zu formulieren. Benutzt wird der Dialog DIRECT. Abbildung 22. Tabelle 1.
Der einzige Text dieses Dialoges - TDIRECT - dient dabei dazu, daß aktuelle Sortierkriterium anzuzeigen. Die vier darunter befindlichen Feldtasten dienen der Selektion dieses Kriteriums. Die Taste BNORMAL ermöglicht den Ausdruck eines normalen (nicht rekursiven) Hauptverzeichnisses. Entsprechend leitet BREKURSI die rekursive Variante ein. BABBDIRE letztendlich dient dem Abbruch des DIRECT-Dialoges. Anmerkung: Beachten Sie bitte auch, daß für das heutige Resource - - DIRECT.RSC - wieder die beiden Dialoge PARAMETE und SYNCHRO benötigt werden, damit die Druckerparameter eingestellt werden können. Die Dialogverwaltung (do_direct, Zeilen 350-391) ist, wie schon der Dialog, sehr schlicht. In der REPEAT-Schleife, die die Dialogausführung einschließt (Zeilen 360-377), wird vor do_dialog der Text TDIRECT initialisiert.
Nach do_dialog wird zwischen den vier Feldtasten, zur Wahl des Sortierkriteriums, und den restlichen drei Feldtasten unterschieden. Bei Wahl einer der vier Tasten für die Sortierkriterien, wird nur der entsprechende Parameter (default sort) umgesetzt und der Dialog wiederholt. Bei Wahl einer anderen Taste wird die Schleife verlassen; es erfolgt eine abschließende Auswertung. In dieser Auswertung ist zunächst, wie schon in der letzten Folge, zu prüfen, ob der Drucker nicht vielleicht bereits für unseren Spooler arbeitet (spoolstatus = unused). Ist diese Bedingung erfüllt - wir stören den Spooler nicht -, so ist nur noch zwischen einem rekursiven Inhaltsverzeichnis (BREKURSI) und einem einfachen Hauptverzeichnis (BNORMAL) zu unterscheiden und in die entsprechende Prozedur zu verzweigen.
Label des Speichermediums: EP.14B 0 00-00-1980 00:00
Directory: O TEIL.5 0 18-06-1988 17:24
Directory :\TEIL.5 O ARTIKEL 0 18-06-1988 17:24 O RSC 0 18-06-1988 17:26 O RUN 0 18-06-1988 17:26 O SOURCE 0 18-06-1988 17:26
Directory :\TEIL.5\ARTIKEL
ART_05.DOC 16013 18-06-1988 17:24
ABB_15.PIC 32000 18-06-1988 17:24
ABB_16.PIC 32000 18-06-1988 17:25
ABB_17.PIC 32000 18-06-1988 17:25
ABB_18.PIC 32000 18-06-1988 17:25
ABB_19.TXT 3948 18-06-1988 17:25
Directory :\TEIL.5\RSC
ZEIT.I 5295 18-06-1988 17:26
ZEIT.RSC 8998 18-06-1988 17:26
ZEIT.RSD 1872 18-06-1988 17:26
Directory :\TEIL.5\RUN
ZEIT.ACC 43055 18-06-1988 17:26
ZEIT.RSC 8998 18-06-1988 17:26
Directory :\TEIL.5\SOURCE
ZEIT.I 5295 18-06-1988 17:27
ZEIT.PAS 3129 18-06-1988 17:27
ZEIT2.PAS 14451 18-06-1988 17:27
Bytes gesamt : 728064 Bytes frei : 473088 Bytes belegt : 254976
Die Accessory-Verwaltung von DIRECT.ACC (Listing 13) hält nun nichts Aufregendes mehr für uns bereit. Die Möglichkeit mehrere Menüleisten von einem Accessoire aus zu belegen, hatten wir bereits beim letzten Mal kennengelernt. Ach ja: Die Definition von bundesland in den Zeilen 19-21. Ein kleiner Pfusch! Da die Parameterverwaltung auch das Bundesland für unser ZEIT.ACC zu verwalten hat, ist diese Deklaration in Listing 13 aufzunehmen, um die Datei SPOOLER1.PAS unverändert einbinden zu können. So! Für heute bin ich damit wieder einmal am Ende (meiner Ausführungen). Als letztes möchte ich Ihnen noch Abbildung 23 präsentieren: Das Resultat unserer heutigen Bemühungen, angewendet auf eine meiner Datendisketten zum Lovely Helper.
Beim nächsten Mal wird Sie ein naturwissenschaftlicher Taschenrechner erwarten, der es schon ein bißchen in sich hat. Er verfügt über:
Mal wieder was ganz und gar Theoretisches und nicht wie heute aus dem Sumpf der Betriebssystemprogrammierung, ln der Hoffnung, daß Ihnen diese Wechselkost gut bekommt, verbleibe ich bis dahin.
{***********************************************}
{* Listing 13 : Resource-Handling für die *}
{* Directoryausgabe *}
{* *}
{* Datei : DIRECT.PAS *}
{* last update: 27.5.88 *}
{***********************************************}
{$s20,p-}
PROGRAM directory;
CONST {$i gemconst.pas}
{$i trixcons.pas}
{$i direct.i}
TYPE {$i gemtype.pas}
{$i trixtype.pas}
bundesland = (schleswig_holstein,hamburg,bremen,niedersachsen,
nordrhein_westfalen,hessen,rheinland_pfalz,
saarland,baden_wuertemberg, bayern);
VAR msg : message_buffer;
apl_name_1 ,
apl_name_2 : str255;
apl_nr ,
menu_nr_1 ,
menu_nr_2 ,
event ,
dummy : short_integer;
direct_dialog ,
parameter_dialog ,
synchro_dialog : dialog_ptr;
{$i gemsubs.pas}
{$i trixsubs.pas}
{$i hilf.pas}
{$i spooler1.pas}
{$i direct1.pas}
FUNCTION initialisieren : boolean;
VAR ok : boolean;
BEGIN
ok:=load_resource('A:\DIRECT.RSC');
IF ok THEN
BEGIN
apl_name_1:=' Directory';
menu_nr_1:=menu_register (apl__nr, apl_name_1);
apl_name_2:=' Druckerparameter'; menu_nr_2:=menu_register(apl_nr,apl_name_2);
find_dialog(paramete,parameter_dialog);
find_dialog(synchro,synchro_dialog);
find_dialog(direct,direct_dialog);
center_dialog(parameter_dialog);
center_dialog(synchro_dialog);
center_dialog(direct_dialog);
io_check(false);
rewrite(spoolchannel,'PRN:'); load_parameter;
END;
initialisieren:=ok;
END;
BEGIN
apl_nr:=init_gem;
IF apl_nr>=0 THEN
IF initialisieren THEN
WHILE true DO
BEGIN
event:=get_event(e_message,0,0,0,0,false,0,0,0,0,
false,0,0,0,0,msg,dummy,dummy,dummy,dummy,dummy,dummy);
IF (event & e_message<>0) AND (msg[0]=ac_open) THEN
BEGIN
IF msg[4]=menu_nr_1 THEN
do_direct;
IF msg[4]=menu_nr_2 THEN
do_parameter;
END;
EnD;
END.
{***********************************************}
{* Listing 12 : Routinen zum Ausdruck eines *}
{* normalen und eines *}
{* rekursiven Partitions- *}
{* Inhaltsverzeichnisses *}
{* und das zugehörige *}
{* Dialog-Handling *}
{* *}
{* Datei : DIRECT1.PAS *}
{* last update: 24.5.1988 *}
{***********************************************}
PROCEDURE inter_date( sys_date : integer;
VAR tag ,
monat ,
jahr : integer);
BEGIN
tag:=sys_date & $001f;
monat:=shr(sys_date & $01e0,5);
jahr:=shr(sys_date & $fe00,9)+1980;
END;
PROCEDURE inter_time( sys_time : integer;
VAR stunde ,
minute ,
sekunde : integer);
BEGIN
stunde:=shr(sys_time & $f800,11);
minute:=shr(sys_time & $07e0,5);
sekunde:=2*(sys_time & $001f);
END;
PROCEDURE ausgabe_dta( offset : integer;
VAR dta : dta_type);
VAR str : str255;
cstr : cstring;
i : integer;
tag ,
monat ,
jahr ,
stunde ,
minute ,
sekunde : integer;
BEGIN
send_string(offset,'');
WITH dta DO
BEGIN
IF reserviert[21]=folder THEN
send_string(2,’O')
ELSE
send_string(2,'');
i:=0;
REPEAT
cstr[i]:=name[i+1];
i:=succ(i);
UNTIL (name[i]=chr(0)) OR (i>14);
cstr[i]:=chr(0);
ctopstr(cstr,str);
send_string(14,str);
writev(str,groesse);
s_expand(7,str);
send_string(9,str);
inter_date(datum,tag,monat,jahr);
writev(str,tag);
n_expand(2,str);
sendestring(3,concat(str,'-'));
writev(str,monat);
n_expand(2,str);
send_string(3,concat(str,'-'));
writev(str,jahr);
send_string(6,str);
inter_time(zeit,stunde,minute,sekunde);
writev(str,stunde);
n_expand(2,str);
send_string(3,concat(str,':'));
writev(str,minute);
n_expand(2,str);
send_string(2,str);
send_signal(lf);
END;
END;
PROCEDURE dtopstr(VAR dta : dta_name;
VAR str : string);
VAR i : integer;
BEGIN
str:='';
i:=1;
WHILE (dta[i]<>chr(0)) AND (i<15) DO
BEGIN
str:=concat(str,dta[i]);
i:=succ(i);
END;
END;
PROCEDURE directory(offset ,
stype : integer);
CONST max_dta = 255;
TYPE dta_buffer_type = RECORD
max : integer;
a : ARRAY [0..max_dta] OF dta_type;
END;
VAR dta_buffer : dta_buffer_type;
pfad : string;
cpfad : cstring;
dta_ptr : dta_ptr_type;
i : integer;
FUNCTION rel(VAR stype : integer;
VAR op1 ,
op2 : dta_type) : boolean;
VAR str1 ,
str2 ,
typ1 ,
typ2 : string;
tagl ,
tag2 ,
monat1 ,
monat2 ,
jahr1 ,
jahr2 ,
stunde1 ,
stunde2 ,
minute1 ,
minute2 ,
sekunde1,
sekunde2: integer;
PROCEDURE gettyp(VAR str, typ : string);
VAR ok : boolean;
i ,
j : integer;
BEGIN
ok:=false;
i:=1;
REPEAT
ok:=ok OR (str[i]='.');
i:=succ(i);
UNTIL ok OR (i>length(str));
typ:='';
IF ok THEN
FOR j:=i-1 TO length(str) DO
typ:=concat(typ,str[j]);
END;
BEGIN
IF ((op1.reserviert[21]=folder) AND
(op2.reserviert[21]=folder)) OR
((op1.reserviert[21]<>folder) AND
(op2.reserviert[21]<>folder)) THEN
CASE stype OF
sort_name : BEGIN
dtopstr(op1.name,str1);
dtopstr(op2.name,str2);
rel:=strl<str2;
END;
sort_date : BEGIN
inter_date(op1.datum,tag1,monat1,jahr1);
inter_date(op2.datum,tag2,monat2,jahr2);
inter_time(op1.zeit,stunde1,minute1,sekunde1);
inter_time(op2.zeit,stunde2,minute2,sekunde2);
IF jahr1<>jahr2 THEN
rel:=jahr1<jahr2
ELSE
IF monat1<>monat2 THEN
rel:=monat1<monat2
ELSE
IF tag1<>tag2 THEN
rel:=tag1<tag2
ELSE
IF stunde1<>stunde2 THEN
rel:=stunde1<stunde2
ELSE
IF minute1<>minute2 THEN
rel:=minute1<minute2
ELSE
rel:=sekunde1<sekunde2;
END;
sort_size : rel:=op1.groesse>op2.groesse;
sort_type : BEGIN
dtopstr(op1.name,str1);
dtopstr(op2.name,str2);
gettyp(str1,typ1);
gettyp(str2,typ2);
IF typ1<>typ2 THEN
rel:=typ1<typ2
ELSE
rel:=str1<str2;
END;
END
ELSE
rel:=op1.reserviert[21]>op2.reserviert[21];
END;
PROCEDURE sort(l, r : integer);
VAR i ,
j : integer;
help ,
pivot: dta_type;
BEGIN
WITH dta_buffer DO
BEGIN
pivot:=a[(l+r) DIV 2];
i:=l;
j:=r;
REPEAT
WHILE rel(stype,a[i],pivot) DO
i:=succ(i);
WHILE rel(stype,pivot,a[j]) DO
j:=pred(j);
IF i<=j THEN
BEGIN
help:=a[i];
a[i]:=a[j];
a[j]:=help;
i:=succ(i);
j:=pred(j);
END;
UNTIL i>j;
END;
IF l<j THEN
sort(l,j);
IF i<r THEN
sort(i,r);
END;
BEGIN
dta_ptr:=fgetdta;
IF dgetpath(cpfad,0)=0 THEN
BEGIN
ctopstr(cpfad,pfad);
send_signal(lf);
send_string(offset,'');
send_string(50,concat(' Directory : ',pfad));
send_signal(lf);
send_signal(lf);
ptocstr('*.*',cpfad);
WITH dta_buffer DO
BEGIN
max:=-1;
IF fsfirst(cpfad,normal_file | read_only | folder)>=0 THEN
REPEAT
IF dta_ptr^.name[1]<>'.' THEN
BEGIN
max:=succ(max);
a[max]:=dta_ptr^;
END;
UNTIL (max=max_dta) OR (fsnext<>0);
IF max>-1 THEN
BEGIN
sort(0,max);
FOR i:=0 TO max DO
ausgabe_dta(offset,a[i]);
END
ELSE
BEGIN
send_string(offset,'');
send_string(50,' Directory hat keine Dateien');
send_signal(lf);
END;
END;
END;
END;
PROCEDURE x_directory(stype : integer);
VAR dta_ptr : dta_ptr_type;
info : buffer_type;
cpfad : cstring;
str : str255;
PROCEDURE dfs(offset : integer);
VAR cpfad : cstring;
dstr : string;
copy : dta_type;
BEGIN
directory(offset,stype);
ptocstr('*.*',cpfad);
IF fsfirst(cpfad,folder)=0 THEN
WITH dta_ptr^ DO
REPEAT
IF (reserviert[21]=folder) AND (name[1]<>'.') THEN
BEGIN
dtopstr(name,dstr);
ptocstr(dstr,cpfad);
IF dsetpath(cpfad)=0 THEN
;
copy:=dta_ptr^;
dfs(offset+2);
dta_ptr^:=copy;
ptocstr('..',cpfad);
IF dsetpath(cpfad)=0 THEN
;
END;
UNTIL fsnext<>0;
END;
BEGIN
dta_ptr:=fgetdta;
ptocstr('\',cpfad);
IF dsetpath(cpfad)=0 THEN
;
send_string(60,concat('Erweiterte Directory des Massenspeichers ', chr(dgetdrive+ord('A'))));
send_signal(lf);
send_signal(lf);
sendestring(50,'Label des Speichermediums ');
send_signal(lf);
send_signal(lf);
ptocstr('*.*',cpfad);
IF fsfirst(cpfad,disk_label)=0 THEN
ausgabe_dta(0,dta_ptr^)
ELSE
BEGIN
send_string(50,‘Medium hat keinen Label');
send_signal(lf);
END;
dfs(0);
dfree(info,0);
send_signal(lf);
writev(str,info[2]*info[3]*info[4]);
s_expand(7,str);
send_string(50,concat('Bytes gesamt :',str));
send_signal(lf);
writev(str,info[1]*info[3]*info[4]);
s_expand(7, str);
send_string(50,concat('Bytes frei :',str));
send_signal(lf);
writev(str,(info[2]-info[1])*info[3]*info[4]);
s_expand(7,str);
send_string(50,concat('Bytes belegt :',str));
send_signal(ff);
END;
PROCEDURE do_direct;
VAR button : integer;
str : str255;
ex : boolean;
BEGIN
ex:=false;
begin_update;
WITH parameter DO
REPEAT
CASE default_sort OF
sort_name : str:='Name ';
sort_date : str:='Zeit ';
sort_size : str:='Größe';
sort_type : str:='Typ ';
END;
set_dtext(direct_dialog,tdirect,str,system_font,te_left);
button:=do_dialog(direct_dialog,0);
obj_setstate(direct_dialog,button,normal,false);
CASE button OF
bname : default_sort:=sort_name;
bdatum : default_sort:=sort_date;
bgroesse : default_sort;=sort_size;
btyp : default_sort;=sort_type;
OTHERWISE : ex:=true;
END;
UNTIL ex;
end_dialog(direct_dialog);
end_update;
IF spoolstatus=unused THEN
CASE button OF
brekursi : x_directory(parameter.default_sort);
bnormal : BEGIN
directory(0,parameter.default_sort);
send_signal(ff);
END;
OTHERWISE : {dummy};
END
ELSE
dummy:=do_alert('[1][Drucker arbeitet bereits][O.K.]',1);
END;
(* resource set indicies for DIRECT *)
CONST
paramete = 0; (* form/dialog *)
paraseit = 3; (* TEXT in tree PARAMETE *)
chr1 = 6; (* TEXT in tree PARAMETE *)
ord1 = 7; (* TEXT in tree PARAMETE *)
makrol = 8; (* FTEXT in tree PARAMETE *)
chr2 = 9; (* TEXT in tree PARAMETE *)
ord2 = 10; (* TEXT in tree PARAMETE *)
makro2 = 11; (* FTEXT in tree PARAMETE *)
chr3 = 12; (* TEXT in tree PARAMETE *)
ord3 = 13; (* TEXT in tree PARAMETE *)
makro3 = 14; (* FTEXT in tree PARAMETE *)
chr4 = 15; (* TEXT in tree PARAMETE *)
ord4 = 16; (* TEXT in tree PARAMETE *)
makro4 = 17; (* FTEXT in tree PARAMETE *)
chr5 = 18; (* TEXT in tree PARAMETE *)
ord5 = 19; (* TEXT in tree PARAMETE *)
makro5 = 20; (* FTEXT in tree PARAMETE *)
chr6 = 21; (* TEXT in tree PARAMETE *)
ord6 = 22; (* TEXT in tree PARAMETE *)
makro6 = 23; (* FTEXT in tree PARAMETE *)
chr7 = 24; (* TEXT in tree PARAMETE *)
ord7 = 25; (* TEXT in tree PARAMETE *)
makro7 = 26; (* FTEXT in tree PARAMETE *)
chr8 = 27; (* TEXT in tree PARAMETE *)
ord8 = 28; (* TEXT in tree PARAMETE *)
makro8 = 29; (* FTEXT in tree PARAMETE *)
chr9 = 30; (* TEXT in tree PARAMETE *)
ord9 = 31; (* TEXT in tree PARAMETE *)
makro9 = 32; (* FTEXT in tree PARAMETE *)
chr10 = 33; (* TEXT in tree PARAMETE *)
ord10 = 34; (* TEXT in tree PARAMETE *)
makro10 = 35; (* FTEXT in tree PARAMETE *)
paraspei = 36; (* BUTTON in tree PARAMETE *)
parasync = 37; (* BUTTON in tree PARAMETE *)
parazuru = 38; (* BUTTON in tree PARAMETE *)
parachec = 39; (* BUTTON in tree PARAMETE *)
paravor = 40; (* BUTTON in tree PARAMETE *)
paratest = 41; (* BUTTON in tree PARAMETE *)
paraexit = 42; (* BUTTON in tree PARAMETE *)
synchro = 1; (* form/dialog *)
synclaen = 3; (* FTEXT in tree SYNCHRO *)
syncgrap = 7; (* FTEXT in tree SYNCHRO *)
synctext = 8; (* FTEXT in tree SYNCHRO *
syncexit = 9; (* BUTTON in tree SYNCHRO*)
syncsetz = 10; (* BUTTON in tree SYNCHR *)
direct = 2; (* form/dialog *)
bname = 5; (* BUTTON in tree DIRECT *)
bdatum = 6; (* BUTTON in tree DIRECT *)
bnormal = 7; (* BUTTON in tree DIRECT *)
babbdire = 8; (* BUTTON in tree DIRECT *)
bgroesse = 9; (* BUTTON in tree DIRECT *)
btyp = 10; (* BUTTON in tree DIRECT *)
brekursi = 11; (* BUTTON in tree DIRECT *)
tdirect = 12; (* TEXT in tree DIRECT *)