DIRECTORY Basics USING [Comparison], BasicTime USING [GMT, Now, nullGMT], Commander USING [CommandProc, Handle, Register], CommanderOps USING [Failed, NextArgument], DFUtilities USING [DirectoryItem, FileItem, Filter, IncludeItem, ParseFromStream, ProcessItemProc, UsingList], IO, PFS, PFSNames, Process USING [CheckForAbort], RedBlackTree USING [Compare, Create, Delete, DestroyTable, EachNode, EnumerateIncreasing, GetKey, Insert, Lookup, Table], Rope; CompareDFwithServerImpl: CEDAR MONITOR IMPORTS BasicTime, Commander, CommanderOps, DFUtilities, IO, PFS, PFSNames, Process, RedBlackTree, Rope = BEGIN GMT: TYPE = BasicTime.GMT; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; PATH: TYPE = PFS.PATH; FileEntry: TYPE = REF FileEntryRep; FileEntryRep: TYPE = RECORD [ fullSrcName: ROPE, uid: PFS.UniqueID, status: FileEntryStatus ]; FileEntryStatus: TYPE = {dfOnly, dirOnly, both, differentDates}; fileCount: ARRAY FileEntryStatus OF CARD ¬ ALL[0]; Switches: TYPE = PACKED ARRAY CHAR['a..'z] OF BOOL; bangH: ROPE ~ "!H"; bangHPath: PATH ~ PFS.PathFromRope["*!H"]; DoIt: PROC [dfNameRope: ROPE, table: RedBlackTree.Table, out: STREAM, verbose, isFile: BOOL] = { EachEntry: RedBlackTree.EachNode = { WITH data SELECT FROM entry: FileEntry => { IF entry.status # both THEN { 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] = { 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['.] }; count: INT ¬ 0; needsComment: BOOL ¬ TRUE; dirList: LIST OF PATH ¬ NIL; RedBlackTree.DestroyTable[table]; -- clear the table from the last run out.PutF1["\n-- ***** Building table from DF at %g\n", [time[BasicTime.Now[]]] ]; VisitClosure[PFS.PathFromRope[dfNameRope.Concat[bangH]], PFS.nullUniqueID, VisitEntry]; ExamineDirectories[dirList, table, out, isFile]; 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; }; EqualUIDs: PROC[uid1, uid2: PFS.UniqueID] RETURNS[BOOL] = { RETURN[ ( uid1.egmt.gmt = uid2.egmt.gmt ) ]; -- all for now }; UIDFromGMT: PROC [gmt: BasicTime.GMT] RETURNS [PFS.UniqueID] ~ INLINE { RETURN [[egmt: [gmt: gmt, usecs: 0]]] }; ShowTable: PROC [out: STREAM, table: RedBlackTree.Table] = { EachEntry: RedBlackTree.EachNode = { 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; }; GetKey: RedBlackTree.GetKey = { RETURN [data]; }; Compare: RedBlackTree.Compare = { 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 } ]; 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}; }; Path: PROC[pt: PFS.PATH] RETURNS[rp: ROPE] ~ { RETURN[PFS.RopeFromPath[pt] ] }; docRope: ROPE ~ "CompareDFwithServer {-o|-a fileName} { -v } dfName"; Commander.Register[ key: "CompareDFwithServer", proc: CompareDFwithServer, doc: docRope]; END. `CompareDFwithServerImpl.mesa Copyright Σ 1991, 1992 by Xerox Corporation. All rights reserved. Willie-s, March 27, 1992 4:26 pm PST Types includes version number really just create date of the file for now indicates the status of the file Option variables Command Procedures [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE] called for each item in the table to do the comparing. explain the status of the file This procedure is used to visit each file in a simple DF closure, where the imports are NOT followed, but the inclusions ARE followed. The mainline of DoIt Phase1, build up data base. Phase2, scan directory. Phase3, dump differences. RETURN[ ( uid1.egmt.gmt = uid2.egmt.gmt ) AND ( uid1.egmt.usecs = uid2.egmt.usecs ) AND ( uid1.host.a = uid2.host.a ) AND ( uid1.host.a = uid2.host.a ) ]; [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE] [data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key] [k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison] When parsing the command line, be prepared for failure. The error is reported to the user Initialization Κ†–(cedarcode) style•NewlineDelimiter ˜codešœ™Kšœ Οeœ7™BK™$K˜—šΟk ˜ Kšœžœ˜Kšœ žœžœ˜$Kšœ žœ!˜0Kšœ žœ˜*Kšœ žœ]˜nKšžœ˜Kšžœ˜Kšœ ˜ Kšœžœ˜Kšœ žœg˜yKšœ˜—K˜šΟnœžœž˜&Kšžœ2žœžœ'˜gK˜Kšœž˜—head™Kšžœžœ žœ˜Kšžœžœžœ˜Kšžœžœžœžœ˜Kšžœžœžœžœ˜K˜Kšœ žœžœ˜#šœžœžœ˜šœ žœ˜Kšœ™—šœžœ ˜K™+—šœ˜K™ —K˜—Kšœžœ+˜@Kš œ žœžœžœžœ˜2Kš œ žœžœžœžœ žœžœ˜3—šœ™Kšœžœ˜Kšœ žœžœ˜*—™š Ÿœžœžœ"žœžœ˜`•StartOfExpansion> -- [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE]šΟb œ˜$KšΠck:™:K™7šžœžœž˜˜šžœžœ˜K™šžœž˜šœ žœ žœ˜Kšœ@žœ(žœžœ˜£Kšžœžœ ˜K˜—šœ ˜ šžœ ž˜šœZ˜ZKšžœ6˜:—Kšžœžœ ˜Kšœ˜——šœ˜KšœH˜HKšžœžœ ˜Kšœ˜—Kšžœ˜—K˜—Kšœ2žœ˜7Kšœ˜Kšžœ˜K˜—Kšžœžœ˜—K˜K˜—šŸ œžœžœžœ˜4KšœXΟsœ’œ ™†Kšœžœ˜Kšœžœ˜Kšœžœžœ˜Kšœ žœžœ˜(Kšœžœ˜Kšœ Οc˜7Kšœ˜šžœ&žœž˜5Kšœžœžœžœ˜=Kšžœ˜—K˜šœ9žœ,˜hš žœ žœžœ#žœ&žœ!žœ˜€Kš œXžœžœ"žœžœ˜ΡKšžœžœ ˜K˜Kšœžœ˜Kšžœ˜ K˜Kšžœžœ˜ ——šžœ&žœž˜5šœ˜Kšžœžœžœ˜)Kšœ6žœ˜;šžœ žœ˜Kšœƒ˜ƒKšžœžœ ˜Kšœžœ˜K˜——K˜*K˜Kšžœ˜—šœžœ˜Kšœ˜Kšœ ˜ Kšœ˜—Kšœ*˜*K˜K˜—šŸ œžœ žœžœžœžœžœžœžœ˜“š žœžœ žœ!žœžœ˜^KšœUžœ˜rKšžœžœ ˜Kšœžœ˜K˜—K˜šΠbnœ!˜)šžœžœž˜šœžœ˜&Kš œ žœžœžœžœžœ˜C—š œžœžœ žœžœ˜8Kšœžœžœ"˜FKšœ˜Kšœ)˜)K˜—šœžœ˜&Kšœ2˜2Kšœžœžœ˜+K˜ Kšœ˜Kšœ&˜&K˜—Kšžœ žœ £'˜C—K˜K˜Kšœžœžœ˜Kšœžœ˜ šœžœ ˜Kšœ˜šœ˜šœžœ žœžœ˜2KšœQžœ˜nKšžœžœ ˜Kšœžœ˜Kšœž˜ Kšžœ˜ K˜——Kšžœž˜ K˜—šžœžœžœ˜šœ)£(˜QKšœžœ˜—Kšœ ˜ Kšœ˜—K˜—šŸ œžœžœ˜#Kšœ žœ˜)Kšœ žœX˜hKšžœžœžœžœ˜.Kšžœ#žœžœžœ˜8Kšžœžœžœžœ˜3Kšžœ!žœžœžœ˜6Kšžœžœžœžœ˜2š žœžœžœžœžœžœž˜7Kšžœ%žœ ž œ˜CKšžœ˜—šžœ žœ˜Kšœ1žœ˜OKšžœžœ ˜Kšœžœ˜K˜—Kšœ žœ˜!K˜K˜—šŸ œžœ˜Kšžœžœžœžœ˜.Kšžœžœ&žœ˜Bšžœžœ žœ˜K˜&Kšžœžœ ˜K˜Kšžœ˜—K˜K˜——K™K˜Kšœžœ˜Kšœžœžœ˜Kš œ žœžœžœžœ˜Kšœ"£$˜FK˜Kšœ™K˜QKšœ žœ)žœ˜WK˜Kšœ™Kšœ0˜0K™Kšœ™K˜IKšœ3˜3K™K˜;K˜K˜—šŸœžœ žœžœžœ"žœ žœ˜išŸ œžœ ˜Kšœ˜Kšœ žœ˜Kšžœ žœ žœžœ˜)šžœ'žœžœž˜Sšœž˜šžœ ž˜&Kšœžœ˜7—Kšžœ˜Kšœ˜—Kšžœ˜—šœžœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ*˜*K˜—KšœO˜OKšžœžœ ˜š žœžœžœžœžœžœž˜7Kšœ žœ%˜3Kšœžœ˜=Kšžœžœ ˜Kšžœ*˜-Kšžœ˜—K˜K˜—š Ÿ œžœ žœ žœžœ˜;Kšžœ'£˜;Kšžœ$žœ'žœžœ!™šK˜K˜—š Ÿ œžœžœžœžœ žœ˜GKšžœ˜%Kšœ˜K˜—šŸ œžœžœ ˜<–> -- [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE]šœ$˜$Kš‘:™:šžœžœž˜Kšœ*˜*Kšžœžœ˜—K˜—Kšœ3˜3K˜K˜—šŸ œžœžœ˜3Kšžœc˜ešžœž˜Kšœ#˜#Kšœ%˜%Kšœ˜Kšœ3˜3Kšžœ˜—K˜K˜—–< -- [data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key]šŸœ˜Kš‘8™8Kšžœ˜K˜K˜—–R -- [k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]šŸœ˜!Kš‘N™NKšœžœžœ˜šžœžœž˜K˜(Kšœžœ˜Kšžœžœ˜—šžœžœž˜Kšœžœ%žœ˜EKšžœ˜—Kšžœ˜K˜K˜—šŸœ˜.šŸœžœžœ˜%Kšœžœžœ˜šžœžœžœž˜*Kšœžœ˜$šžœž˜Kšœžœ˜ Kšœžœžœ˜ K˜SK˜SKšžœ$˜&Kšžœ.˜0Kšžœ˜—Kšœžœ˜ Kšžœ˜—K˜—Kšœžœ ˜Kšœžœ˜ Kšœ žœ˜Kšœžœžœ˜K–@[getKey: RedBlackTree.GetKey, compare: RedBlackTree.Compare]˜Ršœžœžœ˜ K˜—šž˜šœžœLžœžœ ˜fKšœZ™Z—Kšœžœ˜ K˜Kšžœžœžœžœ˜K˜šžœžœž˜Kšœ žœ/£ ˜GKšœžœ£ ˜Kšœžœ£ ˜Kšœžœ£ ˜Kšžœ£˜7Kšžœ˜K˜——šžœžœžœ˜Kšœ žœžœžœ˜2Kšœ žœ˜Kš œ žœžœžœ žœ žœ,žœ˜‹šžœžœžœ˜(KšœD˜DK˜K˜—K˜K˜—˜.šœžœ ˜Kšœo˜oKšžœ˜ K˜—Kšœ˜—Kšžœžœžœ ˜&šž˜K˜—K˜K˜—KšŸœžœžœžœžœžœžœžœ˜O—–x[key: ROPE, proc: Commander.CommandProc, doc: ROPE _ NIL, clientData: REF ANY _ NIL, interpreted: BOOL _ TRUE]™K˜Kšœ žœ8˜E˜YK˜——K˜Kšžœ˜K˜—…—*>μ