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
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
can have source compatibility between the two worlds.
UnitsToVMPages: PROC[units: INT] RETURNS [INT] = {
RETURN[(units+unitsPerVMPage-1)/unitsPerVMPage];
};
T Y P E S & C O N S T A N T S
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;
Major procedures
UC: PROC [c: CHAR] RETURNS [CHAR] = {
RETURN [IF c IN ['a..'z] THEN 'A + (c - 'a) ELSE c];
};
ListSymbols: Commander.CommandProc = TRUSTED {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
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 {
[name: PATH] RETURNS [continue: BOOL]
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
begin ListSymbols
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;
name ← FS.ExpandName[name].fullFName;
namePath ¬ PFS.PathFromRope[name];
IF PFSNames.ShortName[namePath].version.versionKind = none THEN
namePath ¬ PFSNames.SetVersionNumber[namePath, [highest, 0]];
IF NOT Rope.Match["*!*", name] THEN name ← name.Concat["!h"];
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 {
[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]
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 {
Now we have a single-module file. Get the module.
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;
};
Get the source fullFName for later
sourceName ¬ RopeForMobName[mob,
add one to source to compensate for Mimosa bug
[IF mob.nConfigs = 0 THEN mob.source+1 ELSE mob.source]];
Open the output stream (if any)
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.opb ← tB + p.outerPackBlock.offset - SymbolSegment.biases[SymbolSegment.opType];
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]]];
Mimosa doesn't turn bodies into External!
IF body.kind = Callable
THEN MobListerUtils.PrintTree[[subtree[index: bodyTree]], 0, stream, stb]
ELSE {
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] = {
Shell sort of a[l..u)
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
};
Build the tables that we wish were issued by Mimosa instead of the current Pack tables
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;
Get the module name
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 {
Get the module file name
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];
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;
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;
At this point all of the entries have been made.
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;
};
};
Utility procedures
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] = {
If no such named list is found, one is created and inserted into the base.
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]};
};
I N I T
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.