-- File: CC.mesa  Last edited by Sandman on October 11, 1980  2:28 PM

DIRECTORY
  Ascii USING [CR, NUL, SP],
  DebugUsefulDefs USING [ShortCopyREAD, ShortREAD],
  Event USING [Vetoed],
  FormSW,
  ImageDefs USING [RunImage, StopMesa],
  Init USING [CoreSwap],
  Inline USING [COPY],
  OsStaticDefs USING [OsStatics],
  Put USING [Char, Line, Text],
  SegmentDefs USING [
    DeleteFileSegment, FileSegmentHandle, MemoryConfig, NewFile,
    NewFileSegment, Read, WriteAppend],
  Selection USING [Clear, Convert],
  State USING [GetGS],
  Storage USING [
    CopyString, FreePages, FreeString, Pages, String, StringLength],
  StreamDefs USING [DiskHandle, FileNameError, NewByteStream, StreamError],
  StringDefs USING [
    AppendChar, AppendString, AppendSubString, EquivalentSubString,
    MesaToBcplString, SubStringDescriptor, WordsForBcplString],
  SwapperOps USING [memConfig, SystemTable],
  TajoUtility USING [CheckVeto],
  Tool USING [Create, Destroy, MakeFormSW, MakeSWsProc],
  UserTerminal USING [BlinkDisplay],
  Window USING [Box, Handle];
      
CC: PROGRAM 
  IMPORTS DebugUsefulDefs, Event, FormSW, ImageDefs, Init, Inline, Put,
    SegmentDefs, Selection, State, Storage, StreamDefs, StringDefs,
    SwapperOps, TajoUtility, Tool, UserTerminal =
  BEGIN OPEN SegmentDefs, StreamDefs, StringDefs;
  
  compile, bind, run: STRING ← NIL;
  window, formSW, options: Window.Handle ← NIL;
  compiler: STRING ← Storage.CopyString["Compiler.image"];
  binder: STRING ← Storage.CopyString["Mesa.image Binder.bcd"];
  runner: STRING ← Storage.CopyString["Mesa.image"];
  debug: BOOLEAN ← FALSE;

  nComp, nBind, nRun: STRING;
  
  Expand: FormSW.ProcType =
    BEGIN
    ExpandItem[@compile, Rep[cs]];
    ExpandItem[@bind, Rep[bs]];
    ExpandItem[@run, Rep[rs]];
    END;
    
  Compile: FormSW.ProcType =
    BEGIN
    com: DiskHandle;
    s: STRING ← [40];
    IF Storage.StringLength[compile] = 0 THEN RETURN;
    com ← StreamDefs.NewByteStream["Com.cm"L, WriteAppend];
    DoGoodStuff[com, compiler, s, FALSE];
    IF ~EndsInSwitches[compiler] THEN PutString[com, " /"];
    PutString[com, "q; "];
    PutCommand[com, compile ! FileNameError => {
      Error[name]; com.destroy[com]; GOTO out}];
    com.put[com, Ascii.CR];
    com.destroy[com];
    DoCommand[s];
    EXITS out => NULL;
    END;
    
  Bind: FormSW.ProcType =
    BEGIN OPEN SegmentDefs;
    com: DiskHandle;
    s: STRING ← [40];
    IF Storage.StringLength[bind] = 0 THEN RETURN;
    com ← StreamDefs.NewByteStream["Com.cm"L, WriteAppend];
    DoGoodStuff[com, binder, s, FALSE];
    IF ~EndsInSwitches[binder] THEN PutString[com, " /"];
    PutString[com, "q; "];
    PutCommand[com, bind ! FileNameError => {
      Error[name]; com.destroy[com]; GOTO out}];
    com.put[com, Ascii.CR];
    com.destroy[com];
    DoCommand[s];
    EXITS out => NULL;
    END;
    
  Run: FormSW.ProcType =
    BEGIN OPEN SegmentDefs;
    com: DiskHandle;
    system: STRING ← [40];
    who: Runner;
    IF Storage.StringLength[run] = 0 THEN RETURN;
    com ← StreamDefs.NewByteStream["Com.cm"L, WriteAppend];
    who ← CheckForImage[system, run];
    IF who = bcd THEN {
      [] ← CheckForImage[system, runner];
      PutString[com, runner];
      IF ~EndsInSwitches[runner] THEN PutString[com, " /"];
      PutString[com, "q "]};
    PutCommand[com, run ! FileNameError => {
      Error[name]; com.destroy[com]; GOTO out}];
    com.put[com, Ascii.CR];
    com.destroy[com];
    DoCommand[system, who = run];
    EXITS out => NULL;
    END;

  Go: FormSW.ProcType =
    BEGIN OPEN SegmentDefs;
    com: DiskHandle;
    standard: BOOLEAN ← TRUE;
    binding, compiling, running: BOOLEAN ← FALSE;
    who: Runner;
    system: STRING ← [40];
    IF Storage.StringLength[compile] # 0 THEN compiling ← TRUE;
    IF Storage.StringLength[bind] # 0 THEN binding ← TRUE;
    IF Storage.StringLength[run] # 0 THEN running ← TRUE;
    IF ~compiling AND ~binding AND ~running THEN RETURN;
    com ← StreamDefs.NewByteStream["Com.cm"L, WriteAppend];
    IF compiling THEN
      BEGIN
      DoGoodStuff[com, compiler, system, FALSE];
      IF ~EndsInSwitches[compiler] THEN PutString[com, " /"];
      PutString[com, "q; "];
      PutCommand[com, compile ! FileNameError => {
	Error[name]; com.destroy[com]; GOTO out}];
      com.put[com, Ascii.SP];
      END;
    IF binding THEN
      BEGIN
      s: STRING ← [40];
      DoGoodStuff[com, binder, s, compiling];
      IF ~EndsInSwitches[binder] THEN PutString[com, " /"];
      PutString[com, "q; "];
      PutCommand[com, bind ! FileNameError => {
	Error[name]; com.destroy[com]; GOTO out}];
      com.put[com, Ascii.SP];
      IF ~compiling THEN AppendString[system, s];
      END;
    IF running THEN
      BEGIN
      s: STRING ← [40];
      who ← CheckForImage[s, run];
      IF who = bcd THEN {
	DoGoodStuff[com, runner, s, compiling OR binding];
	IF ~EndsInSwitches[runner] THEN PutString[com, " /"];
	PutString[com, "q "];
	PutCommand[com, run ! FileNameError => {
          Error[name]; com.destroy[com]; GOTO out}]}
      ELSE {
	DoGoodStuff[com, run, s, compiling OR binding, TRUE];
	IF ~EndsInSwitches[s] AND ~compiling AND ~binding THEN
	  PutString[com, " /"];
	PutString[com, "q "];
	PutCommand[com, run, s.length ! FileNameError => {
          Error[name]; com.destroy[com]; GOTO out}]};
      END;
    com.put[com, Ascii.CR];
    com.destroy[com];
    DoCommand[
      system, SELECT TRUE FROM
        compiling, binding => FALSE, ENDCASE => who = run];
    EXITS out => NULL;
    END;
    
  DoCommand: PROC [system: STRING, runFile: BOOLEAN ← FALSE] = {
    OPEN SegmentDefs;
    seg: FileSegmentHandle;
    p: POINTER = OsStaticDefs.OsStatics.EventVector;
    veto: BOOLEAN ← FALSE;
    EVItem: TYPE = MACHINE DEPENDENT RECORD [
      type: [0..7777B], length: [0..17B]];
    IF debug THEN {ShowComCM[]; RETURN};
    TajoUtility.CheckVeto[abortSession ! Event.Vetoed => {veto ← TRUE; CONTINUE}];
    IF veto THEN RETURN;
    seg ← NewFileSegment[
      NewFile[system ! FileNameError => {Error[name]; GOTO out}], 1, 1, Read];
    Init.CoreSwap[cleanmaplog ! UNWIND => DeleteFileSegment[seg]];
    FixMemConfig[];
    IF ~runFile THEN ImageDefs.RunImage[seg];
    p↑ ← EVItem[6, WordsForBcplString[system.length]+1];
    MesaToBcplString[system, p+1];
    ImageDefs.StopMesa[];
    EXITS out => NULL};
    
  DoGoodStuff: PROC [
    com: DiskHandle, command, first: STRING, rSwitch, short: BOOLEAN ← FALSE] = {
    i: CARDINAL;
    c: CHARACTER;
    first.length ← i ← 0;
    DO
      SELECT (c ← command[i]) FROM
        '/ => {
	  com.put[com, c]; IF rSwitch THEN com.put[com, 'r]; i←i+1; EXIT};
        Ascii.SP => {
	  IF rSwitch THEN PutString[com, "/r"L];
	  com.put[com, c];
	  i←i+1;
	  EXIT};
	ENDCASE => {AppendChar[first, c]; com.put[com, c]};
      IF (i←i+1) = command.length THEN {
        IF rSwitch THEN PutString[com, "/r;"L]; RETURN};
      ENDLOOP;
    IF short THEN RETURN;
    UNTIL i = command.length DO
      com.put[com, command[i]];
      i ← i + 1;
      ENDLOOP};

  Runner: TYPE = {bcd, image, run};

  ShowComCM: PROC =
    BEGIN
    com: DiskHandle;
    com ← StreamDefs.NewByteStream["Com.cm"L, Read];
    DO Put.Char[NIL, com.get[com ! StreamError => EXIT]] ENDLOOP;
    Put.Char[NIL, Ascii.CR];
    com.destroy[com];
    END;

  CheckForImage: PROC [system, cmd: STRING] RETURNS [who: Runner] =
    BEGIN
    dot, end: CARDINAL;
    ssd: SubStringDescriptor;
    image: SubStringDescriptor ←
      [base: ".image"L, offset: 0, length: (".image"L).length];
    run: SubStringDescriptor ←
      [base: ".run"L, offset: 0, length: (".run"L).length];
    dot ← LAST[CARDINAL];
    end ← 0;
    FOR i: CARDINAL IN [0..cmd.length) DO
      IF dot = LAST[CARDINAL] AND cmd[i] = '. THEN dot ← i;
      IF cmd[i] = Ascii.SP OR cmd[i] = Ascii.CR OR cmd[i] = '/ THEN
	{end ← i; EXIT};
      REPEAT FINISHED => end ← cmd.length;
      ENDLOOP;
    IF dot = LAST[CARDINAL] THEN RETURN[bcd];
    ssd ← [base: cmd, offset: dot, length: end-dot];
    SELECT TRUE FROM
      EquivalentSubString[@ssd, @image] => {
	ssd.offset ← 0;
	ssd.length ← end;
	AppendSubString[system, @ssd];
	who ← image};
      EquivalentSubString[@ssd, @run] => {
	ssd.offset ← 0;
	ssd.length ← end;
	AppendSubString[system, @ssd];
	who ← run};
      ENDCASE => RETURN[bcd];
    RETURN
    END;

  EndsInSwitches: PROC [s: STRING] RETURNS [BOOLEAN] =
    BEGIN
    FOR i: CARDINAL DECREASING IN [0..s.length) DO
      SELECT s[i] FROM
        Ascii.SP => RETURN[FALSE];
        '/ => RETURN[TRUE];
        ENDCASE;
      ENDLOOP;
    RETURN[FALSE];
    END;

  CheckForExtension: PROCEDURE [name, ext: STRING] =
    BEGIN
    FOR i: CARDINAL IN [0..name.length) DO
      IF name[i] = '. THEN RETURN;
      ENDLOOP;
    AppendString[name, ext];
    RETURN
    END;

  PutString: PROCEDURE [com: DiskHandle, run: STRING] = {
    FOR i: CARDINAL IN [0..run.length) DO com.put[com, run[i]]; ENDLOOP};

  GetProc: TYPE = PROC RETURNS [c: CHARACTER];
  PutProc: TYPE = PROC [c: CHARACTER];

  PutCommand: PROCEDURE [com: DiskHandle, s: STRING, ignore: CARDINAL ← 0] =
    BEGIN
    i: CARDINAL ← 0;
    StringChar: GetProc = {
      IF i >= s.length THEN c ← Ascii.NUL ELSE {c ← s[i]; i ← i + 1}};
    TakeChars: PutProc = {com.put[com, c]};
    PutChars[TakeChars, StringChar, ignore];
    RETURN
    END;

  PutChars: PROC [put: PutProc, get: GetProc, ignore: CARDINAL] =
    BEGIN
    NUL: CHARACTER = Ascii.NUL;
    SP: CHARACTER = Ascii.SP;
    c: CHARACTER;
    s: STRING ← [40];
    stream: DiskHandle ← NIL;
    StreamChar: GetProc = 
      BEGIN
      IF stream = NIL THEN c ← Ascii.NUL
      ELSE c ← stream.get[stream ! StreamError =>
	{c ← NUL; stream.destroy[stream]; stream ← NIL; CONTINUE}];
      RETURN
      END;
    DO
      c ← get[];
      IF ignore # 0 THEN {ignore ← ignore - 1; LOOP};
      IF c = NUL THEN EXIT
      ELSE IF c = '@ THEN
	BEGIN
	s.length ← 0;
	DO c ← get[]; IF c = '@ OR c = NUL OR c = SP THEN EXIT; AppendChar[s, c]; ENDLOOP;
	CheckForExtension[s, ".cm"];
	stream ← NewByteStream[s, Read];
	PutChars[put, StreamChar, ignore ! UNWIND => stream.destroy[stream]];
	END
      ELSE put[c];
      ENDLOOP;
    END;

  Error: PROC [name: STRING] = {
    Put.Char[NIL, '!];
    Put.Text[NIL, name];
    Put.Line[NIL, " not found"L];
    UserTerminal.BlinkDisplay[]};

  MaxChars: CARDINAL = 512-4;
  Buffer: TYPE = RECORD [
    link: POINTER TO Buffer ← NIL,
    nChars: CARDINAL ← 0,
    chars: PACKED ARRAY [0..MaxChars) OF CHARACTER ← NULL];

  ExpandItem: PROC [s: POINTER TO STRING, index: CARDINAL] =
    BEGIN OPEN Storage;
    nChars: CARDINAL ← 0;
    hasSelection: BOOLEAN = Selection.Convert[subwindow] = formSW;
    p: POINTER TO PACKED ARRAY [0..0) OF CHARACTER;
    b, list, last: POINTER TO Buffer ← NIL;
    i: CARDINAL ← 0;
    StringChar: GetProc = {
      IF i >= s.length THEN c ← Ascii.NUL ELSE {c ← s[i]; i ← i + 1}};
    TakeChars: PutProc = {
      IF b = NIL THEN {
	b ← Pages[1]; b↑ ← []; last.link ← b; last ← b;
	IF list = NIL THEN list ← b};
      b.chars[b.nChars] ← c;
      IF (b.nChars ← b.nChars + 1) = MaxChars THEN b ← NIL};
    FreeBuffers: PROC = {
      UNTIL list = NIL DO b ← list.link; FreePages[list]; list ← b; ENDLOOP};
    IF StringLength[s↑] = 0 THEN RETURN;
    IF hasSelection AND FormSW.GetSelection[formSW].index = index THEN
      Selection.Clear[];
    PutChars[TakeChars, StringChar, 0 ! FileNameError => {
      Error[name]; FreeBuffers[]; GOTO ret}];
    FOR b ← list, b.link UNTIL b = NIL DO nChars ← nChars + b.nChars ENDLOOP;
    FreeString[s↑];
    s↑ ← String[nChars];
    p ← @s.text;
    FOR b ← list, b.link UNTIL b = NIL DO
      Inline.COPY[from: @b.chars, to: p, nwords: (b.nChars+1)/2];
      p ← p + (b.nChars+1)/2;
      ENDLOOP;
    s.length ← nChars;
    FreeBuffers[];
    FormSW.DisplayItem[formSW, index];
    EXITS ret => NULL;
    END;
    
  FixMemConfig: PROCEDURE =
    BEGIN OPEN SegmentDefs;
    table: POINTER TO SwapperOps.SystemTable = State.GetGS[].gsh.ESV.tables;
    userMemConfig: MemoryConfig;
    DebugUsefulDefs.ShortCopyREAD[
      from: DebugUsefulDefs.ShortREAD[@table.memConfig],
      to: @userMemConfig,
      nwords: SIZE[MemoryConfig]];
    SwapperOps.memConfig ← userMemConfig;
    END;
    
  Parms: TYPE = {expand, compile, bind, run, go, options, cs, bs, rs};
  Rep: PROC [Parms] RETURNS [CARDINAL] = MACHINE CODE BEGIN END;

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN OPEN FormSW;
    items ← AllocateItemDescriptor[Rep[LAST[Parms]]+1];
    items[Rep[expand]] ←
      CommandItem[tag: "Expand"L, place: [0, line0], proc: Expand];
    items[Rep[compile]] ←
      CommandItem[tag: "Compile"L, place: [10*7, line0], proc: Compile];
    items[Rep[bind]] ←
      CommandItem[tag: "Bind"L, place: [21*7, line0], proc: Bind];
    items[Rep[run]] ←
      CommandItem[tag: "Run"L, place: [29*7, line0], proc: Run];
    items[Rep[go]] ← CommandItem[tag: "Go"L, place: [36*7, line0], proc: Go];
    items[Rep[options]] ←
      CommandItem[tag: "Options"L, place: [42*7, line0], proc: OptionsWindow];
    items[Rep[cs]] ← StringItem[
      tag: "Compile"L, place: [0,line1], string: @compile, inHeap: TRUE];
    items[Rep[bs]] ← StringItem[
      tag: "Bind"L, place: [0,line2], string: @bind, inHeap: TRUE];
    items[Rep[rs]] ← StringItem[
      tag: "Run"L, place: [0,line3], string: @run, inHeap: TRUE];
    RETURN[items: items, freeDesc: TRUE];
    END;
    
  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    formSW ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    END;
  
  OptionsWindow: FormSW.ProcType =
    BEGIN
    box: Window.Box = window.box;
    IF options = NIL THEN options ← Tool.Create[
      name: "Options"L, makeSWsProc: OptionSW, initialState: active,
      initialBox: [[box.place.x+15, box.place.y+100], [50*7, 5*15]] ]
    ELSE {
      Put.Line[NIL, "Option window already exists."L];
      UserTerminal.BlinkDisplay[]};
    END;
    
  DoOptions: FormSW.ProcType =
    BEGIN OPEN Storage;
    IF index = 0 THEN
      BEGIN
      FreeString[compiler]; compiler ← CopyString[nComp];
      FreeString[binder]; binder ← CopyString[nBind];
      FreeString[runner]; runner ← CopyString[nRun];
      END;
    IF Selection.Convert[subwindow] = sw THEN Selection.Clear[];
    Tool.Destroy[options];
    options ← NIL;
    END;
    
  OptionSW: Tool.MakeSWsProc =
    {[] ← Tool.MakeFormSW[window: window, formProc: MakeOptions]};
    
  MakeOptions: FormSW.ClientItemsProcType =
    BEGIN OPEN FormSW;
    noptions: CARDINAL = 6;
    nComp ← Storage.CopyString[compiler];
    nBind ← Storage.CopyString[binder];
    nRun ← Storage.CopyString[runner];
    items ← AllocateItemDescriptor[noptions];
    items[0] ← CommandItem[tag: "Apply"L, proc: DoOptions, place: [0,line0]];
    items[1] ← StringItem[
      tag: "Compiler"L, place: [10*7,line0], string: @nComp, inHeap: TRUE];
    items[2] ← BooleanItem[tag: "Debug"L, switch: @debug, place: [0, line1]];
    items[3] ← StringItem[
      tag: "Binder"L, place: [10*7,line1], string: @nBind, inHeap: TRUE];
    items[4] ← CommandItem[tag: "Abort"L, proc: DoOptions, place: [0,line2]];
    items[5] ← StringItem[
      tag: "Runner"L, place: [10*7,line2], string: @nRun, inHeap: TRUE];
    RETURN[items, TRUE]
    END;
    
-- Mainline code
  
  window ← Tool.Create[
    makeSWsProc: MakeSWs, name: "Command Central"L, initialBox: [[0,0],[400,100]]];
  
END...