DIRECTORY IO, Atom, Convert, ConvertUnsafe, MobConnection, File, MobDefs, MobListerUtils, OSMiscOps, RefText, Rope, SymbolOps, Symbols, SymbolSegment, SymbolTable, SymbolTablePrivate, Table; MobConnectionImpl: PROGRAM IMPORTS IO, Atom, Convert, RefText, Rope, SymbolOps, MobListerUtils EXPORTS MobConnection, SymbolTable, SymbolOps = { FormIdentifier: TYPE = ATOM; ModuleID: TYPE = MobConnection.ModuleID; FilterItem: TYPE = MobConnection.FilterItem; Filter: TYPE = MobConnection.Filter; bytesPerUnit: NAT = BYTES[WORD]; unitsPerFilePage: NAT = File.wordsPerPage; bytesPerFilePage: NAT = unitsPerFilePage*bytesPerUnit; bitsPerByte: CARD = 8; bitsPerUnit: CARD = OSMiscOps.bytesPerUnit*bitsPerByte; UnitsToFilePages: PROC[units: INT] RETURNS[INT] = { RETURN[(units+unitsPerFilePage-1)/unitsPerFilePage]}; BytesToFilePages: PROC[bytes: INT] RETURNS[INT] = { RETURN[(bytes+bytesPerFilePage-1)/bytesPerFilePage]}; Base: TYPE = MobDefs.Base; Mob: TYPE = MobDefs.Mob; BitAddress: TYPE = Symbols.BitAddress; BodyRecord: TYPE = Symbols.BodyRecord; BTIndex: TYPE = Symbols.BTIndex; RootBti: BTIndex = Symbols.RootBti; CBTIndex: TYPE = Symbols.CBTIndex; ContextLevel: TYPE = Symbols.ContextLevel; lG: ContextLevel = Symbols.lG; CSEIndex: TYPE = Symbols.CSEIndex; CSENull: CSEIndex = Symbols.CSENull; typeTYPE: CSEIndex = Symbols.typeTYPE; CTIndex: TYPE = MobDefs.CTIndex; CTNull: CTIndex = MobDefs.CTNull; CTRecord: TYPE = MobDefs.CTRecord; CTXIndex: TYPE = Symbols.CTXIndex; CTXNull: CTXIndex = Symbols.CTXNull; CTXRecord: TYPE = Symbols.CTXRecord; EVIndex: TYPE = MobDefs.EVIndex; EVNull: EVIndex = MobDefs.EVNull; EVRecord: TYPE = MobDefs.EVRecord; EXPIndex: TYPE = MobDefs.EXPIndex; EXPLink: TYPE = MobDefs.EXPLink; EXPRecord: TYPE = MobDefs.EXPRecord; FPIndex: TYPE = MobDefs.FPIndex; FPRecord: TYPE = MobDefs.FPRecord; FTIndex: TYPE = MobDefs.FTIndex; FTNull: FTIndex = MobDefs.FTNull; FTSelf: FTIndex = MobDefs.FTSelf; FTRecord: TYPE = MobDefs.FTRecord; HTIndex: TYPE = Symbols.HTIndex; ISEIndex: TYPE = Symbols.ISEIndex; ISENull: ISEIndex = Symbols.ISENull; ISERecord: TYPE = SERecord.id; LFIndex: TYPE = MobDefs.LFIndex; LFNull: LFIndex = MobDefs.LFNull; LFRecord: TYPE = MobDefs.LinkFrag; Link: TYPE = MobDefs.Link; nullLink: Link = MobDefs.nullLink; Namee: TYPE = MobDefs.Namee; NameRecord: TYPE = MobDefs.NameRecord; NameString: TYPE = MobDefs.NameString; NTIndex: TYPE = MobDefs.NTIndex; NTNull: NTIndex = MobDefs.NTNull; NTRecord: TYPE = MobDefs.NTRecord; IMPIndex: TYPE = MobDefs.IMPIndex; IMPRecord: TYPE = MobDefs.IMPRecord; MTIndex: TYPE = MobDefs.MTIndex; MTRecord: TYPE = MobDefs.MTRecord; RefMob: TYPE = REF Mob; RefMTRecord: TYPE = REF MTRecord; RefSGRecord: TYPE = REF SGRecord; RFIndex: TYPE = MobDefs.RFIndex; RFNull: RFIndex = MobDefs.RFNull; RFRecord: TYPE = MobDefs.RefLitFrag; ROPE: TYPE = Rope.ROPE; Name: TYPE = Symbols.Name; nullName: Name = Symbols.nullName; SEIndex: TYPE = Symbols.SEIndex; SENull: SEIndex = Symbols.SENull; SERecord: TYPE = Symbols.SERecord; SGIndex: TYPE = MobDefs.SGIndex; SGNull: SGIndex = MobDefs.SGNull; SGRecord: TYPE = MobDefs.SGRecord; SpaceID: TYPE = MobDefs.SpaceID; SPIndex: TYPE = MobDefs.SPIndex; SPRecord: TYPE = MobDefs.SPRecord; SymbolTableBase: TYPE = REF SymbolTableBaseRep; STB: TYPE = REF SymbolTableBaseRep; SymbolTableBaseRep: PUBLIC TYPE = SymbolTablePrivate.SymbolTableBaseRep; TFIndex: TYPE = MobDefs.TFIndex; TFNull: TFIndex = MobDefs.TFNull; TFRecord: TYPE = MobDefs.TypeFrag; TYPIndex: TYPE = MobDefs.TYPIndex; TYPNull: TYPIndex = MobDefs.TYPNull; TYPRecord: TYPE = MobDefs.TYPRecord; VersionStamp: TYPE = MobDefs.VersionStamp; Error: PUBLIC ERROR [culprit: REF, msg: ROPE] = CODE; Appender: TYPE = RECORD [head, last: LIST OF REF ANY]; LORAFromMob: PUBLIC SAFE PROC [fileName: Rope.ROPE, filter: Filter ¬ ALL[TRUE]] RETURNS [result: LIST OF REF ANY ¬ NIL] = CHECKED { stack: LIST OF Appender ¬ NIL; Begin: SAFE PROC [id: FormIdentifier] = { head: LIST OF REF ANY = LIST[id]; stack ¬ CONS[[head, head], stack]; }; End: SAFE PROC = { a: LIST OF REF ANY ¬ stack.first.head; stack ¬ stack.rest; IF stack = NIL THEN result ¬ a ELSE Put[a]; }; Put: SAFE PROC [a: REF] = {stack.first.last ¬ stack.first.last.rest ¬ LIST[a]}; PutATOM: SAFE PROC [a: ATOM] = {Put[a]}; PutROPE: SAFE PROC [a: ROPE] = {Put[a]}; PutINT: SAFE PROC [a: INT] = {Put[NEW[INT ¬ a]]}; PutBOOL: SAFE PROC [a: BOOL] = {Put[IF a THEN $TRUE ELSE $FALSE]}; PutModuleID: SAFE PROC [a: ModuleID] = {Put[NEW[ModuleID ¬ a]]}; DecodeMob[fileName: fileName, begin: Begin, end: End, putATOM: PutATOM, putROPE: PutROPE, putINT: PutINT, putBOOL: PutBOOL, putModuleID: PutModuleID, filter: filter]; IF stack # NIL THEN ERROR; }; DecodeMob: PUBLIC SAFE PROC [fileName: Rope.ROPE, begin: SAFE PROC [FormIdentifier], end: SAFE PROC, putATOM: SAFE PROC[ATOM], putROPE: SAFE PROC[ROPE], putINT: SAFE PROC[INT], putBOOL: SAFE PROC[BOOL], putModuleID: SAFE PROC[ModuleID], filter: Filter] = TRUSTED { Inner: PROC [mob: MobDefs.MobBase] = { Start: PROC [FormIdentifier] = begin; End: PROC = end; PutRope: PROC [ROPE] = putROPE; PutINT: PROC [int: INT] = putINT; PutLabeledINT: PROC [symbol: ATOM, int: INT] = {Start[symbol]; PutINT[int]; End[]}; PutLabeledBOOL: PROC [symbol: ATOM, bool: BOOL] = {Start[symbol]; putBOOL[bool]; End[]}; PrintStamps: PROC = { IF filter[imports] THEN {Start[$imports]; FOR iti: IMPIndex ¬ IMPIndex.FIRST, iti + IMPRecord.SIZE UNTIL iti = mob.impLimit DO ip: LONG POINTER TO IMPRecord = @itb[iti]; IF LOOPHOLE[iti, CARD] > LOOPHOLE[mob.impLimit, CARD] THEN GO TO Bogus; PutFileStamp[ip.file, ip.name]; REPEAT Bogus => PrintGarbage[]; ENDLOOP; End[]}; IF filter[exports] THEN {Start[$exports]; FOR eti: EXPIndex ¬ EXPIndex.FIRST, eti + etb[eti].nLinks*EXPLink.SIZE + EXPRecord.SIZE UNTIL eti = mob.expLimit DO ee: LONG POINTER TO EXPRecord = @etb[eti]; OnlyTypes: PROC RETURNS [BOOL] = { FOR i: CARD16 IN [0..ee.nLinks) DO IF ee[i].from = [tag: var, modIndex: 0, offset: 0] THEN EXIT; IF ee[i].from.tag # type THEN RETURN [FALSE] ENDLOOP; RETURN [TRUE] }; IF LOOPHOLE[eti, CARD] > LOOPHOLE[mob.expLimit, CARD] THEN GO TO Bogus; IF NOT filter[typeExports] AND OnlyTypes[] THEN LOOP; IF filter[exportedItems] THEN {Start[$export]; PutFileStamp[ee.file, ee.name]; FOR i: CARD16 IN [0..ee.nLinks) DO IF ee[i].from = [tag: var, modIndex: 0, offset: 0] THEN EXIT; Start[SELECT ee[i].from.tag FROM var => $var, proc => $proc, type => $type, ENDCASE => $other]; PutINT[ee[i].to]; End[]; ENDLOOP; End[]} ELSE PutFileStamp[ee.file, ee.name]; REPEAT Bogus => PrintGarbage[]; ENDLOOP; End[]}; IF filter[modules] THEN {Start[$modules]; FOR mti: MTIndex ¬ MTIndex.FIRST, mti + MTSize[mti] UNTIL mti = mob.mtLimit DO mm: LONG POINTER TO MTRecord = @mtb[mti]; IF LOOPHOLE[mti, CARD] > LOOPHOLE[mob.mtLimit, CARD] THEN GO TO Bogus; PutFileStamp[mm.file, mm.name]; REPEAT Bogus => PrintGarbage[]; ENDLOOP; End[]}; }; PutFileStamp: PROC[fti: FTIndex, mName: NameRecord] = { SELECT fti FROM FTNull => PutName[mName]; FTSelf => PutName[mName]; ENDCASE => { ftr: LONG POINTER TO FTRecord = @ftb[fti]; PutStampedName[n: ftr.name, stamp: ftr.version]; }; }; PutMobName: PROC[] = { name: NameRecord = IF mob.nModules=1 AND mob.nConfigs = 0 THEN (@mtb[MTIndex.FIRST]).name ELSE mob.source; PutStampedName[n: name, stamp: mob.version]; }; PrintHeader: PROC = { {Start[$header]; PutLabeledINT[$configurations, mob.nConfigs]; PutLabeledINT[$modules, mob.nModules]; PutLabeledINT[$imports, mob.nImports]; PutLabeledINT[$exports, mob.nExports]; PutLabeledINT[$firstdummy, mob.firstdummy]; PutLabeledINT[$dummies, mob.nDummies]; PutLabeledBOOL[$definitions, mob.definitions]; PutLabeledBOOL[$repackaged, mob.repackaged]; PutLabeledBOOL[$typeexported, mob.typeExported]; PutLabeledBOOL[$inlinefloat, mob.inlineFloat]; PutLabeledBOOL[$mappingstarted, mob.mappingStarted]; PutLabeledBOOL[$mappingfinished, mob.mappingFinished]; PutLabeledBOOL[$versions, mob.versions]; PutLabeledBOOL[$extended, mob.extended]; End[]}; }; PrintConfigs: PROC = { cti: CTIndex ¬ CTIndex.FIRST; IF cti = mob.ctLimit THEN RETURN; {Start[$configurations]; UNTIL cti = mob.ctLimit DO PrintConfig[cti]; cti ¬ cti + CTRecord.SIZE + ctb[cti].nControls*Namee.SIZE; IF LOOPHOLE[cti, CARD] > LOOPHOLE[mob.ctLimit, CARD] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; End[]}; }; PrintConfig: PROC[cti: CTIndex] = { ctp: LONG POINTER TO CTRecord = @ctb[cti]; Start[$configuration]; PutName[ctp.name]; IF ctp.nControls # 0 THEN {Start[$control]; FOR i: CARDINAL IN [0..ctp.nControls) DO WITH c~~ctp.controls[i] SELECT FROM module => {PutName[mtb[c.mti].name]}; config => {Start[$config]; PutName[ctb[c.cti].name]; End[]}; ENDCASE => ERROR; ENDLOOP; End[]}; End[]}; MTSize: PROC[mti: MTIndex] RETURNS[NAT] = { RETURN[MTRecord.SIZE] }; PrintUsing: PROC [stb: SymbolTableBase] = { limit: CTXIndex = LOOPHOLE[stb.stHandle.ctxBlock.size]; ctx: CTXIndex ¬ CTXIndex.FIRST + CTXRecord.nil.SIZE; pairs: LIST OF Pair ¬ NIL; ros: IO.STREAM ¬ IO.ROS[]; firstCopiedHash: Symbols.HTIndex; InDirectory: PROC [ctx: CTXIndex] RETURNS [BOOL] = { FOR dirSei: ISEIndex ¬ SymbolOps.FirstCtxSe[stb, stb.stHandle.directoryCtx], SymbolOps.NextSe[stb, dirSei] UNTIL dirSei = ISENull DO WITH se~~stb.seb[SymbolOps.UnderType[stb, stb.seb[dirSei].idType]] SELECT FROM definition => IF ctx = se.defCtx THEN RETURN [TRUE]; ENDCASE; ENDLOOP; RETURN [FALSE]}; DoContext: PROC [ctx: CTXIndex] = { IF ctx # CTXNull THEN { sei, root: ISEIndex; cp: LONG POINTER TO CTXRecord = @stb.ctxb[ctx]; which: LIST OF Pair ¬ NIL; key, modName: ROPE ¬ NIL; mdi: Symbols.MDIndex; DoSei: PROC [sei: ISEIndex] = { sep: LONG POINTER TO ISERecord = @stb.seb[sei]; IF LOOPHOLE[sep.hash, CARD] < LOOPHOLE[firstCopiedHash, CARD] THEN { name: ROPE ¬ NIL; IF sep.idType = typeTYPE THEN { typeSei: SEIndex ¬ SymbolOps.DecodeType[sep.idInfo]; WITH tse~~stb.seb[typeSei] SELECT FROM id => { IF tse.idCtx # ctx AND InDirectory[tse.idCtx] THEN RETURN}; ENDCASE; }; ros ¬ IO.ROS[ros]; MobListerUtils.PrintSei[sei, ros, stb]; name ¬ ros.RopeFromROS[FALSE]; which.first.names ¬ InsertName[name, which.first.names]}; }; WITH c~~cp SELECT FROM included => { mdi ¬ c.module}; imported => { mdi ¬ stb.ctxb[c.includeLink].module}; ENDCASE => RETURN; ros ¬ IO.ROS[ros]; MobListerUtils.PrintName[stb.mdb[mdi].moduleId, ros, stb]; modName ¬ ros.RopeFromROS[FALSE]; [which, pairs] ¬ FindList[modName, pairs]; IF which.first.file = NIL THEN { modFileName: ROPE ¬ NIL; ros ¬ IO.ROS[ros]; MobListerUtils.PrintName[stb.mdb[mdi].fileId, ros, stb]; modFileName ¬ ros.RopeFromROS[FALSE]; modFileName ¬ modFileName.Flatten[0, modFileName.SkipTo[0, "."]]; which.first.file ¬ modFileName}; root ¬ sei ¬ stb.ctxb[ctx].seList; DO IF sei = SENull THEN EXIT; DoSei[sei]; IF (sei ¬ SymbolOps.NextSe[stb, sei]) = root THEN EXIT; ENDLOOP; }; }; BEGIN firstCopiedHash ¬ LAST[Symbols.HTIndex]; END; FOR dirSei: ISEIndex ¬ SymbolOps.FirstCtxSe[stb, stb.stHandle.directoryCtx], SymbolOps.NextSe[stb, dirSei] UNTIL dirSei = ISENull DO WITH se~~stb.seb[SymbolOps.UnderType[stb, stb.seb[dirSei].idType]] SELECT FROM definition => DoContext[se.defCtx]; ENDCASE; ENDLOOP; FOR dirSei: ISEIndex ¬ SymbolOps.FirstCtxSe[stb, stb.stHandle.importCtx], SymbolOps.NextSe[stb, dirSei] UNTIL dirSei = ISENull DO WITH se~~stb.seb[SymbolOps.UnderType[stb, stb.seb[dirSei].idType]] SELECT FROM definition => DoContext[se.defCtx]; transfer => { bti: BTIndex = SymbolOps.DecodeBti[stb.seb[dirSei].idInfo]; DoContext[stb.bb[bti].localCtx]}; ENDCASE; ENDLOOP; IF pairs # NIL THEN {Start[$directory]; WHILE pairs # NIL DO pair: Pair = pairs.first; names: LIST OF ROPE ¬ pair.names; {Start[$item]; putATOM[Atom.MakeAtom[pair.key]]; IF NOT (pair.key).Equal[pair.file, FALSE] THEN {Start[$from]; PutRope[pair.file]; End[]}; IF filter[using] THEN {Start[$using]; WHILE names # NIL DO PutRope[names.first]; names ¬ names.rest; ENDLOOP; End[]}; End[]}; pairs ¬ pairs.rest; ENDLOOP; End[]}; }; PrintFileName: PROC[fti: FTIndex] = { SELECT fti FROM FTNull => putATOM[$null]; FTSelf => putATOM[$self]; ENDCASE => PutName[ftb[fti].name]; }; PrintLongIndex: PROC[index: CARD] = { }; PrintGarbage: PROC = { Tab[2]; PutRope["? Looks like garbage ..."]; }; Tab: PROC[n: CARDINAL] = { }; PutName: PROC[n: NameRecord] = { putATOM[SymbolFromName[n]]; }; PutStampedName: PROC[n: NameRecord, stamp: VersionStamp] = { IF filter[versions] THEN putModuleID[[SymbolFromName[n], SymbolFromStamp[stamp]]] ELSE PutName[n]; }; NameToRope: PROC[n: NameRecord] RETURNS[ROPE] = { CharSeq: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF CHAR]; ss: LONG POINTER TO CharSeq = LOOPHOLE[ssb]; index: CARDINAL = n+4; len: CARDINAL = ss[index]-0C; i: CARDINAL ¬ index+1; P: SAFE PROC RETURNS [c: CHAR] = TRUSTED { c ¬ ss[i]; i ¬ i + 1 }; RETURN[Rope.FromProc[len: len, p: P]]; }; SymbolFromName: PROC[n: NameRecord] RETURNS [a: ATOM] = { CharSeq: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF CHAR]; ss: LONG POINTER TO CharSeq = LOOPHOLE[ssb]; index: CARDINAL = n+4; len: CARDINAL = ss[index]-0C; text: REF TEXT ¬ RefText.ObtainScratch[len]; FOR i: NAT IN [index+1..index+1+len) DO text[text.length] ¬ ss[i]; text.length ¬ text.length + 1; ENDLOOP; a ¬ Atom.MakeAtomFromRefText[text]; RefText.ReleaseScratch[text]; }; SymbolFromNameAndIndex: PROC[n: NameRecord, card: CARD] RETURNS [a: ATOM] = { CharSeq: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF CHAR]; ss: LONG POINTER TO CharSeq = LOOPHOLE[ssb]; index: CARDINAL = n+4; len: CARDINAL = ss[index]-0C; text: REF TEXT ¬ RefText.ObtainScratch[len+9]; FOR i: NAT IN [index+1..index+1+len) DO text[text.length] ¬ ss[i]; text.length ¬ text.length + 1; ENDLOOP; text[text.length] ¬ '-; text.length ¬ text.length + 1; text ¬ Convert.AppendCard[to: text, from: card, base: 16, showRadix: FALSE]; a ¬ Atom.MakeAtomFromRefText[text]; RefText.ReleaseScratch[text]; }; SymbolFromNameAndVersionStamp: PROC [n: NameRecord, stamp: VersionStamp] RETURNS [a: ATOM] = { DigitArray: TYPE = PACKED ARRAY DigitArrayIndex OF HexDigit; HexDigit: TYPE = [0..16); DigitArrayIndex: TYPE = [0..BITS[VersionStamp]/BITS[HexDigit]); CharSeq: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF CHAR]; ss: LONG POINTER TO CharSeq = LOOPHOLE[ssb]; index: CARDINAL = n+4; len: CARDINAL = ss[index]-0C; text: REF TEXT ¬ RefText.ObtainScratch[len+17]; FOR i: NAT IN [index+1..index+1+len) DO text[text.length] ¬ ss[i]; text.length ¬ text.length + 1; ENDLOOP; text[text.length] ¬ '-; text.length ¬ text.length + 1; FOR i: DigitArrayIndex IN DigitArrayIndex DO d: HexDigit = LOOPHOLE[stamp, DigitArray][i]; text[text.length] ¬ IF d < 10 THEN '0+d ELSE 'a-10+d; text.length ¬ text.length + 1; ENDLOOP; a ¬ Atom.MakeAtomFromRefText[text]; RefText.ReleaseScratch[text]; }; SymbolFromStamp: PROC [stamp: VersionStamp] RETURNS [a: ATOM] = { DigitArray: TYPE = PACKED ARRAY DigitArrayIndex OF HexDigit; HexDigit: TYPE = [0..16); DigitArrayIndex: TYPE = [0..BITS[VersionStamp]/BITS[HexDigit]); text: REF TEXT ¬ RefText.ObtainScratch[17]; text[text.length] ¬ 'v; text.length ¬ text.length + 1; FOR i: DigitArrayIndex IN DigitArrayIndex DO d: HexDigit = LOOPHOLE[stamp, DigitArray][i]; text[text.length] ¬ IF d < 10 THEN '0+d ELSE 'a-10+d; text.length ¬ text.length + 1; ENDLOOP; a ¬ Atom.MakeAtomFromRefText[text]; RefText.ReleaseScratch[text]; }; 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 putATOM[$anon] 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 = mob.ntLimit DO IF proc[ntb, nti] THEN RETURN[nti]; ENDLOOP; RETURN[NTNull]}; tb: MobDefs.Base; ssb: MobDefs.NameString ¬ NIL; evb: MobDefs.Base ¬ NIL; spb: MobDefs.Base ¬ NIL; fpb: MobDefs.Base ¬ NIL; ctb: MobDefs.Base ¬ NIL; mtb: MobDefs.Base ¬ NIL; lfb: MobDefs.Base ¬ NIL; tfb: MobDefs.Base ¬ NIL; rfb: MobDefs.Base ¬ NIL; itb: MobDefs.Base ¬ NIL; etb: MobDefs.Base ¬ NIL; sgb: MobDefs.Base ¬ NIL; ftb: MobDefs.Base ¬ NIL; ntb: MobDefs.Base ¬ NIL; InitTableBases: PROC[ptr: LONG POINTER] = { tb ¬ LOOPHOLE[ptr]; ssb ¬ LOOPHOLE[ptr + mob.ssOffset.units]; ctb ¬ tb + mob.ctOffset.units; mtb ¬ tb + mob.mtOffset.units; IF mob.extended THEN { lfb ¬ tb + mob.lfOffset.units; tfb ¬ tb + mob.tfOffset.units; rfb ¬ tb + mob.rfOffset.units}; itb ¬ tb + mob.impOffset.units; etb ¬ tb + mob.expOffset.units; sgb ¬ tb + mob.sgOffset.units; ftb ¬ tb + mob.ftOffset.units; ntb ¬ tb + mob.ntOffset.units; evb ¬ tb + mob.evOffset.units; spb ¬ tb + mob.spOffset.units; fpb ¬ tb + mob.fpOffset.units; }; tb ¬ NIL; IF mob.versionIdent # MobDefs.VersionID THEN Error[fileName, "not a valid Cedar mob file"]; InitTableBases[mob]; {Start[IF mob.definitions THEN $definitions ELSE IF mob.nConfigs = 0 THEN $program ELSE $configuration]; PutMobName[]; IF filter[directory] AND mob.nConfigs = 0 THEN { mtb: MobDefs.Base = LOOPHOLE[mob+mob.mtOffset.units]; sgh: MobDefs.SGHandle = IF mtb[FIRST[MobDefs.MTIndex]].sseg = MobDefs.SGNull THEN Error[fileName, "NoSymbols"] -- NoSymbols ELSE @sgb[mtb[FIRST[MobDefs.MTIndex]].sseg]; stb: LONG POINTER TO SymbolSegment.STHeader = LOOPHOLE[mob+sgh.base.units]; IF sgh.file = MobDefs.FTSelf AND sgh.units.units # 0 AND stb.versionIdent = SymbolSegment.VersionID THEN PrintUsing[InstallTable[stb]]; }; IF filter = ALL[FALSE] THEN PrintHeader[]; PrintStamps[]; IF filter[configurations] THEN PrintConfigs[]; End[]}; }; theMob: MobDefs.MobBase ~ MobListerUtils.ReadMob[fileName ! MobListerUtils.MobErr => ERROR Error[culprit: fileName, msg: err]]; Inner[theMob ! UNWIND => MobListerUtils.FreeMob[theMob]]; MobListerUtils.FreeMob[theMob]; }; InstallTable: PROC [node: LONG POINTER] RETURNS [SymbolTableBase] = { b: LONG POINTER = node; tB: SymbolSegment.Base = LOOPHOLE[b]; p: LONG POINTER TO SymbolSegment.STHeader = b; base: SymbolTableBase ¬ NEW[SymbolTableBaseRep]; base.cacheInfo ¬ LOOPHOLE[node]; base.hashVec ¬ b+p.hvBlock.offset; base.htb ¬ tB + p.htBlock.offset - SymbolSegment.biases[SymbolSegment.htType]; base.ssb ¬ b + p.ssBlock.offset - SymbolSegment.biases[SymbolSegment.ssType]; base.seb ¬ tB + p.seBlock.offset - SymbolSegment.biases[SymbolSegment.seType]; base.ctxb ¬ tB + p.ctxBlock.offset - SymbolSegment.biases[SymbolSegment.ctxType]; base.mdb ¬ tB + p.mdBlock.offset - SymbolSegment.biases[SymbolSegment.mdType]; base.bb ¬ tB + p.bodyBlock.offset - SymbolSegment.biases[SymbolSegment.bodyType]; base.tb ¬ tB + p.treeBlock.offset - SymbolSegment.biases[SymbolSegment.treeType]; base.ltb ¬ tB + p.litBlock.offset - SymbolSegment.biases[SymbolSegment.ltType]; base.stb ¬ tB + p.sLitBlock.offset - SymbolSegment.biases[SymbolSegment.stType]; base.extb ¬ tB + p.extBlock.offset - SymbolSegment.biases[SymbolSegment.extType]; base.mdLimit ¬ Symbols.MDFirst + p.mdBlock.size; base.extLimit ¬ SymbolSegment.ExtFirst + p.extBlock.size; base.mainCtx ¬ p.outerCtx; base.stHandle ¬ p; IF p.fgRelBase = 0 OR p.fgCount = 0 THEN { base.sourceFile ¬ NIL; base.fgTable ¬ NIL; } ELSE { q: LONG POINTER TO SymbolSegment.FGHeader = LOOPHOLE[b + p.fgRelBase]; source: LONG STRING = LOOPHOLE[q + SIZE[SymbolSegment.FGHeader[0]] - SIZE[StringBody[0]]]; base.sourceFile ¬ source; base.fgTable ¬ DESCRIPTOR[q + q.offset, q.length]; }; RETURN [base]; }; Pair: TYPE = RECORD [key: ROPE, file: ROPE, names: LIST OF ROPE]; FindList: PROC [key: ROPE, base: LIST OF Pair] RETURNS [which,newBase: LIST OF Pair ¬ NIL] = { newBase ¬ base; WHILE which = NIL DO FOR each: LIST OF Pair ¬ newBase, each.rest WHILE each # NIL DO IF key.Equal[each.first.key] THEN {which ¬ each; RETURN}; ENDLOOP; newBase ¬ InsertPair[[key, NIL, NIL], newBase]; ENDLOOP; }; InsertName: PROC [rope: ROPE, list: LIST OF ROPE] RETURNS [LIST OF ROPE] = { lag: LIST OF ROPE ¬ NIL; FOR each: LIST OF ROPE ¬ list, each.rest WHILE each # NIL DO SELECT rope.Compare[each.first, FALSE] FROM $less => EXIT; $equal => SELECT rope.Compare[each.first, TRUE] FROM $less => EXIT; $equal => RETURN [list]; $greater => {}; ENDCASE; $greater => {}; ENDCASE => ERROR; lag ¬ each; ENDLOOP; IF lag = NIL THEN RETURN [CONS[rope, list]] ELSE {lag.rest ¬ CONS[rope, lag.rest]; RETURN [list]}}; InsertPair: PROC [pair: Pair, list: LIST OF Pair] RETURNS [LIST OF Pair] = { lag: LIST OF Pair ¬ NIL; key: ROPE ¬ pair.key; FOR each: LIST OF Pair ¬ list, each.rest WHILE each # NIL DO SELECT key.Compare[each.first.key, FALSE] FROM $less => EXIT; $equal => SELECT key.Compare[each.first.key, TRUE] FROM $less => EXIT; $equal => RETURN [list]; $greater => {}; ENDCASE; $greater => {}; ENDCASE => ERROR; lag ¬ each; ENDLOOP; IF lag = NIL THEN RETURN [CONS[pair, list]] ELSE {lag.rest ¬ CONS[pair, lag.rest]; RETURN [list]}; }; FirstCtxSe: PUBLIC --SymbolOps-- PROC [stb: STB, ctx: CTXIndex] RETURNS [ISEIndex] = { RETURN [IF ctx = CTXNull THEN ISENull ELSE stb.ctxb[ctx].seList]; }; NameForSe: PUBLIC --SymbolOps-- PROC [stb: STB, sei: ISEIndex] RETURNS [Name] = { RETURN [IF sei = ISENull THEN nullName ELSE stb.seb[sei].hash]; }; NextSe: PUBLIC --SymbolOps-- PROC [stb: STB, sei: ISEIndex] RETURNS [ISEIndex] = { IF sei # ISENull THEN WITH id: stb.seb[sei] SELECT FROM sequential => RETURN [sei + SERecord.id.sequential.SIZE]; linked => RETURN [id.link]; ENDCASE; RETURN [ISENull]; }; SubStringForName: PUBLIC --SymbolOps-- PROC [stb: STB, name: Symbols.Name] RETURNS [s: ConvertUnsafe.SubString] = { s.base ¬ stb.ssb; IF name = nullName THEN s.offset ¬ s.length ¬ 0 ELSE s.length ¬ stb.htb[name].ssIndex - (s.offset ¬ stb.htb[name-Symbols.HTRecord.SIZE].ssIndex+1); }; TypeLink: PUBLIC --SymbolOps-- PROC [stb: STB, type: Symbols.Type] RETURNS [Symbols.Type] = { sei: CSEIndex = UnderType[stb, type]; WITH se: stb.seb[sei] SELECT FROM record => WITH se SELECT FROM linked => RETURN [linkType]; ENDCASE; ENDCASE; RETURN [Symbols.nullType]; }; UnderType: PUBLIC --SymbolOps-- PROC [stb: STB, type: Symbols.Type] RETURNS [CSEIndex] = { sei: Symbols.Type ¬ type; WHILE sei # Symbols.nullType DO sep: Symbols.SEPointer = @stb.seb[sei]; IF LOOPHOLE[sei, Table.IndexRep].tag # Symbols.seTag THEN ERROR; -- This happens if a truly nasty sei is used WITH se: sep­ SELECT FROM id => {IF se.idType # Symbols.typeTYPE THEN ERROR; sei ¬ TypeInfo[se.idInfo]}; cons => EXIT; ENDCASE => ERROR; ENDLOOP; RETURN [LOOPHOLE[sei, CSEIndex]]; }; TypeInfo: PROC [info: Symbols.UNSPEC] RETURNS [Symbols.Type] = INLINE { RETURN [LOOPHOLE[info]]; }; XferMode: PUBLIC --SymbolOps-- PROC [stb: STB, type: Symbols.Type] RETURNS [Symbols.TransferMode] = { sei: CSEIndex = UnderType[stb, type]; RETURN [WITH t: stb.seb[sei] SELECT FROM transfer => t.mode, ENDCASE => $none]; }; }. Eξ MobConnectionImpl.mesa Copyright Σ 1989, 1991 by Xerox Corporation. All rights reserved. Based on MobListerImpl.mesa Created by Michael Plass, May 2, 1989 Michael Plass, September 28, 1989 5:08:38 pm PDT IF ip.namedInstance THEN {Start[$instance]; PutInstanceName[[0,0,import[iti]]]; PutName[ip.name]; End[]} ELSE PutName[ip.name]; IF ee.namedInstance THEN {Start[$instance]; PutInstanceName[[0,0,export[eti]]]; PutName[ee.name]; End[]} ELSE PutName[ee.name]; IF mm.namedInstance THEN {Start[$instance]; PutInstanceName[[0,0,module[mti]]]; PutName[mm.name]; End[]} ELSE PutName[mm.name]; PutLabeledINT[$offset, mob.ctOffset.units]; IF ctp.namedInstance THEN {Start[$instancename]; PutInstanceName[[0,0,config[cti]]]; End[]}; PutFileInfo[ctp.file]; IF cti # CTNull THEN {Start[$parent]; PutNameAndIndex[ctb[cti].name, LOOPHOLE[cti]]; End[]}; PrintImports: PROC = { iti: IMPIndex _ IMPIndex.FIRST; stream.PutF["Imports[%g]:\n", [cardinal[mob.impOffset.units]]]; UNTIL iti = mob.impLimit DO PrintImport[iti]; iti _ iti + IMPRecord.SIZE; IF LOOPHOLE[iti, CARD] > LOOPHOLE[mob.impLimit, CARD] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; stream.PutRope["\n\n"]}; PrintImport: PROC[iti: IMPIndex] = { imp: LONG POINTER TO IMPRecord = @itb[iti]; Tab[2]; PutNameAndIndex[imp.name, LOOPHOLE[iti]]; IF imp.port = $module THEN stream.PutRope[" (module)"]; IF imp.namedInstance THEN { stream.PutRope[", instance name: "]; PutInstanceName[[0,0,import[iti]]]}; stream.PutRope[", file: "]; PrintFileName[imp.file]; PrintLongIndex[LOOPHOLE[imp.file]]; stream.PutF[", gfi: %g, ngfi: %g", [cardinal[imp.modIndex]], [cardinal[1]]]}; PrintGlobals: PROC[] = { amperTable: AmperTable _ NIL; units: INT _ 0; frames: INT _ 0; totalProcs: INT _ 0; procs: INT _ 0; waste: INT _ 0; totalWaste: INT _ 0; gfiSlots: INT _ 0; AmperTable: TYPE = LIST OF AmperTableEntry; AmperTableEntry: TYPE = RECORD [name: ROPE, count: INT, size: INT]; FOR mti: MTIndex _ MTIndex.FIRST, mti + MTSize[mti] UNTIL mti = mob.mtLimit DO mtr: LONG POINTER TO MTRecord = @mtb[mti]; frameSize: CARD _ mtr.framesize; gfis: CARDINAL _ 1; DoBody: PROC[symbols: SymbolTableBase] = { DoFields: PROC[rSei: CSEIndex] RETURNS[maxSpan: CARDINAL _ 0] = { WITH t~~symbols.seb[rSei] SELECT FROM record => maxSpan _ DoContext[t.fieldCtx]; ENDCASE; }; DoContext: PROC[ctx: CTXIndex] RETURNS[maxSpan: CARDINAL _ 0] = { FOR sei: ISEIndex _ SymbolOps.FirstCtxSe[symbols, ctx], SymbolOps.NextSe[symbols, sei] UNTIL sei = ISENull DO IF ~symbols.seb[sei].constant THEN maxSpan _ MAX[DoSymbol[sei], maxSpan]; ENDLOOP; }; DoSymbol: PROC[sei: ISEIndex] RETURNS[span: CARDINAL] = { addr: BitAddress = SymbolOps.DecodeBitAddr[symbols.seb[sei].idValue]; size: CARD = (SymbolOps.DecodeCard[symbols.seb[sei].idInfo]+bitsPerUnit-1) / bitsPerUnit; hti: HTIndex = SymbolOps.NameForSe[symbols, sei]; stream.PutRope[" "]; IF hti # MobListerUtils.nullName THEN { check for leading & ss: MobListerUtils.SubString = SymbolOps.SubStringForName[symbols, hti]; IF ss.length # 0 AND ss.base[ss.offset] = '& THEN { ros: STREAM _ IO.ROS[]; rope: ROPE _ NIL; MobListerUtils.PrintName[hti, ros, symbols]; rope _ ros.RopeFromROS[]; FOR each: AmperTable _ amperTable, each.rest WHILE each # NIL DO IF (each.first.name).Equal[rope] THEN { each.first.size _ each.first.size + size; each.first.count _ each.first.count + 1; GO TO found}; ENDLOOP; amperTable _ CONS [[name: rope, count: 1, size: size], amperTable]; EXITS found => {}; }; }; MobListerUtils.PrintName[hti, stream, symbols]; stream.PutF1["\t%g\n", [cardinal[size]]]; RETURN[addr.bd/bitsPerUnit + size]}; CountProcs: PROC RETURNS[n: CARDINAL _ 0] = TRUSTED { Counts all of the procedures prev: Symbols.BTIndex _ FIRST[Symbols.BTIndex]; bti: Symbols.BTIndex _ prev; DO WITH body~~symbols.bb[bti] SELECT FROM Callable => IF NOT body.inline THEN n _ n + 1; ENDCASE; IF symbols.bb[bti].firstSon # Symbols.BTNull THEN bti _ symbols.bb[bti].firstSon ELSE DO prev _ bti; bti _ symbols.bb[bti].link.index; IF bti = Symbols.BTNull THEN RETURN; IF symbols.bb[prev].link.which # parent THEN EXIT; ENDLOOP; ENDLOOP; }; bti: CBTIndex _ LOOPHOLE[RootBti]; frameOverhead: CARDINAL = 4--words of frame overhead-- * UNITS[WORD]; maxSpan: CARDINAL _ frameOverhead-1; typeIn, typeOut: CSEIndex; IF symbols = NIL THEN { -- No symbols, so say so stream.PutRope["Sorry, no symbols available (file must be local).\n"]; RETURN}; [typeIn, typeOut] _ SymbolOps.TransferTypes[symbols, symbols.bb[bti].ioType]; IF typeIn # CSENull THEN { stream.PutRope[" Global arguments:\n"]; maxSpan _ MAX[DoFields[typeIn], maxSpan]}; IF typeOut # CSENull THEN { stream.PutRope[" Global results:\n"]; maxSpan _ MAX[DoFields[typeOut], maxSpan]}; IF symbols.bb[bti].localCtx # CTXNull THEN { stream.PutRope[" Global variables: (name & units)\n"]; maxSpan _ MAX[DoContext[symbols.bb[bti].localCtx], maxSpan]}; IF ~symbols.bb[bti].hints.noStrings THEN stream.PutRope[" Global string literals or string bodies\n"]; IF maxSpan # frameSize AND frameSize > frameOverhead THEN stream.PutF1[ " %g units not in listed variables or overhead\n", [integer[frameSize - maxSpan]]]; stream.PutRope["\n"]; procs _ CountProcs[]}; IF LOOPHOLE[mti, CARD] > LOOPHOLE[mob.mtLimit, CARD] THEN GO TO Bogus; IF mtr.namedInstance THEN {PutInstanceName[[0,0,module[mti]]]; stream.PutRope[": "]}; PutName[mtr.name]; PutFileStamp[mtr.file, mtr.name]; frames _ frames + 1; procs _ 0; WithSymbolsForModule[mti, DoBody]; IF procs # 0 THEN { waste _ 0; stream.PutF["Global frame size: %g, gfi slots: %g, procs: %g (waste: %g)\n\n", [cardinal[frameSize]], [cardinal[gfis]], [cardinal[procs]], [integer[waste]] ]} ELSE { stream.PutF["Global frame size: %g, gfi slots: %g, procs: ?? (waste: ??)\n\n", [cardinal[frameSize]], [cardinal[gfis]] ]; }; gfiSlots _ gfiSlots + gfis; units _ units + frameSize; totalWaste _ totalWaste + waste; totalProcs _ totalProcs + procs; REPEAT Bogus => PrintGarbage[]; ENDLOOP; IF frames > 1 THEN { stream.PutF["%g units in %g frames using %g gfi slots, %g procs (%g waste)\n", [cardinal[units]], [cardinal[frames]], [cardinal[gfiSlots]], [cardinal[totalProcs]], [cardinal[totalWaste]] ]; stream.PutRope["\n&-variables\n"]; FOR each: AmperTable _ amperTable, each.rest WHILE each # NIL DO stream.PutF["\t%g\t%g\t%g\n", [rope[each.first.name]], [integer[each.first.count]], [integer[each.first.size]]]; ENDLOOP; }; }; WithSymbolsForModule: PROC[mti: MTIndex, inner: PROC[symbols: SymbolTableBase]] = { mm: LONG POINTER TO MTRecord = @mtb[mti]; IF mm.sseg = SGNull THEN GO TO loser ELSE { symbols: SymbolTableBase _ NIL; sgr: LONG POINTER TO SGRecord = @sgb[mm.sseg]; start: CARD _ UnitsToFilePages[sgr.base.units]; pages: CARD _ UnitsToFilePages[sgr.units.units]; nMob: MobDefs.MobBase _ mob; IF start = 0 OR sgr.units.units = 0 OR sgr.file = FTNull THEN GO TO loser; IF sgr.file # FTSelf THEN { -- We have to pull in the symbols from the file system. ftr: LONG POINTER TO FTRecord = @ftb[sgr.file]; fileName: ROPE _ NameToRope[ftr.name].Concat[".mob"]; nMob _ MobListerUtils.ReadMob[fileName ! MobListerUtils.MobErr => GO TO loser ]; }; IF nMob.extended THEN pages _ pages + UnitsToFilePages[sgr.extraUnits.units]; DoSymbols[nMob, inner]; MobListerUtils.FreeMob[nMob]; }; EXITS loser => inner[NIL]; }; DoSymbols: PUBLIC PROC [mob: MobDefs.MobBase, proc: PROC[sym: STB]] = { sym: STB _ NIL; sgb: MobDefs.Base = LOOPHOLE[mob+mob.sgOffset.units]; mtb: MobDefs.Base = LOOPHOLE[mob+mob.mtOffset.units]; sgh: MobDefs.SGHandle = IF mtb[FIRST[MobDefs.MTIndex]].sseg = MobDefs.SGNull THEN ERROR --NoSymbols ELSE @sgb[mtb[FIRST[MobDefs.MTIndex]].sseg]; stb: LONG POINTER TO SymbolSegment.STHeader = LOOPHOLE[mob+sgh.base.units]; IF mob.nConfigs # 0 THEN ERROR; -- Configuration; IF sgh.file # MobDefs.FTSelf OR sgh.units.units = 0 THEN ERROR; -- NoSymbols; IF stb.versionIdent # SymbolSegment.VersionID THEN Consistency check failed! ERROR; -- WrongSymbolsVersion; sym _ InstallTable[stb]; proc[sym]; }; PrintExports: PROC[printOffset: BOOL] = { eti: EXPIndex _ EXPIndex.FIRST; IF printOffset THEN stream.PutF1["Exports[%g]:\n", [cardinal[mob.expOffset.units]]]; UNTIL eti = mob.expLimit DO PrintExport[eti]; eti _ eti + etb[eti].nLinks*EXPLink.SIZE + EXPRecord.SIZE; IF LOOPHOLE[eti, CARD] > LOOPHOLE[mob.expLimit, CARD] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; IF dumpLinks # all THEN stream.PutChar['\n]; stream.PutChar['\n]}; PrintExport: PROC[eti: EXPIndex] = { etr: LONG POINTER TO EXPRecord = @etb[eti]; size: CARDINAL _ etr.nLinks; Tab[2]; PutNameAndIndex[etr.name, LOOPHOLE[eti]]; IF etr.port = $module THEN stream.PutRope[" (module)"]; IF etr.namedInstance THEN { stream.PutRope[", instance name: "]; PutInstanceName[[0,0,export[eti]]]}; stream.PutRope[", file: "]; PrintFileName[etr.file]; PrintLongIndex[LOOPHOLE[etr.file]]; stream.PutRope[", "]; IF ~etr.typeExported THEN stream.PutChar['~]; stream.PutF1["typeExported, #links: %g", [cardinal[etr.nLinks]]]; IF dumpLinks = all THEN { mobName: ROPE = NameToRope[ftb[etr.file].name].Concat[".mob"]; mobVersion: VersionStamp = ftb[etr.file].version; exmob: MobDefs.MobBase _ NIL; inner: PROC[exstb: SymbolTableBase] = { FOR i: CARDINAL IN [0..size) DO link: Link = etr.links[i].from; name: ROPE = NameFromIndex[exstb, i]; isInline: BOOL = Rope.Match["*[inline]*", name, FALSE]; isUnbound: BOOL = link = nullLink AND NOT isInline; IF cmd = $Unbound AND NOT isUnbound THEN LOOP; stream.PutF["\n\t\t%g: ", [cardinal[i]]]; IF isUnbound THEN stream.PutRope["** unbound ** "]; stream.PutRope[name]; IF cmd = $Unbound THEN LOOP; stream.PutRope[": "]; SELECT link.tag FROM proc => stream.PutF["proc[%g,%g]", [cardinal[link.modIndex]], [cardinal[link.offset]]]; type => stream.PutF1["type[%g]", [cardinal[link.offset]]]; ENDCASE => stream.PutF["var[%g,%g]", [cardinal[link.modIndex]], [cardinal[link.offset]]]; ENDLOOP; }; -- End of inner stream.PutRope[", links:"]; exmob _ MobListerUtils.ReadMob[mobName ! MobListerUtils.MobErr => CONTINUE]; SELECT TRUE FROM exmob = NIL => { stream.PutRope[mobName]; stream.PutRope[" not found.\n"]; inner[NIL]}; exmob.version # mobVersion => { stream.PutRope[mobName]; stream.PutRope[", version "]; MobListerUtils.PrintVersion[exmob.version, stream]; stream.PutRope[" found, version "]; MobListerUtils.PrintVersion[mobVersion, stream]; stream.PutRope[" needed.\n"]; exmob _ NIL; inner[NIL]; }; ENDCASE => { DoSymbols[exmob, inner]; }; MobListerUtils.FreeMob[exmob ! MobListerUtils.MobErr => CONTINUE]; stream.PutChar['\n]; }; }; PrintExpVars: PROC = { evi: EVIndex _ EVIndex.FIRST; evLimit: EVIndex = mob.evLimit; stream.PutRope["Exported variables:\n"]; UNTIL evi = evLimit DO PrintExpVar[evi]; evi _ evi + evb[evi].length*CARD.SIZE + EVRecord.SIZE; ENDLOOP; stream.PutChar['\n]}; PrintExpVar: PROC[evi: EVIndex] = { evr: LONG POINTER TO EVRecord = @evb[evi]; Tab[2]; stream.PutF["%g, #offsets: %g, offsets:", [cardinal[LOOPHOLE[evi, CARD]]], [cardinal[evr.length]]]; FOR i: CARDINAL IN [1..evr.length] DO IF i MOD 8 = 1 THEN Tab[4] ELSE stream.PutChar[' ]; stream.PutF1["%b", [cardinal[evr.offsets[i]]]]; IF i # evr.length THEN stream.PutChar[',]; ENDLOOP; stream.PutChar['\n]}; PrintSpaces: PROC = { spi: SPIndex _ SPIndex.FIRST; spLimit: SPIndex = mob.spLimit; stream.PutRope["Spaces:\n"]; UNTIL spi = spLimit DO PrintSpace[spi]; spi _ spi + SPRecord.SIZE + spb[spi].length*SpaceID.SIZE; ENDLOOP; stream.PutChar['\n]}; PrintSpace: PROC[spi: SPIndex] = { spr: LONG POINTER TO SPRecord = @spb[spi]; Tab[2]; PrintLongIndex[LOOPHOLE[spi, CARD]]; stream.PutF1[", segment: [%g]", [cardinal[LOOPHOLE[spr.seg, CARD]]]]; stream.PutF1[", #code packs: %g", [cardinal[spr.length]]]; IF spr.length # 0 THEN stream.PutRope[", code packs: "]; FOR i: CARDINAL IN [0..spr.length) DO Tab[4]; stream.PutRope[" code pack "]; PutName[spr.spaces[i].name]; stream.PutRope[", "]; IF ~spr.spaces[i].resident THEN stream.PutChar['~]; stream.PutF["resident, offset: %b, pages: %g\n", [cardinal[spr.spaces[i].offset]], [cardinal[spr.spaces[i].pages]]]; ENDLOOP; }; PrintModules: PROC = { mti: MTIndex _ MTIndex.FIRST; stream.PutF1["Modules[%g]:\n", [cardinal[mob.mtOffset.units]]]; UNTIL mti = mob.mtLimit DO PrintModule[@mtb[mti], mti]; mti _ mti + MTSize[mti]; IF LOOPHOLE[mti, CARD] > LOOPHOLE[mob.mtLimit, CARD] THEN GO TO Bogus; REPEAT Bogus => PrintGarbage[]; ENDLOOP; stream.PutChar['\n]}; PrintModule: PROC[mth: LONG POINTER TO MTRecord, mti: MTIndex] = { Tab[2]; PutNameAndIndex[mth.name, LOOPHOLE[mti]]; IF mth.namedInstance THEN { stream.PutRope["instance name: "]; PutInstanceName[[0,0,module[mti]]]}; stream.PutRope[", file: "]; PrintFileName[mth.file]; PrintLongIndex[LOOPHOLE[mth.file]]; IF mth.config # CTNull THEN { stream.PutRope[", config: "]; PutNameAndIndex[ctb[mth.config].name, LOOPHOLE[mth.config]]}; Tab[4]; IF mth.inlineFloat THEN stream.PutRope["inline float, "] ELSE { PutSwitch: PROC[sw: CHAR, value: BOOL] = { IF ~value THEN stream.PutChar['-]; stream.PutChar[sw]}; stream.PutRope["switches: "]; PutSwitch['b, mth.boundsChecks]; PutSwitch['c, mth.long]; PutSwitch['j, mth.crossJumped]; PutSwitch['l, mth.linkLoc = $codePrefix]; PutSwitch['n, mth.nilChecks]; PutSwitch['s, ~mth.initial]; stream.PutRope[", "]}; IF ~mth.packageable THEN stream.PutChar['~]; stream.PutRope["packageable, "]; IF mth.residentFrame THEN stream.PutRope["resident frame, "]; Tab[4]; stream.PutF["framesize: %g, gfi: %g, ngfi: %g, links: ", [cardinal[mth.framesize]], [cardinal[mth.modIndex]], [cardinal[1]]]; IF mth.linkLoc = $framePrefix THEN stream.PutRope["frame"] ELSE stream.PutRope["code"]; Tab[4]; stream.PutRope["code: "]; PrintSegment[mth.code.sgi]; stream.PutF[", offset: %b, length: %b", [cardinal[mth.code.offset]], [cardinal[mth.code.length]]]; IF mth.code.linkspace THEN stream.PutRope[", link space"]; IF mth.code.packed THEN stream.PutRope[", packed"]; Tab[4]; stream.PutRope["symbols: "]; PrintSegment[mth.sseg]; IF mth.variables # EVNull THEN { Tab[4]; stream.PutF1[ "exported variables: [%g]", [cardinal[LOOPHOLE[mth.variables, CARD]]]]; }; BEGIN Tab[4]; PrintLinks[mth.links]; Tab[4]; PrintTypes[mth.types]; IF mth.frameRefs THEN { Tab[5]; stream.PutF1["frame type: %g", [cardinal[mth.frameType]]]}; Tab[4]; PrintRefLits[mth.refLiterals]; END; stream.PutChar['\n]}; PrintLinks: PROC[lfi: LFIndex] = { stream.PutRope["#links: "]; IF lfi = LFNull THEN stream.PutRope["none"] ELSE { stream.Put[[cardinal[lfb[lfi].length]]]; IF dumpLinks = all THEN { stream.PutRope[", links:"]; FOR i: CARDINAL IN [0..lfb[lfi].length) DO IF i MOD 7 = 0 THEN Tab[6] ELSE stream.PutChar[' ]; PrintControlLink[lfb[lfi].frag[i]]; IF i + 1 # lfb[lfi].length THEN stream.PutChar[',]; ENDLOOP; }; }; }; PrintTypes: PROC[tfi: TFIndex] = { stream.PutRope["#types: "]; IF tfi = TFNull THEN stream.PutRope["none"] ELSE { stream.PutF["%g, offset: %g", [cardinal[tfb[tfi].length]], [cardinal[tfb[tfi].offset]]]; IF dumpLinks # none THEN { stream.PutRope[", indices:"]; FOR i: CARDINAL IN [0..tfb[tfi].length) DO IF i MOD 7 = 0 THEN Tab[6] ELSE stream.PutChar[' ]; stream.PutF1["[%g]", [cardinal[tfb[tfi].frag[i]]]]; IF i + 1 # tfb[tfi].length THEN stream.PutChar[',]; ENDLOOP; }; }; }; PrintRefLits: PROC[rfi: RFIndex] = { stream.PutRope["#ref lits: "]; IF rfi = RFNull THEN stream.PutRope["none"] ELSE { stream.PutF["%g, offset: %g", [cardinal[rfb[rfi].length]], [cardinal[rfb[rfi].offset]]]; IF dumpLinks # none THEN { stream.PutRope[", indices:"]; FOR i: CARDINAL IN [0..rfb[rfi].length) DO IF i MOD 7 = 0 THEN Tab[6] ELSE stream.PutChar[' ]; stream.PutF1["[%g]", [cardinal[rfb[rfi].frag[i]]]]; IF i + 1 # rfb[rfi].length THEN stream.PutChar[',]; ENDLOOP; }; }; }; PrintFiles: PROC = { fti: FTIndex _ FTIndex.FIRST; stream.PutF1["Files[%g]:\n", [cardinal[mob.ftOffset.units]]]; UNTIL fti = mob.ftLimit DO PrintFile[fti]; fti _ fti + FTRecord.SIZE; IF LOOPHOLE[fti, CARD] > LOOPHOLE[mob.ftLimit, CARD] THEN { PrintGarbage[]; EXIT}; ENDLOOP; }; PrintFile: PROC[fti: FTIndex] = { Tab[2]; SELECT fti FROM FTNull => stream.PutRope["(null)"]; FTSelf => stream.PutRope["(self)"]; ENDCASE => { ftr: LONG POINTER TO FTRecord = @ftb[fti]; PutNameAndIndex[ftr.name, LOOPHOLE[fti]]; stream.PutRope[", version: "]; MobListerUtils.PrintVersion[ftr.version, stream]}; }; Get the module name Get the module file name hti: HTIndex _ Symbols.HTFirst; FOR hti: HTIndex _ Symbols.HTFirst, hti + SIZE[HTRecord] UNTIL hti = stb.htLimit DO IF stb.htb[hti].ssIndex = stb.htb[hti - SIZE[HTRecord]].ssIndex THEN { firstCopiedHash _ hti; EXIT}; REPEAT FINISHED => firstCopiedHash _ LENGTH[stb.hashVec]; ENDLOOP; At this point all of the entries have been made. Utility Prints PutFileInfo: PROC [fti: FTIndex] = { Start[$file]; PrintFileName[fti]; PrintLongIndex[LOOPHOLE[fti]]; End[] }; PrintIndex: PROC[index: CARDINAL] = { stream.PutF1[" [%g]", [cardinal[index]]] }; stream.PutF1[" [%g]", [cardinal[index]]] Utility Puts Executable part of ListMob Table Bases dumpLinks: {none, all} _ IF cmd # $ShortMob THEN all ELSE none; If no such named list is found, one is created and inserted into the base. NameFromIndex: PROC[exstb: SymbolTableBase, index: CARDINAL] RETURNS[ROPE _ NIL] = { IF exstb # NIL THEN { btr: LONG POINTER TO BodyRecord = @exstb.bb[RootBti]; ctx: CTXIndex _ btr.localCtx; ctxr: LONG POINTER TO CTXRecord = @exstb.ctxb[ctx]; root: ISEIndex = exstb.ctxb[ctx].seList; sei: ISEIndex _ root; DO sep: LONG POINTER TO ISERecord _ NIL; IF sei = SENull THEN EXIT; sep _ @exstb.seb[sei]; SELECT TRUE FROM ~sep.mark4 OR SymbolOps.LinkMode[exstb, sei] = $manifest => {}; index = LOOPHOLE[sep.idValue, CARD] => { We found the item! ros: IO.STREAM = IO.ROS[]; MobListerUtils.PrintSei[sei, ros, exstb]; SELECT TRUE FROM sep.idType = typeTYPE => {}; sep.constant => ros.PutRope[" [inline]"]; ENDCASE; RETURN[ros.RopeFromROS[]]; }; ENDCASE; IF (sei _ SymbolOps.NextSe[exstb, sei]) = root THEN EXIT; ENDLOOP; }; RETURN[IO.PutFR["* * * * item %g", [cardinal[index]]]]}; Κ5’•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Οeœ6™BKšœ™K™%K™0K˜—KšΟk œ΅˜ΎK˜šΟnœž˜Kšžœ<˜CKšžœ*˜1K˜Kšœžœžœ˜Kšœ žœ˜(Kšœ žœ˜,šœžœ˜$K˜—Kšœžœžœžœ˜ Kšœžœ˜*Kšœžœ!˜6Kšœ žœ˜Kšœ žœ&˜7K˜š Ÿœžœžœžœžœ˜3Kšžœ/˜5K˜—š Ÿœžœžœžœžœ˜3Kšžœ/˜5K˜—Kšœžœ˜Kšœžœ˜Kšœ žœ˜&Kšœ žœ˜&šœ žœ˜ KšŸœ˜#—Kšœ žœ˜"šœžœ˜*Kšœ˜—šœ žœ˜"KšŸœ˜$Kšœ&˜&—šœ žœ˜ KšŸœ˜!—Kšœ žœ˜"šœ žœ˜"KšŸœ˜$—Kšœ žœ˜$šœ žœ˜ KšŸœ˜!—Kšœ žœ˜"Kšœ žœ˜"Kšœ žœ˜ Kšœ žœ˜$Kšœ žœ˜ Kšœ žœ˜"šœ žœ˜ KšŸœ˜!KšŸœ˜!—Kšœ žœ˜"Kšœ žœ˜ šœ žœ˜"KšŸœ˜$—Kšœ žœ˜šœ žœ˜ KšŸœ˜!—Kšœ žœ˜"šœžœ˜Kšœ"˜"—Kšœžœ˜Kšœ žœ˜&Kšœ žœ˜&šœ žœ˜ KšŸœ˜!—Kšœ žœ˜"Kšœ žœ˜"Kšœ žœ˜$Kšœ žœ˜ Kšœ žœ˜"Kšœžœžœ˜Kšœ žœžœ ˜!Kšœ žœžœ ˜!šœ žœ˜ KšŸœ˜!—Kšœ žœ˜$šžœžœžœ˜K˜—Kšœžœ˜Kšœ"˜"šœ žœ˜ KšŸœ˜!—Kšœ žœ˜"šœ žœ˜ KšŸœ˜!—Kšœ žœ˜"Kšœ žœ˜ Kšœ žœ˜ Kšœ žœ˜"Kšœžœžœ˜/Kšžœžœžœ˜#Kšœžœžœ)˜Hšœ žœ˜ KšŸœ˜!—Kšœ žœ˜"šœ žœ˜"KšŸœ˜$—Kšœ žœ˜$Kšœžœ˜*K˜Kš Ÿœžœžœ žœžœžœ˜5K˜K˜—Kš œ žœžœžœžœžœžœ˜7šŸ œžœžœžœžœžœžœžœ žœžœžœžœžœžœ˜ƒKšœžœžœ žœ˜šŸœž œ˜)Kš œžœžœžœžœžœ˜!Kšœžœ˜"Kšœ˜—šŸœž œ˜Kš œžœžœžœžœ˜&K˜Kšžœ žœžœ žœ˜+Kšœ˜—KšŸœž œžœ0žœ˜OKšŸœž œžœ ˜(KšŸœž œžœ ˜(Kš Ÿœž œžœ žœžœ˜1Kš Ÿœž œžœ žœžœžœ ˜BKšŸ œž œžœ˜@Kšœ¦˜¦Kšžœ žœžœžœ˜Kšœ˜K˜—š.Ÿ œžœž œžœ žœžœžœžœ žœžœžœ žœžœžœ žœžœžœ žœžœžœžœžœžœ˜ˆšŸœžœ˜&KšŸœžœ˜%KšŸœžœ˜KšŸœžœžœ ˜KšŸœžœžœ ˜!KšŸ œžœ žœžœ(˜SKšŸœžœ žœžœ*˜XšŸ œžœ˜šžœžœ˜)š žœžœžœžœž˜TKšœžœžœžœ˜*š žœžœžœžœžœž˜:Kšžœžœ˜ —šžœ™KšžœŸœCŸœ™TKšžœ™—Kšœ˜šžœ˜K˜—Kšžœ˜—KšŸœ˜—šžœžœŸœ ˜)š žœžœ žœ žœžœž˜sKšœžœžœžœ˜*šŸ œžœžœžœ˜"šžœžœžœž˜"Kšžœ1žœžœ˜=Kšžœžœžœžœ˜,Kšžœ˜—Kšžœžœ˜ Kšœ˜—Kšžœžœžœžœžœžœžœžœ˜GKš žœžœžœ žœžœ˜5šžœ˜šžœŸœ ˜Kšœ˜šžœžœžœž˜"Kšžœ1žœžœ˜=Kšœžœžœ,žœ ˜_Kšœ˜KšŸœ˜Kšžœ˜—KšŸœ˜—Kšžœ ˜$—šžœ™KšžœŸœCŸœ™TKšžœ™—šžœ˜K˜—Kšžœ˜—KšŸœ˜—šžœžœŸœ ˜)šžœžœžœž˜NKšœžœžœžœ˜)Kšžœžœžœžœžœžœžœžœ˜Fšžœ™KšžœŸœDŸœ™UKšžœ™—K˜šžœ˜K˜—Kšžœ˜—KšŸœ˜—šœ˜K˜——šŸ œžœ%˜7šžœž˜Kšœ˜Kšœ˜šžœ˜ Kšœžœžœžœ˜*Kšœ0˜0Kšœ˜——Kšœ˜K˜—šŸ œžœ˜Kš œžœžœžœžœžœ ˜jKšœ,˜,Kšœ˜K˜—šŸ œžœ˜šœŸœ ˜Kšœ-˜-Kšœ&˜&Kšœ&˜&Kšœ&˜&Kšœ+˜+Kšœ&˜&Kšœ.˜.Kšœ,˜,Kšœ0˜0Kšœ.˜.Kšœ4˜4Kšœ6˜6Kšœ(˜(Kšœ(˜(KšŸœ˜—Kšœ˜K˜—šŸ œžœ˜Kšœžœ˜Kšžœžœžœ˜!šœŸœ˜Kšœ+™+šžœž˜K˜Kšœžœžœ˜:Kšžœžœžœžœžœžœžœžœ˜Fšžœ˜K˜—Kšžœ˜—KšŸœ˜—Kšœ˜K˜—šŸ œžœ˜#Kšœžœžœžœ˜*KšŸœ˜Kšœ˜KšžœžœŸœ5Ÿœ™\Kšœ™Kš žœžœŸœ*žœŸœ™\šžœžœŸœ ˜+šžœžœžœž˜(šžœžœž˜#Kšœ%˜%Kšœ Ÿœ%Ÿœ˜™>—šžœžœž™9šœ ™ K™3K™ ——Kšœ™Kšœ™—Kšžœžœžœžœžœžœžœžœ™FKšžœžœ<™UK™K™!K™K™K™ K™Kšœ"™"K™šžœ žœ™Kšœ ™ šœN™NKšœ™Kšœ™Kšœ™Kšœ™Kšœ™——šžœ™šœN™NKšœ™Kšœ™Kšœ™—™K™——Kšœ™Kšœ™K™ K™ K™šžœ™K™—Kšžœ™—šžœ žœ™šœN™NKšœ™Kšœ™Kšœ™Kšœ™Kšœ™Kšœ™—Kšœ"™"šžœ*žœžœž™@šœ™KšœR™R—Kšžœ™—K™—K™K™—šŸœžœžœ™SKšœžœžœžœ™)Kšžœžœžœžœ™$šžœ™Kšœžœ™Kšœžœžœžœ™.Kšœžœ$™/Kšœžœ%™0Kšœ™Kš žœ žœžœžœžœžœ™Jšžœžœ™Kšœ7™7Kšœžœžœžœ™/Kšœ žœ'™5KšœBžœžœ ™PK™—Kšžœžœ8™MKšœ™Kšœ™K™—šž™Kšœžœ™—K™K™—K™š Ÿ œžœžœžœžœ™GKšœžœžœ™Kšœžœ™5Kšœžœ™5Kšœžœžœ)žœžœ  œžœ žœ™Kš œžœžœžœžœ™KKšžœžœžœ ™1Kš žœžœžœžœ  ™Mšžœ,ž™2K™Kšžœ ™—Kšœ™K™ Kšœ™K™—šŸ œžœžœ™)Kšœžœ™Kšžœ žœA™Tšžœž™K™Kšœ$žœ žœ™:Kšžœžœžœžœžœžœžœžœ™Gšžœ™K™—Kšžœ™—Kšžœžœ™,Kšœ™K™—šŸ œžœ™$Kšœžœžœžœ™+Kšœžœ™K™Kšœžœ™)Kšžœžœ™7šžœžœ™Kšœ$™$Kšœ$™$—Kšœ™Kšœ™Kšœžœ ™#Kšœ™Kšžœžœ™/KšœA™Ašžœžœ™Kšœ žœ1™>Kšœ1™1Kšœžœ™šœžœ™'šžœžœžœ ž™K™Kšœžœ™%Kšœ žœ"žœ™7Kšœ žœžœžœ ™3Kš žœžœžœ žœžœ™.Kšœ)™)Kšžœ žœ"™3Kšœ™Kšžœžœžœ™Kšœ™šžœ ž™™KšœO™O—™Kšœ2™2—šžœ™ KšœN™N——Kšžœ™—Kšœ ™—Kšœ™KšœBžœ™Lšžœžœž™šœžœ™Kšœ™Kšœ ™ Kšœžœ™ —šœ™Kšœ™Kšœ™Kšœ3™3Kšœ#™#Kšœ0™0Kšœ™Kšœ ™ Kšœžœ™ Kšœ™—šžœ™ K™K™——Kšœ8žœ™BKšœ™K™—Kšœ™K™—šŸ œžœ™Kšœžœ™K™Kšœ(™(šžœž™K™Kšœžœžœ žœ™7Kšžœ™—Kšœ™K™—šŸ œžœ™#Kšœžœžœžœ™*K™Kšœ4žœžœ™cšžœžœžœž™%Kšžœžœžœžœ™3Kšœ/™/Kšžœžœ™*Kšžœ™—Kšœ™K™—šŸ œžœ™Kšœžœ™K™Kšœ™šžœž™K™Kšœžœžœ™9Kšžœ™—Kšœ™K™—šŸ œžœ™"Kšœžœžœžœ™*K™Kšœžœžœ™$Kšœ*žœ žœ™EKšœ:™:Kšžœžœ"™8šžœžœžœž™%K™Kšœ™Kšœ™Kšœ™Kšžœžœ™3K™tKšžœ™—šœ™K™——šŸ œžœ™Kšœžœ™Kšœ?™?šžœž™K™K™Kšžœžœžœžœžœžœžœžœ™Fšžœ™K™—Kšžœ™—Kšœ™K™—š Ÿ œžœžœžœžœ™BK™Kšœžœ™)šžœžœ™Kšœ"™"Kšœ$™$—Kšœ™Kšœ™Kšœžœ ™#šžœžœ™Kšœ™Kšœ&žœ™=—K™Kšžœžœ"žœ™?™šŸ œžœžœ žœ™*Kšžœžœ*™8K™—Kšœ™Kšœ ™ Kšœ™Kšœ™Kšœ)™)Kšœ™Kšœ™Kšœ™—Kšžœžœ6™NKšžœžœ$™=K™K™}Kšžœžœžœ™WK™Kšœ6™6K™bKšžœžœ ™:Kšžœžœ™3K™Kšœ™Kšœ™šžœžœ™ Kšœ™šœ ™ Kšœ&žœžœ™G—Kšœ™—šž™™K™K™K™šžœžœ™K™Kšœ;™;—K™K™—Kšžœ™—Kšœ™K™—šŸœžœžœžœ˜+Kšžœ žœ˜šœ˜K˜——šŸ œžœ™"Kšœ™Kšžœžœ™+šžœ™Kšœ(™(šžœžœ™Kšœ™šžœžœžœž™*Kšžœžœžœžœ™3K™#Kšžœžœ™3Kšžœ™—Kšœ™—Kšœ™—šœ™K™——šŸ œžœ™"Kšœ™Kšžœžœ™+šžœ™KšœX™Xšžœžœ™Kšœ™šžœžœžœž™*Kšžœžœžœžœ™3Kšœ3™3Kšžœžœ™3Kšžœ™—Kšœ™—Kšœ™—šœ™K™——šŸ œžœ™$Kšœ™Kšžœžœ™+šžœ™KšœX™Xšžœžœ™Kšœ™šžœžœžœž™*Kšžœžœžœžœ™3Kšœ3™3Kšžœžœ™3Kšžœ™—Kšœ™—Kšœ™—šœ™K™——šŸ œžœ™Kšœžœ™Kšœ=™=šžœž™K™Kšœžœ™š žœžœžœžœžœžœ™;Kšœ™Kšžœ™—Kšžœ™—Kšœ™K™—šŸ œžœ™!Kšœ™šžœž™Kšœ#™#Kšœ#™#šžœ™ Kšœžœžœžœ™*Kšœžœ™)Kšœ™Kšœ2™2——™K™——šŸ œžœ˜+Kšœžœ˜7Kšœžœžœ˜4Kšœžœžœžœ˜Kš œžœžœžœžœ˜K˜!šŸ œžœžœžœ˜4šžœ˜KšœVžœž˜ošžœ?žœž˜NKš œžœžœžœžœ˜4Kšžœ˜—Kšžœ˜—Kšžœžœ˜—šŸ œžœ˜#šžœžœ˜Kšœ˜Kšœžœžœžœ˜/Kšœžœžœžœ˜Kšœžœžœ˜K˜šŸœžœ˜Kšœžœžœžœ˜/š žœžœ žœžœžœžœ˜DKšœžœžœ˜šžœžœ˜K˜4šžœžœž˜&˜Kšžœžœžœžœ˜;—Kšžœ˜—Kšœ˜—Kšœžœžœ˜Kšœ'˜'Kšœžœ˜K˜9—K˜—šžœžœž˜˜ K˜—˜ K˜&—Kšžœžœ˜—K˜Kšœ™Kšœžœžœ˜Kšœ:˜:Kšœžœ˜!K˜K˜*šžœžœžœ˜ Kšœ™Kšœ žœžœ˜Kšœžœžœ˜Kšœ8˜8Kšœžœ˜%K˜AK˜ —K˜"šž˜Kšžœžœžœ˜Kšœ ˜ Kšžœ+žœžœ˜7Kšžœ˜—K˜—K˜—Kšž˜Kšœžœ˜(Kšœ™šžœ'žœ žœž™Sšžœ&žœžœ™FKšœžœ™—Kšžœžœžœ™9Kšžœ™—Kšžœ˜šžœ˜KšœVžœž˜ošžœ?žœž˜NKšœ#˜#Kšžœ˜—Kšžœ˜—šžœ˜KšœSžœž˜lšžœ?žœž˜NKšœ#˜#šœ ˜ Kšœ;˜;Kšœ!˜!—Kšžœ˜—Kšžœ˜—Kšœ0™0šžœ žœžœŸœ ˜'šžœ žœž˜Kšœ˜Kšœžœžœžœ˜!šœŸœ˜Kšœ!˜!š žœžœžœžœŸœ˜=Kšœ˜KšŸœ˜—šžœžœ˜%šžœ žœž˜Kšœ˜K˜Kšžœ˜—KšŸœ˜—KšŸœ˜—K˜Kšžœ˜—KšŸœ˜—Kšœ˜K˜—Kšœ™K˜šŸ œžœ™$KšŸœ™ Kšœ™Kšœžœ™KšŸœ™Kšœ™K™—šŸ œžœ˜%šžœž˜Kšœ˜Kšœ˜Kšžœ˜"—šœ˜K˜——šŸ œžœžœ™%Kšœ(™(Kšœ™K™—šŸœžœžœ˜%Kšœ(™(Kšœ˜K˜—šŸ œžœ˜K˜Kšœ$˜$K˜K˜—šŸœžœžœ˜Kšœ˜K˜—Kšœ ™ K˜šŸœžœ˜ Kšœ˜Kšœ˜K˜—šŸœžœ(˜<šžœ˜Kšžœ9˜=Kšžœ ˜—Kšœ˜K˜—šŸ œžœžœžœ˜1Kšœ žœžœžœžœžœžœžœžœ˜BKš œžœžœžœ žœ˜,Kšœžœ˜Kšœžœ˜K•StartOfExpansion[len: INT, p: PROCšœžœ ˜Kš Ÿœžœžœžœžœžœ˜BKšžœ ˜&Kšœ˜K˜—šŸœžœžœžœ˜9Kšœ žœžœžœžœžœžœžœžœ˜BKš œžœžœžœ žœ˜,Kšœžœ˜Kšœžœ˜K–[len: INT, p: PROCšœžœžœ˜,šžœžœžœž˜'K–[to: REF TEXT, from: CHAR]˜K˜Kšžœ˜—K˜#Kšœ˜Kšœ˜K˜—š Ÿœžœžœžœžœ˜MKšœ žœžœžœžœžœžœžœžœ˜BKš œžœžœžœ žœ˜,Kšœžœ˜Kšœžœ˜K–[len: INT, p: PROCšœžœžœ ˜.šžœžœžœž˜'K–[to: REF TEXT, from: CHAR]˜K˜Kšžœ˜—K˜K˜KšœEžœ˜LK˜#K˜Kšœ˜K˜—šŸœžœ&žœžœ˜^š œ žœžœžœžœ ˜