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



}.