<> <> <> <> <<>> DIRECTORY BasicTime USING [GMT, nullGMT], Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, GetProp, Failed, Parse], 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, Open, NameProc, nullOpenFile, OpenFile, Rename, SetKeep, StreamOpen], FSBackdoor USING [Flush, EntryPtr, Enumerate, EnumerateCacheForNames, MakeFName, NameProc, ScavengeDirectoryAndCache, SetFreeboard, TextFromTextRep, VolumePages], FSPseudoServers USING [DeletePseudoServer, GetPseudoServers, InsertPseudoServer, Lookup, PseudoServerFromRope, PseudoServerList], IO USING [Close, EndOf, EndOfStream, Error, GetBlock, PutBlock, PutChar, PutF, PutFR, PutFR1, PutRope, PutText, Reset, STREAM, TextFromTOS, TOS], List USING [PutAssoc], Process 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], ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer]; FSFileCommandsImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, DFUtilities, FileNames, FS, FSBackdoor, FSPseudoServers, IO, List, Process, 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]>> Process.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]; }; Process.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 _ CommandTool.Parse[cmd, TRUE ! 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; ]; Process.CheckForAbort[]; ENDLOOP; IF fileStream # NIL THEN IO.Close[fileStream]; ENDLOOP; EXITS Die => result _ $Failure; Usage => RETURN[$Failure, "Usage: Type list-of-patterns\n"]; }; PrintFSCacheInfo: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> volName: ROPE _ NIL; size, free, freeboard: INT; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd, TRUE ! 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 ! 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 _ CommandTool.Parse[cmd, TRUE ! 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]>> <> <> <> RETURN[NIL, FileNames.CurrentWorkingDirectory[]]; }; ChangeWorkingDirectory: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> wDir: ROPE; root: BOOL _ FALSE; push: BOOL _ FALSE; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd, TRUE ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; list: LIST OF ROPE _ NARROW[CommandTool.GetProp[cmd, $WorkingDirectoryStack]]; 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[CommandTool.GetProp[cmd, $WorkingDirectoryStack]]; 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]; }; FindExcessVersionsCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> <<>> Laggards: TYPE = RECORD [ s: SEQUENCE size: NAT OF RECORD [ name: ROPE, bang: INT ] ]; laggards: REF Laggards _ NIL; out: STREAM = cmd.out; keep: (0..LAST[NAT]] _ 1; lagIdx: INT _ 0; -- current interesting laggard is laggards[lagIdx] 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 laggards[lagIdx].bang = bang AND Rope.Run[laggards[lagIdx].name, 0, fullFName, 0, FALSE] >= bang THEN { <> SELECT TRUE FROM NOT kill => { <> IO.PutRope[cmd.out, laggards[lagIdx].name]; IO.PutRope[cmd.out, " "]; }; justCache => { IO.PutRope[out, "\nflushing "]; IO.PutRope[out, laggards[lagIdx].name]; FSBackdoor.Flush[laggards[lagIdx].name ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; }; ENDCASE => { IO.PutRope[cmd.out, "\ndeleting "]; IO.PutRope[cmd.out, laggards[lagIdx].name]; FS.Delete[laggards[lagIdx].name ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}]; }; }; laggards[lagIdx].name _ fullFName; laggards[lagIdx].bang _ bang; lagIdx _ (lagIdx+1) MOD laggards.size }; kill: BOOL _ FALSE; justCache: BOOL _ TRUE; DoPattern: PROC [pattern: ROPE] = { laggards _ NEW[Laggards[keep]]; FOR i: INT IN [0..laggards.size) DO laggards[i] _ [name: "", bang: -1] ENDLOOP; lagIdx _ 0; IF justCache THEN FSBackdoor.EnumerateCacheForNames[pattern: pattern, proc: inner ! FS.Error => IF error.group # bug THEN {IO.PutRope[out, FSErrorMsg1[error]]; CONTINUE}] ELSE 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 ! CommandTool.Failed => {msg _ errorMsg; GO TO bogus}]; argBase: INT _ 1; didPattern: BOOL _ FALSE; WITH cmd.procData.clientData SELECT FROM opt: ATOM => SELECT opt FROM $FindReal => {kill _ FALSE; justCache _ FALSE}; $KillReal => {kill _ TRUE; justCache _ FALSE}; $FindCached => {kill _ FALSE; justCache _ TRUE}; $KillCached => {kill _ TRUE; justCache _ TRUE}; ENDCASE => ERROR; ENDCASE; WHILE (argv # NIL) AND (argBase < argv.argc) DO arg: ROPE = argv[argBase]; IF Rope.Length[arg] > 0 AND Rope.Fetch[arg, 0] = '- THEN { FOR j: INT IN [1..Rope.Length[arg]) DO SELECT Rope.Fetch[arg, j] FROM '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 _ "Can't parse number of versions to keep"; 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; }; ENDCASE => NULL; ENDLOOP; -- j } ELSE {DoPattern[arg]; didPattern _ TRUE}; argBase _ argBase+1; ENDLOOP; IF NOT didPattern THEN DoPattern["*"]; 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 ! 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; Process.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]>> <> <> <> out: STREAM _ cmd.out; 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.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Die}]; FOR i: NAT IN [1..argv.argc) DO pattern: ROPE _ 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 _ CommandTool.Parse[cmd, TRUE ! 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] = { Process.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; }; Process.CheckForAbort[]; ENDLOOP; EXITS Die => result _ $Failure; }; <> PseudoServerAddCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> args: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO failed}]; list: FSPseudoServers.PseudoServerList _ NIL; server: ROPE _ NIL; IF args.argc <= 1 THEN { msg _ "Usage: PseudoServerAdd server write read ..."; GO TO failed}; IF args.argc = 2 THEN { <> server _ args[1]; IF FSPseudoServers.DeletePseudoServer[server] # NIL THEN msg _ IO.PutFR1["'%g' deleted from pseudo server list.", [rope[server]]] ELSE msg _ IO.PutFR1["'%g' not in pseudo server list.", [rope[server]]]; RETURN}; [msg, list] _ FSPseudoServers.PseudoServerFromRope[cmd.commandLine]; IF msg # NIL THEN GO TO failed; IF list = NIL THEN {msg _ "empty??"; GO TO failed}; server _ list.first.server; IF FSPseudoServers.Lookup[server] # NIL THEN msg _ IO.PutFR1["'%g' updated in pseudo server list.", [rope[server]]] ELSE msg _ IO.PutFR1["'%g' added to pseudo server list.", [rope[server]]]; FSPseudoServers.InsertPseudoServer[list]; EXITS failed => {result _ $Failure}; }; PseudoServerPrintCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> serverList: FSPseudoServers.PseudoServerList _ FSPseudoServers.GetPseudoServers[]; out: STREAM = cmd.out; PutName: PROC [name: ROPE] = { IO.PutRope[out, " "]; IF Rope.Length[name] = 0 THEN IO.PutRope[out, "$"] ELSE IO.PutRope[out, name]; }; FOR each: FSPseudoServers.PseudoServerList _ serverList, each.rest WHILE each # NIL DO IF each.first.server = NIL THEN LOOP; IO.PutRope[out, " "]; PutName[each.first.server]; IF each.first.avoidCheck THEN PutName[" -r"]; PutName[each.first.write]; FOR readers: LIST OF ROPE _ each.first.read, readers.rest WHILE readers # NIL DO PutName[readers.first]; ENDLOOP; IO.PutRope[out, "\n"]; ENDLOOP; }; <> <<>> SetWD: PROC [cmd: Commander.Handle, wDir: ROPE] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] = { 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[]]; <> WITH CommandTool.GetProp[cmd, $ReadEvalPrintHandle] SELECT FROM rep: ReadEvalPrint.Handle => { name: ROPE _ Rope.Concat["CommandTool: WD = ", wDir]; root: ViewerClasses.Viewer _ rep.viewer; IF root # NIL THEN { root.name _ name; ViewerOps.PaintViewer[viewer: root, hint: caption, clearClient: FALSE]; FOR v: ViewerClasses.Viewer _ root.link, v.link WHILE v # NIL AND v # root DO v.name _ name; ViewerOps.PaintViewer[viewer: v, hint: caption, clearClient: FALSE]; ENDLOOP; }; }; ENDCASE; }; 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 }; 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] = { Process.CheckForAbort[]; RETURN [FALSE]; }; <<>> <> Init: PROCEDURE = { docAdd: ROPE = "add pseudo-server translation\n\t-r: assume replicated files (avoid remote check)"; docPrint: ROPE = "print pseudo-server translations"; 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/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", FindExcessVersionsCommand, "lists excess versions of files to the command output.", $FindCached]; Commander.Register[ "///Commands/CacheKillExcessVersions", FindExcessVersionsCommand, "deletes excess versions of files.", $KillCached]; Commander.Register[ "///Commands/FindExcessVersions", FindExcessVersionsCommand, "lists excess versions of files to the command output.", $FindReal]; Commander.Register[ "///Commands/KillExcessVersions", FindExcessVersionsCommand, "deletes excess versions of files.", $KillReal]; Commander.Register[ "///Commands/PSAdd", PseudoServerAddCommand, docAdd]; Commander.Register[ "///Commands/PseudoServerAdd", PseudoServerAddCommand, docAdd]; Commander.Register[ "///Commands/PSPrint", PseudoServerPrintCommand, docPrint]; Commander.Register[ "///Commands/PseudoServerPrint", PseudoServerPrintCommand, docPrint]; }; Init[]; END.