ArchiveDelverImpl.mesa
Copyright Ó 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) June 7, 1990 2:43 pm PDT
Willie-s, November 12, 1992 2:29 pm PST
Jules Bloomenthal November 22, 1990 5:52 pm PST
Michael Plass, November 27, 1991 12:46 pm PST
DIRECTORY BasicTime, Commander, CommanderOps, Convert, IO, PFS, PFSNames, PFSPrefixMap, Rope;
ArchiveDelverImpl: CEDAR PROGRAM
IMPORTS BasicTime, Commander, CommanderOps, Convert, IO, PFS, PFSNames, PFSPrefixMap, Rope
= BEGIN
Component: TYPE ~ PFSNames.Component;
PATH: TYPE ~ PFSNames.PATH;
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
Laggards: TYPE ~ RECORD [SEQUENCE size: NAT OF PATH];
ArchiveDelverCommand: Commander.CommandProc ~ {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject ~ [
in, out, err: STREAM, commandLine, command: ROPE,
propertyList: List.AList, procData: CommandProcHandle]
Yes: PFS.NameConfirmProc ~ { RETURN[TRUE] };
DoPattern: PROC [pattern: ROPE] ~ {
Pad: PROC [r: ROPE] ~ {THROUGH [0..recurseDepth) DO msgStream.PutRope[r]; ENDLOOP};
Inner: PFS.InfoProc ~ {
[fullFName: PATH, ..., bytes: INT, ...] RETURNS [continue: BOOLTRUE]
local: REF Laggards ~ laggards;
path: PATH ~ fullFName;
ncomp: NAT ~ PFSNames.ComponentCount[path];
shortName: ROPE ¬ PFSNames.ComponentRope[PFSNames.ShortName[path]];
IF Rope.Equal[shortName, "."] OR Rope.Equal[shortName, ".."] THEN RETURN[TRUE];
files ¬ files + 1;
SELECT TRUE FROM
ncomp = 0 => {};
I'm not sure that this can happen, but if so, ignore it
NOT includeZeros AND bytes <= 0 => {};
If ignoring zero-length files, then do it now
recursive AND fileType = 1 => {
thisPath: ROPE ~ PFS.RopeFromPath[path];
recurseDepth ¬ recurseDepth+1;
IF NOT terse THEN {
Pad[" "];
msgStream.PutF["%l%g%l\n", [rope["b"]], [rope[thisPath]], [rope["B"]] ];
};
DoPattern[Rope.Concat[thisPath, "/*"]];
recurseDepth ¬ recurseDepth-1;
};
ENDCASE => {
lastComp: PFSNames.Component ~ PFSNames.Fetch[path, ncomp-1];
lagPath: PATH ~ local[0];
IF lastComp.version.versionKind # numeric THEN RETURN [TRUE];
We don't add files with non-numeric versions!
IF lagPath # NIL THEN {
lagLastCompCount: NAT ~ PFSNames.ComponentCount[lagPath];
lagLastComp: PFSNames.Component;
IF ncomp # lagLastCompCount THEN GO TO noMatch;
lagLastComp ¬ PFSNames.Fetch[lagPath, lagLastCompCount-1];
IF NOT Rope.EqualSubstrs[
lastComp.name.base, lastComp.name.start, lastComp.name.len,
lagLastComp.name.base, lagLastComp.name.start, lagLastComp.name.len,
FALSE] THEN GO TO noMatch;
FOR i: NAT DECREASING IN [0..ncomp-1) DO
IF PFSNames.CompareComponents[
PFSNames.Fetch[path, i],
PFSNames.Fetch[lagPath, i],
FALSE] # equal THEN GO TO noMatch;
ENDLOOP;
IF lagIdx = keep THEN {
We have a duplicate file name that differs only in version number
lagRope: ROPE ~ PFS.RopeFromPath[lagPath];
IF countBytes THEN byteCount ¬ byteCount + PFS.FileInfo[lagPath].bytes;
excess ¬ excess + 1;
FOR i: NAT IN [1..lagIdx) DO local[i-1] ¬ local[i]; ENDLOOP;
lagIdx ¬ lagIdx - 1;
{
didArchive: BOOL ¬ TRUE;
dstPath: PFS.PATH ¬ PFSPrefixMap.Translate[lagPath, pfmTable];
Pad[" "];
Try to archivedelete this file
msgStream.PutF["archive/deleting %g => %g\n", [rope[lagRope]], [rope[PFS.RopeFromPath[dstPath]]] ];
PFS.Copy[from: lagPath, to: dstPath, confirmProc: Yes
! PFS.Error => {
Pad[" "];
msgStream.PutF1[ "\n %g\n\n", [rope[PFSErrorMsg[error]]] ];
didArchive ¬ FALSE; CONTINUE}];
IF doDelete AND didArchive THEN PFS.Delete[lagPath
! PFS.Error => {
Pad[" "];
msgStream.PutF1[ "\n %g\n\n", [rope[PFSErrorMsg[error]]] ];
CONTINUE}];
IF doFlush THEN msgStream.Flush[];
};
};
EXITS
noMatch => clearLaggards[];
};
Add the new file to the tail
IF lagIdx = keep THEN ERROR;
local[lagIdx] ¬ path;
lagIdx ¬ lagIdx + 1;
};
RETURN[TRUE];
};
IF laggards = NIL OR laggards.size < keep THEN laggards ¬ NEW[Laggards[keep]];
files ¬ 0;
excess ¬ 0;
lagIdx ¬ 0;
FOR i: NAT IN [0..laggards.size) DO laggards[i] ¬ NIL; ENDLOOP;
PFS.EnumerateForInfo[pattern: PFS.PathFromRope[pattern], proc: Inner
! PFS.Error => IF error.group # bug THEN {
Pad[" "];
msgStream.PutF1["%g\n", [rope[PFSErrorMsg[error]]] ];
IF doFlush THEN msgStream.Flush[];
CONTINUE;
}];
IF ( recurseDepth = 0 ) OR (( recurseDepth > 0 ) AND ( NOT terse )) THEN {
Pad[" "];
msgStream.PutF["files in pattern: %g, excess files: %g\n",
[integer[files]], [integer[excess]] ];
};
IF doFlush THEN msgStream.Flush[];
};
debug: BOOL ¬ FALSE;
countBytes: BOOL ¬ FALSE;
byteCount: INT ¬ 0;
laggards: REF Laggards ¬ NIL;
keep: (0..LAST[NAT]] ¬ 2;
pfmTable: PFSPrefixMap.PrefixTableList ¬ NIL;
recurseDepth: INT ¬ 0;
files: INT ¬ 0;
excess: INT ¬ 0;
lagIdx: NAT ¬ 0; -- current interesting laggard is laggards[lagIdx]
clearLaggards: PROC ~ {
local: REF Laggards ¬ laggards;
FOR i: NAT IN [0..local.size) DO local[i] ¬ NIL; ENDLOOP;
lagIdx ¬ 0;
};
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd
! CommanderOps.Failed => { msg ¬ errorMsg; GO TO bogus } ];
argBase: INT ¬ 1;
didPattern: BOOL ¬ FALSE;
doDelete: BOOL ¬ TRUE;
includeZeros: BOOL ¬ FALSE;
recursive: BOOL ¬ FALSE;
terse: BOOL ¬ FALSE;
append, doFlush: BOOL ¬ FALSE;
outFileName: ROPE ¬ NIL;
msgStream: STREAM;
WHILE (argv # NIL) AND (argBase < argv.argc) DO
arg: ROPE ~ argv[argBase];
IF Rope.Match["-*", arg]
THEN {
sense: BOOL ¬ TRUE;
FOR j: INT IN [1..Rope.Length[arg]) DO
SELECT Rope.Fetch[arg, j] FROM
'~ -- keep -- => {
sense ¬ NOT sense;
LOOP;
};
'a, 'A -- append to outputFileName -- => {
IF NOT ((argBase ¬ argBase+1) < argv.argc) THEN {
msg ¬ "Missing outputFileName in append"; GO TO bogus};
outFileName ¬ argv[argBase];
append ¬ TRUE;
doFlush ¬ TRUE;
};
'b, 'B -- countBytes (default: FALSE) -- => {
countBytes ¬ sense;
};
'd, 'D -- doDelete (default: TRUE) -- => {
doDelete ¬ sense;
};
'k, 'K -- keep -- => {
k: INT;
IF NOT ((argBase ¬ argBase+1) < argv.argc) THEN {
msg ¬ "Missing number of versions to keep"; GO TO bogus};
k ¬ Convert.IntFromRope[argv[argBase] ! Convert.Error => {
msg ¬ IO.PutFR1["Can't parse number of versions to keep (%g)", [rope[argv[argBase]]] ]; GO TO bogus}];
IF NOT (k IN [1..100]) THEN {
msg ¬ "Can't keep less than 1 nor more than 100 versions"; GO TO bogus};
keep ¬ k;
};
'o, 'O -- outputToFile -- => {
IF NOT ((argBase ¬ argBase+1) < argv.argc) THEN {
msg ¬ "Missing outputFileName"; GO TO bogus};
outFileName ¬ argv[argBase];
doFlush ¬ TRUE;
};
'r, 'R -- recursive? -- => {
recursive ¬ sense;
};
't, 'T -- terse reporting when doing recursion? -- => {
terse ¬ sense;
};
'x, 'X -- translation -- => {
prefix, translation: ROPE;
IF NOT ((argBase ¬ argBase+1) < argv.argc) THEN {
msg ¬ "Missing translation prefix"; GO TO bogus};
prefix ¬ argv[argBase];
IF NOT ((argBase ¬ argBase+1) < argv.argc) THEN {
msg ¬ "Missing translation"; GO TO bogus};
translation ¬ argv[argBase];
pfmTable ¬ PFSPrefixMap.InsertIntoNewPTable[
PFS.PathFromRope[prefix],
PFS.PathFromRope[translation] ];
};
'z, 'Z -- keep -- => {
includeZeros ¬ sense;
};
ENDCASE => NULL;
sense ¬ TRUE;
ENDLOOP;
}
ELSE {
msgStream ¬ CheckOutPut[cmd.out, outFileName, append];
IF pfmTable = NIL THEN {
msgStream.PutRope["\n**** No translation given - quitting\n"];
GOTO bogus;
};
IF countBytes THEN msgStream.PutF["\n***Starting Delver %g (at %g)\n", [rope[cmd.commandLine]], [time[BasicTime.Now[]]] ];
DoPattern[arg];
didPattern ¬ TRUE;
};
argBase ¬ argBase+1;
ENDLOOP;
IF NOT didPattern THEN {
msgStream ¬ CheckOutPut[cmd.out, outFileName, append];
IF pfmTable = NIL THEN {
msgStream.PutRope["\n**** No translation given - quitting\n"];
GOTO bogus;
};
IF countBytes THEN
msgStream.PutF["\n***Starting Delver %g (at %g)\n", [rope[cmd.commandLine]], [time[BasicTime.Now[]]] ];
DoPattern["*"];
};
IF countBytes THEN {
msgStream.PutF[" %g bytes %gdeleted\n", [integer[byteCount]], [rope[IF NOT doDelete THEN "to be " ELSE NIL]]];
msgStream.PutF1["\tFinished at %g\n", [time[BasicTime.Now[]]] ];
};
IF outFileName # NIL THEN msgStream.Close[];
EXITS
bogus => result ¬ $Failure;
};
CheckOutPut: PROC[cmdout: STREAM, outFileName: ROPE, append: BOOL]
RETURNS[outStream: STREAM] = {
outPath: PFS.PATH;
IF outFileName = NIL THEN RETURN[cmdout];
outPath ¬ PFS.PathFromRope[outFileName];
outStream ¬ PFS.StreamOpen[outPath, IF append THEN $append ELSE $create ! PFS.Error => {
cmdout.PutRope[error.explanation]; CONTINUE} ];
IF outStream # NIL THEN {
IF append THEN cmdout.PutF1["Messages will be appended to %g\n", [rope[outFileName]] ]
ELSE cmdout.PutF1["Messages will be written on %g\n", [rope[outFileName]] ]
}
ELSE {
cmdout.PutF1["\n\tCould not open %g - using cmd.out\n", [rope[outFileName]] ];
outStream ¬ cmdout;
};
};
PFSErrorMsg: PROC [error: PFS.ErrorDesc] RETURNS [ROPE] ~ {
RETURN[Rope.Concat["***> PFS.Error: ", error.explanation]];
};
archiveDelverDoc: Rope.ROPE ~ "archive/deletes excess versions of files matching given patterns
-a fileName append output to fileName
-b report number of bytes to be or actually deleted (default: not to count)
-f find excess versions only (default: delete them)
-k n # of versions to keep (default: 2, min: 1, max: 100)
-o fileName write output to fileName
-r perform recursively on sub-directories (default: no recursion)
-t terse reporting when doing recursion on sub-directories (default: verbose)
-x prefix translation prefixmap translation for where to archive the file
-z include zero-length files (default: ignore them)";
Commander.Register[
"ArchiveDelver", ArchiveDelverCommand,
archiveDelverDoc,
$ArchiveDelver];
END.