-- StreamIO.Mesa  Edited by Sandman on August 1, 1980  10:20 AM
-- Updated for Pilot Martin Newell 23-Jul-81 14:02:05

DIRECTORY
  DCSFileTypes USING [tLeaderPage],
  Directory USING [CreateFile, Lookup, UpdateDates, Error],
  File USING [Permissions, Capability, read, write, grow, shrink, delete],
  FileStream USING [Create, EndOf],
  IODefs USING [SP, CR, NumberFormat],
  TTY USING[Handle, SetEcho, GetChar, PutChar,
  	GetEditedString, NewLine,
  	Create,
	PutBackChar],
  Storage USING [Node, Free],
  Stream USING [Handle, GetChar, PutChar, GetWord, PutWord, Delete],
  StreamDefs USING [AccessOptions, StreamHandle, StreamObject, Read, Write, Append],
  String USING [SubString, StringToNumber, AppendChar, AppendNumber];

StreamIO: PROGRAM
  IMPORTS Directory, FileStream, TTY, Storage, Stream, String
  EXPORTS IODefs, StreamDefs =

  BEGIN OPEN Directory, File, FileStream, IODefs, Storage, StreamDefs, String;

  in, out: StreamHandle;
  inStreamRecord,outStreamRecord: StreamObject;
  
  NILTTY: TTY.Handle ← LOOPHOLE[NIL];
    
  --EXPORTS to IODefs
  
  GetInputStream: PUBLIC PROCEDURE RETURNS [StreamHandle] = BEGIN RETURN[in] END;

  GetOutputStream: PUBLIC PROCEDURE RETURNS [StreamHandle] = BEGIN RETURN[out] END;

  SetInputStream: PUBLIC PROCEDURE [s: StreamHandle] =
    BEGIN
    in ← s;
    END;

  SetOutputStream: PUBLIC PROCEDURE [s: StreamHandle] =
    BEGIN
    out ← s;
    END;

  SetEcho: PUBLIC PROCEDURE [new: BOOLEAN] RETURNS [old: BOOLEAN] =
    BEGIN
    IF in.tty#NILTTY THEN RETURN[TTY.SetEcho[in.tty, new]]
    ELSE RETURN[FALSE];
    END;

  -- Character operations


  ReadChar: PUBLIC PROCEDURE RETURNS [CHARACTER] = BEGIN RETURN[in.get[in]]; END;

  WriteChar: PUBLIC PROCEDURE [c: CHARACTER] =
    BEGIN out.put[out,c]; END;

  -- Reading Strings


  GetString: PROCEDURE [s: STRING, t: PROCEDURE [CHARACTER] RETURNS [BOOLEAN]] RETURNS[CHARACTER] =
    BEGIN
    IF in.tty#NILTTY THEN RETURN[TTY.GetEditedString[in.tty,s,t,TRUE]]
    ELSE
    {	s.length ← 0;
    	DO
    		c: CHARACTER ← ReadChar[];
		IF t[c] THEN RETURN[c];
		AppendChar[s,c];
    	ENDLOOP;
    };
    END;

  ReadString: PUBLIC PROCEDURE [s: STRING, t: PROCEDURE [CHARACTER] RETURNS [BOOLEAN]] =
    BEGIN
    [] ← GetString[s,t];
    END;

  ReadID: PUBLIC PROCEDURE [s: STRING] =
    BEGIN
    ReadString[s, IsAtom];
    END;

  ReadLine: PUBLIC PROCEDURE [s: STRING] =
    BEGIN
    ReadString[s, IsCR];
    END;

  IsCR: PRIVATE PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
    BEGIN RETURN[c = CR] END;

  IsAtom: PRIVATE PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
    BEGIN RETURN[c=SP OR c=CR] END;

  Rubout: PUBLIC SIGNAL = CODE;
  LineOverflow: PUBLIC SIGNAL [s: STRING] RETURNS [ns: STRING] = CODE;

  ReadEditedString: PUBLIC PROCEDURE [
    s: STRING, t: PROCEDURE [CHARACTER] RETURNS [BOOLEAN], newstring: BOOLEAN]
    RETURNS [CHARACTER] =
    BEGIN
    IF in.tty#NILTTY THEN RETURN[TTY.GetEditedString[in.tty,s,t,newstring]]
    ELSE RETURN[GetString[s,t]];
    END;

  -- Writing Strings


  WriteString: PUBLIC PROCEDURE [s: STRING] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..s.length) DO out.put[out, s[i]]; ENDLOOP;
    END;

  WriteSubString: PUBLIC PROCEDURE [s: String.SubString] =
    BEGIN
    i: CARDINAL;
    FOR i IN [s.offset..s.offset + s.length) DO out.put[out, s.base[i]]; ENDLOOP;
    END;

  WriteLine: PUBLIC PROCEDURE [s: STRING] = BEGIN WriteString[s]; WriteChar[CR]; END;

  NewLine: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    IF out.tty#NILTTY THEN RETURN[TTY.NewLine[out.tty]]
    ELSE RETURN[FALSE];
    END;

  --  Numerical i/o


  ReadNumber: PUBLIC 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: PUBLIC PROCEDURE RETURNS [INTEGER] =
    BEGIN
    s: STRING ← [10];
    [] ← ReadEditedString[s, IsAtom, TRUE];
    RETURN[StringToNumber[s, 10]]
    END;

  ReadOctal: PUBLIC PROCEDURE RETURNS [UNSPECIFIED] =
    BEGIN
    s: STRING ← [10];
    [] ← ReadEditedString[s, IsAtom, TRUE];
    RETURN[StringToNumber[s, 8]]
    END;

  OutNumber: PUBLIC 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: PUBLIC PROCEDURE [v: UNSPECIFIED, f: NumberFormat] =
    BEGIN OutNumber[out, v, f]; RETURN END;

  WriteDecimal: PUBLIC PROCEDURE [n: INTEGER] =
    BEGIN WriteNumber[n, NumberFormat[10, FALSE, FALSE, 0]]; RETURN END;

  WriteOctal: PUBLIC PROCEDURE [n: UNSPECIFIED] =
    BEGIN
    WriteNumber[n, NumberFormat[8, FALSE, TRUE, 0]];
    IF n ~IN [0..7] THEN WriteChar['B];
    RETURN
    END;
    
--TTY Stream Procedures
  TTYReset: PROCEDURE [s: StreamHandle] =
    BEGIN
    END;
  
  TTYGet: PROCEDURE [s: StreamHandle] RETURNS [CHARACTER] =
    BEGIN
    RETURN[TTY.GetChar[s.tty]];
    END;
  
  TTYPutback: PROCEDURE [s: StreamHandle, c: CHARACTER] =
    BEGIN
    TTY.PutBackChar[s.tty, c];
    END;
  
  TTYPut: PROCEDURE [s: StreamHandle, c: CHARACTER] =
    BEGIN
      TTY.PutChar[s.tty, c];
    END;
  
  TTYEndof: PROCEDURE [s: StreamHandle] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[FALSE];
    END;
  
  TTYDestroy: PROCEDURE [s: StreamHandle] =
    BEGIN
    END;
  
  --EXPORTS to StreamDefs
  
  NewByteStream: PUBLIC PROCEDURE [name: STRING, access: AccessOptions]
    	RETURNS [StreamHandle] =
    BEGIN
    s: StreamHandle ← Node[SIZE[StreamObject]];
    permissions: File.Permissions ←
    	SELECT access FROM
    	Read			=> read,
    	Write			=> write+grow+shrink+delete,
    	Append			=> grow+shrink+delete,
    	Read+Write		=> read+write+grow+shrink+delete,
    	Write+Append		=> write+grow+shrink+delete,
    	Read+Append		=> read+grow+shrink+delete,
    	Read+Write+Append	=> read+write+grow+shrink+delete,
    	ENDCASE => 0;
    s↑ ← [
      	reset: FileReset,
    	get: FileGetChar,
    	putback: FilePutback,
    	put: FilePutChar,
    	endof: FileEndof,
    	destroy: FileDestroy,
    	link: NIL,
    	file: NewStream[name, permissions],
    	tty: NILTTY
    ];
    RETURN[s];
    END;
    
  NewWordStream: PUBLIC PROCEDURE [name: STRING, access: AccessOptions]
    	RETURNS [StreamHandle] =
    BEGIN
    s: StreamHandle ← NewByteStream[name,access];
    s.get ← FileGetWord;
    s.put ← FilePutWord;
    RETURN[s];
    END;

NewStream: PROC [name: LONG STRING, access: File.Permissions]
	RETURNS [Stream.Handle] = {
cap: File.Capability;
old: BOOLEAN ← FALSE;
IF access ~= File.read THEN 
	cap ← Directory.CreateFile[name, DCSFileTypes.tLeaderPage, 0
		! Directory.Error => {
			IF type = fileAlreadyExists THEN old ← TRUE 
			ELSE ERROR;
			CONTINUE
		}]
ELSE old ← TRUE;
IF old THEN cap ← Directory.Lookup[fileName: name, permissions: access
	! Directory.Error => ERROR];
cap ← Directory.UpdateDates[cap, access];
RETURN[FileStream.Create[cap]];
};


--File Stream Procedures

  FileReset: PROCEDURE [s: StreamHandle] =
    BEGIN
    END;
  
  FileGetChar: PROCEDURE [s: StreamHandle] RETURNS [CHARACTER] =
    BEGIN
    RETURN[Stream.GetChar[s.file]];
    END;
  
  FileGetWord: PROCEDURE [s: StreamHandle] RETURNS [WORD] =
    BEGIN
    RETURN[Stream.GetWord[s.file]];
    END;
  
  FilePutback: PROCEDURE [s: StreamHandle, c: CHARACTER] =
    BEGIN
    END;
  
  FilePutChar: PROCEDURE [s: StreamHandle, c: CHARACTER] =
    BEGIN
    Stream.PutChar[s.file, c];
    END;
  
  FilePutWord: PROCEDURE [s: StreamHandle, w: WORD] =
    BEGIN
    Stream.PutWord[s.file, w];
    END;
  
  FileEndof: PROCEDURE [s: StreamHandle] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[FileStream.EndOf[s.file]];
    END;
  
  FileDestroy: PROCEDURE [s: StreamHandle] =
    BEGIN
    Stream.Delete[s.file];
    Free[s];
    END;
  
    
--START code--

  inStreamRecord ← [
    reset: TTYReset,
    get: TTYGet,
    putback: TTYPutback,
    put: TTYPut,
    endof: TTYEndof,
    destroy: TTYDestroy,
    link: NIL,
    file: NIL,
    tty: TTY.Create[""]
  ];
  in ← @inStreamRecord;
  
  outStreamRecord ← [
    reset: TTYReset,
    get: TTYGet,
    putback: TTYPutback,
    put: TTYPut,
    endof: TTYEndof,
    destroy: TTYDestroy,
    link: NIL,
    file: NIL,
    tty: in.tty
  ];
  out ← @outStreamRecord;
  
  END.