DIRECTORY Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, Failed, Parse], Convert, File, FileBackdoor, FSBackdoor, IO, Process USING [CheckForAbort], Rope; PigsInSpace: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, File, FileBackdoor, FSBackdoor, IO, Process, Rope = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; PigsInSpaceProc: Commander.CommandProc = { out: STREAM _ cmd.out; count: INT _ 10; volume: File.Volume; fileCounter: INT _ 0 ; pigItem: TYPE = RECORD [ fullGName: ROPE _ NIL, size: INT _ 0 ]; pigArray: TYPE = RECORD [ items: SEQUENCE length: NAT OF pigItem ]; pigs: REF pigArray; minSize: INT _ 0; lastEntry: FSBackdoor.EntryPtr; localCount, cachedCount, attachedCount: INT _ 0; matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] ={ lastEntry _ entry; RETURN[TRUE, FALSE]; }; acceptProc: SAFE PROC RETURNS [stop: BOOLEAN] = TRUSTED { thisFP: File.FP _ File.nullFP; fileName: ROPE; fileNameLessVersion: ROPE; nameBodyRopeText: Rope.Text; stop _ FALSE; nameBodyRopeText _ FSBackdoor.TextFromTextRep[@lastEntry[lastEntry.nameBody]]; fileNameLessVersion _ IF nameBodyRopeText.InlineFetch[0] # '[ THEN Rope.Concat["[]<>", nameBodyRopeText] ELSE nameBodyRopeText; fileName _ Rope.Cat[fileNameLessVersion, "!", Convert.RopeFromCard[from: lastEntry.version, showRadix: FALSE]]; WITH e: lastEntry^ SELECT FROM local => { thisFP _ e.fp; localCount _ localCount + 1; }; cached => { thisFP _ e.fp; cachedCount _ cachedCount + 1; }; attached => { attachedCount _ attachedCount + 1; }; ENDCASE ; IF thisFP # File.nullFP THEN { -- skips attachments errorFree: BOOL _ TRUE; fileCounter _ fileCounter + 1; IF fileCounter MOD 100 = 0 THEN { IF fileCounter MOD 1000 = 0 THEN cmd.out.PutF["(%g) ", IO.int[fileCounter]] ELSE cmd.out.PutRope[". "]; }; doAFile[thisFP: thisFP, fileName: fileName]; }; }; doAFile: PROC [thisFP: File.FP, fileName: ROPE]= { errorFree: BOOL _ TRUE; file: File.Handle; file _ File.Open[volume, thisFP ! File.Error => { SELECT why FROM unknownFile, unknownPage, inconsistent, software => { errorFree _ FALSE; CONTINUE; }; ENDCASE; }; ]; IF errorFree THEN { size: File.PageCount; victim: NAT _ count+1; minSizeSoFar: INT _ LAST[INT]; Process.CheckForAbort[]; [size: size] _ File.Info[file]; IF size < minSize THEN RETURN; FOR index: INT IN [0..count) DO IF pigs[index].size <= minSize THEN {victim _ index; EXIT}; IF pigs[index].size < minSizeSoFar THEN { victim _ index; minSizeSoFar _ pigs[index].size; }; ENDLOOP; IF victim = count+1 THEN RETURN; pigs[victim].fullGName _ fileName; minSize _ pigs[victim].size; pigs[victim].size _ size; }; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Usage}]; counter: INT _ 1; expectType: TYPE = {num, volName, switch}; expecting: expectType _ num; doCached: BOOL _ TRUE; doLocal: BOOL _ TRUE; doRoot: BOOL _ TRUE; volumeName: ROPE _ NIL; pattern: Rope.Text; IF argv.argc < 1 THEN GOTO Usage; WHILE counter < argv.argc DO arg: ROPE _ argv[counter]; IF arg.IsEmpty[] THEN GOTO Usage; IF arg.InlineFetch[0] = '- THEN { c: CHAR; IF arg.Length[] # 2 THEN GOTO Usage; c _ arg.InlineFetch[1] ; SELECT c FROM 'n => expecting _ num; 'v => expecting _ volName; 'c => doCached _ FALSE; 'l => doLocal _ FALSE; 'r => doRoot _ FALSE; ENDCASE; } ELSE { -- NOT '- SELECT expecting FROM num => count _ Convert.IntFromRope[arg ! Convert.Error => GOTO Usage]; volName => volumeName _ arg; switch => GOTO Usage; ENDCASE; expecting _ switch; }; counter _ counter + 1; ENDLOOP; IF count <= 0 THEN GOTO Usage; pigs _ NEW[pigArray[count]]; SELECT TRUE FROM doCached AND doLocal => pattern _ ""; ~doCached AND doLocal => pattern _ ""; doCached AND ~doLocal => pattern _ "[*"; ~doCached AND ~doLocal => GOTO Usage; ENDCASE; volume _ IF volumeName = NIL THEN File.SystemVolume[] ELSE File.FindVolumeFromName[volumeName]; IF volume = NIL THEN GOTO Usage; IF doRoot THEN { FOR rootFile: File.VolumeFile IN File.VolumeFile DO rootFP: File.FP _ File.nullFP; fileName: ROPE; rootFP _ FileBackdoor.GetRoot[volume, rootFile ! File.Error => LOOP].fp; IF rootFP = File.nullFP THEN LOOP; fileName _ SELECT rootFile FROM checkpoint => "Checkpoint", microcode => "Microcode", germ => "Germ", bootFile => "BootFile", debugger => "Debugger", debuggee => "Debuggee", VM => "VM", VAM => "VAM", client => "FS-root", alpine => "Alpine-root", ENDCASE => "Other-root"; fileName _ Rope.Concat[fileName, " Root File"]; doAFile[rootFP, fileName]; ENDLOOP; }; cmd.out.PutRope["Starting enumerate of FS BTree. A dot is printed for every 100 files\n"]; FSBackdoor.Enumerate[volName: volumeName, nameBodyPattern: pattern, localOnly: ~doCached, allVersions: TRUE, version: FSBackdoor.noVersion, matchProc: matchProc, acceptProc: acceptProc]; cmd.out.PutF["\n File counts: %g local, %g cached, and %g attachments\n", [integer[localCount]], [integer[cachedCount]], [integer[attachedCount]]]; FOR outputLine: INT IN [0..count) DO biggestIndex: NAT _ LAST[NAT]; biggestSize: INT _ 0; FOR index: INT IN [0..count) DO IF pigs[index].size > biggestSize THEN { biggestIndex _ index; biggestSize _ pigs[index].size; }; ENDLOOP; IF biggestIndex = LAST[NAT] THEN EXIT; IO.PutF[out, " %-30g %g pages\n", [rope[pigs[biggestIndex].fullGName]], [integer[pigs[biggestIndex].size]]]; pigs[biggestIndex].size _ -1; ENDLOOP; pigs _ NIL; EXITS Usage => RETURN[$Failure, "Usage: PigsInSpace {count _ 10} {-n count | -v volume | -c | -l}* "]; }; Init: PROCEDURE = { Commander.Register[ "///Commands/PigsInSpace", PigsInSpaceProc, "PigsInSpace {count _ 10} {-n count | -v volume | -c | -l}* -- list `count' largest files (default 10) on volume 'volume' (default is the system volume). The 'c' switch means don't list cached file and the 'l' switch means don't list local files"]; }; Init[]; END. βPigsInSpace.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Bob Hagmann July 4, 1986 9:19:31 am PDT [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] Enumerate the cache, and find the "count" largest files. Enumeration is fairly slow since the files must actually be opened to discover their size. We keep an unsorted sequence of the largest files found so far. This data structure is OK since we are so heavily disk bound. file: File.Handle; file _ File.Open[volume, thisFP ! File.Error => { SELECT why FROM unknownFile, unknownPage, inconsistent, software => { errorFree _ FALSE; CONTINUE; }; ENDCASE; }; ]; IF errorFree THEN { size: File.PageCount; victim: NAT _ count+1; minSizeSoFar: INT _ LAST[INT]; Process.CheckForAbort[]; [size: size] _ File.Info[file]; IF size < minSize THEN RETURN[FALSE]; FOR index: INT IN [0..count) DO IF pigs[index].size <= minSize THEN {victim _ index; EXIT}; IF pigs[index].size < minSizeSoFar THEN { victim _ index; minSizeSoFar _ pigs[index].size; }; ENDLOOP; IF victim = count+1 THEN RETURN[TRUE]; pigs[victim].fullGName _ fileName; minSize _ pigs[victim].size; pigs[victim].size _ size; RETURN[FALSE]; }; Initialization Bob Hagmann April 10, 1985 7:01:05 am PST created Bob Hagmann May 20, 1985 8:17:12 am PDT changes to: DIRECTORY, PigsInSpace, infoProc (local of PigsInSpaceProc) Bob Hagmann June 20, 1985 10:14:59 am PDT modified to do local files and any logical volume Bob Hagmann July 4, 1986 9:14:34 am PDT changes to: PigsInSpaceProc, DIRECTORY, PigsInSpace, doAFile (local of PigsInSpaceProc), acceptProc (local of PigsInSpaceProc) Κ ˜šœ™Jšœ Οmœ1™