WO ist es denn?

Das Zusammenfassen von Dateien in Ordnern führt häufig dass man nicht mehr weiss wo welche Datei abgespeichert ist. Das Programm WO sucht die Datei für Sie.

Die dabei verwendete Suchroutine Search ist das Kernstück der Include-Datei WO.INC. Die Include-Datei bietet den Vorteil eines universellen Einsatzes der Suchroutine, die häufig und in sehr unterschiedlichen Programmen benötigt wird. Hier wird sie in zwei verschiedene Programmrahmen eingebunden:

Das Accessory WO

Nach dem Übersetzen und Binden muß die entstandene Datei WO.PRG in WO.ACC umbenannt und der Rechner neu gebootet werden. Danach steht das Accessory im ersten Pull-Down-Menü zur Verfügung: WO erfragt den Namen der zusuchenden Datei in einer Fileselect-Box. Bei der Eingabe sind selbstverständlich auch die Metazeichen ’*’ und ‘?’ erlaubt. Nach kurzer Suchzeit werden die Namen der gefundenen Dateien mit kompletter Pfadangabe in Alertboxen ausgegeben. Durch die Verwendung von Alert- und Fileselectboxen bei Ein- und Ausgabe wird der Programmieraufwand und damit die Länge des Programms erheblich reduziert. Beide Prozeduren lassen sich jedoch ersetzen ohne die eigentliche Suchroutine zu verändern. Neben der für Accessories obligatorischen Event-Endlosschleife enthält das Hauptprogramm die Prozedur Kill_List. Sie sorgt dafür, daß nach Ablauf des Programms der nicht mehr benötigte Speicherplatz freigegeben wird (dynamische Liste). Die zentrale Routine der Include-Datei ist die Prozedur Search. Sie durchsucht rekursiv alle Ordner nach dem eingegebenen Dateinamen. Da die verwendeten GEMDOS-Funktionen bereits in früheren Ausgaben der ST-Computer ausführlich beschrieben wurden, will ich mich hier auf die Erläuterung des Suchalgorithmus’ und der Speicherstruktur (lineare Liste) beschränken.

Search durchsucht zuerst das eingegebene Verzeichnis nach Ordnern:

Da sich die Anzahl der gefundenen Dateinamen nicht im voraus festlegen lässt, muß zu deren Speicherung eine dynamische Struktur verwendet werden. Die hier verwendete Implementation einer linearen Liste mit Kopf- und Schwanzzeiger vereinfacht das Anfügen von Elementen am Ende der Liste (Prozedur Append). Kopf- und Schwanzzeiger zeigen auf zwei Dummy-Elemente, zwischen denen sich die eigentlichen Listenelemente befinden. Die leere Liste ist also eine Liste mit zwei Dummy-Elementen. Dabei wird in der Prozedur Init_List (willkürlich) festgelegt, daß die next-Komponente des Schwanzzeigers auf das vorangehende Element zeigt (Abb. 1 und 2). Die restlichen Routinen der Include-Datei dienen lediglich der Typkonvertierung String < — > PACKED ARRAY OF CHAR. Die GEMDOS-Routinen verwenden für die Parameterübergabe Zeichenketten, die durch ein Nullbyte terminiert werden, die Fileselect- bzw. Alertbox-Routinen des ST Pascal Plus hingegen den Typ String.

Abb. 1: Die leere Liste
Abb. 2: Liste mit Dateinamen D_1, D_2, ..., D_n

Das zweite Programm (Listing 3) verwendet die Suchroutine zur Erweiterung der ST Pascal-Funktion Load_Resource. Die Prozedur Load_RSC sucht mittels WO eine Resource-Datei und lädt diese gegebenenfalls. Programm und zugehörige RSC-Datei müssen so nicht mehr im gleichen Ordner abgespeichert sein. Existieren jedoch mehrere Dateien gleichen Namens, kann es sein, daß die falsche geladen wird! Die Prozedur WO kann aber leicht erweitert werden, so daß neben den Namen der gefundenen Dateien auch deren Länge ausgegeben wird (Variable length im Typ DTA). Bei der Entwicklung des Programms traten zwei weitere Probleme auf, die ich nicht verschweigen möchte:

1.) Die Alertbox-Routine verlangt, daß die Größe der auszugebenden Box 25% des ganzen Bildschirms nicht überschreitet. Bei der Ausgabe sehr langer Pfadnamen kann es dadurch zu Systemabstürzen kommen. Meine Versuche, den auszugebenden Pfad durch Einfügen von ‘|’ in mehrere Zeilen zu zerlegen, schlugen fehl, obwohl der Ausgabe-String syntaktisch korrekt war.

2.) Ist an den Rechner kein Laufwerk B: angeschlossen, reagiert die Fileselect-Routine fehlerhaft: ändert man den Pfad der Box in ‘B:\‘ und klickt das grau-gepunktete Feld des Dateifensters an, so kommt es nach der Aufforderung v zum Crash (Teile der Alertbox werden zum Pfadnamen). Dieser Fehler läßt sich m.E. nur durch eine neue Fileselect-Routine, wie sie z.B. Tempus verwendet, umgehen (s.a. PD-Software).

Literatur:

H.-D. Jankowski, J.F. Reschke, D. Rabich:
ATARI ST-Profibuch, Sybex-Verlag, 1988.

Alex Esser:
Auf der Schwelle zum Licht, Directory-Verwaltung, ST Computer 7/8/9 1988.

{$A+, D-}    { Compileroptionen für Accessory }
{ falls nötig, Stapel mit S-Option vergrößern }

PROGRAM Wo_Accessory(INPUT,OUTPUT);

{               ***************************
                *                         *
                *        LISTING 2        *
                * (c) MAXON Computer GmbH *
                ***************************

    Accessory-Implementation der Datei-Suchroutine Wo
    Entwickelt mit ST Pascal Plus 1.20 von CCD
    Florian Nold, Lessingstr. 4, 7830 Emmendingen 
    Version 1R2 27.02.1989 
}

CONST {$I GEMCONST.PAS }

TYPE {$I GEMTYPE.PAS }
    file_type   = PACKED ARRAY [1..14] OF CHAR;
    path_type   = PACKED ARRAY[1..Max_Path] OF CHAR;
    path_string = STRING [Max_Path] ; 
    listpointer = ^list; { lineare Liste mit Kopf- }
    list        = RECORD { und Schwanz-Zeiger.     }
                    path : path_string; 
                    next : listpointer;
                  END;

VAR startpath     : path_string;
    ap_id,menu_id : integer;
    head,tail     : listpointer;
    acc_name      : Str255;

{$I GEMSUBS.PAS )
{$I WO.INC }

PROCEDURE Get_Searchpath(VAR startpath:path_string) ;
{ Eingaberoutine mit Fileselectbox für Startsuchpfad }
VAR ok : boolean;

    FUNCTION DGetDrv:integer; GEMDOS($19);
    { ermittelt akt. Laufwerk: 0=A, 1=B, ... }

BEGIN
    startpath:=concat(chr(DGetDrv+65),':\*.*'); 
    ok:=Get_In_File(startpath,startpath);

    IF NOT ok
        THEN startpath;
END; { Get_Searchpath }

PROCEDURE Print_List(head,tail:listpointer);
{gibt Elemente der lin. Liste in Alertboxen aus} 
VAR h_pointer   : listpointer;
    alertstring : str255; 
    button      : integer;
BEGIN
    h_pointer:=head^.next; 
    button:=2;
    {Solange weder das Listenende erreicht,noch der 
     Abbruch-Button geklickt ist, wird ausgegeben} 
    WHILE (h_pointer<>tail) AND (button=2) DO 
    BEGIN
        alertstring:=h_pointer^.path;
        alertstring:=concat('[0][Gefunden : |        ',alertstring); 
        alertstring:=concat(alertstring,'][Abbruch| Weiter]');
        button:=Do_Alert(alertstring,2); 
        h_pointer:=h_pointer^.next;
    END; { WHILE }
    button:=Do_Alert('[3][Keine weiteren Files][OK]',1);
END; { Print_List }

PROCEDURE Kill_List(VAR head,tail:listpointer);
{ Löscht Liste, d.h. gibt den reserv. Speicher frei }
VAR h_pointer1,h_pointer2 : listpointer;
BEGIN
    h_pointer1:=head;
    WHILE h_pointer1<>tail DO 
        BEGIN
            h_pointer2:=h_pointer1; 
            h_pointer1:=h_pointer2^.next; 
            dispose(h_pointer2);
        END; { WHILE } 
    dispose(tail);
END; { Kill_List }


PROCEDURE Event_Loop;
{ Endlosschleife zur Erfassung von Ereignissen } 
VAR event, dummy    : integer;
    msg             : Message_Buffer;
BEGIN
    WHILE true DO {Ein Accessory wird nie beendet ! }
        BEGIN
            { Erfassen eines Ereignisses }
            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);
            { Es wird nur das Anklicken des Menüpunkts 'WO ?' verarbeitet. }
            CASE msg[0] OF
                AC_Open: IF msg[4]=menu_id 
                            THEN 
                                BEGIN
                                    Get_Searchpath(startpath); 
                                    IF startpath<>''
                                        THEN
                                            BEGIN
                                                Wo(startpath,head,tail);
                                                Print_List(head,tail); 
                                                Kill_List(head,tail);
                                            END;
                                END; { AC_0pen }
            END; { CASE }
        END; { WHILE }
END; { Event_Loop }


BEGIN { MAIN }
    acc_name:='  WO ?'; 
    ap_id:=Init_GEM;
    IF ap_id>=0 
        THEN 
            BEGIN
                { Acc.-Name in Menüleiste eintragen } 
                menu_id:=Menu_Register(ap_id,acc_name); 
                Event_Loop; { springt in Endlosschleife } 
            END;
END. { WO_Accessory }

PROGRAM Wo_Load_Resource(INPUT,OUTPUT);

{               ***************************
                *                         *
                *         LISTING 3       *
                * (c) MAXON Computer GmbH *
                ***************************
    
    Verwendung der Include-Datei WO.INC beim Laden 
    einer Resource-Datei.
    Entwickelt mit ST Pascal Plus 1.20 von CCD
    Florian Nold, Lessingstr. 4, 7830 Emmendingen
    Version 1R2 27.02.1989 }

CONST {$I GEMCONST.PAS }

TYPE {$I GEMTYPE.PAS }
     path_string = STRING [Max_JPath] ;
     listpointer = ^list;
     list        = RECORD
                    path : path_string; 
                    next : listpointer;
                   END;

VAR rsc_name : path_string; 
    dummy    : integer;

{$I GEMSUBS.PAS }
{$I WO.INC }

FUNCTION Load_RSC(filename:path_string):boolean;
{ sucht die Resource-Datei in allen Ordnern und lädt
  die Datei, falls vorhanden }

VAR head,tail listpointer;

    PROCEDURE Kill_List(VAR head,tail:listpointer); 
    { Löscht Liste, d.h. gibt den reserv. Speicher frei }
    VAR h_pointer1,h_pointer2 : listpointer;
    BEGIN
        h_pointer1:=head;
        WHILE h_pointer1<>tail DO 
            BEGIN
                h_pointer2:=h_pointer1; 
                h_pointer1:=h_pointer2^.next; 
                dispose(h_pointer2);
            END; 
        dispose(tail);
    END; { Kill_List }

BEGIN
    Wo(filename,head, tail);
    IF head^.next<>tail 
        THEN
            Load_RSC:=Load_Resource(head^.next^.path)
        ELSE Load_RSC:=false;
    Kill_List(head,tail);
END; { Load_RSC }


BEGIN { MAIN } 
    dummy:=Init_GEM; 
    rsc_name:='A:\PASCAL.RSC';
    IF Load_RSC(rsc_name) = false 
        THEN dummy:=Do_Alert('[3][RSC-Datei nicht|gefunden][OK]',1);
    Exit_GEM 
END.

{               ***************************
                *                         *
                *         LISTING 1       *
                * (c) MAXON Computer GmbH *
                ***************************

    Include-Modul WO.INC: Durchsucht alle Ordnerebenen
    einer Disk/Partition nach dem eingegebenen Namen.
    Entwickelt mit ST Pascal Plus 1.20 von CCD
    Florian Nold, Lessingstr. 4, 7830 Emmendingen 
    Version 1R2 27.02.1989 }

PROCEDURE Wo(inpathstr:path_string;
             VAR head,tail:listpointer);

TYPE file_type = PACKED ARRAY [1..14] OF CHAR; 
     path_type = PACKED ARRAY[1..Max_Path] OF CHAR;
    { Definition der Disk-Transfer-Adress }
    DTA = RECORD
            reserved : PACKED ARRAY[0..19] OF BYTE;
            attribut : integer; 
            time     : integer;
            date     : integer;
            length   : long_integer; 
            filename : file_type;
          END;

VAR filename,backslash,allfiles : file_type; 
    path : path_type; 
    i : integer;

    { Benötigte Gemdos-Routinen ; }

    PROCEDURE Fsetdta(VAR file_daten:DTA);
            GEMDOS($1A);
    { Setzt die Anfangsadresse der DTA }

    FUNCTION Fsfirst(VAR name:path_type;
                     attr:integer):integer;
            GEMDOS($4E);
    { Durchsucht das (akt.) Directory nach Dateien 
      bzw. Ordner, auf die der angegebene Name und 
      das Attribut passen. }

    FUNCTION Fsnext:integer; GEMDOS($4F);
    { Setzt die mit Fsfirst begonnene Suche fort. }

    PROCEDURE Init_List(VAR head,tail: listpointer);
    { Initialisiert eine leere lineare Liste }
    BEGIN
        new(head); 
        new(tail); 
        head^.next:=tail; 
        tail^.next:=head;
    END; { Init_List }


    PROCEDURE Append(path : path_string;
                     VAR head,tail:listpointer);
    { hängt Element path ans Ende der lineare Liste an. }
    VAR h_pointer : listpointer;
    BEGIN
        h_pointer:=tail;
        new(tail^.next);
        tail^.next^.next:=h_pointer;
        tail^.next^.path:=tail^.path;
        tail^.path:=path;
        tail:=tail^.next;
    END; { Append }


    PROCEDURE Merge_Path_File(VAR path:path_type;
                              filename:file_type);
    { Verbindet den aktuellen Pfad mit (neu gefunden) Ordnernamen.}
    VAR i,j : integer;
    BEGIN 
        i:=1;
        WHILE path[i]<>chr(0) DO 
            i:=i+1; 
        j:=1;
        REPEAT
            path[i+j-1]:=filename[j];
            j:=j+1;
        UNTIL filename[j]=chr(0);
    END; { Merge_Path_File }


    PROCEDURE PathToStr(inpath:path_type;
                        VAR outstr:Path_string); 
    {Wandelt Zeichenkette vom Type path in STRING} 
    VAR i : integer;
    BEGIN 
        i:=1;
        outstr:='';
        WHILE (inpath[i] <> chr(0)) AND (i < Max_path) DO
            BEGIN
                outstr:=concat(outstr,inpath[i]); 
                i:=i+1;
            END;
    END; { PathToStr }


    PROCEDURE StrToPath(inpath: path_string;
                        VAR path:path_type;
                        VAR filename: file_type);
    { Zerlegt den Eingabe-Pfad (STRING) in Pfad und 
      Dateiname (PACKED ARRAYS).
      z.B. A:\AUTO\WO.* ---> A:\AUTO\ und WO.* }
    VAR i,backslashpos : integer;
        h_path: path_string;
    BEGIN
        FOR i:=1 TO 14 DO
            filename[i]:=chr(0);
        FOR i:=1 TO Max_path DO 
            path[i]:=chr(0);
        { Ermittle Position des letzten \ } 
        backslashpos:=1;
        FOR i:=1 TO length(inpath) DO 
            IF inpath[i] = '\'
                THEN backslashpos:=i; 
        h_path:=copy(inpath,1,backslashpos); 
        inpath:=copy(inpath,backslashpos+1,length(inpath)-backslashpos); 
        h_path:=concat(h_path,chr(0)); 
        inpath:=concat(inpath,chr(0));
        FOR i:=1 TO length(h_path) DO 
            path[i]:=h_path[i];
        FOR i:=1 TO length(inpath) DO 
            filename[i]:=inpath[i];
    END; { StrToPath }


    PROCEDURE Search(path:path_type;searchfile:file_type;
                     VAR head,tail:listpointer);
    { eigentliche Suchroutine }
    VAR aktdta  : DTA;
        error,i : integer; 
        h_path  : path_type; 
        h_string: STRING[Max_Path];

    BEGIN
        h_path:=path;
        Merge_Path_File(h_path,allfiles);
        Fsetdta(aktdta);
        { Suche zuerst alle Ordner einer Directory-Ebene } 
        error:=Fsfirst(h_path,$10);
        WHILE error=0 DO 
            BEGIN
                IF (aktdta.attribut&$10 <> 0) AND (aktdta.filename[1] <> '.')
                THEN
                    BEGIN { Ordner gefunden } 
                        h_path:=path;
                        Merge_Path_File(h_path,aktdta.filename); 
                        Merge_Path_File(h_path,backslash); 
                        Search(h_path,searchfile,head,tail);
                        Fsetdta(aktdta);
                    END; 
                error:=Fsnext;
            END;
        { keine weiteren Ordner in dieser Ebene. ==> suche nach passenden Dateien } 
        h_path:=path;
        Merge_Path_File(h_path,searchfile); 
        error:=Fsfirst(h_path,$0);
        WHILE error=0 DO 
            BEGIN
                IF (aktdta.attribut&$18 = 0)
                THEN
                    BEGIN
                        h_path:=path;
                        Merge_Path_File(h_path,aktdta.filename); 
                        PathToStr(h_path,h_string);
                        Append(h_string,head,tail);
                    END; { Of THEN } 
                h_path:=path; 
                error:=Fsnext;
            END; { Of WHILE }
    END; { Search }

BEGIN { PROCEDURE Wo }
    {initialisiere die 'konstanten' Pfade *.* und\} 
    allfiles[1]:='*';allfiles[2]:='.';allfiles[3]:='*';
    FOR i:=4 TO 14 DO allfiles[i]:=chr(0); 
    backslash[1]:='\';
    FOR i:=2 TO 14 DO backslash[i]:=chr(0); 
    Init_List(head,tail); { erzeuge leere Liste }
    {trenne Eingabe-String in Pfad und Filename } 
    StrToPath(inpathstr,path,filename);
    { ... und beginne die Suche }
    Search(path,filename,head,tail);
END; { WO.INC }

Florian Nold
Aus: ST-Computer 12 / 1989, Seite 94

Links

Copyright-Bestimmungen: siehe Über diese Seite