-- ListUnbound.Mesa  Edited by Sweet on July 8, 1980  9:42 AM

DIRECTORY
  AltoDefs: FROM "altodefs",
  BcdDefs USING [
    Base, BCD, EXPIndex, EXPRecord, FTIndex, FTNull, FTSelf, NameRecord, NullName,
    VersionID],
  BcdOps USING [NameString],
  CommanderDefs USING [AddCommand, CommandBlockHandle],
  IODefs USING [WriteString],
  ListerDefs USING [
    FileSegmentHandle, IncorrectVersion, Indent, Load, MultipleModules, NoCode,
    NoFGT, NoSymbols, PrintMachine, PrintSei, SetRoutineSymbols],
  OutputDefs USING [
    CloseOutput, OpenOutput, PutCR, PutDecimal, PutString, PutSubString, PutTime],
  SegmentDefs USING [
    DefaultVersion, DeleteFileSegment, FileNameError, FileSegmentAddress,
    FileSegmentHandle, MoveFileSegment, NewFile, NewFileSegment, Read, SwapError,
    SwapIn, SwapOut, Unlock],
  String USING [AppendString, AppendSubString, SubStringDescriptor],
  Symbols USING [ISEIndex, ISENull],
  SymbolTable USING [Acquire, Base, Release, TableForSegment];

ListUnbound: PROGRAM
  IMPORTS
    CommanderDefs, IODefs, ListerDefs, OutputDefs, SegmentDefs, String,
    SymbolTable
  EXPORTS ListerDefs =
  BEGIN OPEN OutputDefs, BcdDefs;
  
  bcd: POINTER TO BCD;
  tb: Base;
  ssb: BcdOps.NameString;
  evb: Base;
  spb: Base;
  ctb: Base;
  mtb: Base;
  itb: Base;
  etb: Base;
  sgb: Base;
  ftb: Base;
  ntb: Base;
  
  InstallBcd: PROCEDURE [seg: SegmentDefs.FileSegmentHandle] =
    BEGIN OPEN SegmentDefs;
    size: CARDINAL;
    IF ~seg.swappedin THEN SwapIn[seg];
    bcd ← FileSegmentAddress[seg];
    IF (size ← bcd.nPages) # seg.pages THEN
      BEGIN
      Unlock[seg];
      MoveFileSegment[seg, seg.base, size];
      SwapIn[seg];
      bcd ← FileSegmentAddress[seg];
      END;
    tb ← LOOPHOLE[bcd];
    ssb ← LOOPHOLE[bcd + bcd.ssOffset];
    ctb ← tb + bcd.ctOffset;
    mtb ← tb + bcd.mtOffset;
    itb ← tb + bcd.impOffset;
    etb ← tb + bcd.expOffset;
    sgb ← tb + bcd.sgOffset;
    ftb ← tb + bcd.ftOffset;
    ntb ← tb + bcd.ntOffset;
    evb ← tb + bcd.evOffset;
    spb ← tb + bcd.spOffset;
    RETURN
    END;
    
  UnstallBcd: PROCEDURE [seg: SegmentDefs.FileSegmentHandle] =
    BEGIN OPEN SegmentDefs;
    IF seg.swappedin THEN Unlock[seg];
    SwapOut[seg];
    RETURN
    END;
    
  WriteBcdID: PROCEDURE [name: STRING, bcd: POINTER TO BCD] =
    BEGIN
    PutString[name];
    PutString[" configured "];
    PutTime[LOOPHOLE[bcd.version.time]];
    IF bcd.source # BcdDefs.NullName THEN
      BEGIN PutString[" from "]; PutName[bcd.source]; END;
    PutString[" by "];
    ListerDefs.PrintMachine[bcd.version];
    IF bcd.versionIdent # VersionID THEN
      BEGIN
      PutString["  Obsolete VersionID = "];
      PutDecimal[bcd.versionIdent]
      END;
    PutCR[];
    PutString["  Configured by "];
    PutTime[LOOPHOLE[bcd.creator.time]];
    PutString[" "];
    ListerDefs.PrintMachine[bcd.creator];
    PutCR[];
    PutCR[];
    RETURN
    END;
    
  PrintUnbound: PROCEDURE =
    BEGIN
    eti: EXPIndex ← FIRST[EXPIndex];
    UNTIL eti = bcd.expLimit DO
      CheckExport[
	eti !
	SegmentDefs.FileNameError =>
	  BEGIN OPEN etb[eti];
	  PutString["Can't find "L];
	  PutName[ftb[file].name];
	  PutCR[];
	  CONTINUE
	  END;
	ListerDefs.MultipleModules =>
	  BEGIN OPEN etb[eti];
	  PutString["Bad format for "L];
	  PutName[ftb[file].name];
	  PutCR[];
	  CONTINUE
	  END;
	ListerDefs.NoSymbols =>
	  BEGIN OPEN etb[eti];
	  PutString["No symbols for "L];
	  PutName[ftb[file].name];
	  PutCR[];
	  CONTINUE
	  END;
	ListerDefs.IncorrectVersion =>
	  BEGIN OPEN etb[eti];
	  PutString["Wrong version: "L];
	  PutName[ftb[file].name];
	  PutCR[];
	  CONTINUE
	  END];
      UnloadSymbols[];
      eti ← eti + etb[eti].size + SIZE[EXPRecord];
      IF LOOPHOLE[eti, CARDINAL] > LOOPHOLE[bcd.expLimit, CARDINAL] THEN
	GO TO Bogus;
      REPEAT Bogus => PrintGarbage[];
      ENDLOOP;
    RETURN
    END;
    
  CheckExport: PUBLIC PROCEDURE [eti: EXPIndex] =
    BEGIN OPEN etb[eti];
    i: CARDINAL;
    n: CARDINAL ← 0;
    sei: Symbols.ISEIndex;
    FOR i IN [0..size) DO
      IF links[i].gfi = 0 THEN
	BEGIN
	IF ~loaded THEN LoadSymbols[file];
	sei ← SeiForItem[i];
	IF sei = Symbols.ISENull THEN LOOP;
	IF n = 0 THEN
	  BEGIN
	  PutString["Unbound exports to "L];
	  PutName[name];
	  PutString[": "L];
	  END;
	IF n MOD 3 = 0 THEN Tab[2] ELSE PutString[", "L];
	n ← n + 1;
	ListerDefs.PrintSei[sei];
	END;
      ENDLOOP;
    IF n # 0 THEN BEGIN PutCR[]; PutCR[]; END;
    RETURN
    END;
    
  SeiForItem: PROCEDURE [item: CARDINAL] RETURNS [sei: Symbols.ISEIndex] =
    BEGIN OPEN symbols;
    FOR sei ← FirstCtxSe[stHandle.outerCtx], NextSe[sei] UNTIL sei =
      Symbols.ISENull DO
      IF seb[sei].idValue = item THEN
	SELECT LinkMode[
	  sei] FROM
	  val => IF seb[sei].extended THEN RETURN[Symbols.ISENull] ELSE RETURN;
	  ref => RETURN[sei];
	  manifest => LOOP; -- constant
	  
	  ENDCASE => RETURN[Symbols.ISENull];
      ENDLOOP;
    ERROR;
    END;
    
  symbols: SymbolTable.Base ← NIL;
  sseg: SegmentDefs.FileSegmentHandle ← NIL;
  loaded: BOOLEAN ← FALSE;
  
  LoadSymbols: PROCEDURE [file: FTIndex] =
    BEGIN OPEN ListerDefs;
    s: STRING ← [60];
    cseg: SegmentDefs.FileSegmentHandle;
    IF file = FTNull OR file = FTSelf OR symbols # NIL OR sseg # NIL THEN ERROR;
    GetBcdName[s, ftb[file].name];
    [code: cseg, symbols: sseg] ← Load[s ! NoCode, NoFGT => RESUME ];
    IF cseg # NIL THEN SegmentDefs.DeleteFileSegment[cseg];
    symbols ← SymbolTable.Acquire[SymbolTable.TableForSegment[sseg]];
    SetRoutineSymbols[symbols];
    loaded ← TRUE;
    END;
    
  UnloadSymbols: PROCEDURE =
    BEGIN OPEN SegmentDefs;
    IF symbols # NIL THEN SymbolTable.Release[symbols];
    IF sseg # NIL THEN DeleteFileSegment[sseg ! SwapError => CONTINUE];
    symbols ← NIL;
    sseg ← NIL;
    loaded ← FALSE;
    END;
    
  -- Utility Prints
  
  
  PrintGarbage: PROCEDURE =
    BEGIN
    Tab[2];
    PutString["? looks like garbage to me ..."];
    PutCR[];
    RETURN
    END;
    
  GetBcdName: PUBLIC PROCEDURE [s: STRING, n: NameRecord] =
    BEGIN
    i: CARDINAL;
    ssd: String.SubStringDescriptor ←
      [base: @ssb.string, offset: n, length: MIN[ssb.size[n], 100]];
    s.length ← 0;
    String.AppendSubString[s, @ssd];
    FOR i IN [0..s.length) DO IF s[i] = '. THEN RETURN ENDLOOP;
    String.AppendString[s, ".bcd"L];
    RETURN
    END;
    
  -- Utility Puts
  
  
  PutName: PUBLIC PROCEDURE [n: NameRecord] =
    BEGIN
    ssd: String.SubStringDescriptor ←
      [base: @ssb.string, offset: n, length: MIN[ssb.size[n], 100]];
    PutSubString[@ssd];
    RETURN
    END;
    
  Tab: PROCEDURE [n: CARDINAL] = BEGIN ListerDefs.Indent[n]; RETURN END;
    
  UnboundExports: PROCEDURE [root: STRING] =
    BEGIN
    i: CARDINAL;
    bcdfile: STRING ← [40];
    seg: SegmentDefs.FileSegmentHandle;
    BEGIN OPEN String;
    AppendString[bcdfile, root];
    FOR i IN [0..bcdfile.length) DO
      IF bcdfile[i] = '. THEN EXIT;
      REPEAT FINISHED => AppendString[bcdfile, ".bcd"];
      ENDLOOP;
    END;
    BEGIN OPEN SegmentDefs;
    seg ← NewFileSegment[
      NewFile[bcdfile, Read, DefaultVersion ! FileNameError => GO TO NoFile], 1,
      1, Read];
    InstallBcd[seg];
    OpenOutput[root, ".xl"L];
    WriteBcdID[bcdfile, bcd];
    PrintUnbound[];
    CloseOutput[];
    UnstallBcd[seg];
    EXITS NoFile => IODefs.WriteString["File not found"];
    END;
    RETURN
    END;
    
  Init: PROCEDURE =
    BEGIN
    command: CommanderDefs.CommandBlockHandle;
    command ← CommanderDefs.AddCommand[
      "UnboundExports", LOOPHOLE[UnboundExports], 1];
    command.params[0] ← [type: string, prompt: "Bcd name"];
    END;
    
  Init[];
  
  END....