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], Directory: TYPE USING [Error, Handle, 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, EquivalentString, SubString, SubStringDescriptor], ProcBcds: TYPE USING [Innards, ProcDep, ProcMod], RTBcd: TYPE USING[RTBase], Space: TYPE USING [Create, CreateUniformSwapUnits, Delete, Handle, LongPointer, Map, nullHandle, virtualMemory], Subr: TYPE USING [AllocateString, Any, ControlChars, FreeString, 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, Subr, Time EXPORTS ProcBcds = { useonespace: BOOL = TRUE; -- FALSE has never worked Sym: TYPE = POINTER TO SymRecord; SymRecord: TYPE = RECORD[ stHandle: LONG POINTER TO SymbolSegment.STHeader _ NIL, stHandleSpace: Space.Handle _ Space.nullHandle, 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 ]; 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; 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 { 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]; 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] = { uns: UNSPECIFIED; 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; sourcefile: LONG STRING _ Subr.AllocateString[100]; interfacename: LONG STRING _ Subr.AllocateString[100]; switches: LONG STRING _ Subr.AllocateString[100]; {ENABLE UNWIND => { Subr.FreeString[sourcefile]; Subr.FreeString[interfacename]; Subr.FreeString[switches]}; 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]; GO TO return; }; PutName[innards, innards.bcd.source, sourcefile]; IF sourcefile.length >0 AND sourcefile[sourcefile.length -1] = '. THEN sourcefile.length _ sourcefile.length - 1; IF Subr.ControlChars[sourcefile] THEN { CWF.WF0[" Error - bad sourcefile.\n"L]; GO TO return; }; success _ GetModuleName[innards, interfacename]; IF NOT success THEN GO TO return; 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 { 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; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[sourcefile]; Subr.FreeString[interfacename]; Subr.FreeString[switches]; }; PrintDirectory: PROC [innards: ProcBcds.Innards, procDep: ProcBcds.ProcDep, uns: UNSPECIFIED, print: BOOL, bcdfilename: LONG STRING] RETURNS [ntype: CARDINAL] = { modulename: LONG STRING _ Subr.AllocateString[100]; filename: LONG STRING _ Subr.AllocateString[100]; fti: BcdDefs.FTIndex _ FIRST[BcdDefs.FTIndex]; {ENABLE UNWIND => {Subr.FreeString[modulename]; Subr.FreeString[filename]}; ntype _ 0; UNTIL fti = innards.bcd.ftLimit DO PutName[innards, innards.ftb[fti].name, filename]; IF NOT Subr.Any[filename, '.] THEN LongString.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]; GO TO return; }; ENDLOOP; IF print THEN CWF.WF1[" %d types.\n"L, @ntype]; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[modulename]; Subr.FreeString[filename]; }; GuessModulename: PROC [innards: ProcBcds.Innards, fti: BcdDefs.FTIndex, modulename: LONG STRING] RETURNS[LONG 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]; }; 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: LONG STRING _ Subr.AllocateString[100]; nullstr: STRING _ ""L; {ENABLE UNWIND => {Subr.FreeString[shortstr]}; 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; }; -- of ENABLE UNWIND Subr.FreeString[shortstr]; RETURN[success, ntype]; }; InitSym: PROC[innards: ProcBcds.Innards, sym: Sym, symseg: BcdDefs.SGIndex, less: BOOL, bcdfilename: LONG STRING] RETURNS[success: BOOL _ FALSE] = { npages: CARDINAL; cap: File.Capability; local: BOOL; base: CARDINAL; p: LONG POINTER; sfnsym: LONG STRING _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[sfnsym]}; IF innards.sgb[symseg].class ~= symbols THEN { CWF.WF0["Error - badly formed symbols.\n"L]; GO TO return; }; IF innards.sgb[symseg].pages = 0 THEN { CWF.WF1["Warning - No symbols in %s.\n"L, bcdfilename]; GO TO 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]; GO TO 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]; GO TO return; }; 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]; }; success _ TRUE; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[sfnsym]; }; 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] = { mdi: Symbols.MDIndex _ FIRST[Symbols.MDIndex]; sfn: LONG STRING _ Subr.AllocateString[100]; smodulename: LONG STRING _ Subr.AllocateString[100]; substr: LongString.SubStringDescriptor; {ENABLE UNWIND => {Subr.FreeString[sfn]; Subr.FreeString[smodulename]}; ntype _ 0; success _ FALSE; DO 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 "." 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]; GO TO return; }; ENDLOOP; IF print THEN CWF.WF1["%u types, "L, @ntype]; success _ TRUE; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[sfn]; Subr.FreeString[smodulename]; }; FindSourceFileName: PROC[sym: Sym, tostr: LONG STRING] = { IF sym.stHandle.fgRelPgBase > 0 AND sym.stHandle.fgPgCount > 0 THEN { tostr.length _ 0; LongString.AppendString[tostr, @sym.fgptr.sourceFile]; IF tostr.length > 0 AND tostr[tostr.length-1] = '. THEN tostr.length _ tostr.length - 1; -- strip "." } ELSE tostr.length _ 0; }; 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: LONG STRING] RETURNS[success: BOOL] = { 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]; 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] = { OPEN innards.etb[eti]; stemp: LONG STRING _ Subr.AllocateString[100]; stemp1: LONG STRING _ Subr.AllocateString[100]; vers: BcdDefs.VersionStamp; {ENABLE UNWIND => {Subr.FreeString[stemp]; Subr.FreeString[stemp1]}; 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]; GO TO return; }; vers _ innards.ftb[file].version; procDep[exports, stemp, stemp1, vers, uns]; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[stemp]; Subr.FreeString[stemp1]; }; 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: LONG STRING _ Subr.AllocateString[100]; stemp1: LONG STRING _ Subr.AllocateString[100]; vers: BcdDefs.VersionStamp; {ENABLE UNWIND => {Subr.FreeString[stemp]; Subr.FreeString[stemp1]}; 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]; GO TO return; }; vers _ innards.ftb[file].version; procDep[imports, stemp, stemp1, vers, uns]; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[stemp]; Subr.FreeString[stemp1]; }; 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; }; PrintConfigDepends: PROC[innards: ProcBcds.Innards, procDep: ProcBcds.ProcDep,uns: UNSPECIFIED, notcodebound, stopnesting: BOOL] = { cti: BcdDefs.CTIndex _ FIRST[BcdDefs.CTIndex]; modname: LONG STRING _ Subr.AllocateString[100]; filename: LONG STRING _ Subr.AllocateString[100]; topcti: BcdDefs.CTIndex; ok: BOOL; {ENABLE UNWIND => {Subr.FreeString[modname]; Subr.FreeString[filename]}; 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]; GO TO return; }; ENDLOOP; ProcessModuleTable[innards, procDep, uns, notcodebound]; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[modname]; Subr.FreeString[filename]; }; ProcessModuleTable: PROC [innards: ProcBcds.Innards, procDep: ProcBcds.ProcDep, uns: UNSPECIFIED, notcodebound: BOOL] = { symseg, codeseg: BcdDefs.SGIndex; mti: BcdDefs.MTIndex _ FIRST[BcdDefs.MTIndex]; modname: LONG STRING _ Subr.AllocateString[100]; filename: LONG STRING _ Subr.AllocateString[100]; cti: BcdDefs.CTIndex; {ENABLE UNWIND => {Subr.FreeString[modname]; Subr.FreeString[filename]}; 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]; GO TO 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 LongString.EquivalentString[filename, modname] THEN { symfilevers: TimeStamp.Stamp; 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]; GO TO 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]; GO TO return; }; ENDLOOP; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[modname]; Subr.FreeString[filename]; }; 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: LONG STRING] = { i: INTEGER; str: LONG STRING _ Subr.AllocateString[100]; ssd: LongString.SubStringDescriptor _ [base: @innards.ssb.string, offset: n, length: MIN[innards.ssb.size[n], 100]]; {ENABLE UNWIND => {Subr.FreeString[str]}; LongString.AppendSubString[str, @ssd]; 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]; }; -- of ENABLE UNWIND Subr.FreeString[str]; }; PrintFileName: PROC [innards: ProcBcds.Innards, fti: BcdDefs.FTIndex, stemp: LONG 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 LongString.AppendString[stemp, ".bcd"L]; }; RETURN }; }. ¨ProcBcdsImpl.Mesa last edit April 5, 1982 10:59 am last edit May 23, 1983 4:36 pm, Russ Atkinson (short STRING to LONG STRING) hash table map in at least one page to get total # of pages not a bcd or invalid version ID in header now map in the right number of pages if it is a defs file, we simply record the interface name CWF.WF1["sourcefiles %s\n"L, sourcefile]; at this point, success must always be TRUE since this code now calls the procDep and procMod procs call again to avoid overflow in the database returns NIL if can't find modulename headerseg initted from ReadIn init from bcd init symbols header offset is # words off # base page number, size is # words in range foreach module entry since each filename ends in ".bcd.", we strip the trailing "." off skip the module itself CWF.WF2["module %s file %s\n"L, smodulename, sfn]; fine grain table is in sgb[].extrapages remove trailing "." CWF.WF1["sourcefile <%s>\n"L, tostr]; free the segment 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 CWF.WF1["Name: %s\n"L, interfacename]; if any one of the bcds ins not codebound, then the config is not codebound 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 uses heuristics note: 1yr = 31,536,000 seconds omit a leading directory name if no '.' is present, explicitely append ".bcd" at the end of stemp Ê%˜šœ™Jšœ ™ JšœK™K—J˜šÏk ˜ šœ œ˜Jšœ¶˜¶—Jšœœœ˜!Jšœ œœ˜.Jšœ œœ˜'Jšœœœ$˜4Jšœ œœ ˜5Jšœ œœ ˜!šœ œ˜JšœR˜R—Jšœ œœ˜1Jšœœœ ˜šœœ˜Jšœ^˜^—šœœ˜JšœD˜D—Jšœ œœ8˜KJšœœœ!˜:Jšœœœ˜Jšœœœ ˜Jšœ œœ ˜J˜—šœœ˜Jšœœ4˜?Jšœ ˜J˜Jšœ œœÏc˜3J˜Jšœœœœ ˜!J˜Jšœ œœ˜Jš œ œœœœ˜7J˜/J™Jšœ ™ Jš œœ œœœœœ˜IJ˜)J™Jšœœœœž ˜&J˜*J™Jšœœž˜1J˜*Jšœœž˜:J™Jš œœœœœž'˜\J˜+J˜J˜—Jšœ œœœ˜!J˜šÏnœœœ ˜>Jšœœ˜ Jšœœ˜Jš œ#œœœœ˜EJšœœ˜Jšœ0™0J˜Mšœœ˜˜)J˜%——JšœL˜PJ˜8šœ/œ˜7Jšœ)™)J˜%J˜*Jšœœ˜Jšœ ˜Jšœ˜J˜—J˜šœ œ˜J˜%Jšœ$™$J˜Qšœœ˜˜)J˜%——JšœL˜PJ˜8J˜—J˜FJ˜7J˜—J˜šŸœœœ˜?Jšœ œ˜#Jšœœ%˜;J˜0J˜0J˜1J˜1J˜0J˜0J˜0J˜0J˜0J˜0J˜—J˜šŸ œœœ!˜8Jšœ+œœ˜8J˜%Jšœ/ž˜FJ˜—J˜šŸ œœ˜Jšœiœœœ˜‰Jšœœœ˜KJšœ œ˜Jšœœ˜.Jšœ œ˜šœU˜YJšœœ˜—Jšœœ˜J˜?Jšœ œ˜Jšœ œœ˜3Jšœœœ˜6Jšœ œœ˜1šœ˜šœ˜ JšœX˜X—Jšœœ˜%J˜Jšœœœ˜%šœ/œ˜7Jšœ'˜*Jšœœ˜ J˜—Jšœ9™9J˜1šœœ'˜FJ˜*—Jšœ)™)šœœ˜'Jšœ$˜'Jšœœ˜ J˜—J˜0Jš œœ œœœ˜!Jšœ0™0Jšœ1™1J˜$šœœ œ˜Jšœ$œ˜AJšœœ˜9Jšœœ ˜AJšœœ˜=Jš œœœ œ œœœ˜XJšœœ˜;Jšœœ"˜AJšœœ œž#˜b˜FJ˜!—J˜—Jš œœ œ'œœ˜PJ˜"šœ˜ šœ˜˜(J˜/J˜Jšœœœœ˜Jšœœœœœœœ˜@——šœ˜šœœœ˜?Jšœœd˜z—˜(J˜EJšœ œ)˜=J˜TJ˜—J˜——šœœ œ˜šœ˜š˜J˜A—š˜Jšœ:œ˜T——J˜—šœœ œ˜J˜2—š œ œœ œ œ œ˜AJšœ,™,˜J˜J˜/Jšœœ œ˜8J˜@J˜J—J˜—J˜2šœ œ˜Jšœ8œ˜?Jš œœœœœ˜>J˜—Jšœ œ˜Jšœ˜Jšœžœ˜—JšœW˜WJšœ˜—J˜šŸœ˜Jš œ< œ œœœ˜pJšœœ˜Jšœ œœ˜3Jšœ œœ˜1Jšœœ˜.šœœœ=˜KJ˜ šœ˜"J˜2šœœ˜"J˜+—˜=J˜)—J˜Jšœ œ˜#š œœœœœ˜IJšœœ˜ —šœ ˜Jšœ9˜ œ˜KJšœ œ˜Jšœ$™$Jšœœ˜0Jšœœ˜0š œœœœœ˜Kšœœ˜%J˜4Jšœ ˜J˜—Jšœ œ˜$Jšœ˜—š œœœœœ˜Kšœœ˜%J˜4Jšœ ˜J˜—Jšœ œ,˜Jšœ2˜5Jšœœ˜ J˜—Jšœ™šœ ˜šœ˜Jšœœœ˜J˜Jšœœ˜Jšœœ˜šœ œ˜2Jšœœ˜2—J˜*J˜+Jšœœ.˜Ašœœœ˜šœ œ˜JšœD˜D——J˜—šœ˜˜MJ˜—Jšœ œœ˜I˜7J˜8—šœœ œœ0˜OJ˜8—Jšœœ.˜Ašœœœ˜šœœ œœ˜5šœ ˜ Jšœ˜J˜4J˜———J˜——Jšœ œ˜Jšœ˜Jšœžœ˜—Jšœ˜J˜—J˜šŸœœ;œ˜SJšœ œœ˜6JšœB™BJšœœ˜Jšœ œ#˜7J˜J˜AJ˜>šœ#˜%Jšœ(˜,Jšœ/˜3—J˜"J˜CJ˜J˜—šŸœœ&˜>Jšœ œ œ˜9Jšœ œœ˜Jšœ œ œ˜+Jšœ™JšœB™BJšœœ˜.Jšœœœ˜,Jšœ œœ˜4J˜'šœœœ9˜GJ˜ Jšœ œ˜š˜Jšœ™Jšœœ œ˜@Jšœœœ˜J˜6J˜J˜1J˜4J˜J˜)šœœ˜1Jšœž ˜*—Jšœ2™2J˜J˜=Jšœ œ˜#Jš œœœœœ˜<šœ˜JšœE˜HJšœœ˜ J˜—Jšœ˜—Jšœœœ˜-Jšœ œ˜Jšœ˜Jšœžœ˜—Jšœ3˜3J˜—J˜šŸœœ œ˜:Jšœ'™'šœœœ˜EJ˜J˜6Jšœ™šœœ˜7Jšœ"ž ˜.—J˜—Jšœ˜Jšœ%™%J˜—J˜šŸœœ˜Jšœ™šœ'œ˜/J˜ J˜%J˜—šœ!œ˜)J˜J˜J˜—šœ"œ˜*J˜J˜ J˜—šœ"œ˜*J˜J˜ J˜—šœ$œ˜,J˜J˜"J˜—J˜—J˜šŸœœ>˜TJ˜Jšœ˜Jšœ˜JšœD˜HJ˜—J˜šŸ œœœ+ œ˜RJšœ œ˜Jšœ;™;Jšœ/™/Jšœ™Jšœœ˜.šœœ˜!šœœ˜:J˜——šœ˜šœ˜"šœ*œ˜2˜'J˜—Jšœ&™&Jšœ˜J˜—Jšœ'œ˜>šœœœœ˜:Jšœœ˜Jšœ$˜'Jšœœ˜J˜—Jšœ˜——Jšœœ˜ J˜J˜—šŸ œœ6˜HJš œ œ œœœ˜:Jšœœ˜0J˜ šœ˜#J˜(J˜Jšœ$œ˜<š œœœœœ˜EJšœœœ˜—šœ ˜Jšœ$˜'Jšœ˜J˜—Jšœ˜—Jšœœœ˜+J˜J˜šŸ œœ4˜EJšœ œ˜0Jšœ˜Jšœœœ˜.Jšœœœ˜/J˜šœœœ6˜DJ˜J˜%šœœ˜/Jšœœœ˜8Jšœ˜Jšœœ˜ J˜—J˜!J˜+Jšœ˜Jšœžœ˜—Jšœ0˜0Jšœ˜J˜—šŸ œœ6˜HJš œ œ œœœ˜:Jšœœ˜0Jšœ œ˜J˜ šœ˜#J˜(J˜Jšœ œ˜$š œœœœœ˜JJšœœ˜ —šœ ˜Jšœ$˜'Jšœ˜J˜—Jšœ˜—Jšœœœ˜,J˜——˜šŸ œœ3˜DJšœ œ˜1Jšœ˜Jšœœœ˜.Jšœœœ˜/J˜šœœœ6˜DJ˜J˜%Jšœœ˜/šœœœ˜8Jšœ˜Jšœœ˜ J˜—J˜!J˜+Jšœ˜Jšœžœ˜—Jšœ0˜0J˜J˜——šŸ œœ5˜GJšœœ˜ Jšœ9™9Jšœ™Jšœœ˜.Jšœœ˜šœ˜"šœ"œ˜*Jšœœ˜:Jšœœ"˜@J˜—Jšœ)œ˜@Jš œœœœœ˜Dšœ˜Jšœ˜Jšœ˜J˜—Jšœ˜—J˜—J˜šŸœœ1˜IJšœœ˜J˜Jšœœ˜.Jšœœ˜šœ˜"šœœ˜'šœ)œ˜1J˜$šœ$œ˜,Jšœ ˜#Jšœ˜J˜—J˜Jšœ-˜/J˜—J˜—šœ œœ˜1Jšœ œ&œ˜GJšœ œ˜-Jšœ œ˜-Jšœœ˜—Jš œœœœœ˜Dšœ˜Jšœ˜Jšœ˜J˜—Jšœ˜—J˜—J˜šŸœœ˜4Jšœ œœ˜PJšœ²™²J˜Jšœ™Jšœ,™,Jšœ'™'Jšœ6™6J˜Jšœœ˜.Jšœ œœ˜0Jšœ œœ˜1J˜Jšœœ˜ šœœœ:˜Hšœ˜"Jšœœ˜ Jšœ#ž˜8šœœ+˜OJ˜$Jšœ˜—šœ˜Jšœ-œœ˜>—šœœ)œ˜8J˜1J˜8šœ˜Jšœœœ ˜1Jšœ˜J˜+J˜—J˜—Jšœ)œ˜@š œœœœœ˜Dšœ˜Jšœ˜Jšœœ˜ J˜——Jšœ˜—J˜8Jšœ˜Jšœžœ˜—Jšœ4˜4J˜J˜—šŸœ˜Jšœ< œœ˜\Jšœ˜J˜!Jšœœ˜.Jšœ œœ˜0Jšœ œœ˜1J˜šœœœ:˜Hšœ˜"J˜šœ(˜/Jšœ*œœ˜6J˜Jšœ˜—šœ*œ˜2J˜šœ&œ˜.Jšœ˜!Jšœœ˜ J˜—šœ+˜-Jšœ,œ˜5˜0J˜ —J˜0šœœ/˜5šœ˜J˜J˜<˜'J˜—J˜——J˜—šœ(˜*Jšœ)œ˜2J˜$šœ$œ˜,Jšœ ˜#Jšœœ˜ J˜—J˜1J˜8šœœœ˜*Jšœ˜!J˜+J˜—J˜—J˜—šœ œœ˜1Jšœ œ&œ˜GJšœ œ˜-Jšœ œ˜-Jšœœ˜—š œœœœœ˜Dšœ˜Jšœ˜Jšœœ˜ J˜——Jšœ˜—Jšœ˜Jšœžœ˜—Jšœ4˜4J˜J˜—šŸ œœœœœœ œ˜KJšœ™Jšœ™Jšœœœ˜%Jšœœ˜3J˜—J˜šŸœœ; œ˜XJšœœ˜ Jšœœœ˜,˜%Jšœ/œ˜N—šœœœ˜)Jšœ™J˜&Jšœœœœ˜1šœ˜Jšœ œ œœ˜(J˜ Jšœ˜—šœ˜ Jšœ!˜%Jšœ˜—Jšœžœ˜—Jšœ˜Jšœ˜J˜—šŸ œœ: œ˜]JšœC™Cšœ˜˜Jšœ˜Jšœ˜J˜—˜Jšœ˜Jšœ˜J˜—šœ˜ J˜/Jšœœœ)˜HJ˜—Jš˜—J˜J˜—J˜J˜—…—gp‹=