-- BLList.mesa -- last edited by Satterthwaite on September 15, 1982 5:35 pm DIRECTORY BcdDefs: TYPE USING [ Base, BCD, Link, ControlItem, CTIndex, CTNull, CTRecord, EXPIndex, EXPRecord, EVIndex, EVNull, EVRecord, FPIndex, FPRecord, FTIndex, FTNull, FTRecord, FTSelf, IMPIndex, IMPRecord, LFIndex, LFNull, MTIndex, MTRecord, Namee, NameRecord, NTIndex, NTNull, NTRecord, NullName, NullLink, RFIndex, RFNull, SGIndex, SGNull, SGRecord, SpaceID, SPIndex, SPRecord, TFIndex, TFNull, TYPNull, VersionID, VersionStamp], BcdOps: TYPE USING [BcdBase, MTHandle, NameString], CharIO: TYPE USING [PutChar, PutDecimal, PutOctal, PutString, PutSubString], FileSegment: TYPE USING [Pages, Span], ListerOps: TYPE USING [], ListerUtil: TYPE USING [ CreateStream, MapPages, Message, PrintRTBcd, PutTime, PutVersionId, SetFileName, TTYStream], OSMiscOps: TYPE USING [FileError, FindFile], Space: TYPE USING [Error, Handle, LongPointer, Delete], Stream: TYPE USING [Handle, Delete], Strings: TYPE USING [String, SubStringDescriptor]; BLList: PROGRAM IMPORTS CharIO, ListerUtil, OSMiscOps, Space, Stream EXPORTS ListerOps = { OPEN BcdDefs; -- output streams out: Stream.Handle ← NIL; OpenOutput: PROC [root: Strings.String] = { outName: STRING ← [40]; ListerUtil.SetFileName[outName, root, "bl"L]; out ← ListerUtil.CreateStream[outName]}; CloseOutput: PROC = { Stream.Delete[out]; out ← NIL}; -- table bases bcdSpace: Space.Handle; bcd: BcdOps.BcdBase; tb: BcdDefs.Base; ssb: BcdOps.NameString; evb: BcdDefs.Base; spb: BcdDefs.Base; fpb: BcdDefs.Base; ctb: BcdDefs.Base; mtb: BcdDefs.Base; lfb: BcdDefs.Base; tfb: BcdDefs.Base; rfb: BcdDefs.Base; itb: BcdDefs.Base; etb: BcdDefs.Base; sgb: BcdDefs.Base; ftb: BcdDefs.Base; ntb: BcdDefs.Base; -- a more tolerant version of ListerUtil.LoadBcd defaultSpan: FileSegment.Span = [base: 1, pages: 10]; -- default estimate InstallBcd: PROC [fileName: Strings.String, span: FileSegment.Span] = { seg: FileSegment.Pages; seg ← [ file: OSMiscOps.FindFile[fileName, ! OSMiscOps.FileError => {GO TO noFile}], span: span]; DO bcdSpace ← ListerUtil.MapPages[seg]; bcd ← bcdSpace.LongPointer; IF bcd.nPages <= seg.span.pages OR seg.span.pages >= 256 THEN EXIT; seg.span.pages ← MIN[bcd.nPages, 256]; Space.Delete[bcdSpace]; ENDLOOP; tb ← LOOPHOLE[bcd]; ssb ← LOOPHOLE[bcd + bcd.ssOffset]; ctb ← tb + bcd.ctOffset; mtb ← tb + bcd.mtOffset; IF bcd.extended THEN { lfb ← tb + bcd.lfOffset; tfb ← tb + bcd.tfOffset; rfb ← tb + bcd.rfOffset}; itb ← tb + bcd.impOffset; etb ← tb + bcd.expOffset; sgb ← tb + bcd.sgOffset; ftb ← tb + bcd.ftOffset; ntb ← tb + bcd.ntOffset; evb ← tb + bcd.evOffset; spb ← tb + bcd.spOffset; fpb ← tb + bcd.fpOffset EXITS noFile => bcd ← NIL}; UnstallBcd: PROC [] = { Space.Delete[bcdSpace]}; WriteBcdID: PROC [name: Strings.String, bcd: BcdOps.BcdBase] = { PutString[name]; PutString[", version "L]; ListerUtil.PutVersionId[out, bcd.version]; IF bcd.source # NullName THEN { PutString["\n source "L]; PutName[bcd.source]; PutString[" of "L]; ListerUtil.PutTime[out, bcd.sourceVersion.time]}; IF bcd.versionIdent # BcdDefs.VersionID THEN { PutString["\n (obsolete) version ID = "L]; PutDecimal[bcd.versionIdent]}; PutString["\n creator "L]; ListerUtil.PutVersionId[out, bcd.creator]; PutString["\n\n"L]}; PrintStamps: PROC = { PutString["Imports:\n\n"L]; FOR iti: IMPIndex ← IMPIndex.FIRST, iti + IMPRecord.SIZE UNTIL iti = bcd.impLimit DO OPEN ii: itb[iti]; IF LOOPHOLE[iti, CARDINAL] > LOOPHOLE[bcd.impLimit, CARDINAL] THEN GO TO Bogus; IF ii.namedInstance THEN {PutInstanceName[[import[iti]]]; PutString[": "L]}; PutName[ii.name]; PutFileStamp[ii.file, ii.name]; REPEAT Bogus => PrintGarbage[]; ENDLOOP; PutChar['\n]; PutString["Exports:\n\n"L]; FOR eti: EXPIndex ← EXPIndex.FIRST, eti + etb[eti].size + EXPRecord.SIZE UNTIL eti = bcd.expLimit DO OPEN ee: etb[eti]; IF LOOPHOLE[eti, CARDINAL] > LOOPHOLE[bcd.expLimit, CARDINAL] THEN GO TO Bogus; IF ee.namedInstance THEN {PutInstanceName[[export[eti]]]; PutString[": "L]}; PutName[ee.name]; PutFileStamp[ee.file, ee.name]; REPEAT Bogus => PrintGarbage[]; ENDLOOP; PutString["\nModules:\n\n"L]; FOR mti: MTIndex ← MTIndex.FIRST, mti + MTSize[mti] UNTIL mti = bcd.mtLimit DO OPEN mm: mtb[mti]; IF LOOPHOLE[mti, CARDINAL] > LOOPHOLE[bcd.mtLimit, CARDINAL] THEN GO TO Bogus; IF mm.namedInstance THEN {PutInstanceName[[module[mti]]]; PutString[": "L]}; PutName[mm.name]; PutFileStamp[mm.file, mm.name]; REPEAT Bogus => PrintGarbage[]; ENDLOOP}; PutFileStamp: PROC [fti: FTIndex, mName: NameRecord] = { OPEN ftb[fti]; SELECT fti FROM FTNull => PutString["(null)"L]; FTSelf => PutString["(self)"L]; ENDCASE => { IF name # mName THEN {PutString[", file: "L]; PutName[name]}; PutString[", version: "L]; ListerUtil.PutVersionId[out, version]}; PutChar['\n]}; dumpLinks: {none, rt, all} ← none; PrintBcd: PROC = { PrintHeader[]; PrintConfigs[]; PrintImports[]; PrintExports[]; PrintExpVars[]; PrintModules[]; PrintFiles[]; PrintFramePacks[]; PrintSpaces[]}; PrintHeader: PROC = { PutString["Configurations: "L]; PutDecimal[bcd.nConfigs]; PutString[", Modules: "L]; PutDecimal[bcd.nModules]; PutString[", Imports: "L]; PutDecimal[bcd.nImports]; PutString[", Exports: "L]; PutDecimal[bcd.nExports]; PutString[", Dummy: "L]; PutDecimal[bcd.firstdummy]; PutString[", #Dummies: "L]; PutDecimal[bcd.nDummies]; PutChar['\n]; IF ~bcd.definitions THEN PutChar['~]; PutString["definitions, "L]; IF ~bcd.repackaged THEN PutChar['~]; PutString["repackaged, "L]; IF ~bcd.typeExported THEN PutChar['~]; PutString["type exported, "L]; IF ~bcd.tableCompiled THEN PutChar['~]; PutString["table compiled, "L]; IF ~bcd.versions THEN PutChar['~]; PutString["versions, "L]; IF ~bcd.extended THEN PutChar['~]; PutString["extended\n\n"L]}; PrintConfigs: PROC = { cti: CTIndex ← CTIndex.FIRST; PutString["Configurations"L]; PrintIndex[bcd.ctOffset]; PutString[":\n"L]; UNTIL cti = bcd.ctLimit DO PrintConfig[cti]; cti ← cti + CTRecord.SIZE + ctb[cti].nControls*ControlItem.SIZE; IF LOOPHOLE[cti, CARDINAL] > LOOPHOLE[bcd.ctLimit, CARDINAL] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; PutChar['\n]}; PrintConfig: PROC [cti: CTIndex] = { OPEN ctb[cti]; Tab[2]; PutName[name]; PrintIndex[cti]; IF namedInstance THEN { PutString[", instance name: "L]; PutInstanceName[[config[cti]]]}; PutString[", file: "L]; PrintFileName[file]; PrintIndex[file]; IF config # CTNull THEN { PutString[", parent: "L]; PutName[ctb[config].name]; PrintIndex[config]}; PutString[", #controls: "L]; PutDecimal[nControls]; IF nControls # 0 THEN { PutString[", controls:"L]; FOR i: CARDINAL IN [0..nControls) DO IF i MOD 6 = 0 THEN Tab[6] ELSE PutString[", "L]; WITH c: controls[i] SELECT FROM module => PutName[mtb[c.mti].name]; config => {PutName[ctb[c.cti].name]; PutChar['*]}; ENDCASE => ERROR; PrintIndex[controls[i]]; ENDLOOP}; PutChar['\n]}; PrintImports: PROC = { iti: IMPIndex ← IMPIndex.FIRST; PutString["Imports"L]; PrintIndex[bcd.impOffset]; PutChar[':]; PutChar['\n]; UNTIL iti = bcd.impLimit DO PrintImport[iti]; iti ← iti + IMPRecord.SIZE; IF LOOPHOLE[iti, CARDINAL] > LOOPHOLE[bcd.impLimit, CARDINAL] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; PutChar['\n]; PutChar['\n]}; PrintImport: PROC [iti: IMPIndex] = { OPEN itb[iti]; Tab[2]; PutName[name]; PrintIndex[iti]; IF port = $module THEN PutString[" (module)"L]; IF namedInstance THEN { PutString[", instance name: "L]; PutInstanceName[[import[iti]]]}; PutString[", file: "L]; PrintFileName[file]; PrintIndex[file]; PutString[", gfi: "L]; PutDecimal[gfi]; PutString[", ngfi: "L]; PutDecimal[ngfi]}; PrintExports: PROC = { eti: EXPIndex ← EXPIndex.FIRST; PutString["Exports"L]; PrintIndex[bcd.expOffset]; PutChar[':]; PutChar['\n]; UNTIL eti = bcd.expLimit DO PrintExport[eti]; eti ← eti + etb[eti].size + EXPRecord.SIZE; IF LOOPHOLE[eti, CARDINAL] > LOOPHOLE[bcd.expLimit, CARDINAL] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; IF dumpLinks # all THEN PutChar['\n]; PutChar['\n]}; PrintExport: PROC [eti: EXPIndex] = { OPEN etb[eti]; Tab[2]; PutName[name]; PrintIndex[eti]; IF port = $module THEN PutString[" (module)"L]; IF namedInstance THEN { PutString[", instance name: "L]; PutInstanceName[[export[eti]]]}; PutString[", file: "L]; PrintFileName[file]; PrintIndex[file]; PutString[", "L]; IF ~typeExported THEN PutChar['~]; PutString["typeExported"L]; PutString[", #links: "L]; PutDecimal[size]; IF dumpLinks = all THEN { PutString[", links:"L]; FOR i: CARDINAL IN [0..size) DO IF i MOD 7 = 0 THEN Tab[4] ELSE PutChar[' ]; PrintControlLink[links[i]]; IF i + 1 # size THEN PutChar[',]; ENDLOOP}; IF dumpLinks = all THEN PutChar['\n]}; PrintExpVars: PROC = { evi: EVIndex ← EVIndex.FIRST; evLimit: EVIndex = bcd.evLimit; PutString["Exported variables:\n"L]; UNTIL evi = evLimit DO PrintExpVar[evi]; evi ← evi + evb[evi].length + EVRecord.SIZE; ENDLOOP; PutChar['\n]}; PrintExpVar: PROC [evi: EVIndex] = { OPEN evb[evi]; Tab[2]; PrintIndex[evi]; PutString[", #offsets: "L]; PutDecimal[length]; PutString[", offsets:"L]; FOR i: CARDINAL IN [1..length] DO IF i MOD 8 = 1 THEN Tab[4] ELSE PutChar[' ]; PutOctal[offsets[i]]; IF i # length THEN PutChar[',]; ENDLOOP; PutChar['\n]}; PrintSpaces: PROC = { spi: SPIndex ← SPIndex.FIRST; spLimit: SPIndex = bcd.spLimit; PutString["Spaces:\n"L]; UNTIL spi = spLimit DO PrintSpace[spi]; spi ← spi + SPRecord.SIZE + spb[spi].length*SpaceID.SIZE; ENDLOOP; PutChar['\n]}; PrintSpace: PROC [spi: SPIndex] = { OPEN spb[spi]; Tab[2]; PrintIndex[spi]; PutString[", segment: "L]; PrintIndex[seg]; PutString[", #code packs: "L]; PutDecimal[length]; IF length # 0 THEN PutString[", code packs: "L]; FOR i: CARDINAL IN [0..length) DO Tab[4]; PutString[" code pack "L]; PutName[spaces[i].name]; PutString[", "L]; IF ~spaces[i].resident THEN PutChar['~]; PutString["resident, offset: "L]; PutOctal[spaces[i].offset]; PutString[", pages: "L]; PutDecimal[spaces[i].pages]; PutChar['\n]; ENDLOOP}; PrintModules: PROC = { mti: MTIndex ← MTIndex.FIRST; PutString["Modules"L]; PrintIndex[bcd.mtOffset]; PutString[":\n"L]; UNTIL mti = bcd.mtLimit DO PrintModule[@mtb[mti], mti]; mti ← mti + MTSize[mti]; IF LOOPHOLE[mti, CARDINAL] > LOOPHOLE[bcd.mtLimit, CARDINAL] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; PutChar['\n]}; PrintModule: PROC [mth: BcdOps.MTHandle, mti: MTIndex] = { OPEN mth; Tab[2]; PutName[name]; PrintIndex[mti]; IF namedInstance THEN { PutString["instance name: "L]; PutInstanceName[[module[mti]]]}; PutString[", file: "L]; PrintFileName[file]; PrintIndex[file]; IF config # CTNull THEN { PutString[", config: "L]; PutName[ctb[config].name]; PrintIndex[config]}; Tab[4]; IF tableCompiled THEN PutString["table compiled, "L] ELSE { PutSwitch: PROC [sw: CHAR, value: BOOL] = { IF ~value THEN PutChar['-]; PutChar[sw]}; PutString["switches: "L]; PutSwitch['b, boundsChecks]; PutSwitch['c, long]; PutSwitch['j, crossJumped]; PutSwitch['l, linkLoc = $code]; PutSwitch['n, nilChecks]; PutSwitch['s, ~initial]; PutString[", "L]}; IF ~packageable THEN PutChar['~]; PutString["packageable, "L]; IF residentFrame THEN PutString["resident frame, "L]; Tab[4]; PutString["framesize: "L]; PutDecimal[framesize]; PutString[", gfi: "L]; PutDecimal[gfi]; PutString[", ngfi: "L]; PutDecimal[ngfi]; PutString[", links: "L]; PutString[IF linkLoc = $frame THEN "frame"L ELSE "code"L]; Tab[4]; PutString["code: "L]; PrintSegment[code.sgi]; PutString[", offset: "L]; PutOctal[code.offset]; PutString[", length: "L]; PutOctal[code.length]; IF code.linkspace THEN PutString[", link space"L]; IF code.packed THEN PutString[", packed"L]; Tab[4]; PutString["symbols: "L]; PrintSegment[sseg]; IF variables # EVNull THEN { Tab[4]; PutString["exported variables: "L]; PrintIndex[variables]}; WITH mm: mth↑ SELECT FROM direct => { Tab[4]; PutString["#links: "L]; PutDecimal[mm.length]; IF dumpLinks = all THEN { PutString[", links:"L]; FOR i: CARDINAL IN [0..mm.length) DO IF i MOD 7 = 0 THEN Tab[6] ELSE PutChar[' ]; PrintControlLink[mm.frag[i]]; IF i + 1 # mm.length THEN PutChar[',]; ENDLOOP}}; indirect => {Tab[4]; PrintLinks[mm.links]}; multiple => { Tab[4]; PrintLinks[mm.links]; Tab[4]; PrintTypes[mm.types]; IF mm.frameRefs THEN { Tab[5]; PutString["frame type: "L]; PutDecimal[mm.frameType]}; Tab[4]; PrintRefLits[mm.refLiterals]}; ENDCASE; PutChar['\n]}; MTSize: PROC [mti: MTIndex] RETURNS [NAT] = { RETURN [WITH m: mtb[mti] SELECT FROM direct => MTRecord.direct.SIZE + m.length*Link.SIZE, indirect => MTRecord.indirect.SIZE, multiple => MTRecord.multiple.SIZE, ENDCASE => ERROR]}; PrintLinks: PROC [lfi: LFIndex] = { PutString["#links: "L]; IF lfi = LFNull THEN PutString["none"L] ELSE { PutDecimal[lfb[lfi].length]; IF dumpLinks = all THEN { PutString[", links:"L]; FOR i: CARDINAL IN [0..lfb[lfi].length) DO IF i MOD 7 = 0 THEN Tab[6] ELSE PutChar[' ]; PrintControlLink[lfb[lfi].frag[i]]; IF i + 1 # lfb[lfi].length THEN PutChar[',]; ENDLOOP}}}; PrintTypes: PROC [tfi: TFIndex] = { PutString["#types: "L]; IF tfi = TFNull THEN PutString["none"L] ELSE { PutDecimal[tfb[tfi].length]; PutString[", offset: "L]; PutDecimal[tfb[tfi].offset]; IF dumpLinks # none THEN { PutString[", indices:"L]; FOR i: CARDINAL IN [0..tfb[tfi].length) DO IF i MOD 7 = 0 THEN Tab[6] ELSE PutChar[' ]; PrintRTIndex[tfb[tfi].frag[i]]; IF i + 1 # tfb[tfi].length THEN PutChar[',]; ENDLOOP}}}; PrintRefLits: PROC [rfi: RFIndex] = { PutString["#ref lits: "L]; IF rfi = RFNull THEN PutString["none"L] ELSE { PutDecimal[rfb[rfi].length]; PutString[", offset: "L]; PutDecimal[rfb[rfi].offset]; IF dumpLinks # none THEN { PutString[", indices:"L]; FOR i: CARDINAL IN [0..rfb[rfi].length) DO IF i MOD 7 = 0 THEN Tab[6] ELSE PutChar[' ]; PrintRTIndex[rfb[rfi].frag[i]]; IF i + 1 # rfb[rfi].length THEN PutChar[',]; ENDLOOP}}}; PrintFramePacks: PROC = { fpi: FPIndex ← FPIndex.FIRST; fpLimit: FPIndex = bcd.fpLimit; PutString["Frame Packs:\n"L]; UNTIL fpi = fpLimit DO PrintFramePack[fpi]; fpi ← fpi + FPRecord.SIZE + fpb[fpi].length*MTIndex.SIZE; ENDLOOP; PutChar['\n]}; PrintFramePack: PROC [fpi: FPIndex] = { OPEN fpb[fpi]; Tab[2]; PutName[name]; PrintIndex[fpi]; PutString[", #modules: "L]; PutDecimal[length]; PutString[", modules:\n"L]; FOR i: CARDINAL IN [0..length) DO IF i MOD 4 = 0 THEN Tab[4] ELSE PutChar[' ]; PutName[mtb[modules[i]].name]; PrintIndex[modules[i]]; IF i # length - 1 THEN PutChar[',]; ENDLOOP; PutChar['\n]}; PrintSegment: PROC [sgi: SGIndex] = { IF sgi = BcdDefs.SGNull THEN PutString["(null)"L] ELSE { PrintFileName[sgb[sgi].file]; PutString[" [base: "L]; PutDecimal[sgb[sgi].base]; PutString[", pages: "L]; PutDecimal[sgb[sgi].pages]; IF sgb[sgi].extraPages # 0 THEN {PutChar['+]; PutDecimal[sgb[sgi].extraPages]}; PutChar[']]}}; PrintFiles: PROC = { fti: FTIndex ← FTIndex.FIRST; PutString["Files"L]; PrintIndex[bcd.ftOffset]; PutString[":\n"L]; UNTIL fti = bcd.ftLimit DO PrintFile[fti]; fti ← fti + FTRecord.SIZE; IF LOOPHOLE[fti, CARDINAL] > LOOPHOLE[bcd.ftLimit, CARDINAL] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; PutChar['\n]; PutChar['\n]}; PrintFile: PROC [fti: FTIndex] = { OPEN ftb[fti]; Tab[2]; SELECT fti FROM FTNull => PutString["(null)"L]; FTSelf => PutString["(self)"L]; ENDCASE => { PutName[name]; PrintIndex[fti]; PutString[", version: "L]; ListerUtil.PutVersionId[out, version]}}; PrintRT: PROC = {PrintRTBcdExt[FALSE]}; PrintRTSorted: PROC = {PrintRTBcdExt[TRUE]}; PrintRTBcdExt: PROC [sorted: BOOL] = { PrintHeader[]; PrintConfigs[]; PrintModules[]; IF ~bcd.extended OR bcd.rtPages.pages = 0 THEN PutString["No RT Extensions"L] ELSE { ListerUtil.PrintRTBcd[out, bcd, sorted]; PrintSymbolSegments[]; PrintFiles[]}; PutChar['\n]; PutChar['\n]}; PrintSymbolSegments: PROC = { sgi: SGIndex ← SGIndex.FIRST; PutString["Symbol Segments\n"L]; UNTIL sgi = bcd.sgLimit DO IF sgb[sgi].class = $symbols THEN { Tab[1]; PrintIndex[sgi]; PutChar[' ]; PrintSegment[sgi]}; sgi ← sgi + SGRecord.SIZE; ENDLOOP; PutChar['\n]; PutChar['\n]}; -- Utility Prints PrintControlLink: PROC [link: Link] = { SELECT TRUE FROM (link = BcdDefs.NullLink) => PutString["(null link)"L]; link.proc => { PutString["proc["L]; PutDecimal[link.gfi]; PutChar[',]; PutDecimal[link.ep]; PutChar[']]}; link.type => { PutString["type["L]; IF link.typeID = BcdDefs.TYPNull THEN PutString["null"L] ELSE PutDecimal[LOOPHOLE[link.typeID]]; PutChar[']]}; ENDCASE => { PutString["var["L]; PutDecimal[link.vgfi]; PutChar[',]; PutDecimal[link.var]; PutChar[']]}}; PrintRTIndex: PROC [index: NAT] = { PutChar['[]; PutDecimal[index]; PutChar[']]}; PrintFileName: PROC [fti: FTIndex] = { SELECT fti FROM FTNull => PutString["(null)"L]; FTSelf => PutString["(self)"L]; ENDCASE => PutName[ftb[fti].name]}; PrintIndex: PROC [index: UNSPECIFIED] = { PutString[" ["L]; PutDecimal[index]; PutChar[']]}; PrintGarbage: PROC = { Tab[2]; PutString["? Looks like garbage ...\n"L]}; PrintAnonName: PROC = {PutString[" (anon) "L]}; Tab: PROC [n: CARDINAL] = { PutChar['\n]; THROUGH [1..n/8] DO PutChar['\t] ENDLOOP; THROUGH [1..n MOD 8] DO PutChar[' ] ENDLOOP}; -- Utility Puts PutChar: PROC [c: CHAR] = INLINE {CharIO.PutChar[out, c]}; PutString: PROC [s: Strings.String] = INLINE {CharIO.PutString[out, s]}; PutDecimal: PROC [i: INTEGER] = INLINE {CharIO.PutDecimal[out, i]}; PutOctal: PROC [n: UNSPECIFIED] = INLINE {CharIO.PutOctal[out, n]}; PutName: PROC [n: NameRecord] = { ssd: Strings.SubStringDescriptor ← [base: @ssb.string, offset: n, length: MIN[ssb.size[n], 100]]; CharIO.PutSubString[out, @ssd]}; PutInstanceName: PROC [n: Namee] = { FindName: PROC [ntb: Base, nti: NTIndex] RETURNS [stop: BOOL] = { RETURN [ntb[nti].item = n]}; nti: NTIndex = EnumerateNameTable[FindName]; IF nti = NTNull THEN PrintAnonName[] ELSE PutName[ntb[nti].name]}; EnumerateNameTable: PROC [ proc: PROC [Base, NTIndex] RETURNS [BOOL]] RETURNS [nti: NTIndex] = { FOR nti ← NTIndex.FIRST, nti + NTRecord.SIZE UNTIL nti = bcd.ntLimit DO IF proc[ntb, nti] THEN RETURN[nti]; ENDLOOP; RETURN [NTNull]}; ListVersion: PUBLIC PROC [root: Strings.String] = { fileName: Strings.String ← [100]; ListerUtil.SetFileName[fileName, root, "bcd"L]; InstallBcd[fileName, defaultSpan]; IF bcd = NIL THEN ListerUtil.Message["File not found"L] ELSE { out ← ListerUtil.TTYStream[]; PutChar['\n]; PutString[fileName]; PutString[", version "L]; ListerUtil.PutVersionId[out, bcd.version]; IF bcd.versionIdent # VersionID THEN { PutString["\n (obsolete) version ID = "L]; PutDecimal[bcd.versionIdent]} ELSE IF bcd.source # BcdDefs.NullName THEN { PutString["\n source "L]; PutName[bcd.source]; PutString[" of "L]; ListerUtil.PutTime[out, bcd.sourceVersion.time]}; PutString["\n creator "L]; ListerUtil.PutVersionId[out, bcd.creator]; PutChar['\n]; Stream.Delete[out]; out ← NIL; UnstallBcd[]}}; BcdProc: PROC [root: Strings.String, span: FileSegment.Span, proc: PROC] = { fileName: Strings.String ← [100]; ListerUtil.SetFileName[fileName, root, "bcd"L]; InstallBcd[fileName, span]; IF bcd = NIL THEN ListerUtil.Message["File not found"L] ELSE { OpenOutput[root]; WriteBcdID[fileName, bcd]; IF bcd.versionIdent # BcdDefs.VersionID THEN ListerUtil.Message["Obsolete format, ouput may be garbage"L]; proc[]; CloseOutput[]; UnstallBcd[]}}; ListStamps: PUBLIC PROC [root: Strings.String] = { BcdProc[root, [1, 10], PrintStamps]}; ListFiles: PUBLIC PROC [root: Strings.String] = { BcdProc[root, defaultSpan, PrintFiles]}; BcdSegment: PUBLIC PROC [ root: Strings.String, span: FileSegment.Span, links: BOOL] = { dumpLinks ← IF links THEN all ELSE none; BEGIN BcdProc[root, span, PrintBcd ! Space.Error => {GO TO BadSegment}]; EXITS BadSegment => ListerUtil.Message["Bad Segment"L]; END; dumpLinks ← none}; ListRTBcd: PUBLIC PROC [root: Strings.String, sorted: BOOL] = { dumpLinks ← rt; BcdProc[root, defaultSpan, IF sorted THEN PrintRTSorted ELSE PrintRT]; dumpLinks ← none}; ListBcd: PUBLIC PROC [root: Strings.String, links: BOOL] = { IF links THEN dumpLinks ← all; BcdProc[root, defaultSpan, PrintBcd]; dumpLinks ← none}; }.