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: REF ← NIL, msg: Rope.ROPE ← NIL];
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: ROPE ← NIL;
lastFileDirStuff: ROPE ← NIL;
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]];
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: INT← ABS[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......