language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/general/filename.mod
IMPLEMENTATION MODULE FileNames;
(********************************************************)
(* *)
(* File name parsing *)
(* *)
(* Programmer: P. Moylan *)
(* Last edited: 21 February 1995 *)
(* Status: Just started *)
(* Apparently complete but untested. *)
(* *)
(* This module, which is part of the file system *)
(* and is not intended for end-user use, looks *)
(* after translating file name strings into a *)
(* more convenient internal form. *)
(* *)
(* It is still questionable whether the syntax *)
(* imposed by this module is the best one. *)
(* Need to check the rules for DOS file names *)
(* more carefully. Should perhaps also report *)
(* an incompletely consumed name string as an *)
(* error. *)
(* *)
(********************************************************)
FROM Windows IMPORT (* for testing *)
(* type *) Window,
(* proc *) OpenSimpleWindow, WriteString, WriteChar, WriteLn;
FROM Devices IMPORT
(* proc *) NullDevice, IdentifyDevice;
FROM Storage1 IMPORT
(* proc *) ALLOCATE, DEALLOCATE;
(************************************************************************)
TYPE CharSet = SET OF CHAR;
CONST Nul = CHR(0);
(************************************************************************)
(* FOR TESTING *)
(************************************************************************)
CONST testing = FALSE;
VAR debug: Window; (* used only for testing *)
(************************************************************************)
(* PARSING A FILE NAME *)
(************************************************************************)
PROCEDURE WriteFileName (w: Window; name: FileName);
(* For testing: writes name in window w. *)
BEGIN
WriteString (w, name.fname); WriteChar (w, ".");
WriteString (w, name.fext);
END WriteFileName;
(************************************************************************)
PROCEDURE SimpleScan (VAR (*IN*) string: ARRAY OF CHAR;
VAR (*OUT*) substring: ARRAY OF CHAR;
VAR (*OUT*) separator: CHAR;
VAR (*INOUT*) point: CARDINAL);
(* Extracts a substring of "string", starting at string[point] *)
(* and continuing until one of the characters ".", ":", "\", " ", *)
(* or Nul is found. The terminating character is returned as *)
(* "separator". (If we reach the end of the string, Nul is *)
(* returned as the separator.) On return, point is updated so that *)
(* string[point] is the first character after the separator, except *)
(* in the end-of-string case where the value of point is irrelevant.*)
(* Alphabetic characters are converted to upper case in substring. *)
(* If array substring is longer than the substring extracted by *)
(* this procedure, the trailing character positions are not *)
(* disturbed - it is up to the caller to supply defaults. *)
CONST SeparatorSet = CharSet {Nul, " ", ".", ":", "\"};
VAR j: CARDINAL;
BEGIN
j := 0;
LOOP
IF point > HIGH(string) THEN EXIT (*LOOP*)
ELSIF string[point] IN SeparatorSet THEN EXIT(*LOOP*)
ELSIF j <= HIGH(substring) THEN
substring[j] := CAP(string[point]); INC(j);
END (*IF*);
INC (point);
END (*LOOP*);
IF point <= HIGH(string) THEN
separator := string[point]; INC(point);
ELSE
separator := Nul;
END (*IF*);
END SimpleScan;
(************************************************************************)
PROCEDURE Scan (VAR (*IN*) string: ARRAY OF CHAR;
VAR (*OUT*) name: FileName;
VAR (*OUT*) separator: CHAR;
VAR (*INOUT*) point: CARDINAL);
(* Extracts a file name from "string", starting at string[point]. *)
(* The terminating character is returned as "separator". (If we *)
(* reach the end of the string, Nul is returned as the separator). *)
(* On return, point is updated so that string[point] is the first *)
(* character after the separator, except in the end-of-string case *)
(* where the value of point is irrelevant. *)
BEGIN
name.fname := " "; name.fext := " ";
SimpleScan (string, name.fname, separator, point);
IF separator = "." THEN
SimpleScan (string, name.fext, separator, point);
END (*IF*);
END Scan;
(************************************************************************)
(* THE EXTERNALLY CALLABLE PROCEDURES *)
(************************************************************************)
PROCEDURE Parse (name: ARRAY OF CHAR; VAR (*OUT*) device: Device;
VAR (*OUT*) unit: CARDINAL; VAR (*OUT*) result: NameList;
VAR (*OUT*) StartAtRoot: BOOLEAN);
(* Translates a text string "name" into NameList form. If the *)
(* string includes a device specification this is returned as *)
(* (device, unit); otherwise we return with device = NullDevice. *)
(* Output parameter StartAtRoot is TRUE iff the filename string *)
(* started with a '\'. *)
CONST Terminators = CharSet {Nul, " ", ".", ":"};
VAR separator: CHAR;
place: CARDINAL;
devicestring: EightChar;
previous, current: NameList;
BEGIN
place := 0; devicestring := " "; result := NIL;
(* Extract the device name, if present. *)
SimpleScan (name, devicestring, separator, place);
IF separator = ":" THEN
IdentifyDevice (devicestring, device, unit);
ELSE
devicestring := " ";
device := NullDevice(); place := 0;
END (*IF*);
IF testing THEN
WriteString (debug, "The device name is ");
WriteString (debug, devicestring); WriteLn (debug);
END (*IF*);
(* A leading "\" indicates the root directory. *)
StartAtRoot := name[place] = "\";
IF StartAtRoot THEN
INC (place);
END (*IF*);
(* Check whether this is all that was supplied. *)
IF (place > HIGH(name)) OR (name[place] IN Terminators) THEN
RETURN;
END (*IF*);
(* Now fill in all the details of the path. *)
current := NIL;
REPEAT
previous := current;
NEW (current);
IF previous = NIL THEN result := current;
ELSE previous^.child := current;
END (*IF*);
WITH current^ DO
Scan (name, string, separator, place);
child := NIL;
END (*WITH*);
UNTIL separator <> '\';
END Parse;
(************************************************************************)
PROCEDURE DiscardNameList (VAR (*INOUT*) path: NameList);
(* Disposes of the storage occupied by a NameList. Returns NIL. *)
VAR current: NameList;
BEGIN
WHILE path <> NIL DO
current := path; path := current^.child;
DISPOSE (current);
END (*WHILE*);
END DiscardNameList;
(************************************************************************)
BEGIN
IF testing THEN
OpenSimpleWindow (debug, 0, 4, 0, 79);
END (*IF*);
END FileNames.