<> <> <> <> <<>> DIRECTORY BasicTime USING [GMT, nullGMT], Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, Failed, Parse, StarExpansion], Convert USING [Error, IntFromRope], DFUtilities USING [DateToStream], FileNames USING [ConvertToSlashFormat, CurrentWorkingDirectory, DirectoryContaining, GetShortName, HomeDirectory, IsADirectory, IsAPattern, ResolveRelativePath, StripVersionNumber], FS USING [Close, Copy, Delete, EnumerateForNames, Error, ErrorDesc, ExpandName, FileInfo, GetDefaultWDir, Open, NameProc, nullOpenFile, OpenFile, Rename, SetDefaultWDir, SetKeep, StreamOpen], FSBackdoor USING [Flush, EntryPtr, Enumerate, EnumerateCacheForNames, MakeFName, NameProc, ScavengeDirectoryAndCache, SetFreeboard, TextFromTextRep, VolumePages], IO USING [Close, EndOf, EndOfStream, Error, GetBlock, PutBlock, PutChar, PutF, PutFR, PutRope, PutText, Reset, STREAM, TextFromTOS, TOS], List USING [Assoc, PutAssoc], ProcessExtras USING [CheckForAbort], ProcessProps USING [GetPropList], ReadEvalPrint USING [Handle], Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, Index, Length, Match, Replace, ROPE, Run, SkipTo, Substr, Text], ViewerOps USING [PaintViewer]; FSFileCommandsImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, DFUtilities, FileNames, FS, FSBackdoor, IO, List, ProcessExtras, ProcessProps, Rope, ViewerOps = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; FSErrorMsg: PROC [error: FS.ErrorDesc] RETURNS [ROPE] = { SELECT error.group FROM lock => RETURN[" -- locked!\n"]; ENDCASE => IF error.code = $unknownFile THEN RETURN [" -- not found!\n"] ELSE RETURN[Rope.Cat[" -- FS.Error: ", error.explanation, "\n"]]; }; FSErrorMsg1: PROC [error: FS.ErrorDesc] RETURNS [ROPE] = { SELECT error.group FROM lock => RETURN[" -- locked!"]; ENDCASE => IF error.code = $unknownFile THEN RETURN [" -- not found!"] ELSE RETURN[Rope.Concat["\n -- FS.Error: ", error.explanation]]; }; createKeep: CARDINAL _ 2; DeleteFiles: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> out: STREAM = cmd.out; exactLevelMatch: BOOL _ FALSE; anglesRequired: INT _ 0; deleteIt: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> ProcessExtras.CheckForAbort[]; continue _ TRUE; IF exactLevelMatch AND anglesRequired # CountAngles[fullFName] THEN RETURN; IO.PutRope[out, " deleting "]; IO.PutRope[out, fullFName]; FS.Delete[fullFName ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE} ]; IO.PutRope[out, "\n"]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; FOR i: NAT IN [1..argv.argc) DO pattern: ROPE _ argv[i]; IF Rope.Match["-*", pattern] THEN { <> sense: BOOL _ TRUE; FOR i: INT IN [1..Rope.Length[pattern]) DO c: CHAR = Rope.Fetch[pattern, i]; SELECT c FROM '~ => {sense _ NOT sense; LOOP}; 'x, 'X => exactLevelMatch _ sense; ENDCASE; sense _ TRUE; ENDLOOP; LOOP; }; {ENABLE FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}; pattern _ FileNames.ResolveRelativePath[pattern]; pattern _ FS.ExpandName[pattern].fullFName; IF pattern.Length[] = 0 THEN LOOP; IF exactLevelMatch THEN anglesRequired _ CountAngles[pattern]; IF Rope.Find[pattern, "!"] = -1 THEN pattern _ Rope.Concat[pattern, "!L"]; IF Rope.Find[pattern, "*"] = -1 THEN [] _ deleteIt[pattern] ELSE FS.EnumerateForNames[pattern, deleteIt]; }; ProcessExtras.CheckForAbort[]; ENDLOOP; EXITS Die => result _ $Failure; }; TypeFile: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> block: REF TEXT _ NEW[TEXT[256]]; nBytesRead: NAT _ 0; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc < 2 THEN GOTO Usage; FOR i: NAT IN [1..argv.argc) DO fileStream: STREAM _ FS.StreamOpen[FileNames.ResolveRelativePath[argv[i]] ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}]; WHILE NOT IO.EndOf[fileStream] DO nBytesRead _ IO.GetBlock[fileStream, block, 0, 256 ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; IO.PutBlock[cmd.out, block, 0, nBytesRead ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; ProcessExtras.CheckForAbort[]; ENDLOOP; IF fileStream # NIL THEN IO.Close[fileStream]; ENDLOOP; EXITS Die => result _ $Failure; Usage => RETURN[$Failure, "Usage: Type list-of-patterns\n"]; }; PDWD: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> RETURN[NIL, FS.GetDefaultWDir[]]; }; SDWD: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> dir: ROPE; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc # 2 THEN GOTO Usage; dir _ FileNames.ResolveRelativePath[argv[1]]; IF argv[1].Length < 3 THEN GOTO Usage; FS.SetDefaultWDir[dir ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GOTO Die}]; [result, msg] _ PDWD[cmd]; EXITS Usage => RETURN[$Failure, "Usage: SetDefaultWorkingDirectory directoryName"]; Die => result _ $Failure; }; PrintFSCacheInfo: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> volName: ROPE _ NIL; size, free, freeboard: INT; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc > 2 THEN {msg _ "Usage: PrintFSCacheInfo {volumeName}"; GO TO Die}; IF argv.argc = 2 THEN volName _ argv[1]; [size, free, freeboard] _ FSBackdoor.VolumePages[volName ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GOTO Die}]; msg _ IO.PutFR[" size = %g, free = %g, freeboard = %g\n", [integer[size]], [integer[free]], [integer[freeboard]] ]; EXITS Die => result _ $Failure; }; SetKeep: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> pattern: ROPE; keep: INT; bangIndex: PROC [r: ROPE] RETURNS [INT] = { FOR i: INT DECREASING IN [0 .. Rope.Length[r]) DO IF Rope.Fetch[r, i] = '! THEN RETURN [i]; ENDLOOP; RETURN [Rope.Length[r]]; }; setIt: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> fullFName _ Rope.Substr[fullFName, 0, bangIndex[fullFName]]; cmd.out.PutF[" processing %g", [rope[fullFName]]]; FS.SetKeep[fullFName, keep]; cmd.out.PutChar['\n]; RETURN[NOT AbortRequested[cmd]]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc # 3 THEN GO TO Usage; keep _ Convert.IntFromRope[argv[1] ! Convert.Error => GO TO Usage]; pattern _ Rope.Replace[base: argv[2], start: bangIndex[argv[2]], with: "!H"]; FS.EnumerateForNames[pattern, setIt ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}]; EXITS Die => result _ $Failure; Usage => RETURN[$Failure, "Usage: SetKeep keep pattern"]; }; SetFSFreeboard: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> freeBoard: INT; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc # 2 THEN GOTO Usage; freeBoard _ Convert.IntFromRope[argv[1] ! Convert.Error => GOTO Usage]; FSBackdoor.SetFreeboard[freeBoard ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GOTO Die;}]; EXITS Usage => RETURN[$Failure, "Usage: SetFreeboard nPages"]; Die => result _ $Failure; }; PrintWorkingDirectory: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> wDir: ROPE _ FileNames.CurrentWorkingDirectory[]; rep: ReadEvalPrint.Handle _ NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cmd.propertyList]]; IF rep # NIL AND rep.viewer # NIL THEN { <> rep.viewer.name _ Rope.Concat["CommandTool: WD = ", wDir]; ViewerOps.PaintViewer[viewer: rep.viewer, hint: caption, clearClient: FALSE]; }; RETURN[NIL, wDir]; }; ChangeWorkingDirectory: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> wDir: ROPE; root: BOOL _ FALSE; push: BOOL _ FALSE; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; list: LIST OF ROPE _ NARROW[List.Assoc[$WorkingDirectoryStack, cmd.propertyList]]; SELECT cmd.procData.clientData FROM $CDR => root _ TRUE; $PDR => root _ push _ TRUE; $PD => push _ TRUE; ENDCASE; IF push THEN list _ CONS[FileNames.CurrentWorkingDirectory[], list]; SELECT argv.argc FROM 1 => wDir _ IF root THEN "///" ELSE FileNames.HomeDirectory[]; 2 => { wDir _ argv[1]; IF root THEN wDir _ FS.ExpandName[wDir, "///" ! FS.Error => {msg _ FSErrorMsg[error]; GO TO oops}; ].fullFName; }; ENDCASE => {msg _ "Usage: ChangeWorkingDirectory directoryName"; GO TO oops}; [result, msg] _ SetWD[cmd, wDir]; IF result # $Failure AND push THEN cmd.propertyList _ List.PutAssoc[$WorkingDirectoryStack, list, cmd.propertyList]; EXITS oops => result _ $Failure; }; PopWD: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> list: LIST OF ROPE _ NARROW[List.Assoc[key: $WorkingDirectoryStack, aList: cmd.propertyList]]; IF list = NIL THEN RETURN[NIL, FileNames.CurrentWorkingDirectory[]]; [result, msg] _ SetWD[cmd, list.first]; list _ list.rest; cmd.propertyList _ List.PutAssoc[key: $WorkingDirectoryStack, val: list, aList: cmd.propertyList]; }; SetWD: PROC [cmd: Commander.Handle, wDir: ROPE] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] = { rep: ReadEvalPrint.Handle; wDir _ FileNames.ResolveRelativePath[FileNames.ConvertToSlashFormat[wDir]]; <> IF wDir.Length[] = 0 THEN RETURN[$Failure, "empty working directory"]; IF wDir.Fetch[0] # '/ THEN wDir _ Rope.Concat[FileNames.CurrentWorkingDirectory[], wDir]; IF wDir.Fetch[wDir.Length[] - 1] # '/ THEN wDir _ Rope.Concat[wDir, "/"]; IF wDir.Length[] < 3 THEN RETURN[NIL]; msg _ wDir; [] _ List.PutAssoc[key: $WorkingDirectory, val: wDir, aList: ProcessProps.GetPropList[]]; <> rep _ NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: cmd.propertyList]]; IF rep # NIL AND rep.viewer # NIL THEN { rep.viewer.name _ Rope.Concat["CommandTool: WD = ", wDir]; ViewerOps.PaintViewer[viewer: rep.viewer, hint: caption, clearClient: FALSE]; }; }; FindExcessVersionsCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> out: STREAM = cmd.out; lagName: ROPE _ NIL; lagBang: INT _ 0; inner: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> bang: INT _ Rope.Length[fullFName]; pos: INT _ bang; continue _ TRUE; WHILE pos > 0 DO IF Rope.Fetch[fullFName, pos _ pos - 1] = '! THEN {bang _ pos; EXIT}; ENDLOOP; IF lagBang = bang AND Rope.Run[lagName, 0, fullFName, 0, FALSE] >= bang THEN { <> IF kill THEN { IO.PutRope[cmd.out, "\ndeleting "]; IO.PutRope[cmd.out, lagName]; FS.Delete[lagName ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; } ELSE { <> IO.PutRope[cmd.out, lagName]; IO.PutRope[cmd.out, " "]; }; }; lagName _ fullFName; lagBang _ bang; }; kill: BOOL _ FALSE; DoPattern: PROC [pattern: ROPE] = { FS.EnumerateForNames[pattern: pattern, proc: inner ! FS.Error => IF error.group # bug THEN { IO.PutRope[cmd.out, FSErrorMsg1[error]]; CONTINUE; }]; IO.PutRope[cmd.out, "\n"]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO bogus}]; WITH cmd.procData.clientData SELECT FROM opt: ATOM => SELECT opt FROM $Find => kill _ FALSE; $Kill => kill _ TRUE; ENDCASE; ENDCASE; IF argv = NIL OR argv.argc <= 1 THEN DoPattern["*"] ELSE FOR i: NAT IN [1..argv.argc) DO DoPattern[argv[i]]; ENDLOOP; EXITS bogus => result _ $Failure; }; CacheFindExcessVersionsCommand: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> out: STREAM = cmd.out; lagName: ROPE _ NIL; lagBang: INT _ 0; inner: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> bang: INT _ Rope.Length[fullFName]; pos: INT _ bang; continue _ TRUE; WHILE pos > 0 DO IF Rope.Fetch[fullFName, pos _ pos - 1] = '! THEN {bang _ pos; EXIT}; ENDLOOP; IF lagBang = bang AND Rope.Run[lagName, 0, fullFName, 0, FALSE] >= bang THEN { <> IF kill THEN { IO.PutRope[out, "\nflushing "]; IO.PutRope[out, lagName]; FSBackdoor.Flush[lagName ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; } ELSE { <> IO.PutRope[out, lagName]; IO.PutRope[out, " "]; }; }; lagName _ fullFName; lagBang _ bang; }; DoPattern: PROC [pattern: ROPE] = { FSBackdoor.EnumerateCacheForNames[pattern: pattern, proc: inner ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; IO.PutRope[cmd.out, "\n"]; }; kill: BOOL _ FALSE; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO bogus}]; WITH cmd.procData.clientData SELECT FROM opt: ATOM => SELECT opt FROM $Find => kill _ FALSE; $Kill => kill _ TRUE; ENDCASE; ENDCASE; IF argv = NIL OR argv.argc <= 1 THEN DoPattern["*"] ELSE FOR i: NAT IN [1..argv.argc) DO DoPattern[argv[i]]; ENDLOOP; EXITS bogus => result _ $Failure; }; FSFlushCache: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> out: STREAM = cmd.out; pattern: ROPE; flushIt: FSBackdoor.NameProc = { out.PutF[" flushing %g", [rope[fullGName]]]; FSBackdoor.Flush[fullGName ! FS.Error => { IF error.group = $lock THEN { out.PutRope[" -- Locked!"]; CONTINUE; } ELSE out.PutRope[" -- Error!\n"]; }]; out.PutRope["\n"]; RETURN[NOT AbortRequested[cmd]]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; FOR i: NAT IN [1..argv.argc) DO pattern _ FileNames.ResolveRelativePath[argv[i]]; {ENABLE FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}; IF Rope.Find[pattern, "*"] = -1 THEN [] _ flushIt[pattern] ELSE FSBackdoor.EnumerateCacheForNames[flushIt, NIL, pattern]; }; IF AbortRequested[cmd] THEN EXIT; ENDLOOP; EXITS Die => result _ $Failure; }; FSListBTree: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> out: STREAM _ cmd.out; pattern: Rope.Text _ NIL; printLocal: BOOL _ FALSE; printAttached: BOOL _ FALSE; printCached: BOOL _ TRUE; anySwitch: BOOL _ FALSE; localText: REF TEXT _ NEW[TEXT[64]]; localOut: STREAM _ IO.TOS[localText]; accept: PROC RETURNS [stop: BOOL] = { IO.PutText[out, IO.TextFromTOS[localOut]]; RETURN[stop: AbortRequested[cmd]]; }; print: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL _ FALSE] = UNCHECKED { name: ROPE = FSBackdoor.MakeFName[ FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version]; IO.Reset[localOut]; WITH e: entry^ SELECT FROM local => IF printLocal THEN { IO.PutF[localOut, "L %g\n", [rope[name]]]; accept _ TRUE; }; attached => IF printAttached THEN { toName: ROPE = FSBackdoor.TextFromTextRep[@entry[e.attachedTo]]; IO.PutF[localOut, "A %-30g (%g)\n", [rope[name]], [rope[toName]]]; accept _ TRUE; }; cached => IF printCached THEN { IO.PutF[localOut, "C %-40g ", [rope[name]]]; DFUtilities.DateToStream[localOut, [explicit, e.used] ]; IO.PutRope[localOut, "\n"]; accept _ TRUE; }; ENDCASE => ERROR; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc = 1 THEN FSBackdoor.Enumerate[ volName: NIL, nameBodyPattern: "[*", localOnly: FALSE, allVersions: TRUE, version: [0], matchProc: print, acceptProc: accept] ELSE FOR i: NAT IN [1..argv.argc) DO IF argv[i].Fetch[0] = '- THEN { IF NOT anySwitch THEN { printLocal _ FALSE; printAttached _ FALSE; printCached _ FALSE; anySwitch _ TRUE; }; FOR j: INT IN [1..argv[i].Length[]) DO SELECT argv[i].Fetch[j] FROM 'c => printCached _ TRUE; 'l => printLocal _ TRUE; 'a => printAttached _ TRUE; ENDCASE; ENDLOOP; ProcessExtras.CheckForAbort[]; LOOP; }; pattern _ Rope.Flatten[FileNames.ResolveRelativePath[argv[i]]]; FSBackdoor.Enumerate[ volName: NIL, nameBodyPattern: pattern, localOnly: FALSE, allVersions: TRUE, version: [0], matchProc: print, acceptProc: accept]; IF AbortRequested[cmd] THEN EXIT; ENDLOOP; EXITS Die => result _ $Failure; }; FSLoad: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> argv: CommandTool.ArgumentVector; out: STREAM _ cmd.out; pattern: ROPE; fetch: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> file: FS.OpenFile _ FS.nullOpenFile; out.PutRope[" loading "]; out.PutRope[fullFName]; { file _ FS.Open[fullFName, read ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; GO TO err} ]; FS.Close[file]; EXITS err => {}}; out.PutRope["\n"]; RETURN[NOT AbortRequested[cmd]]; }; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; FOR i: NAT IN [1..argv.argc) DO pattern _ FileNames.ResolveRelativePath[argv[i]]; IF pattern.Find["!"] = -1 THEN pattern _ Rope.Concat[pattern, "!H"]; FS.EnumerateForNames[pattern, fetch ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg1[error]; GO TO Die}]; IF AbortRequested[cmd] THEN EXIT; ENDLOOP; EXITS Die => result _ $Failure; }; FSScavenge: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> volName: ROPE _ NIL; argv: CommandTool.ArgumentVector _ ExpandAndParse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; IF argv.argc > 1 THEN volName _ argv[1]; FSBackdoor.ScavengeDirectoryAndCache[ volName ! FS.Error => {msg _ FSErrorMsg[error]; GO TO Die}]; EXITS Die => result _ $Failure; }; <<>> <> <> <> <> <> <> <> CopyAndRename: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> forceCopy: BOOL _ FALSE; destinationDirectory: ROPE _ NIL; leftArrowExists: BOOL; doACopy: BOOL _ cmd.procData.clientData = $Copy; doStore: BOOL _ cmd.procData.clientData = $Store; dirBeforeStar: ROPE; dirBeforeStarLength: INT; retainStructure: BOOL _ FALSE; updateOnly: BOOL _ FALSE; exactLevelMatch: BOOL _ FALSE; anglesRequired: INT _ 0; HandleAFile: PROC [to, from: ROPE] = { ProcessExtras.CheckForAbort[]; cmd.out.PutF[" %g _ %g", [rope[to]], [rope[from]]]; {ENABLE FS.Error => IF error.group # bug THEN {msg _ FSErrorMsg1[error]; GO TO skipIt}; IF updateOnly THEN { sourceTime: BasicTime.GMT _ BasicTime.nullGMT; destTime: BasicTime.GMT _ BasicTime.nullGMT; sourceTime _ FS.FileInfo[from].created; destTime _ FS.FileInfo[to ! FS.Error => IF error.group # bug THEN CONTINUE].created; IF sourceTime = destTime AND sourceTime # BasicTime.nullGMT THEN { <> cmd.out.PutRope["\n -- not copied, create dates match"]; GO TO skipIt; }; }; IF doACopy THEN FS.Copy[from: from, to: to, keep: createKeep, attach: NOT forceCopy] ELSE FS.Rename[from: from, to: to]; EXITS skipIt => {}; }; cmd.out.PutRope["\n"]; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; nArgs: NAT _ argv.argc; { <> i: NAT _ 1; length: INT; Bump: PROC [scratch: NAT] = { FOR j: NAT IN (scratch..nArgs) DO argv[j - 1] _ argv[j]; ENDLOOP; nArgs _ nArgs - 1; }; <<>> WHILE i < nArgs DO length _ argv[i].Length[]; SELECT TRUE FROM length = 0 => Bump[i]; argv[i].Fetch[0] = '- => { FOR j: INT IN [1..length) DO SELECT argv[i].Fetch[j] FROM 'c, 'C => forceCopy _ TRUE; 'r, 'R => retainStructure _ TRUE; 'u, 'U => updateOnly _ TRUE; 'x, 'X => exactLevelMatch _ TRUE; ENDCASE; ENDLOOP; Bump[i]; }; ENDCASE => i _ i + 1; ENDLOOP; }; -- end of switch processing <<>> <> leftArrowExists _ nArgs >= 3 AND Rope.Equal[argv[2], "_"]; FOR i: NAT IN [1..nArgs) DO argv[i] _ FileNames.ConvertToSlashFormat[FileNames.ResolveRelativePath[argv[i]]]; ENDLOOP; IF leftArrowExists THEN { IF FileNames.IsADirectory[argv[1]] THEN destinationDirectory _ argv[1] ELSE { IF nArgs # 4 OR FileNames.IsAPattern[argv[1]] OR FileNames.IsAPattern[argv[3]] THEN RETURN[$Failure, "Bad syntax for copying a file"]; HandleAFile[from: argv[3], to: argv[1]]; RETURN[IF msg # NIL THEN $Failure ELSE NIL, msg]; }; } ELSE destinationDirectory _ FileNames.ConvertToSlashFormat[FileNames.CurrentWorkingDirectory[]]; <> FOR i: NAT IN [(IF leftArrowExists THEN 3 ELSE 1) .. nArgs) DO IF FileNames.IsADirectory[argv[i]] THEN { msg _ Rope.Concat["Cannot copy a directory: ", argv[i]]; GO TO Die; }; IF FileNames.IsAPattern[argv[i]] THEN { pattern: ROPE _ argv[i]; handleIt: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> to: ROPE; short: ROPE _ NIL; continue _ TRUE; IF SkipFunny[fullFName] THEN RETURN; IF exactLevelMatch AND anglesRequired # CountAngles[fullFName] THEN RETURN; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF retainStructure THEN to _ FileNames.StripVersionNumber[fullFName.Substr[dirBeforeStarLength]] ELSE to _ FileNames.GetShortName[fullFName]; to _ Rope.Concat[destinationDirectory, to]; HandleAFile[from: fullFName, to: to]; RETURN[msg = NIL]; }; IF pattern.Find["!"] = -1 THEN pattern _ Rope.Concat[pattern, "!H"]; IF pattern.Fetch[0] # '/ THEN pattern _ Rope.Concat[FileNames.CurrentWorkingDirectory[], pattern]; IF exactLevelMatch THEN anglesRequired _ CountAngles[pattern]; dirBeforeStar _ FileNames.DirectoryContaining[ path: pattern, pos: Rope.Index[s1: pattern, s2: "*"]]; dirBeforeStarLength _ dirBeforeStar.Length[]; <> FS.EnumerateForNames[pattern, handleIt ! FS.Error => IF error.group # $bug THEN {msg _ FSErrorMsg[error]; GO TO Die}]; } ELSE { to: ROPE _ Rope.Concat[destinationDirectory, FileNames.GetShortName[argv[i]]]; HandleAFile[from: argv[i], to: to]; IF msg # NIL THEN GO TO Die; }; ProcessExtras.CheckForAbort[]; ENDLOOP; EXITS Die => result _ $Failure; }; SkipFunny: PROC [fullFName: ROPE] RETURNS [BOOL] = { lastPoint, bangIndex: INT _ 0; FOR i: INT DECREASING IN [0 .. fullFName.Length[]) DO SELECT fullFName.Fetch[i] FROM '! => bangIndex _ i; '> => {lastPoint _ i; EXIT}; ENDCASE; ENDLOOP; RETURN[ bangIndex = lastPoint + 1 ]; -- skip [..]<..>!1 }; ExpandAndParse: PROC [cmd: Commander.Handle] RETURNS [CommandTool.ArgumentVector] = { <> CommandTool.StarExpansion[cmd]; RETURN [CommandTool.Parse[cmd]]; }; CountAngles: PROC [pattern: ROPE] RETURNS [count: INT _ 0] = { <> len: INT = Rope.Length[pattern]; pos: INT _ 0; WHILE pos < len DO pos _ Rope.SkipTo[pattern, pos+1, ">/"]; count _ count + 1; ENDLOOP; IF Rope.Match["/*", pattern] THEN <> count _ count - 1; }; AbortRequested: PROC [cmd: Commander.Handle] RETURNS [BOOL] = { ProcessExtras.CheckForAbort[]; RETURN [FALSE]; }; Init: PROCEDURE = { Commander.Register[ "///Commands/Copy", CopyAndRename, "Copy newFile _ oldFile, or Copy directory _ {pattern}*\n -c: force copy, -r: retain structure, -u: update only, -x: eXact level match", $Copy]; Commander.Register[ "///Commands/Del", DeleteFiles, "Delete {switch | pattern}*\n -x: eXact level match"]; Commander.Register[ "///Commands/Delete", DeleteFiles, "Delete {switch | pattern}*\n -x: eXact level match"]; Commander.Register[ "///Commands/Rename", CopyAndRename, "Rename newFile _ oldFile, or Rename directory _ {pattern}*\n -r: retain structure, -u: update only, -x: eXact level match"]; Commander.Register[ "///Commands/Type", TypeFile, "Type fileName (in executive window)"]; Commander.Register[ "///Commands/PrintDefaultWorkingDirectory", PDWD, "Print FS default working directory"]; Commander.Register[ "///Commands/SetDefaultWorkingDirectory", SDWD, "SetDefaultWorkingDirectory directoryName"]; Commander.Register[ "///Commands/PrintFSCacheInfo", PrintFSCacheInfo, "PrintFSCacheInfo {volumeName}, Print FS cache info"]; Commander.Register[ "///Commands/SetKeep", SetKeep, "SetKeep keep {pattern}*"]; Commander.Register[ "///Commands/SetFreeboard", SetFSFreeboard, "SetFreeboard nPages -- set FS cache limit"]; Commander.Register[ "///Commands/PrintWorkingDirectory", PrintWorkingDirectory, "Print working directory"]; Commander.Register[ "///Commands/PWD", PrintWorkingDirectory, "Print working directory"]; Commander.Register[ "///Commands/ChangeWorkingDirectory", ChangeWorkingDirectory, "Change working directory", $CD]; Commander.Register[ "///Commands/CD", ChangeWorkingDirectory, "Change working directory", $CD]; Commander.Register[ "///Commands/CDR", ChangeWorkingDirectory, "Change working directory (root relative)", $CDR]; Commander.Register[ "///Commands/PushWorkingDirectory", ChangeWorkingDirectory, "Push working directory", $PD]; Commander.Register[ "///Commands/Push", ChangeWorkingDirectory, "Push working directory", $PD]; Commander.Register[ "///Commands/PushR", ChangeWorkingDirectory, "Push working directory (root relative)", $PDR]; Commander.Register[ "///Commands/PopWorkingDirectory", PopWD, "Pop working directory"]; Commander.Register[ "///Commands/Pop", PopWD, "Pop working directory"]; Commander.Register[ "///Commands/FlushCache", FSFlushCache, "FlushCache {pattern}* -- flush FS cache"]; Commander.Register[ "///Commands/ListBTree", FSListBTree, "ListBTree {pattern}* -- list FS local directory and cache"]; Commander.Register[ "///Commands/FSLoad", FSLoad, "Load {pattern}* -- retrieve files"]; Commander.Register[ "///Commands/Scavenge", FSScavenge, "Scavenge {volumeName} -- FS scavenge"]; Commander.Register[ "///Commands/CacheFindExcessVersions", CacheFindExcessVersionsCommand, "lists excess versions of files to the command output.", $Find]; Commander.Register[ "///Commands/CacheKillExcessVersions", CacheFindExcessVersionsCommand, "deletes excess versions of files.", $Kill]; Commander.Register[ "///Commands/FindExcessVersions", FindExcessVersionsCommand, "lists excess versions of files to the command output.", $Find]; Commander.Register[ "///Commands/KillExcessVersions", FindExcessVersionsCommand, "deletes excess versions of files.", $Kill]; }; <<>> <> Init[]; END.