Added the Streams module

This commit is contained in:
ceriel 1988-01-29 11:35:45 +00:00
parent a22ab5c7b7
commit 9d83605ccd
5 changed files with 555 additions and 1 deletions

View file

@ -23,3 +23,4 @@ random.def
Traps.def
CSP.def
Epilogue.def
Streams.def

View file

@ -3,6 +3,7 @@ CSP.mod
PascalIO.mod
RealInOut.mod
InOut.mod
Streams.mod
Terminal.mod
TTY.mod
ASCII.mod

View 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:

143
lang/m2/libm2/Streams.def Normal file
View file

@ -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.

409
lang/m2/libm2/Streams.mod Normal file
View file

@ -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.