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: BOOLFALSE;
printKeep: BOOLFALSE;
printAttach: BOOLFALSE;
unBackedUp: BOOLFALSE;
foundPattern: BOOLFALSE;
printFullNames: BOOLFALSE;
newLine: BOOLTRUE;
level: BOOLTRUE;
notLevel: BOOLFALSE;
dirBeforeStar: Rope.ROPE;
dirBeforeStarLength: INT;
createTime: REF TEXTNEW[TEXT[30]];
directories: LIST OF Rope.ROPENIL;
any: BOOLFALSE;
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: BOOLFALSE;
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.ROPENIL;
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: BOOLTRUE;
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.STREAMNIL;
block: REF TEXTNEW[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: BOOLFALSE;
printAttached: BOOLFALSE;
printCached: BOOLTRUE;
anySwitch: BOOLFALSE;
printThis: Rope.ROPE;
createTime: REF TEXTNEW[TEXT[30]];
accept: PROC RETURNS [stop: BOOLEAN] = {
out.PutRope[printThis];
RETURN[stop: AbortRequested[cmd]];
};
print: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEANFALSE] = 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: BOOLFALSE;
destinationDirectory: Rope.ROPENIL;
leftArrowExists: BOOL;
doACopy: BOOL ← cmd.procData.clientData = $Copy;
level: BOOLTRUE;
dirBeforeStar: Rope.ROPE;
dirBeforeStarLength: INT;
retainStructure: BOOLFALSE;
HandleAFile: PROC [to, from: Rope.ROPE, wantedCreateTime: BasicTime.GMT] = {
locked: BOOLFALSE;
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: BOOLFALSE] 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
Init[];
END.