CompareDFwithDirImpl.mesa
Copyright Ó 1991, 1992, 1993 by Xerox Corporation. All rights reserved.
Willie-s, March 1, 1993 2:39 pm PST
DIRECTORY
Basics USING [Comparison],
BasicTime USING [GMT, Now],
Commander USING [CommandProc, Handle, Register],
CommanderOps USING [Failed, NextArgument],
DFUtilities USING [IncludeItem, ParseFromStream, ProcessItemProc],
IO,
PFS,
PFSNames,
Process USING [CheckForAbort],
RedBlackTree USING [Compare, Create, EachNode, EnumerateIncreasing, GetKey, Insert, Lookup, Table],
Rope;
CompareDFwithDirImpl: CEDAR MONITOR
IMPORTS BasicTime, Commander, CommanderOps, DFUtilities, IO, PFS, PFSNames, Process, RedBlackTree, Rope
= BEGIN
Types
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
PATH: TYPE = PFS.PATH;
FileEntry: TYPE = REF FileEntryRep;
FileEntryRep: TYPE = RECORD [
srcName: ROPE,
short name only
status: FileEntryStatus
indicates the status of the file
];
FileEntryStatus: TYPE = {dfOnly, dirOnly, both};
fileCount: ARRAY FileEntryStatus OF CARD ¬ ALL[0];
Switches: TYPE = PACKED ARRAY CHAR['a..'z] OF BOOL;
Option variables
bangH: ROPE ~ "!H";
startBangHPath: PATH ~ PFS.PathFromRope["*!H"];
Command Procedures
DoIt: PROC[dfNameRope: ROPE, dir: ROPE, switches: Switches, out: STREAM] = {
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 => {
out.PutF1["~~dfOnly: %g\n", [rope[entry.srcName]] ];
dfOnlyCount ¬ dfOnlyCount + 1;
};
dirOnly => {
out.PutF1[" **serverOnly: %g; ", [rope[entry.srcName]] ];
IF prev # NIL THEN out.PutF1["after %g\n", [rope[prev]] ]
ELSE
out.PutRope["at beginning\n"];
dirOnlyCount ¬ dirOnlyCount + 1;
};
ENDCASE;
};
fileCount[entry.status] ¬ fileCount[entry.status].SUCC;
prev ¬ entry.srcName;
Process.CheckForAbort[];
RETURN;
};
ENDCASE => ERROR;
};
VisitEntry: PROC [name: PATH] = {
new: FileEntry ¬ NIL;
ropeName: ROPE ¬ PFSNames.ShortNameRope[name];
Process.CheckForAbort[];
WITH RedBlackTree.Lookup[table, ropeName] SELECT FROM
entry: FileEntry => RETURN;
ENDCASE;
WITH RedBlackTree.Lookup[table, ropeName] SELECT FROM
entry: FileEntry => RETURN;
ENDCASE;
new ¬ NEW[FileEntryRep ¬ [
srcName: ropeName,
status: dfOnly]];
RedBlackTree.Insert[table, new, ropeName];
};
VisitClosure: PROC [dfName: PATH, visitor: PROC [name: PATH]] = {
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]]] ];
};
EachItem: DFUtilities.ProcessItemProc = {
WITH item SELECT FROM
incl: REF DFUtilities.IncludeItem => {
path1: PATH ¬ PFS.PathFromRope[incl.path1];
CheckCount[];
visitor[path1];
};
ENDCASE => { i: INT ¬ 0; }; -- handy for setting breakpoints - (bj)
};
prefix: PATH ¬ NIL;
in: STREAM;
in ¬ PFS.StreamOpen[
fileName: dfName
! PFS.Error => IF error.code = $unknownFile THEN {
out.PutF["-- PFS.Error[%g] - from dfFile %g\n", [rope[error.explanation]],
[rope[PFS.RopeFromPath[dfName]]] ];
in ¬ NIL;
inFileNotFound ¬ TRUE;
CONTINUE;
}
ELSE REJECT
];
IF in#NIL THEN {
out.PutF["\n***** Building table from %g at %g\n", [rope[dfNameRope]], [time[BasicTime.Now[]]] ];
DFUtilities.ParseFromStream[in, EachItem -- global variable, hack for now! - (bj)
! UNWIND => in.Close[]];
in.Close[]
};
};
CheckCount: PROC ~ {
IF (count ¬ count + 1) MOD 10 # 0 THEN RETURN;
IF count MOD 100 = 0 THEN {
out.PutF1["(%g) ", [integer[count]] ];
}
ELSE out.PutChar['.]
};
The mainline of DoIt
count: INT ¬ 0;
dfOnlyCount, dirOnlyCount: INT ¬ 0;
table: RedBlackTree.Table ¬ RedBlackTree.Create[getKey: GetKey, compare: Compare];
inFileNotFound: BOOL ¬ FALSE;
prev: ROPE ¬ NIL;
Phase1, build up data base.
VisitClosure[PFS.PathFromRope[dfNameRope.Concat[bangH]], VisitEntry];
IF inFileNotFound THEN RETURN;
Phase2, scan directory.
ExamineDirectory[dir, table, out, switches];
Phase3, dump differences.
out.PutF1["\n***** Examining tree at %g\n", [time[BasicTime.Now[]]] ];
RedBlackTree.EnumerateIncreasing[table, EachEntry];
IF dfOnlyCount # 0 THEN
out.PutF["\t==> %g files only in %g\n", [integer[dfOnlyCount]], [rope[dfNameRope]] ];
IF dirOnlyCount # 0 THEN
out.PutF["\t==> %g files only in %g\n", [integer[dirOnlyCount]], [rope[dir]] ];
out.PutF1["\n{Done at %g}\n", [time[BasicTime.Now[]]] ];
};
ExamineDirectory: PROC[dir: ROPE, table: RedBlackTree.Table, out: STREAM, switches: Switches] = {
CheckCount: PROC ~ {
IF (count ¬ count + 1) MOD 10 # 0 THEN RETURN;
IF count MOD 100 = 0 THEN {
out.PutF1["(%g) ", [integer[count]] ];
}
ELSE out.PutChar['.]
};
NameInfoProc: PFS.InfoProc = {
new: FileEntry;
ropeName: ROPE;
IF fileType = PFS.tDirectory THEN RETURN;
ropeName ¬ PFSNames.ShortNameRope[fullFName];
CheckCount[];
IF switches['p] THEN {  -- pcedar style df's
hasDash: BOOL ¬ ( Rope.Find[ropeName, "-"] # -1 );
IF hasDash AND NOT Rope.Match["*-Suite.df", ropeName, FALSE] THEN RETURN;
};
WITH RedBlackTree.Lookup[table, ropeName] SELECT FROM
entry: FileEntry => {
entry.status ¬ both;
RETURN;
};
ENDCASE;
new ¬ NEW[FileEntryRep ¬ [
srcName: ropeName,
status: dirOnly]];
RedBlackTree.Insert[table, new, ropeName];
};
count: INT ¬ 0;
pos: INT ~ Rope.Find[dir, "*"];
out.PutF["\n\n***** Enumerating directory %g at %g\n", [rope[dir]], [time[BasicTime.Now[]]] ];
IF pos > 0 THEN
PFS.EnumerateForInfo[PFS.PathFromRope[Rope.Concat[dir, bangH]], NameInfoProc]
ELSE
PFS.EnumerateForInfo[PFSNames.Cat[PFS.PathFromRope[dir], startBangHPath], NameInfoProc];
};
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.PutF1[out, "[name: %g, state: ", [rope[entry.srcName]] ];
SELECT entry.status FROM
dfOnly => out.PutRope["dfOnly]\n"];
dirOnly => out.PutRope["dirOnly]\n"];
both => out.PutRope["both]\n"];
ENDCASE;
};
GetKey: RedBlackTree.GetKey = {
[data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key]
RETURN [data];
};
Compare: RedBlackTree.Compare = {
[k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]
key: ROPE ¬ NIL;
WITH k SELECT FROM
ent: FileEntry => key ¬ ent.srcName;
rope: ROPE => key ¬ rope;
ENDCASE => ERROR;
WITH data SELECT FROM
ent: FileEntry => RETURN [Rope.Compare[key, ent.srcName, FALSE]];
ENDCASE;
ERROR;
};
CompareDFwithDir: 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};
IN ['a..'z] => switches[char] ¬ sense;
IN ['A..'Z] => switches[char + ('a-'A)] ¬ sense;
ENDCASE;
sense ¬ TRUE;
ENDLOOP;
};
out: STREAM ¬ cmd.out;
dfName: ROPE;
dirName: ROPE;
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 => IF dfName = NIL THEN dfName ¬ arg ELSE dirName ¬ arg;
ENDLOOP;
IF dfName = NIL THEN dfName ¬ "Cedar.df";
IF dirName = NIL THEN dirName ¬ "/Cedar/Top/";
DoIt[dfName, dirName, switches, out
! PFS.Error => {
out.PutF["-- PFS.Error[%g] - quitting.\n\t\t(at %g)\n\n", [rope[error.explanation]], [time[BasicTime.Now[]]] ];
CONTINUE;
};
];
EXITS
failed => {result ¬ $Failure};
};
Initialization
docRope: ROPE ~ "CompareDFwithDir {dfName {-p} <default Cedar.df> dirName <default /Cedar/Top/>}";
Commander.Register[ key: "CompareDFwithDir", proc: CompareDFwithDir, doc: docRope];
END.