-- VerifyDFImpl.Mesa, last edit February 4, 1983 11:36 am by Schmidt -- last edit April 12, 1983 12:33 pm by Paul Rovner -- Pilot 6.0/ Mesa 7.0 -- Usage: -- VerifyDF dffile -- where there is at least one "+" in the df file -- -- or alternatively -- VerifyDF bcdfile -- -- or alternatively -- VerifyDF dffile bcdfile -- -- if no extension is present on the first argument, -- assume it is a DF file -- an optional /f after VerifyDF and before the args causes verifydf to print -- the flattened DF file on Flattened.dffile.DF -- note that calling any procedure in VerifyDFInterface will -- cause a start trap that will register "VerifyDF" -- it is ok to call this from the Simple Exec DIRECTORY BcdOps: TYPE USING [BcdBase], Commander: TYPE USING[CommandProc, Register], CWF: TYPE USING [SetCode, SetWriteProcedure, SWF1, SWF2, SWF3, WF0, WF1, WF2, WF3, WF4, WFCR], DFSubr: TYPE USING [AllocateDFSeq, Criterion, DF, DFSeq, FlattenDF, FreeDFSeq, InsertCRs, LookupDF, NextDF, ReadInDir, SortByCap, SortByFileName, StripLongName, TooManyEntries, WriteOut], Directory: TYPE USING [Error, Handle, ignore, Lookup, UpdateDates], File: TYPE USING [Capability, nullCapability, read, Unknown], FQ: TYPE USING[FileQuery, Result], IFSFile: TYPE USING [CantOpen, Close, Error, FileHandle, GetTimes, UnableToLogin], IO: TYPE USING[Handle, PutChar, PutF, rope, UserAborted, STREAM, UserAbort, ResetUserAbort, PutRope], IOMisc: TYPE USING[AskUser], LeafSubr: TYPE USING [Open, PrintLeafAccessFailure, PrintLeafProblem, RemoteMap, StopLeaf], LongString: TYPE USING [AppendString, EquivalentString], ProcBcds: TYPE USING [InnardsObject, InstallAddressesBcd, PrintDepends, ProcDep, ProcMod, ReadInSegmentsBcd, UnstallBcd], Resource: TYPE USING[Acquire, Release, AbortProc], Rope: TYPE USING[Fetch, Find, ROPE, Text, Cat], RopeInline: TYPE USING[InlineFlatten], Space: TYPE USING [Create, Delete, Handle, LongPointer, Map, nullHandle, Unmap, virtualMemory], UnsafeSTP: TYPE USING [Error], STPSubr: TYPE USING [StopSTP], Stream: TYPE USING [Delete, Handle], String: TYPE USING [AppendString], Subr: TYPE USING [AbortMyself, Any, CheckForModify, CopyString, debugflg, EndsIn, errorflg, FileError, FreeString, GetCreateDateWithSpace, GetRemoteFilenameProp, MakeTTYProcs, NewStream, PackedTime, PrintGreeting, strcpy, SubrStop, TTYProcs, Write], Time: TYPE USING [Current], TimeStamp: TYPE USING[Null, Stamp], UECP: TYPE USING[Argv, Parse], VerifyDFInterface: TYPE USING []; VerifyDFImpl: PROGRAM IMPORTS Commander, CWF, DFSubr, Directory, File, FQ, IFSFile, IO, IOMisc, LeafSubr, LongString, ProcBcds, Resource, Rope, RopeInline, Space, STP: UnsafeSTP, STPSubr, Stream, String, Subr, Time, UECP EXPORTS VerifyDFInterface = { -- MDS usage!! stdout: IO.Handle; -- end of mds MAXFILES: CARDINAL = 1200; NPAGESINHUGEZONE: CARDINAL = 500; abortProc: Resource.AbortProc = TRUSTED{ --PROC [data: REF ANY] RETURNS[abort: BOOL]; in: IO.STREAM = NARROW[data, IO.STREAM]; abort _ in.UserAbort[]; IF abort THEN in.ResetUserAbort[]; }; Main: Commander.CommandProc = TRUSTED { --PROC [cmd: Handle] ENABLE UNWIND => [] _ Resource.Release[$VerifyDF]; h: Subr.TTYProcs; in: IO.Handle = cmd.in; out: IO.Handle = cmd.out; success: BOOL _ FALSE; otherOwner: Rope.ROPE _ NIL; [success, otherOwner] _ Resource.Acquire[resource: $VerifyDF, owner: "VerifyDF", waitForIt: FALSE]; IF NOT success THEN { out.PutRope[Rope.Cat["Waiting for ", otherOwner, " to finish..."]]; [success, ] _ Resource.Acquire[resource: $VerifyDF, owner: "VerifyDF", waitForIt: TRUE, abortProc: abortProc, abortProcData: in ]; IF NOT success THEN { out.PutRope["ABORTED\n"]; RETURN; } ELSE out.PutRope["proceeding\n"]; }; h _ Subr.MakeTTYProcs[in, out, NIL, MyConfirm]; VerifyDFUsingProcs[h, cmd.commandLine]; [] _ Resource.Release[$VerifyDF]; }; VerifyDFUsingProcs: PROC[h: Subr.TTYProcs, commandLine: Rope.ROPE] = { strdffilename: STRING _ [100]; strbcdfilename: STRING _ [100]; starttime: Subr.PackedTime; printFlattened: BOOL _ FALSE; checkForOverwrite: BOOL _ TRUE; argv: UECP.Argv _ UECP.Parse[commandLine]; dffilename, bcdfilename, firstname, tok: Rope.ROPE; parm: CARDINAL; flat: Rope.Text; Cleanup: PROC = { STPSubr.StopSTP[]; LeafSubr.StopLeaf[]; Subr.SubrStop[]; }; { ENABLE { IFSFile.Error, IFSFile.UnableToLogin => { LeafSubr.PrintLeafProblem[reason]; GOTO leave; }; IFSFile.CantOpen => { LeafSubr.PrintLeafAccessFailure[reason]; GOTO leave; }; STP.Error => { CWF.WF0["FTP Error. "L]; IF error ~= NIL THEN CWF.WF2["message: %s, code %u in Stp.Mesa\n"L, error, @code]; GOTO leave; }; Subr.AbortMyself, IO.UserAborted => { h.out.ResetUserAbort[]; CWF.WF0["VerifyDF Aborted.\n"L]; GOTO leave; }; UNWIND => Cleanup[]; }; -- set up WF stuff stdout _ h.out; [] _ CWF.SetWriteProcedure[MyPutChar]; Subr.debugflg _ Subr.errorflg _ FALSE; Subr.PrintGreeting["VerifyDF"L]; starttime _ Time.Current[]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; parm _ 1; DO IF parm >= argv.argc THEN GOTO usage; tok _ argv[parm]; IF tok.Fetch[0] = '- OR tok.Fetch[0] = '/ THEN SELECT tok.Fetch[1] FROM 'a, 'A => checkForOverwrite _ FALSE; 'f, 'F => { printFlattened _ TRUE; Subr.debugflg _ TRUE; }; ENDCASE => h.out.PutF["Unknown flag '%s'\n", IO.rope[tok]] ELSE EXIT; parm _ parm + 1; ENDLOOP; firstname _ argv[parm]; parm _ parm + 1; IF Rope.Find[s1: firstname, s2: ".bcd", case: FALSE] > 0 THEN { bcdfilename _ firstname; dffilename _ NIL; } ELSE { dffilename _ firstname; IF parm < argv.argc THEN bcdfilename _ argv[parm]; }; flat _ RopeInline.InlineFlatten[bcdfilename]; IF flat ~= NIL THEN Subr.strcpy[strbcdfilename, LOOPHOLE[flat]]; flat _ RopeInline.InlineFlatten[dffilename]; IF flat ~= NIL THEN Subr.strcpy[strdffilename, LOOPHOLE[flat]]; -- bcdfilename and dffilename may be stored in! VerifyBcds[bcdfilename: strbcdfilename, dffilename: strdffilename, h: h, checkForOverwrite: checkForOverwrite, printFlattened: printFlattened, useHugeZone: TRUE, wantRTVersionID: 0 ! DFSubr.TooManyEntries => { CWF.WF0["Error - there were too many entries in all the DF files that needed to be read in.\n"L]; CWF.WF0["Try a smaller set of DF files.\n"L]; Subr.errorflg _ TRUE; CONTINUE; } ]; IF Subr.errorflg THEN CWF.WF0["There were Error(s) in the DF file(s).\n"L]; starttime _ Time.Current[] - starttime; CWF.WF1["\nTotal elapsed time for VerifyDF %lr\n"L, @starttime]; EXITS leave => NULL; usage =>CWF.WF0["Usage: VerifyDF DFFileName BCDFileName or VerifyDF BCDFileName or VerifyDF DFFileName\n"L]; }; Cleanup[]; }; -- assumes a HugeZone already exists and is available -- if wantRTVersionID = 0 then simply checks agreement VerifyBcds: PUBLIC PROC[bcdfilename, dffilename: LONG STRING, h: Subr.TTYProcs, checkForOverwrite, printFlattened, useHugeZone: BOOL, wantRTVersionID: CARDINAL] = { dfseq, dfseqother: DFSubr.DFSeq _ NIL; goaround: BOOL; ldspace: Space.Handle _ Space.nullHandle; Cleanup: PROC = { DFSubr.FreeDFSeq[@dfseq]; DFSubr.FreeDFSeq[@dfseqother]; IF ldspace ~= Space.nullHandle THEN Space.Delete[ldspace]; ldspace _ Space.nullHandle; }; { ENABLE UNWIND => Cleanup[]; dfseq _ DFSubr.AllocateDFSeq[maxEntries: MAXFILES, zoneType: IF useHugeZone THEN huge ELSE shared]; dfseqother _ DFSubr.AllocateDFSeq[maxEntries: MAXFILES, zoneType: shared]; IF dffilename ~= NIL AND dffilename.length > 0 THEN { IF NOT Subr.Any[dffilename, '.] THEN LongString.AppendString[dffilename, ".DF"L]; DFSubr.FlattenDF[dfseq: dfseq, dffilename: dffilename, h: h, setRecorder: TRUE, checkForOverwrite: checkForOverwrite, printStatus: Subr.debugflg]; IF dfseq.size = 0 THEN GOTO leave; }; IF printFlattened THEN { sfn: STRING _ [100]; root: STRING _ [100]; Subr.strcpy[root, dffilename]; IF Subr.EndsIn[root, ".df"L] THEN root.length _ root.length - 3; CWF.SWF1[sfn, "%s.Flat"L, root]; IF Subr.CheckForModify[sfn, h] THEN { sh: Stream.Handle _ Subr.NewStream[sfn, Subr.Write]; DFSubr.WriteOut[dfseq: dfseq, topLevelFile: NIL, outputStream: sh, print: FALSE, altoCompatibility: FALSE, wrapUsingLists: FALSE]; Stream.Delete[sh]; CWF.WF1["Flattened DF file written on %s.\n"L, sfn]; }; }; -- now analyze the BCD -- the algorithm used is as follows: -- initally all .need flags are FALSE, all .eval flags are false -- for each file we discover, the corresponding .need flag is set to TRUE -- on the nxt goround, a file is analyzed if .need is TRUE and .eval is FALSE -- after analysis, the .eval flag is set to TRUE goaround _ TRUE; ldspace _ Space.Create[size: 1, parent: Space.virtualMemory]; Space.Map[ldspace]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; IF bcdfilename ~= NIL AND bcdfilename.length > 0 THEN { wantRTVersionID _ AnalBcd[bcdfilename, dfseq, dfseqother, h, ldspace, wantRTVersionID]; IF dffilename.length = 0 THEN { dfdep: DFSubr.DF _ DFSubr.NextDF[dfseqother]; -- add top-level .Bcd to needed list dfdep.shortname _ Subr.CopyString[bcdfilename, dfseqother.dfzone]; }; } ELSE { anal: BOOL _ FALSE; df: DFSubr.DF; FOR i: CARDINAL IN [0 .. dfseq.size) DO IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; df _ @dfseq[i]; IF df.topmark AND Subr.EndsIn[df.shortname, ".bcd"L]THEN { wantRTVersionID _ AnalBcd[df.shortname, dfseq, dfseqother, h, ldspace, wantRTVersionID]; anal _ TRUE; }; ENDLOOP; IF NOT anal THEN { CWF.WF1["Error - no '+' entries in %s.\n"L, dffilename]; Subr.errorflg _ TRUE; RETURN; }; }; WHILE goaround DO goaround _ FALSE; FOR i: CARDINAL IN [0.. dfseq.size) DO IF NOT dfseq[i].eval AND dfseq[i].need THEN { IF Subr.EndsIn[dfseq[i].shortname, ".bcd"L] OR NOT Subr.Any[dfseq[i].shortname, '.] THEN wantRTVersionID _ AnalBcd[dfseq[i].shortname, dfseq, dfseqother, h, ldspace, wantRTVersionID]; dfseq[i].eval _ TRUE; goaround _ TRUE; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; }; ENDLOOP; ENDLOOP; CheckReadOnlyFiles[dfseq, h, ldspace]; -- also prints unused entries CheckForCommonBlunders[dfseq]; -- now print out extra missing entries IF dfseqother.size > 0 THEN { shortname: STRING _ [100]; miss: STRING _ [100]; CWF.WF0["Missing Entries:\n"L]; [] _ DFSubr.StripLongName[dffilename, NIL, NIL, shortname]; IF shortname.length > 0 THEN CWF.SWF1[miss, "MissingEntries.%s$"L, shortname] ELSE Subr.strcpy[miss, "MissingEntries.DF$"L]; IF Subr.CheckForModify[miss, h] THEN { sh: Stream.Handle; DFSubr.SortByCap[dfseqother, 0, dfseqother.size-1]; FillInCreateDates[dfseqother, ldspace, h]; DFSubr.SortByFileName[dfseqother, 0, dfseqother.size-1, FALSE]; DFSubr.InsertCRs[dfseqother]; sh _ Subr.NewStream[miss, Subr.Write]; DFSubr.WriteOut[dfseq: dfseqother, topLevelFile: NIL, outputStream: sh, print: TRUE]; Stream.Delete[sh]; CWF.WF1["List written on %s.\n"L, miss]; }; } ELSE CWF.WF0["\nNo files were omitted from the DF files.\n"L]; EXITS leave => NULL; }; Cleanup[]; }; -- Analyze Bcd AnalBcd: PROC[startsfn: LONG STRING, dfseq, dfseqother: DFSubr.DFSeq, h: Subr.TTYProcs, ldspace: Space.Handle, wantRTVersionID: CARDINAL] RETURNS[newRTVersionID: CARDINAL] = { dftop: DFSubr.DF; innardsobject: ProcBcds.InnardsObject _ [bcdheaderspace: Space.nullHandle]; sfn: STRING _ [100]; skip: BOOL _ FALSE; -- versionstamp rule: -- versionstamp is set by procMod -- or first call on procDep, subsequent calls to procDep -- do not change it -- dftop, sfn passed from the open procMod: ProcBcds.ProcMod = { uns _ NIL; -- check RTBcd.VersionID IF rtVersionID > 0 THEN { IF wantRTVersionID = 0 THEN newRTVersionID _ rtVersionID ELSE newRTVersionID _ wantRTVersionID; IF rtVersionID ~= newRTVersionID THEN CWF.WF1["Warning - there are two different RTBcd version stamps (RTBcd.VersionID) mentioned in this DF file. One of the files involved is %s.\n"L, sfn]; }; IF dftop ~= NIL THEN { dftop.eval _ TRUE; IF dftop.versionstamp ~= TimeStamp.Null AND dftop.versionstamp ~= bcdvers THEN { skip _ TRUE; CWF.WF4["Error - the version of %s listed in the DF file is stamped %a, but %s wants it to be of %a.\n"L, dftop.shortname, @bcdvers, dftop.recorder, @dftop.versionstamp]; Subr.errorflg _ TRUE; }; dftop.versionstamp _ bcdvers; Subr.FreeString[dftop.recorder, dfseq.dfzone]; dftop.recorder _ Subr.CopyString[dftop.shortname, dfseq.dfzone]; }; IF (dftop = NIL OR NOT dftop.readonly) AND sourcefile.length > 0 THEN { -- if not readonly, ask about sourcefile dfsrc: DFSubr.DF; dfsrc _ DFSubr.LookupDF[dfseq, sourcefile]; IF dfsrc = NIL AND DFSubr.LookupDF[dfseqother, sourcefile] = NIL THEN { CWF.WF3["Error - %s is the source file for %s\n\tbut %s is not in the df file(s).\n"L, sourcefile, sfn, sourcefile]; dfsrc _ DFSubr.NextDF[dfseqother]; dfsrc.shortname _ Subr.CopyString[sourcefile, dfseqother.dfzone]; dfsrc.createtime _ sourcevers.time; Subr.errorflg _ TRUE; }; IF dfsrc.createtime > 0 AND dfsrc.createtime ~= sourcevers.time THEN { CWF.WF4["Error - %s of %lt is the source file for %s,\n\tbut the DF file lists version of %lt.\n"L, dfsrc.shortname, @sourcevers.time, sfn, @dfsrc.createtime]; Subr.errorflg _ TRUE; }; dfsrc.eval _ TRUE; }; }; -- filename will probably have ".bcd" at the end -- look at otherdepends, canignore, and defstype only -- sfn, dfseq, dfseqother, skip, and dftop passed in from outside procDep: ProcBcds.ProcDep = { dfdep: DFSubr.DF; IF skip OR filename.length = 0 THEN RETURN; IF relcode ~= otherdepends AND relcode ~= canignore AND relcode ~= defstype THEN RETURN; IF dfseq.size = 0 OR (dfdep _ DFSubr.LookupDF[dfseq, filename]) = NIL THEN { IF DFSubr.LookupDF[dfseqother, filename] = NIL THEN { CWF.WF2["Error - %s is not in the DF file(s),\n\tbut %s depends on it.\n"L, filename, sfn]; Subr.errorflg _ TRUE; dfdep _ DFSubr.NextDF[dfseqother]; dfdep.shortname _ Subr.CopyString[filename, dfseqother.dfzone]; dfdep.versionstamp _ bcdvers; }; } ELSE { IF NOT dfdep.need THEN { -- forces analysis only if not READONLY IF dftop ~= NIL THEN dfdep.need _ NOT dftop.readonly ELSE dfdep.need _ TRUE; }; -- -- check its the right version IF bcdvers = TimeStamp.Null OR LongString.EquivalentString[filename, sfn] THEN RETURN; IF dfdep.versionstamp = TimeStamp.Null THEN { dfdep.versionstamp _ bcdvers; -- enter who recorded this entry Subr.FreeString[dfdep.recorder, dfseq.dfzone]; dfdep.recorder _ Subr.CopyString[sfn, dfseq.dfzone]; }; IF bcdvers # dfdep.versionstamp AND dfdep.recorder ~= NIL THEN { IF LongString.EquivalentString[dfdep.shortname, dfdep.recorder] THEN CWF.WF4["Error - %s is stamped %a\n but %s wants it to be %a.\n"L, dfdep.shortname, @dfdep.versionstamp, sfn, @bcdvers] ELSE { CWF.WF4["Error - %s wants %s of %a\n but %s"L, sfn, dfdep.shortname, @bcdvers, dfdep.recorder]; CWF.WF1[" wants it to be %a.\n"L, @dfdep.versionstamp]; }; Subr.errorflg _ TRUE; }; }; }; { ENABLE { UNWIND => { IF innardsobject.fh ~= NIL THEN IFSFile.Close[innardsobject.fh]; innardsobject.fh _ NIL; }; File.Unknown => { CWF.WF1["ERROR File.Unknown - problem reading %s.\n"L, sfn]; GOTO err; }; }; success: BOOL; newRTVersionID _ wantRTVersionID; Subr.strcpy[sfn, startsfn]; IF NOT Subr.Any[sfn, '.] THEN String.AppendString[sfn, ".Bcd"L]; dftop _ DFSubr.LookupDF[dfseq, sfn]; IF dftop ~= NIL AND dftop.readonly THEN RETURN; -- don't analyze readonly things IF dftop ~= NIL THEN [innardsobject.cap, innardsobject.fh] _ CachedBcd[dftop, h, ldspace ! Subr.FileError => { IF error = wrongVersion THEN CWF.WF2[" File %s of %lt cannot be found.\n"L, sfn, @dftop.createtime] ELSE CWF.WF1[" File %s cannot be found.\n"L, sfn]; GOTO err }; IFSFile.UnableToLogin => IF reason = io THEN { CWF.WF1["Unable to analyze - no Leaf server on %s.\n"L, dftop.host]; GOTO err; }; ] ELSE { innardsobject.cap _ Directory.Lookup[fileName: sfn, permissions: Directory.ignore ! Directory.Error => { CWF.WF1[" File %s cannot be fourd.\n"L, sfn]; GOTO err }]; CWF.WF1["Analyzing %s."L, sfn]; }; CWF.WFCR[]; ProcBcds.ReadInSegmentsBcd[@innardsobject]; ProcBcds.InstallAddressesBcd[@innardsobject]; [success] _ ProcBcds.PrintDepends[innards: @innardsobject, procMod: procMod, procDep: procDep, print: FALSE, calltwice: FALSE, less: TRUE, bcdfilename: sfn]; IF NOT success THEN { CWF.WF1["Error - couldn't analyze %s correctly.\n"L, sfn]; Subr.errorflg _ TRUE; }; IF innardsobject.fh ~= NIL THEN { IFSFile.Close[innardsobject.fh]; innardsobject.fh _ NIL; }; ProcBcds.UnstallBcd[@innardsobject]; EXITS err => Subr.errorflg _ TRUE; }}; -- if fh is NIL, is local, otherwise remote CachedBcd: PROC[df: DFSubr.DF, h: Subr.TTYProcs, ldspace: Space.Handle] RETURNS[cap: File.Capability, fh: IFSFile.FileHandle] = { localcreatetime: LONG CARDINAL; fh _ NIL; cap _ File.nullCapability; { cap _ Directory.Lookup[fileName: df.shortname, permissions: Directory.ignore ! Directory.Error => GOTO notonlocal]; localcreatetime _ Subr.GetCreateDateWithSpace[cap, ldspace]; IF localcreatetime = df.createtime OR (df.createtime = 0 AND df.host = NIL) THEN { CWF.WF1["Analyzing %s."L, df.shortname]; cap _ Directory.UpdateDates[cap, File.read]; RETURN[cap, NIL]; }; EXITS notonlocal => NULL; }; IF LongString.EquivalentString[df.host, "Unknown"L] THEN ERROR Subr.FileError[notFound]; CWF.WF3["Analyzing [%s]<%s>%s."L, df.host, df.directory, df.shortname]; fh _ LeafOpenWithCreate[df.host, df.directory, df.shortname, df.version, df.createtime, df.criterion, h]; -- may raise Subr.FileError }; -- looks on remote server for file with createtime -- enumerates if necessary -- may raise Subr.FileError LeafOpenWithCreate: PROC[host, directory, shortname: LONG STRING, version: CARDINAL, createtime: LONG CARDINAL, criterion: DFSubr.Criterion, tty: Subr.TTYProcs] RETURNS[fh: IFSFile.FileHandle] = { sfn: STRING _ [100]; IF version > 0 AND criterion = none AND createtime = 0 THEN CWF.SWF3[sfn, "<%s>%s!%u"L, directory, shortname, @version] ELSE CWF.SWF2[sfn, "<%s>%s"L, directory, shortname]; -- this gets !H fh _ LeafSubr.Open[host, sfn, tty, oldReadOnly]; IF createtime ~= 0 AND IFSFile.GetTimes[fh].create ~= createtime THEN { fres: FQ.Result; remoteVersion: CARDINAL; targetFileName: STRING _ [125]; IFSFile.Close[fh]; [fres: fres, remoteVersion: remoteVersion] _ FQ.FileQuery[host, directory, shortname, version, createtime, FALSE, tty, targetFileName]; SELECT fres FROM foundCorrectVersion => { CWF.SWF3[sfn, "<%s>%s!%u"L, directory, shortname, @remoteVersion]; fh _ LeafSubr.Open[host, sfn, tty, oldReadOnly]; }; foundWrongVersion => ERROR Subr.FileError[wrongVersion]; notFound => ERROR Subr.FileError[notFound]; ENDCASE => ERROR; }; }; -- look at ReadOnly files -- see if their create times agree with their functional stamps CheckReadOnlyFiles: PROC[dfseq: DFSubr.DFSeq, h: Subr.TTYProcs, ldspace: Space.Handle] = { space: Space.Handle _ Space.Create[size: 1, parent: Space.virtualMemory]; fh: IFSFile.FileHandle; bcd: BcdOps.BcdBase; df: DFSubr.DF; cap: File.Capability; bcd _ Space.LongPointer[space]; CWF.WF0["(Examining ReadOnly files.)\n"L]; FOR i: CARDINAL IN [0 .. dfseq.size) DO IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; df _ @dfseq[i]; IF df.readonly AND Subr.EndsIn[df.shortname, "bcd"L] AND df.versionstamp ~= TimeStamp.Null THEN { ENABLE File.Unknown => { CWF.WF1["ERROR File.Unknown - problem reading %s.\n"L, df.shortname]; LOOP; }; df.eval _ TRUE; [cap, fh] _ CachedBcd[df, h, ldspace ! Subr.FileError => { IF error = wrongVersion THEN CWF.WF2[" File %s of %lt cannot be fourd.\n"L, df.shortname, @df.createtime] ELSE CWF.WF1[" File %s cannot be fourd.\n"L, df.shortname]; Subr.errorflg _ TRUE; LOOP; }; IFSFile.UnableToLogin => IF reason = io THEN { CWF.WF1["Unable to analyze - no Leaf server on %s.\n"L, df.host]; LOOP; }; ]; CWF.WFCR[]; IF fh = NIL THEN Space.Map[space, [cap, 1]] ELSE LeafSubr.RemoteMap[space, fh, 0]; IF bcd.version ~= df.versionstamp THEN { CWF.WF3["Error - %s of %lt has a version stamp of %a,\n"L, df.shortname, @df.createtime, @bcd.version]; CWF.WF2["\tbut %s wants one with version stamp %a.\n"L, df.recorder, @df.versionstamp]; Subr.errorflg _ TRUE; }; Space.Unmap[space]; IF fh ~= NIL THEN IFSFile.Close[fh]; }; ENDLOOP; Space.Delete[space]; }; -- fill in create dates for entries where we know the version stamp but -- don't know their create dates; involves looking on the local disk FillInCreateDates: PROC[dfseq: DFSubr.DFSeq, ldspace: Space.Handle, h: Subr.TTYProcs] = { df: DFSubr.DF; bcd: BcdOps.BcdBase; space: Space.Handle _ Space.Create[size: 1, parent: Space.virtualMemory]; host: STRING _ [30]; directory: STRING _ [100]; fullname: STRING _ [125]; short: STRING _ [100]; FillDefs: PROC = { IF df.host = NIL THEN df.host _ Subr.CopyString["Unknown"L, dfseq.dfzone]; IF df.directory = NIL THEN df.directory _ Subr.CopyString["Unknown"L, dfseq.dfzone]; }; CWF.WF0["(Filling in create dates from files on local disk... "L]; DFSubr.ReadInDir[dfseq]; FOR i: CARDINAL IN [0 .. dfseq.size) DO { IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; df _ @dfseq[i]; IF NOT df.presentonlocaldisk THEN GOTO next; IF Subr.EndsIn[df.shortname, "bcd"L] AND df.createtime = 0 AND df.versionstamp ~= TimeStamp.Null THEN { Space.Map[space, [df.cap, 1]]; bcd _ Space.LongPointer[space]; -- this is the bcd version stamp IF df.versionstamp = bcd.version THEN { df.createtime _ Subr.GetCreateDateWithSpace[df.cap, ldspace]; Subr.GetRemoteFilenameProp[df.cap, fullname]; IF fullname.length > 0 THEN { [] _ DFSubr.StripLongName[fullname, host, directory, short]; IF LongString.EquivalentString[short, df.shortname] AND host.length > 0 AND directory.length > 0 THEN { IF df.host ~= NIL THEN Subr.FreeString[df.host, dfseq.dfzone]; df.host _ Subr.CopyString[host, dfseq.dfzone]; IF df.directory ~= NIL THEN Subr.FreeString[df.directory, dfseq.dfzone]; df.directory _ Subr.CopyString[directory, dfseq.dfzone]; Space.Unmap[space]; LOOP; -- avoid FillDefs } }; }; Space.Unmap[space]; } ELSE IF df.createtime ~= 0 AND df.createtime = Subr.GetCreateDateWithSpace[df.cap, ldspace] THEN { Subr.GetRemoteFilenameProp[df.cap, fullname]; IF fullname.length > 0 THEN { [] _ DFSubr.StripLongName[fullname, host, directory, short]; IF LongString.EquivalentString[short, df.shortname] AND host.length > 0 AND directory.length > 0 THEN { IF df.host ~= NIL THEN Subr.FreeString[df.host, dfseq.dfzone]; df.host _ Subr.CopyString[host, dfseq.dfzone]; IF df.directory ~= NIL THEN Subr.FreeString[df.directory, dfseq.dfzone]; df.directory _ Subr.CopyString[directory, dfseq.dfzone]; LOOP; -- avoid FillDefs } }; }; GOTO next; EXITS next => FillDefs[]; }; ENDLOOP; Space.Delete[space]; CWF.WF0["done.)\n"L]; }; CheckForCommonBlunders: PROC[dfseq: DFSubr.DFSeq] = { df: DFSubr.DF; p: BOOL; -- check various features of the DF file p _ FALSE; -- Directory XXX FOR i: CARDINAL IN [0 .. dfseq.size) DO df _ @dfseq[i]; IF NOT df.readonly AND df.releaseDirectory = NIL THEN { IF NOT p THEN { CWF.WF2["Warning - these files are in directories that are not ReadOnly and have no ReleaseAs or CameFrom clause.\n\t%s in %s"L, df.shortname, df.recorder]; p _ TRUE; } ELSE CWF.WF2[", %s in %s"L, df.shortname, df.recorder]; }; ENDLOOP; IF p THEN CWF.WFCR[]; p _ FALSE; -- ReadOnly XXX ReleaseAs YYY -- doesn't work because Imports look like -- ReadOnly XXX ReleaseAs YYY -- FOR i: CARDINAL IN [0 .. dfseq.size) DO -- df _ @dfseq[i]; -- IF df.readonly AND df.releaseDirectory ~= NIL AND NOT df.cameFrom THEN { -- IF NOT p THEN { -- CWF.WF2["Warning - these files are in directories that are ReadOnly and also have ReleaseAs clauses.\n\t%s in %s"L, -- df.shortname, df.recorder]; -- p _ TRUE; -- } -- ELSE -- CWF.WF2[", %s in %s"L, df.shortname, df.recorder]; -- }; -- ENDLOOP; -- IF p THEN CWF.WFCR[]; p _ FALSE; -- ReleaseAs Cedar>Top>X.DF FOR i: CARDINAL IN [0 .. dfseq.size) DO df _ @dfseq[i]; IF NOT df.cameFrom AND df.releaseDirectory ~= NIL AND (Count[df.releaseDirectory, '>] > 1 OR Subr.EndsIn[df.releaseDirectory, ".df"L]) THEN { IF NOT p THEN { CWF.WF2["Warning - the ReleaseAs clauses for these files may be incorrect.\n\t%s in %s"L, df.shortname, df.recorder]; p _ TRUE; } ELSE CWF.WF2[", %s in %s"L, df.shortname, df.recorder]; }; ENDLOOP; IF p THEN CWF.WFCR[]; p _ FALSE; -- print out warnings about wrong RTBcd version stamps FOR i: CARDINAL IN [0 .. dfseq.size) DO df _ @dfseq[i]; IF NOT df.eval AND (Subr.EndsIn[df.shortname, ".Mesa"L] OR Subr.EndsIn[df.shortname, ".Bcd"L] OR Subr.EndsIn[df.shortname, ".Config"L]) AND NotDuplicated[dfseq, i] THEN { IF NOT p THEN { CWF.WF2["Warning - these files are not needed by any top-level bcd files.\n\t%s in %s"L, df.shortname, df.recorder]; p _ TRUE; } ELSE CWF.WF2[", %s in %s"L, df.shortname, df.recorder]; }; ENDLOOP; IF p THEN CWF.WFCR[]; }; NotDuplicated: PROC[dfseq: DFSubr.DFSeq, i: CARDINAL] RETURNS[BOOL] = { dfi, dfj: DFSubr.DF; dfi _ @dfseq[i]; IF dfi.createtime = 0 THEN RETURN[TRUE]; FOR j: CARDINAL IN [0 .. i) DO dfj _ @dfseq[j]; IF dfj.eval AND dfj.createtime = dfi.createtime AND LongString.EquivalentString[dfj.shortname, dfi.shortname] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; Count: PROC[s: LONG STRING, c: CHAR] RETURNS[nOcurrences: CARDINAL] = { nOcurrences _ 0; FOR i: CARDINAL IN [0 .. s.length) DO IF s[i] = c THEN nOcurrences _ nOcurrences + 1; ENDLOOP; }; CWFAProc: PROC[lp: LONG POINTER, fmt: LONG STRING, wp: PROC[CHARACTER]] = { pts: LONG POINTER TO TimeStamp.Stamp _ lp; hex: PACKED ARRAY [0 .. 12) OF [0 .. 16) _ LOOPHOLE[pts^]; FOR i: CARDINAL IN [0 .. 12) DO IF hex[i] IN [0 .. 9] THEN wp['0 + hex[i]] ELSE wp['A + (hex[i] - 10)]; ENDLOOP; }; MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR] RETURNS[CHAR] = { value: ATOM; value _ IOMisc.AskUser[msg: msg, in: in, out: out, keyList: LIST[$Yes, $No, $All, $Quit]]; -- order is important SELECT value FROM $All => RETURN['a]; $No => RETURN['n]; $Quit => RETURN['q]; $Yes => RETURN['y]; ENDCASE => ERROR; }; MyPutChar: PROC[ch: CHAR] = { stdout.PutChar[ch]; }; -- start code Commander.Register[key: "VerifyDF", proc: Main, doc: "verify consistency and completeness of a .df file"]; CWF.SetCode['a, CWFAProc]; }. Ę Q˜Jšˆ Ďcœ€œŸœĎk œ žœžœžœžœžœžœžœLžœ žœžœžœžœžœ7žœžœ0žœžœžœ žœžœBžœžœžœ+žœ žœžœžœžœžœžœVžœžœ/žœžœvžœžœ'žœžœžœžœžœžœžœ_žœžœžœžœžœžœžœžœžœžœüžœžœžœžœžœžœžœ$žœžœžœžœ žœžœ žœOžœ2žœžœœžœ œžœžœ žœžœ*žœ-œžœžœžœžœžœžœžœ<žœœžœžœ>žœžœžœžœžœžœĹžœžœžœ žœőžœŹžœžœ žœ+žœ žœHžœ_Ďnœžœ%žœžœžœ8žœžœžœžœ žœžœFžœ žœŸœžœOžœXžœUžœžœžœžœ žœžœžœ>žœ"žœ0žœ!žœžœœžœ@žœBžœžœžœ!žœžœžœžœžœžœžœžœžœ'žœ&žœžœ žœ&žœžœžœžœ.žœ,žœžœ-žœžœžœžœUžœ žœžœžœ8žœ žœžœžœ 0œŁžœ7žœbžœ>žœžœ žœžœžœ]žœ?žœ žœ žœ~mŸ œžœžœžœžœGžœžœ)žœ žœ1ŸœžœDžœžœBžœžœ<žœžœ žœžœ9žœžœžœžœžœžœžœžœ‚žœLžœžœžœžœžœ žœžœ-žœžœ"žœ žœžœjžœžœžœžœžœ>œ%œAœKœOœ2œ žœWžœžœžœžœžœžœžœ_žœžœžœ%œQžœ žœžœžœžœžœžœžœžœžœžœ)žœ žœ#žœqžœ žœžœžœžœžœIžœžœžœ žœžœžœžœžœžœžœžœžœžœžœ/žœžœ"žœžœžœžœžœžœžœžœ+œ!'œžœžœžœžœ žœEžœžœžœžœžœ0žœ-žœžœšžœ‚žœ žœžœ1žœžœ8žœ žœŸœžœ žœžœ_žœžœžœžœUžœžœžœœ"œ:œœ#œ(žœœžœžœžœžœ!žœ&žœžœžœŚžœ žœžœžœžœ&žœžœžœžœĆžœ¨žœ žœžœžœžœžœ )œžœ4žœ žœžœ+žœžœžœŚžœ žœžœ%žœžœśžœžœ1œ6œBœ0žœžœžœžœžœžœžœžœžœžœžœžœ.žœžœžœ)žœžœžœržœŁžœžœžœ žœ(œžœ žœžœžœžœžœ œœžœžœ,žœžœžœ%žœ)!œvžœžœžœžœžœ>žœžœŒžœžœužœQžœžœžœ žœžœžœ8žœžœ=žœ!žœCžœžœžœKžœ žœžœžœžœ!œžœ žœžœežœžœžœJžœžœ1žœ1žœ žœžœNžœžœxžœ0žœžœ#žœžœÍžœ žœžœžœžœ žœžœJžœžœžœžœ;žœ.žœžœ ,Ÿ œžœ žœ.žœEžœžœžœ†žœMžœ!žœžœ žœžœžœWžœžœ žœžœžœ2žœžœžœ˛œjŸœžœžœžœ žœžœžœ4žœ$žœžœ žœžœžœžœ:žœžœ-œ3žœžœ+žœ žœžœžœNžœ@žœžœžœ žœ•žœ0žœžœžœ ZŸœžœÉžœ;žœ)žœžœžœžœžœžœžœ'žœžœ%žœ#žœžœžœGžœžœIžœžœžœYžœžœKžœžœ1žœ žœ žœEžœžœžœžœžœžœ$žœ&žœ žœžœnžœnžœ#žœžœžœžœŽŸœžœOžœjžœžœžœžœŸœžœžœ žœžœ<žœžœžœGžœ[žœžœžœžœžœžœžœ'žœžœžœžœ žœ#žœžœ#žœK!œžœžœ|žœžœQžœ8žœžœžœ žœ žœžœlžœžœžœ•žœœ5žœžœžœ>žœ7žœžœNžœ7žœžœžœžœ žœžœižœžœžœxžœœžœ žœžœžœŸœžœ&žœžœ)œžœœžœžœžœžœžœžœ žœžœžœžœžœžœžœ§žœ žœžœ8žœžœžœžœžœ žœœ*œœ+œœLœœwœœ œœœ6œœ œœžœœžœžœžœžœžœžœžœžœžœ*žœ+žœžœžœžœžœ€žœ žœžœ8žœžœžœžœžœ žœ7œžœžœžœžœžœžœ žœ+žœ)žœ+žœžœžœžœžœžœžœ žœžœ8žœžœžœžœžœ Ÿ œžœžœžœžœžœžœžœžœžœžœžœžœ žœžœ žœ#žœ;žœžœžœžœžœžœŸœžœžœžœžœžœžœžœžœžœžœžœ žœ&žœŸœžœžœžœžœžœžœž œ žœžœžœžœžœ žœ žœ žœžœžœ žœžœžœ žœžœžœŸ œžœ žœžœžœ žœžœžœžœžœ`žœœžœžœ žœžœžœžœžœžœŸ œžœžœ!œkžœ˜ôÚ—…—mvzÍ