DIRECTORY BasicTime, Commander, CommanderOps, Convert, IO, PFS, PFSNames, Rope; DelverImpl: CEDAR PROGRAM IMPORTS BasicTime, Commander, CommanderOps, Convert, IO, PFS, PFSNames, 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]; DelverCommand: Commander.CommandProc ~ { 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; IF findOnly THEN { Pad[" "]; msgStream.PutF1["%g\n", [rope[lagRope]] ]; } ELSE { Pad[" "]; msgStream.PutF1["deleting %g\n", [rope[lagRope]] ]; PFS.Delete[lagPath ! PFS.Error => { Pad[" "]; msgStream.PutF1[ "\n %g", [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; 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; findOnly: BOOL ¬ FALSE; 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 -- => { countBytes ¬ sense; }; 'f, 'F -- findOnly -- => { findOnly ¬ 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; }; 'z, 'Z -- keep -- => { includeZeros ¬ sense; }; ENDCASE => NULL; sense ¬ TRUE; ENDLOOP; } ELSE { msgStream ¬ CheckOutPut[cmd.out, outFileName, append]; 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 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 findOnly 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]]; }; delverDoc: Rope.ROPE ~ "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) -z include zero-length files (default: ignore them)"; Commander.Register[ "DelVer", DelverCommand, delverDoc, $DelVer]; END. B DelverImpl.mesa Copyright Σ 1990, 1991 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) June 7, 1990 2:43 pm PDT Willie-s, May 3, 1991 10:46 am PDT 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 just list the name to the command output Really try to delete this file Add the new file to the tail Κ g•NewlineDelimiter –(cedarcode) style™headšœ™Icodešœ Οeœ7™BL™,L™"L™/L™-—˜LšΟk œ.žœžœ˜O—šœ žœž˜Lšžœ.žœžœ˜LLšœž˜L˜Lšœ žœ˜%Lšžœžœ žœ˜Lšžœžœžœ˜Lšžœžœžœžœ˜Lš œ žœžœžœžœžœžœ˜5—L˜šΟn œ˜(Lš œžœ žœžœžœžœ™:šœ™Lšœžœžœ™1Lšœ6™6L™—šŸ œžœ žœ˜#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š žœžœžœ žœžœ˜