<<>> <> <> <> <> DIRECTORY Archives, Args, Ascii, Basics USING [Card32FromF, Comparison, FWORD], BasicTime USING [GMT], CodeControl USING [CreateDecodingStream], Commander, CommanderOps, FS USING [ComponentPositions, ExpandName, Error, StreamOpen], IO USING [GetFWord, GetLength, GetLineRope, EndOfStream, Close, Error, STREAM, PutF, PutF1, PutRope, rope, SetIndex, noWhereStream], Rope USING [Compare, Concat, CompareSubstrs, Equal, Fetch, Find, Index, IsEmpty, Length, Match, ROPE, Run, Substr], Process USING [CheckForAbort], UserProfile USING [ProfileChangedProc, CallWhenProfileChanges, Token] ; ArchivesImpl: CEDAR MONITOR IMPORTS Args, Basics, CodeControl, Commander, CommanderOps, FS, IO, Rope, Process, UserProfile EXPORTS Archives = BEGIN OPEN Archives; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; GMT: TYPE ~ BasicTime.GMT; <> OpenDirectory: PUBLIC PROC [directory: ROPE ¬ NIL, msg: STREAM¬ NIL] RETURNS [h: Handle] = BEGIN p: Parameters = GetParameters[]; h ¬ NEW [HandleRecord]; h.fileName¬ IF directory # NIL THEN directory ELSE p.fileName; h.msg¬ IF msg = NIL THEN IO.noWhereStream ELSE msg; DoOpenDirectory[h]; IF h.compressedInput = NIL THEN RETURN [NIL]; END; DoOpenDirectory: PROC [h: Handle] = BEGIN ENABLE IO.Error, IO.EndOfStream => GOTO Out; compressedInput: STREAM; nSegments: CARD ¬ 0; dirPos: CARD ¬ 0; lastSegCountPos: CARD; lastSegCount: CARD ¬ 0; starts: Starts; compressedInput ¬ h.compressedInput ¬ FS.StreamOpen[h.fileName, read ! FS.Error => GOTO Out]; lastSegCountPos ¬ compressedInput.GetLength[]-(2*BYTES[Basics.FWORD]); compressedInput.SetIndex[lastSegCountPos]; h.lastSegCount ¬ Basics.Card32FromF[compressedInput.GetFWord[]]; dirPos ¬ Basics.Card32FromF[compressedInput.GetFWord[]]; nSegments ¬ h.nSegments ¬ (lastSegCountPos-dirPos)/(2*BYTES[Basics.FWORD]); compressedInput.SetIndex[dirPos]; starts ¬ NEW[StartSequence[nSegments]]; FOR i: CARD IN [0..nSegments) DO starts[i].keyPos ¬ Basics.Card32FromF[compressedInput.GetFWord[]]; starts[i].segPos ¬ Basics.Card32FromF[compressedInput.GetFWord[]]; ENDLOOP; FOR i: CARD IN [0..nSegments) DO compressedInput.SetIndex[starts[i].keyPos]; starts[i].key ¬ compressedInput.GetLineRope[]; ENDLOOP; h.starts ¬ starts; EXITS Out => h.compressedInput ¬ NIL; END; -- of DoOpenDirectory CloseDirectory: PUBLIC PROC [h: Handle] = BEGIN h.compressedInput.Close[]; h.compressedInput ¬ NIL; END; EntriesPerSegment: CARDINAL = 500; GetDecodingStream: PROC [h: Handle, segmentNum: CARD] RETURNS [s: STREAM] ~ { h.compressedInput.SetIndex[h.starts[segmentNum].segPos]; s ¬ CodeControl.CreateDecodingStream[input~h.compressedInput, codingMethod: "F4KS"]; }; UncompressDirectory: PROC [h: Handle, out: STREAM] = BEGIN { ENABLE IO.EndOfStream => GOTO done; starts: Starts ¬ h.starts; FOR i: CARD IN [0..h.nSegments) DO input: STREAM; prev: ROPE ¬ NIL; entries: CARD ¬ IF i = h.nSegments-1 THEN h.lastSegCount ELSE EntriesPerSegment; input ¬ GetDecodingStream[h, i]; FOR i: CARD IN [0..entries) DO line: ROPE; run: CARD; run ¬ Basics.Card32FromF[input.GetFWord[]]; line ¬ input.GetLineRope[]; prev ¬ line ¬ Rope.Concat[Rope.Substr[prev, 0, run], line]; out.PutF1["%g\n", IO.rope[line]]; ENDLOOP; input.Close[]; ENDLOOP; EXITS done => NULL; }; END; EnumerateDirectory: PUBLIC ENTRY PROC [h: Handle, pattern: ROPE, consumer: ArchiveConsumer] ~ { ENABLE UNWIND => NULL; patternPrefix: ROPE ¬ Rope.Substr[pattern, 0, Rope.Index[pattern, 0, "*"]]; startSeg: CARD ¬ FindSeg[h, patternPrefix]; FOR i: CARD IN [startSeg..h.nSegments) DO IF NOT EnumerateSegment[h, i, patternPrefix, pattern, consumer] THEN EXIT; Process.CheckForAbort[]; ENDLOOP; }; FindSeg: PROC [h: Handle, lowKey: ROPE] RETURNS [firstSeg: CARD] ~ { <> low, mid, high: CARD; IF lowKey.IsEmpty[] THEN RETURN[0]; low ¬ 0; high ¬ h.nSegments-1; mid ¬ (low+high)/2; DO SELECT Rope.Compare[lowKey, h.starts[mid].key, FALSE] FROM less => { high ¬ mid-1; mid ¬ (low+high)/2; }; greater => { low ¬ mid; mid ¬ (low+high)/2; }; equal => { high ¬ mid; mid ¬ (low+high)/2; }; ENDCASE; SELECT TRUE FROM mid=low => RETURN[low]; high ERROR; ENDCASE => NULL; ENDLOOP; }; EnumerateSegment: PROC [h: Handle, segNum: CARD, patternPrefix, pattern: ROPE, consumer: ArchiveConsumer] RETURNS [continue: BOOL ¬ TRUE] ~ { starts: Starts ¬ h.starts; input: STREAM ¬ GetDecodingStream[h, segNum]; entries: CARD ¬ IF segNum = h.nSegments-1 THEN h.lastSegCount ELSE EntriesPerSegment; prefixLen: CARD ¬ patternPrefix.Length[]; line: ROPE ¬ NIL; matchLen: CARD ¬ 0; FOR i: CARD IN [0..entries) WHILE continue DO run: CARD; lineTail: ROPE; file: ROPE; restLen: CARD; run ¬ Basics.Card32FromF[input.GetFWord[]]; matchLen ¬ MIN[matchLen, run]; lineTail ¬ input.GetLineRope[]; line ¬ Rope.Concat[Rope.Substr[line, 0, run], lineTail]; file ¬ line.Substr[0, line.Find["\t"]]; matchLen ¬ matchLen + Rope.Run[s1: patternPrefix, pos1: matchLen, s2: file, pos2: matchLen, case: FALSE]; restLen ¬ prefixLen - matchLen; SELECT Rope.CompareSubstrs[s1: patternPrefix, start1: matchLen, len1: restLen, s2: file, start2: matchLen, len2: restLen, case: FALSE] FROM less => { continue ¬ FALSE }; equal => { IF Rope.Match[pattern: pattern, object: file, case: FALSE] THEN continue ¬ consumer[line]; matchLen ¬ patternPrefix.Length[]; }; greater => { NULL; }; ENDCASE => NULL; ENDLOOP; input.Close[]; }; UncompressDirectoryCommand: Commander.CommandProc ~ { file: ROPE ¬ Args.GetRope[cmd, 0]; h: Handle ¬ OpenDirectory[IF Rope.IsEmpty[file] THEN NIL ELSE file, cmd.err]; IF h # NIL THEN { cmd.err.PutF1["Uncompressing %g ... \n", IO.rope[h.fileName]]; UncompressDirectory[h, cmd.out]; CloseDirectory[h]; }; }; ListArchivesCommand: Commander.CommandProc = BEGIN h: Handle ¬ NIL; BEGIN EachEntry: ArchiveConsumer = BEGIN sepPos: CARD ¬ Rope.Find[line, "\t"]; fileName: ROPE ~ Rope.Substr[line, 0, sepPos]; rest: ROPE ~ Rope.Substr[line, sepPos+1]; cp: FS.ComponentPositions; newDir: ROPE; cp ¬ FS.ExpandName[fileName].cp; newDir ¬ fileName.Substr[0, cp.base.start]; IF NOT lastDir.Equal[newDir, FALSE] THEN cmd.out.PutF1["%g\n", IO.rope[newDir]]; lastDir ¬ newDir; cmd.out.PutF[" %g\t%g\n", IO.rope[fileName.Substr[cp.base.start]], IO.rope[rest]]; END; pattern, r: ROPE ¬ NIL; lastDir: ROPE ¬ NIL; argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd: cmd ! CommanderOps.Failed => {cmd.out.PutF1["%g\n", IO.rope[errorMsg]]; GOTO Failed}]; IF argv.argc <= 1 THEN {cmd.out.PutRope["\n"]; RETURN; }; h ¬ OpenDirectory[msg~cmd.err]; IF h = NIL THEN RETURN; 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; }; <> IO.PutRope[cmd.out, "Enumerating ... \n "]; EnumerateDirectory[h, arg, EachEntry]; IO.PutRope[cmd.out, "Done\n"]; ENDLOOP; CloseDirectory[h]; EXITS Failed => NULL; END; END; GetParameters: PUBLIC PROC RETURNS [Parameters] = {RETURN[parameters]}; parameters: Parameters¬ NIL; ReactToProfile: PUBLIC ENTRY UserProfile.ProfileChangedProc = BEGIN ENABLE UNWIND => NULL; params: Parameters ¬ NEW[ParametersRecord]; params.fileName ¬ UserProfile.Token["Archives.DirectoryName", "/ux/project/archivist/data/Archives.CompressedDirectory"]; <> parameters ¬ params; END; ReactToProfile[edit]; UserProfile.CallWhenProfileChanges[ReactToProfile]; Commander.Register["UncompressDirectory", UncompressDirectoryCommand]; Commander.Register["ListArchives", ListArchivesCommand, "ListArchives pattern ... \t patterns must be absolute names using brackety []<> syntax."]; END....