From: ceriel Date: Fri, 29 Jan 1988 11:35:45 +0000 (+0000) Subject: Added the Streams module X-Git-Tag: release-5-5~3636 X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=9d83605ccda652acfd9f1ac5ec29c9441a580680;p=ack.git Added the Streams module --- diff --git a/lang/m2/libm2/.distr b/lang/m2/libm2/.distr index dd1754cef..7ef476437 100644 --- a/lang/m2/libm2/.distr +++ b/lang/m2/libm2/.distr @@ -23,3 +23,4 @@ random.def Traps.def CSP.def Epilogue.def +Streams.def diff --git a/lang/m2/libm2/LIST b/lang/m2/libm2/LIST index f70b0be5a..0e407d519 100644 --- a/lang/m2/libm2/LIST +++ b/lang/m2/libm2/LIST @@ -3,6 +3,7 @@ CSP.mod PascalIO.mod RealInOut.mod InOut.mod +Streams.mod Terminal.mod TTY.mod ASCII.mod diff --git a/lang/m2/libm2/Makefile b/lang/m2/libm2/Makefile index 432029aba..fd76aa3f0 100644 --- a/lang/m2/libm2/Makefile +++ b/lang/m2/libm2/Makefile @@ -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 index 000000000..4bdc85168 --- /dev/null +++ b/lang/m2/libm2/Streams.def @@ -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 index 000000000..23f472ecd --- /dev/null +++ b/lang/m2/libm2/Streams.mod @@ -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.