-- File BcdSymbolPack.mesa
-- Last edited by Satterthwaite on May 10, 1983 10:23 am
-- Last edited by Lewis on 27-Mar-81 10:04:27

DIRECTORY
  Alloc: TYPE USING [AddNotify, DropNotify, Handle, Notifier],
  BcdComData: TYPE USING [table],
  BcdDefs: TYPE USING [
    Base, fttype, FTIndex, FTNull, FTSelf, lftype, Link,
    mttype, MTIndex, MTNull, MTRecord, NameRecord,
    sgtype, SGIndex, SGNull, sstype, VersionID, VersionStamp],
  BcdErrorDefs: TYPE USING [InterfaceId],
  BcdFileDefs: TYPE USING [CapabilityForFile, UnknownFile],
  BcdOps: TYPE USING [BcdBase, NameString],
  File: TYPE USING [Capability, nullCapability],
  FileSegment: TYPE USING [Pages],
  OSMiscOps: TYPE USING [FindFile, FileError],
  Space: TYPE USING [
    Handle, nullHandle, virtualMemory,
    Create, Delete, LongPointer, Map],
  Strings: TYPE USING [
    AppendString, AppendSubString, EqualSubStrings,
    String, SubString, SubStringDescriptor],
  Symbols: TYPE USING [
    ISEIndex, CSEIndex, CTXIndex, BitAddress, ISENull],
  SymbolTable: TYPE USING [
    Base, Handle, nullHandle, Acquire, Missing, Release];

BcdSymbolPack: PROGRAM
    IMPORTS
      Alloc, BcdFileDefs, data: BcdComData, OSMiscOps, Space, Strings,
      SymbolTable
    EXPORTS BcdErrorDefs = {
  OPEN BcdDefs;

  InterfaceId: TYPE ~ BcdErrorDefs.InterfaceId;
  
  mtb, lfb, sgb, ftb: BcdDefs.Base;
  ssb: BcdOps.NameString;

  Notifier: Alloc.Notifier ~ {
    mtb ← base[mttype]; lfb ← base[lftype];
    sgb ← base[sgtype]; ftb ← base[fttype];
    ssb ← base[sstype]};


  nullPages: SymbolTable.Handle ~ SymbolTable.nullHandle;

  initialized: BOOL ← FALSE;
  
  Initialize: PUBLIC PROC ~ {
    IF initialized THEN Finalize[];
    initialized ← TRUE};
    
  Finalize:  PUBLIC PROC ~ {initialized ← FALSE};
    

  ExportItemName: PUBLIC PROC [
      export: InterfaceId, ep: CARDINAL,
      userProc: PROC [Strings.SubString]] ~ { 
    symbolPages: SymbolTable.Handle;
    (data.table).AddNotify[Notifier];
    symbolPages ← InterfaceSymbols[export];
    IF symbolPages # nullPages THEN
      PrintNameFromInterface[symbolPages, ep, userProc];
    (data.table).DropNotify[Notifier]};

  ImportItemName: PUBLIC PROC [
      import: InterfaceId, ep: CARDINAL,
      clientMti: BcdDefs.MTIndex, linkOffset: CARDINAL,
      userProc: PROC [Strings.SubString]] ~ { 
    symbolPages: SymbolTable.Handle;
    (data.table).AddNotify[Notifier];
    IF clientMti = BcdDefs.MTNull THEN RETURN;
    symbolPages ← ModuleSymbols[clientMti];
    IF symbolPages # nullPages THEN
      PrintNameFromModule[symbolPages, clientMti, linkOffset, userProc]
    ELSE {
      symbolPages ← InterfaceSymbols[import];
      IF symbolPages # nullPages THEN
      	PrintNameFromInterface[symbolPages, ep, userProc]};
    (data.table).DropNotify[Notifier]};


  ModuleSymbols: PROC [mti: BcdDefs.MTIndex]
      RETURNS [symbolPages: SymbolTable.Handle] ~ { 
    sgi: BcdDefs.SGIndex ~ mtb[mti].sseg;
    IF sgi = BcdDefs.SGNull THEN symbolPages ← nullPages
    ELSE { 
      symbolPages.file ← BcdFileDefs.CapabilityForFile[sgb[sgi].file
	      ! BcdFileDefs.UnknownFile => {GO TO notFound}];
      symbolPages.span ← [base~sgb[sgi].base, pages~sgb[sgi].pages]};
    EXITS 
      notFound => RETURN [nullPages]};
 

  PrintNameFromModule: PROC [
      symbolPages: SymbolTable.Handle,
      importerMti: BcdDefs.MTIndex,
      linkOffset: CARDINAL,
      userProc: PROC [Strings.SubString]] ~ { 
    symbols: SymbolTable.Base ← NIL;
    symbols ← SymbolTable.Acquire[symbolPages
      ! SymbolTable.Missing => {CONTINUE}];
    IF symbols # NIL THEN { 
      OPEN Symbols;
      key: Symbols.BitAddress ~ ComputeAddress[importerMti, linkOffset];
      iCtx: CTXIndex ~ symbols.stHandle.importCtx;
      sei: ISEIndex;
      type: CSEIndex;

      Imported: PROC [sei: Symbols.ISEIndex] RETURNS [BOOL] ~ INLINE {
        RETURN [symbols.seb[sei].linkSpace AND ~symbols.seb[sei].extended]};

      FOR iSei: ISEIndex ← symbols.FirstCtxSe[iCtx], symbols.NextSe[iSei] UNTIL iSei = ISENull DO
        type ← symbols.UnderType[symbols.seb[iSei].idType];
        WITH t~~symbols.seb[type] SELECT FROM
          definition =>
            FOR sei ← symbols.FirstCtxSe[t.defCtx], symbols.NextSe[sei] UNTIL sei = ISENull DO
      	      IF Imported[sei] AND symbols.seb[sei].idValue = key THEN GO TO Found;
      	      ENDLOOP;
          ref =>
            IF Imported[iSei] AND symbols.seb[iSei].idValue = key THEN {
	      sei ← iSei;  GO TO Found};
          ENDCASE => NULL;
        REPEAT
          Found => {
            d: Strings.SubStringDescriptor;
            symbols.SubStringForName[@d, symbols.seb[sei].hash];
            userProc[@d
	      ! UNWIND => {
	        SymbolTable.Release[symbols]; (data.table).DropNotify[Notifier]}]};
        ENDLOOP;
      SymbolTable.Release[symbols]}};

  ComputeAddress: PROC [mti: BcdDefs.MTIndex, linkOffset: CARDINAL] 
      RETURNS [Symbols.BitAddress] ~ {
    linkLength: CARDINAL ~ (WITH m~~mtb[mti] SELECT FROM
      direct => m.length,
      indirect => lfb[m.links].length,
      multiple => lfb[m.links].length,
      ENDCASE => ERROR);
    RETURN [[wd~(linkLength-1)-linkOffset, bd~0]]};


  InterfaceSymbols: PROC [id: InterfaceId] RETURNS [symbolPages: SymbolTable.Handle] ~ { 
    s: STRING ← [100];
    file: File.Capability ← File.nullCapability;
    headerSpace: Space.Handle ← Space.nullHandle;

    DeleteHeader: PROC ~ {
      IF headerSpace # Space.nullHandle THEN {
	Space.Delete[headerSpace];
	headerSpace ← Space.nullHandle}};

    IF id.fti # FTNull THEN {
      s: STRING ← [100];
      GetBcdName[s, ftb[id.fti].name];
      file ← OSMiscOps.FindFile[s, $read ! OSMiscOps.FileError => {CONTINUE}]};
    IF file = File.nullCapability THEN symbolPages ← nullPages
    ELSE {
      ENABLE {
	UNWIND => {NULL};
	ANY => {GO TO badFile}};
      BcdBase: PROC [p: LONG POINTER] RETURNS [BcdDefs.Base] ~ INLINE {
	RETURN [LOOPHOLE[p, BcdDefs.Base]]};
      bcd: BcdOps.BcdBase;
      bcdPages: CARDINAL ← 8;
      iMtb, iFtb, iSgb: BcdDefs.Base;
      iSsb: BcdOps.NameString;
      iMti: BcdDefs.MTIndex;
      iSgi: BcdDefs.SGIndex;
      d1, d2: Strings.SubStringDescriptor;
      DO
        headerSpace ← Space.Create[size~bcdPages, parent~Space.virtualMemory];
	headerSpace.Map[window~[file~file, base~1]];
	bcd ← headerSpace.LongPointer[];
	IF bcd.versionIdent # BcdDefs.VersionID THEN GO TO badFile;
	IF bcdPages >= bcd.nPages THEN EXIT;
	bcdPages ← bcd.nPages;
	Space.Delete[headerSpace];  headerSpace ← Space.nullHandle
	ENDLOOP;
      IF bcd.nConfigs # 0 OR ~bcd.definitions THEN
        GO TO badFile;	-- no packaged bcd's (for now)
      iSsb ← LOOPHOLE[bcd + bcd.ssOffset];
      iMtb ← BcdBase[bcd + bcd.mtOffset];
      iFtb ← BcdBase[bcd + bcd.ftOffset];
      iSgb ← BcdBase[bcd + bcd.sgOffset];
      d1 ← [base~@ssb.string, offset~id.name, length~ssb.size[id.name]];
      d2.base ← @iSsb.string;
      iMti ← BcdDefs.MTIndex.FIRST;
      UNTIL iMti = bcd.mtLimit DO
	d2.offset ← iMtb[iMti].name;
	d2.length ← iSsb.size[iMtb[iMti].name];
	IF Strings.EqualSubStrings[@d1, @d2] THEN EXIT;
	iMti ← iMti + (WITH m~~iMtb[iMti] SELECT FROM
		  direct => BcdDefs.MTRecord.direct.SIZE + m.length*BcdDefs.Link.SIZE,
		  indirect => BcdDefs.MTRecord.indirect.SIZE,
		  multiple => BcdDefs.MTRecord.multiple.SIZE,
		  ENDCASE => ERROR);
	REPEAT
	  FINISHED =>
	    IF bcd.nModules = 1 THEN iMti ← BcdDefs.MTIndex.FIRST ELSE GOTO badFile;
  	ENDLOOP;
      iSgi ← iMtb[iMti].sseg;
      IF iSgi = BcdDefs.SGNull OR iSgb[iSgi].pages = 0
       OR iSgb[iSgi].file # BcdDefs.FTSelf OR bcd.version # ftb[id.fti].version
	THEN GO TO badFile;
      symbolPages ← [
        file~file,
	span~[base~iSgb[iSgi].base, pages~iSgb[iSgi].pages]];
      DeleteHeader[];
      EXITS
	badFile => {DeleteHeader[]; symbolPages ← nullPages}};
    RETURN};
    
  GetBcdName: PROC [s: Strings.String, n: NameRecord] ~ {
    d: Strings.SubStringDescriptor ←
      [base~@ssb.string, offset~n, length~MIN[ssb.size[n], 40]];
    s.length ← 0;
    Strings.AppendSubString[s, @d];
    FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '. THEN RETURN ENDLOOP;
    Strings.AppendString[s, ".bcd"L]};

  PrintNameFromInterface: PROC [
      symbolPages: SymbolTable.Handle,
      ep: CARDINAL,
      userProc: PROC [Strings.SubString]] ~ { 
    symbols: SymbolTable.Base ← NIL;
    symbols ← SymbolTable.Acquire[symbolPages
      ! SymbolTable.Missing => {CONTINUE}];
    IF symbols # NIL THEN { 
      OPEN Symbols;
      outerCtx: CTXIndex ~ symbols.stHandle.outerCtx;
      d: Strings.SubStringDescriptor;
      FOR sei: ISEIndex ← symbols.FirstCtxSe[outerCtx], symbols.NextSe[sei] 
       UNTIL sei = ISENull DO
	SELECT symbols.LinkMode[sei] FROM
	  $val, $ref, $type => 
	    IF symbols.seb[sei].idValue = ep THEN {
	      symbols.SubStringForName[@d, symbols.seb[sei].hash];
              userProc[@d
	        ! UNWIND => {
	          SymbolTable.Release[symbols]; (data.table).DropNotify[Notifier]}]};
	  ENDCASE;
        ENDLOOP;
      SymbolTable.Release[symbols]}};

  }.