-- file GLList.mesa 
-- last edited by Satterthwaite, September 16, 1982 8:36 am

DIRECTORY
  BcdDefs: TYPE USING [Base, Link, MTIndex, MTRecord, MTNull],
  BcdOps: TYPE USING [BcdBase, NameString],
  CharIO: TYPE USING [PutChar, PutDecimal, PutString, PutSubString],
  FileSegment: TYPE USING [Pages, nullPages],
  ListerOps: TYPE USING [],
  ListerUtil: TYPE USING [
    CreateStream, LoadBcd, LoadModule, MapPages, Message, PutFileID,
    SetFileName, SetRoutineSymbols, TTYStream, UnknownModule],
  PrincOps: TYPE USING [globalbase],
  Space: TYPE USING [Handle, Delete, LongPointer, nullHandle],
  Stream: TYPE USING [Delete, Handle],
  Strings: TYPE USING [
    String, SubStringDescriptor, AppendString, AppendSubString],
  Symbols: TYPE USING [
    HTIndex, ISEIndex, CSEIndex, CTXIndex, CBTIndex, BitAddress,
    HTNull, ISENull, CSENull, CTXNull, RootBti, WordLength],
  SymbolTable: TYPE USING [Acquire, Base, Release, SetCacheSize];

GLList: PROGRAM
    IMPORTS CharIO, ListerUtil, Space, Stream, Strings, SymbolTable
    EXPORTS ListerOps = {
  OPEN Symbols;
  
 -- output streams
 
  out: Stream.Handle ← NIL;
  
  OpenOutput: PROC [root: Strings.String, tty: BOOL] = {
    IF tty THEN out ← ListerUtil.TTYStream[]
    ELSE {
      outName: STRING ← [40];
      ListerUtil.SetFileName[outName, root, "gl"L];
      out ← ListerUtil.CreateStream[outName]}};
    
  CloseOutput: PROC = {Stream.Delete[out]; out ← NIL};
    
 -- symbol printing
 
  symbols: SymbolTable.Base ← NIL;

  DoSymbol: PROC [sei: ISEIndex] RETURNS [span: CARDINAL] = {
    addr: BitAddress = symbols.seb[sei].idValue;
    size: CARDINAL = symbols.seb[sei].idInfo/WordLength;
    hti: HTIndex = symbols.HashForSe[sei];
    d: Strings.SubStringDescriptor;
    n: NAT;
    CharIO.PutString[out,"    "L];
    IF hti = HTNull THEN {
      CharIO.PutString[out, "(anon)"L];
      n ← ("(anon)"L).length}
    ELSE {
      symbols.SubStringForHash[@d, hti];
      CharIO.PutSubString[out, @d];
      n ← d.length};
    WHILE n < 16 DO CharIO.PutChar[out, ' ]; n ← n + 1 ENDLOOP;
    CharIO.PutChar[out, '\t];  CharIO.PutDecimal[out, size];
    CharIO.PutChar[out, '\n];
    RETURN [addr.wd + size]};
      
  DoContext: PROC [ctx: CTXIndex] RETURNS [maxSpan: CARDINAL ← 0] = {
    FOR sei: ISEIndex ← symbols.FirstCtxSe[ctx], symbols.NextSe[sei]
     UNTIL sei = ISENull DO
      IF ~symbols.seb[sei].constant THEN
        maxSpan ← MAX[DoSymbol[sei], maxSpan];
      ENDLOOP};
      
  DoFields: PROC [rSei: CSEIndex] RETURNS [maxSpan: CARDINAL] = {
    RETURN [WITH t: symbols.seb[rSei] SELECT FROM
      record => DoContext[t.fieldCtx],
      ENDCASE => 0]};
      
  DoBody: PROC [bti: Symbols.CBTIndex, frameSize: CARDINAL] = {
    frameOverhead: CARDINAL = PrincOps.globalbase+1;	-- for start trap pointer
    maxSpan: CARDINAL ← PrincOps.globalbase;
    typeIn, typeOut: CSEIndex;
    [typeIn, typeOut] ← symbols.TransferTypes[symbols.bb[bti].ioType];
    IF typeIn # CSENull THEN {
      CharIO.PutString[out, "  Global arguments:\n"L];
      maxSpan ← MAX[DoFields[typeIn], maxSpan]};
    IF typeOut # CSENull THEN {
      CharIO.PutString[out, "  Global results:\n"L];
      maxSpan ← MAX[DoFields[typeOut], maxSpan]};
    IF symbols.bb[bti].localCtx # CTXNull THEN {
      CharIO.PutString[out, "  Global variables:\n"L];
      maxSpan ← MAX[DoContext[symbols.bb[bti].localCtx], maxSpan]};
    IF ~symbols.bb[bti].hints.noStrings THEN
      CharIO.PutString[out, "  Global string literals or string bodies\n"L];
    IF maxSpan # frameSize AND frameSize > frameOverhead THEN {
      CharIO.PutString[out, "  "L];
      CharIO.PutDecimal[out, frameSize - maxSpan];
      CharIO.PutString[out, " words not in listed variables or overhead\n"L]};
    CharIO.PutChar[out, '\n]};
 
 -- module enumeration
 
  DoGlobals: PROC [root: Strings.String] = {
    bcdFile: Strings.String ← [100];
    bcdSeg: FileSegment.Pages ← FileSegment.nullPages;
    bcdSpace: Space.Handle;

    bcd: BcdOps.BcdBase ← NIL;
    mtb: BcdDefs.Base ← NIL;
    ssb: BcdOps.NameString;
      
    EnumerateModules: PROC [proc: PROC [BcdDefs.MTIndex] RETURNS [BOOL]]
        RETURNS [BcdDefs.MTIndex] = {
      mti: BcdDefs.MTIndex ← BcdDefs.MTIndex.FIRST;
      UNTIL mti = bcd.mtLimit DO
	IF proc[mti] THEN RETURN [mti];
	mti ← mti + (WITH m: mtb[mti] SELECT FROM
		direct => BcdDefs.MTRecord.direct.SIZE + m.length*BcdDefs.Link.SIZE,
		indirect => BcdDefs.MTRecord.indirect.SIZE,
		multiple => BcdDefs.MTRecord.multiple.SIZE,
		ENDCASE => ERROR)
	ENDLOOP;
      RETURN [BcdDefs.MTNull]};
    
    DoModule: PROC [mti: BcdDefs.MTIndex] RETURNS [BOOL←FALSE] = {
      d: Strings.SubStringDescriptor ← [
	base: @ssb.string, offset: mtb[mti].name, length: ssb.size[mtb[mti].name]];
      name: Strings.String ← [100];
      sSeg: FileSegment.Pages;
      CharIO.PutString[out, "Module: "L];  CharIO.PutSubString[out, @d];
      IF mtb[mti].tableCompiled THEN GO TO Table;
      Strings.AppendSubString[name, @d];
      [symbols: sSeg] ← ListerUtil.LoadModule[bcdSeg, name
	! ListerUtil.UnknownModule => {GOTO NoModule}];
      IF ~bcd.definitions THEN {
	CharIO.PutString[out, ", frame size: "L];
	CharIO.PutDecimal[out, mtb[mti].framesize]};
      IF mtb[mti].ngfi > 1 THEN {
	CharIO.PutString[out, ", gfi slots: "L];
	CharIO.PutDecimal[out, mtb[mti].ngfi]};
      CharIO.PutChar[out, '\n];
      IF sSeg = FileSegment.nullPages THEN GO TO NoSymbols;
      SymbolTable.SetCacheSize[0];	-- flush cache
      symbols ← SymbolTable.Acquire[sSeg];
      ListerUtil.SetRoutineSymbols[symbols];
      IF symbols.stHandle.definitionsFile THEN
	CharIO.PutString[out, "  No global frame\n\n"L]
      ELSE DoBody[Symbols.RootBti, mtb[mti].framesize];
      SymbolTable.Release[symbols]; symbols ← NIL;
      EXITS
	Table => CharIO.PutString[out, " -- table compiled\n\n"L];
	NoModule => CharIO.PutString[out, " -- not found in file\n\n"L];
	NoSymbols => CharIO.PutString[out, "  symbols not available\n\n"L]};
    
    MakeBcdFilename[bcdFile, root];
    bcdSeg ← ListerUtil.LoadBcd[bcdFile];
    bcdSpace ← ListerUtil.MapPages[bcdSeg];
    IF bcdSpace # Space.nullHandle THEN {
      ListerUtil.PutFileID[out];
      bcd ← bcdSpace.LongPointer;
      mtb ← LOOPHOLE[bcd, BcdDefs.Base] + bcd.mtOffset;
      ssb ← LOOPHOLE[bcd + bcd.ssOffset];
      [] ← EnumerateModules[DoModule];
      ssb ← NIL;  mtb ← NIL;  bcd ← NIL;
      Space.Delete[bcdSpace]}
    ELSE ListerUtil.Message["File could not be opened"L]};
    
 -- overall control
 
  MakeBcdFilename: PROC [bcd, root: Strings.String] = {
    Strings.AppendString[bcd, root];
    FOR i: CARDINAL IN [0..bcd.length) DO IF bcd[i] = '. THEN RETURN ENDLOOP;
    Strings.AppendString[bcd, ".bcd"L]};
    
  ListGlobals: PUBLIC PROC [root: Strings.String, ttyOut: BOOL] = {
    OpenOutput[root, ttyOut];
    DoGlobals[root];
    CloseOutput[]};

  }.