ListArchivesImpl.mesa
Copyright (C) 1984, Xerox Corporation. All rights reserved.
Tim Diebert: December 12, 1984 11:58:16 am PST
DIRECTORY
Ascii USING [Upper],
Basics USING [BytePair, Comparison],
BasicTime USING [GMT, earliestGMT, Period, ToNSTime, nullGMT, FromNSTime, Unpacked, Unpack, Now],
BTreeSimple USING [Tree, Compare, CompareEntries, Value, Key, InternalKey, EntryKey, New, Open, SetState, EnumerateRecords, KeyFromEntry],
Commander USING [CommandProc, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
FS USING [ComponentPositions, ExpandName, Error, OpenFile, Open, Close],
IO USING [PutRope, PutF, PutFR, int, rope],
Rope USING [Compare, FromProc, Concat, Substr, Equal, Length, Fetch, Index, ROPE],
UserProfile USING [Token]
;
ListArchivesImpl: CEDAR PROGRAM
IMPORTS Ascii, BasicTime, BTreeSimple, Commander, CommandTool, FS, IO, Rope, UserProfile
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
GMT: TYPE ~ BasicTime.GMT;
lcToBytes: TYPE ~ MACHINE DEPENDENT RECORD
[b0 (0: 0 .. 7), b1 (0: 8 .. 15), b2 (1: 0 .. 7), b3 (1: 8 .. 15): CHAR];
ListArchivesCommandProc: Commander.CommandProc = BEGIN
CommandProc: TYPE = PROC [cmd: Handle] RETURNS [result: REFNIL, msg: Rope.ROPENIL];
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
p: PROC [key: BTreeSimple.InternalKey, value: BTreeSimple.Value] RETURNS [continue: BOOLEAN] = BEGIN
file: ROPE;
created: GMT;
[file, created] ← InternalKeyToFileNameAndTime[key];
SELECT Match[file, pattern] FROM
fit => BEGIN
cp: FS.ComponentPositions ← FS.ExpandName[file].cp;
newDir: ROPE ← file.Substr[0, cp.base.start];
IF NOT lastFileDirStuff.Equal[newDir, FALSE]
THEN cmd.out.PutF["%g\n", IO.rope[newDir]];
lastFileDirStuff ← newDir;
cmd.out.PutF[" %g\t%g %g\n", IO.rope[file.Substr[cp.base.start]], IO.rope[RFC822Date[created]], IO.rope[FileInfoFromValue[value].fileInfo]];
RETURN [TRUE];
END;
compatible => RETURN [TRUE];
clash => RETURN [FALSE];
ENDCASE => ERROR;
END;
TryPattern: PROC [r: ROPE] = BEGIN
start: ROPE;
key: BTreeSimple.Key;
lastFileDirStuff ← NIL;
r ← FS.ExpandName[r ! FS.Error => GOTO Failed].fullFName;
start ← Rope.Substr[r, 0, Rope.Index[r, 0, "*"]];
pattern ← r;
key ← FileNameAndTimeToKey[start, BasicTime.earliestGMT];
[] ← BTreeSimple.EnumerateRecords[tree: tree, key: key, relation: greater, Proc: p];
EXITS Failed => RETURN;
END;
tree: BTreeSimple.Tree;
file: FS.OpenFile;
pattern: ROPENIL;
lastFileDirStuff: ROPENIL;
treeName: ROPE ← UserProfile.Token["Archivist.BTreeServerName",
"[Indigo]<Archivist>BTrees>Archivist.btree"];
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd
! CommandTool.Failed => {cmd.out.PutF["%g\n", IO.rope[errorMsg]]; GOTO Failed}];
IF argv.argc <= 1 THEN {cmd.out.PutRope["\n"]; RETURN; };
tree ← BTreeSimple.New[compareProcs:
[compare: CompareProc, compareEntries: CompareEntriesProc]];
file ← FS.Open[treeName
! FS.Error => {cmd.out.PutF["%g\n", IO.rope[error.explanation]]; GOTO Failed}];
BTreeSimple.Open[tree: tree, file: file];
FOR i: NAT IN [1..argv.argc) DO
arg: ROPE = argv[i];
IF Rope.Length[arg] = 0 THEN LOOP;
IF Rope.Fetch[arg, 0] = '- THEN {
This argument sets switches for the remaining patterns
-- Since there are not switches -- LOOP;
};
Now the argument is assumed to be a file pattern.
TryPattern[arg];
ENDLOOP;
BTreeSimple.SetState[tree, closed];
FS.Close[file];
EXITS Failed => RETURN;
END;
Internal Procs
CompareProc: BTreeSimple.Compare = TRUSTED BEGIN
Compare: TYPE = UNSAFE PROCEDURE [key: InternalKey, entryKey: EntryKey] RETURNS [Comparison];
keyFileName, entryKeyFileName: ROPE;
keyCreated, entryKeyCreated: GMT;
comp: Basics.Comparison;
p: INT ← 0;
[keyFileName, keyCreated] ← InternalKeyToFileNameAndTime[key];
[entryKeyFileName, entryKeyCreated] ← EntryKeyToFileNameAndTime[entryKey];
p ← BasicTime.Period[keyCreated, entryKeyCreated];
comp ← Rope.Compare[s1: keyFileName, s2: entryKeyFileName, case: FALSE];
IF comp # equal THEN RETURN [comp];
IF p < 0 THEN RETURN [less];
IF p > 0 THEN RETURN [greater];
Times are the same.
RETURN [equal];
END;
CompareEntriesProc: BTreeSimple.CompareEntries = TRUSTED BEGIN
CompareEntries: TYPE = UNSAFE PROCEDURE [entryKey1, entryKey2: EntryKey] RETURNS [Comparison];
RETURN [less];
END;
FileInfoFromValue: PROC [v: BTreeSimple.Value] RETURNS [fileInfo: ROPE] = BEGIN
bp: Basics.BytePair;
cnt: CARDINAL ← v.words[0];
i: INT ← -1;
p: PROC RETURNS [CHAR] = BEGIN
i ← i + 1;
IF (i MOD 2) = 0
THEN BEGIN
bp ← LOOPHOLE [v.words[i/2+1]];
RETURN [LOOPHOLE [bp.high]];
END
ELSE RETURN [LOOPHOLE [bp.low]];
END;
fileInfo ← Rope.FromProc[cnt, p];
RETURN [(IF fileInfo.Length[] <= 1 THEN NIL ELSE Rope.Substr[fileInfo, 1])];
END;
FileNameAndTimeToKey: PROC
[fileName: ROPE, created: GMT] RETURNS [BTreeSimple.Key] = BEGIN
RETURN [Rope.Concat[TimeToRope[created], fileName]];
END;
InternalKeyToFileNameAndTime: PROC
[iKey: BTreeSimple.InternalKey] RETURNS [fileName: ROPE, created: GMT] = BEGIN
created ← RopeToTime[Rope.Substr[iKey, 0, 4]];
fileName ← Rope.Substr[iKey, 4];
RETURN [fileName, created];
END;
EntryKeyToFileNameAndTime: PROC
[eKey: BTreeSimple.EntryKey] RETURNS [fileName: ROPE, created: GMT] = TRUSTED BEGIN
[fileName, created] ← InternalKeyToFileNameAndTime[BTreeSimple.KeyFromEntry[eKey]];
END;
TimeToRope: PROC [t: GMT] RETURNS [ROPE] = BEGIN
i: INT ← -1;
lcb: lcToBytes ← LOOPHOLE [BasicTime.ToNSTime[t]];
p: PROC RETURNS[CHAR] = BEGIN
i ← i + 1;
SELECT i FROM
0 => RETURN [lcb.b0];
1 => RETURN [lcb.b1];
2 => RETURN [lcb.b2];
3 => RETURN [lcb.b3];
ENDCASE => ERROR;
END;
RETURN [Rope.FromProc[4, p]];
END;
RopeToTime: PROC [r: ROPE] RETURNS [GMT] = BEGIN
lcb: lcToBytes;
lcb.b0 ← Rope.Fetch[r, 0];
lcb.b1 ← Rope.Fetch[r, 1];
lcb.b2 ← Rope.Fetch[r, 2];
lcb.b3 ← Rope.Fetch[r, 3];
RETURN [BasicTime.FromNSTime[ LOOPHOLE [lcb, LONG CARDINAL]]];
END;
MatchResult: TYPE = {fit, compatible, clash};
The match result is computed on the assumtion that name is always GE the pattern prefix (characters up to the first star). In this case, "compatible" means that it is sensible to present another name GE the current one, and "clash" means that such a name cannot "fit".
Match: PROC [name: ROPE, pattern: ROPE] RETURNS [MatchResult] = BEGIN
SubMatch: PROC [i1: INT, len1: INT, i2: INT, len2: INT] RETURNS [MatchResult] = BEGIN
"1" is the pattern, "2" is the name
WHILE len1 > 0 DO
c1: CHAR = Rope.Fetch[pattern, i1];
IF c1 = '*
THEN BEGIN -- quick kill for * at end of pattern
IF len1 = 1 THEN RETURN [fit];
else must take all combinations
BEGIN -- first, accept the *
j1: INT = i1 + 1;
nlen1: INT = len1 - 1;
j2: INT ← i2;
nlen2: INT ← len2;
WHILE nlen2 >= 0 DO
IF SubMatch[j1, nlen1, j2, nlen2] = fit THEN RETURN [fit];
j2 ← j2 + 1;
nlen2 ← nlen2 - 1;
ENDLOOP;
END;
RETURN [compatible];
END;
IF len2 = 0 THEN RETURN [compatible];
at this point demand an exact match in both strings
IF Ascii.Upper[c1] # Ascii.Upper[Rope.Fetch[name, i2]] THEN RETURN [clash];
i1 ← i1 + 1;
len1 ← len1 - 1;
i2 ← i2 + 1;
len2 ← len2 - 1;
ENDLOOP;
RETURN [IF len2 = 0 THEN fit ELSE clash];
END;
RETURN [SubMatch [0, Rope.Length[pattern], 0, Rope.Length[name]]];
END;
RFC822Date: PUBLIC PROC[gmt: BasicTime.GMT← BasicTime.nullGMT] RETURNS[date: ROPE] =
-- generates arpa standard time, dd mmm yy hh:mm:ss zzz
BEGIN OPEN IO;
upt: BasicTime.Unpacked ←
BasicTime.Unpack[IF gmt = BasicTime.nullGMT THEN BasicTime.Now[] ELSE gmt];
zone: ROPE;
month, tyme, year: ROPE;
timeFormat: ROPE = "%02g:%02g:%02g %g"; -- "hh:mm:ss zzz"
dateFormat: ROPE = "%2g %g %g %g";  -- "dd mmm yy timeFormat"
arpaNeg: BOOL← upt.zone > 0;
aZone: INTABS[upt.zone];
zDif: INT← aZone / 60;
zMul: INT← zDif * 60;
IF (zMul = aZone) AND arpaNeg THEN
BEGIN IF upt.dst = yes
THEN
SELECT zDif FROM
0 => zone← "UT";
4 => zone← "EDT";
5 => zone← "CDT";
6 => zone← "MDT";
8 => zone← "PDT";
ENDCASE
ELSE
SELECT zDif FROM
0 => zone← "UT";
5 => zone← "EST";
6 => zone← "CST";
7 => zone← "MST";
8 => zone← "PST";
ENDCASE;
END;
IF zone = NIL THEN BEGIN
mm: INT← aZone - zMul;
zone← PutFR[IF arpaNeg THEN "-%02g%02g" ELSE "+%02g%02g", int[zDif], int[mm]];
END;
SELECT upt.month FROM
January => month← "Jan";
February => month← "Feb";
March => month← "Mar";
April => month← "Apr";
May => month← "May";
June => month← "Jun";
July => month← "Jul";
August => month← "Aug";
September => month← "Sep";
October => month← "Oct";
November => month← "Nov";
December => month← "Dec";
unspecified => ERROR;
ENDCASE => ERROR;
year← Rope.Substr[PutFR[NIL, int[upt.year]], 2];
tyme← PutFR[timeFormat, int[upt.hour], int[upt.minute], int[upt.second], rope[zone]];
date← PutFR[dateFormat, int[upt.day], rope[month], rope[year], rope[tyme]];
END;
Commander.Register[key: "ListArchives", proc: ListArchivesCommandProc, doc: "ListArchives pattern"];
END......