FileCmdsImpl:
CEDAR
PROGRAM
IMPORTS Commander, CommandTool, Convert, FileSets, FS, IO, Misp, Rope
={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 ANY ← NIL,
set: FileSet ← NIL,
ids: IdentificationScheme ← [],
touchy: BOOL ← TRUE,
createdNeeded: BOOL ← FALSE,
data: REF ANY ← NIL];
fcEnv: Misp.Environment ← NIL;
idsKey: ATOM ← $FileSetsIdentificationScheme;
Generic:
PROC [cmd: Commander.Handle]
RETURNS [result:
REF
ANY ←
NIL, msg:
ROPE ←
NIL]
--Commander.CommandProc-- = {
AddMsg:
PROC [more:
ROPE] =
{IF msg = NIL THEN msg ← more ELSE msg ← msg.Cat["; ", more]};
command: Command ← NARROW[cmd.procData.clientData];
cls: STREAM ← IO.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]];
};
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: ROPE ← NIL];
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 ← Replace[Replace[Replace[Replace[cpf.pattern, "<fileName>", fn.fsName], "<created>", createdR], "<primaryVolume>", fn.primaryVolume], "<backupVolume>", fn.backupVolume];
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: ROPE ← NIL,
first: BOOL ← TRUE];
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 ← Replace[Replace[Replace[Replace[caf.perFile, "<fileName>", fn.fsName], "<created>", createdR], "<primaryVolume>", fn.primaryVolume], "<backupVolume>", fn.backupVolume];
caf.commandLine ← caf.commandLine.Cat[IF caf.first THEN caf.prefix ELSE caf.infix, perThis];
caf.first ← FALSE;
};
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\" \"pattern between files\" \"command postfix\" fileSetExpression}"]];
ListFileSet: TYPE = REF ListFileSetRep;
ListFileSetRep:
TYPE =
RECORD [
long: BOOL ← FALSE,
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: BOOL ← TRUE;
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: BOOL ← TRUE;
useCreate: BOOL ← instance.ids.askFS OR instance.ids.create;
create: GMT ← IF 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[];
}.