-- ContextPack.Mesa
-- Edited by:  Bruce October 8, 1980  6:46 PM
-- Edited by:  Johnsson September 16, 1980  5:08 PM
-- Edited by:  Sandman July 18, 1980  10:24 AM

DIRECTORY
  BcdDefs USING [Base, CTIndex, CTNull, GFTIndex, MTIndex, NameRecord],
  BcdOps USING [
    BcdBase, CTHandle, FindName, MTHandle, NameString, ProcessConfigs, ProcessModules],
  DContext USING [],
  DebugOps USING [Abort, Numeric, ShortREAD, StringExpToOctal, UserAborted],
  DLoadState USING [
    Acquire, AcquireBcd, Enumerate, GetMap, Invalid, Map, MapConfigToReal,
    MapRealToConfig, ReleaseBcd, Release, ReleaseMap],
  DOutput USING [Blanks, Char, Decimal, EOL, Line, Octal, SubString, Text],
  DPsb USING [Current, Frame],
  Frames USING [Invalid],
  Gf USING [
    Check, CopiedFrame, Deleted, Display, DisplayGFTEntry, Frame, GFI, Handle,
    Name, Original, Validate],
  Lf USING [GF, Handle, NoAccessLink, Validate],
  MachineDefs USING [
    ConfigIndex, FHandle, GFHandle, GfiToFrame, GFTIndex, NullConfig, NullGF,
    nullPHandle, PHandle],
  State USING [AllStrings, Get, GetGS, GSHandle, Handle, strings, Strings],
  Storage USING [String],
  String USING [AppendString],
  Strings USING [EqualSubStrings, SubStringDescriptor],
  UserInput USING [userAbort];
  
ContextPack: PROGRAM
  IMPORTS BcdOps, DebugOps, DOutput, Strings, Frames, Gf, Lf, DLoadState,
    DPsb, MachineDefs, State, Storage, String, UserInput
  EXPORTS DContext =
  BEGIN OPEN BcdDefs, BcdOps, MachineDefs;
  
  strings: State.Strings ← State.AllStrings[];
  data: State.GSHandle ← State.GetGS[];
  
  -- setting
  
  Reset: PUBLIC PROC =
    BEGIN
    h: State.Handle ← State.Get[];
    SetLocal[DebugOps.ShortREAD[@data.StatePtr.dest !
      DebugOps.Abort => CONTINUE]];
    h.pContext ← DPsb.Current[];
    h.howSet ← state;
    END;
    
  SetOctal: PUBLIC PROC [p: POINTER] =
    BEGIN IF ~Gf.Validate[p] THEN SetLocal[p] ELSE SetGlobal[p] END;
    
  SetGlobal: PUBLIC PROC [gf: MachineDefs.GFHandle] =
    BEGIN
    h: State.Handle ← State.Get[];
    IF ~Gf.Validate[gf] THEN {CleanupInvalidContext[gf]; RETURN};
    WriteContext[gf];
    h.gContext ← gf;
    h.lContext ← NIL;
    h.pContext ← nullPHandle;
    h.howSet ← global;
    END;
    
  SetLocal: PUBLIC PROC [lf: MachineDefs.FHandle] =
    BEGIN
    h: State.Handle ← State.Get[];
    gf: MachineDefs.GFHandle ← NIL;
    IF ~Lf.Validate[lf] THEN {CleanupInvalidContext[lf]; RETURN};
    gf ← Lf.GF[lf ! Frames.Invalid, Lf.NoAccessLink => CONTINUE];
    WriteContext[gf];
    h.lContext ← lf;
    h.gContext ← gf;
    h.howSet ← local;
    END;
    
  SetProcess: PUBLIC PROC [p: MachineDefs.PHandle] =
    BEGIN
    h: State.Handle ← State.Get[];
    SetLocal[DPsb.Frame[p]];
    h.pContext ← p;
    h.howSet ← psb;
    END;
    
  CleanupInvalidContext: PROCEDURE [f: UNSPECIFIED] =
    BEGIN OPEN DOutput;
    EOL[]; Octal[f]; Text[" is not a valid frame!"L];
    IF ~data.initBCD THEN SIGNAL DebugOps.Abort ELSE InitConfig[];
    RETURN
    END;
    
InitConfig: PROCEDURE =
  BEGIN
  h: State.Handle ← State.Get[];
  bcd: BcdOps.BcdBase;
  h.lContext ← NIL; h.gContext ← NIL; h.pContext ← nullPHandle;
  h.config ← DLoadState.Acquire[ ! DLoadState.Invalid =>
    {h.config ← NullConfig; h.cti ← CTNull; GOTO noContext}] - 1;
  bcd ← DLoadState.AcquireBcd[h.config];
  h.cti ← IF bcd.nConfigs = 0 AND bcd.nModules = 1
    THEN CTNull ELSE FIRST[CTIndex];
  data.initBCD ← FALSE;
  Cleanup[bcd];
  EXITS
    noContext => NULL;
  END;
  
WriteContext: PROCEDURE [f: MachineDefs.GFHandle] =
  BEGIN
  h: State.Handle ← State.Get[];
  cgfi: GFTIndex;
  config: ConfigIndex;
  bcd: BcdOps.BcdBase;
  FindWhichModule: PROCEDURE[mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
    BEGIN
    IF cgfi IN [mth.gfi..mth.gfi+mth.ngfi) THEN
      BEGIN h.cti ← mth.config; RETURN[TRUE]; END;
    RETURN[FALSE];
    END;
  h.pContext ← nullPHandle;
  [] ← DLoadState.Acquire[ ! DLoadState.Invalid =>
    {h.lContext ← NIL; h.gContext ← NIL; GOTO noContext}];
  [cgfi, config] ← MapRC[Gf.Original[f]];
  IF config = NullConfig THEN ERROR Frames.Invalid[f];
  h.config ← config;
  bcd ← DLoadState.AcquireBcd[config];
  [] ← BcdOps.ProcessModules[bcd, FindWhichModule ! UNWIND => Cleanup[bcd]];
  data.initBCD ← FALSE;
  Cleanup[bcd];
  RETURN
  EXITS noContext => NULL;
  END;
  
  SetRootConfig: PUBLIC PROC [config: STRING] =
    BEGIN
    Rcount: CARDINAL ← 0;
    configdesc: Strings.SubStringDescriptor;
    savecti: CTIndex ← CTNull;
    saveconfig: ConfigIndex;
    data: State.Handle ← State.Get[];
    
    GetSetUp: PROCEDURE [config: ConfigIndex] RETURNS [BOOLEAN] =
      BEGIN
      found: BOOLEAN ← FALSE;
      bcd: BcdBase;
      CheckForRoot: PROC [cth: CTHandle, cti: CTIndex] RETURNS [BOOLEAN] =
	BEGIN
	IF UserInput.userAbort THEN Cleanup[bcd,TRUE];
	IF cth.config # CTNull THEN RETURN[FALSE];
	IF ~(found ← TestName[cth.name]) THEN
	  IF cth.namedInstance
	    THEN found ← TestName[FindName[bcd,[config[cti]]]];
	IF found THEN
	  BEGIN savecti ← cti; Rcount ← Rcount+1; saveconfig ← config; END;
	RETURN[FALSE]
	END;
      TestName: PROCEDURE [name: NameRecord] RETURNS [BOOLEAN] =
	BEGIN OPEN Strings;
	tempssb: NameString = LOOPHOLE[bcd+bcd.ssOffset];
	ssd: SubStringDescriptor ← 
	  [base: @tempssb.string, offset: name, length: tempssb.size[name]];
	RETURN[EqualSubStrings[@configdesc, @ssd]]
	END;
      bcd ← DLoadState.AcquireBcd[config];
      IF bcd.nConfigs = 0 AND bcd.nModules = 1 THEN
	BEGIN  
	mth: MTHandle ← @LOOPHOLE[bcd+bcd.mtOffset, Base][FIRST[MTIndex]];
	IF ~(found ← TestName[mth.name]) THEN 
	  IF mth.namedInstance  
	    THEN found ← TestName[FindName[bcd,[module[FIRST[MTIndex]]]]];
	IF found THEN
	  BEGIN Rcount ← Rcount+1; saveconfig ← config; savecti ← CTNull; END;
	END
      ELSE [] ← ProcessConfigs[bcd, CheckForRoot];
      DLoadState.ReleaseBcd[bcd];
      RETURN[FALSE]
      END;
      
    strings[rconfig] ← config;
    configdesc ← Strings.SubStringDescriptor[
      base: config, offset: 0, length: config.length];
    [] ← DLoadState.Acquire[];
    [] ← DLoadState.Enumerate[recentfirst,GetSetUp ! UNWIND => DLoadState.Release[]];
    SELECT Rcount FROM
      = 0 => BEGIN DLoadState.Release[]; NotFound[config] END;
      = 1 => SetupRootConfig[saveconfig, savecti];
      ENDCASE => {DLoadState.Release[]; WriteAmbiguousContext[config, Rcount]};
    RETURN
    END;
    
  SetConfig: PUBLIC PROC [config: STRING] =
    BEGIN
    bcd: BcdBase;
    count: CARDINAL ← 0;
    configdesc: Strings.SubStringDescriptor;
    savecti: CTIndex;
    data: State.Handle ← State.Get[];
    
    CheckConfigName: PROC [cth: CTHandle, cti: CTIndex] RETURNS [BOOLEAN] =
      BEGIN
      found: BOOLEAN ← FALSE;
      ssb: NameString ← LOOPHOLE[bcd+bcd.ssOffset];
      TestName: PROCEDURE [ssb: NameString, name: NameRecord] RETURNS [BOOLEAN] =
	BEGIN OPEN Strings;
	ssd: SubStringDescriptor ← 
	  [base: @ssb.string, offset: name, length: ssb.size[name]];
	RETURN[EqualSubStrings[@configdesc, @ssd]]
	END;
	
      IF UserInput.userAbort THEN SIGNAL DebugOps.UserAborted;
      IF ~SameConfig[bcd, cth.config, data.cti] THEN RETURN[FALSE];
      IF ~(found ← TestName[ssb, cth.name]) THEN
	IF cth.namedInstance THEN
	  found ← TestName[ssb, FindName[bcd,[config[cti]]]];
    IF found THEN BEGIN count ← count + 1; savecti ← cti; END;
    RETURN[FALSE]
    END;
    strings[config] ← config;
    configdesc ← Strings.SubStringDescriptor[
      base: config, offset: 0, length: config.length];
    IF data.cti = CTNull 
      THEN BEGIN DOutput.Text[" -- Not allowed !"L]; RETURN END;
    [] ← DLoadState.Acquire[];
    bcd ← DLoadState.AcquireBcd[data.config];
    [] ← ProcessConfigs[bcd, CheckConfigName ! UNWIND => Cleanup[bcd]];
    SELECT count FROM
      = 0 => NotFound[config];
      = 1 => IF SetupConfig[bcd, savecti, data.config] THEN data.cti ← savecti;
      ENDCASE => WriteAmbiguousContext[config, count ! UNWIND => Cleanup[bcd]];
    Cleanup[bcd];
    RETURN
    END;
    
  NotFound: PROC [s: STRING]= {
    DOutput.EOL[]; DOutput.Text[s]; DOutput.Line[" not found!"L]};
    
  SetModule: PUBLIC PROC [mod: STRING] = {
    gf: MachineDefs.GFHandle ←
      IF DebugOps.Numeric[mod] THEN LOOPHOLE[DebugOps.StringExpToOctal[mod]] 
      ELSE Gf.Frame[mod ! Gf.CopiedFrame => RESUME];
    strings[module] ← mod;
    IF gf = NIL THEN RETURN;
    SetGlobal[gf]};
    
  -- retrieving
  
  GetOctal: PUBLIC PROC RETURNS [p: POINTER] =
    BEGIN RETURN [State.Get[].h.lContext] END;
    
  GetGlobal: PUBLIC PROC RETURNS [gf: MachineDefs.GFHandle] =
    BEGIN RETURN [State.Get[].h.gContext] END;
    
  GetLocal: PUBLIC PROC RETURNS [lf: MachineDefs.FHandle] =
    BEGIN RETURN [State.Get[].h.lContext] END;
    
  GetProcess: PUBLIC PROC RETURNS [p: MachineDefs.PHandle] =
    BEGIN RETURN [State.Get[].h.pContext] END;
    
  GetRootConfig: PUBLIC PROC RETURNS [config: STRING] =
    BEGIN config ← strings[rconfig] END;
    
  GetConfig: PUBLIC PROC RETURNS [config: STRING] =
    BEGIN config ← strings[config] END;
    
  GetModule: PUBLIC PROC RETURNS [mod: STRING] =
    BEGIN
    mod ← strings[module];
    IF mod # NIL THEN RETURN;
    mod ← Storage.String[40];
    strings[module] ← mod;
    Gf.Name[mod,GetGlobal[]];
    END;
    
  GetRootConfigIndex: PUBLIC PROC
      RETURNS [config: ConfigIndex] =
    BEGIN RETURN [State.Get[].h.config] END;
    
  GetConfigIndex: PUBLIC PROC RETURNS [cti: BcdDefs.CTIndex] =
    BEGIN RETURN [State.Get[].h.cti] END;
    
  -- utilities
  
DisplayCurrent: PUBLIC PROCEDURE =
  BEGIN OPEN DLoadState;
  h: State.Handle ← State.Get[];
  module: STRING ← [40];
  bcd: BcdOps.BcdBase ← NIL;
  BEGIN ENABLE UNWIND => Cleanup[bcd];
  [] ← DLoadState.Acquire[ ! DLoadState.Invalid => GOTO noContext];
  DOutput.EOL[];
  Gf.Display[h.gContext, "Module:"L];
  IF h.lContext # NIL THEN
    BEGIN DOutput.Text[", L"L]; WCS[]; DOutput.Octal[h.lContext]; END;
  IF h.pContext # nullPHandle THEN
    BEGIN DOutput.Text[", PSB"L]; WCS[]; DOutput.Octal[h.pContext]; END;
  DOutput.EOL[];
  bcd ← DLoadState.AcquireBcd[h.config];
  IF bcd.nConfigs # 0 THEN
    BEGIN
    cth: CTHandle ← @LOOPHOLE[bcd+bcd.ctOffset, Base][h.cti];
    ssb: NameString ← LOOPHOLE[bcd+bcd.ssOffset];
    DOutput.Text["  Configuration"L]; WCS[];
    IF cth.namedInstance THEN
      BEGIN
      PrintName[ssb, BcdOps.FindName[bcd,[config[h.cti]]]];
      WCS[];
      END;
    PrintName[ssb,cth.name];
    DOutput.EOL[];
    END;
  END;
  Cleanup[bcd];
  RETURN
  EXITS
    noContext => DOutput.Text["No valid context!!"L];
  END;
  
  ListConfigs: PUBLIC PROCEDURE =
    BEGIN
    PrintConfigs: PROCEDURE [config: ConfigIndex] RETURNS [BOOLEAN] =
      BEGIN
      bcd: BcdBase;
      tempssb: NameString;
      ListSons: PROCEDURE [level: CARDINAL, parent: CTIndex] =
	BEGIN
	WriteNames: PROC [cth: CTHandle, cti: CTIndex] RETURNS [BOOLEAN] =
	  BEGIN
	  IF cth.config = parent THEN
	    BEGIN
	    IF UserInput.userAbort THEN Cleanup[bcd,TRUE];
	    DOutput.EOL[];
	    DOutput.Blanks[level*2];
	    IF cth.namedInstance THEN
	      BEGIN
	      PrintName[tempssb, FindName[bcd,[config[cti]]]];
	      DOutput.Text[": "L];
	      END;
	    PrintName[tempssb, cth.name];
	    ListSons[level+1, cti];
	    END;
	  RETURN[FALSE]
	  END;
	[] ← EnumerateConfigNames[bcd, WriteNames];
	RETURN
	END;
      bcd ← DLoadState.AcquireBcd[config];
      tempssb ← LOOPHOLE[bcd+bcd.ssOffset];
      ListSons[0, CTNull];
      DLoadState.ReleaseBcd[bcd];
      RETURN[FALSE]
      END;
    [] ← DLoadState.Acquire[];
    [] ← DLoadState.Enumerate[recentfirst, PrintConfigs ! UNWIND => DLoadState.Release[]];
    DLoadState.Release[];
    RETURN
    END;
    
  EnumerateConfigNames: PROCEDURE [
    bcd: BcdBase, proc: PROCEDURE [CTHandle, CTIndex] RETURNS [BOOLEAN]]
    RETURNS[CTIndex]=
    BEGIN
    mth: MTHandle = @LOOPHOLE[bcd+bcd.mtOffset, Base][FIRST[MTIndex]];
    tempssb: NameString = LOOPHOLE[bcd+bcd.ssOffset];
    IF bcd.nConfigs = 0 AND bcd.nModules = 1 THEN
      BEGIN
      DOutput.EOL[];
      IF mth.namedInstance THEN
	BEGIN
	PrintName[tempssb, FindName[bcd,[module[FIRST[MTIndex]]]]];
	DOutput.Text[": "L];
	END;
      PrintName[tempssb, mth.name];
      RETURN[CTNull];
      END;
    RETURN[ProcessConfigs[bcd, proc].cti]
    END;
    
  DisplayConfig: PUBLIC PROCEDURE =
    BEGIN
    bcd: BcdBase ← NIL;
    ssb: NameString;
    map: DLoadState.Map;
    data: State.Handle ← State.Get[];
    PrintModules: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      IF UserInput.userAbort THEN Cleanup[bcd,TRUE];
      DOutput.EOL[];
      IF ~SameConfig[bcd, mth.config, data.cti] THEN RETURN[FALSE];
      IF mth.namedInstance THEN
	BEGIN
	PrintName[ssb, FindName[bcd,[module[mti]]]];
	DOutput.Text[": "L];
	END;
      PrintName[ssb, mth.name]; DOutput.Text[", G: "L];
      Gf.DisplayGFTEntry[map[mth.gfi]];
      RETURN[FALSE];
      END;
    BEGIN ENABLE UNWIND => Cleanup[bcd];
    [] ← DLoadState.Acquire[];
    bcd ← DLoadState.AcquireBcd[data.config];
    map ← DLoadState.GetMap[data.config];
    ssb ← LOOPHOLE[bcd+bcd.ssOffset];
    DOutput.Blanks[2];
    IF bcd.nConfigs # 0 THEN
      BEGIN
      cth: CTHandle ← @LOOPHOLE[bcd+bcd.ctOffset, Base][data.cti];
      IF cth.namedInstance THEN
	BEGIN
	PrintName[ssb, FindName[bcd,[config[data.cti]]]];
	DOutput.Text[": "L];
	END;
      PrintName[ssb, cth.name];
      END;
    [] ← ProcessModules[bcd,PrintModules];
    DLoadState.ReleaseMap[map];
    END;
    Cleanup[bcd];
    RETURN;
    END;
    
  SetupRootConfig: PROCEDURE [config: ConfigIndex, cti: CTIndex] =
    BEGIN
    -- LoadState already in
    bcd: BcdBase ← DLoadState.AcquireBcd[config];
    IF SetupConfig[bcd, cti, config] THEN
      BEGIN
      data: State.Handle ← State.Get[];
      data.config ← config;
      data.cti ← cti;
      END;
    Cleanup[bcd]; -- releases LoadState
    RETURN
    END;
    
  SetupConfig: PROCEDURE [
    bcd: BcdBase, cti: CTIndex, config: ConfigIndex] RETURNS [BOOLEAN] =
    BEGIN
    mth: MTHandle;
    data: State.Handle ← State.Get[];
    FindFirstModule: PROC [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      RETURN[SameConfig[bcd, mth.config, cti] AND
	~Gf.Deleted[DLoadState.MapConfigToReal[mth.gfi, config]]]
      END;
    mth ← ProcessModules[bcd, FindFirstModule].mth;
    IF mth # NIL THEN
      BEGIN
      data.gContext ← GfiToFrame[DLoadState.MapConfigToReal[mth.gfi, config]];
      data.lContext ← NIL;
      data.pContext ← nullPHandle;
      data.howSet ← global;
      RETURN[TRUE];
      END
    ELSE
      BEGIN
      DOutput.Text[" -- Not Allowed !"L];
      RETURN[FALSE];
      END;
    END;
    
  Enumerate: PUBLIC PROCEDURE [
    proc: PROCEDURE [MachineDefs.GFHandle] RETURNS [BOOLEAN]] =
    --sequences through frames of modules in current config
    BEGIN
    bcd: BcdBase;
    map: DLoadState.Map;
    data: State.Handle ← State.Get[];
    
    SearchModules: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      frame: MachineDefs.GFHandle;
      IF UserInput.userAbort THEN SIGNAL DebugOps.UserAborted;
      IF ~SameConfig[bcd, mth.config, data.cti] THEN RETURN[FALSE];
      frame ← GfiToFrame[map[mth.gfi]];
      IF frame = NullGF THEN RETURN[FALSE];
      Gf.Check[frame];
      IF proc[frame] THEN RETURN[TRUE];
      RETURN[FALSE];
      END;
      
    CleanupMap: PROCEDURE [map: DLoadState.Map, bcd: BcdBase] =
      BEGIN
      DLoadState.ReleaseMap[map];
      Cleanup[bcd];
      RETURN
      END;
      
    [] ← DLoadState.Acquire[ ! DLoadState.Invalid => GOTO nil];
    map ← DLoadState.GetMap[data.config];
    [] ← ProcessModules[bcd ← DLoadState.AcquireBcd[data.config], SearchModules
      ! UNWIND => CleanupMap[map, bcd]];
    CleanupMap[map, bcd];
    RETURN
    EXITS
      nil => RETURN;
    END;
    
  WriteAmbiguousContext: PROCEDURE [s: STRING, c: CARDINAL] =
    BEGIN OPEN DOutput;
    Char['!]; Text[s]; Text[" has "L]; Decimal[c];
    Line[" instances -- this is an ambiguous reference."L];
    RETURN
    END;
    
  MapRC: PUBLIC PROCEDURE [f: MachineDefs.GFHandle]
    RETURNS [cgfi: MachineDefs.GFTIndex, config: MachineDefs.ConfigIndex] =
    BEGIN
    [cgfi, config] ← DLoadState.MapRealToConfig[Gf.GFI[f]];
    RETURN
    END;
    
  CheckForExtension: PROCEDURE [name, ext: STRING] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..name.length) DO
      IF name[i] = '. THEN RETURN;
      ENDLOOP;
    String.AppendString[name, ext];
    RETURN
    END;
    
  Cleanup: PROCEDURE [bcd: BcdBase, abort: BOOLEAN ← FALSE] =
    BEGIN
    IF bcd # NIL THEN DLoadState.ReleaseBcd[bcd];
    DLoadState.Release[];
    IF abort THEN SIGNAL DebugOps.UserAborted;
    END;
    
  PrintName: PROCEDURE [ssb: NameString, name: NameRecord] =
    BEGIN
    ssd: Strings.SubStringDescriptor ←
      [base: @ssb.string, offset: name, length: ssb.size[name]];
    DOutput.SubString[@ssd];
    RETURN
    END;
    
  WCS: PROC = {DOutput.Text[": "L]};
  
  SameConfig: PUBLIC PROC [bcd: BcdOps.BcdBase, child, parent: CTIndex]
      RETURNS [BOOLEAN]=
    BEGIN OPEN BcdDefs;
    cti: BcdDefs.CTIndex;
    ctb: Base = LOOPHOLE[bcd+bcd.ctOffset];
    --checks to see if child is related to parent
    FOR cti ← child, ctb[cti].config UNTIL cti = CTNull DO
      IF cti = parent THEN RETURN[TRUE];
      ENDLOOP;
    RETURN[parent = CTNull]
    END;
    
  END.