Der Computeruser, der über sich ewig das Damoklesschwert des Stromausfalls oder des 'Daten auf Disk X defekt?’ sieht, lässt natürlich bei jedem Abspeichern das alte File irgendwie umbenennen und überschreibt es selbstverständlich nicht. Aber, wenn nach arbeitsreicher Nacht alles geklappt hat und man die diversen BAK-, DUP-, SBK- etc. Files nicht mehr benötigt?
Grund für das 1. Utility: ‘DelBak‘. Und dann doch der Frust: Die wichtigste Datei wurde nicht korrekt abgespeichert, das Backup-File natürlich gelöscht. Aber irgendwo auf der Platte war doch noch eine alte Version! Nur wo? Schon mal durch ‘zig Ordner durchgeklickt? Womit wir beim 2.Utility wären: ‘Find’.
DelBak sucht auf einem Laufwerk nach allen Dateien mit den Endungen BAK (1st Word, GFA-BASIC etc.), DUP (Tempus) und SBK (Signum). Diese werden dem Benutzer jeweils auf dem Bildschirm angezeigt, der sie dann mit beliebiger Taste außer n/N löschen kann. Das zu durchsuchende Laufwerk muß beim Programmstart (‘DELBAK.TTP’) als Argument übergeben werden.
Beispiel:
delbak d sucht alle Backups auf Laufwerk D
Find sucht, wieder auf einem vorgegebenen Laufwerk, nach Dateien, deren Name den übergebenen String enthält.
Beispiel:
find d delbak findet (in unserem Fall) die Dateien delbak.m, delbak.mtp und delbak.ttp.
Es werden jeweils alle passenden Dateien zusammen mit dem jeweiligen Suchpfad ausgegeben.
Beide Utilities entstanden nach demselben Muster, was man auch an der ähnlichen Programmstruktur erkennen kann. Sie sind aber doch unterschiedlich genug, um beide abzudrucken, da eine Erklärung der nötigen Umbauten zuviel Platz einnehmen würde.
Kernpunkt der beiden Programme ist die Routine DirQuery. Im Prinzip ist sie eine Zusammenfassung der beiden GEMDOS-Routinen Fsfirst und Fsnext, wobei bei jeder Übereinstimmung eines Directory-Eintrags mit dem vorgegebenen Pattern und den Fileattributen eine vom Programmierer festgelegte Prozedur aufgerufen wird. Diese Prozedur (in unserem Programmen wäre das TestIfBAK bzw. TestIfFound) erhält als Parameter den Pfadnamen des gefundenen Eintrags sowie einen Record, der den Eintrag näher beschreibt. Dieser Record entspricht der Datenstruktur, die von Fsfirst/Fsnext zurückgeliefert wird.
Die Argumentzeilenauswertung gestaltet sich (dank einem guten Library-Modul) recht einfach: Mit InitArgCV wird die Anzahl der Argumenten+ in die Variable ArgC gelegt, ArgV ist ein Array von Pointern, welche auf die einzelnen Argumentstrings zeigen. ArgV[0] ist dabei immer leer (unter UNIX steht hier der Name des aufgerufenen Programms), DelBak muß daher auf 2 ‘Argumente’ testen, Find auf 3. Finden die beiden Programme nicht die richtige Anzahl Argumente vor, geben sie einen kurzen Text mit der Syntax des Aufrufs aus.
Wenn einem Megamax-Modulisten der Import aus Fast-Strings spanisch vorkommt: Es ist zwar kein Standardmodul (ätsch), aber auf zwei Arten erhältlich:
Das Modul selbst stammt von Thomas Tempelmann (Danke!!) und erfüllt in etwa die gleichen Aufgaben wie das standardmäßige Stringmodul, allerdings viel schneller (keine Fehlerkontrolle, VAR-Parameter). Wenn man also nicht in der Lage ist, das FastStrings-Modul zu bekommen, kann man auch das normale verwenden.
Beim Linken müssen außer M2INIT keine Module dazugelinkt werden. Wenn man zu den Glücklichen gehört, die bereits einen optimierenden Linker besitzen, erhält man zwei wirklich kleine (< 5kB) Programme in Modula-2!! Viele mögen sich fragen, ob man das Ende eines solchen Programmes in Modula-2 überhaupt erlebt. Nun, man tut es. Das Suchen auf einer 8 MByte-Partition (Füllungsgrad ca. 50%) dauerte etwa 8s! Wenn man da keinen Geschwindigkeitsrausch bekommt ...
(*************************************************
* TITEL : DelBak *
* ZWECK : Löscht Backup-Dateien *
* AUTOREN : (c) 1988 WuSeL-Soft *
* Martin Wunderli & Patrick Seemann *
* DATUM : 01.09.1988 : Version 1.0 *
* SPRACHE : MODULA-2 (MEGAMAX MODULA V1.0) *
* Compilerversion 3.6a *
************************************************)
(*$E MTP *)
MODULE DelBak;
FROM ArgCV IMPORT
(* TYPES *) ArgStr, PtrArgStr,
(* PROCS *) InitArgCV;
FROM Directory IMPORT
(* TYPES *) FileAttr, FileAttrSet, DirEntry, DirQueryProc, Drive,
(* PROCS *) DirQuery, Delete, DefaultDrive, StrToDrive, SetDefaultDrive;
FROM FastStrings IMPORT
(* PROCS *) Append, Assign, Pos;
FROM Terminal IMPORT
(* PROCS *) WriteString, WriteLn, Write, Read;
TYPE str4 = ARRAY [0..3] OF CHAR;
VAR Result : INTEGER;
Pattern : ArgStr;
NewDrive,
BAK, DUP, SBK : str4;
James : CHAR;
ArgC : CARDINAL;
ArgV : ARRAY [0..1] OF PtrArgStr;
AktDrive : Drive;
(************************************************)
PROCEDURE TestIfBAK ( (* in *) Pfad : ARRAY OF CHAR;
(* in *) Entry : DirEntry): BOOLEAN;
VAR NeuerPfad : ArgStr;
BEGIN
IF (subdirAttr IN Entry.attr) THEN
IF NOT(Entry.name[0] = THEN
Assign (Pfad, NeuerPfad);
Append (Entry.name,NeuerPfad);
Append (Pattern, NeuerPfad);
DirQuery (NeuerPfad,FileAttrSet{subdirAttr},TestIfBAK,Result);
END (* IF *);
ELSE
IF (Pos (BAK,Entry.name) >= 0) OR
(Pos (DUP,Entry.name) >= 0) OR
(Pos (SBK,Entry.name) >= 0) THEN
Write (" ");
WriteString (Pfad);
WriteString (Entry.name);
WriteString (" Löschen (_/N)? ");
Read (James);
IF CAP(James) <> "N" THEN
Assign (Pfad, NeuerPfad);
Append (Entry.name,NeuerPfad);
Delete (NeuerPfad,Result);
END (* IF *);
WriteLn;
END (* IF *);
END (* IF *);
RETURN TRUE
END TestIfBAK;
(************************************************)
BEGIN
Pattern := "\*.*":
NewDrive := "X:";
BAK := ".BAK";
DUP := ".DUP";
SBK := ".SBK";
InitArgCV (ArgC, ArgV);
IF ArgC = 2 THEN
AktDrive := DefaultDrive();
NewDrive[0] := CAP(ArgV[1]^[0]);
SetDefaultDrive (StrToDrive (NewDrive));
DirQuery (Pattern, FileAttrSet{subdirAttr}, TestIfBAK, Result);
SetDefaultDrive (AktDrive);
ELSE
WriteString (" Usage: delbak <drive>");
WriteLn;
END (* IF *);
END DelBak.
DelBak Programm
(*************************************************
* TITEL : Find *
* ZWECK : Findet eine Datei auf dem über- *
* gegebenen Laufwerk *
* AUTOREN : (c) 1988 WuSeL-Soft *
* Martin Wunderli & Patrick Seemann*
* DATUM : 01.09.1988 : Version 1.0 *
* SPRACHE : MODULA-2 (MEGAMAX MODULA V1.0) *
* Compilerversion 3.6a *
************************************************)
(*$E MTP *)
MODULE Find;
FROM ArgCV IMPORT
(* TYPES *) ArgStr, PtrArgStr,
(* PROCS *) InitArgCV;
FROM Directory IMPORT
(* TYPES *) FileAttr, FileAttrSet, DirEntry, DirQueryProc, Drive,
(* PROCS *) DirQuery, Delete, DefaultDrive, StrToDrive, SetDefaultDrive;
FROM FastStrings IMPORT
(* PROCS *) Append, Assign, Length, Pos;
FROM Terminal IMPORT
(* PROCS *) WriteString, WriteLn, Read;
TYPE str4 = ARRAY [0..3] OF CHAR;
VAR Result : INTEGER;
Pattern, File : ArgStr;
NewDrive : str4;
ArgC, i : CARDINAL;
ArgV : ARRAY [0..2] OF PtrArgStr;
AktDrive : Drive;
James : CHAR;
(*************************************************
PROCEDURE TestlfFound ( (* in *) Pfad : ARRAY OF CHAR;
(* in *) Entry : DirEntry): BOOLEAN;
VAR NeuerPfad : ArgStr;
BEGIN
IF (subdirAttr IN Entry.attr) THEN
IF NOT(Entry.name[0] = THEN
Assign (Pfad, NeuerPfad);
Append (Entry.name,NeuerPfad);
Append (Pattern, NeuerPfad);
DirQuery (NeuerPfad,FileAttrSet{subdirAttr}, TestIfFound, Result);
END (* IF *);
ELSE
IF (Pos (File, Entry.name) >= 0) THEN
WriteString (" Found: ") ;
WriteString (Pfad);
WriteString (Entry.name);
WriteLn;
END (* IF *);
END (* IF *);
RETURN TRUE
END TestIfFound;
(************************************************)
BEGIN
NewDrive := "X:";
Pattern := "\*.*";
InitArgCV (ArgC, ArgV);
IF ArgC = 3 THEN
AktDrive := DefaultDrive();
NewDrive[0] := CAP(ArgV[1]^[0]);
SetDefaultDrive (StrToDrive (NewDrive));
FOR i := 0 TO Length (ArgV[2]^) DO
File [i] := CAP (ArgV[2]^[i]);
END (* FOR i *);
DirQuery (Pattern, FileAttrSet{subdirAttr}, TestIfFound, Result);
SetDefaultDrive (AktDrive);
ELSE
WriteString ("Usage: find <drive> <filename>");
WriteLn;
END (* IF *);
WriteString ("Bitte Taste drücken!");
Read (James);
END Find.
Find Programm