fileinfo/src/DirTree.m3


--------------------------------------------------------------------------
MODULE DirTree;

IMPORT Time, TextSeq;
IMPORT FileInfo, FileStatus, FindExpr, BoolSeq, RegEx;
IMPORT APN AS APN, APNSeq AS APNSeq;
--------------------------------------------------------------------------
PROCEDURE SimpleTextDirLayout(self : SimpleTextLayout; pn : APN.T;
                              time : Time.T; size : LONGINT;
                              <*UNUSED*>level : CARDINAL;
                              lastPrefix : BoolSeq.T) : TEXT =
  VAR
    res := "";
    ilast := lastPrefix.size() - 1;
  BEGIN
    FOR i := 0 TO ilast DO
      WITH last = lastPrefix.get(i) DO
        IF i < ilast THEN
          IF last THEN
            res := res & "    ";
          ELSE
            res := res & "  | ";
          END;
        ELSE
          IF last THEN
            res := res & "  \\- ";
          ELSE
            res := res & "  |- ";
          END;
        END;
      END;
    END;
    RETURN res & self.fmtDir(pn, time, size) & "\n";
  END SimpleTextDirLayout;
--------------------------------------------------------------------------
PROCEDURE SimpleTextFileLayout(self : SimpleTextLayout; pn : APN.T;
                               time : Time.T; size : LONGINT;
                               <*UNUSED*>level : CARDINAL;
                               lastPrefix : BoolSeq.T) : TEXT =
  VAR
    res := "";
    ilast := lastPrefix.size() - 1;
  BEGIN
    FOR i := 0 TO ilast DO
      WITH last = lastPrefix.get(i) DO
        IF i < ilast THEN
          IF last THEN
            res := res & "    ";
          ELSE
            res := res & "  | ";
          END;
        ELSE
          IF last THEN
            res := res & "  \\- ";
          ELSE
            res := res & "  |- ";
          END;
        END;
      END;
    END;
    RETURN res & self.fmtFile(pn, time, size) & "\n";
  END SimpleTextFileLayout;
--------------------------------------------------------------------------
PROCEDURE SimpleTextFmtDir(<*UNUSED*>self : SimpleTextLayout;
                           pn : APN.T;
                           <*UNUSED*>time : Time.T;
                           <*UNUSED*>size : LONGINT) : TEXT =
  BEGIN
    RETURN APN.Last(pn).denotation();
  END SimpleTextFmtDir;
--------------------------------------------------------------------------
PROCEDURE SimpleTextFmtFile(<*UNUSED*>self : SimpleTextLayout;
                            pn : APN.T;
                            <*UNUSED*>time : Time.T;
                            <*UNUSED*>size : LONGINT) : TEXT =
  BEGIN
    RETURN APN.Last(pn).denotation();
  END SimpleTextFmtFile;
--------------------------------------------------------------------------
PROCEDURE SimpleTextNoSort(<*UNUSED*>self : SimpleTextLayout;
                           list : APNSeq.T) : APNSeq.T =
  BEGIN
    RETURN list;
  END SimpleTextNoSort;
--------------------------------------------------------------------------
PROCEDURE Layout(cache   :  FileInfo.T;
                 root    :  APN.T;
                 lfuns   :  LayoutClosure := NIL;
                 ignDir  :  FindExpr.T := NIL;
                 ignFile :  FindExpr.T := NIL;
                 sc      := SortCriterion.None;
                 update  := FALSE) : TEXT =

  PROCEDURE L(p : APN.T; level : CARDINAL; lastPrefix : BoolSeq.T) : TEXT =

    PROCEDURE LevelLayout(list : APNSeq.T) : TEXT =
      VAR
        fn  :  APN.T;
        res := "";
      BEGIN
        list := lfuns.sort(list);
        FOR i := 0 TO list.size() - 1 DO
          fn := list.get(i);
          VAR lp := NEW(BoolSeq.T).init(); BEGIN
            lp := BoolSeq.Cat(lp, lastPrefix);
            lp.addhi(i = list.size() - 1);
            res := res & L(fn, level + 1, lp);
          END;
        END;
        RETURN res;
      END LevelLayout;

    VAR
      dir        :  APNSeq.T;
      dirs       :  APNSeq.T;
      files      :  APNSeq.T;
      fn         :  APN.T;
      s, fstat   :  FileStatus.T;
      base       :  TEXT;
      base2      :  TEXT;
      ignoreDir  :  BOOLEAN;
      ignoreFile :  BOOLEAN;
      itemLayout :  TEXT := "";
      partLayout :  TEXT := "";
    BEGIN
      s  := cache.getStatus(p);
      IF NOT s.exists THEN RETURN "not found: " & p.denotation() END;
      base := APN.Last(p).denotation();
      TRY
        ignoreDir := ignDir # NIL AND ignDir.test(base);
      EXCEPT ELSE
        ignoreDir := FALSE;
      END;
      TRY
        ignoreFile := ignFile # NIL AND ignFile.test(base);
      EXCEPT ELSE
        ignoreFile := FALSE;
      END;
      IF s.isFile AND NOT ignoreFile THEN
        itemLayout := lfuns.file(p, s.modified, s.size, level, lastPrefix);
      ELSIF s.isDir AND NOT ignoreDir THEN
        itemLayout := lfuns.dir(p, s.modified, s.size, level, lastPrefix);
        dir  := cache.getDir(p);
        IF sc # SortCriterion.None THEN
          dirs := NEW(APNSeq.T).init();
          files := NEW(APNSeq.T).init();
          FOR i := 0 TO dir.size() - 1 DO
            fn := dir.get(i);
            base2 := APN.Last(fn).denotation();
            fstat := cache.getStatus(fn);
            TRY
              IF fstat.isDir AND
                (ignDir = NIL OR NOT ignDir.test(base2)) THEN
                dirs.addhi(fn);
              ELSIF fstat.isFile AND
                (ignFile = NIL OR NOT ignFile.test(base2)) THEN
                files.addhi(fn);
              END;
            EXCEPT
              RegEx.Error =>
              IF fstat.isDir THEN
                dirs.addhi(fn);
              ELSIF fstat.isFile THEN
                files.addhi(fn);
              END;
            END;
          END;
          IF sc = SortCriterion.DirsFirst THEN
            partLayout := LevelLayout(APNSeq.Cat(dirs, files));
          ELSIF sc = SortCriterion.FilesFirst THEN
            partLayout := LevelLayout(APNSeq.Cat(files, dirs));
          END;
        ELSE
          partLayout := LevelLayout(dir);
        END;
      ELSIF NOT ignoreDir AND NOT ignoreFile THEN
        itemLayout := lfuns.file(p, s.modified, s.size, level, lastPrefix) &
                          " (unknown file type)\n";
      END;
      RETURN itemLayout & partLayout;
    END L;

  VAR
    last : BoolSeq.T;
  BEGIN (* Layout *)
    IF cache = NIL THEN RETURN NIL END;
    IF update THEN
      cache.updateRec(root, emptyTextSeq, emptyAPNSeq, ignDir, ignFile);
    END;
    IF lfuns = NIL THEN
      lfuns := simpleTextLayout;
    END;
    last := NEW(BoolSeq.T).init();
    last.addhi(TRUE);
    RETURN L(root, 0, last);
  END Layout;

VAR
  emptyTextSeq := NEW (TextSeq.T).init();
  emptyAPNSeq  := NEW (APNSeq.T).init();
BEGIN
  simpleTextLayout := NEW(SimpleTextLayout);
END DirTree.

interface FileInfo is in:


interface FileStatus is in:


interface RegEx is in: