FileCmdsImpl.Mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Spreitzer, December 6, 1984 8:44:46 pm PST
DIRECTORY Atom, Basics, BasicTime, Commander, CommandExtras, CommandTool, Convert, DFUtilities, FileCmds, FS, IO, OrderedSymbolTableRef, ProcessExtras, Rope, Tempus, TiogaOps, ViewerClasses, ViewerTools, WalnutExtras;
FileCmdsImpl: CEDAR PROGRAM
IMPORTS Atom, Commander, CommandExtras, CommandTool, Convert, DFUtilities, FileCmds, FS, IO, OrderedSymbolTableRef, ProcessExtras, Rope, Tempus, TiogaOps, WalnutExtras
EXPORTS FileCmds
={OPEN FileCmds;
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 ← longWithVersion,
touchy: BOOLTRUE,
createdNeeded: BOOLFALSE,
data: REF ANYNIL];
Error: PUBLIC ERROR [message: ROPE] = CODE;
For syntax errors and such.
Miss: PUBLIC SIGNAL [name: ROPE, created: GMT] = CODE;
Informational signal: Couldn't identify.
GetCreated: PUBLIC PROC [fn: FileNote] RETURNS [created: GMT] = {
IF (created ← fn.created) # noGMT THEN RETURN;
IF NOT fn.createTried THEN {
ok: BOOLTRUE;
ct: GMT ← noGMT;
fn.createTried ← TRUE;
[created: ct] ← FS.FileInfo[name: fn.name, remoteCheck: FALSE !FS.Error => {ok ← FALSE; CONTINUE}];
IF ok THEN fn.created ← created ← ct};
};
Size: PUBLIC PROC [fs: FileSet] RETURNS [numberOfFiles: INT] =
{numberOfFiles ← fs.elts.Size[]};
Lookup: PUBLIC PROC [fs: FileSet, fn: FileNote] RETURNS [found: FileNote] =
{found ← NARROW[fs.elts.Lookup[fn]]};
Delete: PUBLIC PROC [fs: FileSet, fn: FileNote] RETURNS [found: FileNote] =
{found ← NARROW[fs.elts.Delete[fn]]};
Generic: PROC [cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL] --Commander.CommandProc-- = {
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.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["-longWithVersion", FALSE] THEN instance.ids ← longWithVersion
ELSE IF switch.Equal["-shortAndCreate", FALSE] THEN instance.ids ← shortAndCreate
ELSE IF switch.Equal["-askFS", FALSE] THEN instance.ids ← askFS
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;
};
Error => {
result ← $Failure;
msg ← message;
GOTO GiveUp;
};
};
IF command.AfterParse # NIL THEN command.AfterParse[instance];
instance.set ← Eval[instance.setAsAny, instance.ids];
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["total %g\n", IO.rope[instance.set.summary]];
EXITS
GiveUp => RETURN;
};
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: ROPEIF instance.createdNeeded THEN Convert.RopeFromTime[fn.created] ELSE "??";
commandLine: ROPE ← Replace[Replace[Replace[Replace[cpf.pattern, "<fileName>", fn.name], "<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 {-longWithVersion|-shortAndCreate|-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: ROPEIF instance.createdNeeded THEN Convert.RopeFromTime[fn.created] ELSE "??";
perThis: ROPE ← Replace[Replace[Replace[Replace[caf.perFile, "<fileName>", fn.name], "<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 {-longWithVersion|-shortAndCreate|-askFS|-touchy|-tough}* || {\"command prefix\" \"pattern per file\" \"pattern between files\" \"command postfix\" fileSetExpression}"]];
ListFileSet: TYPE = REF ListFileSetRep;
ListFileSetRep: TYPE = RECORD [
long: BOOLFALSE];
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];
instance.cmd.out.PutRope[fn.name];
IF lfs.long THEN instance.cmd.out.PutF[" %g", IO.time[fn.created]];
instance.cmd.out.PutRope["\n"]};
listFileSet: Command ← NEW [CommandRep ← [
Start: StartListFileSet,
HandleSwitch: HandleListFileSetSwitch,
AfterParse: AfterListFileSetParse,
PerFile: PerListFileSetFile,
usage: "Usage: ListFileSet {-longWithVersion|-shortAndCreate|-askFS|-touchy|-tough|-l|-s}* || {fileSetExpression}"]];
AfterDeleteParse: PROC [instance: Instance] = {
instance.createdNeeded ← SELECT instance.ids FROM
longWithVersion, askFS => FALSE,
shortAndCreate => TRUE,
ENDCASE => ERROR};
PerDeleteFile: PROC [instance: Instance, fn: FileNote] = {
ok: BOOLTRUE;
instance.cmd.out.PutRope[fn.name];
SELECT instance.ids FROM
longWithVersion, askFS => NULL;
shortAndCreate => instance.cmd.out.PutF[" of %g", IO.time[fn.created]];
ENDCASE => ERROR;
instance.cmd.out.PutRope[" ... "];
FS.Delete[
name: fn.name,
wantedCreatedTime: SELECT instance.ids FROM
longWithVersion, askFS => noGMT,
shortAndCreate => fn.created,
ENDCASE => ERROR
!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 {-longWithVersion|-shortAndCreate|-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.name]]};
archiveFileSet: Command ← NEW [CommandRep ← [
AfterParse: AfterArchiveRequestParse,
PerFile: PerArchiveRequestFile,
usage: "Usage: ArchiveFileSet {-longWithVersion|-shortAndCreate|-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.name], IO.time[fn.created], IO.rope[fn.primaryVolume], IO.rope[fn.backupVolume]]};
retrieveFileSet: Command ← NEW [CommandRep ← [
AfterParse: AfterRetrieveRequestParse,
PerFile: PerRetrieveRequestFile,
usage: "Usage: RetrieveFileSet {-longWithVersion|-shortAndCreate|-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;
};
Eval: PUBLIC PROC [ra: REF ANY, ids: IdentificationScheme] RETURNS [fs: FileSet] = {
Add: PROC [fn: FileNote] --FileConsumer-- = {
prevFN: FileNote ← fs.Lookup[fn];
SELECT TRUE FROM
prevFN = NIL => {
fs.elts.Insert[fn];
};
prevFN # NIL => IF GetCreated[fn] # GetCreated[prevFN] THEN ERROR Error[IO.PutFR["%g found with two create dates", IO.rope[fn.name]]];
ENDCASE => ERROR;
};
Subtract: PROC [fn: FileNote] --FileConsumer-- = {
[] ← fs.Delete[fn];
};
NoteFile: PROC [fullFName, attachedTo: ROPE, created: GMT, bytes: INT, keep: CARDINAL] RETURNS [continue: BOOLEAN] --FS.InfoProc-- = {
fn: FileNote ← Map[fullFName, created, ids];
IF fn # NIL THEN Add[fn];
continue ← TRUE;
};
fs ← NEW [FileSetRep ← [
elts: OrderedSymbolTableRef.CreateTable[SELECT ids FROM longWithVersion => CompareLongs, shortAndCreate => CompareShorts, askFS => CompareLongs, ENDCASE => ERROR],
summary: NIL]];
WITH ra SELECT FROM
l: LORA => {
WITH l.first SELECT FROM
a: ATOM => SELECT a FROM
$df, $source, $derived, $sourceAndDerived, $public, $private, $publicAndPrivate, $defining, $imported, $definingAndImported, $remote, $local => {
filter: DFUtilities.Filter ← [filterC: defining];
dfw: DFWhich ← remote;
dfNames: LIST OF ROPENIL;
FOR dl: LORA ← l, dl.rest WHILE dl # NIL DO
WITH dl.first SELECT FROM
a: ATOM => SELECT a FROM
$df => NULL;
$source => filter.filterA ← source;
$derived => filter.filterA ← derived;
$sourceAndDerived => filter.filterA ← all;
$public => filter.filterB ← public;
$private => filter.filterB ← private;
$publicAndPrivate => filter.filterB ← all;
$defining => filter.filterC ← defining;
$imported => filter.filterC ← imported;
$definingAndImported => filter.filterC ← all;
$remote => dfw ← remote;
$local => dfw ← local;
ENDCASE => ERROR Error[IO.PutFR["Non-understood DF atom %g", IO.atom[a]]];
sl: LORA => {
numOnlys, i: INT ← 0;
IF sl.first # $using THEN ERROR Error[IO.PutFR["DF using list %g must begin with atom $using", IO.refAny[sl]]];
IF filter.list # NIL THEN ERROR Error[IO.PutFR["DF list %g contains more than one using list", IO.refAny[l]]];
FOR nl: LORA ← sl.rest, nl.rest WHILE nl # NIL DO
numOnlys ← numOnlys + 1;
ENDLOOP;
filter.list ← NEW [DFUtilities.UsingList[numOnlys]];
FOR nl: LORA ← sl.rest, nl.rest WHILE nl # NIL DO
filter.list[i] ← [FALSE, NARROW[nl.first]];
i ← i + 1;
ENDLOOP;
IF (filter.list.nEntries ← i) # numOnlys THEN ERROR;
};
r: ROPE => {
Consume: PROC [fullFName: ROPE] RETURNS [continue: BOOLEAN] --FS.NameProc-- = {
dfNames ← CONS[fullFName, dfNames];
continue ← TRUE;
};
pattern: ROPEIF r.Find["!"] < 0 THEN r.Cat["!H"] ELSE r;
FS.EnumerateForNames[pattern, Consume];
};
ENDCASE => ERROR Error[IO.PutFR["Non-understood DF thing %g", IO.refAny[dl.first]]];
ENDLOOP;
FOR dfNames ← dfNames, dfNames.rest WHILE dfNames # NIL DO
EnumDF[dfName: dfNames.first, to: Add, filter: filter, which: dfw, ids: ids];
ENDLOOP;
fs.summary ← Convert.RopeFromInt[fs.Size[]]};
$archive, $online => {
dateRope: ROPENARROW[l.rest.first];
date: GMT ← Tempus.Parse[dateRope].time;
EnumArchiveMsg[date, Add, SELECT a FROM $archive => archive, $online => online, ENDCASE => ERROR, ids];
fs.summary ← Convert.RopeFromInt[fs.Size[]]};
$union => {
fs.summary ← "(union";
FOR rest: LORA ← l.rest, rest.rest WHILE rest # NIL DO
sub: FileSet ← Eval[rest.first, ids];
fs.summary ← fs.summary.Cat[" ", Convert.RopeFromInt[sub.Size[]]];
EnumSet[sub, Add];
ENDLOOP;
fs.summary ← Convert.RopeFromInt[fs.Size[]].Cat[":", fs.summary, ")"];
};
$diff => {
first: BOOLTRUE;
fs.summary ← "(diff";
FOR rest: LORA ← l.rest, rest.rest WHILE rest # NIL DO
sub: FileSet ← Eval[rest.first, ids];
fs.summary ← fs.summary.Cat[" ", Convert.RopeFromInt[sub.Size[]]];
EnumSet[sub, IF first THEN Add ELSE Subtract];
first ← FALSE;
ENDLOOP;
fs.summary ← Convert.RopeFromInt[fs.Size[]].Cat[":", fs.summary, ")"];
};
$sdiff => {--symmetric difference
first: BOOLTRUE;
fs.summary ← "(sdiff";
FOR rest: LORA ← l.rest, rest.rest WHILE rest # NIL DO
SubtractIntersection: PROC [fn: FileNote] --FileConsumer-- = {
IF fs.Lookup[fn] = NIL THEN RETURN;
[] ← fs.Delete[fn];
[] ← sub.Delete[fn];
};
sub: FileSet ← Eval[rest.first, ids];
fs.summary ← fs.summary.Cat[" ", Convert.RopeFromInt[sub.Size[]]];
EnumSet[sub, SubtractIntersection];
EnumSet[sub, Add];
first ← FALSE;
ENDLOOP;
fs.summary ← Convert.RopeFromInt[fs.Size[]].Cat[":", fs.summary, ")"];
};
$intersection => {
first: BOOLTRUE;
fs.summary ← "(intersection";
FOR rest: LORA ← l.rest, rest.rest WHILE rest # NIL DO
sub: FileSet ← Eval[rest.first, ids];
Filter: PROC [fn: FileNote] = {
IF sub.Lookup[fn] = NIL THEN [] ← fs.Delete[fn]};
fs.summary ← fs.summary.Cat[" ", Convert.RopeFromInt[sub.Size[]]];
IF first THEN first ← FALSE ELSE EnumSet[fs, Filter];
ENDLOOP;
fs.summary ← Convert.RopeFromInt[fs.Size[]].Cat[":", fs.summary, ")"];
};
ENDCASE => ERROR Error[IO.PutFR["Can't process %g", IO.refAny[ra]]];
ENDCASE => ERROR Error[IO.PutFR["Can't process %g", IO.refAny[ra]]];
};
r: ROPE => {
FS.EnumerateForInfo[pattern: r, proc: NoteFile];
fs.summary ← Convert.RopeFromInt[fs.Size[]]};
a: ATOM => {
FS.EnumerateForInfo[pattern: Atom.GetPName[a], proc: NoteFile];
fs.summary ← Convert.RopeFromInt[fs.Size[]]};
ENDCASE => ERROR Error[IO.PutFR["Can't process %g", IO.refAny[ra]]];
};
EnumSet: PUBLIC PROC [fs: FileSet, to: FileConsumer] = {
PerNote: PROC [ra: REF ANY] RETURNS [stop: BOOL] = {
fn: FileNote ← NARROW[ra];
ProcessExtras.CheckForAbort[];
to[fn];
stop ← FALSE};
fs.elts.EnumerateIncreasing[PerNote];
fs ← fs};
FindByDate: PROC [date: GMT] RETURNS [v: Viewer] = {
list: LIST OF Viewer ← WalnutExtras.EnumWalnutViewers[keepSeparate: TRUE].msgList;
FOR list ← list, list.rest WHILE list # NIL DO {
root: TiogaOps.Ref ← TiogaOps.ViewerDoc[list.first];
curNode: TiogaOps.Ref ← root;
FOR curNode: TiogaOps.Ref ← TiogaOps.StepForward[root], TiogaOps.StepForward[curNode] WHILE curNode # NIL DO
from: STREAMIO.RIS[TiogaOps.GetRope[curNode].Cat["\n"]];
FOR i: INT ← from.SkipWhitespace[], from.SkipWhitespace[] WHILE NOT from.EndOf[] DO
keyword: ROPE ← from.GetTokenRope[MyBreak].token;
ProcessExtras.CheckForAbort[];
IF keyword.Equal["Date:", FALSE] THEN {
restOfLine: ROPE ← from.GetLineRope[];
thisDate: GMT ← Tempus.Parse[restOfLine].time;
IF thisDate = date THEN RETURN [list.first] ELSE GOTO NotThisOne;
};
IF keyword.Fetch[keyword.Length[]-1] # ': THEN GOTO NotThisOne;
IF keyword.Equal["Archived:"] OR keyword.Equal["Archive:"] OR keyword.Equal["Retrieved:"] OR keyword.Equal["Retrieve:"] THEN GOTO NotThisOne;
[] ← from.GetLineRope[];
ENDLOOP;
ENDLOOP;
ERROR Error[IO.PutFR["No date in Walnut message viewer named %g", IO.refAny[list.first.name]]];
EXITS
NotThisOne => NULL;
} ENDLOOP;
ERROR Error[IO.PutFR["no message viewer on an archive message dated %g", IO.time[date]]];
};
Map: PROC [fileName: ROPE, created: GMT, ids: IdentificationScheme] RETURNS [fn: FileNote] = {
createTried: BOOLFALSE;
SELECT ids FROM
askFS => {
ok: BOOLTRUE;
createTried ← TRUE;
[fullFName: fileName, created: created] ← FS.FileInfo[name: fileName, wantedCreatedTime: created, remoteCheck: FALSE !FS.Error => {ok ← FALSE; CONTINUE}];
IF NOT ok THEN {
SIGNAL Miss[fileName, created];
RETURN [NIL]};
};
longWithVersion => {
cp: FS.ComponentPositions;
[fileName, cp] ← FS.ExpandName[fileName];
IF cp.ver.length # 0 THEN NULL
ELSE {
ok: BOOLTRUE;
full: ROPENIL;
createTried ← TRUE;
[fullFName: fileName, created: created] ← FS.FileInfo[name: fileName, wantedCreatedTime: created, remoteCheck: FALSE !FS.Error => {ok ← FALSE; CONTINUE}];
IF NOT ok THEN {
SIGNAL Miss[fileName, created];
RETURN [NIL]};
};
};
shortAndCreate => {
full: ROPENIL;
cp: FS.ComponentPositions;
[full, cp] ← FS.ExpandName[fileName];
fileName ← full.Substr[start: cp.base.start, len: cp.ext.start + cp.ext.length - cp.base.start];
IF created # noGMT THEN NULL
ELSE {
ok: BOOLTRUE;
createTried ← TRUE;
[created: created] ← FS.FileInfo[name: full !FS.Error => {ok ← FALSE; CONTINUE}];
IF NOT ok THEN {
SIGNAL Miss[fileName, created];
RETURN [NIL]};
};
};
ENDCASE => ERROR;
fn ← NEW [FileNoteRep ← [name: fileName, created: created, createTried: createTried]];
};
EnumArchiveMsg: PUBLIC PROC [date: GMT, to: FileConsumer, which: ArchiveWhich, ids: IdentificationScheme] = {
heading: BOOLTRUE;
v: Viewer ← FindByDate[date];
root: TiogaOps.Ref ← TiogaOps.ViewerDoc[v];
curNode: TiogaOps.Ref ← root;
FOR curNode: TiogaOps.Ref ← TiogaOps.StepForward[root], TiogaOps.StepForward[curNode] WHILE curNode # NIL DO
from: STREAMIO.RIS[TiogaOps.GetRope[curNode].Cat["\n"]];
Skip: PROC [toke: ROPE] = {
got: ROPE ← from.GetTokenRope[MyBreak].token;
IF NOT got.Equal[toke] THEN ERROR Error[IO.PutFR["Got %g instead of %g in archive message of %g", IO.refAny[got], IO.refAny[toke], IO.time[date]]];
};
InsistEOL: PROC = {
c: CHAR ← from.GetChar[];
IF c # '\n THEN ERROR Error[IO.PutFR["Got %g instead of newline in archive message of %g", IO.char[c], IO.time[date]]];
};
FOR i: INT ← from.SkipWhitespace[], from.SkipWhitespace[] WHILE NOT from.EndOf[] DO
keyword: ROPE ← from.GetTokenRope[MyBreak].token;
ProcessExtras.CheckForAbort[];
IF keyword.Equal["Archived:"] THEN {
fileName, pv, bv: ROPE;
gmt: GMT;
fn: FileNote;
heading ← FALSE;
fileName ← from.GetTokenRope[MyBreak].token;
Skip["of"];
gmt ← from.GetTime[];
Skip["on"];
pv ← from.GetTokenRope[MyBreak].token;
Skip["or"];
bv ← from.GetTokenRope[MyBreak].token;
InsistEOL[];
IF (fn ← Map[fileName: fileName, created: gmt, ids: ids]) # NIL THEN {
fn.primaryVolume ← pv;
fn.backupVolume ← bv;
to[fn];
};
} ELSE
IF keyword.Equal["Retrieved:"] THEN {
fromName, toName: ROPE;
gmt: GMT;
fn: FileNote;
heading ← FALSE;
fromName ← from.GetTokenRope[MyBreak].token;
Skip["of"];
gmt ← from.GetTime[];
Skip["as"];
toName ← from.GetTokenRope[MyBreak].token;
InsistEOL[];
IF (fn ← Map[fileName: SELECT which FROM archive => fromName, online => toName, ENDCASE => ERROR, created: gmt, ids: ids]) # NIL THEN to[fn];
} ELSE
IF keyword.Equal["Archive:"] THEN {
fileName: ROPE;
fn: FileNote;
heading ← FALSE;
fileName ← from.GetTokenRope[MyBreak].token;
InsistEOL[];
IF HasWildcards[fileName] THEN {
Consume: PROC [fullFName: ROPE] RETURNS [continue: BOOL] --FS.NameProc-- = {
IF (fn ← Map[fullFName, noGMT, ids]) # NIL THEN to[fn];
continue ← TRUE};
FS.EnumerateForNames[fileName, Consume];
}
ELSE {
IF (fn ← Map[fileName, noGMT, ids]) # NIL THEN to[fn];
};
} ELSE
IF keyword.Equal["Retrieve:"] THEN {
fileName, pv, bv: ROPE;
gmt: GMT;
fn: FileNote;
heading ← FALSE;
fileName ← from.GetTokenRope[MyBreak].token;
Skip["of"];
gmt ← from.GetTime[];
Skip["from"];
pv ← from.GetTokenRope[MyBreak].token;
Skip["or"];
bv ← from.GetTokenRope[MyBreak].token;
InsistEOL[];
IF (fn ← Map[fileName, gmt, ids]) # NIL THEN {
fn.primaryVolume ← pv;
fn.backupVolume ← bv;
to[fn];
};
} ELSE
{
IF NOT heading THEN ERROR Error[IO.PutFR["Got non-archiving keyword %g among archiving keywords in archive message of %g", IO.rope[keyword], IO.time[date]]];
IF keyword.Length[] = 0 THEN ERROR;
IF keyword.Fetch[keyword.Length[]-1] # ': THEN ERROR Error[IO.PutFR["Found non-keyword %g in archive message of %g", IO.refAny[keyword], IO.time[date]]];
[] ← from.GetLineRope[];
};
ENDLOOP;
from.Close[];
ENDLOOP;
};
HasWildcards: PROC [fileName: ROPE] RETURNS [hasem: BOOL] =
{hasem ← fileName.Find["*"] >= 0};
MyBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = {
cc ← IF char IN ['\000 .. ' ] THEN sepr ELSE other;
};
RefineTime: PROC [fileName: ROPE, date: DFUtilities.Date] RETURNS [gmt: GMT] = {
SELECT date.format FROM
explicit => RETURN [date.gmt];
omitted, greaterThan, notEqual => RETURN [GetGMT[fileName]];
ENDCASE => ERROR;
};
GetGMT: PROC [fileName: ROPE] RETURNS [created: GMT] = {
ok: BOOLTRUE;
[created: created] ← FS.FileInfo[name: fileName, remoteCheck: FALSE !FS.Error => {ok ← FALSE; CONTINUE}];
IF NOT ok THEN created ← noGMT;
};
EnumDF: PUBLIC PROC [dfName: ROPE, date: DFUtilities.Date ← [], to: FileConsumer, filter: DFUtilities.Filter, which: DFWhich, ids: IdentificationScheme] = {
dir: ROPE ← "?";
readOnly: BOOLFALSE;
PerItem: PROC [item: REF ANY] RETURNS [stop: BOOLFALSE] = {
ProcessExtras.CheckForAbort[];
WITH item SELECT FROM
di: REF DFUtilities.DirectoryItem => {dir ← di.path1; readOnly ← di.readOnly};
fi: REF DFUtilities.FileItem => {
useDir: ROPESELECT which FROM remote => dir, local => NIL, ENDCASE => ERROR;
fiName: ROPE;
fiCP: FS.ComponentPositions;
fn: FileNote;
[fiName, fiCP, ] ← FS.ExpandName[fi.name, useDir];
IF (fn ← SELECT which FROM
remote => Map[fiName, fi.date.gmt, ids],
local => Map[DFUtilities.RemoveVersionNumber[fiName], noGMT, ids],
ENDCASE => ERROR) # NIL THEN to[fn];
};
ii: REF DFUtilities.ImportsItem => {
subFilter: DFUtilities.Filter ← [
comments: filter.comments,
filterA: filter.filterA,
filterB: IF ii.form = $exports THEN $public ELSE filter.filterB,
filterC: $all,
list: IF ii.form = $list THEN ii.list ELSE filter.list
];
EnumDF[ii.path1, ii.date, to, subFilter, which, ids];
};
ii: REF DFUtilities.IncludeItem => EnumDF[ii.path1, ii.date, to, filter, which, ids];
ci: REF DFUtilities.CommentItem => NULL;
wi: REF DFUtilities.WhiteSpaceItem => NULL;
ENDCASE => ERROR;
};
file: FS.OpenFile ← FS.nullOpenFile;
from: STREAM;
file ← FS.Open[
name: dfName,
wantedCreatedTime: SELECT date.format FROM
explicit => date.gmt,
omitted, greaterThan, notEqual => BasicTime.nullGMT,
ENDCASE => ERROR];
IF file = FS.nullOpenFile THEN RETURN;
from ← FS.StreamFromOpenFile[file];
DFUtilities.ParseFromStream[in: from, proc: PerItem, filter: filter !DFUtilities.SyntaxError => {ERROR Error[IO.PutFR["Syntax error near [%g] in DF-file %g: %g", IO.int[from.GetIndex[]], IO.rope[dfName], IO.rope[reason]]]}];
from.Close[];
};
CompareLongs: PROC [r1, r2: REF ANY] RETURNS [c: Basics.Comparison] -- OrderedSymbolTableRef.CompareProc-- = {
fn1: FileNote ← NARROW[r1];
fn2: FileNote ← NARROW[r2];
c ← fn1.name.Compare[fn2.name, FALSE]};
CompareShorts: PROC [r1, r2: REF ANY] RETURNS [c: Basics.Comparison] -- OrderedSymbolTableRef.CompareProc-- = {
fn1: FileNote ← NARROW[r1];
fn2: FileNote ← NARROW[r2];
IF (c ← fn1.name.Compare[fn2.name, FALSE]) = equal THEN {
c ← SELECT BasicTime.Period[from: fn1.created, to: fn2.created] FROM
<0 => less,
=0 => equal,
>0 => greater,
ENDCASE => ERROR;
};
};
Register: PROC [key: ROPE, command: Command, doc: ROPE] = {
Commander.Register[key: key, proc: Generic, doc: doc, clientData: command];
CommandExtras.MakeUninterpreted[Commander.Lookup[CommandTool.CurrentWorkingDirectory[].Cat[key]]];
};
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"];
};
Start[];
}.