-- file TableSymbols.mesa
-- last edited by Satterthwaite, May 28, 1982 8:55 am

DIRECTORY
  BcdDefs: TYPE,
  BcdOps: TYPE USING [BcdBase, MTHandle],
  File: TYPE USING [Capability],
  FileSegment: TYPE USING [Pages, nullPages],
  OSMiscOps: TYPE USING [FindFile],
  Space: TYPE USING [
    Handle, nullHandle, virtualMemory, Create, LongPointer, Map, Delete],
  Strings: TYPE USING [String, SubStringDescriptor, AppendString],
  Symbols: TYPE,
  SymbolTable: TYPE USING [Base, Handle, Acquire, Release],
  Table: TYPE USING [Base],
  TableCommand: TYPE;

TableSymbols: PROGRAM
    IMPORTS OSMiscOps, Space, Strings, SymbolTable
    EXPORTS TableCommand = {

 -- public interface

  BadInterface: PUBLIC ERROR [Strings.String] = CODE;

  FindInterface: PUBLIC PROC [id, file: Strings.String] RETURNS [
	version: BcdDefs.VersionStamp ← BcdDefs.NullVersion,
	pages: FileSegment.Pages ← FileSegment.nullPages] = {
    headerSpace: Space.Handle ← Space.nullHandle;

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

    bcd: BcdOps.BcdBase;
    mtb: BcdOps.MTHandle;
    sgb: Table.Base;
    sSeg: BcdDefs.SGIndex;
    s: STRING ← [40];
    Strings.AppendString[s, IF file = NIL THEN id ELSE file];
    FOR i: CARDINAL IN [0 .. s.length) DO
      IF s[i] = '. THEN EXIT;
      REPEAT
	FINISHED => Strings.AppendString[s, ".bcd"L];
      ENDLOOP;

      BEGIN
      ENABLE {
	UNWIND => {NULL};
	ANY => {GO TO badFile}};
      BcdBase: PROC [p: LONG POINTER] RETURNS [BcdDefs.Base] = INLINE {
	RETURN [LOOPHOLE[p, BcdDefs.Base]]};
      file: File.Capability;
      bcdPages: CARDINAL ← 8;
      file ← OSMiscOps.FindFile[s, read];
      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.nModules # 1 THEN GO TO badFile;
      mtb ← BcdBase[bcd + bcd.mtOffset];
      sgb ← BcdBase[bcd + bcd.sgOffset];  sSeg ← mtb.sseg;
      IF sSeg = BcdDefs.SGNull OR sgb[sSeg].pages = 0 OR sgb[sSeg].file # BcdDefs.FTSelf
	THEN GO TO badFile;
      version ← bcd.version;
      pages ← [file: file, span: [base: sgb[sSeg].base, pages: sgb[sSeg].pages]];
      DeleteHeader[];
      EXITS
	badFile => {DeleteHeader[]; ERROR BadInterface[s]};
      END;

    IF ~CheckId[pages, id] THEN ERROR BadInterface[id];
    RETURN};

  CheckId: PROC [symbols: SymbolTable.Handle, id: Strings.String]
      RETURNS [found: BOOLEAN] = {
    base: SymbolTable.Base ← SymbolTable.Acquire[symbols];
      BEGIN
      OPEN Symbols, base;
      ss: Strings.SubStringDescriptor ← [base:id, offset:0, length:id.length];
      sei: ISEIndex = SearchContext[FindString[@ss], stHandle.directoryCtx];
      found ← sei # ISENull AND seb[sei].public;
      END;
    SymbolTable.Release[base];
    RETURN};


  FindItem: PUBLIC PROC [symbols: SymbolTable.Handle, item: Strings.String]
      RETURNS [size, entry: CARDINAL] = {
    base: SymbolTable.Base ← SymbolTable.Acquire[symbols];
      BEGIN
      OPEN Symbols, base;
      itemSei: ISEIndex ← ISENull;
      ss: Strings.SubStringDescriptor ← [base:item, offset:0, length:item.length];
      hti: HTIndex = FindString[@ss];
      IF hti = HTNull THEN GO TO fail;
      size ← 0;
      FOR sei: ISEIndex ← FirstCtxSe[stHandle.outerCtx], NextSe[sei] UNTIL sei = ISENull DO
	IF LinkMode[sei] # manifest THEN {
	  size ← size + 1; IF seb[sei].hash = hti THEN itemSei ← sei};
	ENDLOOP;
      IF itemSei = ISENull THEN GO TO fail;
      entry ← seb[itemSei].idValue;
      EXITS
	fail => ERROR BadInterface[item];
      END;
    SymbolTable.Release[base];
    RETURN};

  }.