ArchivesImpl.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Chauser, October 24, 1991 5:07 pm PDT
Willie-s, July 20, 1992 4:25 pm PDT
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;
FileState: TYPE ~ {none, pending, backup, complete};
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] ~ {
finds the first segment that might contain lowKey
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<low => 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 {
This argument sets switches for the remaining patterns
-- Since there are not switches -- LOOP;
};
Now the argument is assumed to be a file pattern.
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"];
If you change the default file name, please update the ArchivesDoc.
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....