-- ExecStreamIO.Mesa
-- Edited by Sandman on July 1, 1980  8:33 AM
-- Edited by Brotz on June 15, 1982  1:34 PM
-- Edited by Levin on January 20, 1981  5:05 PM
DIRECTORY
  Editor USING [MakeCharIndexVisible, RefreshFromFirstChange],
  inD: FROM "InteractorDefs" USING [CharIndex, StopBlinkingCaret],
  intCommon USING [target],
  IODefs USING [BS, ControlA, ControlH, ControlQ, ControlR, ControlV, ControlW,
    ControlX, CR, DEL, ESC, LF, NumberFormat, SP],
  LaurelExecImpDefs USING [cmMnp, ioState, lock, RefreshCaret, ShortenTypeScript,
    SpliceExecutiveIntoEditor],
  StreamDefs USING [GetDefaultDisplayStream, GetDefaultKey, StreamError, StreamHandle],
  String USING [AppendChar, AppendNumber, StringToNumber, SubString,
    SubStringDescriptor],
  vmD: FROM "VirtualMgrDefs" USING [CharIndex, ComposedMessage,
    ComposedMessagePtr, InsertSubstringInMessage, StartMessageInsertion,
    StopMessageInsertion, UnAppendMessageChar];
ExecStreamIO: MONITOR LOCKS LaurelExecImpDefs.lock
  IMPORTS Editor, inD, intC: intCommon, LaurelExecImpDefs, StreamDefs, String, vmD
  EXPORTS IODefs
  SHARES IODefs, StreamDefs = PUBLIC
  
  BEGIN OPEN StreamDefs, IODefs, String;
  
  in, out: StreamHandle;
  beginLine, echo: PRIVATE BOOLEAN ← TRUE;
  
  GetInputStream: PROCEDURE RETURNS [StreamHandle] = BEGIN RETURN[in] END;
    
  GetOutputStream: PROCEDURE RETURNS [StreamHandle] = BEGIN RETURN[out] END;
    
  SetInputStream: PROCEDURE [s: StreamHandle] = BEGIN in ← s END;
    
  SetOutputStream: PROCEDURE [s: StreamHandle] =
    BEGIN out ← s; beginLine ← TRUE; END;
    
  SetEcho: PROCEDURE [new: BOOLEAN] RETURNS [old: BOOLEAN] =
    BEGIN old ← echo; echo ← new END;
    
  -- Character operations
  
  
  ReadChar: PROCEDURE RETURNS [CHARACTER] = BEGIN RETURN[in.get[in]]; END;
    
  WriteChar: PROCEDURE [c: CHARACTER] =
    BEGIN out.put[out, c]; beginLine ← c = CR; END;
    
  -- Reading Strings
  
  
  ReadString: PROCEDURE [s: STRING, t: PROCEDURE [CHARACTER] RETURNS [BOOLEAN]] =
    BEGIN WriteChar[ReadEditedString[s, t, TRUE]]; END;
    
  ReadID: PROCEDURE [s: STRING] =
    BEGIN [] ← ReadEditedString[s, IsAtom, TRUE]; END;
    
  ReadLine: PROCEDURE [s: STRING] =
    BEGIN [] ← ReadEditedString[s, IsCR, TRUE]; WriteChar[CR]; END;
    
  IsCR: PRIVATE PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
    BEGIN RETURN[c = CR] END;
    
  IsAtom: PRIVATE PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
    BEGIN RETURN[IF c = SP OR c = CR THEN TRUE ELSE FALSE] END;
    
  Rubout: SIGNAL = CODE;
  LineOverflow: SIGNAL [s: STRING] RETURNS [ns: STRING] = CODE;
  
  ReadEditedString: PROCEDURE [
    s: STRING, t: PROCEDURE [CHARACTER] RETURNS [BOOLEAN], newstring: BOOLEAN]
    RETURNS [CHARACTER] =
    BEGIN
    c: CHARACTER;
    i: CARDINAL;
    state: {ti, v, li};
    c ← in.get[in];
    IF newstring THEN
      IF c = ESC THEN BEGIN WriteString[s]; c ← in.get[in]; END ELSE s.length ← 0;
    UNTIL t[c] DO
      SELECT c FROM
DEL => SIGNAL Rubout;
ControlA, ControlH =>
  BEGIN
  IF s.length > 0 THEN
    BEGIN
    IF echo THEN
      WITH out SELECT FROM
Display => clearChar[out, s[s.length - 1]];
ENDCASE => out.put[out, c];
    s.length ← s.length - 1;
    END;
  END;
ControlW, ControlQ =>
  BEGIN -- text to be backed up is of the form
  -- ...<li><v><ti>;   the <v> and <ti> are to be removed.
  state ← ti;
  FOR i DECREASING IN [0..s.length) DO
    SELECT s[
      i] FROM
      IN [’A..’Z], IN [’a..’z], IN [’0..’9] =>
IF state = ti THEN state ← v;
      ENDCASE => IF state = v THEN state ← li;
    IF state = li THEN GO TO Done;
    IF echo THEN
      WITH out SELECT FROM
Display => clearChar[out, s[i]];
ENDCASE => out.put[out, ControlA];
    REPEAT Done => s.length ← i + 1; FINISHED => s.length ← 0;
    ENDLOOP;
  END;
ControlR => IF echo THEN BEGIN WriteChar[CR]; WriteString[s]; END;
ControlX =>
  BEGIN
  IF echo THEN
    WITH out SELECT FROM
      Display => clearCurrentLine[out];
      ENDCASE => out.put[out, c];
  s.length ← 0;
  END;
ControlV =>
  BEGIN
  WHILE s.length >= s.maxlength DO s ← SIGNAL LineOverflow[s]; ENDLOOP;
  s[s.length] ← c ← in.get[in];
  s.length ← s.length + 1;
  IF echo THEN WriteChar[c];
  END;
ENDCASE =>
  BEGIN
  WHILE s.length >= s.maxlength DO s ← SIGNAL LineOverflow[s]; ENDLOOP;
  s[s.length] ← c;
  s.length ← s.length + 1;
  IF echo THEN WriteChar[c];
  END;
      c ← in.get[in];
      ENDLOOP;
    RETURN[c];
    END;
    
  -- Writing Strings
  
  
WriteString: PROCEDURE [s: STRING] =
BEGIN
ssd: String.SubStringDescriptor ← [base: s, offset: 0, length: s.length];
WriteSubString[@ssd];
END;  -- of WriteString --
WriteSubString: ENTRY PROCEDURE [s: String.SubString] =
BEGIN
OPEN LaurelExecImpDefs;
start: vmD.CharIndex;
resumeScan: CARDINAL ← s.offset;
ssEnd: CARDINAL = s.offset + s.length;
runLength: CARDINAL;
cm: vmD.ComposedMessagePtr;
WITH s: out SELECT FROM
  Display => NULL;
  ENDCASE => ERROR StreamDefs.StreamError[out, StreamType ! UNWIND => NULL];
IF ioState = originalScreen THEN {ioState ← typescriptActive; SpliceExecutiveIntoEditor[]};
start ← intC.target.point;
cm ← vmD.ComposedMessage[cmMnp.message];
inD.StopBlinkingCaret[];
[] ← Editor.MakeCharIndexVisible[start, cmMnp];
IF start > 60000 THEN {LaurelExecImpDefs.ShortenTypeScript[]; start ← intC.target.point};
UNTIL resumeScan >= ssEnd DO
  startInSS: CARDINAL ← resumeScan;
  FOR i: CARDINAL IN [startInSS .. ssEnd) DO
    SELECT s.base[i] FROM
      LF => {runLength ← i - startInSS; resumeScan ← i + 1; EXIT};
      CR => {runLength ← i - startInSS + 1; resumeScan ← i + 1; EXIT};
      BS => BEGIN
        runLength ← i - startInSS;
        IF runLength > 0 THEN resumeScan ← i
        ELSE BEGIN
          resumeScan ← i + 1;
          start ← start - 1;
          vmD.UnAppendMessageChar[cm];
          Editor.RefreshFromFirstChange[start, 1, 0, cmMnp];
          [] ← Editor.MakeCharIndexVisible[start, cmMnp];
          END;
        EXIT;
        END;
      ENDCASE;
    REPEAT
    FINISHED => {runLength ← ssEnd - startInSS; resumeScan ← ssEnd};
    ENDLOOP;
  IF runLength = 0 THEN LOOP;
  vmD.StartMessageInsertion[cm, start];
  [] ← vmD.InsertSubstringInMessage[cm, s.base, startInSS, runLength];
  vmD.StopMessageInsertion[cm];
  Editor.RefreshFromFirstChange[start, 0, runLength, cmMnp];
  start ← start + runLength;
  [] ← Editor.MakeCharIndexVisible[start, cmMnp];
  ENDLOOP;
intC.target.point ← start;
LaurelExecImpDefs.RefreshCaret[FALSE];
IF s.length ~= 0 THEN beginLine ← s.base[s.offset + s.length - 1] = CR;
END;  -- of WriteSubString --
    
  WriteLine: PROCEDURE [s: STRING] = BEGIN WriteString[s]; WriteChar[CR]; END;
    
  NewLine: PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN[beginLine] END;
    
  --  Numerical i/o
  
  
  ReadNumber: PROCEDURE [default: UNSPECIFIED, radix: CARDINAL]
    RETURNS [UNSPECIFIED] =
    BEGIN
    s: STRING ← [10];
    IF radix = 10 AND LOOPHOLE[default, INTEGER] < 0 THEN
      BEGIN default ← -default; s[0] ← ’-; s.length ← 1 END;
    AppendNumber[s, default, radix];
    IF radix = 8 THEN AppendChar[s, ’B];
    [] ← ReadEditedString[s, IsAtom, TRUE];
    RETURN[StringToNumber[s, radix]];
    END;
    
  ReadDecimal: PROCEDURE RETURNS [INTEGER] =
    BEGIN
    s: STRING ← [10];
    [] ← ReadEditedString[s, IsAtom, TRUE];
    RETURN[StringToNumber[s, 10]]
    END;
    
  ReadOctal: PROCEDURE RETURNS [UNSPECIFIED] =
    BEGIN
    s: STRING ← [10];
    [] ← ReadEditedString[s, IsAtom, TRUE];
    RETURN[StringToNumber[s, 8]]
    END;
    
  OutNumber: PROCEDURE [
    stream: StreamHandle, val: INTEGER, format: NumberFormat] =
    BEGIN
    i: CARDINAL;
    neg: BOOLEAN ← FALSE;
    fill: CHARACTER ← (IF format.zerofill THEN ’0 ELSE ’ );
    s: STRING ← [17];
    IF val < 0 AND ~format.unsigned THEN BEGIN val ← -val; neg ← TRUE END;
    AppendNumber[s, val, format.base];
    i ← s.length;
    IF neg THEN
      BEGIN
      i ← i + 1;
      IF format.zerofill THEN BEGIN stream.put[stream, ’-]; neg ← FALSE END;
      END;
    THROUGH (i..format.columns] DO stream.put[stream, fill] ENDLOOP;
    IF neg THEN stream.put[stream, ’-];
    FOR i IN [0..s.length) DO stream.put[stream, s[i]] ENDLOOP;
    RETURN
    END;
    
  WriteNumber: PROCEDURE [v: UNSPECIFIED, f: NumberFormat] =
    BEGIN OutNumber[out, v, f]; beginLine ← FALSE; RETURN END;
    
  WriteDecimal: PROCEDURE [n: INTEGER] =
    BEGIN WriteNumber[n, NumberFormat[10, FALSE, FALSE, 0]]; RETURN END;
    
  WriteOctal: PROCEDURE [n: UNSPECIFIED] =
    BEGIN
    WriteNumber[n, NumberFormat[8, FALSE, TRUE, 0]];
    IF n ~ IN [0..7] THEN WriteChar[’B];
    RETURN
    END;
    
  in ← StreamDefs.GetDefaultKey[ ! ANY => CONTINUE];
  out ← StreamDefs.GetDefaultDisplayStream[ ! ANY => CONTINUE];
  
  END.