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 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]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 { -- Since there are not switches -- LOOP; }; TryPattern[arg]; ENDLOOP; BTreeSimple.SetState[tree, closed]; FS.Close[file]; EXITS Failed => RETURN; END; CompareProc: BTreeSimple.Compare = TRUSTED BEGIN 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]; RETURN [equal]; END; CompareEntriesProc: BTreeSimple.CompareEntries = TRUSTED BEGIN 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}; Match: PROC [name: ROPE, pattern: ROPE] RETURNS [MatchResult] = BEGIN SubMatch: PROC [i1: INT, len1: INT, i2: INT, len2: INT] RETURNS [MatchResult] = BEGIN 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]; 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]; 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...... úListArchivesImpl.mesa Copyright (C) 1984, Xerox Corporation. All rights reserved. Tim Diebert: December 12, 1984 11:58:16 am PST CommandProc: TYPE = PROC [cmd: Handle] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL]; CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...] This argument sets switches for the remaining patterns Now the argument is assumed to be a file pattern. Internal Procs Compare: TYPE = UNSAFE PROCEDURE [key: InternalKey, entryKey: EntryKey] RETURNS [Comparison]; Times are the same. CompareEntries: TYPE = UNSAFE PROCEDURE [entryKey1, entryKey2: EntryKey] RETURNS [Comparison]; 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". "1" is the pattern, "2" is the name else must take all combinations at this point demand an exact match in both strings Ê c˜™J™˜>JšœJ˜JJšœ2˜2JšœAœ˜HJšœœœ˜#Jšœœœ˜Jšœœœ ˜J™Jšœ ˜Jšœ˜J˜—šœ1œ˜>Kš žœœœ œ"œ™^Jšœ˜Jšœ˜J˜—J˜š žœœœ œ˜OJ˜Jšœœ˜Jšœœ˜ šœœœœ˜J˜ šœœ˜šœ˜ Jšœœ˜Jšœœ ˜Jš˜—Jšœœœ ˜ —Jšœ˜—Jšœ!˜!Jš œœœœœ˜LJšœ˜J˜—šžœ˜Jšœ œ œœ˜@Jšœ.˜4Jšœ˜—J˜šžœ˜"Jšœ œ œ œ˜NJ˜.Jšœ ˜ Jšœ˜Jšœ˜—J˜šžœ˜Jš œœ œ œœ˜SJšœS˜SJšœ˜J˜—š ž œœœœœ˜0Jšœœ˜ Jšœœ˜2šœœœœ˜J˜ šœ˜ Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ˜—Jšœ˜—Jšœ˜šœ˜J˜——š ž œœœœœ˜0Jšœ˜J˜J˜J˜J˜Jšœœœœ˜>Jšœ˜J˜—šœ œ˜-Kšœ™—š žœœœ œœ˜Ešžœœœœœœœ˜UK™#šœ ˜Kšœœ˜#Kšœ˜ šœœŸ%˜0Kšœ œœ˜Kšœ™šœŸ˜Kšœœ ˜Kšœœ ˜Kšœœ˜ Kšœœ˜šœ ˜Kšœ&œœ˜:Kšœ ˜ Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜Kšœ˜—Kšœ œœ˜%Kšœ3™3Kšœ5œœ ˜KKšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜—Kšœœ œœ˜)Kšœ˜—Kšœ<˜BKšœ˜—J˜š ž œœœœœœ˜TJšŸ8˜8Jšœœœ˜šœ˜Jšœœœœ˜K—Jšœœ˜ Jšœœ˜Jšœ œŸ˜:Jšœ œŸ˜>J˜Jšœ œ˜Jšœœœ ˜Jšœœ ˜Jšœœ ˜šœœ ˜"šœœ˜š˜šœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jš˜——š˜šœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜———Jšœ˜—J˜šœœ ˜Jšœœ˜Jšœ œ œ œ"˜NJšœ˜—J˜šœ ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœœ˜Jšœœ˜J˜—Jšœœ˜0JšœU˜UJšœK˜KJ˜Jšœ˜—Jšœd˜dJ˜Jšœ˜ ——…— -}