(*********************************************************************)
(* Title: SCTermStream - Tiny Kernel                                 *)
(* LastEdit: "Wed Nov 14 16:08:14 1984" by Mick Jordan               *)
(* Author: Mick Jordan                                               *)
(* Copyright (C) 1984 by Acorn Research Centre                       *)
(*********************************************************************)

IMPLEMENTATION MODULE SCTermStream;


FROM SYSTEM IMPORT WORD;
FROM Streams IMPORT
  Create, Stream, StreamProcs, ErrorCode, OptionalFunction;
FROM StreamRep IMPORT Procs, DefaultProcs, SetStreamError, NullFlush;
FROM CharCodes IMPORT CReturnCh, NewLineCh, EscapeCh;
IMPORT BadStream;

IMPORT IO;


TYPE 
  Status =
    RECORD
      opened: BOOLEAN;
      stream: Stream;
      ioOpened: BOOLEAN;
      io: CARDINAL;
      bspPending: BOOLEAN;
      lastByte: INTEGER;
      translateCRToNL: BOOLEAN;
      procs: Procs;
    END;


VAR status: Status;
(* only ever one stream so it can be global *)


PROCEDURE TranslateCR(yes: BOOLEAN);
  BEGIN
    status.translateCRToNL := yes;
  END TranslateCR;


PROCEDURE Implements(s: Stream; f: OptionalFunction): BOOLEAN;
  BEGIN
    RETURN FALSE;
  END Implements;


PROCEDURE Backspace(s: Stream);
  BEGIN
    status.bspPending := TRUE;
  END Backspace;


PROCEDURE Get(s: Stream): WORD;
  BEGIN
    IF status.bspPending THEN
      status.bspPending := FALSE;
    ELSE status.lastByte := IO.SReadByte(status.io);
      IF status.lastByte < 0 THEN
        SetStreamError(s, status.lastByte);
        status.lastByte := 0;
      ELSE
        IF status.translateCRToNL
        AND (status.lastByte = INTEGER(CReturnCh)) THEN
          status.lastByte := INTEGER(NewLineCh);
        END;
      END;
    END;
    RETURN WORD(status.lastByte)
  END Get;


PROCEDURE PutByte(s: Stream; byte: CARDINAL): BOOLEAN;

  VAR r: INTEGER;

  BEGIN
    r := IO.SWriteByte(status.io, byte);
    IF r < 0 THEN SetStreamError(s, r); RETURN FALSE ELSE RETURN TRUE END;
  END PutByte;


PROCEDURE Put(s: Stream; ch: WORD);

  VAR c: CARDINAL;
    r: INTEGER;

  BEGIN
    c := CARDINAL(ch);
    IF status.translateCRToNL AND (c = CARDINAL(NewLineCh)) THEN
      IF PutByte(s, CARDINAL(CReturnCh))
      AND PutByte(s, CARDINAL(NewLineCh)) THEN
      END;
    ELSE IF PutByte(s, c) THEN END;
    END;
  END Put;


PROCEDURE Delete(s: Stream);
  BEGIN
    status.opened := FALSE;
  END Delete;


PROCEDURE PanosSCStream(VAR s: CARDINAL; VAR error: INTEGER): BOOLEAN;

  VAR r: INTEGER;

  BEGIN
    r := IO.FindUpdate("bbc:");
    IF r >= 0 THEN
      s := CARDINAL(r);
      RETURN TRUE;
    ELSE error := r;
      RETURN FALSE;
    END;
  END PanosSCStream;


PROCEDURE CreateInOutput(): Stream;

  VAR panosError: INTEGER;
    s: Stream;

  BEGIN
    WITH status DO
      IF NOT opened THEN
        ioOpened := ioOpened OR PanosSCStream(io, panosError);
        IF ioOpened THEN
          opened := TRUE;
          stream := Create(StreamProcs(procs), 0);
          bspPending := FALSE;
          lastByte := 0;
          RETURN stream
        ELSE s := BadStream.CreateInOutput();
          SetStreamError(s, panosError);
          RETURN s;
        END
      ELSE RETURN stream;
      END;
    END;
  END CreateInOutput;


PROCEDURE StreamErrorCode(s: Stream): INTEGER;
  BEGIN
    RETURN INTEGER(s^.streamError)
  END StreamErrorCode;


BEGIN
  WITH status DO
    procs := DefaultProcs(8);
    procs^.Get := Get; procs^.Put := Put;
    procs^.Flush := NullFlush;
    procs^.Implements := Implements; 
    procs^.Delete := Delete;
    procs^.Backspace := Backspace;
    opened := FALSE;
    ioOpened := FALSE;
    translateCRToNL := TRUE;
  END;
END SCTermStream .
