FileCmdsImpl.Mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Spreitzer, February 11, 1986 1:13:28 pm PST
Mike Spreitzer June 19, 1986 8:33:20 pm PDT
DIRECTORY Atom, Basics, BasicTime, Commander, CommandTool, Convert, FileSets, FS, IO, Misp, Rope, TextReplace, ViewerClasses, ViewerTools;
FileCmdsImpl: CEDAR PROGRAM
IMPORTS Atom, Commander, CommandTool, Convert, FileSets, FS, IO, Misp, Rope, TextReplace
={OPEN FileSets;
Command: TYPE = REF CommandRep;
CommandRep: TYPE = RECORD [
cmdData: REF ANY,
Start: PROC [instance: Instance] ← NIL,
HandleSwitch: PROC [instance: Instance, switch: ROPE] RETURNS [handled: BOOL] ← NIL,
HandleArg: PROC [instance: Instance] RETURNS [handled: BOOL] ← NIL,
AfterParse: PROC [instance: Instance],
set instance.createdNeeded.
PerFile: PROC [instance: Instance, fn: FileNote],
Finish: PROC [instance: Instance] RETURNS [result: REF ANY, msg: ROPE] ← NIL,
usage: ROPE
];
Instance: TYPE = REF InstanceRep;
InstanceRep: TYPE = RECORD [
cmd: Commander.Handle,
of: Command,
cls: STREAM,
argCount, misses, subFailures: INT ← 0,
setAsAny: REF ANYNIL,
set: FileSet ← NIL,
ids: IdentificationScheme ← [],
touchy: BOOLTRUE,
createdNeeded: BOOLFALSE,
data: REF ANYNIL];
fcEnv: Misp.Environment ← NIL;
idsKey: ATOM ← $FileSetsIdentificationScheme;
Generic: PROC [cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL] --Commander.CommandProc-- = {
AddMsg: PROC [more: ROPE] =
{IF msg = NIL THEN msg ← more ELSE msg ← msg.Cat["; ", more]};
command: Command ← NARROW[cmd.procData.clientData];
cls: STREAMIO.RIS[cmd.commandLine];
instance: Instance ← NEW [InstanceRep ← [cmd, command, cls]];
DoIt: PROC [fn: FileNote] --FileConsumer-- = {
created: GMT ← GetCreated[fn];
IF instance.createdNeeded AND created = noGMT THEN {
cmd.err.PutF["Couldn't get a create time for %g\n", IO.rope[fn.id.name]];
instance.subFailures ← instance.subFailures + 1;
}
ELSE {
command.PerFile[instance, fn];
};
};
IF command.Start # NIL THEN command.Start[instance];
FOR i: INT ← cls.SkipWhitespace[], cls.SkipWhitespace[] WHILE NOT cls.EndOf[] DO
IF cls.PeekChar = '- THEN {
switch: ROPE ← cls.GetTokenRope[MyBreak].token;
IF switch.Equal["-askFS", FALSE] THEN instance.ids.askFS ← TRUE
ELSE IF switch.Equal["-withoutServer", FALSE] THEN instance.ids.server ← FALSE
ELSE IF switch.Equal["-withServer", FALSE] THEN instance.ids.server ← TRUE
ELSE IF switch.Equal["-withoutDirectory", FALSE] THEN instance.ids.directory ← FALSE
ELSE IF switch.Equal["-withDirectory", FALSE] THEN instance.ids.directory ← TRUE
ELSE IF switch.Equal["-withoutVersion", FALSE] THEN instance.ids.version ← FALSE
ELSE IF switch.Equal["-withVersion", FALSE] THEN instance.ids.version ← TRUE
ELSE IF switch.Equal["-withoutCreate", FALSE] THEN instance.ids.create ← FALSE
ELSE IF switch.Equal["-withCreate", FALSE] THEN instance.ids.create ← TRUE
ELSE IF switch.Equal["-touchy", FALSE] THEN instance.touchy ← TRUE
ELSE IF switch.Equal["-tough", FALSE] THEN instance.touchy ← FALSE
ELSE IF command.HandleSwitch = NIL OR NOT command.HandleSwitch[instance, switch] THEN GOTO CmdSyntaxErr;
}
ELSE {
ENABLE IO.Error => IF ec = SyntaxError THEN {
result ← $Failure;
msg ← IO.PutFR["Syntax error while parsing %g'th argument", IO.int[instance.argCount + 1]];
GOTO GiveUp --because the turkey language won't let us simply RETURN from catch phrases
};
SELECT TRUE FROM
command.HandleArg = NIL => {
SELECT instance.argCount FROM
0 => instance.setAsAny ← instance.cls.GetRefAny[];
ENDCASE => GOTO CmdSyntaxErr;
};
command.HandleArg # NIL => {
IF NOT command.HandleArg[instance] THEN GOTO CmdSyntaxErr;
};
ENDCASE => ERROR;
instance.argCount ← instance.argCount + 1;
};
REPEAT
CmdSyntaxErr => RETURN [$Failure, command.usage];
ENDLOOP;
IF instance.setAsAny = NIL THEN RETURN [$Failure, command.usage];
now we're done with parse, eval & execute
{ENABLE {
Miss => {
cmd.err.PutF["Failed to identify %g", IO.rope[name]];
IF created # noGMT THEN cmd.err.PutF[" of %g", IO.time[created]];
cmd.err.PutRope["\n"];
instance.misses ← instance.misses + 1;
RESUME;
};
Warning => {
cmd.err.PutRope[message];
cmd.err.PutRope["\n"];
RESUME;
};
Error => {
result ← $Failure;
AddMsg[message];
GOTO GiveUp;
};
};
ans: REF ANY;
IF command.AfterParse # NIL THEN command.AfterParse[instance];
Misp.Bind[idsKey, NEW [IdentificationScheme ← instance.ids], fcEnv, TRUE];
ans ← Misp.Eval[instance.setAsAny, fcEnv, NIL];
WITH ans SELECT FROM
fs: FileSet => instance.set ← fs;
ENDCASE => RETURN [$Failure, IO.PutFR["Expression yielded %g, instead of a FileSet", IO.refAny[ans]]];
IF instance.misses = 0 OR NOT instance.touchy THEN EnumSet[instance.set, DoIt];
};
IF instance.subFailures > 0 OR (instance.touchy AND instance.misses > 0) THEN RETURN [$Failure, IO.PutFR["%g files missed and %g command(s) failed", IO.int[instance.misses], IO.int[instance.subFailures]]];
IF command.Finish # NIL THEN [result, msg] ← command.Finish[instance];
cmd.out.PutF["%g files total\n", IO.rope[instance.set.summary]];
EXITS
GiveUp => RETURN;
};
MyBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = {
cc ← IF char IN ['\000 .. ' ] THEN sepr ELSE other;
};
CmdPerFile: TYPE = REF CmdPerFileRep;
CmdPerFileRep: TYPE = RECORD [
pattern: ROPENIL];
StartCmdPerFile: PROC [instance: Instance] = {
cpf: CmdPerFile ← NEW [CmdPerFileRep ← []];
instance.data ← cpf};
HandleCmdPerFileArg: PROC [instance: Instance] RETURNS [handled: BOOL] = {
cpf: CmdPerFile ← NARROW[instance.data];
SELECT instance.argCount FROM
0 => cpf.pattern ← instance.cls.GetRopeLiteral[];
1 => instance.setAsAny ← instance.cls.GetRefAny[];
ENDCASE => RETURN [FALSE];
handled ← TRUE};
AfterCmdPerFileParse: PROC [instance: Instance] = {
cpf: CmdPerFile ← NARROW[instance.data];
instance.createdNeeded ← cpf.pattern.Find["<created>"] > 0;
};
PerCmdPerFileFile: PROC [instance: Instance, fn: FileNote] = {
cpf: CmdPerFile = NARROW[instance.data];
createdR: ROPE = IF instance.createdNeeded THEN Convert.RopeFromTime[GetCreated[fn]] ELSE "??";
commandLine: ROPE = DoReplacements[cpf.pattern, fn, createdR];
thisResult: REF ANY;
thisResult ← CommandTool.DoCommand[commandLine: commandLine, parent: instance.cmd];
IF thisResult = $Failure THEN instance.subFailures ← instance.subFailures + 1;
};
cmdPerFile: Command ← NEW [CommandRep ← [
cmdData: NIL,
Start: StartCmdPerFile,
HandleArg: HandleCmdPerFileArg,
AfterParse: AfterCmdPerFileParse,
PerFile: PerCmdPerFileFile,
usage: "Usage: CmdPerFile {-withServer|-withoutServer|-withDirectory|-withoutDirectory|-withVersion|-withoutVersion|-withCreate|-withoutCreate|-askFS|-touchy|-tough}* || {\"command pattern\" fileSetExpression}"]];
CmdAllFiles: TYPE = REF CmdAllFilesRep;
CmdAllFilesRep: TYPE = RECORD [
prefix, perFile, infix, postfix, commandLine: ROPENIL,
first: BOOLTRUE];
StartCmdAllFiles: PROC [instance: Instance] = {
caf: CmdAllFiles ← NEW [CmdAllFilesRep ← []];
instance.data ← caf};
HandleCmdAllFilesArg: PROC [instance: Instance] RETURNS [handled: BOOL] = {
caf: CmdAllFiles ← NARROW[instance.data];
SELECT instance.argCount FROM
0 => caf.prefix ← instance.cls.GetRopeLiteral[];
1 => caf.perFile ← instance.cls.GetRopeLiteral[];
2 => caf.infix ← instance.cls.GetRopeLiteral[];
3 => caf.postfix ← instance.cls.GetRopeLiteral[];
4 => instance.setAsAny ← instance.cls.GetRefAny[];
ENDCASE => RETURN [FALSE];
handled ← TRUE};
AfterCmdAllFilesParse: PROC [instance: Instance] = {
caf: CmdAllFiles ← NARROW[instance.data];
instance.createdNeeded ← caf.perFile.Find["<created>"] > 0;
};
PerCmdAllFilesFile: PROC [instance: Instance, fn: FileNote] = {
caf: CmdAllFiles = NARROW[instance.data];
createdR: ROPE = IF instance.createdNeeded THEN Convert.RopeFromTime[GetCreated[fn]] ELSE "??";
perThis: ROPE = DoReplacements[caf.perFile, fn, createdR];
caf.commandLine ← caf.commandLine.Cat[IF caf.first THEN caf.prefix ELSE caf.infix, perThis];
caf.first ← FALSE;
};
DoReplacements: PROC [pattern: ROPE, fn: FileNote, createdR: ROPE] RETURNS [replaced: ROPE] = {
full: ROPE;
cp: FS.ComponentPositions;
Map: PROC [data: REF ANY, in: ROPE] RETURNS [out: ROPE] = {
out ← SELECT Atom.MakeAtom[in] FROM
$fileName => fn.fsName,
$server => GetField[cp.server],
$dir => GetField[cp.dir],
$directory => full.Substr[0, cp.base.start],
$subDirs => GetSubdir[],
$base => GetField[cp.base],
$ext => IF cp.ext.start > cp.base.start+cp.base.length THEN Rope.Concat[".", GetField[cp.ext]] ELSE "",
$short => GetFields[cp.base, cp.ext],
$ver => IF cp.ver.start > cp.ext.start+cp.ext.length THEN Rope.Concat["!", GetField[cp.ver]] ELSE "",
$created => createdR,
$primaryVolume => fn.primaryVolume,
$backupVolume => fn.backupVolume,
ENDCASE => "??";
};
GetSubdir: PROC RETURNS [subDir: ROPE] = INLINE {
subDir ← IF cp.subDirs.length # 0 THEN GetField[cp.subDirs].Concat[">"] ELSE "";
};
GetField: PROC [pos: FS.Position] RETURNS [rope: ROPE] = INLINE {
rope ← full.Substr[pos.start, pos.length];
};
GetFields: PROC [pi, pf: FS.Position] RETURNS [rope: ROPE] = INLINE {
rope ← full.Substr[pi.start, pf.start+pf.length-pi.start];
};
rm: TextReplace.RopeMap = NEW [TextReplace.RopeMapRep ← [NIL]];
[full, cp, ] ← FS.ExpandName[fn.fsName];
TRUSTED {rm.Map ← Map};
replaced ← rm.Nest[].Apply[pattern];
full ← full;
};
FinishCmdAllFiles: PROC [instance: Instance] RETURNS [result: REF ANY, msg: ROPE] = {
caf: CmdAllFiles ← NARROW[instance.data];
caf.commandLine ← caf.commandLine.Cat[caf.postfix];
result ← CommandTool.DoCommand[commandLine: caf.commandLine, parent: instance.cmd];
};
cmdAllFiles: Command ← NEW [CommandRep ← [
Start: StartCmdAllFiles,
HandleArg: HandleCmdAllFilesArg,
AfterParse: AfterCmdAllFilesParse,
PerFile: PerCmdAllFilesFile,
Finish: FinishCmdAllFiles,
usage: "Usage: CmdAllFiles {-withServer|-withoutServer|-withDirectory|-withoutDirectory|-withVersion|-withoutVersion|-withCreate|-withoutCreate|-askFS|-touchy|-tough}* || {\"command prefix\" \"pattern per file\" \"separator\" \"command postfix\" fileSetExpression}"]];
ListFileSet: TYPE = REF ListFileSetRep;
ListFileSetRep: TYPE = RECORD [
long: BOOLFALSE,
byteSum: INT ← 0];
StartListFileSet: PROC [instance: Instance] = {
lfs: ListFileSet ← NEW [ListFileSetRep ← []];
instance.data ← lfs};
HandleListFileSetSwitch: PROC [instance: Instance, switch: ROPE] RETURNS [handled: BOOL] = {
lfs: ListFileSet ← NARROW[instance.data];
IF switch.Equal["-l", FALSE] THEN lfs.long ← TRUE
ELSE IF switch.Equal["-s", FALSE] THEN lfs.long ← FALSE
ELSE RETURN [FALSE];
handled ← TRUE};
AfterListFileSetParse: PROC [instance: Instance] = {
lfs: ListFileSet ← NARROW[instance.data];
instance.createdNeeded ← lfs.long};
PerListFileSetFile: PROC [instance: Instance, fn: FileNote] = {
lfs: ListFileSet ← NARROW[instance.data];
IF lfs.long THEN {
bytes: INT ← -1;
ok: BOOLTRUE;
bytes ← FS.FileInfo[name: fn.fsName, wantedCreatedTime: GetCreated[fn] !FS.Error => {ok ← FALSE; CONTINUE}].bytes;
instance.cmd.out.PutF["%8g ", IO.int[bytes]];
IF ok THEN lfs.byteSum ← lfs.byteSum + bytes;
};
instance.cmd.out.PutRope[fn.id.name];
IF lfs.long THEN {
instance.cmd.out.PutF["\t%g", IO.time[GetCreated[fn]]];
};
instance.cmd.out.PutRope["\n"]};
FinishListFileSet: PROC [instance: Instance] RETURNS [result: REF ANY, msg: ROPE] = {
lfs: ListFileSet ← NARROW[instance.data];
result ← NIL;
msg ← NIL;
IF lfs.long THEN msg ← IO.PutFR["%g bytes total", IO.int[lfs.byteSum]];
};
listFileSet: Command ← NEW [CommandRep ← [
Start: StartListFileSet,
HandleSwitch: HandleListFileSetSwitch,
AfterParse: AfterListFileSetParse,
PerFile: PerListFileSetFile,
Finish: FinishListFileSet,
usage: "Usage: ListFileSet {-withServer|-withoutServer|-withDirectory|-withoutDirectory|-withVersion|-withoutVersion|-withCreate|-withoutCreate|-askFS|-touchy|-tough|-l|-s}* || {fileSetExpression}"]];
AfterDeleteParse: PROC [instance: Instance] = {
instance.createdNeeded ← instance.ids.askFS OR instance.ids.create};
PerDeleteFile: PROC [instance: Instance, fn: FileNote] = {
ok: BOOLTRUE;
useCreate: BOOL ← instance.ids.askFS OR instance.ids.create;
create: GMTIF useCreate THEN GetCreated[fn] ELSE noGMT;
name: ROPE ← fn.fsName;
instance.cmd.out.PutRope[name];
IF useCreate THEN instance.cmd.out.PutF[" of %g", IO.time[create]];
instance.cmd.out.PutRope[" ... "];
FS.Delete[
name: name,
wantedCreatedTime: create
!FS.Error => {
instance.cmd.out.PutF[" Error[%g, %g]\n", IO.atom[error.code], IO.rope[error.explanation]];
ok ← FALSE;
instance.subFailures ← instance.subFailures + 1;
CONTINUE;
}];
IF ok THEN instance.cmd.out.PutRope[" ok\n"];
};
deleteFileSet: Command ← NEW [CommandRep ← [
AfterParse: AfterDeleteParse,
PerFile: PerDeleteFile,
usage: "Usage: DeleteFileSet {-withServer|-withoutServer|-withDirectory|-withoutDirectory|-withVersion|-withoutVersion|-withCreate|-withoutCreate|-askFS|-touchy|-tough}* || {fileSetExpression}"]];
AfterArchiveRequestParse: PROC [instance: Instance] = {
instance.createdNeeded ← FALSE};
PerArchiveRequestFile: PROC [instance: Instance, fn: FileNote] = {
instance.cmd.out.PutF["Archive: %g\n", IO.rope[fn.fsName]]};
archiveFileSet: Command ← NEW [CommandRep ← [
AfterParse: AfterArchiveRequestParse,
PerFile: PerArchiveRequestFile,
usage: "Usage: ArchiveFileSet {-withServer|-withoutServer|-withDirectory|-withoutDirectory|-withVersion|-withoutVersion|-withCreate|-withoutCreate|-askFS|-touchy|-tough}* || {fileSetExpression}"]];
AfterRetrieveRequestParse: PROC [instance: Instance] = {
instance.createdNeeded ← TRUE};
PerRetrieveRequestFile: PROC [instance: Instance, fn: FileNote] = {
instance.cmd.out.PutF["Retrieve: %g of %g from %g or %g\n", IO.rope[fn.fsName], IO.time[GetCreated[fn]], IO.rope[fn.primaryVolume], IO.rope[fn.backupVolume]]};
retrieveFileSet: Command ← NEW [CommandRep ← [
AfterParse: AfterRetrieveRequestParse,
PerFile: PerRetrieveRequestFile,
usage: "Usage: RetrieveFileSet {-withServer|-withoutServer|-withDirectory|-withoutDirectory|-withVersion|-withoutVersion|-withCreate|-withoutCreate|-askFS|-touchy|-tough}* || {fileSetExpression}"]];
Replace: PROC [start, match, replace: ROPE] RETURNS [finish: ROPE] = {
ml: INT ← match.Length[];
rl: INT ← replace.Length[];
len: INT ← (finish ← start).Length[];
loc: INT ← finish.Index[0, match];
WHILE loc < len DO
finish ← finish.Substr[len: loc].Cat[replace, finish.Substr[start: loc+ml]];
len ← finish.Length[];
loc ← finish.Index[loc + rl, match];
ENDLOOP;
};
Register: PROC [key: ROPE, command: Command, doc: ROPE] = {
Commander.Register[key: key, proc: Generic, doc: doc, clientData: command, interpreted: FALSE];
};
Start: PROC = {
Register["CmdPerFile", cmdPerFile, "execute a command for every file in ..."];
Register["CmdAllFiles", cmdAllFiles, "execute a command that takes every file in ..."];
Register["ListFileSet", listFileSet, "list files in a set"];
Register["lfs", listFileSet, "list files in a set"];
Register["DeleteFileSet", deleteFileSet, "delete files in a set"];
Register["dfs", deleteFileSet, "delete files in a set"];
Register["ArchiveFileSet", archiveFileSet, "print request to archive files in a set"];
Register["afs", archiveFileSet, "print request to files in a set"];
Register["RetrieveFileSet", retrieveFileSet, "print request to retrieves files in a set from archive"];
Register["rfs", retrieveFileSet, "print request to retrieves files in a set from archive"];
fcEnv ← Misp.NewEnvironment[name: "FileSets", sizeGuess: 200];
Misp.DefinePrimitives[fcEnv];
};
Start[];
}.