DIRECTORY Basics USING [], Commander USING [CommandProc, Handle, Register], CommanderOps USING [Failed, ParseToList], DesiredPackageSymbols, IO USING [Close, EndOf, GetLineRope, PutChar, PutF, PutF1, PutFR1, PutRope, RIS, RopeFromROS, ROS, STREAM], List USING [Compare, CompareProc, Sort], ListRTMob USING [PrintRTMob], Literals USING [Base, LitDescriptor, LTIndex, LTNull, LTRecord, MSTIndex, STIndex, STNull], MobDefs USING [Base, FTIndex, FTRecord, FTSelf, Mob, MobBase, MTHandle, MTIndex, MTNull, MTRecord, NameRecord, SGHandle, SGNull, SGRecord, VersionID], MobLister USING [ListMob], MobListerUtils USING [FreeMob, InitMobTab, MobErr, PrintLongIndex, PrintName, PrintSE, PrintSei, PrintTree, PrintVersion, ReadMob, ShortName], PackageSymbols, PFS USING [EnumerateForNames, Error, NameProc, PATH, PathFromRope, RopeFromPath, StreamOpen], PFSNames USING [SetVersionNumber, ShortName], Rope USING [Cat, Compare, Concat, Equal, Fetch, Flatten, Length, Match, Replace, ROPE, SkipTo], SortedSymbolLister USING [AddSymbols, PrintInterface], SymbolOps, Symbols, SymbolSegment USING [Base, biases, bodyType, ctxType, ExtFirst, extType, FGHeader, htType, ltType, mdType, seType, ssType, STHeader, stType, treeType, VersionID], SymbolTable USING [SymbolTableBaseRep], SymbolTablePrivate USING [SymbolTableBase, SymbolTableBaseRep], Tree USING [Base, NodeName], VM USING [WordsForPages]; KLister: PROGRAM IMPORTS MobLister, Commander, CommanderOps, IO, List, MobListerUtils, ListRTMob, PFS, PFSNames, Rope, SortedSymbolLister, SymbolOps, VM EXPORTS SymbolTable = BEGIN unitsPerVMPage: NAT = VM.WordsForPages[1]; -- this instead of wordsPerPage so that we UnitsToVMPages: PROC[units: INT] RETURNS [INT] = { RETURN[(units+unitsPerVMPage-1)/unitsPerVMPage]; }; Mob: TYPE = MobDefs.Mob; BitAddress: TYPE = Symbols.BitAddress; BTIndex: TYPE = Symbols.BTIndex; BTRecord: TYPE = Symbols.BodyRecord; CSEIndex: TYPE = Symbols.CSEIndex; typeTYPE: CSEIndex = Symbols.typeTYPE; ContextLevel: TYPE = Symbols.ContextLevel; lZ: ContextLevel = Symbols.lZ; CTXIndex: TYPE = Symbols.CTXIndex; CTXNull: CTXIndex = Symbols.CTXNull; CTXRecord: TYPE = Symbols.CTXRecord; FTIndex: TYPE = MobDefs.FTIndex; FTRecord: TYPE = MobDefs.FTRecord; HTIndex: TYPE = Symbols.HTIndex; ISEIndex: TYPE = Symbols.ISEIndex; ISENull: ISEIndex = Symbols.ISENull; ISERecord: TYPE = SERecord.id; LTIndex: TYPE = Literals.LTIndex; LTNull: LTIndex = Literals.LTNull; LTRecord: TYPE = Literals.LTRecord; LitDescriptor: TYPE = Literals.LitDescriptor; MDIndex: TYPE = Symbols.MDIndex; MSTIndex: TYPE = Literals.MSTIndex; MSTNull: MSTIndex = LOOPHOLE[STNull]; MTIndex: TYPE = MobDefs.MTIndex; MTNull: MTIndex = MobDefs.MTNull; MTRecord: TYPE = MobDefs.MTRecord; Name: TYPE = Symbols.Name; nullName: Name = Symbols.nullName; NodeName: TYPE = Tree.NodeName; RefMob: TYPE = REF Mob; RefMTRecord: TYPE = REF MTRecord; RefSGRecord: TYPE = REF SGRecord; RootBti: BTIndex = Symbols.RootBti; ROPE: TYPE = Rope.ROPE; SEIndex: TYPE = Symbols.SEIndex; SENull: SEIndex = Symbols.SENull; SERecord: TYPE = Symbols.SERecord; SGRecord: TYPE = MobDefs.SGRecord; STIndex: TYPE = Literals.STIndex; STNull: STIndex = Literals.STNull; STREAM: TYPE = IO.STREAM; Switches: TYPE = PACKED ARRAY CHAR['A..'Z] OF BOOL; SymbolTableBase: TYPE = REF SymbolTableBaseRep; SymbolTableBaseRep: PUBLIC TYPE = SymbolTablePrivate.SymbolTableBaseRep; TransferMode: TYPE = Symbols.TransferMode; TypeClass: TYPE = Symbols.TypeClass; PackDescriptor: TYPE = LONG DESCRIPTOR FOR ARRAY OF PackageSymbols.OuterPackRecord; InnerPackDescriptor: TYPE = LONG DESCRIPTOR FOR ARRAY OF PackageSymbols.InnerPackRecord; OuterPackRecordSeq: TYPE = RECORD [ SEQUENCE len: NAT OF DesiredPackageSymbols.OuterPackRecord ]; InnerPackRecordSeq: TYPE = RECORD [ SEQUENCE len: NAT OF DesiredPackageSymbols.InnerPackRecord ]; DPackDescriptor: TYPE = LONG DESCRIPTOR FOR ARRAY OF DesiredPackageSymbols.OuterPackRecord; UC: PROC [c: CHAR] RETURNS [CHAR] = { RETURN [IF c IN ['a..'z] THEN 'A + (c - 'a) ELSE c]; }; ListSymbols: Commander.CommandProc = TRUSTED { ENABLE CommanderOps.Failed => {msg ¬ errorMsg; GO TO failed}; name: ROPE; combinedSymbols, totalFiles: LIST OF REF ANY ¬ NIL; sortedSymbols: BOOL ¬ (cmd.procData.clientData = $SortedSymbols) OR (cmd.procData.clientData = $SortedDefs); any: BOOL ¬ FALSE; switches: Switches ¬ ALL[FALSE]; args: LIST OF ROPE ¬ CommanderOps.ParseToList[cmd].list; filesDone: INT ¬ 0; namePath: PFS.PATH; EachName: PFS.NameProc = TRUSTED { ENABLE MobListerUtils.MobErr => { msg ¬ err; continue ¬ FALSE; GO TO failed }; mob: MobDefs.MobBase; Cleanup: PROC = { MobListerUtils.FreeMob[mob]; }; fullFName: ROPE ~ PFS.RopeFromPath[name]; filesDone ¬ filesDone + 1; mob ¬ MobListerUtils.ReadMob[fullFName]; {ENABLE UNWIND => Cleanup[]; short: ROPE = MobListerUtils.ShortName[fullFName]; configOk: BOOL ¬ SELECT cmd.procData.clientData FROM $Mob, $ShortMob, $Exports, $Files, $Globals, $RTMob, $Unbound => TRUE, ENDCASE => FALSE; defsOK: BOOL ¬ SELECT cmd.procData.clientData FROM $Mob, $ShortMob, $Files, $Symbols, $Using, $SortedSymbols, $SortedDefs, $Interface => TRUE, ENDCASE => FALSE; SELECT TRUE FROM mob.versionIdent # MobDefs.VersionID => cmd.out.PutF1["Not a valid Cedar mob file: %g\n", [rope[short]]]; mob.nConfigs # 0 AND ~configOk => (cmd.out).PutF1["Bound configurations not supported: %g\n", [rope[short]]]; mob.definitions AND NOT defsOK => (cmd.out).PutF1["Definitions files not supported: %g\n", [rope[short]]]; ~mob.definitions AND cmd.procData.clientData = $Interface => (cmd.out).PutF1["File is not an interface: %g\n", [rope[short]]]; switches['M] AND (mob.nConfigs # 0 OR mob.definitions) => continue ¬ TRUE; sortedSymbols AND ~mob.definitions AND cmd.procData.clientData = $SortedDefs => continue ¬ TRUE; ENDCASE => { totalFiles ¬ CONS[short, totalFiles]; combinedSymbols ¬ ProcessSymbols[mob, cmd, fullFName, switches, combinedSymbols]; }; Cleanup[]; continue ¬ TRUE; }; EXITS failed => NULL; }; -- End of EachName IF sortedSymbols THEN (cmd.out).PutRope["Combined symbols listing to Symbols.sort\n"]; WHILE args # NIL DO ENABLE PFS.Error => { msg ¬ error.explanation; GO TO failed }; name ¬ args.first; args ¬ args.rest; SELECT TRUE FROM Rope.Match["-*", name] => { sense: BOOL ¬ TRUE; num: INT ¬ 0; FOR i: INT IN [1..name.Length[]) DO c: CHAR ¬ name.Fetch[i]; SELECT c FROM IN ['A..'Z] => switches[c] ¬ sense; IN ['a..'z] => switches[c-('a-'A)] ¬ sense; IN ['0..'9] => num ¬ num * 10 + (c - '0); '~ => sense ¬ NOT sense; ENDCASE; ENDLOOP; LOOP; }; Rope.Match["*.mob", name, FALSE], Rope.Match["*.mob!*", name, FALSE] => {}; ENDCASE => name ¬ name.Concat[".mob"]; any ¬ TRUE; namePath ¬ PFS.PathFromRope[name]; IF PFSNames.ShortName[namePath].version.versionKind = none THEN namePath ¬ PFSNames.SetVersionNumber[namePath, [highest, 0]]; filesDone ¬ 0; MobListerUtils.InitMobTab[]; PFS.EnumerateForNames[PFS.PathFromRope[name], EachName]; IF result # NIL THEN GO TO failed; IF filesDone = 0 THEN IO.PutF1[cmd.out, "No matches found for '%g'\n", [rope[name]] ]; ENDLOOP; IF sortedSymbols THEN { stream: STREAM ¬ NIL; lastChar: CHAR ¬ 0C; CompareCaseless: List.CompareProc = TRUSTED { r1: ROPE = NARROW[ref1]; r2: ROPE = NARROW[ref2]; RETURN [r1.Compare[r2, FALSE]]; }; totalFiles ¬ List.Sort[totalFiles, CompareCaseless]; stream ¬ PFS.StreamOpen[PFS.PathFromRope["Symbols.sort"], $create]; stream.PutRope["Combined symbols for: "]; WHILE totalFiles # NIL DO stream.PutF1["%g ", [rope[NARROW[totalFiles.first]]]]; totalFiles ¬ totalFiles.rest; ENDLOOP; stream.PutRope["\n\n"]; combinedSymbols ¬ List.Sort[combinedSymbols, CompareCaseless]; WHILE combinedSymbols # NIL DO line: ROPE; entry: ROPE = NARROW[combinedSymbols.first]; ch: CHAR ¬ UC[entry.Fetch[0]]; ris: IO.STREAM ¬ IO.RIS[entry]; IF ch # lastChar THEN {stream.PutF1["--%g\n", [character[ch]]]; lastChar ¬ ch}; WHILE ~ris.EndOf[] DO line ¬ ris.GetLineRope[]; stream.PutF1[" %g\n", [rope[line]]]; ENDLOOP; combinedSymbols ¬ combinedSymbols.rest; ENDLOOP; stream.Close[]; }; IF NOT any THEN { result ¬ $Failure; msg ¬ IO.PutFR1["Usage: %g file ...", [rope[cmd.command]]]; }; EXITS failed => result ¬ $Failure; }; ProcessSymbols: PROC [mob: MobDefs.MobBase, cmd: Commander.Handle, fullFName: ROPE, switches: Switches, combinedSymbols: LIST OF REF ANY ¬ NIL] RETURNS [newCombinedSymbols: LIST OF REF ANY] = { short: ROPE = MobListerUtils.ShortName[fullFName]; data: REF = cmd.procData.clientData; sortedSymbols: BOOL ¬ (data = $SortedSymbols) OR (data = $SortedDefs); stream: STREAM; sourceName: ROPE ¬ NIL; sym: SymbolTableBase ¬ NIL; ext: ROPE ¬ GetExt[cmd]; 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]; outName: ROPE ¬ NIL; newCombinedSymbols ¬ combinedSymbols; IF ext # NIL THEN { SELECT TRUE FROM switches['C] => ext ¬ NIL; Rope.Match["*.mob", short, FALSE] => outName ¬ short.Replace[short.Length[]-4, 4, ext]; ENDCASE => outName ¬ short.Concat[ext]; }; IF mob.nConfigs = 0 THEN { mtr: MobDefs.MTHandle; mtb: MobDefs.Base; sgb: MobDefs.Base; sgr: MobDefs.SGHandle; pages: CARDINAL; IF sgh.file # MobDefs.FTSelf OR sgh.units.units = 0 THEN ERROR; --NoSymbols; IF stb.versionIdent # SymbolSegment.VersionID THEN ERROR; -- WrongSymbolsVersion; sym ¬ InstallTable[stb]; mtb ¬ LOOPHOLE[mob + mob.mtOffset.units]; mtr ¬ @mtb[FIRST[MTIndex]]; sgb ¬ LOOPHOLE[mob + mob.sgOffset.units]; sgr ¬ @sgb[mtr.sseg]; pages ¬ IF mob.extended THEN UnitsToVMPages[sgr.units.units+sgr.extraUnits.units] ELSE UnitsToVMPages[sgr.units.units]; IF pages = 0 THEN ERROR; }; sourceName ¬ RopeForMobName[mob, [IF mob.nConfigs = 0 THEN mob.source+1 ELSE mob.source]]; IF ext = NIL THEN { stream ¬ cmd.out; stream.PutF1["Listing for %g\n", [rope[fullFName]] ]; } ELSE { (cmd.out).PutF[ "%g output to %g\n", [rope[cmd.command]], [rope[outName]]]; stream ¬ PFS.StreamOpen[PFS.PathFromRope[outName], $create]; stream.PutRope[outName]; }; stream.PutF1["\n object: %g {", [rope[short]]]; MobListerUtils.PrintVersion[mob.version, stream]; stream.PutRope[Rope.Cat["}\n source: ", sourceName, " {"]]; MobListerUtils.PrintVersion[mob.sourceVersion, stream, TRUE]; stream.PutRope["}\n creator: {"]; MobListerUtils.PrintVersion[mob.creator, stream]; stream.PutRope["}\n\n"]; SELECT cmd.procData.clientData FROM $Mob => MobLister.ListMob[stream, mob, $Mob]; $Bodies => PrintBodies[stream, sym]; $Code => stream.PutRope["CodeList not available"]; $FGT => stream.PutRope["FGTList not available"]; $Exports => MobLister.ListMob[stream, mob, $Exports]; $Files => PrintFiles[stream, mob]; $Globals => MobLister.ListMob[stream, mob, $Globals]; $RTMob => ListRTMob.PrintRTMob[stream, mob]; $Symbols => PrintSymbols[mob.definitions, stream, sym, mob]; $Unbound => MobLister.ListMob[stream, mob, $Unbound]; $SortedSymbols, $SortedDefs => newCombinedSymbols ¬ SortedSymbolLister.AddSymbols[rList: combinedSymbols, stb: sym]; $Interface => SortedSymbolLister.PrintInterface[st: stream, stb: sym]; $Using => PrintUsing[stream, sym]; ENDCASE; IF stream # NIL AND stream # cmd.out THEN stream.Close[]; }; 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]; }; GetExt: PROC [cmd: Commander.Handle] RETURNS [ext: ROPE ¬ NIL] = { SELECT cmd.procData.clientData FROM $Mob, $ShortMob => ext ¬ ".mobList"; $Bodies => ext ¬ ".bodyList"; $Code => ext ¬ ".codeList"; $Exports => ext ¬ ".exportsList"; $FGT => ext ¬ ".fgtList"; $Files => ext ¬ ".filesList"; $Globals => ext ¬ ".globalFramesList"; $RTMob => ext ¬ ".rtMobList"; $Symbols => ext ¬ ".symbolList"; $Unbound => ext ¬ ".unboundList"; $Using => ext ¬ ".usingList"; $SortedSymbols, $SortedDefs => ext ¬ NIL; $Interface => ext ¬ ".ifList"; ENDCASE => ext ¬ ".list"; }; PrintBodyClass: PROC [stream: STREAM, class: Symbols.ProcClass] = { s: ROPE ¬ SELECT class FROM Blank =>"blank", Outer => "outer", Inner => "inner", Install => "install", Init => "init", Catch => "catch", Scope => "scope", Fork => "fork", ENDCASE => "(??)"; stream.PutRope[s]; }; PrintBodies: PUBLIC PROC [stream: STREAM, stb: SymbolTableBase] = { PrintBody: PROC [bti: BTIndex] RETURNS [BOOL] = { body: LONG POINTER TO BTRecord = @stb.bb[bti]; stream.PutF1["Body %g: ", [cardinal[LOOPHOLE[bti]]]]; PrintBodyClass[stream, body.class]; stream.PutRope[" "]; WITH b~~body SELECT FROM Callable => { MobListerUtils.PrintSei[b.id, stream, stb]; IF b.inline THEN stream.PutRope[" [inline]"] ELSE { stream.PutF1[", ep: %g", [cardinal[b.entryIndex]]]; IF b.kind = Inner THEN stream.PutF1[", frame addr: %g", [cardinal[b.frameOffset]]]; }; stream.PutRope[", attrs: "]; stream.PutChar[IF ~b.noXfers THEN 'x ELSE '-]; stream.PutChar[IF b.hints.safe THEN 's ELSE '-]; stream.PutChar[IF b.hints.nameSafe THEN 'n ELSE '-]; IF ~b.hints.noStrings THEN stream.PutRope["\n string literals"]; }; ENDCASE => stream.PutRope["(anon)"]; stream.PutRope["\n context: "]; MobListerUtils.PrintLongIndex[body.localCtx, stream]; stream.PutF1[", level: %g", [cardinal[body.level]]]; WITH body.info SELECT FROM Internal => { stream.PutF1[", frame size: %g", [cardinal[frameSize]]]; stream.PutRope[", tree root: "]; MobListerUtils.PrintLongIndex[bodyTree, stream]}; External => stream.PutF1[", bytes: %g", [cardinal[bytes]]]; ENDCASE; stream.PutRope["\n\n"]; RETURN [FALSE]}; [] ¬ SymbolOps.EnumerateBodies[stb, RootBti, PrintBody]; stream.PutRope["\n"]; }; PrintGlobalFrames: PUBLIC PROC [stream: STREAM, stb: SymbolTableBase] = { PrintBody: PROC [bti: BTIndex] RETURNS [BOOL] = { body: LONG POINTER TO BTRecord = @stb.bb[bti]; stream.PutRope["Body"]; WITH b~~body SELECT FROM Callable => { MobListerUtils.PrintSei[b.id, stream, stb]; IF b.inline THEN stream.PutRope[" [inline]"] ELSE { stream.PutF1[", ep: %g", [cardinal[b.entryIndex]]]; IF b.kind = Inner THEN stream.PutF1[", frame addr: %g", [cardinal[b.frameOffset]]]; }; stream.PutRope[", attrs: "]; stream.PutChar[IF ~b.noXfers THEN 'x ELSE '-]; stream.PutChar[IF b.hints.safe THEN 's ELSE '-]; stream.PutChar[IF b.hints.nameSafe THEN 'n ELSE '-]; IF ~b.hints.noStrings THEN stream.PutRope["\n string literals"]}; ENDCASE => RETURN [FALSE]; stream.PutRope["\n context: "]; MobListerUtils.PrintLongIndex[body.localCtx, stream]; stream.PutF1[", level: %g", [cardinal[body.level]]]; WITH body.info SELECT FROM Internal => { stream.PutF1[", frame size: %g", [cardinal[frameSize]]]; IF body.kind = Callable THEN MobListerUtils.PrintTree[[subtree[index: bodyTree]], 0, stream, stb] ELSE {stream.PutRope[", tree root: "]; MobListerUtils.PrintLongIndex[bodyTree, stream]}}; ENDCASE; stream.PutRope["\n\n"]; RETURN [FALSE]}; [] ¬ SymbolOps.EnumerateBodies[stb, RootBti, PrintBody]; stream.PutRope["\n"]; }; PrintOuterPackRecord: PROC [stream: STREAM, oph: LONG POINTER TO PackageSymbols.OuterPackRecord, stb: SymbolTableBase] = { stream.PutF1[" [entryIndex: %g, name: ", [cardinal[oph.entryIndex]]]; MobListerUtils.PrintName[name: oph.hti, stream: stream, stb: stb]; IF oph.length # 0 THEN stream.PutF1[", length: %g", [cardinal[oph.length]]]; IF oph.firstSon # PackageSymbols.IPNull THEN stream.PutF1[", firstSon: %g", [cardinal[oph.firstSon]]]; IF oph.placed THEN stream.PutRope[", placed"]; IF oph.attr1 THEN stream.PutRope[", attr1"]; IF oph.attr2 THEN stream.PutRope[", attr2"]; IF oph.resident THEN stream.PutRope[", resident"]; stream.PutRope["]\n"]; }; NameForEntryIndex: PROC [stb: SymbolTableBase, entryIndex: CARDINAL] RETURNS [name: Symbols.Name] = { VisitBody: PROC [bti: BTIndex] RETURNS [BOOL] = { body: LONG POINTER TO BTRecord = @stb.bb[bti]; WITH b~~body SELECT FROM Callable => { IF ~b.inline THEN { IF b.entryIndex = entryIndex THEN { name ¬ SymbolOps.NameForSe[stb, b.id]; RETURN[TRUE]; }; }; }; ENDCASE => NULL; RETURN [FALSE]}; name ¬ Symbols.nullName; [] ¬ SymbolOps.EnumerateBodies[stb, RootBti, VisitBody]; }; PrintInnerPackRecord: PROC [stream: STREAM, iph: LONG POINTER TO PackageSymbols.InnerPackRecord, stb: SymbolTableBase] = { name: Symbols.Name ¬ NameForEntryIndex[stb, iph.entryIndex]; stream.PutRope[" ["]; IF iph.length # 0 THEN stream.PutF1["length: %g, ", [cardinal[iph.length]]]; stream.PutF1["entryIndex: %g, (name: ", [cardinal[iph.entryIndex]]]; MobListerUtils.PrintName[name: name, stream: stream, stb: stb]; stream.PutRope[")"]; IF iph.lastSon THEN stream.PutRope[", lastSon"]; stream.PutRope["]\n"]; }; PrintDesiredOuterPackRecord: PROC [stream: STREAM, oph: LONG POINTER TO DesiredPackageSymbols.OuterPackRecord, stb: SymbolTableBase] = { stream.PutRope[" [name: "]; MobListerUtils.PrintName[name: oph.hti, stream: stream, stb: stb]; stream.PutRope[", bti: "]; MobListerUtils.PrintLongIndex[oph.bti, stream]; stream.PutRope[", class: "]; PrintBodyClass[stream, oph.class]; IF oph.firstSon # DesiredPackageSymbols.IPNull THEN stream.PutF1[", firstSon: %g", [cardinal[oph.firstSon]]]; stream.PutRope["]\n"]; }; PrintDesiredInnerPackRecord: PROC [stream: STREAM, iph: LONG POINTER TO DesiredPackageSymbols.InnerPackRecord, stb: SymbolTableBase] = { stream.PutRope[" [name: "]; MobListerUtils.PrintName[name: iph.hti, stream: stream, stb: stb]; stream.PutRope[", bti: "]; MobListerUtils.PrintLongIndex[iph.bti, stream]; stream.PutRope[", class: "]; PrintBodyClass[stream, iph.class]; IF iph.lastSon THEN stream.PutRope[", lastSon"]; stream.PutRope["]\n"]; }; PrintSymbols: PUBLIC PROC [definitions: BOOL, stream: STREAM, stb: SymbolTableBase, mob: MobDefs.MobBase] = { stheader: LONG POINTER TO SymbolSegment.STHeader = stb.stHandle; nDOuter: CARDINAL ¬ 0; outer: REF OuterPackRecordSeq ¬ NIL; inner: REF InnerPackRecordSeq ¬ NIL; ctx: CTXIndex; limit: CTXIndex; limit ¬ LOOPHOLE[stb.stHandle.ctxBlock.size]; ctx ¬ CTXIndex.FIRST + CTXRecord.nil.SIZE; UNTIL ctx = limit DO PrintContext[ctx, definitions, stream, stb]; stream.PutRope["\n\n"]; ctx ¬ ctx + (WITH stb.ctxb[ctx] SELECT FROM included => CTXRecord.included.SIZE, imported => CTXRecord.imported.SIZE, ENDCASE => CTXRecord.simple.SIZE); ENDLOOP; IF ~definitions THEN { -- print desired pack arrays stream.PutRope["\n"]; [outer, inner] ¬ BuildDesiredPackArrays[stb, mob]; nDOuter ¬ outer.len; stream.PutF1["\n Desired OuterPackRecords: (%g)\n", [cardinal[nDOuter]]]; FOR i: CARD IN [0..outer.len) DO stream.PutF1[" %g: ", [cardinal[i]]]; PrintDesiredOuterPackRecord[stream, @outer[i], stb]; ENDLOOP; outer ¬ NIL; stream.PutF1["\n\nDesired InnerPackRecords: (%g)\n", [cardinal[inner.len]]]; FOR i: CARD IN [0..inner.len) DO stream.PutF1[" %g: ", [cardinal[i]]]; PrintDesiredInnerPackRecord[stream, @inner[i], stb]; ENDLOOP; stream.PutRope["\n"]; inner ¬ NIL; }; }; SortPackInfo: PROC [a: DPackDescriptor, l, u: CARDINAL] = { h: CARDINAL ¬ u - l; DO h ¬ h/2; FOR k: CARDINAL IN [l+h .. u) DO i: CARDINAL ¬ k; j: CARDINAL ¬ k-h; key: Symbols.Name ¬ a[k].hti; t: DesiredPackageSymbols.OuterPackRecord ¬ a[k]; WHILE LOOPHOLE[key, CARD] < LOOPHOLE[a[j].hti, CARD] DO a[i] ¬ a[j]; i ¬ j; IF j < l+h THEN EXIT; j ¬ j-h; ENDLOOP; a[i] ¬ t; ENDLOOP; IF h <= 1 THEN EXIT; ENDLOOP }; NBodies: PROC [stb: SymbolTableBase] RETURNS [nOuter, nInnerBodies: CARDINAL] = { CountOuterBodies: PROC [bti: Symbols.BTIndex] = { WITH body: bb[bti] SELECT FROM Callable => IF ~body.inline THEN { CountInnerBodies[bti]; nOuter ¬ nOuter + 1; }; ENDCASE }; CountInnerBodies: PROC [bti: Symbols.BTIndex] = { WITH body: bb[bti] SELECT FROM Callable => CountInner[bti]; ENDCASE }; CountInner: PROC [root: Symbols.BTIndex] = { ProcessBody: PROC [bti: Symbols.BTIndex] RETURNS [BOOL] = { WITH body: bb[bti] SELECT FROM Callable => IF (~body.inline AND body.level > Symbols.lL) OR (body.class = Scope) OR (body.class = Catch) THEN nInnerBodies ¬ nInnerBodies + 1; ENDCASE => NULL; RETURN [FALSE]; }; IF root # Symbols.RootBti THEN [] ¬ SymbolOps.EnumerateBodies[stb, root, ProcessBody] ELSE FOR sonBti: Symbols.BTIndex ¬ SymbolOps.SonBti[stb, root], SymbolOps.SiblingBti[stb, sonBti] UNTIL sonBti = Symbols.BTNull DO WITH body: bb[sonBti] SELECT FROM Callable => NULL; -- processed as an outer body ENDCASE => [] ¬ SymbolOps.EnumerateBodies[ stb, sonBti, ProcessBody]; ENDLOOP; }; bb: Symbols.Base = stb.bb; nOuter ¬ 0; nInnerBodies ¬ 0; CountOuterBodies[Symbols.RootBti]; FOR bti: Symbols.BTIndex ¬ SymbolOps.SonBti[stb, Symbols.RootBti], SymbolOps.SiblingBti[stb, bti] UNTIL bti = Symbols.BTNull DO CountOuterBodies[bti]; ENDLOOP; nOuter ¬ nOuter + 1; -- For the installation proc body }; BuildDesiredPackArrays: PROC [stb: SymbolTableBase, mob: MobDefs.MobBase] RETURNS [outer: REF OuterPackRecordSeq, inner: REF InnerPackRecordSeq] = { stheader: LONG POINTER TO SymbolSegment.STHeader = stb.stHandle; nInnerBodies: CARDINAL; nOuter: CARDINAL; [nOuter, nInnerBodies] ¬ NBodies[stb]; IF nOuter # 0 THEN { BodyLength: PROC [info: Symbols.BodyInfo] RETURNS [CARDINAL] = INLINE { RETURN [WITH info SELECT FROM External => bytes, ENDCASE => 0]; }; OuterBody: PROC [bti: Symbols.BTIndex] = { WITH body: bb[bti] SELECT FROM Callable => IF ~body.inline THEN { hti: Symbols.Name ¬ SymbolOps.NameForSe[stb, body.id]; ss: SymbolOps.SubString ¬ SymbolOps.SubStringForName[stb, hti]; outer[next] ¬ DesiredPackageSymbols.OuterPackRecord[ hti: hti, bti: bti, firstSon: InnerBodies[bti], class: body.class ]; next ¬ next + 1; }; ENDCASE }; InnerBodies: PROC [root: Symbols.BTIndex] RETURNS [origin: DesiredPackageSymbols.IPIndex] = { ProcessBody: PROC [bti: Symbols.BTIndex] RETURNS [BOOL] = { WITH body: bb[bti] SELECT FROM Callable => IF (~body.inline AND body.level > Symbols.lL) OR (body.class = Scope) OR (body.class = Catch) THEN { buffer ¬ DesiredPackageSymbols.InnerPackRecord[ hti: SymbolOps.NameForSe[stb, body.id], bti: bti, lastSon: FALSE, class: body.class ]; IF origin = DesiredPackageSymbols.IPNull THEN origin ¬ nextIP; inner[nextIP] ¬ buffer; nextIP ¬ nextIP + 1; }; ENDCASE => NULL; RETURN [FALSE]; }; buffer: DesiredPackageSymbols.InnerPackRecord; origin ¬ DesiredPackageSymbols.IPNull; IF root # Symbols.RootBti THEN [] ¬ SymbolOps.EnumerateBodies[stb, root, ProcessBody] ELSE FOR sonBti: Symbols.BTIndex ¬ SymbolOps.SonBti[stb, root], SymbolOps.SiblingBti[stb, sonBti] UNTIL sonBti = Symbols.BTNull DO WITH body: bb[sonBti] SELECT FROM Callable => NULL; -- processed as an outer body ENDCASE => [] ¬ SymbolOps.EnumerateBodies[ stb, sonBti, ProcessBody]; ENDLOOP; IF origin # DesiredPackageSymbols.IPNull THEN { inner[nextIP-1].lastSon ¬ TRUE; }; }; bb: Symbols.Base = stb.bb; outerPtr: LONG POINTER TO DesiredPackageSymbols.OuterPackRecord; outerDesc: DPackDescriptor; next: CARDINAL ¬ 0; nextIP: DesiredPackageSymbols.IPIndex ¬ DesiredPackageSymbols.IPIndex.FIRST; outer ¬ NEW[OuterPackRecordSeq[nOuter]]; inner ¬ NEW[InnerPackRecordSeq[nInnerBodies]]; outerPtr ¬ @outer[0]; outerDesc ¬ DESCRIPTOR[outerPtr, nOuter]; OuterBody[Symbols.RootBti]; FOR bti: Symbols.BTIndex ¬ SymbolOps.SonBti[stb, Symbols.RootBti], SymbolOps.SiblingBti[stb, bti] UNTIL bti = Symbols.BTNull DO OuterBody[bti]; ENDLOOP; OuterBody[InstallationBody[stb]]; IF next # nOuter OR nextIP # nInnerBodies THEN ERROR; SortPackInfo[outerDesc, 1, nOuter]; }; }; InstallationBody: PROC [stb: SymbolTableBase] RETURNS [bti: Symbols.BTIndex] = { sth: LONG POINTER TO SymbolSegment.STHeader = stb.stHandle; bti ¬ Symbols.BTFirst; WHILE bti - Symbols.BTFirst < sth.bodyBlock.size DO body: LONG POINTER TO BTRecord = @stb.bb[bti]; IF body.class = Install THEN RETURN[bti]; WITH x: body SELECT FROM Callable => bti ¬ bti + SIZE[Callable BTRecord]; Other => bti ¬ bti + SIZE[Other BTRecord]; ENDCASE => ERROR; ENDLOOP; ERROR; -- There should always be an installation bti }; PrintContext: PROC [ctx: CTXIndex, definitionsOnly: BOOL, stream: STREAM, stb: SymbolTableBase] = { sei, root: ISEIndex; cp: LONG POINTER TO CTXRecord = @stb.ctxb[ctx]; stream.PutRope["Context: "]; MobListerUtils.PrintLongIndex[ctx, stream]; IF stb.ctxb[ctx].level # lZ THEN stream.PutF1[", level: %g", [cardinal[cp.level]]]; WITH c~~cp SELECT FROM included => { stream.PutRope[", copied from: "]; MobListerUtils.PrintName[stb.mdb[c.module].moduleId, stream, stb]; stream.PutRope[" ["]; MobListerUtils.PrintName[stb.mdb[c.module].fileId, stream, stb]; stream.PutRope[", "]; MobListerUtils.PrintVersion[stb.mdb[c.module].stamp, stream]; stream.PutRope["], context: "]; MobListerUtils.PrintLongIndex[c.map, stream]}; imported => { stream.PutRope[", imported from: "]; MobListerUtils.PrintName[stb.mdb[stb.ctxb[c.includeLink].module].moduleId, stream, stb]}; ENDCASE; root ¬ sei ¬ stb.ctxb[ctx].seList; DO IF sei = SENull THEN EXIT; MobListerUtils.PrintSE[sei, 2, definitionsOnly , stream, stb]; IF (sei ¬ SymbolOps.NextSe[stb, sei]) = root THEN EXIT; ENDLOOP; }; PrintUsing: PROC [stream: STREAM, stb: SymbolTableBase] = { limit: CTXIndex = LOOPHOLE[stb.stHandle.ctxBlock.size]; ctx: CTXIndex ¬ CTXIndex.FIRST + CTXRecord.nil.SIZE; firstUsing: BOOL ¬ TRUE; pairs: LIST OF Pair ¬ NIL; ros: 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: 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 stream.PutRope["No DIRECTORY.\n"] ELSE { stream.PutRope["DIRECTORY\n"]; WHILE pairs # NIL DO pair: Pair = pairs.first; names: LIST OF ROPE ¬ pair.names; stream.PutRope[" "]; stream.PutRope[pair.key]; IF NOT (pair.key).Equal[pair.file, FALSE] THEN { stream.PutF1[": FROM \"%g\"", [rope[pair.file]]]}; stream.PutRope[" USING ["]; WHILE names # NIL DO stream.PutRope[names.first]; IF names.rest # NIL THEN stream.PutRope[", "]; names ¬ names.rest; ENDLOOP; stream.PutRope[IF pairs.rest # NIL THEN "],\n" ELSE "];\n"]; pairs ¬ pairs.rest; ENDLOOP; }; }; PrintFiles: PROC [stream: STREAM, mob: MobDefs.MobBase] = { nFiles: CARDINAL = (mob.ftLimit-FIRST[FTIndex])/SIZE[FTRecord]; IF nFiles IN [1..1024] THEN { ftb: MobDefs.Base ¬ LOOPHOLE[mob + mob.ftOffset.units]; fti: MobDefs.FTIndex ¬ FIRST[FTIndex]; stream.PutF1["# files: %g\n\n", [integer[nFiles]]]; FOR fti ¬ FTIndex.FIRST, fti+FTRecord.SIZE UNTIL fti = mob.ftLimit DO ftp: LONG POINTER TO FTRecord ¬ @ftb[fti]; stream.PutF["%g - fti: %g", [rope[RopeForMobName[mob, ftp.name]]], [integer[LOOPHOLE[fti]]]]; stream.PutRope[", version: "]; MobListerUtils.PrintVersion[ftp.version, stream]; stream.PutRope["\n"]; ENDLOOP; }; }; RopeForMobName: PROC[mob: MobDefs.MobBase, n: MobDefs.NameRecord] RETURNS[ROPE] = { CharSeq: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF CHAR]; ssb: MobDefs.Base = LOOPHOLE[mob + mob.ssOffset.units]; ss: LONG POINTER TO CharSeq = LOOPHOLE[ssb]; index: CARDINAL = n+4; len: CARDINAL = ss[index]-0C; ros: STREAM = IO.ROS[]; FOR i: NAT IN [index+1..index+len] DO ros.PutChar[ss[i]]; ENDLOOP; RETURN[ros.RopeFromROS[]]}; 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]}; }; Commander.Register[ "MobLister", ListSymbols, "List the contents of a mob file.", $Mob]; Commander.Register[ "XBodyLister", ListSymbols, "List the bodies for a mob file.", $Bodies]; Commander.Register[ "XCodeLister", ListSymbols, "List the code for a mob file (not available).", $Code]; Commander.Register[ "XExportsLister", ListSymbols, "List the exports for a mob file.", $Exports]; Commander.Register[ "XFGTLister", ListSymbols, "List the fine grain table for a mob file (not available).", $FGT]; Commander.Register[ "XFilesLister", ListSymbols, "List the items used by a mob file.", $Files]; Commander.Register[ "XGlobalFramesLister", ListSymbols, "List the global frames for a mob file.", $Globals]; Commander.Register[ "ShortMobLister", ListSymbols, "List the symbols (no links) for a mob file.", $ShortMob]; Commander.Register[ "XSymbolLister", ListSymbols, "List the symbols for a mob file.", $Symbols]; Commander.Register[ "RTMobLister", ListSymbols, "List the symbols for a mob file.", $RTMob]; Commander.Register[ "XUnboundLister", ListSymbols, "List the items used by a mob file.", $Unbound]; Commander.Register[ "XUsingLister", ListSymbols, "List the items used by a mob file.", $Using]; Commander.Register[ "XSortedSymbolLister", ListSymbols, "Produce a sorted list of all symbols in a collection of files (writes Symbols.sorted)", $SortedSymbols]; Commander.Register[ "XSortedDefsLister", ListSymbols, "Produce a sorted list of all symbols in the defs files of a collection of files (writes Symbols.sorted)", $SortedDefs]; Commander.Register[ "XInterfaceLister", ListSymbols, "Produce a list of all interface record items the defs files of a collection of files (writes Symbols.sorted)", $Interface]; END. δ KLister.mesa Copyright Σ 1985, 1989, 1990, 1991 by Xerox Corporation. All rights reserved. Sweet October 9, 1985 2:45:46 pm PDT Satterthwaite March 8, 1986 5:20:20 pm PST Andy Litman July 25, 1988 6:35:02 pm PDT JKF February 27, 1990 11:39:08 am PST Russ Atkinson (RRA) November 21, 1989 11:42:52 pm PST Willie-s, February 11, 1991 6:15 pm PST Michael Plass, November 26, 1991 4:22 pm PST can have source compatibility between the two worlds. T Y P E S & C O N S T A N T S Major procedures [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] [name: PATH] RETURNS [continue: BOOL] begin ListSymbols name _ FS.ExpandName[name].fullFName; IF NOT Rope.Match["*!*", name] THEN name _ name.Concat["!h"]; [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] Now we have a single-module file. Get the module. Get the source fullFName for later add one to source to compensate for Mimosa bug Open the output stream (if any) base.opb _ tB + p.outerPackBlock.offset - SymbolSegment.biases[SymbolSegment.opType]; Mimosa doesn't turn bodies into External! IF body.kind = Callable THEN MobListerUtils.PrintTree[[subtree[index: bodyTree]], 0, stream, stb] ELSE { Shell sort of a[l..u) Build the tables that we wish were issued by Mimosa instead of the current Pack tables 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 procedures If no such named list is found, one is created and inserted into the base. I N I T Κ*b–(cedarcode) style•NewlineDelimiter ™codešœ ™ Kšœ ΟeœC™NKšΟy$™$Kšž*™*Kšž(™(KšΟkœ"™%K™5K™'K™,K˜šŸ ˜ KšœŸœ˜Kšœ Ÿœ!˜0Kšœ Ÿœ˜)K˜Kš ŸœŸœDŸœŸœŸœ˜kKšœŸœ˜(Kšœ Ÿœ˜Kšœ ŸœM˜[KšœŸœ‰˜–Kšœ Ÿœ ˜KšœŸœz˜ŽK˜KšŸœŸœ&Ÿœ*˜]Kšœ Ÿœ˜-KšœŸœGŸœ ˜_KšœŸœ˜6Kšœ ˜ Kšœ˜KšœŸœ˜’Kšœ Ÿœ˜'KšœŸœ'˜?KšœŸœ˜KšŸœŸœ˜K˜——šΟnœŸ˜KšŸœ%Ÿœ#Ÿœ1Ÿ˜‡KšŸœ ˜KšœŸœ˜KšœŸœŸœΟc*˜UKšœ5™5š  œŸœŸœŸœŸœ˜2KšŸœ*˜0Kšœ˜—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š œ ŸœŸœŸœŸœ ŸœŸœ˜3KšœŸœŸœ˜/KšœŸœŸœ)˜HKšœŸœ˜*Kšœ Ÿœ˜$Kš œŸœŸœŸ œŸœŸœŸœ ˜SKš œŸœŸœŸ œŸœŸœŸœ ˜XšœŸœŸœ˜#KšŸœŸœŸœ&˜:K˜—šœŸœŸœ˜#KšŸœŸœŸœ&˜:K˜—Kš œŸœŸœŸ œŸœŸœŸœ'˜[K˜K˜—Kšœ™™š ŸœŸœŸœŸœŸœ˜%Kš ŸœŸœŸœ ŸœŸœ˜4Kšœ˜K˜—š  œŸœ˜.Kš œŸœ ŸœŸœŸœŸœ™:KšŸœ˜Kšœ(ŸœŸœ ˜6KšœŸœ˜ Kš œŸœŸœŸœŸœŸœ˜3šœŸœ˜Kšœ+Ÿœ)˜V—KšœŸœŸœ˜KšœŸœŸœ˜ KšœŸœŸœŸœ&˜8Kšœ Ÿœ˜Kšœ ŸœŸœ˜K˜•StartOfExpansion. -- [fullFName: ROPE] RETURNS [continue: BOOL]š œŸœ Ÿœ˜"KšœŸœŸœ Ÿœ™%KšŸ˜Kšœ1ŸœŸœŸœ ˜GKšœ˜š œŸœ˜K˜K˜—Kšœ ŸœŸœ˜)Kšœ˜Kšœ(˜(K˜šœŸœŸœ˜KšœŸœ'˜2šœ ŸœŸœŸ˜4KšœAŸœ˜FKšŸœŸœ˜—šœŸœŸœŸ˜2KšœVŸœ˜[KšŸœŸœ˜—šŸœŸœŸ˜˜'KšœA˜A—šœŸœ ˜!KšœK˜K—šœŸœŸœ ˜!KšœH˜H—šœŸœ(˜Kšœ˜Kšœ˜šŸœŸœŸ˜šœ˜KšœŸœŸœ˜KšœŸœ˜ šŸœŸœŸœŸ˜#KšœŸœ˜šŸœŸ˜ KšŸœ!˜#KšŸœ)˜+KšŸœ'˜)KšœŸœ˜KšŸœ˜—KšŸœ˜—KšŸœ˜K˜—KšœŸœŸœ˜KKšŸœ˜&—KšœŸœ˜ KšœŸœ™%Kšœ Ÿœ˜"KšŸœ9Ÿœ>˜}KšŸœŸœŸœ™=Kšœ˜K˜KšŸœŸœ˜8Kš Ÿœ ŸœŸœŸœŸœ˜"šŸœŸ˜KšŸœ>˜@—KšŸœ˜—šŸœŸœ˜KšœŸœŸœ˜Kšœ Ÿœ˜–> -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]š œŸœ˜-Kš œŸœŸœŸœŸœŸœ™:KšœŸœŸœ˜KšœŸœŸœ˜KšŸœŸœ˜Kšœ˜—Kšœ4˜4Kšœ Ÿœ Ÿœ(˜CKšœ)˜)šŸœŸœŸ˜KšœŸœ˜6Kšœ˜KšŸœ˜—Kšœ˜Kšœ>˜>šŸœŸœŸ˜KšœŸœ˜ KšœŸœŸœ˜,KšœŸœŸœ˜Kš œŸœŸœŸœŸœ˜KšŸœŸœ:˜OšŸœŸ˜Kšœ˜Kšœ%˜%KšŸœ˜—Kšœ'˜'KšŸœ˜—Kšœ˜Kšœ˜—šŸœŸœŸœ˜Kšœ˜KšœŸœ3˜;Kšœ˜—KšŸœ˜"Kšœ˜K˜—š œŸœ:Ÿœ'ŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜ΒKšœŸœ'˜2KšœŸœ˜$KšœŸœŸœ˜FKšœŸœ˜Kšœ ŸœŸœ˜KšœŸœ˜KšœŸœ˜KšœŸœ˜5KšœŸœ˜5KšœŸœŸœ)ŸœŸœ‘ œŸœ Ÿœ˜‘Kš œŸœŸœŸœŸœ˜KKšœ ŸœŸœ˜Kšœ%˜%K˜šŸœŸœŸœ˜šŸœŸœŸ˜KšœŸœ˜šœŸœ˜$Kšœ2˜2—KšŸœ ˜'—K˜—šŸœŸœ˜Kšœ1™1Kšœ˜KšœŸ˜Kšœ˜Kšœ˜KšœŸœ˜Kš ŸœŸœŸœŸœ‘ ˜LKšŸœ,ŸœŸœ‘˜QKšœ˜KšœŸœ˜)Kšœ Ÿœ ˜KšœŸœ˜)Kšœ˜Kš œŸœŸœŸœ6Ÿœ!˜wKšŸœ ŸœŸœ˜K˜—Kšœ"™"šœ ˜ K™.KšœŸœŸœŸœ˜9—K™Kšœ™šŸœŸ˜ šŸœ˜K˜Kšœ5˜5Kšœ˜—šŸœ˜šœ˜Kšœ;˜;—Kšœ Ÿœ Ÿœ!˜˜E—KšŸœ˜————Kšœ˜—Kšœ˜Kšœ ˜ Kšœ˜Kšœ"˜"šŸœ_ŸœŸ˜Kšœ˜KšŸœ˜—Kšœ‘!˜6K˜K˜KšœV™V—š  œŸœ.Ÿœ ŸœŸœ˜”Kšœ ŸœŸœŸœ'˜@KšœŸœ˜KšœŸœ˜K˜Kšœ&˜&šŸœ Ÿœ˜š   œŸœŸœŸœŸœ˜GKš ŸœŸœŸœŸœŸœ˜?Kšœ˜—š  œŸœ˜*šŸœŸœŸ˜˜ šŸœŸœ˜Kšœ6˜6Kšœ?˜?šœ4˜4Kšœ ˜ K˜ K˜Kšœ˜K˜—K˜K˜——KšŸ˜—Kšœ˜—š  œŸœŸœ,˜]š  œŸœŸœŸœ˜;šŸœŸœŸ˜˜ š ŸœŸœŸœŸœŸœ˜dšœ/˜/Kšœ'˜'K˜ Kšœ Ÿœ˜Kšœ˜Kšœ˜—KšŸœ'Ÿœ˜>Kšœ˜K˜K˜——KšŸœŸœ˜—KšŸœŸœ˜Kšœ˜—Kšœ.˜.Kšœ&˜&šŸœ˜KšŸœ7˜;šŸ˜šŸœY˜\šŸœŸ˜ šŸœŸœŸ˜!Kšœ Ÿœ‘˜/KšŸœ>˜E—KšŸœ˜————šŸœ'Ÿœ˜/KšœŸœ˜Kšœ˜—Kšœ˜—Kšœ˜Kšœ ŸœŸœŸœ'˜@Kšœ˜KšœŸœ˜KšœFŸœ˜LK˜KšœŸœ˜(KšœŸœ#˜.Kšœ˜Kšœ Ÿ œ˜)K˜K˜šŸœ_ŸœŸ˜Kšœ˜KšŸœ˜—Kšœ!˜!KšŸœŸœŸœŸœ˜5Kšœ#˜#K˜—Kšœ˜K˜—š œŸœŸœ˜PKšœŸœŸœŸœ'˜;Kšœ˜šŸœ,Ÿ˜3KšœŸœŸœŸœ˜.KšŸœŸœŸœ˜)šŸœ ŸœŸ˜KšœŸœ˜0KšœŸœ˜*KšŸœŸœ˜—KšŸœ˜—KšŸœ‘-˜4K˜K˜—š  œŸœ"Ÿœ Ÿœ˜cK˜KšœŸœŸœŸœ˜/Kšœ˜Kšœ+˜+KšŸœŸœ3˜SšŸœŸœŸ˜˜ Kšœ"˜"KšœB˜BKšœ˜Kšœ@˜@Kšœ˜Kšœ=˜=Kšœ˜Kšœ.˜.—˜ Kšœ$˜$KšœY˜Y—KšŸœ˜—K˜"šŸ˜KšŸœŸœŸœ˜Kšœ>˜>KšŸœ+ŸœŸœ˜7KšŸœ˜—Kšœ˜K˜—š  œŸœ Ÿœ˜;KšœŸœ˜7KšœŸœŸœ˜4Kšœ ŸœŸœ˜KšœŸœŸœŸœ˜KšœŸœŸœŸœ˜K˜!š  œŸœŸœŸœ˜4šŸœ˜KšœVŸœŸ˜ošŸœ?ŸœŸ˜NKš œŸœŸœŸœŸœ˜4KšŸœ˜—KšŸœ˜—KšŸœŸœ˜—š  œŸœ˜#šŸœŸœ˜Kšœ˜KšœŸœŸœŸœ˜/KšœŸœŸœŸœ˜KšœŸœŸœ˜K˜ š œŸœ˜KšœŸœŸœŸœ˜/š ŸœŸœ ŸœŸœŸœŸœ˜DKšœŸœŸœ˜šŸœŸœ˜Kšœ4˜4šŸœŸœŸ˜&˜KšŸœŸœŸœŸœ˜;—KšŸœ˜—Kšœ˜—KšœŸœŸœ˜Kšœ'˜'KšœŸœ˜Kšœ9˜9—K˜—šŸœŸœŸ˜˜ Kšœ˜—˜ Kšœ&˜&—KšŸœŸœ˜—K˜Kšœ™KšœŸœŸœ˜Kšœ:˜:KšœŸœ˜!K˜Kšœ*˜*šŸœŸœŸœ˜ Kšœ™Kšœ ŸœŸœ˜KšœŸœŸœ˜Kšœ8˜8KšœŸœ˜%KšœA˜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™0KšŸœ ŸœŸœ"˜5šŸœ˜Kšœ˜šŸœ ŸœŸ˜Kšœ˜KšœŸœŸœŸœ˜!Kšœ˜Kšœ˜šŸœŸœŸœŸœ˜0Kšœ2˜2—Kšœ˜šŸœ ŸœŸ˜Kšœ˜KšŸœŸœŸœ˜.K˜KšŸœ˜—Kš œŸœŸœŸœŸœ ˜