m3tk/src/files/Common/M3PathFile.m3


Copyright (C) 1994, Digital Equipment Corp.

MODULE M3PathFile;

IMPORT Rd, OSError, FileRd, Text, RdExtras, ASCII, Pathname, TextList;
IMPORT M3PathElem, M3PathElemList, M3PathElemOS;

PROCEDURE Read(
    dir := M3PathElem.CurrentDir;
    name := FileName;
    doTransitiveClosure := TRUE)
    : M3PathElemList.T
    RAISES {OSError.E, Rd.Failure}=
  VAR
    result: M3PathElemList.T := NIL;
  BEGIN
    IF NOT doTransitiveClosure THEN result := ReadOneDir(dir, name);
    ELSE result :=  ReadAllDirs(dir, name);
    END;
    RETURN result;
  END Read;

PROCEDURE ReadOneDir(
    dir := "";
    name := FileName)
    : M3PathElemList.T
    RAISES {OSError.E, Rd.Failure}=
  VAR
    result: M3PathElemList.T := NIL;
  BEGIN
    AddOneDir("", Concat(dir, name), result);
    RETURN result;
  END ReadOneDir;
PRIVATE
PROCEDURE AddOneDir(
    relativeTo := "";
    m3pathName := FileName;
    VAR result: M3PathElemList.T)
    RAISES {OSError.E, Rd.Failure}=
  VAR
    s: Rd.T;
    readOnly := FALSE;
    name: TEXT;
  BEGIN
    TRY
      s := FileRd.Open(m3pathName);
    EXCEPT
    | OSError.E =>  RETURN (* ignore if cannot be opened *)
    END;

    TRY (*finally close *)
      LOOP
        TRY
	  name := ReadName(s, readOnly);
	  AddUniqueName(relativeTo, name, readOnly, list := result);
        EXCEPT Rd.EndOfFile => EXIT;
        END;
      END; (*loop through file *)
    FINALLY Rd.Close(s);
    END;
  END AddOneDir;
PRIVATE
PROCEDURE ReadName(
    s: Rd.T;
    VAR (*out*) readOnly: BOOLEAN)
    : Text.T
    RAISES {Rd.Failure, Rd.EndOfFile} =
  CONST SpaceOrTab = SET OF CHAR{' ', '\t'};
  BEGIN
    readOnly := FALSE;
    TRY
      WHILE RdExtras.Skip(s, SpaceOrTab)
            IN SET OF CHAR{'#'} + SET OF CHAR{'\n', '\r', '\f'} DO
        FlushLine(s);
      END;
      VAR result := RdExtras.GetText(s);
      BEGIN
      	IF ASCII.Upper[RdExtras.Skip(s, SpaceOrTab)] = 'R' THEN
	  readOnly := TRUE;
	END; (* if *)
        RETURN result;
      END;
    FINALLY FlushLine(s);
    END;
  END ReadName;
PRIVATE
PROCEDURE ReadAllDirs(
    dir, m3PathName: Text.T)
    : M3PathElemList.T
    RAISES {Rd.Failure, OSError.E}=
  VAR
    initialList: M3PathElemList.T := NIL;
  BEGIN
    AddOneDir("", Concat(dir, m3PathName), initialList);
    DirWalk(dir, m3PathName, initialList);
    RETURN initialList;
  END ReadAllDirs;
PRIVATE
PROCEDURE DirWalk(
    relativeTo: Text.T;
    m3PathName: Text.T;
    VAR listSoFar: M3PathElemList.T)
    RAISES {OSError.E, Rd.Failure}=
  VAR
    temp := listSoFar;
  BEGIN
    WHILE temp # NIL DO
      WITH t = temp.head.text() DO
        AddOneDir(t, Concat(relativeTo, Concat(t, m3PathName)), listSoFar);
        temp := temp.tail;
      END;
    END; (*while*)
  END DirWalk;
PRIVATE
PROCEDURE Concat(head, tail: Text.T): Text.T RAISES {} =
  BEGIN
    IF IsLocalDir(tail) THEN RETURN head;
    ELSIF IsLocalDir(head) THEN RETURN tail;
    ELSE RETURN M3PathElemOS.RemoveParentDenotations(
                    Pathname.Join(head, tail, NIL));
    END;
  END Concat;

PROCEDURE IsLocalDir(dir: Text.T): BOOLEAN RAISES {} =
  BEGIN
    RETURN (Text.Length(dir) = 0) OR Text.Equal(dir, Pathname.Current);
  END IsLocalDir;
PRIVATE
PROCEDURE AddUniqueName(
    dir, name: Text.T;
    readOnly := FALSE;
    VAR list: M3PathElemList.T)
    RAISES {OSError.E} =
  VAR
    expName := M3PathElemOS.EnvExpand(name); (* full name, expanded *)
    unexpName: Text.T; (* full name, unexpanded *)
  BEGIN
    IF Pathname.Absolute(expName) THEN
      unexpName := name;
    ELSE
      (* concat dir & name, both expanded and not *)
      unexpName := Concat(dir, name);
      expName := Concat(dir, expName);
    END;
    VAR t := list;
        elem := M3PathElem.FromText(expName, unexpName, readOnly);
    BEGIN
      WHILE t # NIL DO
        IF t.head = elem THEN RETURN
        ELSE t := t.tail;
        END
      END;
      list := M3PathElemList.AppendD(list, M3PathElemList.List1(elem));
    END
  END AddUniqueName;
PRIVATE
PROCEDURE FlushLine(s: Rd.T) RAISES {Rd.Failure, Rd.EndOfFile}=
  BEGIN
    EVAL RdExtras.Skip(s, ASCII.Asciis - ASCII.Set{'\n', '\r', '\f'},
                       unget := FALSE);
  END FlushLine;

BEGIN
END M3PathFile.