DoIt:
PROC [dfNameRope:
ROPE, table: RedBlackTree.Table, out:
STREAM, verbose, isFile:
BOOL] = {
EachEntry: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
called for each item in the table to do the comparing.
WITH data
SELECT
FROM
entry: FileEntry => {
IF entry.status # both
THEN {
explain the status of the file
SELECT entry.status
FROM
dfOnly =>
IF verbose
THEN {
out.PutF["--*****dfOnly: %g (%g)\n", [rope[entry.fullSrcName]],
IF entry.uid.egmt.gmt = BasicTime.nullGMT THEN [rope["nullGMT"]] ELSE [time[entry.uid.egmt.gmt]] ];
IF isFile THEN out.Flush[];
};
dirOnly => {
IF verbose
THEN
out.PutF["Delete %g\n\t-- (%g)\n", [rope[entry.fullSrcName]], [time[entry.uid.egmt.gmt]] ]
ELSE out.PutF1["Delete %g\n", [rope[entry.fullSrcName]] ];
IF isFile THEN out.Flush[];
};
differentDates => {
out.PutF1["-- ## different dates for %g\n", [rope[entry.fullSrcName]] ];
IF isFile THEN out.Flush[];
};
ENDCASE;
};
fileCount[entry.status] ¬ fileCount[entry.status].SUCC;
Process.CheckForAbort[];
RETURN;
};
ENDCASE => ERROR;
};
VisitEntry:
PROC [name:
PATH, uid:
PFS.UniqueID] = {
This procedure is used to visit each file in a simple DF closure, where the imports are NOT followed, but the inclusions ARE followed.
new: FileEntry ¬ NIL;
bytes: INT ¬ 0;
suffix: PATH ¬ NIL;
ropeName: ROPE ¬ PFS.RopeFromPath[name];
fileInfoName: PATH;
status: FileEntryStatus ¬ both; -- assume things are ok
Process.CheckForAbort[];
WITH RedBlackTree.Lookup[table, ropeName]
SELECT
FROM
entry: FileEntry => IF EqualUIDs[entry.uid, uid] THEN RETURN;
ENDCASE;
[fullFName: fileInfoName, uniqueID: uid, bytes: bytes] ¬
PFS.FileInfo[name: name, wantedUniqueID: uid !
PFS.Error =>
IF ( error.code = $unknownFile )
OR ( error.code = $unknownUniqueID )
OR ( error.code = $unknownCreatedTime )
OR ( error.code = $unknownServer )
THEN {
out.PutF["\n-- *PFS.Error[%g]\n\tasking for %g(%g)\n", [rope[error.explanation]], [rope[PFS.RopeFromPath[fileInfoName]]], IF uid.egmt.gmt = BasicTime.nullGMT THEN [rope["nullGMT"]] ELSE [time[uid.egmt.gmt]] ];
IF isFile THEN out.Flush[];
status ¬ dfOnly;
needsComment ¬ TRUE;
CONTINUE;
}
ELSE REJECT];
WITH RedBlackTree.Lookup[table, ropeName]
SELECT
FROM
entry: FileEntry => {
IF EqualUIDs[entry.uid, uid] THEN RETURN;
fileCount[differentDates] ¬ fileCount[differentDates].SUCC;
IF verbose
THEN {
out.PutF["-- $Deleting %g (%g) from table; new date is %g\n", [rope[ropeName]],
[time[entry.uid.egmt.gmt]], [time[uid.egmt.gmt]] ];
IF isFile THEN out.Flush[];
needsComment ¬ TRUE;
};
[] ¬ RedBlackTree.Delete[table, ropeName];
};
ENDCASE;
new ¬
NEW[FileEntryRep ¬ [
fullSrcName: ropeName,
uid: uid,
status: status]];
RedBlackTree.Insert[table, new, ropeName];
};
VisitClosure:
PROC [dfName:
PATH, uid:
PFS.UniqueID,
visitor:
PROC [name:
PATH, uid:
PFS.UniqueID],
usingList:
REF DFUtilities.UsingList ¬
NIL] = {
ENABLE
PFS.Error =>
IF ( error.code = $unknownServer )
OR
( error.code = $unknownFile )
THEN {
out.PutF["\n-- ****PFS.Error[%g], in dfFile: %g\n", [rope[error.explanation]], [rope[PFS.RopeFromPath[dfName]]] ];
IF isFile THEN out.Flush[];
needsComment ¬ TRUE;
};
EachItem: DFUtilities.ProcessItemProc = {
WITH item
SELECT
FROM
dir:
REF DFUtilities.DirectoryItem =>
prefix ¬ IF dir.readOnly THEN NIL ELSE PFS.PathFromRope[dir.path1];
file:
REF DFUtilities.FileItem =>
IF prefix #
NIL
THEN {
name: PATH = PFSNames.ExpandName[PFS.PathFromRope[file.name], prefix];
UpdateDirList[name];
visitor[name, UIDFromGMT[file.date.gmt]];
};
incl:
REF DFUtilities.IncludeItem => {
thisUID: PFS.UniqueID = UIDFromGMT[incl.date.gmt];
path1: PATH ¬ PFS.PathFromRope[incl.path1];
CheckCount[];
visitor[path1, thisUID];
VisitClosure[path1, thisUID, visitor];
};
ENDCASE => { i: INT ¬ 0; }; -- handy for setting breakpoints - (bj)
};
prefix: PATH ¬ NIL;
in: STREAM;
in ¬
PFS.StreamOpen[
fileName: dfName,
wantedUniqueID: uid
!
PFS.Error =>
IF error.code = $unknownFile
THEN {
out.PutF["-- PFS.Error[%g] - from dfFile %g\n", [rope[error.explanation]],
[rope[PFS.RopeFromPath[dfName]]] ];
IF isFile THEN out.Flush[];
needsComment ¬ TRUE;
in ¬ NIL;
CONTINUE;
}
ELSE REJECT
];
IF in#
NIL
THEN {
DFUtilities.ParseFromStream[in, EachItem
-- global variable, hack for now! - (bj)
! UNWIND => in.Close[]];
in.Close[]
};
};
UpdateDirList:
PROC[path:
PATH] = {
thisDIR: PATH ¬ PFSNames.Directory[path];
lastsubDir: ROPE ¬ PFSNames.ComponentRope[
PFSNames.Fetch[thisDIR, PFSNames.ComponentCount[thisDIR]-1]];
IF lastsubDir.Equal["Top", FALSE] THEN RETURN;
IF lastsubDir.Equal["Documentation", FALSE] THEN RETURN;
IF lastsubDir.Equal["Commands", FALSE] THEN RETURN;
IF lastsubDir.Equal["FamousFiles", FALSE] THEN RETURN;
IF lastsubDir.Equal["Derived", FALSE] THEN RETURN;
FOR pL:
LIST
OF
PATH ¬ dirList, pL.rest
UNTIL pL=
NIL
DO
IF PFSNames.Compare[pL.first, thisDIR, FALSE] = equal THEN RETURN;
ENDLOOP;
IF verbose
THEN {
out.PutF1["\n-- &&Adding %g to dirList\n", [rope[PFS.RopeFromPath[thisDIR]]] ];
IF isFile THEN out.Flush[];
needsComment ¬ TRUE;
};
dirList ¬ CONS[thisDIR, dirList];
};
CheckCount:
PROC ~ {
IF (count ¬ count + 1) MOD 10 # 0 THEN RETURN;
IF needsComment THEN { out.PutRope["-- "]; needsComment ¬ FALSE };
IF count
MOD 100 = 0
THEN {
out.PutF1["(%g) ", [integer[count]] ];
IF isFile THEN out.Flush[];
}
ELSE out.PutChar['.]
};
The mainline of DoIt
count: INT ¬ 0;
needsComment: BOOL ¬ TRUE;
dirList: LIST OF PATH ¬ NIL;
RedBlackTree.DestroyTable[table]; -- clear the table from the last run
Phase1, build up data base.
out.PutF1["\n-- ***** Building table from DF at %g\n", [time[BasicTime.Now[]]] ];
VisitClosure[PFS.PathFromRope[dfNameRope.Concat[bangH]], PFS.nullUniqueID, VisitEntry];
Phase2, scan directory.
ExamineDirectories[dirList, table, out, isFile];
Phase3, dump differences.
out.PutF1["\n-- ***** Examining tree at %g\n", [time[BasicTime.Now[]]] ];
RedBlackTree.EnumerateIncreasing[table, EachEntry];
out.PutF1["\n-- {Done at %g}\n", [time[BasicTime.Now[]]] ];
};
ExamineDirectories:
PROC[dirList:
LIST
OF
PATH, table: RedBlackTree.Table, out:
STREAM, isFile:
BOOL] = {
NameInfoProc:
PFS.InfoProc = {
new: FileEntry;
ropeName: ROPE;
IF fileType = PFS.tDirectory THEN RETURN;
WITH RedBlackTree.Lookup[table, ropeName ¬
PFS.RopeFromPath[fullFName]]
SELECT
FROM
entry: FileEntry =>
{
IF EqualUIDs[entry.uid, uniqueID]
THEN
entry.status ¬ both ELSE entry.status ¬ differentDates;
RETURN;
};
ENDCASE;
new ¬
NEW[FileEntryRep ¬ [
fullSrcName: ropeName,
uid: uniqueID,
status: dirOnly]];
RedBlackTree.Insert[table, new, ropeName];
};
out.PutF1["\n-- ***** Enumerating directories %g\n", [time[BasicTime.Now[]]] ];
IF isFile THEN out.Flush[];
FOR pL:
LIST
OF
PATH ¬ dirList, pL.rest
UNTIL pL=
NIL
DO
toSearch: PATH ¬ PFSNames.Cat[pL.first, bangHPath];
out.PutF1["--\t(%g)\N", [rope[PFS.RopeFromPath[toSearch]]] ];
IF isFile THEN out.Flush[];
PFS.EnumerateForInfo[toSearch, NameInfoProc];
ENDLOOP;
};
ShowTable:
PROC [out:
STREAM, table: RedBlackTree.Table] = {
EachEntry: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
WITH data
SELECT
FROM
entry: FileEntry => ShowEntry[out, entry];
ENDCASE => ERROR;
};
RedBlackTree.EnumerateIncreasing[table, EachEntry];
};
ShowEntry:
PROC [out:
STREAM, entry: FileEntry] = {
IO.PutF[out, "[name: %g, date: %g, state: ",
[rope[entry.fullSrcName]], [time[entry.uid.egmt.gmt]] ];
SELECT entry.status
FROM
dfOnly => out.PutRope["dfOnly]\n"];
dirOnly => out.PutRope["dirOnly]\n"];
both => out.PutRope["both]\n"];
differentDates => out.PutRope["differentDates]\n"];
ENDCASE;
};
Compare: RedBlackTree.Compare = {
[k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]
key: ROPE ¬ NIL;
WITH k
SELECT
FROM
ent: FileEntry => key ¬ ent.fullSrcName;
rope: ROPE => key ¬ rope;
ENDCASE => ERROR;
WITH data
SELECT
FROM
ent: FileEntry => RETURN [Rope.Compare[key, ent.fullSrcName, FALSE]];
ENDCASE;
ERROR;
};
CompareDFwithServer: Commander.CommandProc ~ {
ProcessSwitches:
PROC [arg:
ROPE] ~ {
sense: BOOL ¬ TRUE;
FOR index:
INT
IN [0..Rope.Length[arg])
DO
char: CHAR ¬ Rope.Fetch[arg, index];
SELECT char
FROM
'- => LOOP;
'~ => {sense ¬ NOT sense; LOOP};
'a, 'A => { outFileName ¬ CommanderOps.NextArgument[cmd]; switches[char] ¬ sense };
'o, 'O => { outFileName ¬ CommanderOps.NextArgument[cmd]; switches[char] ¬ sense };
IN ['a..'z] => switches[char] ¬ sense;
IN ['A..'Z] => switches[char + ('a-'A)] ¬ sense;
ENDCASE;
sense ¬ TRUE;
ENDLOOP;
};
out: STREAM ¬ cmd.out;
dfName: ROPE;
outFileName: ROPE;
isFile: BOOL ¬ FALSE;
table: RedBlackTree.Table ¬ RedBlackTree.Create[getKey: GetKey, compare: Compare];
switches: Switches ¬
ALL[
FALSE];
DO
arg:
ROPE ¬ CommanderOps.NextArgument[cmd ! CommanderOps.Failed => { msg ¬ errorMsg;
GO
TO failed } ];
When parsing the command line, be prepared for failure. The error is reported to the user
ch: CHAR;
Process.CheckForAbort[];
IF arg = NIL THEN EXIT;
ch ¬ Rope.Fetch[arg, 0];
SELECT
TRUE
FROM
( ch = '- ) AND ( arg.Length[] = 2 ) => ProcessSwitches[arg]; -- switch
( ch = '{ ) => LOOP; -- ignore
( ch = '} ) => LOOP; -- ignore
( ch = '$ ) => LOOP; -- ignore
ENDCASE => dfName ¬ arg -- translations or other things
ENDLOOP;
IF outFileName #
NIL
THEN {
outPath: PFS.PATH ~ PFS.PathFromRope[outFileName];
outStream: STREAM;
outStream ¬ PFS.StreamOpen[outPath, IF switches['a] THEN $append ELSE $create ! PFS.Error => {
out.PutRope[error.explanation]; CONTINUE} ];
IF (isFile ¬ ( outStream #
NIL ))
THEN {
out.PutF1["Messages will be written on %g\n", [rope[outFileName]] ];
out ¬ outStream;
};
};
DoIt[dfName, table, out, switches['v] , isFile
!
PFS.Error => {
out.PutF["-- PFS.Error[%g] - quitting.\n\t\t(at %g)\n\n", [rope[error.explanation]], [time[BasicTime.Now[]]] ];
CONTINUE;
};
];
IF outFileName # NIL THEN out.Close[];
EXITS
failed => {result ¬ $Failure};
};