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;
};
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[];
};
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....