TrickleChargeToTarImpl.mesa
Copyright Ó 1989 by Xerox Corporation. All rights reserved.
Willie-Sue, August 3, 1989 5:03:58 pm PDT
TrickleChargeToTar (switches) srcDir dstFile
moves files from remote directory to a tar file.
DIRECTORY
Ascii USING [Lower],
Basics,
BasicTime USING [GMT, Now, nullGMT],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
Convert,
DFUtilities USING [DirectoryItem, FileItem, Filter, ImportsItem, IncludeItem, ParseFromStream, ProcessItemProc, UsingList],
FileNames,
FS USING [ComponentPositions, ExpandName, EnumerateForInfo, Error, FileInfo, InfoProc, StreamOpen, tDirectory],
FSBackdoor,
IO,
List USING [CompareProc],
Process USING [CheckForAbort],
RedBlackTree USING [Compare, Create, Delete, EachNode, EnumerateIncreasing, GetKey, Insert, Lookup, Table],
RefText,
Rope,
SunNFSRemoteFile,
Tar,
TarFileFormat,
TarPrivate,
UnixRemoteFile,
UXTime USING [DOWN, FromGMT];
TrickleChargeToTarImpl: CEDAR MONITOR
IMPORTS Ascii, BasicTime, Commander, CommandTool, Convert, DFUtilities, FileNames, FS, IO, Process, RedBlackTree, Rope, RefText, SunNFSRemoteFile, TarPrivate, UnixRemoteFile, UXTime
= BEGIN
Types
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
TBLOCK: NAT ~ TarFileFormat.TBLOCK;
Switches: TYPE = PACKED ARRAY CHAR['a..'z] OF BOOL;
TCType: TYPE = {fullDirectory, oneDF, allDFs};
TCInfo: TYPE = REF TCInfoRec;
TCInfoRec: TYPE = RECORD[
pseudoServer, translation: ROPE, -- pseudoServer and its translation, for source
tarFileName: ROPE,
server: ROPE,
srcPrefixToSurpress: ROPE,  -- prefix path for source to supress
restOfSrcPrefix: ROPE,
srcPrefix: ROPE,
otherArg: ROPE, -- for oneDF and allDFs
tcType: TCType ← fullDirectory
];
FileEntry: TYPE = REF FileEntryRep;
FileEntryRep: TYPE = RECORD [
name: ROPE,
includes version # (translated - with real server) - fs format
short: ROPE,
Does not include the srcPrefix, does include version # - fs format
fullUnix: ROPE,
full unix name except for server, includes version #, unix format
relative: ROPE,
Does not include the srcPrefixToSurpress, does include version #, unix format; has "./" prepended (to put into tar file header)
caseFileName: ROPE,
relativeCaseName: ROPE,
date: GMT,
create date of the file
len: INT,
byte count of the file (useful redundancy)
state: FileEntryState
indicates the state of the file (obvious)
];
FileEntryState: TYPE = {init, doingHeader, copying, moved};
Documentation
dirDoc: ROPE = " tarFile srcDir
moves files from srcDir to tarFile
";
allDfsDoc: ROPE = "{ { pseudoServerName translation } } tarFile srcDirToBeSupressed subDir {subdirForDfs}
moves files from srcDirToBeSupressed/srcDir{/subdirForDfs} to tarFile
";
oneDfDoc: ROPE = "{ { pseudoServerName translation } } tarFile srcDirToBeSupressed subDir subDirAndDfName
moves files from srcDirToBeSupressed/srcDir/subDirAndDfName to tarFile
";
Option variables
maxRetries: NAT ← 10;
# of times to retry connectionRejected from STP
retrySeconds: NAT ← 20;
# of seconds between retry attempts
nameText: REF TEXT = NEW[TEXT[RefText.line]];
Command Procedures
DoIt: ENTRY PROC [tcInfo: TCInfo, out: STREAM, blocking: NAT ← 20, bestEfforts: BOOLTRUE] = {
ENABLE UNWIND => NULL;
enumerateForDfFiles: BOOL ← tcInfo.tcType = allDFs;
errorsEncountered: BOOLFALSE;
filter: DFUtilities.Filter ← [FALSE, all, all, defining];
table: RedBlackTree.Table ← RedBlackTree.Create[getKey: GetKey, compare: Compare];
tarFile: STREAM;
unixH, tarUnixH: UnixRemoteFile.UnixServerHandle;
path: UnixRemoteFile.Path;
currentFullDirPath: ROPENIL;
EachEntry: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
called for each item in the table to do the moving.
WITH data SELECT FROM
entry: FileEntry => {
IF entry.state # moved THEN CopyFileToTar[entry];
RETURN;
};
ENDCASE => ERROR;
};
CopyFileToTar: PROC [entry: FileEntry] = {
srcStream, caseStream: STREAMNIL;
retriedCount: NAT ← 0;
fileInfo: Tar.FileInfo;
mTime: BasicTime.GMT;
thisDirPath: ROPE ← GetFullDirPath[entry.fullUnix];
IF NOT thisDirPath.Equal[currentFullDirPath] THEN {
thisPath: UnixRemoteFile.Path ← [thisDirPath, NIL];
UnixRemoteFile.Close[unixH, path.dirFD];
path.dirFD ← UnixRemoteFile.Open[unixH, [currentFullDirPath ← thisDirPath, NIL]];
};
path.path ← entry.fullUnix;
entry.state ← doingHeader;
srcStream ← UnixRemoteFile.OpenReadStream[unixH, path];
[ , fileInfo.mode, , fileInfo.uid, fileInfo.gid, fileInfo.size, , , , , , , mTime, ] ← UnixRemoteFile.GetInfoFromStream[srcStream];
fileInfo.mtime ← UXTime.DOWN[UXTime.FromGMT[mTime]];
fileInfo.name ← entry.relative;
WriteHeaderToTarFile[tarFile, TarPrivate.HeaderFromFileInfo[fileInfo]];
entry.state ← copying;
StreamToStreamCopy[to: tarFile, from: srcStream];  -- pads to TBLOCK
srcStream.Close[];
now get the .~case~ file for this name, from short w/o version
path.path ← entry.caseFileName;
caseStream ← UnixRemoteFile.OpenReadStream[unixH, path !
UnixRemoteFile.Error => IF code = $noent THEN CONTINUE ELSE REJECT];
IF caseStream = NIL THEN {
fileInfo.size ← 0;  -- no case file, use info from regular file
}
ELSE {
[ , fileInfo.mode, , fileInfo.uid, fileInfo.gid, fileInfo.size, , , , , , , mTime, ] ← UnixRemoteFile.GetInfoFromStream[caseStream];
fileInfo.mtime ← UXTime.DOWN[UXTime.FromGMT[mTime]];
caseStream.Close[];
};
fileInfo.name ← entry.relativeCaseName;
WriteHeaderToTarFile[tarFile, TarPrivate.HeaderFromFileInfo[fileInfo]];
tarFile.Flush[];
entry.state ← moved;
filesMoved ← CheckCount[filesMoved, out];
};
VisitEntry: PROC [name: ROPE, date: GMT] = {
This procedure is used to visit each file in a simple DF closure, where the imports are NOT followed, but the inclusions ARE followed.
newE: FileEntry ← NIL;
bytes: INT ← 0;
short: ROPENIL;
Process.CheckForAbort[];
WITH RedBlackTree.Lookup[table, name] SELECT FROM
entry: FileEntry => IF entry.date = date THEN RETURN;
ENDCASE;
[fullFName: name, bytes: bytes, created: date] ← FS.FileInfo[name: name, wantedCreatedTime: date !
FS.Error => IF error.code = $unknownFile OR error.code = $unknownCreatedTime OR (error.code = $unknownServer AND bestEfforts) THEN {
out.PutF1["\tFS.Error[%g]\n", [rope[error.explanation]]];
name ← NIL;
CONTINUE;
}
ELSE REJECT];
short ← ReduceToShort[tcInfo, name];
IF short = NIL THEN RETURN;
WITH RedBlackTree.Lookup[table, name] SELECT FROM
entry: FileEntry => {
IF entry.date = date THEN RETURN;
[] ← RedBlackTree.Delete[table, name];
};
ENDCASE;
newE ← NEW[FileEntryRep ← [
name: name,
short: short,
date: date,
len: bytes,
state: init]];
UnixNames[tcInfo, newE];
RedBlackTree.Insert[table, newE, name];
filesSeenDuringEnumeration ← CheckCount[filesSeenDuringEnumeration, out];
bytesSeenDuringEnumeration ← bytesSeenDuringEnumeration + bytes;
};
VisitClosure: PROC [tcInfo: TCInfo, dfName: ROPE, date: GMT,
visitor: PROC [name: ROPE, date: GMT],
usingList: REF DFUtilities.UsingList ← NIL] = {
EachItem: DFUtilities.ProcessItemProc = {
WITH item SELECT FROM
dir: REF DFUtilities.DirectoryItem => prefix ← TranslateHost[tcInfo, dir.path1];
file: REF DFUtilities.FileItem => {
name: ROPE = Rope.Concat[prefix, file.name];
xName: ROPEIF prefix = NIL THEN TranslateHost[tcInfo, name] ELSE name;
IF xName = NIL THEN xName ← name;
visitor[xName, file.date.gmt];
};
incl: REF DFUtilities.IncludeItem => {
file: ROPE ← TranslateHost[tcInfo, incl.path1];
visitor[file, incl.date.gmt];
VisitClosure[tcInfo, file, incl.date.gmt, visitor];
};
imports: REF DFUtilities.ImportsItem => { -- this stuff is for me - (bj)
};
ENDCASE => { i: INT ← 0; }; -- hany for setting breakpoints - (bj)
};
prefix: ROPENIL;
in: STREAM;
xName: ROPE = TranslateHost[tcInfo, dfName];
in ← FS.StreamOpen[
fileName: xName,
wantedCreatedTime: date
! FS.Error => IF error.code = $unknownServer AND bestEfforts THEN {
out.PutF1["FS.Error[%g] - best efforts requested so continuing\n", [rope[error.explanation]]];
in ← NIL;
CONTINUE;
}
ELSE REJECT
];
IF in#NIL THEN {
filter.list ← usingList;
DFUtilities.ParseFromStream[in, EachItem, filter -- global variable, hack for now! - (bj)
! UNWIND => in.Close[]];
in.Close[]
};
};
The mainline of DoIt
filesMoved: INT ← 0;
tarBytes: INT ← 0;
filesSeenDuringEnumeration: INT ← 0;
bytesSeenDuringEnumeration: INT ← 0;
out.PutF["\n\tTar'ing files from %g%g to %g\n", [rope[tcInfo.srcPrefix]], [rope[tcInfo.otherArg]], [rope[tcInfo.tarFileName]] ];
Phase1, build up data base. Don't move any files.
out.PutF1["\n***** Building file table at %g\n", [time[BasicTime.Now[]]] ];
SELECT tcInfo.tcType FROM
oneDF => {
Trickling a df file together with the files it controls
VisitClosure[tcInfo, tcInfo.srcPrefix.Cat[tcInfo.otherArg, "!H"], BasicTime.nullGMT, VisitEntry];
};
allDFs => { 
Trickling df files in a directory together with the files controlled by those df files.
srcPrefix: ROPE = tcInfo.srcPrefix;
srcPrefixLen: INT = tcInfo.srcPrefix.Length[];
EachDfFile: FS.InfoProc = {
[fullFName: ROPE, attachedTo: ROPE, created: GMT, bytes: INT, keep: CARDINAL] RETURNS [continue: BOOL]
Process.CheckForAbort[];
continue ← TRUE;
IF NOT Rope.IsPrefix[srcPrefix, fullFName, FALSE] THEN {
out.PutF["\t***%g is not a prefix of %g - ignoring\n",
[rope[srcPrefix]], [rope[fullFName]] ];
RETURN;
};
VisitClosure[tcInfo: tcInfo, dfName: fullFName, date: BasicTime.nullGMT, visitor: VisitEntry];
};
FS.EnumerateForInfo[srcPrefix.Concat["*.df!H"], EachDfFile];
};
fullDirectory => {  -- Trickling a whole directory.
EachFile: FS.InfoProc = {
[fullFName: ROPE, attachedTo: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL, fileType: FileType] RETURNS [continue: BOOL]
continue ← TRUE;
Process.CheckForAbort[];
IF fileType=FS.tDirectory THEN RETURN;
IF Rope.IsPrefix[tcInfo.srcPrefix, fullFName, FALSE] THEN {
new: FileEntry ← NIL;
short: ROPENIL;
WITH RedBlackTree.Lookup[table, fullFName] SELECT FROM
entry: FileEntry => {
IF entry.date = created THEN RETURN;
[] ← RedBlackTree.Delete[table, fullFName];
};
ENDCASE;
short ← Rope.Substr[fullFName, tcInfo.srcPrefix.Length[]];
IF Rope.Match["!*", short] THEN RETURN;
This is likely to be the controlling file entry for an IFS
(or it could just be a bogus file to be ignored)
new ← NEW[FileEntryRep ← [
name: fullFName, short: short, date: created, len: bytes, state: init]];
RedBlackTree.Insert[table, new, fullFName];
filesSeenDuringEnumeration ← CheckCount[filesSeenDuringEnumeration, out];
bytesSeenDuringEnumeration ← bytesSeenDuringEnumeration + bytes;
};
};
FS.EnumerateForInfo[tcInfo.srcPrefix.Concat["*!h"], EachFile];
};
ENDCASE => ERROR;
out.PutF["\nEnumerated files: %g, bytes: %g\n",
[integer[filesSeenDuringEnumeration]], [integer[bytesSeenDuringEnumeration]] ];
IF filesSeenDuringEnumeration = 0 THEN RETURN; -- nothing to do
tarFile ← FS.StreamOpen[tcInfo.tarFileName, $create !
FS.Error => IF error.code = $globalCreation THEN CONTINUE ELSE REJECT];
IF tarFile = NIL THEN { -- need to open a remote file, assumed on a sun
accessMode: UnixRemoteFile.Mode ~ 774B;  -- world read access
tarPath: UnixRemoteFile.Path;
full, server, foo: ROPE;
cp: FS.ComponentPositions;
[full, cp, ] ← FS.ExpandName[tcInfo.tarFileName];
server ← full.Substr[cp.server.start, cp.server.length];
foo ← FileNames.ConvertToSlashFormat[full];
tarUnixH ← UnixRemoteFile.CreateHandle[server];
foo ← foo.Substr[ foo.SkipTo[1, "/"] ];
tarPath.path ← foo.Substr[ 0, foo.FindBackward["/"] ];
tarPath.dirFD ← UnixRemoteFile.Open[tarUnixH, tarPath];
tarPath.path ← foo;
tarFile ← UnixRemoteFile.OpenWriteStream[tarUnixH, tarPath, accessMode];
};
unixH ← UnixRemoteFile.CreateHandle[tcInfo.server];
Phase2, copy files. Don't change the entries (except for the 'moved' field).
out.PutF1["\n***** Copying files at %g\n", [time[BasicTime.Now[]]] ];
RedBlackTree.EnumerateIncreasing[table, EachEntry];
FinishAndPadTarFileToBlocking[tarFile, blocking];
tarBytes ← tarFile.GetLength[];
tarFile.Close[];  -- get it out there
out.PutF["\n Moved %g files, resulting tarFile is %g bytes\n",
[integer[filesMoved]], [integer[tarBytes]] ];
out.PutF1["\n{Done at %g}\n", [time[BasicTime.Now[]]] ];
};
CheckCount: PROC[num: INT, out: STREAM] RETURNS[res: INT] = {
IF ( res ← num + 1 ) MOD 10 = 0 THEN IF res MOD 100 = 0 THEN
out.PutF["(%g) ", [integer[res]] ] ELSE out.PutChar['.];
};
ReduceToShort: PROC [tcInfo: TCInfo, name: ROPE] RETURNS [short: ROPE] = {
all names in unix format form
IF NOT Rope.IsPrefix[tcInfo.srcPrefix, name, FALSE] THEN RETURN[NIL];
short ← name.Substr[tcInfo.srcPrefix.Length[]];
};
UnixNames: PROC [tcInfo: TCInfo, entry: FileEntry] = {
all names in unix format form
foo, foo2, foo3, full: ROPE;
cp: FS.ComponentPositions;
version: FSBackdoor.Version ← FSBackdoor.noVersion;
versionCard: CARD ← 0;
lastSlash: INT ← 0;
[full, cp, ] ← FS.ExpandName[entry.name];
versionCard ← Convert.CardFromRope[full.Substr[cp.ver.start, cp.ver.length] ! Convert.Error => CONTINUE];
IF versionCard # 0 THEN version ← [versionCard];
foo ← FileNames.ConvertToSlashFormat[FileNames.StripVersionNumber[entry.name]];
foo2 ← foo.Substr[ foo.SkipTo[1, "/"] ];
foo3 ← SunNFSRemoteFile.EncodeVersionInNFSName[foo2, version];
must map to all lower case
nameText.length ← 0;
FOR i: INT IN [0 .. foo3.Length[]) DO
[] ← RefText.InlineAppendChar[nameText, Ascii.Lower[foo3.Fetch[i]]];
ENDLOOP;
entry.fullUnix ← Rope.FromRefText[nameText];
entry.relative ← Rope.Concat["./", entry.fullUnix.Substr[tcInfo.srcPrefixToSurpress.Length[]] ];
now make up case file name, starting from foo2
lastSlash ← foo2.FindBackward["/"];
nameText.length ← 0;
FOR i: INT IN [0 .. lastSlash+1) DO
[] ← RefText.InlineAppendChar[nameText, Ascii.Lower[foo2.Fetch[i]]];
ENDLOOP;
[] ← RefText.AppendRope[nameText, SunNFSRemoteFile.caseFileNamePrefix];
FOR i: INT IN [lastSlash+1 .. foo2.Length[]) DO
[] ← RefText.InlineAppendChar[nameText, foo2.Fetch[i]];
ENDLOOP;
entry.caseFileName ← Rope.FromRefText[nameText];
entry.relativeCaseName ← Rope.Concat["./", entry.caseFileName.Substr[tcInfo.srcPrefixToSurpress.Length[]] ];
};
GetFullDirPath: PROC[ r: ROPE ] RETURNS [fullDirPath: ROPE ] = {
assumes in slash format, lower case already
pos: INT ← r.FindBackward["/"];
RETURN[ r.Substr[0, pos] ];
};
copyBuffer: REF TEXTNEW[TEXT[TBLOCK]];
nullBuffer: REF TEXTNEW[TEXT[TBLOCK]];
StreamToStreamCopy: PROC[to, from: STREAM] = {
pads out to a Tar.TBLOCK boundary
bytes: NAT;
DO
IF ( ( bytes ← from.GetBlock[copyBuffer, 0, TBLOCK] ) = 0 ) THEN EXIT;
to.PutBlock[copyBuffer];
IF bytes < TBLOCK THEN {
to.PutBlock[block: nullBuffer, startIndex: bytes];
EXIT;
};
ENDLOOP
};
WriteHeaderToTarFile: PROC[ tarFile: STREAM, h: TarFileFormat.Header ] = TRUSTED {
unsafe: Basics.UnsafeBlock ~ [LOOPHOLE[LONG[@h]], 0, TarFileFormat.headerBytes];
tarFile.UnsafePutBlock[unsafe];
tarFile.PutBlock[block: nullBuffer, startIndex: TarFileFormat.headerBytes]; -- pad to TBLOCK
};
FinishAndPadTarFileToBlocking: PROC[tarFile: STREAM, blocking: NAT] = {
nBlocks, needed: INT;
len: INT ~ tarFile.GetIndex[];
IF ( len MOD TBLOCK ) # 0 THEN {
tarFile.Close[];  -- so we can examine the remains
ERROR;
}; -- someone messed up
tarFile.PutBlock[nullBuffer]; tarFile.PutBlock[nullBuffer]; -- 2 nullBuffer's for EOF
nBlocks ← tarFile.GetIndex[] / TBLOCK;
needed ← nBlocks MOD blocking;
FOR i: INT IN [0..needed) DO tarFile.PutBlock[nullBuffer]; ENDLOOP;
};
TranslateHost: PROC [tcInfo: TCInfo, path: ROPE] RETURNS [ROPE] = {
IF Rope.Match["[*]*", path] THEN {
rPos: INT ← Rope.SkipTo[path, 1, "]"];
host: ROPE ← Rope.Substr[path, 1, rPos-1];
rest: ROPE ← Rope.Substr[path, rPos+1];
sourceHost: ROPE ← TranslateForSource[tcInfo, host];
IF sourceHost = NIL THEN RETURN[path];
IF NOT Rope.Equal[host, sourceHost, FALSE] THEN {
IF NOT Rope.IsPrefix["[", sourceHost] THEN sourceHost ← Rope.Cat["[", sourceHost, "]"];
IF (Rope.Fetch[sourceHost, Rope.Length[sourceHost]-1] = '>) AND Rope.IsPrefix["<", rest]
THEN rest ← Rope.Substr[rest, 1];
RETURN [Rope.Concat[sourceHost, rest]];
};
};
RETURN [path];
};
TranslateForSource: PROC [tcInfo: TCInfo, server: ROPE] RETURNS [ROPE] = {
Translates the server name into the server name specified for the source. If no such translation is present, returns NIL.
IF Rope.Equal[tcInfo.pseudoServer, server, FALSE] THEN RETURN [tcInfo.translation];
RETURN[NIL];
};
GetKey: RedBlackTree.GetKey = {
[data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key]
RETURN [data];
};
Compare: RedBlackTree.Compare = {
[k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]
key: ROPENIL;
WITH k SELECT FROM
ent: FileEntry => key ← ent.name;
rope: ROPE => key ← rope;
ENDCASE => ERROR;
WITH data SELECT FROM
ent: FileEntry => RETURN [Rope.Compare[key, ent.name, FALSE]];
ENDCASE;
ERROR;
};
CompareEntries: List.CompareProc = {
[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]
WITH ref1 SELECT FROM
ent1: FileEntry =>
WITH ref2 SELECT FROM
ent2: FileEntry =>
RETURN [Rope.Compare[ent1.name, ent2.name, FALSE]];
ENDCASE;
ENDCASE;
ERROR;
};
AllDfsCmdProc: Commander.CommandProc = { [result, msg] ← Common[allDFs, cmd] };
OneDfCmdProc: Commander.CommandProc = { [result, msg] ← Common[oneDF, cmd] };
DirCmdProc: Commander.CommandProc = { [result, msg] ← Common[fullDirectory, cmd] };
Common: PROC[tcType: TCType, cmd: Commander.Handle]
RETURNS[result: REF, msg: ROPE] = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
out: STREAM = cmd.out;
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd, starExpand: FALSE
! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
When parsing the command line, be prepared for failure. The error is reported to the user
this: TCInfo ← NEW[TCInfoRec ← [ tcType: tcType ] ];
i: NAT ← 1;
WHILE i < argv.argc DO
Each argument is an argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user.
arg: ROPE ← argv[i];
Process.CheckForAbort[];
It is a good idea to periodically check for a process abort request.
IF arg.Length[] = 0 THEN { i ← i + 1; LOOP };
Ignore null arguments (it is not easy to generate them, even).
SELECT arg.Fetch[0] FROM
'{ => {
next two args are a pseudoServer and its translation
this.pseudoServer ← argv[i ← i + 1];
this.translation ← argv[i ← i + 1];
arg ← argv[i ← i + 1];
IF arg.Fetch[0] # '} THEN GOTO failed;
i ← i + 1;
LOOP;
};
'$ => EXIT; -- end of current set of instructions
ENDCASE => {};
SELECT TRUE FROM
( this.tarFileName = NIL ) => this.tarFileName ← arg;
( this.server = NIL ) => this.server ← arg;
( this.srcPrefixToSurpress = NIL ) => {
IF NOT Rope.Match["*>", arg] THEN {
msg ← IO.PutFR1["Source not a directory (%g)", [rope[arg]] ];
GO TO failed;
};
this.srcPrefixToSurpress ← arg;
};
( this.restOfSrcPrefix = NIL ) => {
IF NOT Rope.Match["*>", arg] THEN {
msg ← IO.PutFR1["restOfSrcPrefix not a directory (%g)", [rope[arg]] ];
GO TO failed;
};
this.restOfSrcPrefix ← arg;
};
( this.otherArg # NIL) => {
msg ← IO.PutFR1["Extra argument (%g)", [rope[arg]] ];
GO TO failed;
};
ENDCASE => this.otherArg ← arg;
i ← i + 1;
ENDLOOP;
this.srcPrefix ← IO.PutFR["[%g]%g%g", [rope[this.server]], [rope[this.srcPrefixToSurpress]], [rope[this.restOfSrcPrefix]] ];
this.srcPrefixToSurpress ← FileNames.ConvertToSlashFormat[this.srcPrefixToSurpress];
this.restOfSrcPrefix ← FileNames.ConvertToSlashFormat[this.restOfSrcPrefix];
DoIt[this, out !
FS.Error => {
out.PutF["FS.Error[%g], stopping (at %g)\n\n", [rope[error.explanation]], [time[BasicTime.Now[]]] ];
CONTINUE;
} ];
EXITS
failed => {result ← $Failure};
};
Initialization
Commander.Register[ key: "TarChargeOneDF", proc: OneDfCmdProc,
doc: oneDfDoc, interpreted: TRUE];
Commander.Register[ key: "TarChargeAllDFs", proc: AllDfsCmdProc,
doc: allDfsDoc, interpreted: TRUE];
Commander.Register[ key: "TarChargeDirectory", proc: DirCmdProc,
doc: dirDoc, interpreted: TRUE];
Commander.Register[ key: "TrOne", proc: OneDfCmdProc,
doc: oneDfDoc, interpreted: TRUE];
Commander.Register[ key: "TrAll", proc: AllDfsCmdProc,
doc: allDfsDoc, interpreted: TRUE];
Commander.Register[ key: "TrDir", proc: DirCmdProc,
doc: dirDoc, interpreted: TRUE];
fill the nullBuffer with null's, for later use
{
FOR i: NAT IN [0..TBLOCK) DO nullBuffer[i] ← '\000; ENDLOOP;
nullBuffer.length ← TBLOCK;
};
END.