dirfp/src/Main.m3


 Copyright 1996-2000, Critical Mass, Inc.  All rights reserved. 
 See file COPYRIGHT-CMASS for details. 

UNSAFE MODULE Main;

IMPORT Atom, AtomList, File, FileRd, Fingerprint, FS, OSError, Params, Pathname;
IMPORT Rd, Stdio, Text, Thread, Word, Wr;

PROCEDURE Gen (arg: TEXT) =
  BEGIN
    IF Text.GetChar (arg, 0) = '@' THEN
      GenFile (Text.Sub (arg, 1));
    ELSE
      Visit (arg);
    END;
  END Gen;

PROCEDURE GenFile (nm: TEXT) =
  VAR
    rd    : Rd.T;
    start : INTEGER;
    len   : INTEGER;
    buf   : ARRAY [0..255] OF CHAR;
  BEGIN
    TRY
      rd := FileRd.Open (nm);
      TRY
        WHILE NOT Rd.EOF (rd) DO
          len := Rd.GetSubLine (rd, buf);
          start := 0;
          WHILE (start < len) AND WhiteSpace [ORD (buf[start])] DO INC (start); END;
          WHILE (len > start) AND WhiteSpace [ORD (buf[len-1])] DO DEC (len); END;
          IF start < len THEN
            Visit (Text.FromChars (SUBARRAY (buf, start, len - start)));
          END;
        END;
      FINALLY
        Rd.Close (rd);
      END;
    EXCEPT
    | OSError.E (ec) =>
        Out ("** unable to open ", nm, OSErr (ec));
    | Rd.Failure (ec) =>
        Out ("** trouble reading ", nm, OSErr (ec));
    | Thread.Alerted =>
        Out ("** interrupted while reading ", nm);
    END;
  END GenFile;

PROCEDURE Visit (nm: TEXT) =
  BEGIN
    IF IsDirectory (nm)
      THEN VisitDir (nm);
      ELSE VisitFile (nm);
    END;
  END Visit;
--------------------------------------------------------- directories ---

PROCEDURE VisitDir (dir: TEXT) =
  VAR iter: FS.Iterator;  nm: TEXT;
  BEGIN
    TRY
      iter := FS.Iterate (dir);
      WHILE iter.next (nm) DO
        Visit (Pathname.Join (dir, nm, NIL));
      END;
    EXCEPT OSError.E (ec) =>
      Out ("trouble enumerating contents of ", dir, OSErr (ec));
    END;
    iter.close ();
  END VisitDir;
--------------------------------------------------------------- files ---

TYPE
  Buffer = REF ARRAY OF File.Byte;

VAR
  buf := NEW (Buffer, 10000);
  WhiteSpace: ARRAY File.Byte OF BOOLEAN;

PROCEDURE VisitFile (nm: TEXT) =
  VAR
    len := Inhale (nm, buf);
    ptr := LOOPHOLE (buf, REF ARRAY OF CHAR);
    fp  := Fingerprint.FromChars (SUBARRAY (ptr^, 0, len), Fingerprint.OfEmpty);
  BEGIN
    <*ASSERT BYTESIZE(CHAR) = BYTESIZE(File.Byte)*>
    Out (nm, " ", FPText (fp));
  END VisitFile;

PROCEDURE FPText (READONLY fp: Fingerprint.T): TEXT =
  CONST Digit = ARRAY [0..15] OF CHAR { '0','1','2','3','4','5','6','7',
                                        '8','9','a','b','c','d','e','f' };
  VAR buf: ARRAY [0..15] OF CHAR;  len := 0;
  BEGIN
    FOR i := 0 TO 7 DO
      buf [len] := Digit [Word.Extract (fp.byte[i], 4, 4)];  INC (len);
      buf [len] := Digit [Word.Extract (fp.byte[i], 0, 4)];  INC (len);
    END;
    RETURN Text.FromChars (buf);
  END FPText;

PROCEDURE Inhale (fn: TEXT;  VAR buf: Buffer): CARDINAL =
  VAR f: File.T;  len, next, got: INTEGER;
  BEGIN
    TRY
      f := FS.OpenFileReadonly (fn);
      len := VAL(f.status().size, INTEGER);
      IF len > NUMBER (buf^) THEN ExpandBuf (buf, len); END;

      next := 0;
      WHILE (len > 0) DO
        got := f.read (SUBARRAY (buf^, next, len), mayBlock := TRUE);
        INC (next, got);
        DEC (len, got);
      END;

      f.close ();
    EXCEPT OSError.E(ec) =>
      Out ("trouble reading ", fn, OSErr (ec));
      RETURN 0;
    END;

    RETURN RemoveBlanks (buf, next);
  END Inhale;

PROCEDURE RemoveBlanks (buf: Buffer;  len: CARDINAL) : CARDINAL =
  VAR s0: CARDINAL := 0;  s1: CARDINAL := 0;  c: File.Byte;
  BEGIN
    WHILE (s0 < len) DO
      c := buf[s0];
      IF WhiteSpace [c] THEN
        buf[s1] := ORD (' ');  INC (s1);  INC (s0);
        WHILE (s0 < len) AND WhiteSpace [buf[s0]] DO INC (s0); END;
      ELSE
        buf[s1] := c;  INC (s0);  INC (s1);
      END;
    END;
    RETURN s1;
  END RemoveBlanks;

PROCEDURE ExpandBuf (VAR buf: Buffer;  len: INTEGER) =
  VAR n := NUMBER (buf^);
  BEGIN
    WHILE (n < len) DO INC (n, n); END;
    buf := NEW (Buffer, n);
  END ExpandBuf;
---------------------------------------------------------------- misc ---

PROCEDURE IsDirectory (file: TEXT): BOOLEAN =
  BEGIN
    TRY
      WITH stat = FS.Status (file) DO
        RETURN stat.type = FS.DirectoryFileType;
      END
    EXCEPT
    | OSError.E => RETURN FALSE;
    END
  END IsDirectory;

PROCEDURE OSErr (args: AtomList.T): TEXT =
  VAR msg : TEXT := NIL;
  BEGIN
    WHILE (args # NIL) DO
      IF (msg = NIL) THEN  msg := ": ";  ELSE  msg := msg & "  ***  ";  END;
      msg  := msg & Atom.ToText (args.head);
      args := args.tail;
    END;
    IF (msg = NIL) THEN msg := ": ** NO INFO **"; END;
    RETURN msg;
  END OSErr;

PROCEDURE Out (a, b, c, d: TEXT := NIL) =
  <*FATAL ANY*>
  VAR wr := Stdio.stdout;
  BEGIN
    IF (a # NIL) THEN Wr.PutText (wr, a); END;
    IF (b # NIL) THEN Wr.PutText (wr, b); END;
    IF (c # NIL) THEN Wr.PutText (wr, c); END;
    IF (d # NIL) THEN Wr.PutText (wr, d); END;
    Wr.PutText (wr, Wr.EOL);
    Wr.Flush (wr);
  END Out;

BEGIN
  FOR i := FIRST (WhiteSpace) TO LAST (WhiteSpace) DO
    WhiteSpace[i] := FALSE;
  END;
  WhiteSpace [ORD(' ')]  := TRUE;
  WhiteSpace [ORD('\t')] := TRUE;
  WhiteSpace [ORD('\r')] := TRUE;
  WhiteSpace [ORD('\n')] := TRUE;
  WhiteSpace [ORD('\f')] := TRUE;

  FOR i := 1 TO Params.Count-1 DO
    Gen (Params.Get (i));
  END;
END Main.