-- MesaNub.Mesa Edited by Sandman on September 12, 1980  8:43 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoFileDefs USING [CFA, FA],
  BcdOps USING [BcdBase],
  ControlDefs USING [ControlModule, GlobalFrameHandle, NullGlobalFrame],
  DisplayDefs USING [DestroyDisplay, DisplayControl],
  ImageDefs USING [AddFileRequest, FileRequest, ImageTime],
  IODefs USING [CR, SP, NewLine, WriteChar, WriteLine, WriteOctal, WriteString],
  LoaderOps USING [FileNotFound, Load, New, VersionMismatch],
  MiscDefs USING [CallDebugger],
  MiscOps USING [bypassExec, ReleaseDebuggerBitmap],
  NubOps USING [],
  SegmentDefs USING [FileHandle, Read, ReleaseFile, UnlockFile],
  StreamDefs USING [
    CreateByteStream, DestroyKeyHandler, GetFA, JumpToFA, Read, StartKeyHandler,
    StreamHandle],
  StringDefs USING [AppendChar, AppendString, LowerCase],
  Storage USING [Node, CopyString, Free, FreeString],
  TimeDefs USING [AppendDayTime, DefaultTime, UnpackDT];

MesaNub: PROGRAM
  IMPORTS
    DisplayDefs, ImageDefs, IODefs, LoaderOps, MiscDefs, MiscOps, SegmentDefs,
    StreamDefs, StringDefs, Storage, TimeDefs
  EXPORTS MiscDefs, NubOps =
  BEGIN OPEN SegmentDefs;

  BadFile: SIGNAL [name: STRING] = CODE;
  BadVersion: SIGNAL [name: STRING] = CODE;

  cmFile: FileHandle ← NIL;

  AddComCmRequest: PUBLIC PROCEDURE =
    BEGIN OPEN Storage;
    comcmRequest ← Node[SIZE[ImageDefs.FileRequest]];
    comcmRequest↑ ←
      [name: CopyString["Com.Cm."L], file: NIL, access: Read, link:];
    ImageDefs.AddFileRequest[comcmRequest];
    IF cmFile # NIL THEN {UnlockFile[cmFile]; ReleaseFile[cmFile]; cmFile ← NIL};
    END;

  RemoveComCmRequest: PUBLIC PROCEDURE =
    BEGIN
    cmFile ← comcmRequest.file;
    Storage.FreeString[comcmRequest.name];
    Storage.Free[comcmRequest];
    END;

  comcmRequest: POINTER TO ImageDefs.FileRequest;

  Done: SIGNAL = CODE;

  Switches: TYPE = PACKED ARRAY CHARACTER ['a..'z] OF BOOLEAN;
  DefaultSwitches: Switches =
    [FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, --'a..'i
      FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, --'j..'r
      TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE]; --'s..'z

  switches: Switches;
  postAnyway, alreadyBasic, doneHearld: BOOLEAN;

  ProcessSwitches: PROCEDURE [s: STRING] =
    BEGIN
    i: CARDINAL;
    c: CHARACTER;
    inverse: {this, next, no} ← no;
    FOR i IN [0..s.length) DO
      SELECT c ← StringDefs.LowerCase[s[i]] FROM
	IN ['a..'z] => switches[c] ← inverse # this;
	'- => inverse ← next;
	ENDCASE;
      inverse ← IF inverse = next THEN this ELSE no;
      ENDLOOP;
    END;

  GetToken: PROCEDURE [
    com: StreamDefs.StreamHandle, token, ext, switches: STRING] =
    BEGIN
    s: STRING;
    c: CHARACTER;
    token.length ← ext.length ← switches.length ← 0;
    s ← token;
    WHILE ~com.endof[com] DO
      SELECT (c ← com.get[com]) FROM
	IODefs.SP, IODefs.CR =>
	  IF token.length # 0 OR ext.length # 0 OR switches.length # 0 THEN
	    RETURN;
	'. => s ← ext;
	'/ => s ← switches;
	ENDCASE => StringDefs.AppendChar[s, c];
      ENDLOOP;
    RETURN
    END;

  cfa: AltoFileDefs.CFA;

  CommandLineCFA: PUBLIC PROCEDURE RETURNS [POINTER TO AltoFileDefs.CFA] =
    BEGIN RETURN[@cfa] END;

  LoadSystems: PUBLIC PROCEDURE [skipImage: BOOLEAN ← FALSE] =
    BEGIN
    user: ControlDefs.ControlModule;
    p: PROCESS;
    cfa.fp ← cmFile.fp;
    switches ← DefaultSwitches;
    IF skipImage THEN SkipImage[@cfa.fa];
    DO
      switches ← DefaultSwitches;
      user ← LoadUser[@cfa.fa ! Done => EXIT];
      CheckSwitches[];
      IF switches['s] AND user # [frame[ControlDefs.NullGlobalFrame]] THEN
	BEGIN p ← FORK StartModule[user]; JOIN p END;
      ENDLOOP;
    END;

  StartModule: PROCEDURE [f: ControlDefs.ControlModule] =
    BEGIN
    IF f.multiple OR ~f.frame.started THEN
      START LOOPHOLE[f, PROGRAM][ ! ABORTED => CONTINUE];
    RETURN
    END;

  LoadUser: PROCEDURE [fa: POINTER TO AltoFileDefs.FA]
    RETURNS [user: ControlDefs.ControlModule] =
    BEGIN OPEN IODefs, StreamDefs;
    com: StreamHandle;
    name: STRING ← [40];
    ext: STRING ← [10];
    switch: STRING ← [5];
    com ← CreateByteStream[cmFile, Read];
    user ← [frame[ControlDefs.NullGlobalFrame]];
    BEGIN
    StreamDefs.JumpToFA[com, fa ! ANY => GO TO finished];
    GetToken[com, name, ext, switch];
    IF name.length = 0 AND switch.length = 0 THEN GO TO finished;
    StreamDefs.GetFA[com, fa];
    com.destroy[com];
    ProcessSwitches[switch];
    IF switches['b] THEN ConvertToBasic[];
    IF name.length # 0 AND ~switches['c] THEN
      BEGIN
      IF ext.length = 0 THEN ext ← "bcd"L;
      IF ~alreadyBasic OR postAnyway THEN PostName[name];
      StringDefs.AppendChar[name, '.];
      StringDefs.AppendString[name, ext];
      user ← LoadNew[name !
	BadVersion => {BadVersionError[name]; RESUME};
	BadFile => {BadFileError[name]; GOTO exit}];
      IF ~alreadyBasic OR postAnyway THEN PostAddress[user];
      END;
    IF switches['c] THEN ProcessSwitches[name];
    EXITS
      exit => NULL;
      finished => BEGIN com.destroy[com ! ANY => CONTINUE]; SIGNAL Done; END;
    END;
    END;

  LoadNew: PROCEDURE [name: STRING] RETURNS [ControlDefs.ControlModule] =
    BEGIN OPEN LoaderOps;
    bcd: BcdOps.BcdBase;
    bcd ← Load[name ! BadFile, UNWIND => NULL; ANY => ERROR BadFile[name]];
    RETURN[
      New[bcd, ~switches['l], FALSE !
	BadFile, BadVersion, UNWIND => NULL;
	VersionMismatch => {SIGNAL BadVersion[name]; RESUME};
	FileNotFound => ERROR BadFile[name];
	ANY => ERROR BadFile[name]]];
    END;

  ConvertToBasic: PUBLIC PROCEDURE =
    BEGIN
    IF alreadyBasic THEN RETURN;
    alreadyBasic ← TRUE;
    DisplayDefs.DestroyDisplay[];
    StreamDefs.DestroyKeyHandler[];
    END;

  IsBasic: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN[alreadyBasic]; END;

  EnableOutput: PUBLIC PROCEDURE = BEGIN postAnyway ← TRUE; END;

  PostName: PROCEDURE [name: STRING] =
    BEGIN OPEN IODefs;
    IF ~doneHearld THEN WriteHerald[];
    WriteChar['>];
    WriteString[name];
    END;

  PostAddress: PROCEDURE [user: UNSPECIFIED] =
    BEGIN OPEN IODefs; WriteString[" -- "]; WriteOctal[user]; WriteChar[CR]; END;

  BadFileError: PROCEDURE [s: STRING] = {
    IF alreadyBasic THEN RETURN;
    IF ~IODefs.NewLine[] THEN IODefs.WriteChar[IODefs.CR];
    IODefs.WriteString["!File: "L];
    IODefs.WriteString[s]};

  BadVersionError: PROCEDURE [s: STRING] = {
    IF alreadyBasic THEN RETURN;
    IF ~IODefs.NewLine[] THEN IODefs.WriteChar[IODefs.CR];
    IODefs.WriteString["!File: "L];
    IODefs.WriteString[s];
    IODefs.WriteString[" referenced in different versions"L]};

  WriteHerald: PUBLIC PROCEDURE =
    BEGIN OPEN TimeDefs;
    h: STRING = "Alto/Mesa 6.0 of "L;
    time: STRING ← [18];
    AppendDayTime[time, UnpackDT[ImageDefs.ImageTime[]]];
    time.length ← time.length - 3;
    IODefs.WriteString[h];
    IODefs.WriteLine[time];
    time.length ← 0;
    AppendDayTime[time, UnpackDT[DefaultTime]];
    time.length ← time.length - 3;
    IODefs.WriteLine[time];
    doneHearld ← TRUE;
    END;

  SkipImage: PROCEDURE [fa: POINTER TO AltoFileDefs.FA] =
    BEGIN OPEN IODefs, StreamDefs;
    com: StreamHandle;
    name: STRING ← [40];
    ext: STRING ← [10];
    switch: STRING ← [5];
    com ← CreateByteStream[cmFile, Read];
    GetToken[com, name, ext, switch];
    GetFA[com, fa];
    com.destroy[com];
    ProcessSwitches[switch];
    CheckSwitches[];
    END;

  CheckSwitches: PROCEDURE =
    BEGIN
    IF switches['q] THEN MiscOps.bypassExec ← TRUE;
    IF switches['k] THEN MiscOps.ReleaseDebuggerBitmap[];
    IF switches['b] THEN ConvertToBasic[];
    IF switches['d] THEN MiscDefs.CallDebugger["You Called?"L];
    END;

  -- Main body

  StreamDefs.StartKeyHandler; -- Start keyboard process

  AddComCmRequest[];
  START DisplayDefs.DisplayControl;
  STOP;
  RemoveComCmRequest[];
  RESTART DisplayDefs.DisplayControl;
  postAnyway ← alreadyBasic ← doneHearld ← FALSE;
  LoadSystems[TRUE];

  END...