FSFileCommandsImpl.mesa
L. Stewart, December 16, 1983 1:51 pm
Paul Rovner, September 28, 1983 1:01 pm
Russ Atkinson, September 29, 1983 10:28 am
Mike Schroeder, December 7, 1983 11:34 am
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, "*"];
Find the root of all names that will return from the enumeration.
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[];
Now change the commander herald, if there is one
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"];
Do a bit of checking!
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[]];
Now change the commander herald, if there is one
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];
};
There will be three forms of Copy:
Copy newFile ← oldFile
copy a single file to a new name
Copy directory ← list-of-files and patterns
copy a group of files to a new directory each with the same shortname as before
If the directory ← is omitted, files will be copied to the current working directory
Copy -c -- Copy, don't use Attach
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;
Process switches
{
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
First find out whether there is a ← anywhere. If there is, it must be the second arg.
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[]];
If we get here, then for each of the filenames and patterns, copy the file to the destination directory.
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"];
};
Initialization
END.