-- StreamDefsImpl.mesa
-- Updated for Pilot Martin Newell 23-Jul-81 14:02:05
-- Last changed by Doug Wyatt, 13-Oct-81 17:46:21
-- Last changed by Bill Paxton, 22-Jan-82  8:35:38

DIRECTORY
  DCSFileTypes USING [tLeaderPage],
  Directory USING [CreateFile, Error, Lookup],
  File USING [Capability, grow, LimitPermissions, Permissions, read, shrink, write],
  FileStream USING [Create, EndOf, SetIndex],
  JaMStorage USING [Zone],
  Inline USING [BITAND, BITOR],
  Stream USING [Delete, GetChar, GetWord, Handle, PutChar, PutWord],
  StreamDefs USING [AccessOptions, Append, Read, StreamErrorCode,
    StreamHandle, StreamObject, Write];

StreamDefsImpl: PROGRAM
IMPORTS Directory, File, FileStream, JaMStorage, Inline, Stream
EXPORTS StreamDefs = {
OPEN StreamDefs;

zone: UNCOUNTED ZONE = JaMStorage.Zone[];

StreamError: PUBLIC SIGNAL[stream: StreamHandle, error: StreamErrorCode] = CODE;
FileNameError: PUBLIC SIGNAL[name: LONG STRING] = CODE;

IsSet: PROC[a,b: UNSPECIFIED] RETURNS[BOOLEAN] = INLINE {
  RETURN[Inline.BITAND[a,b]#0] };
Set: PROC[p: POINTER TO UNSPECIFIED, b: UNSPECIFIED] = INLINE {
  p↑ ← Inline.BITOR[p↑,b] };

NewByteStream: PUBLIC PROC[name: LONG STRING, access: AccessOptions]
  RETURNS [StreamHandle] = { OPEN File;
  data: Data ← NIL;
  s: StreamHandle ← NIL;
  permissions: Permissions ← 0;
  IF IsSet[access,Read] THEN Set[@permissions,read];
  IF IsSet[access,Write] THEN Set[@permissions,write+shrink];
  IF IsSet[access,Append] THEN Set[@permissions,write+grow];
  IF permissions=0 THEN permissions ← read;
  data ← NewStream[name,permissions];
  s ← zone.NEW[StreamObject ← [reset: FileReset, get: FileGetChar,
    putback: FilePutback, put: FilePutChar, endof: FileEndof, destroy: FileDestroy,
    data: data]];
  RETURN[s];
  };

NewWordStream: PUBLIC PROC[name: LONG STRING, access: AccessOptions]
  RETURNS [StreamHandle] = {
  s: StreamHandle ← NewByteStream[name,access];
  s.get ← FileGetWord; s.put ← FilePutWord;
  RETURN[s];
  };

Data: TYPE = LONG POINTER TO FileStreamData;
FileStreamData: TYPE = RECORD[file: Stream.Handle];

NewStream: PROC[name: LONG STRING, access: File.Permissions]
  RETURNS[Data] = {
  cap: File.Capability;
  new: BOOLEAN ← FALSE;
  cap ← Directory.Lookup[fileName: name, permissions: access !
    Directory.Error => SELECT type FROM
      fileNotFound => { new ← TRUE; CONTINUE };
      invalidFileName => GOTO NotFound;
      ENDCASE];
  IF new THEN {
    IF IsSet[access,File.read] THEN GOTO NotFound;
    cap ← Directory.CreateFile[fileName: name,
      fileType: DCSFileTypes.tLeaderPage, size: 0];
    cap ← File.LimitPermissions[cap,access];
    };
  RETURN[zone.NEW[FileStreamData ← [FileStream.Create[cap]]]];
  EXITS NotFound => ERROR FileNameError[name];
  };

-- File Stream Procedures

  FileReset: PROC[s: StreamHandle] = {
    data: Data ← s.data;
    FileStream.SetIndex[data.file,0];
    };
  
  FileGetChar: PROC[s: StreamHandle] RETURNS [CHARACTER] = {
    data: Data ← s.data;
    RETURN[Stream.GetChar[data.file]];
    };
  
  FileGetWord: PROC[s: StreamHandle] RETURNS [WORD] = {
    data: Data ← s.data;
    RETURN[Stream.GetWord[data.file]];
    };
  
  FilePutback: PROC[s: StreamHandle, c: CHARACTER] = {
    ERROR StreamError[s,StreamOperation];
    };
  
  FilePutChar: PROC[s: StreamHandle, c: CHARACTER] = {
    data: Data ← s.data;
    Stream.PutChar[data.file, c];
    };
  
  FilePutWord: PROC[s: StreamHandle, w: WORD] = {
    data: Data ← s.data;
    Stream.PutWord[data.file, w];
    };
  
  FileEndof: PROC[s: StreamHandle] RETURNS [BOOLEAN] = {
    data: Data ← s.data;
    RETURN[FileStream.EndOf[data.file]];
    };
  
  FileDestroy: PROC[s: StreamHandle] = {
    data: Data ← s.data;
    Stream.Delete[data.file];
    zone.FREE[@data];
    zone.FREE[@s];
    };

}.