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 ~ { Yes: PFS.NameConfirmProc ~ { RETURN[TRUE] }; DoPattern: PROC [pattern: ROPE] ~ { Pad: PROC [r: ROPE] ~ {THROUGH [0..recurseDepth) DO msgStream.PutRope[r]; ENDLOOP}; Inner: PFS.InfoProc ~ { 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 => {}; NOT includeZeros AND bytes <= 0 => {}; 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]; 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 { 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[" "]; 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[]; }; 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. , 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 [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject ~ [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] [fullFName: PATH, ..., bytes: INT, ...] RETURNS [continue: BOOL _ TRUE] I'm not sure that this can happen, but if so, ignore it If ignoring zero-length files, then do it now We don't add files with non-numeric versions! We have a duplicate file name that differs only in version number Try to archivedelete this file Add the new file to the tail Κ ž•NewlineDelimiter –(cedarcode) style™head™Icodešœ Οeœ=™HL™,L™'L™/L™-—˜LšΟk œ.žœžœ˜]—šœžœž˜ Lšžœ.žœžœ˜ZLšœž˜L˜Lšœ žœ˜%Lšžœžœ žœ˜Lšžœžœžœ˜Lšžœžœžœžœ˜Lš œ žœžœžœžœžœžœ˜5—L˜šΟnœ˜/Lš œžœ žœžœžœžœ™:šœ™Lšœžœžœ™1Lšœ6™6L™—L•StartOfExpansion* -- [FSBackdoor.Version] RETURNS [BOOLEAN]šŸœžœžœžœ˜,šŸ œžœ žœ˜#Lš Ÿœžœžœžœžœžœ˜SšŸœžœ ˜Lš œ žœžœžœ žœžœ™GLšœžœ˜Lšœžœ ˜Lšœžœ!˜+Lšœ žœ4˜CLš žœžœžœžœžœ˜OL˜šžœžœž˜šœ˜L™7—šžœžœ˜&L™-—šœ žœ˜Lšœ žœžœ˜(L˜šžœžœžœ˜Lšœ ˜ LšœH˜HL˜—LšΟb œ˜'L˜L˜—šžœ˜ Lšœ=˜=Lšœ žœ ˜šžœ(žœžœžœ˜=L™-—šžœ žœžœ˜Lšœžœ$˜9Lšœ ˜ Lšžœžœžœžœ ˜/L˜:Lš žœžœ•žœžœžœžœ ˜΅š žœžœž œžœž˜(Lš žœRžœ žœžœžœ ˜vLšžœ˜—šžœžœ˜L™ALšœ žœžœ˜*Lšžœ žœžœ˜GL˜L˜Lš žœžœžœ žœžœ˜L˜ L™LšœEžœ˜dšžœ2˜5šœžœ ˜Lšœ ˜ L˜Lšžœ˜ L˜—Lšžœ žœi˜{Lšœ˜Lšœ žœ˜Lšœ˜——L˜Lšžœ˜L˜—šžœžœ žœ˜L˜6šžœ žœžœ˜L˜>Lšžœ˜ L˜—Lšžœ žœh˜zLšœ˜L˜—šžœ žœ˜Lš œDžœžœ žœ žœžœ˜nLšœ@˜@L˜—Lšžœžœžœ˜,šž˜L˜—L˜L˜—šŸ œžœ žœžœ žœžœ žœ˜aLšœ žœžœ˜Lšžœžœžœžœ ˜)Lšœ žœ˜(Lš œ žœžœžœ žœ žœ/žœ˜ˆšžœ žœžœ˜šžœžœH˜VLšžœG˜K—L˜šžœ˜LšœN˜NL˜L˜——L˜L˜—š Ÿ œžœ žœ žœžœ˜;Lšžœ5˜;Lšœ˜L˜—Lšœžœ˜œL˜šœ˜L˜&L˜L˜—L˜Lšžœ˜L˜L˜—…—!Κ0”