pkgobj/src/Common/PackageLib.m3


Copyright (C) 1994, Digital Equipment Corp.
PackageLib.m3

MODULE PackageLib;

IMPORT FileSys, OSError, PackageObj, Pathname, NetPath, PkgErr,
       RefList, TextList, TextListSort, Rd, Text, Time, Thread;

EXCEPTION FatalError; <* FATAL FatalError *>

PROCEDURE Enumerate (e: EnumClosure): PackageObj.DirEnum
    RAISES {OSError.E} =
  CONST StatsPerWait = 40;
  VAR waitCtr: CARDINAL := StatsPerWait;
  PROCEDURE Build (path: FileSys.FN): PackageObj.DirEnum
      RAISES {OSError.E} =
    VAR t: TEXT;
        edir, cpath, spath: FileSys.FN;
        eList: FileSys.Enumeration;
        res: PackageObj.DirEnum := NIL;
        elem: PackageObj.DirElem;
    BEGIN
      TRY
        edir := e.acquire();
        IF path # NIL THEN
          edir := Pathname.Join(edir, path, NIL);
        END;
        eList := FileSys.Enumerate (edir);
      FINALLY
        e.release();
      END;
      (* hack for broken List generic *)
      (* IF eList = NIL THEN RETURN NIL; END; *)
      eList := TextList.ReverseD(TextListSort.SortD(eList));
      WHILE eList # NIL DO
        t := eList.head;
        IF NOT SpecialFile(t) THEN
          (* gross hackery for lack of multithreaded OS *)
          DEC(waitCtr);
          IF waitCtr = 0 THEN
            Thread.Pause(5.0D-2);
            waitCtr := StatsPerWait;
          END;
          (* end of hackery *)
          elem := NEW (PackageObj.DirElem);
          elem.children := NIL;
          elem.arc := t;
          cpath := Pathname.Join(path, t, NIL);
          TRY
            spath := Pathname.Join(e.acquire(), cpath, NIL);
            elem.info := FileSys.GetInfo(spath, FALSE);
            IF elem.info.type = FileSys.FileType.SLink THEN
              elem.referent := FileSys.ReadLink(spath);
            ELSE
              elem.referent := NIL;
            END;
          FINALLY
            e.release();
          END;
          CASE elem.info.type OF
          | FileSys.FileType.Other =>
          | FileSys.FileType.Dir =>
              elem.children := Build(cpath);
              res := RefList.Cons(elem, res);
          ELSE
              res := RefList.Cons(elem, res);
          END;
        END;
        eList := eList.tail;
      END;
      RETURN res;
    END Build;
  BEGIN
    RETURN Build(NIL);
  END Enumerate;

PROCEDURE Compare(src, dest: PackageObj.DirEnum; cl: DiffClosure)
    RAISES {Thread.Alerted} =
  VAR
     dirName: FileSys.FN := NIL;
  PROCEDURE CompareInner(
      arc: TEXT; src, dest: PackageObj.DirEnum; cl: DiffClosure)
      RAISES {Stop, Thread.Alerted} =
    VAR e1,e2: PackageObj.DirElem;
        comp: [-1..1];
        saveDir := dirName;
    BEGIN
      IF src = NIL AND dest = NIL THEN RETURN; END;
      IF arc # NIL THEN dirName := Pathname.Join(dirName, arc, NIL); END;
      WHILE src # NIL OR dest # NIL DO
        IF dest = NIL THEN
          e1 := src.head;
          comp := -1;
        ELSIF src = NIL THEN
          e2 := dest.head;
          comp := 1;
        ELSE
          e1 := src.head;
          e2 := dest.head;
          comp := TextCompare(e1.arc, e2.arc);
        END;
        CASE comp OF
        | 1 =>
            dest := dest.tail;
            cl.report(dirName, DiffType.NoSrc, e2);
            CompareInner(e2.arc, NIL, e2.children, cl);
        | 0 =>
            src := src.tail;
            dest := dest.tail;
            cl.report(dirName, CompareInfo(e1,e2), e1);
            CompareInner(e1.arc, e1.children, e2.children, cl);
        | -1 =>
            src := src.tail;
            cl.report(dirName, DiffType.NoDest, e1);
            CompareInner(e1.arc, e1.children, NIL, cl);
        END;
      END;
      dirName := saveDir;
    END CompareInner;
  BEGIN
    TRY
      CompareInner(NIL, src, dest, cl);
    EXCEPT Stop =>
    END;
  END Compare;

PROCEDURE TextCompare(t1, t2: TEXT) : [-1..1] =
  BEGIN
    IF t1 = NIL THEN
      IF t2 = NIL THEN RETURN 0; END;
      RETURN 1;
    ELSIF t2 = NIL THEN
      RETURN -1;
    ELSE
      RETURN Text.Compare(t1, t2);
    END;
  END TextCompare;

PROCEDURE CompareInfo(e1, e2: PackageObj.DirElem): DiffType =
  BEGIN
    IF e1.info.type # e2.info.type THEN RETURN DiffType.TypesDiffer; END;
    CASE e1.info.type OF
    | FileSys.FileType.Dir => RETURN DiffType.Same;
    | FileSys.FileType.Normal =>
       IF e1.info.date = e2.info.date THEN
         IF e1.info.length # e2.info.length THEN
           RETURN DiffType.LengthsDiffer;
         END;
         IF e1.info.perm # e2.info.perm THEN
           RETURN DiffType.ModesDiffer;
         END;
       ELSIF e1.info.date > e2.info.date THEN
         RETURN DiffType.SrcNewer;
       ELSE
         RETURN DiffType.SrcOlder;
       END;
    | FileSys.FileType.SLink =>
       IF NOT Text.Equal(e1.referent, e2.referent) THEN
         RETURN DiffType.LinksDiffer;
       END;
    | FileSys.FileType.Other => RAISE FatalError;
    END;
    RETURN DiffType.Same;
  END CompareInfo;

PROCEDURE SpecialFile (t: Text.T): BOOLEAN =
  BEGIN
    IF Text.GetChar (t, 0) # '.' THEN RETURN FALSE;  END;
    RETURN Text.Equal (t, VersionFile) OR Text.Equal (t, ExportLinkFile);
  END SpecialFile;

PROCEDURE SetDirDates(
    <*UNUSED*>path: FileSys.FN; <*UNUSED*>enum: PackageObj.DirEnum) =
  BEGIN
  END SetDirDates;

TYPE
  SpecialSource = PackageObj.Source OBJECT
  OVERRIDES
    enum := SpecialEnumerate;
    pullFile := NullPullFile;
    links := NullExportLinks;
  END;

PROCEDURE SpecialEnumerate(<*UNUSED*> s: SpecialSource) : PackageObj.Enum
    RAISES {} =
  BEGIN
    RETURN PackageObj.Enum{Time.Now(), NIL};
  END SpecialEnumerate;

PROCEDURE NullPullFile(<*UNUSED*>s: SpecialSource;
                       <*UNUSED*>path: NetPath.T) : Rd.T
    RAISES {PkgErr.E} =
  BEGIN
    PkgErr.Raise(PkgErr.NoSuchFile);
    RETURN NIL;
  END NullPullFile;

PROCEDURE NullExportLinks(<*UNUSED*> s: SpecialSource) : PackageObj.ExportLinks
    RAISES {} =
  BEGIN
    RETURN NIL;
  END NullExportLinks;

PROCEDURE EmptySource() : PackageObj.Source =
  BEGIN
    RETURN NEW(SpecialSource);
  END EmptySource;

BEGIN
END PackageLib.