<> <> <> <> <> <<>> DIRECTORY BasicTime USING [GMT, MonthOfYear, nullGMT, Unpack, Unpacked], Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, Failed, Parse, StarExpansion], Convert USING [AppendCard, Error, IntFromRope], FileNames USING [ConvertToSlashFormat, CurrentWorkingDirectory, DirectoryContaining, FirstSubdirectory, GetShortName, HomeDirectory, InASubdirectory, IsADirectory, IsAPattern, ResolveRelativePath, StripVersionNumber], FS USING [Close, Copy, Delete, EnumerateForInfo, EnumerateForNames, Error, ErrorDesc, FileInfo, GetDefaultWDir, InfoProc, Open, NameProc, nullOpenFile, OpenFile, Rename, SetDefaultWDir, SetKeep, StreamOpen], FSBackdoor USING [Flush, EntryPtr, Enumerate, EnumerateCacheForNames, MakeFName, NameProc, ScavengeDirectoryAndCache, SetFreeboard, TextFromTextRep, VolumePages], IO USING [card, Close, EndOf, EndOfStream, Error, GetBlock, int, PutBlock, PutChar, PutF, PutFR, PutRope, rope, STREAM, text], List USING [Assoc, PutAssoc], ProcessExtras USING [CheckForAbort], ProcessProps USING [GetPropList], ReadEvalPrint USING [Handle], RefText USING [AppendChar, AppendTextRope], Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, Index, Length, Replace, ROPE, Substr, Text], ViewerOps USING [PaintViewer]; FSFileCommandsImpl: CEDAR PROGRAM IMPORTS BasicTime, Commander, CommandTool, Convert, FileNames, FS, FSBackdoor, IO, List, ProcessExtras, ProcessProps, RefText, Rope, ViewerOps = BEGIN FSErrorMsg: PROC [error: FS.ErrorDesc] RETURNS [Rope.ROPE] = { RETURN[Rope.Cat[" ... FS.Error[", error.explanation, "]\n"]]; }; createKeep: CARDINAL _ 2; AppendShortTime: PROC [to: REF TEXT, from: BasicTime.GMT] RETURNS [REF TEXT] = { u: BasicTime.Unpacked _ BasicTime.Unpack[from]; Month: ARRAY BasicTime.MonthOfYear[January .. December] OF Rope.Text = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; to _ Convert.AppendCard[to, u.day, 10, FALSE]; to _ RefText.AppendChar[to, '-]; to _ RefText.AppendTextRope[to, Month[u.month]]; to _ RefText.AppendChar[to, '-]; to _ Convert.AppendCard[to, u.year MOD 100, 10, FALSE]; to _ RefText.AppendChar[to, ' ]; to _ Convert.AppendCard[to, u.hour, 10, FALSE]; to _ RefText.AppendChar[to, ':]; IF u.minute < 10 THEN to _ RefText.AppendChar[to, '0]; to _ Convert.AppendCard[to, u.minute, 10, FALSE]; to _ RefText.AppendChar[to, ':]; IF u.second < 10 THEN to _ RefText.AppendChar[to, '0]; to _ Convert.AppendCard[to, u.second, 10, FALSE]; RETURN[to]; }; ListFiles: Commander.CommandProc = { argv: CommandTool.ArgumentVector; long: BOOL _ FALSE; printKeep: BOOL _ FALSE; printAttach: BOOL _ FALSE; unBackedUp: BOOL _ FALSE; foundPattern: BOOL _ FALSE; printFullNames: BOOL _ FALSE; newLine: BOOL _ TRUE; level: BOOL _ TRUE; notLevel: BOOL _ FALSE; dirBeforeStar: Rope.ROPE; dirBeforeStarLength: INT; createTime: REF TEXT _ NEW[TEXT[30]]; directories: LIST OF Rope.ROPE _ NIL; any: BOOL _ FALSE; AddToUniqueList: PROC [name: Rope.ROPE] = { l: LIST OF Rope.ROPE _ directories; WHILE l # NIL DO IF l.first.Equal[name, FALSE] THEN RETURN; l _ l.rest; ENDLOOP; directories _ CONS[name, directories]; }; SetSwitches: PROC [switchArgs: Rope.ROPE] = { FOR i: INT IN [1..switchArgs.Length[]) DO SELECT switchArgs.Fetch[i] FROM 'l => long _ TRUE; 'u => unBackedUp _ TRUE; 'a => { printAttach _ TRUE; long _ TRUE; }; 'k => { printKeep _ TRUE; long _ TRUE; }; 'o => newLine _ FALSE; 'f => printFullNames _ TRUE; 'r => notLevel _ TRUE; 'e => { long _ TRUE; printAttach _ TRUE; printKeep _ TRUE; }; ENDCASE; ENDLOOP; }; print: FS.NameProc = { attached: BOOL _ FALSE; IF unBackedUp THEN attached _ FS.FileInfo[name: fullFName, remoteCheck: FALSE].attachedTo # NIL; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF level AND FileNames.InASubdirectory[parent: dirBeforeStar, path: fullFName] THEN { IF NOT unBackedUp THEN { AddToUniqueList[FileNames.FirstSubdirectory[parent: dirBeforeStar, path: fullFName]]; }; RETURN[NOT AbortRequested[cmd]]; }; IF NOT unBackedUp OR (unBackedUp AND NOT attached) THEN { IF NOT newLine THEN cmd.out.PutRope[" "]; IF NOT printFullNames THEN fullFName _ fullFName.Substr[dirBeforeStarLength]; cmd.out.PutRope[fullFName]; IF newLine THEN cmd.out.PutChar['\n]; }; RETURN[NOT AbortRequested[cmd]]; }; printLong: FS.InfoProc = { createTime.length _ 0; createTime _ AppendShortTime[to: createTime, from: created]; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF level AND FileNames.InASubdirectory[parent: dirBeforeStar, path: fullFName] THEN { IF NOT unBackedUp OR attachedTo = NIL THEN { AddToUniqueList[FileNames.FirstSubdirectory[parent: dirBeforeStar, path: fullFName]]; }; RETURN[NOT AbortRequested[cmd]]; }; IF NOT printFullNames THEN fullFName _ fullFName.Substr[dirBeforeStarLength]; IF unBackedUp THEN { IF attachedTo = NIL THEN { IF long THEN { cmd.out.PutF["%-30g %7g %g", IO.rope[fullFName], IO.int[bytes], IO.text[createTime]]; IF printKeep THEN cmd.out.PutF[" keep %g\n", IO.card[keep]] ELSE cmd.out.PutRope["\n"]; } ELSE { cmd.out.PutRope[" "]; cmd.out.PutRope[fullFName]; }; }; RETURN[NOT AbortRequested[cmd]]; }; IF attachedTo = NIL THEN { cmd.out.PutF["%-30g %7g %g", IO.rope[fullFName], IO.int[bytes], IO.text[createTime]]; IF printKeep THEN cmd.out.PutF[" keep %g\n", IO.card[keep]] ELSE cmd.out.PutRope["\n"]; } ELSE { cmd.out.PutF["%-30g %7g %g", IO.rope[fullFName], IO.int[bytes], IO.text[createTime]]; IF printKeep THEN cmd.out.PutF[" keep %2g", IO.card[keep]] ELSE cmd.out.PutChar[' ]; IF printAttach THEN { cmd.out.PutRope[" ("]; cmd.out.PutRope[attachedTo]; cmd.out.PutChar[')]; }; cmd.out.PutChar['\n]; }; RETURN[NOT AbortRequested[cmd]]; }; EachPattern: PROC [pattern: Rope.ROPE] = { directories _ NIL; pattern _ FileNames.ResolveRelativePath[pattern]; pattern _ FileNames.ConvertToSlashFormat[pattern]; IF pattern.Fetch[0] # '/ THEN pattern _ Rope.Concat[FileNames.CurrentWorkingDirectory[], pattern]; level _ pattern.Find["**"] = -1; IF notLevel THEN level _ FALSE; IF pattern.Fetch[pattern.Length[] - 1] = '/ THEN pattern _ Rope.Concat[pattern, "*"]; <> dirBeforeStar _ FileNames.DirectoryContaining[path: pattern, pos: Rope.Index[s1: pattern, s2: "*"]]; dirBeforeStarLength _ dirBeforeStar.Length[]; IF long THEN { FS.EnumerateForInfo[pattern, printLong ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; CONTINUE; }]; } ELSE FS.EnumerateForNames[pattern, print ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; CONTINUE; }]; IF NOT long AND NOT newLine THEN cmd.out.PutRope["\n"]; IF msg # NIL THEN RETURN; { l: LIST OF Rope.ROPE _ NIL; WHILE directories # NIL DO l _ CONS[directories.first, l]; directories _ directories.rest; ENDLOOP; directories _ l; }; WHILE directories # NIL DO IF NOT printFullNames THEN directories.first _ directories.first.Substr[dirBeforeStarLength]; cmd.out.PutRope[directories.first]; cmd.out.PutChar['\n]; directories _ directories.rest; ENDLOOP; }; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; FOR i: NAT IN [1..argv.argc) DO IF argv[i].Length[] = 0 THEN LOOP; IF argv[i].Fetch[0] = '- THEN { SetSwitches[argv[i]]; LOOP; }; any _ TRUE; EachPattern[argv[i]]; IF msg # NIL THEN RETURN[$Failure, msg]; IF AbortRequested[cmd, TRUE] THEN EXIT; ENDLOOP; IF NOT any THEN EachPattern[Rope.Concat[FileNames.CurrentWorkingDirectory[], "*"]]; }; DeleteFiles: Commander.CommandProc = { argv: CommandTool.ArgumentVector; pattern: Rope.ROPE; level: BOOL _ TRUE; dirBeforeStar: Rope.ROPE; dirBeforeStarLength: INT; deleteIt: FS.NameProc = { fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF level AND FileNames.InASubdirectory[parent: dirBeforeStar, path: fullFName] THEN RETURN[NOT AbortRequested[cmd]]; cmd.out.PutF[" deleting %g", [rope[fullFName]]]; FS.Delete[fullFName ! FS.Error => { IF error.group = $lock THEN { cmd.out.PutRope[" -- Locked!"]; CONTINUE; } ELSE cmd.out.PutRope[" -- Error!\n"]; }]; cmd.out.PutRope["\n"]; RETURN[NOT AbortRequested[cmd]]; }; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; FOR i: NAT IN [1..argv.argc) DO pattern _ FileNames.ResolveRelativePath[argv[i]]; pattern _ FileNames.ConvertToSlashFormat[pattern]; IF pattern.Length[] = 0 THEN LOOP; IF pattern.Fetch[0] # '/ THEN pattern _ Rope.Concat[FileNames.CurrentWorkingDirectory[], pattern]; IF Rope.Find[pattern, "*"] = -1 THEN { cmd.out.PutF[" deleting %g", IO.rope[pattern]]; FS.Delete[pattern ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; GOTO Die;}]; cmd.out.PutChar['\n]; } ELSE { level _ pattern.Find["**"] = -1; dirBeforeStar _ FileNames.DirectoryContaining[path: pattern, pos: Rope.Index[s1: pattern, s2: "*"]]; dirBeforeStarLength _ dirBeforeStar.Length[]; IF Rope.Find[pattern, "!"] = -1 THEN pattern _ Rope.Concat[pattern, "!L"]; FS.EnumerateForNames[pattern, deleteIt ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; GOTO Die;}]; }; IF AbortRequested[cmd, TRUE] THEN { EXIT; }; ENDLOOP; EXITS Die => RETURN[$Failure, msg]; }; TypeFile: Commander.CommandProc = {{ argv: CommandTool.ArgumentVector; fileStream: IO.STREAM _ NIL; block: REF TEXT _ NEW[TEXT[256]]; nBytesRead: NAT _ 0; CommandTool.StarExpansion[cmd]; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; IF argv.argc < 2 THEN GOTO Usage; FOR i: NAT IN [1..argv.argc) DO fileStream _ FS.StreamOpen[FileNames.ResolveRelativePath[argv[i]] ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; GOTO Die;}]; WHILE NOT fileStream.EndOf[] DO nBytesRead _ fileStream.GetBlock[block: block, startIndex: 0, count: 256 ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; cmd.out.PutBlock[block: block, startIndex: 0, count: nBytesRead ! IO.EndOfStream => EXIT; IO.Error => EXIT; ]; IF AbortRequested[cmd, TRUE] THEN GOTO Die; ENDLOOP; IF fileStream # NIL THEN fileStream.Close[]; ENDLOOP; EXITS Die => RETURN[$Failure, msg]; Usage => RETURN[$Failure, "Usage: Type list-of-patterns\n"]; }; }; PDWD: Commander.CommandProc = { RETURN[NIL, FS.GetDefaultWDir[]]; }; SDWD: Commander.CommandProc = {{ argv: CommandTool.ArgumentVector; dir: Rope.ROPE; CommandTool.StarExpansion[cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF msg # NIL THEN RETURN[$Failure, msg]; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; 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 => RETURN[$Failure, msg]; }; }; PrintFSCacheInfo: Commander.CommandProc = { argv: CommandTool.ArgumentVector; out: IO.STREAM _ cmd.out; volName: Rope.ROPE; size, free, freeboard: INT; CommandTool.StarExpansion[cmd]; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; IF argv.argc > 2 THEN RETURN[$Failure, "Usage: PrintFSCacheInfo {volumeName}"]; IF argv.argc = 2 THEN volName _ argv[1] ELSE volName _ NIL; [size, free, freeboard] _ FSBackdoor.VolumePages[volName ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; GOTO Die;}]; out.PutF[" size = %g, free = %g, freeboard = %g\n", [integer[size]], [integer[free]], [integer[freeboard]] ]; EXITS Die => RETURN[$Failure, NIL]; }; SetKeep: Commander.CommandProc = {{ argv: CommandTool.ArgumentVector; pattern: Rope.ROPE; keep: INT; bangIndex: PROC [r: Rope.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.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.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; IF argv.argc # 3 THEN GOTO Usage; keep _ Convert.IntFromRope[argv[1] ! Convert.Error => GOTO 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]; GOTO Die;}]; EXITS Die => RETURN[$Failure, msg]; Usage => RETURN[$Failure, "Usage: SetKeep keep pattern"]; }; }; SetFSFreeboard: Commander.CommandProc = {{ argv: CommandTool.ArgumentVector; freeBoard: INT; CommandTool.StarExpansion[cmd]; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; 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 => RETURN[$Failure, msg]; }; }; PrintWorkingDirectory: Commander.CommandProc = { wDir: Rope.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 = { argv: CommandTool.ArgumentVector; rep: ReadEvalPrint.Handle; wDir: Rope.ROPE; CommandTool.StarExpansion[cmd]; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; SELECT argv.argc FROM 1 => wDir _ FileNames.HomeDirectory[]; 2 => wDir _ FileNames.ResolveRelativePath[FileNames.ConvertToSlashFormat[argv[1]]]; ENDCASE => RETURN[$Failure, "Usage: ChangeWorkingDirectory directoryName"]; <> IF wDir.Length[] = 0 THEN RETURN[$Failure, "Usage: ChangeWorkingDirectory directoryName"]; 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[$Failure, "New directory name too short"]; [] _ 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]; }; }; FSFlushCache: Commander.CommandProc = { argv: CommandTool.ArgumentVector; out: IO.STREAM _ cmd.out; pattern: Rope.ROPE; flushIt: FSBackdoor.NameProc = { out.PutF[" flushing %g", IO.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.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; FOR i: NAT IN [1..argv.argc) DO pattern _ FileNames.ResolveRelativePath[argv[i]]; IF Rope.Find[pattern, "*"] = -1 THEN { FSBackdoor.Flush[pattern ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; GOTO Die;}]; out.PutF[" flushing %g\n", IO.rope[pattern]]; } ELSE FSBackdoor.EnumerateCacheForNames[flushIt, NIL, pattern ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; GOTO Die;}]; IF AbortRequested[cmd, TRUE] THEN EXIT; ENDLOOP; EXITS Die => RETURN[$Failure, msg]; }; FSListBTree: Commander.CommandProc = { argv: CommandTool.ArgumentVector; out: IO.STREAM _ cmd.out; pattern: Rope.Text; printLocal: BOOL _ FALSE; printAttached: BOOL _ FALSE; printCached: BOOL _ TRUE; anySwitch: BOOL _ FALSE; printThis: Rope.ROPE; createTime: REF TEXT _ NEW[TEXT[30]]; accept: PROC RETURNS [stop: BOOLEAN] = { out.PutRope[printThis]; RETURN[stop: AbortRequested[cmd]]; }; print: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN _ FALSE] = UNCHECKED { WITH e: entry^ SELECT FROM local => IF printLocal THEN { printThis _ IO.PutFR["L %g\n", [rope[FSBackdoor.MakeFName[ FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version]]]]; accept _ TRUE; }; attached => IF printAttached THEN { toName: Rope.ROPE = FSBackdoor.TextFromTextRep[@entry[e.attachedTo]]; printThis _ IO.PutFR["A %-30g (%g)\n", [rope[FSBackdoor.MakeFName[ FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version]]], [rope[toName]]]; accept _ TRUE; }; cached => IF printCached THEN { createTime.length _ 0; createTime _ AppendShortTime[to: createTime, from: e.used]; printThis _ IO.PutFR["C %-40g %g\n", [rope[FSBackdoor.MakeFName[ FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version]]], [text[createTime]]]; accept _ TRUE; }; ENDCASE => ERROR; }; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; 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; [] _ AbortRequested[cmd, TRUE]; 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; [] _ AbortRequested[cmd, TRUE]; }; FSLoad: Commander.CommandProc = { argv: CommandTool.ArgumentVector; out: IO.STREAM _ cmd.out; pattern: Rope.ROPE; fetch: FS.NameProc = { file: FS.OpenFile _ FS.nullOpenFile; out.PutRope[" loading "]; out.PutRope[fullFName]; out.PutRope["\n"]; file _ FS.Open[fullFName, read ! FS.Error => { IF error.group = $lock THEN CONTINUE ELSE out.PutF[ " ... FS.Error[%g] on opening %g\n", [rope[error.explanation]], [rope[fullFName]] ]; }]; IF file # FS.nullOpenFile THEN FS.Close[file]; RETURN[NOT AbortRequested[cmd]]; }; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; 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 _ FSErrorMsg[error]; GOTO Die;}]; IF AbortRequested[cmd] THEN EXIT; ENDLOOP; EXITS Die => RETURN[$Failure, msg]; }; FSScavenge: Commander.CommandProc = { argv: CommandTool.ArgumentVector; volName: Rope.ROPE; CommandTool.StarExpansion[cmd]; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; IF argv.argc > 1 THEN volName _ argv[1] ELSE volName _ NIL; FSBackdoor.ScavengeDirectoryAndCache[volName ! FS.Error => { msg _ FSErrorMsg[error]; CONTINUE; }]; IF msg # NIL THEN RETURN[$Failure, msg]; }; <<>> <> <> <> <> <> <> <> CopyAndRename: Commander.CommandProc = { argv: CommandTool.ArgumentVector; nArgs: NAT; forceCopy: BOOL _ FALSE; destinationDirectory: Rope.ROPE _ NIL; leftArrowExists: BOOL; doACopy: BOOL _ cmd.procData.clientData = $Copy; level: BOOL _ TRUE; dirBeforeStar: Rope.ROPE; dirBeforeStarLength: INT; retainStructure: BOOL _ FALSE; HandleAFile: PROC [to, from: Rope.ROPE, wantedCreateTime: BasicTime.GMT] = { locked: BOOL _ FALSE; IF doACopy THEN FS.Copy[from: from, to: to, wantedCreatedTime: wantedCreateTime, keep: createKeep, attach: NOT forceCopy ! FS.Error => { IF error.group = $lock THEN { locked _ TRUE; CONTINUE; }; IF error.group # $bug THEN { msg _ FSErrorMsg[error]; CONTINUE; }; }] ELSE FS.Rename[from: from, to: to ! FS.Error => { IF error.group = $lock THEN { locked _ TRUE; CONTINUE; }; IF error.group # $bug THEN { msg _ FSErrorMsg[error]; CONTINUE; }; }]; IF msg # NIL THEN RETURN; cmd.out.PutF[" %g _ %g", IO.rope[to], IO.rope[from]]; IF locked THEN cmd.out.PutRope[" Locked!\n"] ELSE cmd.out.PutRope["\n"]; }; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN RETURN[$Failure, msg]; nArgs _ 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 => forceCopy _ TRUE; 'r => retainStructure _ TRUE; ENDCASE; ENDLOOP; Bump[i]; }; ENDCASE => i _ i + 1; ProcessExtras.CheckForAbort[]; 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], wantedCreateTime: BasicTime.nullGMT]; 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 RETURN[$Failure, Rope.Concat["Cannot copy a directory: ", argv[i]]]; IF FileNames.IsAPattern[argv[i]] THEN { pattern: Rope.ROPE _ argv[i]; handleIt: FS.InfoProc = { to: Rope.ROPE; IF SkipFunny[fullFName] THEN RETURN[TRUE]; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF level AND FileNames.InASubdirectory[parent: dirBeforeStar, path: fullFName] THEN RETURN[NOT AbortRequested[cmd]]; IF retainStructure THEN to _ Rope.Concat[destinationDirectory, FileNames.StripVersionNumber[fullFName.Substr[dirBeforeStarLength]]] ELSE to _ Rope.Concat[destinationDirectory, FileNames.GetShortName[fullFName]]; HandleAFile[from: fullFName, to: to, wantedCreateTime: created]; RETURN[msg = NIL]; }; IF pattern.Find["!"] = -1 THEN pattern _ Rope.Concat[pattern, "!H"]; level _ pattern.Find["**"] = -1; IF pattern.Fetch[0] # '/ THEN pattern _ Rope.Concat[FileNames.CurrentWorkingDirectory[], pattern]; dirBeforeStar _ FileNames.DirectoryContaining[path: pattern, pos: Rope.Index[s1: pattern, s2: "*"]]; dirBeforeStarLength _ dirBeforeStar.Length[]; FS.EnumerateForInfo[pattern: pattern, proc: handleIt ! FS.Error => IF error.group # $bug THEN { msg _ FSErrorMsg[error]; CONTINUE; } ]; IF msg # NIL THEN RETURN[$Failure, msg]; } ELSE { to: Rope.ROPE _ Rope.Concat[destinationDirectory, FileNames.GetShortName[argv[i]]]; HandleAFile[from: argv[i], to: to, wantedCreateTime: BasicTime.nullGMT]; IF msg # NIL THEN RETURN[$Failure, msg]; }; ProcessExtras.CheckForAbort[]; ENDLOOP; }; LoadThisOne: PROC [name: Rope.ROPE, cmd: Commander.Handle] = { file: FS.OpenFile _ FS.nullOpenFile; file _ FS.Open[name, read ! FS.Error => { IF error.group = $lock THEN CONTINUE ELSE cmd.out.PutF[ " ... FS.Error[%g] on opening %g\n", [rope[error.explanation]], [rope[name]] ]; }]; IF file # FS.nullOpenFile THEN FS.Close[file]; }; <<>> SkipFunny: PROC [fullFName: Rope.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 }; AbortRequested: PROC [cmd: Commander.Handle, reset: BOOL _ FALSE] RETURNS [BOOL] = { ProcessExtras.CheckForAbort[]; RETURN [FALSE]; }; Init: PROCEDURE = { Commander.Register[key: "///Commands/Copy", proc: CopyAndRename, doc: "Copy newFile _ oldFile, or Copy directory _ list-of-patterns", clientData: $Copy]; Commander.Register[key: "///Commands/List", proc: ListFiles, doc: "List {-l(ong format) -u(n backed up), -a(ttached), -e(verything), -o(ne line)} list-of-patterns"]; Commander.Register[key: "///Commands/LS", proc: ListFiles, doc: "LS {-l(ong format) -u(n backed up), -a(ttached), -e(verything), -o(ne line)} list-of-patterns"]; Commander.Register[key: "///Commands/Delete", proc: DeleteFiles, doc: "Delete list-of-patterns"]; Commander.Register[key: "///Commands/Rename", proc: CopyAndRename, doc: "Rename newFile _ oldFile, or Rename directory _ list-of-patterns"]; Commander.Register[key: "///Commands/Type", proc: TypeFile, doc: "Type fileName (in executive window)"]; Commander.Register[key: "///Commands/PrintDefaultWorkingDirectory", proc: PDWD, doc: "Print FS default working directory"]; Commander.Register[key: "///Commands/SetDefaultWorkingDirectory", proc: SDWD, doc: "SetDefaultWorkingDirectory directoryName"]; Commander.Register[key: "///Commands/PrintFSCacheInfo", proc: PrintFSCacheInfo, doc: "PrintFSCacheInfo {volumeName}, Print FS cache info"]; Commander.Register[key: "///Commands/SetKeep", proc: SetKeep, doc: "SetKeep keep list-of-patterns"]; Commander.Register[key: "///Commands/SetFreeboard", proc: SetFSFreeboard, doc: "SetFreeboard nPages -- set FS cache limit"]; Commander.Register[key: "///Commands/PrintWorkingDirectory", proc: PrintWorkingDirectory, doc: "Print working directory"]; Commander.Register[key: "///Commands/PWD", proc: PrintWorkingDirectory, doc: "Print working directory"]; Commander.Register[key: "///Commands/ChangeWorkingDirectory", proc: ChangeWorkingDirectory, doc: "Change working directory"]; Commander.Register[key: "///Commands/CD", proc: ChangeWorkingDirectory, doc: "Change working directory"]; Commander.Register[key: "///Commands/FlushCache", proc: FSFlushCache, doc: "FlushCache list-of-patterns -- flush FS cache"]; Commander.Register[key: "///Commands/ListBTree", proc: FSListBTree, doc: "ListBTree list-of-patterns -- list FS local directory and cache"]; Commander.Register[key: "///Commands/FSLoad", proc: FSLoad, doc: "Load list-of-patterns -- retrieve files"]; Commander.Register[key: "///Commands/Scavenge", proc: FSScavenge, doc: "Scavenge {volumeName} -- FS scavenge"]; }; <<>> <> Init[]; END.