-- RStatsDFImpl.Mesa, last edit January 3, 1983 5:16 pm DIRECTORY BcdDefs: TYPE USING[FTIndex, NameRecord], BcdOps: TYPE USING[BcdBase, FTHandle, NameString, ProcessFiles], ConvertUnsafe: TYPE USING[ToRope], CS: TYPE USING[EndsIn, EquivalentRope, EquivalentRS, Flat, z], DFSubr: TYPE USING[AllocateDFSeq, DF, DFSeq, FreeDFSeq, ParseStream], FileIO: TYPE USING[Open], IFSFile: TYPE USING[Close], IO: TYPE USING [card, Close, Flush, Handle, Put, PutChar, PutF, PutFR, PutRope, rope, string], LeafSubr: TYPE USING[RemoteMap], List: TYPE USING[Length, Sort, UniqueSort], ProcBcds: TYPE USING[InnardsObject, InstallAddressesBcd, ReadInSegmentsBcd, UnstallBcd], Rope: TYPE USING [Cat, Concat, Flatten, FromChar, Length, ROPE, Text], RStatsSupport: TYPE USING[BcdDep, CompareBcdDep, CompareDFDep, CompareDFRec, CompareXRef, DFDep, DFRec, LeafOpenWithCreate, XRef], Space: TYPE USING[Create, Delete, Handle, LongPointer, nullHandle, Unmap, virtualMemory], Stream: TYPE USING[Delete, Handle], STPSubr: TYPE USING[CachedOpen, StpStateRecord], Subr: TYPE USING[AbortMyself, EndsIn, FileError, TTYProcs], TypeScript: TYPE USING[TS, UserAbort]; RStatsDFImpl: PROGRAM IMPORTS BcdOps, ConvertUnsafe, CS, DFSubr, FileIO, IFSFile, IO, LeafSubr, List, ProcBcds, Rope, RStatsSupport, Space, STPSubr, Stream, Subr, TypeScript EXPORTS RStatsSupport = { DFRec: TYPE = RStatsSupport.DFRec; DFDep: TYPE = RStatsSupport.DFDep; XRef: TYPE = RStatsSupport.XRef; BcdDep: TYPE = RStatsSupport.BcdDep; -- DF analysis procedures ProcessDFList: PUBLIC PROC[topDFName: Rope.Text, typeScript: TypeScript.TS, out: IO.Handle, tty: Subr.TTYProcs, dfrec: LIST OF REF DFRec, xrefFileName: Rope.ROPE] RETURNS[nDFFiles: INT] = { file: IO.Handle; dfxref: LIST OF REF XRef; dfrec ← LOOPHOLE[List.UniqueSort[LOOPHOLE[dfrec], RStatsSupport.CompareDFRec]]; FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO IF TypeScript.UserAbort[typeScript] THEN SIGNAL Subr.AbortMyself; dfxref ← AnalyzeDF[l.first, out, tty, dfxref, dfrec]; ENDLOOP; out.Put[IO.string[ "\n\nThe Depends On Relation:\n\nDFFile on <Directory> depends on\n\tDFFile list\n\n"L]]; FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO PrintDF[l.first, out, TRUE]; ENDLOOP; out.Put[IO.string[ "\n\nThe Referenced By Relation:\n\nDFFile on <Directory> is referenced by\n\tDFFile list\n\n"L]]; FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO l.first.refBy ← LOOPHOLE[List.UniqueSort[LOOPHOLE[l.first.refBy], RStatsSupport.CompareDFDep]]; PrintDF[l.first, out, FALSE]; ENDLOOP; nDFFiles ← List.Length[LOOPHOLE[dfrec]]; -- now handle the XRef dfxref ← LOOPHOLE[List.Sort[LOOPHOLE[dfxref], RStatsSupport.CompareXRef]]; file ← FileIO.Open[xrefFileName, overwrite]; file.PutF["DF Cross Reference for %s.\nTotal of %d files.\n\n", IO.rope[topDFName], IO.card[List.Length[LOOPHOLE[dfxref]]]]; file.Put[IO.string["Lines ending with (R) appear as ReadOnly entries.\n"L]]; file.Put[IO.string["For multiple DF files, choose one not marked (R) if possible.\n\n"L]]; FOR l: LIST OF REF XRef ← dfxref, l.rest UNTIL l = NIL DO sfn: Rope.ROPE ← IO.PutFR["<%s>%s", IO.rope[l.first.dfpath], IO.rope[l.first.dfshortname]]; file.PutF["%-35s %-35s%s\n", IO.rope[l.first.shortname], IO.rope[sfn], IO.rope[IF l.first.readOnly THEN " (R)" ELSE NIL]]; ENDLOOP; file.Close[]; out.PutF["\n\nCross reference stored on %s.\n", IO.rope[xrefFileName]]; }; ProcessBcdAnalysis: PUBLIC PROC[typeScript: TypeScript.TS, out: IO.Handle, tty: Subr.TTYProcs, dfrec: LIST OF REF DFRec, specialFiles: BOOL] = { dfxref: LIST OF REF XRef; dfrec ← LOOPHOLE[List.UniqueSort[LOOPHOLE[dfrec], RStatsSupport.CompareDFRec]]; FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO IF TypeScript.UserAbort[typeScript] THEN SIGNAL Subr.AbortMyself; dfxref ← AnalyzeDF[l.first, out, tty, dfxref, dfrec]; ENDLOOP; -- now handle the XRef dfxref ← LOOPHOLE[List.Sort[LOOPHOLE[dfxref], RStatsSupport.CompareXRef]]; AnalyzeBcdDep[out, tty, dfxref, dfrec, specialFiles]; PrintBcdDependency[out, dfrec]; out.Flush[]; EmitCompileStatement[out, dfxref]; }; AnalyzeDF: PROC[dfptr: REF DFRec, out: IO.Handle, tty: Subr.TTYProcs, olddfxref: LIST OF REF XRef, dfrec: LIST OF REF DFRec] RETURNS[dfxref: LIST OF REF XRef] = { stpStateRecord: STPSubr.StpStateRecord ← [checkForOverwrite: FALSE]; df: DFSubr.DF; dfseq: DFSubr.DFSeq ← DFSubr.AllocateDFSeq[500, shared]; sh: Stream.Handle; dfxref ← olddfxref; out.PutF["Reading <%s>%s ... ", IO.rope[dfptr.directory], IO.rope[dfptr.shortname]]; [sh] ← STPSubr.CachedOpen[host: LOOPHOLE[dfptr.host], directory: LOOPHOLE[dfptr.directory], shortname: LOOPHOLE[dfptr.shortname], version: dfptr.version, wantcreatetime: 0, h: tty, wantExplicitVersion: TRUE, onlyOne: TRUE, stpState: @stpStateRecord ! Subr.FileError => { out.PutF["not found.\n"]; GOTO error; }]; DFSubr.ParseStream[sh: sh, dfseq: dfseq, dffilename: LOOPHOLE[dfptr.shortname], using: NIL, noremoteerrors: TRUE, forceReadonly: FALSE, omitNonPublic: FALSE, interestingNestedDF: NIL, ancestor: NIL, nLevel: 0, h: tty]; Stream.Delete[sh]; FOR i: CARDINAL IN [0 .. dfseq.size) DO df ← @dfseq[i]; IF Subr.EndsIn[df.shortname, ".df"L] AND df.atsign THEN { dfptr.dep ← CONS[CS.z.NEW[DFDep ← [directory: NIL, shortname: ConvertUnsafe.ToRope[df.shortname], createtime: df.createtime]], dfptr.dep]; AddToRefBy[df.host, df.directory, df.shortname, dfptr, out, dfrec]; } ELSE { -- add to cross reference list dfxref ← CONS[CS.z.NEW[XRef ← [shortname: ConvertUnsafe.ToRope[df.shortname], host: ConvertUnsafe.ToRope[df.host], directory: ConvertUnsafe.ToRope[df.directory], version: df.version, createtime: df.createtime, dfshortname: dfptr.shortname, dfpath: dfptr.directory, readOnly: df.readonly, compiled: FALSE, eval: FALSE]], dfxref]; }; ENDLOOP; DFSubr.FreeDFSeq[@dfseq]; out.PutChar['\n]; dfptr.dep ← LOOPHOLE[List.UniqueSort[LOOPHOLE[dfptr.dep], RStatsSupport.CompareDFDep]]; EXITS error => NULL; }; AddToRefBy: PROC[host, directory, shortname: LONG STRING, dfptr: REF DFRec, out: IO.Handle, dfrec: LIST OF REF DFRec] = { -- look for shortname, add dfptr to the refBy list FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO d: REF DFRec ← l.first; IF CS.EquivalentRS[d.shortname, shortname] AND CS.EquivalentRS[d.directory, directory] AND CS.EquivalentRS[d.host, host] THEN { -- found it d.refBy ← CONS[CS.z.NEW[DFDep ← [directory: dfptr.directory, shortname: dfptr.shortname, createtime: 0]], d.refBy]; RETURN; }; ENDLOOP; out.PutF["<%s>%s is not part of the release (ref by %s)\n", IO.string[directory], IO.string[shortname], IO.rope[dfptr.shortname]]; }; TabSize: CARDINAL = 4; LineLength: CARDINAL = 80; PrintDF: PROC[dfrec: REF DFRec, out: IO.Handle, depends: BOOL] = { l: LIST OF REF DFDep; current: INT ← TabSize; out.PutF[IF depends THEN "\n%s on <%s> depends on:\n\t" ELSE "\n%s on <%s> referenced by:\n\t", IO.rope[dfrec.shortname], IO.rope[dfrec.directory]]; l ← IF depends THEN dfrec.dep ELSE dfrec.refBy; UNTIL l = NIL DO len: INT ← l.first.shortname.Length[] + 1; IF current + len > LineLength THEN { current ← TabSize; out.Put[IO.string["\n\t"L]]; }; out.PutF["%s ", IO.rope[l.first.shortname]]; current ← current + len; l ← l.rest; ENDLOOP; out.PutChar['\n]; }; AnalyzeBcdDep: PROC[out: IO.Handle, tty: Subr.TTYProcs, dfxref: LIST OF REF XRef, dfrec: LIST OF REF DFRec, specialFiles: BOOL] = { space: Space.Handle ← Space.Create[1, Space.virtualMemory]; FOR l: LIST OF REF XRef ← dfxref, l.rest UNTIL l = NIL DO IF NOT l.first.readOnly AND CS.EndsIn[l.first.shortname, ".bcd"L] AND (specialFiles OR NOT CS.EndsIn[l.first.shortname, "impl.bcd"L]) THEN ForEachBcd[l.first, out, tty, dfxref, dfrec, space, specialFiles]; ENDLOOP; Space.Delete[space]; }; depTable: ARRAY [0 .. 5) OF Rope.Text ← [ "Exec.Bcd", "Segments.bcd", "STP.Bcd", "Streams.bcd", "TTY.Bcd"]; ForEachBcd: PROC[thisBcd: REF XRef, out: IO.Handle, tty: Subr.TTYProcs, dfxref: LIST OF REF XRef, dfrec: LIST OF REF DFRec, space: Space.Handle, specialFiles: BOOL] = { r: Rope.ROPE; sfn: Rope.Text; innardsobject: ProcBcds.InnardsObject ← [bcdheaderspace: Space.nullHandle]; depArray: ARRAY [0 .. LENGTH[depTable]) OF BOOL ← ALL[FALSE]; p: BOOL; ForEachFile: PROC[fth: BcdOps.FTHandle, fti: BcdDefs.FTIndex] RETURNS [stop: BOOL] = { bcdName: Rope.Text; stop ← FALSE; bcdName ← NameToRope[fth.name, innardsobject.ssb]; IF NOT CS.EndsIn[bcdName, ".bcd"L] THEN bcdName ← CS.Flat[Rope.Concat[bcdName, ".bcd"]]; FOR i: CARDINAL IN [0 .. LENGTH[depTable]) DO IF CS.EquivalentRope[bcdName, depTable[i]] THEN depArray[i] ← TRUE; ENDLOOP; FOR l: LIST OF REF XRef ← dfxref, l.rest UNTIL l = NIL DO IF CS.EquivalentRope[l.first.shortname, bcdName] THEN { DFDependsOnDF[thisBcd.dfshortname, l.first, out, dfrec]; thisBcd.bcdChild ← CONS[l.first, thisBcd.bcdChild]; EXIT; }; REPEAT FINISHED => out.PutF["Cannot find %s in list of all files.\n", IO.rope[bcdName]]; ENDLOOP; }; r ← IO.PutFR["<%s>%s", IO.rope[thisBcd.directory], IO.rope[thisBcd.shortname]]; IF thisBcd.version > 0 THEN r ← Rope.Cat[r, IO.PutFR["!%d", IO.card[thisBcd.version]]]; sfn ← CS.Flat[r]; out.PutF["%s ", IO.rope[sfn]]; innardsobject.fh ← RStatsSupport.LeafOpenWithCreate[LOOPHOLE[thisBcd.host], LOOPHOLE[thisBcd.directory], LOOPHOLE[thisBcd.shortname], thisBcd.version, thisBcd.createtime, out, tty ! Subr.FileError => { out.PutF["Error - cannot open %s\n", IO.rope[sfn]]; GOTO leave; }]; IF NOT specialFiles THEN { bcd: BcdOps.BcdBase ← Space.LongPointer[space]; LeafSubr.RemoteMap[space, innardsobject.fh, 0]; IF NOT bcd.definitions THEN { Space.Unmap[space]; IFSFile.Close[innardsobject.fh]; RETURN; }; Space.Unmap[space]; }; ProcBcds.ReadInSegmentsBcd[@innardsobject]; ProcBcds.InstallAddressesBcd[@innardsobject]; [] ← BcdOps.ProcessFiles[innardsobject.bcd, ForEachFile]; thisBcd.eval ← TRUE; IFSFile.Close[innardsobject.fh]; ProcBcds.UnstallBcd[@innardsobject]; out.PutChar['\n]; p ← TRUE; FOR i: CARDINAL IN [0 .. LENGTH[depTable]) DO IF depArray[i] THEN { IF p THEN { out.PutF["%s depends on %s", IO.rope[thisBcd.shortname], IO.rope[depTable[i]]]; p ← FALSE; } ELSE out.PutF[", %s", IO.rope[depTable[i]]]; }; ENDLOOP; IF NOT p THEN out.PutF["\n"]; EXITS leave => NULL; }; DFDependsOnDF: PROC[parentdf: Rope.Text, child: REF XRef, out: IO.Handle, dfrec: LIST OF REF DFRec] = { parentdfrec, childdfrec: REF DFRec; FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO IF CS.EquivalentRope[parentdf, l.first.shortname] THEN { parentdfrec ← l.first; EXIT; }; REPEAT FINISHED => { out.PutF["Cannot find %s in list of DF files.\n", IO.rope[parentdf]]; RETURN; }; ENDLOOP; FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO IF CS.EquivalentRope[child.dfshortname, l.first.shortname] THEN { childdfrec ← l.first; EXIT; }; REPEAT FINISHED => { out.PutF["Cannot find %s in list of DF files.\n", IO.rope[child.dfshortname]]; RETURN; }; ENDLOOP; parentdfrec.bcdDep ← CONS[CS.z.NEW[BcdDep ← [dfrec: childdfrec, bcdName: child.shortname]], parentdfrec.bcdDep]; }; NameToRope: PROC[name: BcdDefs.NameRecord, namestring: BcdOps.NameString] RETURNS[rope: Rope.Text] = { r: Rope.ROPE ← NIL; FOR i: CARDINAL IN [0 .. namestring.size[name]) DO r ← r.Cat[Rope.FromChar[namestring.string.text[name + i]]]; ENDLOOP; rope ← Rope.Flatten[r]; }; PrintBcdDependency: PROC[out: IO.Handle, dfrec: LIST OF REF DFRec] = { FOR l: LIST OF REF DFRec ← dfrec, l.rest UNTIL l = NIL DO l.first.bcdDep ← LOOPHOLE[List.UniqueSort[LOOPHOLE[l.first.bcdDep], RStatsSupport.CompareBcdDep]]; PrintBcdDependencyForADF[l.first, out]; ENDLOOP; }; PrintBcdDependencyForADF: PROC[dfrec: REF DFRec, out: IO.Handle] = { current: INT ← TabSize; out.PutF["\n%s on <%s> bcd depends on:\n\t", IO.rope[dfrec.shortname], IO.rope[dfrec.directory]]; FOR l: LIST OF REF BcdDep ← dfrec.bcdDep, l.rest UNTIL l = NIL DO s: Rope.ROPE; len: INT; IF CS.EquivalentRope[l.first.dfrec.shortname, dfrec.shortname] THEN LOOP; s ← IO.PutFR["%s (%s) ", IO.rope[l.first.dfrec.shortname], IO.rope[l.first.bcdName]]; len ← s.Length[]; IF current + len > LineLength THEN { current ← TabSize; out.Put[IO.string["\n\t"L]]; }; out.PutRope[s]; current ← current + len; ENDLOOP; out.PutChar['\n]; }; EmitCompileStatement: PROC[out: IO.Handle, dfxref: LIST OF REF XRef] = { out.PutRope["Compile all files:\n\nCompiler "]; FOR l: LIST OF REF XRef ← dfxref, l.rest UNTIL l = NIL DO IF NOT l.first.compiled AND l.first.eval THEN GenCompile[l.first, out]; ENDLOOP; out.PutRope[";\n\n"]; }; GenCompile: PROC[x: REF RStatsSupport.XRef, out: IO.Handle] = { IF x.compiled OR NOT x.eval THEN RETURN; FOR l: LIST OF REF XRef ← x.bcdChild, l.rest UNTIL l = NIL DO IF NOT l.first.compiled AND l.first # x THEN GenCompile[l.first, out]; ENDLOOP; out.PutF["%s ", IO.rope[x.shortname]]; x.compiled ← TRUE; }; }.