-- 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;
};
}.