-- FastDir.mesa  edited: Sandman  July 15, 1980  4:18 PM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoFileDefs USING [FP],
  DirectoryDefs USING [EnumerateDirectory],
  FastDirDefs USING [
    Item, ItemHandle, HashVal, NilFileSwitches, FileSwitches, StringItem,
    FileType, LocalDir, Hash],
  String USING [AppendChar, EquivalentString],
  Storage USING [Node, CopyString];

FastDir: PROGRAM
  IMPORTS DirectoryDefs, FastDirDefs, String, Storage EXPORTS FastDirDefs =
  BEGIN OPEN FastDirDefs;
  
  StringHead: POINTER TO StringItem;
  ItemHead: ARRAY FileType OF ItemHandle;
  extensions: ARRAY FileType OF STRING =
    ["mesa", "bcd", "config", "symbols", "code", "image"];
  fileSwitches: FileSwitches;
  
  NewString: PROCEDURE [s: STRING] RETURNS [STRING] =
    BEGIN
    p: POINTER TO StringItem;
    hash: HashVal = Hash[s];
    FOR p ← StringHead, p.link UNTIL p = NIL DO
      IF p.hash = hash AND String.EquivalentString[s, p.s] THEN RETURN[p.s];
      ENDLOOP;
    p ← Storage.Node[SIZE[Item]];
    p.link ← StringHead;
    StringHead ← p;
    p.hash ← hash;
    RETURN[p.s ← Storage.CopyString[s]]
    END;
    
  EnterItem: PROCEDURE [s: STRING, t: FileType] RETURNS [p: ItemHandle] = INLINE
    BEGIN
    IF (p ← LookupItem[s, t]) = NIL THEN
      BEGIN p ← NewItem[s]; p.link ← ItemHead[t]; ItemHead[t] ← p; END;
    RETURN
    END;
    
  EnumerateList: PUBLIC PROCEDURE [
    ft: FileType, proc: PROCEDURE [ItemHandle] RETURNS [BOOLEAN]]
    RETURNS [ih: ItemHandle] =
    BEGIN
    FOR ih ← ItemHead[ft], ih.link UNTIL ih = NIL OR proc[ih] DO  ENDLOOP;
    END;
    
  GetExtension: PUBLIC PROCEDURE [ft: FileType] RETURNS [ext: STRING] =
    BEGIN RETURN[extensions[ft]] END;
    
  GetFiles: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, fn: STRING]
    RETURNS [BOOLEAN] =
    BEGIN
    i: FileType;
    p: ItemHandle;
    ext: STRING ← [40];
    StripExtension[fn, ext];
    FOR i IN FileType DO
      IF fileSwitches[i] AND String.EquivalentString[ext, extensions[i]] THEN
	BEGIN p ← EnterItem[fn, i]; p.fp ← fp↑; EXIT; END;
      ENDLOOP;
    RETURN[FALSE]
    END;
    
  Init: PROCEDURE =
    BEGIN
    i: FileType;
    StringHead ← NIL;
    fileSwitches ← NilFileSwitches;
    FOR i IN FileType DO ItemHead[i] ← NIL ENDLOOP;
    END;
    
  LookupItem: PUBLIC PROCEDURE [s: STRING, t: FileType] RETURNS [p: ItemHandle] =
    BEGIN
    hash: HashVal = Hash[s];
    FOR p ← ItemHead[t], p.link UNTIL p = NIL DO
      IF p.hash = hash AND String.EquivalentString[s, p.name] THEN EXIT; ENDLOOP;
    RETURN
    END;
    
  NewItem: PROCEDURE [s: STRING] RETURNS [p: ItemHandle] = INLINE
    BEGIN
    p ← Storage.Node[SIZE[Item]];
    p.hash ← Hash[s];
    p.name ← NewString[s];
    RETURN
    END;
    
  ScanDir: PUBLIC PROCEDURE [
    files: FileSwitches,
    proc: PROCEDURE [
      PROCEDURE [POINTER TO AltoFileDefs.FP, STRING] RETURNS [BOOLEAN]]] =
    BEGIN
    fileSwitches ← files;
    IF proc = LocalDir THEN DirectoryDefs.EnumerateDirectory[GetFiles]
    ELSE proc[GetFiles];
    END;
    
  StripExtension: PUBLIC PROCEDURE [name, ext: STRING] =
    BEGIN
    i, j: CARDINAL;
    i ← (j ← name.length) - 1;
    IF name[i] = '. THEN i ← (j ← i) - 1;
    FOR i ← i, i - 1 UNTIL name[i] = '. DO
      IF name[i] = '! THEN j ← i; IF i = 0 THEN RETURN; ENDLOOP;
    name.length ← i;
    IF ext = NIL THEN RETURN;
    ext.length ← 0;
    UNTIL (i ← i + 1) = j DO String.AppendChar[ext, name[i]]; ENDLOOP;
    RETURN
    END;
    
  StripSwitches: PUBLIC PROCEDURE [name, switches: STRING] =
    BEGIN OPEN String;
    i, j: CARDINAL;
    FOR i ← 0, i + 1 UNTIL i = name.length DO
      IF name[i] = '/ THEN EXIT; REPEAT FINISHED => RETURN; ENDLOOP;
    IF switches = NIL THEN BEGIN name.length ← i; RETURN END;
    FOR j IN (i..name.length) DO AppendChar[switches, name[j]] ENDLOOP;
    name.length ← i;
    RETURN;
    END;
    
  Init[];
  
  END.