Added the Streams module
authorceriel <none@none>
Fri, 29 Jan 1988 11:35:45 +0000 (11:35 +0000)
committerceriel <none@none>
Fri, 29 Jan 1988 11:35:45 +0000 (11:35 +0000)
lang/m2/libm2/.distr
lang/m2/libm2/LIST
lang/m2/libm2/Makefile
lang/m2/libm2/Streams.def [new file with mode: 0644]
lang/m2/libm2/Streams.mod [new file with mode: 0644]

index dd1754c..7ef4764 100644 (file)
@@ -23,3 +23,4 @@ random.def
 Traps.def
 CSP.def
 Epilogue.def
+Streams.def
index f70b0be..0e407d5 100644 (file)
@@ -3,6 +3,7 @@ CSP.mod
 PascalIO.mod
 RealInOut.mod
 InOut.mod
+Streams.mod
 Terminal.mod
 TTY.mod
 ASCII.mod
index 432029a..fd76aa3 100644 (file)
@@ -6,7 +6,7 @@ SOURCES =       ASCII.def EM.def MathLib0.def Processes.def \
                random.def Semaphores.def Unix.def RealConver.def \
                Strings.def InOut.def Terminal.def TTY.def \
                Mathlib.def PascalIO.def Traps.def CSP.def \
-               Epilogue.def
+               Epilogue.def Streams.def
 
 all:
 
diff --git a/lang/m2/libm2/Streams.def b/lang/m2/libm2/Streams.def
new file mode 100644 (file)
index 0000000..4bdc851
--- /dev/null
@@ -0,0 +1,143 @@
+DEFINITION MODULE Streams;
+(*
+ * This module provides sequential IO through streams.
+ * A stream is either a text stream or a binary stream, and is either in
+ * reading, writing or appending mode.
+ * By default, there are three open text streams, connected to standard input,
+ * standard output, and standard error respectively.
+ * These are text streams.
+ * When connected to a terminal, the standard output and standard error
+ * streams are linebuffered.
+ * The user can create more streams with the OpenStream call, and
+ * delete streams with the CloseStream call.
+ * Streams are automatically closed at program termination.
+ *)
+
+  FROM SYSTEM IMPORT BYTE;
+
+  TYPE StreamKind = (text, binary, none);
+       StreamMode = (reading, writing, appending);
+       StreamResult = (succeeded, illegaloperation,
+                       nomemory, openfailed, nostream, endoffile);
+       StreamBuffering = (unbuffered, linebuffered, blockbuffered);
+  TYPE Stream;
+
+  VAR  InputStream, OutputStream, ErrorStream: Stream;
+
+  PROCEDURE OpenStream(VAR stream: Stream;
+                      filename: ARRAY OF CHAR; 
+                      kind: StreamKind;
+                      mode: StreamMode;
+                      VAR result: StreamResult);
+  (* Associates a stream with the file named filename.
+     If kind = none, result is set to illegaloperation.
+     mode has one of the follwing values:
+       reading:                the file is opened for reading
+       writing:                the file is truncated or created for writing
+       appending:      the file is opened for writing at end of file,
+                       or created for writing.
+     On failure, result is set to openfailed.
+     On success, result is set to succeeded.
+  *)
+
+  PROCEDURE SetStreamBuffering( stream: Stream;
+                               b: StreamBuffering;
+                               VAR result: StreamResult);
+  (* This procedure is only allowed for output streams.
+     The three types of buffering available are unbuffered, linebuffered,
+     and blockbuffered. When an output stream is unbuffered, the output
+     appears as soon as written; when it is blockbuffered, output is saved
+     up and written as a block; When it is linebufferded (only possible for
+     text output streams), output is saved up until a newline is encountered
+     or input is read from standard input.
+  *)
+
+  PROCEDURE CloseStream(VAR stream: Stream; VAR result: StreamResult);
+  (* Closes the stream.
+     result is set to nostream if "stream" was not associated with a stream.
+  *)
+
+  PROCEDURE FlushStream(stream: Stream; VAR result: StreamResult);
+  (* Flushes the stream.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to illegaloperation if "stream" is not an output or
+     appending stream.
+  *)
+
+  PROCEDURE EndOfStream(stream: Stream; VAR result: StreamResult): BOOLEAN;
+  (* Returns true if the stream is an input stream, and the end of the file
+     has been reached.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to illegaloperation if the stream is not an input stream.
+  *)
+
+  PROCEDURE Read(stream: Stream; VAR ch: CHAR; VAR result: StreamResult);
+  (* reads a character from the stream. Certain character translations may
+     occur, such as the mapping of the end-of-line sequence to the
+     character 12C.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to endoffile if EndOfStream would have returned TRUE before the
+     call to Read. In this case, "ch" is set to 0C.
+     It is set to illegaloperation if the stream is not a text input stream.
+  *)
+
+  PROCEDURE ReadByte(stream: Stream; VAR byte: BYTE; VAR result: StreamResult);
+  (* reads a byte from the stream. No character translations occur.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to endoffile if EndOfStream would have returned TRUE before the
+     call to ReadByte. In this case, "byte" is set to 0C.
+     It is set to illegaloperation if the stream is not a binary input stream.
+  *)
+
+  PROCEDURE ReadBytes(stream: Stream;
+                     VAR bytes: ARRAY OF BYTE;
+                     VAR result: StreamResult);
+  (* reads bytes from the stream. No character translations occur.
+     The number of bytes is determined by the size of the parameter.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to endoffile if there are not enough bytes left on the stream.
+     In this case, the rest of the bytes are set to 0C.
+     It is set to illegaloperation if the stream is not a binary input stream.
+  *)
+
+  PROCEDURE Write(stream: Stream; ch: CHAR; VAR result: StreamResult);
+  (* writes a character to the stream. Certain character translations may
+     occur, such as the mapping of a line-feed or carriage return (12C or 15C)
+     to the end-of-line sequence of the system.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to illegaloperation if the stream is not a text output stream.
+  *)
+
+  PROCEDURE WriteByte(stream: Stream; byte: BYTE; VAR result: StreamResult);
+  (* writes a byte to the stream. No character translations occur.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to illegaloperation if the stream is not a binary output stream.
+  *)
+
+  PROCEDURE WriteBytes(stream: Stream;
+                      VAR bytes: ARRAY OF BYTE;
+                      VAR result: StreamResult);
+  (* writes bytes to the stream. No character translations occur.
+     The number of bytes written is equal to the size of the parameter.
+     result is set to nostream if "stream" was not associated with a stream.
+     It is set to illegaloperation if the stream is not a binary output stream.
+  *)
+
+  PROCEDURE GetPosition(stream: Stream;
+                       VAR position: LONGINT;
+                       VAR result: StreamResult);
+  (* gives the actual read/write position in "position".
+     "result" is set to illegaloperation if "stream" is not a stream.
+  *)
+
+  PROCEDURE SetPosition(stream: Stream;
+                       position: LONGINT;
+                       VAR result: StreamResult);
+  (* sets the actual read/write position to "position".
+     "result" is set to illegaloperation if "stream" is not a stream or
+     the stream was opened for appending and the position is in front of
+     the current position, or it failed for some other
+     reason (f.i. when the stream is connected to a terminal).
+  *)
+
+END Streams.
diff --git a/lang/m2/libm2/Streams.mod b/lang/m2/libm2/Streams.mod
new file mode 100644 (file)
index 0000000..23f472e
--- /dev/null
@@ -0,0 +1,409 @@
+(*$R-*)
+IMPLEMENTATION MODULE Streams;
+
+  FROM SYSTEM IMPORT BYTE, ADR;
+  IMPORT Unix, TTY, Storage, Epilogue;
+
+  CONST BUFSIZ = 1024; (* tunable *)
+  TYPE IOB = RECORD
+               kind: StreamKind;
+               mode: StreamMode;
+               eof: BOOLEAN;
+               buffering: StreamBuffering;
+               next : Stream;
+               fildes: INTEGER;
+               cnt, maxcnt: INTEGER;
+               bufferedcnt: INTEGER;
+               buf: ARRAY[1..BUFSIZ] OF BYTE;
+             END;
+       Stream = POINTER TO IOB;
+  VAR
+       ibuf, obuf, ebuf: IOB;
+       head: Stream;
+
+  PROCEDURE getstruct(VAR stream: Stream);
+  BEGIN
+        stream := head;
+        WHILE (stream # NIL) AND (stream^.kind # none) DO
+                stream := stream^.next;
+        END;
+        IF stream = NIL THEN
+               IF NOT Storage.Available(SIZE(IOB)) THEN
+                       RETURN;
+               END;
+                Storage.ALLOCATE(stream,SIZE(IOB));
+                stream^.next := head;
+                head := stream;
+        END;
+  END getstruct;
+  PROCEDURE freestruct(stream: Stream);
+  BEGIN
+       stream^.kind := none;
+  END freestruct;
+
+  PROCEDURE OpenStream(VAR stream: Stream;
+                      filename: ARRAY OF CHAR; 
+                      kind: StreamKind;
+                      mode: StreamMode;
+                      VAR result: StreamResult);
+    VAR fd: INTEGER;
+       i: CARDINAL;
+  BEGIN
+       IF kind = none THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       getstruct(stream);
+       IF stream = NIL THEN
+               result := nomemory;
+               RETURN;
+       END;
+        WITH stream^ DO
+                FOR i := 0 TO HIGH(filename) DO
+                        buf[i+1] := BYTE(filename[i]);
+                END;
+                buf[HIGH(filename)+2] := BYTE(0C);
+       END;
+       IF (mode = reading) THEN
+               fd := Unix.open(ADR(stream^.buf), 0);
+       ELSE
+               fd := -1;
+               IF (mode = appending) THEN
+                       fd := Unix.open(ADR(stream^.buf), 1);
+                       IF fd >= 0 THEN
+                               IF (Unix.lseek(fd, 0D , 2) < 0D) THEN ; END;
+                       END;
+               END;
+               IF fd < 0 THEN
+                       fd := Unix.creat(ADR(stream^.buf), 666B);
+               END;
+       END;
+       IF fd < 0 THEN
+               result := openfailed;
+               freestruct(stream);
+               stream := NIL;
+               RETURN;
+       END;
+       result := succeeded;
+       stream^.fildes := fd;
+       stream^.kind := kind;
+       stream^.mode := mode;
+       stream^.buffering := blockbuffered;
+       stream^.bufferedcnt := BUFSIZ;
+       stream^.maxcnt := 0;
+       stream^.eof := FALSE;
+       IF mode = reading THEN
+               stream^.cnt := 1;
+       ELSE
+               stream^.cnt := 0;
+       END;
+  END OpenStream;
+
+  PROCEDURE SetStreamBuffering( stream: Stream;
+                               b: StreamBuffering;
+                               VAR result: StreamResult);
+  BEGIN
+       result := succeeded;
+       IF (stream = NIL) OR (stream^.kind = none) THEN
+               result := nostream;
+               RETURN;
+       END;
+       IF (stream^.mode = reading) OR
+          ((b = linebuffered) AND (stream^.kind = binary)) THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       FlushStream(stream, result);
+       IF b = unbuffered THEN
+               stream^.bufferedcnt := 1;
+       END;
+       stream^.buffering := b;
+  END SetStreamBuffering;
+
+  PROCEDURE FlushStream(stream: Stream; VAR result: StreamResult);
+  BEGIN
+       result := succeeded;
+       IF (stream = NIL) OR (stream^.kind = none) THEN
+               result := nostream;
+               RETURN;
+       END;
+       WITH stream^ DO
+               IF mode = reading THEN
+                       result := illegaloperation;
+                       RETURN;
+               END;
+               IF (cnt > 0) AND (Unix.write(fildes, ADR(buf), cnt) < 0) THEN
+                       ;
+               END;
+               cnt := 0;
+       END;
+  END FlushStream;
+
+  PROCEDURE CloseStream(VAR stream: Stream; VAR result: StreamResult);
+  BEGIN
+       IF (stream # NIL) AND (stream^.kind # none) THEN
+               result := succeeded;
+               IF stream^.mode # reading THEN
+                       FlushStream(stream, result);
+               END;
+               IF Unix.close(stream^.fildes) < 0 THEN ; END;
+               freestruct(stream);
+       ELSE
+               result := nostream;
+       END;
+       stream := NIL;
+  END CloseStream;
+       
+  PROCEDURE EndOfStream(stream: Stream; VAR result: StreamResult): BOOLEAN;
+  BEGIN
+       result := succeeded;
+       IF (stream = NIL) OR (stream^.kind = none) THEN
+               result := nostream;
+               RETURN FALSE;
+       END;
+       IF stream^.mode # reading THEN
+               result := illegaloperation;
+               RETURN FALSE;
+       END;
+       IF stream^.eof THEN RETURN TRUE; END;
+       RETURN (CHAR(NextByte(stream)) = 0C) AND stream^.eof;
+  END EndOfStream;
+
+  PROCEDURE FlushLineBuffers();
+  VAR  s: Stream;
+       result: StreamResult;
+  BEGIN
+       s := head;
+       WHILE s # NIL DO
+               IF (s^.kind # none) AND (s^.buffering = linebuffered) THEN
+                       FlushStream(s, result);
+               END;
+               s := s^.next;
+       END;
+  END FlushLineBuffers;
+
+  PROCEDURE NextByte(stream: Stream): BYTE;
+  VAR c: BYTE;
+  BEGIN
+       WITH stream^ DO
+               IF cnt <= maxcnt THEN
+                       c := buf[cnt];
+               ELSE
+                       IF eof THEN RETURN BYTE(0C); END;
+                       IF stream = InputStream THEN
+                               FlushLineBuffers();
+                       END;
+                       maxcnt := Unix.read(fildes, ADR(buf), bufferedcnt);
+                       cnt := 1;
+                       IF maxcnt <= 0 THEN
+                               eof := TRUE;
+                               c := BYTE(0C);
+                       ELSE
+                               c := buf[1];
+                       END;
+               END;
+       END;
+       RETURN c;
+  END NextByte;
+
+  PROCEDURE Read(stream: Stream; VAR ch: CHAR; VAR result: StreamResult);
+  VAR EoF: BOOLEAN;
+  BEGIN
+       ch := 0C;
+       EoF := EndOfStream(stream, result);
+       IF result # succeeded THEN RETURN; END;
+       IF EoF THEN
+               result := endoffile;
+               RETURN;
+       END;
+       WITH stream^ DO
+               ch := CHAR(buf[cnt]);
+               INC(cnt);
+       END;
+  END Read;
+
+  PROCEDURE ReadByte(stream: Stream; VAR byte: BYTE; VAR result: StreamResult);
+  VAR EoF: BOOLEAN;
+  BEGIN
+       byte := BYTE(0C);
+       EoF := EndOfStream(stream, result);
+       IF result # succeeded THEN RETURN; END;
+       IF EoF THEN
+               result := endoffile;
+               RETURN;
+       END;
+       WITH stream^ DO
+               byte := buf[cnt];
+               INC(cnt);
+       END;
+  END ReadByte;
+
+  PROCEDURE ReadBytes(stream: Stream;
+                     VAR bytes: ARRAY OF BYTE;
+                     VAR result: StreamResult);
+  VAR i: CARDINAL;
+  BEGIN
+       FOR i := 0 TO HIGH(bytes) DO
+               ReadByte(stream, bytes[i], result);
+       END;
+  END ReadBytes;
+
+  PROCEDURE Write(stream: Stream; ch: CHAR; VAR result: StreamResult);
+  BEGIN
+       IF (stream = NIL) OR (stream^.kind = none) THEN
+               result := nostream;
+               RETURN;
+       END;
+       IF (stream^.kind # text) OR (stream^.mode = reading) THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       WITH stream^ DO
+               INC(cnt);
+               buf[cnt] := BYTE(ch);
+               IF (cnt >= bufferedcnt) OR
+                  ((ch = 12C) AND (buffering = linebuffered))
+               THEN
+                       FlushStream(stream, result);
+               END;
+       END;
+  END Write;
+
+  PROCEDURE WriteByte(stream: Stream; byte: BYTE; VAR result: StreamResult);
+  BEGIN
+       IF (stream = NIL) OR (stream^.kind = none) THEN
+               result := nostream;
+               RETURN;
+       END;
+       IF (stream^.kind # binary) OR (stream^.mode = reading) THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       WITH stream^ DO
+               INC(cnt);
+               buf[cnt] := byte;
+               IF cnt >= bufferedcnt THEN
+                       FlushStream(stream, result);
+               END;
+       END;
+  END WriteByte;
+
+  PROCEDURE WriteBytes(stream: Stream; VAR bytes: ARRAY OF BYTE; VAR result: StreamResult);
+  VAR i: CARDINAL;
+  BEGIN
+       FOR i := 0 TO HIGH(bytes) DO
+               WriteByte(stream, bytes[i], result);
+       END;
+  END WriteBytes;
+
+  PROCEDURE EndIt;
+  VAR h, h1 : Stream;
+      result: StreamResult;
+  BEGIN
+       h := head;
+       WHILE h # NIL DO
+               h1 := h;
+               CloseStream(h1, result);
+               h := h^.next;
+       END;
+  END EndIt;
+
+  PROCEDURE GetPosition(s: Stream; VAR position: LONGINT;
+                       VAR result: StreamResult);
+  BEGIN
+       IF (s = NIL) OR (s^.kind = none) THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       IF (s^.mode # reading) THEN FlushStream(s, result); END;
+       position := Unix.lseek(s^.fildes, 0D, 1);
+       IF position < 0D THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       IF s^.mode = reading THEN
+               position := position + LONG(s^.maxcnt - s^.cnt + 1);
+       END;
+  END GetPosition;
+
+  PROCEDURE SetPosition(s: Stream; position: LONGINT; VAR result: StreamResult);
+  VAR currpos: LONGINT;
+  BEGIN
+       currpos := 0D;
+       IF (s = NIL) OR (s^.kind = none) THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       IF (s^.mode # reading) THEN
+               FlushStream(s, result);
+       ELSE
+               s^.maxcnt := 0;
+               s^.eof := FALSE;
+       END;
+       IF s^.mode = appending THEN
+               currpos := Unix.lseek(s^.fildes, 0D, 1);
+               IF currpos < 0D THEN
+                       result := illegaloperation;
+                       RETURN;
+               END;
+       END;
+       IF position < currpos THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       currpos := Unix.lseek(s^.fildes, position, 0);
+       IF currpos < 0D THEN
+               result := illegaloperation;
+               RETURN;
+       END;
+       result := succeeded;
+  END SetPosition;
+
+BEGIN
+       InputStream := ADR(ibuf);
+       OutputStream := ADR(obuf);
+       ErrorStream := ADR(ebuf);
+       WITH ibuf DO
+               kind := text;
+               mode := reading;
+               eof := FALSE;
+               next := ADR(obuf);
+               fildes := 0;
+               maxcnt := 0;
+               cnt := 1;
+               bufferedcnt := BUFSIZ;
+       END;
+       WITH obuf DO
+               kind := text;
+               mode := writing;
+               eof := TRUE;
+               next := ADR(ebuf);
+               fildes := 1;
+               maxcnt := 0;
+               cnt := 0;
+               bufferedcnt := BUFSIZ;
+               IF TTY.isatty(1) THEN
+                       buffering := linebuffered;
+               ELSE
+                       buffering := blockbuffered;
+               END;
+       END;
+       WITH ebuf DO
+               kind := text;
+               mode := writing;
+               eof := TRUE;
+               next := NIL;
+               fildes := 2;
+               maxcnt := 0;
+               cnt := 0;
+               bufferedcnt := BUFSIZ;
+               IF TTY.isatty(2) THEN
+                       buffering := linebuffered;
+               ELSE
+                       buffering := blockbuffered;
+               END;
+       END;
+       head := InputStream;
+       IF Epilogue.CallAtEnd(EndIt) THEN ; END;
+END Streams.