-- ProcBcdsImpl.Mesa, last edit April 5, 1982 10:59 am
-- Pilot 6.0/ Mesa 7.0

DIRECTORY
  BcdDefs: TYPE USING [Base, CTIndex, CTNull, CTRecord, EXPIndex, EXPRecord, FTIndex, FTNull, 
  	FTRecord, FTSelf, IMPIndex, IMPRecord, Link, MTIndex, MTRecord, NameRecord, 
	SGIndex, VersionID, VersionStamp],
  CWF: TYPE USING [SWF0, WF0, WF1, WF3],
  Directory: TYPE USING [Error, Handle, ignore, Lookup],
  Environment: TYPE USING [wordsPerPage],
  File: TYPE USING [Capability, nullCapability, read],
  FileParms: TYPE USING [nullSymbolSpace, SymbolSpace],
  LeafSubr: TYPE USING [RemoteMap],
  LongString: TYPE USING [AppendString, AppendSubString, SubString, SubStringDescriptor],
  ProcBcds: TYPE USING [Innards, ProcDep, ProcMod],
  RTBcd: TYPE USING[RTBase],
  Space: TYPE USING [Create, CreateUniformSwapUnits, Delete, Handle, LongPointer, Map, 
  	nullHandle, virtualMemory],
  String: TYPE USING [AppendString, EquivalentString],
  Subr: TYPE USING [Any, ControlChars, GetCreateDate, SubStrCopy, strcpy],
  Symbols: TYPE USING [HTIndex, HTNull, HTRecord, MDIndex, MDRecord, OwnMdi],
  SymbolSegment: TYPE USING [FGHeader, STHeader, VersionID],
  Table: TYPE USING [Base],
  Time: TYPE USING [Current],
  TimeStamp: TYPE USING [Stamp];

ProcBcdsImpl: PROGRAM 
IMPORTS CWF, Directory, LeafSubr, LongString, Space, String, Subr, Time
EXPORTS ProcBcds = {

useonespace: BOOL = TRUE;	-- FALSE has never worked

-- uses NO MDS!!!

InvalidBcd: PUBLIC SIGNAL = CODE;

ReadInSegmentsBcd: PUBLIC PROC [innards: ProcBcds.Innards] = {
local: BOOL;
npages: CARDINAL;
IF innards.cap = File.nullCapability AND innards.fh = NIL THEN ERROR;
local ← innards.fh = NIL;
-- map in at least one page to get total # of pages
innards.bcdheaderspace ← Space.Create[size: 10, parent: Space.virtualMemory];
IF local THEN 
	Space.Map[space: innards.bcdheaderspace, 
		window: [file: innards.cap, base: 1]]
ELSE LeafSubr.RemoteMap[space: innards.bcdheaderspace, fh: innards.fh, base: 0];
innards.bcd ← Space.LongPointer[innards.bcdheaderspace];
IF innards.bcd.versionIdent ~= BcdDefs.VersionID THEN {
	-- not a bcd or invalid version ID in header
	Space.Delete[innards.bcdheaderspace];
	innards.bcdheaderspace ← Space.nullHandle;
	innards.bcd ← NIL;
	SIGNAL InvalidBcd;
	RETURN;
	};
npages ← innards.bcd.nPages;
IF npages > 10 THEN {
	Space.Delete[innards.bcdheaderspace];
	-- now map in the right number of pages
	innards.bcdheaderspace ← Space.Create[size: npages, parent: Space.virtualMemory];
	IF local THEN 
		Space.Map[space: innards.bcdheaderspace, 
			window: [file: innards.cap, base: 1]]
	ELSE LeafSubr.RemoteMap[space: innards.bcdheaderspace, fh: innards.fh, base: 0];
	innards.bcd ← Space.LongPointer[innards.bcdheaderspace];
	};
Space.CreateUniformSwapUnits[parent: innards.bcdheaderspace, size: 8];
innards.upperLimit ← npages * Environment.wordsPerPage;
};

InstallAddressesBcd: PUBLIC PROC[innards: ProcBcds.Innards] = {
innards.tb ← LOOPHOLE[innards.bcd];
innards.ssb ← LOOPHOLE[innards.bcd + innards.bcd.ssOffset];
innards.ctb ← innards.tb + innards.bcd.ctOffset;
innards.mtb ← innards.tb + innards.bcd.mtOffset;
innards.itb ← innards.tb + innards.bcd.impOffset;
innards.etb ← innards.tb + innards.bcd.expOffset;
innards.sgb ← innards.tb + innards.bcd.sgOffset;
innards.ftb ← innards.tb + innards.bcd.ftOffset;
innards.ntb ← innards.tb + innards.bcd.ntOffset;
innards.evb ← innards.tb + innards.bcd.evOffset;
innards.spb ← innards.tb + innards.bcd.spOffset;
innards.fpb ← innards.tb + innards.bcd.fpOffset;
};
    
UnstallBcd: PUBLIC PROC [innards: ProcBcds.Innards] = { 
IF innards.bcdheaderspace = Space.nullHandle THEN ERROR;
Space.Delete[innards.bcdheaderspace];
innards↑ ← [bcdheaderspace: Space.nullHandle];	-- others are defaulted
};
    
PrintDepends: PUBLIC PROC[innards: ProcBcds.Innards, 
	procMod: ProcBcds.ProcMod,procDep: ProcBcds.ProcDep, 
	print, calltwice, less: BOOL, bcdfilename: LONG STRING] 
	RETURNS[success, isconfig, isdefns: BOOL, 
	nimp, nexp, ntype: CARDINAL] = BEGIN
uns: UNSPECIFIED;
sourcefile: STRING ← [100];
interfacename: STRING ← [100];
switches: STRING ← [100];
cti: BcdDefs.CTIndex ← FIRST[BcdDefs.CTIndex];
nullstring: STRING ← ""L;
altoCode, boundsChecks, cedarSwitch, crossJump, linksInCode, nilChecks, sortByUsage: BOOL ← FALSE;
notcodebound: BOOL;
symbolSpace: FileParms.SymbolSpace ← FileParms.nullSymbolSpace;
rtVersionID: CARDINAL ← 0;

success ← isconfig ← isdefns ← FALSE;
nexp ← nimp ← ntype ← 0;
IF nullstring.length ~= 0 THEN ERROR;
IF innards.bcd.versionIdent ~= BcdDefs.VersionID THEN {
	CWF.WF0[" Error - wrong bcd version.\n"L];
	RETURN;
	};
-- if it is a defs file, we simply record the interface name
PutName[innards, innards.bcd.source, sourcefile];
IF sourcefile.length >0 AND sourcefile[sourcefile.length -1] = '. THEN
		sourcefile.length ← sourcefile.length - 1;
-- CWF.WF1["sourcefiles %s\n"L, sourcefile];
IF Subr.ControlChars[sourcefile] THEN {
	CWF.WF0[" Error - bad sourcefile.\n"L];
	RETURN;
	};
success ← GetModuleName[innards, interfacename];
IF NOT success THEN RETURN;
-- at this point, success must always be TRUE since
-- this code now calls the procDep and procMod procs
isconfig ← innards.bcd.nConfigs > 0;
IF NOT isconfig THEN {
	sSeg: BcdDefs.SGIndex ← innards.mtb[FIRST[BcdDefs.MTIndex]].sseg;
	altoCode ←  innards.mtb[FIRST[BcdDefs.MTIndex]].altoCode;
	boundsChecks ←  innards.mtb[FIRST[BcdDefs.MTIndex]].boundsChecks;
	crossJump ←  innards.mtb[FIRST[BcdDefs.MTIndex]].crossJumped;
	cedarSwitch ←  IF NOT altoCode THEN innards.mtb[FIRST[BcdDefs.MTIndex]].long ELSE FALSE;
	nilChecks ←  innards.mtb[FIRST[BcdDefs.MTIndex]].nilChecks;
	linksInCode ← innards.mtb[FIRST[BcdDefs.MTIndex]].linkLoc = code;
	sortByUsage ← NOT innards.mtb[FIRST[BcdDefs.MTIndex]].initial;	-- /s switch, initial = FALSE is /s
	symbolSpace ← [file: innards.cap, span: [base: innards.sgb[sSeg].base,
		pages: innards.sgb[sSeg].pages]];
	};
notcodebound ← IF isconfig THEN 
	NotCodeBound[innards, BcdDefs.CTNull] ELSE TRUE;
isdefns ← innards.bcd.definitions;
IF isdefns THEN 
	uns ← procMod[sourcefile, interfacename,
		innards.bcd.version, innards.bcd.sourceVersion,
		innards.bcd.creator, 
		TRUE, FALSE, FALSE, FALSE,
		FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, symbolSpace, 0]
ELSE {
	IF innards.bcd.extended AND innards.bcd.rtPages.pages > 0 THEN 
		rtVersionID ← LOOPHOLE[innards.bcd + innards.bcd.rtPages.relPageBase*Environment.wordsPerPage, RTBcd.RTBase].versionIdent;
	uns ← procMod[sourcefile, interfacename,
		innards.bcd.version, innards.bcd.sourceVersion, innards.bcd.creator, 
		FALSE, isconfig, NOT notcodebound, innards.bcd.tableCompiled,
		altoCode, boundsChecks, cedarSwitch, crossJump, linksInCode, nilChecks, sortByUsage,
		symbolSpace, rtVersionID];
	};
IF NOT isconfig THEN {
	IF innards.bcd.extended THEN
		ntype ← PrintDirectory[innards, procDep, uns, print, bcdfilename]
	ELSE [,ntype] ← PrintSymbolsFile[innards, procDep, uns, print, FALSE, less, bcdfilename];
	};
IF NOT isdefns THEN 
	nexp ← PrintExports[innards, procDep, uns, print];
IF calltwice AND NOT isdefns AND (ntype > 50 OR nexp > 30) THEN {
	-- call again to avoid overflow in the database
	uns ← procMod[sourcefile, interfacename,
		innards.bcd.version, innards.bcd.sourceVersion,
		innards.bcd.creator, FALSE, isconfig, NOT notcodebound, 
		innards.bcd.tableCompiled, altoCode, boundsChecks, cedarSwitch, 
		crossJump, linksInCode, nilChecks, sortByUsage, symbolSpace, rtVersionID];
	};
nimp ← PrintImports[innards, procDep, uns, print];
IF isconfig THEN {
	 PrintConfigDepends[innards, procDep, uns, notcodebound, FALSE];
	 IF NOT notcodebound AND print THEN CWF.WF0["(code bound)\n"L];
	};
success ← TRUE;
RETURN;
END;
    
PrintDirectory: PROC[innards: ProcBcds.Innards, procDep: ProcBcds.ProcDep, 
	uns: UNSPECIFIED, print: BOOL, bcdfilename: LONG STRING] 
	RETURNS[ntype: CARDINAL] = {
modulename: STRING  ← [100];
filename: STRING ← [100];
fti: BcdDefs.FTIndex ← FIRST[BcdDefs.FTIndex];
ntype ← 0;
UNTIL fti = innards.bcd.ftLimit DO
      PutName[innards, innards.ftb[fti].name, filename];
      IF NOT Subr.Any[filename, '.] THEN
		String.AppendString[filename, ".bcd"L];
      procDep[defstype, GuessModulename[innards, fti, modulename], 
      		filename, innards.ftb[fti].version, uns];
      ntype ← ntype + 1;
      fti ← fti + SIZE[BcdDefs.FTRecord];
      IF LOOPHOLE[fti, CARDINAL] > LOOPHOLE[innards.bcd.ftLimit, CARDINAL] THEN
	GO TO Bogus;
      REPEAT Bogus => {
		CWF.WF1["Error - Garbage FileTable in %s.\n"L, bcdfilename];
		RETURN;
		};
      ENDLOOP;
IF print THEN CWF.WF1[" %d types.\n"L, @ntype];
};

-- returns NIL if can't find modulename
GuessModulename: PROC[innards: ProcBcds.Innards, fti: BcdDefs.FTIndex, modulename: STRING] 
	RETURNS[STRING] = {
iti: BcdDefs.IMPIndex ← FIRST[BcdDefs.IMPIndex];
eti: BcdDefs.EXPIndex ← FIRST[BcdDefs.EXPIndex];
WHILE LOOPHOLE[iti, CARDINAL] < LOOPHOLE[innards.bcd.impLimit, CARDINAL] DO
	IF innards.itb[iti].file = fti THEN {
		PutName[innards, innards.itb[iti].name, modulename];
		RETURN[modulename];
		};
	iti ← iti + SIZE[BcdDefs.IMPRecord];
	ENDLOOP;
WHILE LOOPHOLE[eti, CARDINAL] < LOOPHOLE[innards.bcd.expLimit, CARDINAL] DO
	IF innards.etb[eti].file = fti THEN {
		PutName[innards, innards.etb[eti].name, modulename];
		RETURN[modulename];
		};
	eti ← eti + SIZE[BcdDefs.IMPRecord] + innards.etb[eti].size;
	ENDLOOP;
RETURN[NIL];
};

Sym: TYPE = POINTER TO SymRecord;

SymRecord: TYPE = RECORD[
stHandle: LONG POINTER TO SymbolSegment.STHeader ← NIL,
stHandleSpace: Space.Handle ← Space.nullHandle,
-- 
-- hash table
ht:	LONG DESCRIPTOR FOR ARRAY Symbols.HTIndex OF Symbols.HTRecord ← NULL,
htSpace: Space.Handle ← Space.nullHandle,
--
ssb: 	LONG STRING ← NIL,		-- id string
ssbSpace: Space.Handle ← Space.nullHandle,
--
mdb: 	Table.Base ← NIL,		-- module directory base
mdbSpace: Space.Handle ← Space.nullHandle,
mdLimit: Symbols.MDIndex ← NULL,		-- module directory size
--
fgptr: LONG POINTER TO SymbolSegment.FGHeader ← NIL,	-- ptr to beginning of fine grain table
fgptrSpace: Space.Handle ← Space.nullHandle
];

-- headerseg initted from ReadIn
PrintSymbolsFile: PUBLIC PROC[innards: ProcBcds.Innards, 
	procDep: ProcBcds.ProcDep, uns: UNSPECIFIED, print, allsyms, less: BOOL,
	bcdfilename: LONG STRING] RETURNS[success: BOOL, ntype: CARDINAL] = {
mti, mtLimit: BcdDefs.MTIndex;
symseg: BcdDefs.SGIndex;
symrecord: SymRecord ← [];
shortstr: STRING ← [100];
nullstr: STRING ← ""L;

ntype ← 0;
success ← FALSE;
mti ← FIRST[BcdDefs.MTIndex];
mtLimit ← IF allsyms THEN innards.bcd.mtLimit ELSE mti + 1;
UNTIL LOOPHOLE[mti, CARDINAL] >= LOOPHOLE[mtLimit, CARDINAL] DO
	symseg ← innards.mtb[mti].sseg;
	success ← InitSym[innards, @symrecord, symseg, less, bcdfilename];
	IF NOT success THEN EXIT;
	[success, ntype] ← PrintModuleEntries[innards, @symrecord, 
		procDep, uns, print, bcdfilename];
	IF NOT success THEN EXIT;
	IF NOT less THEN {
		FindSourceFileName[@symrecord, shortstr];
		IF shortstr.length > 0 THEN
			procDep[sourcefile, nullstr, shortstr,
				symrecord.stHandle.sourceVersion, uns];
		};
	FreeSym[@symrecord];
	mti ← mti + (WITH m: innards.mtb[mti] SELECT FROM
		     direct => SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link],
		     indirect => SIZE[BcdDefs.MTRecord[indirect]],
		     multiple => SIZE[BcdDefs.MTRecord[multiple]],
		    ENDCASE => ERROR)
      	ENDLOOP;
RETURN[success, ntype];
};

-- init from bcd
InitSym: PROC[innards: ProcBcds.Innards, sym: Sym, symseg: BcdDefs.SGIndex, less: BOOL,
	bcdfilename: LONG STRING] RETURNS[success: BOOL] = {
npages: CARDINAL;
sfnsym: STRING ← [100];
cap: File.Capability;
local: BOOL;
base: CARDINAL;
p: LONG POINTER;

success ← FALSE;
IF innards.sgb[symseg].class ~= symbols THEN {
	CWF.WF0["Error - badly formed symbols.\n"L];
	RETURN;
	}; 
IF innards.sgb[symseg].pages = 0 THEN {
	CWF.WF1["Warning - No symbols in %s.\n"L, bcdfilename];
	RETURN;
	};
IF innards.sgb[symseg].file ~= BcdDefs.FTSelf THEN {
	PrintFileName[innards, innards.sgb[symseg].file, sfnsym];
	cap ← Directory.Lookup[fileName: sfnsym, permissions: File.read
		! Directory.Error => GOTO err];
	EXITS
	err => {
		CWF.WF1["Error - can't find '%s'\n"L, sfnsym];
		RETURN;
		};
	}
ELSE cap ← innards.cap;
npages ← innards.sgb[symseg].pages;
IF NOT less THEN npages ← npages + innards.sgb[symseg].extraPages;
IF useonespace THEN 
	sym.stHandleSpace ← Space.Create[parent: Space.virtualMemory,
		size: npages]
ELSE 
	sym.stHandleSpace ← Space.Create[parent: Space.virtualMemory,
		size: (SIZE[SymbolSegment.STHeader]/Environment.wordsPerPage) + 1];
local ← innards.fh = NIL;
IF local THEN {
	base ← innards.sgb[symseg].base;
	Space.Map[space: sym.stHandleSpace, 
		window: [file: cap, base: base]]
	}
ELSE {
	base ← innards.sgb[symseg].base - 1;
	LeafSubr.RemoteMap[sym.stHandleSpace, innards.fh, base];
	};
sym.stHandle ← Space.LongPointer[sym.stHandleSpace];
IF sym.stHandle.versionIdent ~= SymbolSegment.VersionID THEN {
	CWF.WF0["Error - Symbols versions do not agree.\n"L];
	RETURN;
	};
-- init symbols header
IF useonespace THEN {
	b: LONG POINTER;
	tB: Table.Base;
	b ← LOOPHOLE[sym.stHandle];
	tB ← LOOPHOLE[sym.stHandle];
	sym.ht         ← DESCRIPTOR[b+sym.stHandle.htBlock.offset,
			sym.stHandle.htBlock.size/SIZE[Symbols.HTRecord]];
	sym.ssb        ← b + sym.stHandle.ssBlock.offset;
	sym.mdb        ← tB + sym.stHandle.mdBlock.offset;
	sym.mdLimit    ← FIRST[Symbols.MDIndex] + sym.stHandle.mdBlock.size;
	IF NOT less THEN 
		sym.fgptr ← 	LOOPHOLE[sym.stHandle + 
				sym.stHandle.fgRelPgBase * Environment.wordsPerPage];
	}
ELSE {
	[p, sym.htSpace] ← GetSpace[innards, sym, base, sym.stHandle.htBlock.offset, 
		sym.stHandle.htBlock.size];
	sym.ht ← DESCRIPTOR[p, sym.stHandle.htBlock.size/SIZE[Symbols.HTRecord]];
	[sym.ssb, sym.ssbSpace] ← GetSpace[innards, sym, base, 
		sym.stHandle.ssBlock.offset, sym.stHandle.ssBlock.size];
	[LOOPHOLE[sym.mdb, LONG POINTER], sym.mdbSpace] ← GetSpace[innards, sym, base, 
		sym.stHandle.mdBlock.offset, sym.stHandle.mdBlock.size];
	sym.mdLimit    ← FIRST[Symbols.MDIndex] + sym.stHandle.mdBlock.size;
	IF NOT less THEN 
		[LOOPHOLE[sym.fgptr, LONG POINTER], sym.fgptrSpace] ← GetSpace[innards, sym, base, 
			sym.stHandle.fgRelPgBase * Environment.wordsPerPage,
			Environment.wordsPerPage];
	};
RETURN[TRUE];
};

-- offset is # words off # base page number, size is # words in range
GetSpace: PROC[innards: ProcBcds.Innards, sym: Sym, pbase, offset, size: CARDINAL] 
	RETURNS[buffer: LONG POINTER, space: Space.Handle] = {
pstart, pnum: CARDINAL;
pageoffset: CARDINAL ← offset/Environment.wordsPerPage;
pstart ← pbase + pageoffset;
pnum ← ((size+offset)/Environment.wordsPerPage) - pageoffset + 1;
space ← Space.Create[parent: Space.virtualMemory, size: pnum];
IF innards.cap ~= File.nullCapability THEN 
	Space.Map[space, [innards.cap, pstart]]
ELSE LeafSubr.RemoteMap[space, innards.fh, pstart];
buffer ← Space.LongPointer[space];
buffer ← buffer + (offset - (pageoffset*Environment.wordsPerPage));
};

PrintModuleEntries: PROC[innards: ProcBcds.Innards, sym: Sym, 
	procDep: ProcBcds.ProcDep, uns: UNSPECIFIED, print: BOOL,
	bcdfilename: LONG STRING] 
	RETURNS[success: BOOL, ntype: CARDINAL] = {
-- foreach module entry
-- since each filename ends in ".bcd.", we strip the trailing "." off
mdi: Symbols.MDIndex ← FIRST[Symbols.MDIndex];
sfn: STRING ← [100];
smodulename: STRING ← [100];
substr: LongString.SubStringDescriptor;

ntype ← 0;
success ← FALSE;
DO
	-- skip the module itself
	IF mdi = Symbols.OwnMdi THEN mdi ← mdi + SIZE[Symbols.MDRecord];
	IF mdi = sym.mdLimit THEN EXIT;
	SubStringForHash[sym, @substr, sym.mdb[mdi].moduleId];
	smodulename.length ← 0;
	LongString.AppendSubString[smodulename, @substr];
	SubStringForHash[sym, @substr, sym.mdb[mdi].fileId];
	sfn.length ← 0;
	LongString.AppendSubString[sfn, @substr];
	IF sfn.length > 0 AND sfn[sfn.length-1] = '. THEN
		sfn.length ← sfn.length - 1;		-- strip "."
	-- CWF.WF2["module %s file %s\n"L, smodulename, sfn];
	ntype ← ntype + 1;
	procDep[defstype, smodulename, sfn, sym.mdb[mdi].stamp, uns];
	mdi ← mdi + SIZE[Symbols.MDRecord];
	IF LOOPHOLE[mdi, CARDINAL] > LOOPHOLE[sym.mdLimit, CARDINAL]
	THEN {
		CWF.WF1["Warning - symbols MDRecord is garbage in %s.\n"L, bcdfilename];
		RETURN;
		};
	ENDLOOP;
IF print THEN CWF.WF1["%u types, "L, @ntype];
success ← TRUE;
};

FindSourceFileName: PROC[sym: Sym, tostr: STRING] = {
-- fine grain table is in sgb[].extrapages
IF sym.stHandle.fgRelPgBase > 0 AND sym.stHandle.fgPgCount > 0 THEN {
	tostr.length ← 0;
	LongString.AppendString[tostr, @sym.fgptr.sourceFile];
	-- remove trailing "."
	IF tostr.length > 0 AND tostr[tostr.length-1] = '. THEN
		tostr.length ← tostr.length - 1;		-- strip "."
	}
ELSE 	tostr.length ← 0;
-- CWF.WF1["sourcefile <%s>\n"L, tostr];
};

-- free the segment
FreeSym: PROC[sym: Sym] = { 
IF sym.stHandleSpace ~= Space.nullHandle THEN {
	Space.Delete[sym.stHandleSpace];
	sym.stHandleSpace ← Space.nullHandle;
	};
IF sym.htSpace ~= Space.nullHandle THEN {
	Space.Delete[sym.htSpace];
	sym.htSpace ← Space.nullHandle;
	};
IF sym.ssbSpace ~= Space.nullHandle THEN {
	Space.Delete[sym.ssbSpace];
	sym.ssbSpace ← Space.nullHandle;
	};
IF sym.mdbSpace ~= Space.nullHandle THEN {
	Space.Delete[sym.mdbSpace];
	sym.mdbSpace ← Space.nullHandle;
	};
IF sym.fgptrSpace ~= Space.nullHandle THEN {
	Space.Delete[sym.fgptrSpace];
	sym.fgptrSpace ← Space.nullHandle;
	};
};

SubStringForHash: PROC [sym: Sym, s: LongString.SubString, hti: Symbols.HTIndex] = {
s.base ← sym.ssb;
IF hti = Symbols.HTNull
THEN s.offset ← s.length ← 0
ELSE s.length ← sym.ht[hti].ssIndex - (s.offset ← sym.ht[hti-1].ssIndex)
};

GetModuleName: PUBLIC PROC[innards: ProcBcds.Innards, interfacename: STRING] 
	RETURNS[success: BOOL] = {
-- unofficially, a defs file or impl modules name is the first
-- index in the module table.  Not so for configs.
-- they are in the config table
cti: BcdDefs.CTIndex ← FIRST[BcdDefs.CTIndex];
IF innards.bcd.nConfigs = 0 THEN 
	PutName[innards, innards.mtb[FIRST[BcdDefs.MTIndex]].name,
		interfacename]
ELSE 
    	UNTIL cti = innards.bcd.ctLimit DO
      		IF innards.ctb[cti].config = BcdDefs.CTNull THEN {
			PutName[innards, innards.ctb[cti].name,
				interfacename];
			-- CWF.WF1["Name: %s\n"L, interfacename];
        		EXIT;
			};
      		cti ← cti+innards.ctb[cti].nControls + SIZE[BcdDefs.CTRecord];
      		IF LOOPHOLE[cti, CARDINAL] > LOOPHOLE[innards.bcd.ctLimit,
			CARDINAL] THEN {
			CWF.WF0["Garbage Garbage Garbage.\n"L];
			RETURN[FALSE];
			};
      		ENDLOOP;
RETURN[TRUE];
};

	
PrintExports: PROC[innards: ProcBcds.Innards, procDep: ProcBcds.ProcDep,
	uns: UNSPECIFIED, print: BOOL] RETURNS[nexp: CARDINAL] = {
    eti: BcdDefs.EXPIndex ← FIRST[BcdDefs.EXPIndex];
    nexp ← 0;
    UNTIL eti = innards.bcd.expLimit DO
      PrintExport[innards, eti, procDep, uns];
      nexp ← nexp + 1;
      eti ← eti + innards.etb[eti].size + SIZE[BcdDefs.EXPRecord];
      IF LOOPHOLE[eti, CARDINAL] > LOOPHOLE[innards.bcd.expLimit, CARDINAL]
	THEN GO TO Bogus;
      REPEAT Bogus => {
		CWF.WF0["Garbage Garbage Garbage.\n"L];
		RETURN;
		};
      ENDLOOP;
    IF print THEN CWF.WF1[" %d exp, "L, @nexp];
    };
    
  PrintExport: PROC [innards: ProcBcds.Innards, eti: BcdDefs.EXPIndex, 
	procDep: ProcBcds.ProcDep, uns: UNSPECIFIED] =
    BEGIN OPEN innards.etb[eti];
    stemp: STRING ← [100];
    stemp1: STRING ← [100];
    vers: BcdDefs.VersionStamp;
    PutName[innards, name, stemp];
    PrintFileName[innards, file, stemp1];
    IF stemp.length = 0 OR Subr.ControlChars[stemp]
    OR stemp1.length = 0 OR Subr.ControlChars[stemp1] THEN {
	CWF.WF0["name garbaged up\n"L];
	RETURN;
	};
    -- CWF.WF1["%s"L, stemp];
    -- CWF.WF0[", file: "];
    -- CWF.WF1["%s"L, stemp1];
    vers ← innards.ftb[file].version;
    procDep[exports, stemp, stemp1, vers, uns];
    END;

  PrintImports: PROC[innards: ProcBcds.Innards, procDep: ProcBcds.ProcDep,
	uns: UNSPECIFIED, print: BOOL] RETURNS[nimp: CARDINAL] = {
    iti: BcdDefs.IMPIndex ← FIRST[BcdDefs.IMPIndex];
    nullstring: STRING ← ""L;
    nimp ← 0;
    UNTIL iti = innards.bcd.impLimit DO
      PrintImport[innards, iti, procDep, uns];
      nimp ← nimp + 1;
      iti ← iti + SIZE[BcdDefs.IMPRecord];
      IF LOOPHOLE[iti, CARDINAL] > LOOPHOLE[innards.bcd.impLimit, CARDINAL] THEN
	GO TO Bogus;
      REPEAT Bogus => {
		CWF.WF0["Garbage Garbage Garbage.\n"L];
		RETURN;
		};
      ENDLOOP;
    IF print THEN CWF.WF1[" %d imp.\n"L, @nimp];
};

  PrintImport: PROC [innards: ProcBcds.Innards, iti: BcdDefs.IMPIndex,
	procDep: ProcBcds.ProcDep, uns: UNSPECIFIED] = { 
    OPEN innards.itb[iti];
    stemp: STRING ← [100];
    stemp1: STRING ← [100];
    vers: BcdDefs.VersionStamp;
    PutName[innards, name, stemp];
    PrintFileName[innards, file, stemp1];
    IF stemp.length = 0 OR Subr.ControlChars[stemp]
    OR stemp1.length = 0 OR Subr.ControlChars[stemp1] THEN {
	CWF.WF0["name garbaged up\n"L];
	RETURN;
	};
    vers ← innards.ftb[file].version;
    procDep[imports, stemp, stemp1, vers, uns];
    };
    
-- if any one of the bcds ins not codebound, then the config
-- is not codebound
NotCodeBound: PROC[innards: ProcBcds.Innards, parent: BcdDefs.CTIndex] 
	RETURNS [notcodebound: BOOL] = {
cti: BcdDefs.CTIndex ← FIRST[BcdDefs.CTIndex];
notcodebound ← FALSE;
UNTIL cti = innards.bcd.ctLimit DO
	IF innards.ctb[cti].config = parent THEN {
		notcodebound ← notcodebound OR NotCodeBound[innards, cti];
		notcodebound ← notcodebound OR NotCodeBoundModule[innards, cti];
		};
	cti ← cti + innards.ctb[cti].nControls + SIZE[BcdDefs.CTRecord];
	IF LOOPHOLE[cti, CARDINAL] > LOOPHOLE[innards.bcd.ctLimit, CARDINAL]
	THEN {
		CWF.WF0["Garbage Config.\n"L];
		RETURN;
		};
	ENDLOOP;
};

NotCodeBoundModule: PROC[innards: ProcBcds.Innards, cti: BcdDefs.CTIndex]
	 RETURNS[notcodebound: BOOL] = {
codeseg: BcdDefs.SGIndex;
mti: BcdDefs.MTIndex ← FIRST[BcdDefs.MTIndex];
notcodebound ← FALSE;
UNTIL mti = innards.bcd.mtLimit DO
	IF innards.mtb[mti].config = cti THEN {
		IF innards.mtb[mti].file ~= BcdDefs.FTSelf THEN {
			codeseg ← innards.mtb[mti].code.sgi;
			IF innards.sgb[codeseg].class ~= code THEN {
				CWF.WF0["Error - not code seg\n"L];
				RETURN;
				};
			notcodebound ← notcodebound
			OR innards.sgb[codeseg].file ~= BcdDefs.FTSelf;
			};
		};
	mti ← mti + (WITH m: innards.mtb[mti] SELECT FROM
		     direct => SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link],
		     indirect => SIZE[BcdDefs.MTRecord[indirect]],
		     multiple => SIZE[BcdDefs.MTRecord[multiple]],
		    ENDCASE => ERROR);
	IF LOOPHOLE[mti, CARDINAL] > LOOPHOLE[innards.bcd.mtLimit, CARDINAL]
	THEN {
		CWF.WF0["Garbage Module.\n"L];
		RETURN;
		};
	ENDLOOP;
};

-- a config .bcd depends on
-- 	itself
--	all module entries for itself
-- 	all config entries (not FTSelf) one level away
-- 	module entries for config's that are FTSelf's away from the top

-- for each config entry:
--	if parent = CTNull, print config and modules
-- 	if parent[parent] = CTNull print config
--	if (file = FTSelf)* and parent = CTNull, print modules

PrintConfigDepends: PROC[innards: ProcBcds.Innards, 
	procDep: ProcBcds.ProcDep,uns: UNSPECIFIED, notcodebound, stopnesting: BOOL] = {
cti: BcdDefs.CTIndex ← FIRST[BcdDefs.CTIndex];
modname: STRING ← [100];
filename: STRING ← [100];
topcti: BcdDefs.CTIndex;
ok: BOOL;

UNTIL cti = innards.bcd.ctLimit DO
	ok ← FALSE;
	topcti ← innards.ctb[cti].config;		-- first level parent
	WHILE topcti ~= BcdDefs.CTNull AND innards.ctb[topcti].file = BcdDefs.FTSelf DO
		topcti ← innards.ctb[topcti].config;
		ENDLOOP;
	IF topcti = BcdDefs.CTNull 
	OR innards.ctb[topcti].config = BcdDefs.CTNull THEN ok ← TRUE;
	IF ok AND innards.ctb[cti].file ~= BcdDefs.FTSelf THEN {
		PutName[innards, innards.ctb[cti].name, modname];
		PrintFileName[innards, innards.ctb[cti].file, filename];
		procDep[IF notcodebound THEN otherdepends 
			ELSE canignore, modname, filename,
			innards.ftb[innards.ctb[cti].file].version,
			uns];
		};
	cti ← cti + innards.ctb[cti].nControls + SIZE[BcdDefs.CTRecord];
	IF LOOPHOLE[cti, CARDINAL] > LOOPHOLE[innards.bcd.ctLimit, CARDINAL]
	THEN {
		CWF.WF0["Garbage Config.\n"L];
		RETURN;
		};
	ENDLOOP;
ProcessModuleTable[innards, procDep, uns, notcodebound];
};

ProcessModuleTable: PROC[innards: ProcBcds.Innards, procDep: ProcBcds.ProcDep, 
	uns: UNSPECIFIED, notcodebound: BOOL] ={
symseg, codeseg: BcdDefs.SGIndex;
mti: BcdDefs.MTIndex ← FIRST[BcdDefs.MTIndex];
modname: STRING ← [100];
filename: STRING ← [100];
cti: BcdDefs.CTIndex;

UNTIL mti = innards.bcd.mtLimit DO
	cti ← innards.mtb[mti].config;
	WHILE innards.ctb[cti].file = BcdDefs.FTSelf DO
		IF innards.ctb[cti].config = BcdDefs.CTNull THEN EXIT;
		cti ← innards.ctb[cti].config;
		ENDLOOP;
	IF innards.ctb[cti].config = BcdDefs.CTNull THEN {
		symseg ← innards.mtb[mti].sseg;
		IF innards.sgb[symseg].class ~= symbols THEN {
			CWF.WF0["Error - not symseg\n"L];
			RETURN;
			};
		IF innards.sgb[symseg].file ~= BcdDefs.FTSelf
		AND innards.sgb[symseg].file ~= BcdDefs.FTNull THEN {
			PrintFileName[innards, innards.sgb[symseg].file,
				filename];
			PutName[innards, innards.mtb[mti].name,modname];
			IF NOT String.EquivalentString[filename, modname]
			THEN  {
				symfilevers: TimeStamp.Stamp;
				-- symfilevers ← GetCorrectCreateDate[filename,
					-- innards.ftb[
					-- innards.sgb[symseg].file].version];
				symfilevers ← innards.ftb[innards.sgb[symseg].file].version;
				procDep[symbolsfile, modname, filename,
					symfilevers, uns];
				};
			};
		IF innards.mtb[mti].file ~= BcdDefs.FTSelf
		AND innards.mtb[mti].file ~= BcdDefs.FTNull THEN {
			codeseg ← innards.mtb[mti].code.sgi;
			IF innards.sgb[codeseg].class ~= code THEN {
				CWF.WF0["Error - not code seg\n"L];
				RETURN;
				};
			PutName[innards, innards.mtb[mti].name, modname];
			PrintFileName[innards, innards.mtb[mti].file, filename];
			procDep[IF notcodebound THEN otherdepends 
				ELSE canignore, modname,filename,
				innards.ftb[innards.mtb[mti].file].version,
				uns];
			};
		};
	mti ← mti + (WITH m: innards.mtb[mti] SELECT FROM
		     direct => SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link],
		     indirect => SIZE[BcdDefs.MTRecord[indirect]],
		     multiple => SIZE[BcdDefs.MTRecord[multiple]],
		    ENDCASE => ERROR);
	IF LOOPHOLE[mti, CARDINAL] > LOOPHOLE[innards.bcd.mtLimit, CARDINAL]
	THEN {
		CWF.WF0["Garbage Module.\n"L];
		RETURN;
		};
	ENDLOOP;
};

-- skip, since there were too many errors with version stamps
-- that were not create dates
-- the dates on .Symbols files are off by up to 30 seconds
GetCorrectCreateDate: PROC[filename: STRING, verstamp: TimeStamp.Stamp]
	RETURNS[stamp: TimeStamp.Stamp] = {
cap: File.Capability;
filetime: LONG CARDINAL;
i,j: INT;
stamp ← verstamp;
-- fix in correct create date
cap ← Directory.Lookup[fileName: filename, permissions: Directory.ignore
	! Directory.Error => GOTO err];
[create: filetime] ← Subr.GetCreateDate[cap];
-- create date here is actually the time stamp
-- if differ by more than 30 seconds, file is no good
i ← filetime;
j ← verstamp.time;
IF IsRealTime[verstamp.time] AND ABS[i - j] > 30 THEN 
	CWF.WF3["Error in symbols file- %s is dated %lt but bound configuration wanted %lt.\n"L, filename,
		@filetime, @verstamp.time]
ELSE stamp.time ← filetime;
EXITS
err => 	NULL;		-- if its not on the disk, we must assume verstamp
};

-- uses heuristics
-- note: 1yr = 31,536,000 seconds
IsRealTime: PUBLIC PROC[stamp: LONG CARDINAL] RETURNS[isrealtime: BOOL] = {
time: LONG CARDINAL ← Time.Current[];
RETURN[time >= stamp AND stamp >= time - 30000000];
};

  PutName: PROC [innards: ProcBcds.Innards, n: BcdDefs.NameRecord, 
	stemp: STRING] =
    BEGIN
    i: INTEGER;
    str: STRING ← [100];
    ssd: LongString.SubStringDescriptor ←
      [base: @innards.ssb.string, offset: n, 
	length: MIN[innards.ssb.size[n], 100]];
    LongString.AppendSubString[str, @ssd];
    -- omit a leading directory name
    i ← IF str.length > 0 THEN str.length - 1 ELSE 0;
    WHILE i >= 0 DO
    	IF str[i] = '> OR str[i] = '/ THEN EXIT;
    	i ← i - 1;
    	ENDLOOP;
    IF i >= 0 THEN 
    	Subr.SubStrCopy[stemp, str, i+1]
    ELSE Subr.strcpy[stemp, str];
    RETURN
    END;

-- if no '.' is present, explicitely append ".bcd" at the end of stemp
  PrintFileName: PROC [innards: ProcBcds.Innards, fti: BcdDefs.FTIndex, 
	stemp: STRING] =
    {
    SELECT fti FROM
      BcdDefs.FTNull => {
		CWF.SWF0[stemp, "(null)"L];
		ERROR;
		};
      BcdDefs.FTSelf => {
		CWF.SWF0[stemp, "(self)"L];
		ERROR;
		};
      ENDCASE => {
		PutName[innards, innards.ftb[fti].name, stemp];
		IF NOT Subr.Any[stemp, '.] THEN
			String.AppendString[stemp, ".bcd"L];
		};
      RETURN
    };
    
    
}.
OldInitSym: PROC[innards: ProcBcds.Innards, sym: Sym, symseg: BcdDefs.SGIndex, less: BOOL,
	bcdfilename: LONG STRING] RETURNS[success: BOOL] = {
b: LONG POINTER;
tB: Table.Base; 

success ← FALSE;
IF innards.sgb[symseg].class ~= symbols THEN {
	CWF.WF0["Error - badly formed symbols.\n"L];
	RETURN;
	}; 
IF innards.sgb[symseg].pages = 0 THEN {
	CWF.WF1["Error - No symbols in %s.\n"L, bcdfilename];
	RETURN;
	};
IF innards.bcdheaderspace ~= Space.nullHandle THEN {
	npages: CARDINAL;
	sfnsym: STRING ← [100];
	cap: File.Capability;
	IF innards.sgb[symseg].file ~= BcdDefs.FTSelf THEN {
		PrintFileName[innards, innards.sgb[symseg].file, sfnsym];
		cap ← Directory.Lookup[fileName: sfnsym, permissions: File.read
			! Directory.Error => GOTO err];
		EXITS
		err => {
			CWF.WF1["Error - can't find '%s'\n"L, sfnsym];
			RETURN;
			};
		}
	ELSE cap ← innards.cap;
	npages ← innards.sgb[symseg].pages;
	IF NOT less THEN npages ← npages + innards.sgb[symseg].extraPages;
	sym.symbolspace ← Space.Create[parent: Space.virtualMemory,
		size: npages];
	Space.Map[space: sym.symbolspace, 
		window: [file: cap, base: innards.sgb[symseg].base]];
	sym.stHandle ← Space.LongPointer[sym.symbolspace];
	}
ELSE {
	IF ((innards.sgb[symseg].base * Environment.wordsPerPage)
	+ SIZE[SymbolSegment.STHeader]) > innards.upperLimit THEN {
		CWF.WF0["Error - symbol segment not in memory.\n"L];
		RETURN;
		};
	sym.stHandle ← LOOPHOLE[innards.bcd +
		((innards.sgb[symseg].base-1) * Environment.wordsPerPage)];
	};
-- init symbols header
b ← LOOPHOLE[sym.stHandle];
tB ← LOOPHOLE[sym.stHandle];
IF sym.stHandle.versionIdent ~= SymbolSegment.VersionID THEN {
	CWF.WF0["Error - Symbols versions do not agree.\n"L];
	RETURN;
	};
sym.ht         ← DESCRIPTOR[b+sym.stHandle.htBlock.offset,
		sym.stHandle.htBlock.size/SIZE[Symbols.HTRecord]];
sym.ssb        ← b + sym.stHandle.ssBlock.offset;
sym.seb        ← tB + sym.stHandle.seBlock.offset;
sym.ctxb       ← tB + sym.stHandle.ctxBlock.offset;
sym.mdb        ← tB + sym.stHandle.mdBlock.offset;
sym.bb         ← tB + sym.stHandle.bodyBlock.offset;
sym.tb         ← tB + sym.stHandle.treeBlock.offset;
sym.ltb        ← tB + sym.stHandle.litBlock.offset;
sym.extb       ← tB + sym.stHandle.extBlock.offset;
sym.mdLimit    ← FIRST[Symbols.MDIndex] + sym.stHandle.mdBlock.size;
sym.extLimit   ← FIRST[SymbolSegment.ExtIndex] + sym.stHandle.extBlock.size;
sym.mainCtx    ← sym.stHandle.outerCtx;
RETURN[TRUE];
};

-- if the file is code-bound, then we don't want to call
-- the modules it depends on
PrintConfigDepends: PROC[innards: ProcBcds.Innards, parent: BcdDefs.CTIndex,
	procDep: ProcBcds.ProcDep,uns: UNSPECIFIED, notcodebound, stopnesting: BOOL] = {
cti: BcdDefs.CTIndex ← FIRST[BcdDefs.CTIndex];
modname: STRING ← [100];
filename: STRING ← [100];

UNTIL cti = innards.bcd.ctLimit DO
	IF innards.ctb[cti].config = parent THEN {
		IF innards.ctb[cti].file ~= BcdDefs.FTSelf THEN {
			PutName[innards, innards.ctb[cti].name, modname];
			PrintFileName[innards, innards.ctb[cti].file, filename];
			procDep[IF notcodebound THEN otherdepends 
				ELSE canignore, modname, filename,
				innards.ftb[innards.ctb[cti].file].version,
				uns];
			};
		-- will only print confignames below this level if stopnesting is true
		-- stopnesting will only be true if the config entry
		-- is not FTSelf, indicating a config bound in a config
		-- and not syntactically nested
		PrintConfigDepends[innards, cti, procDep, uns, notcodebound, 
			NOT notcodebound];
		IF notcodebound OR innards.ctb[cti].file = BcdDefs.FTSelf THEN 
			ProcessModuleTable[innards, cti, procDep, uns, notcodebound];
		};
	cti ← cti + innards.ctb[cti].nControls + SIZE[BcdDefs.CTRecord];
	IF LOOPHOLE[cti, CARDINAL] > LOOPHOLE[innards.bcd.ctLimit, CARDINAL]
	THEN {
		CWF.WF0["Garbage Config.\n"L];
		RETURN;
		};
	ENDLOOP;
};

ProcessModuleTable: PROC[innards: ProcBcds.Innards, cti: BcdDefs.CTIndex,
	procDep: ProcBcds.ProcDep, uns: UNSPECIFIED, notcodebound: BOOL] ={
symseg, codeseg: BcdDefs.SGIndex;
mti: BcdDefs.MTIndex ← FIRST[BcdDefs.MTIndex];
modname: STRING ← [100];
filename: STRING ← [100];

UNTIL mti = innards.bcd.mtLimit DO
	IF innards.mtb[mti].config = cti THEN {
		symseg ← innards.mtb[mti].sseg;
		IF innards.sgb[symseg].class ~= symbols THEN {
			CWF.WF0["Error - not symseg\n"L];
			RETURN;
			};
		IF innards.sgb[symseg].file ~= BcdDefs.FTSelf
		AND innards.sgb[symseg].file ~= BcdDefs.FTNull THEN {
			PrintFileName[innards, innards.sgb[symseg].file,
				filename];
			PutName[innards, innards.mtb[mti].name,modname];
			IF NOT String.EquivalentString[filename, modname]
			THEN  {
				symfilevers: TimeStamp.Stamp;
				-- symfilevers ← GetCorrectCreateDate[filename,
					-- innards.ftb[
					-- innards.sgb[symseg].file].version];
				symfilevers ← innards.ftb[innards.sgb[symseg].file].version;
				procDep[symbolsfile, modname, filename,
					symfilevers, uns];
				};
			};
		IF innards.mtb[mti].file ~= BcdDefs.FTSelf
		AND innards.mtb[mti].file ~= BcdDefs.FTNull THEN {
			codeseg ← innards.mtb[mti].code.sgi;
			IF innards.sgb[codeseg].class ~= code THEN {
				CWF.WF0["Error - not code seg\n"L];
				RETURN;
				};
			PutName[innards, innards.mtb[mti].name, modname];
			PrintFileName[innards, innards.mtb[mti].file, filename];
			procDep[IF notcodebound THEN otherdepends 
				ELSE canignore, modname,filename,
				innards.ftb[innards.mtb[mti].file].version,
				uns];
			};
		};
	mti ← mti + (WITH m: innards.mtb[mti] SELECT FROM
		     direct => SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link],
		     indirect => SIZE[BcdDefs.MTRecord[indirect]],
		     multiple => SIZE[BcdDefs.MTRecord[multiple]],
		    ENDCASE => ERROR);
	IF LOOPHOLE[mti, CARDINAL] > LOOPHOLE[innards.bcd.mtLimit, CARDINAL]
	THEN {
		CWF.WF0["Garbage Module.\n"L];
		RETURN;
		};
	ENDLOOP;
};