Added the Streams module
This commit is contained in:
parent
a22ab5c7b7
commit
9d83605ccd
|
@ -23,3 +23,4 @@ random.def
|
|||
Traps.def
|
||||
CSP.def
|
||||
Epilogue.def
|
||||
Streams.def
|
||||
|
|
|
@ -3,6 +3,7 @@ CSP.mod
|
|||
PascalIO.mod
|
||||
RealInOut.mod
|
||||
InOut.mod
|
||||
Streams.mod
|
||||
Terminal.mod
|
||||
TTY.mod
|
||||
ASCII.mod
|
||||
|
|
|
@ -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
143
lang/m2/libm2/Streams.def
Normal 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
409
lang/m2/libm2/Streams.mod
Normal 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.
|
Loading…
Reference in a new issue