Lovely Helper: Ein Desk-Accessory, Teil 6

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:

  1. Zusätzlich zum Ausdruck des Hauptverzeichnisses kann optional der Ausdruck aller Unterverzeichnisse geschehen.
  2. Die Sortierung erfolgt nach denselben vier Kriterien wie im Betriebssystem, also nach Name, Größe, Datum oder Typ.
  3. 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:

  1. Der Ausdruck des aktuellen Ordners
  2. 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:

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 *)

Dirk Brockhaus
Aus: ST-Computer 10 / 1989, Seite 132

Links

Copyright-Bestimmungen: siehe Über diese Seite