DoIt:
ENTRY PROC [tcInfo: TCInfo, out:
STREAM, blocking:
NAT ← 20, bestEfforts:
BOOL ←
TRUE] = {
ENABLE UNWIND => NULL;
enumerateForDfFiles: BOOL ← tcInfo.tcType = allDFs;
errorsEncountered: BOOL ← FALSE;
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:
ROPE ←
NIL;
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: STREAM ← NIL;
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: ROPE ← NIL;
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: ROPE ← IF 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: ROPE ← NIL;
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: ROPE ← NIL;
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];
};
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[]]] ];
};
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[]] ];
};
Common:
PROC[tcType: TCType, cmd: Commander.Handle]
RETURNS[result:
REF, msg:
ROPE] = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
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
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};
};