Der Directorydruck
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:
- ZusÀtzlich zum Ausdruck des Hauptverzeichnisses kann optional der Ausdruck aller Unterverzeichnisse geschehen.
- Die Sortierung erfolgt nach denselben vier Kriterien wie im Betriebssystem, also nach Name, GröĂe, Datum oder Typ.
- Der Ausdruck geschieht ĂŒber unsere Spoolerroutinen. Dadurch werden sĂ€mtliche, eventuell im Inhaltsverzeichnis enthaltene Sonderzeichen - etwa Umlaute -auch auf Ihrem Drucker korrekt wiedergegeben.
Die XENIX-Struktur des TOS
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
\ ..\..
Abb. 21: Der Aufbau des Directories gleicht einem Baum
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:
- Der Ausdruck des aktuellen Ordners
- Der Durchlauf durch sÀmtliche Ordner des Dateibaumes, mit jeweiligem Aufruf von Punkt 1.
Ausdruck eines Ordners
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;
Ein wenig Rekursion x_directory
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.
Abb. 22
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
Wieder ein wenig GEM
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.
**Erweiterte Directory des Massenspeichers A**
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
**Abb. 23: Ein rekursives Inhaltsverzeichnis**
Das Accessory DIRECT
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.
Vorausschau
Beim nĂ€chsten Mal wird Sie ein naturwissenschaftlicher Taschenrechner erwarten, der es schon ein biĂchen in sich hat. Er verfĂŒgt ĂŒber:
- Punkt-vor-Strich-Rechnung
- Klammerung (die zulÀssigen Klammerebenen können Sie selbst bestimmen)
- Die wichtigsten naturwissenschaftlichen Funktionen (sin, cos, tan, ln, ... ) und ihre Umkehrungen
- Die trigonometrischen Funktionen können in allen, gelĂ€ufigen Einheiten (DEG, RAD und GRAD) durchgefĂŒhrt werden
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 *)