(* (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. See the copyright notice in the ACK home directory, in the file "Copyright". *) (*$R-*) IMPLEMENTATION MODULE PascalIO; (* Module: Pascal-like Input/Output Author: Ceriel J.H. Jacobs Version: $Header$ *) IMPORT Conversions; IMPORT Traps; IMPORT RealConversions; FROM Streams IMPORT Stream, StreamKind, StreamMode, StreamResult, InputStream, OutputStream, OpenStream, CloseStream, EndOfStream, Read, Write, StreamBuffering; FROM Storage IMPORT Allocate; FROM SYSTEM IMPORT ADR; TYPE charset = SET OF CHAR; btype = (Preading, Pwriting, free); CONST spaces = charset{11C, 12C, 13C, 14C, 15C, ' '}; TYPE IOstream = RECORD type: btype; done, eof : BOOLEAN; ch: CHAR; next: Text; stream: Stream; END; Text = POINTER TO IOstream; numbuf = ARRAY[0..255] OF CHAR; VAR ibuf, obuf: IOstream; head: Text; result: StreamResult; PROCEDURE Reset(VAR InputText: Text; Filename: ARRAY OF CHAR); BEGIN doclose(InputText); getstruct(InputText); WITH InputText^ DO OpenStream(stream, Filename, text, reading, result); IF result # succeeded THEN Traps.Message("could not open input file"); HALT; END; type := Preading; done := FALSE; eof := FALSE; END; END Reset; PROCEDURE Rewrite(VAR OutputText: Text; Filename: ARRAY OF CHAR); BEGIN doclose(OutputText); getstruct(OutputText); WITH OutputText^ DO OpenStream(stream, Filename, text, writing, result); IF result # succeeded THEN Traps.Message("could not open output file"); HALT; END; type := Pwriting; END; END Rewrite; PROCEDURE CloseOutput(); VAR p: Text; BEGIN p := head; WHILE p # NIL DO doclose(p); p := p^.next; END; END CloseOutput; PROCEDURE doclose(Xtext: Text); BEGIN IF Xtext # Notext THEN WITH Xtext^ DO IF type # free THEN CloseStream(stream, result); type := free; END; END; END; END doclose; PROCEDURE getstruct(VAR Xtext: Text); BEGIN Xtext := head; WHILE (Xtext # NIL) AND (Xtext^.type # free) DO Xtext := Xtext^.next; END; IF Xtext = NIL THEN Allocate(Xtext,SIZE(IOstream)); Xtext^.next := head; head := Xtext; END; END getstruct; PROCEDURE Error(tp: btype); BEGIN IF tp = Preading THEN Traps.Message("input text expected"); ELSE Traps.Message("output text expected"); END; HALT; END Error; PROCEDURE ReadChar(InputText: Text; VAR ch : CHAR); BEGIN ch := NextChar(InputText); IF InputText^.eof THEN Traps.Message("unexpected EOF"); HALT; END; InputText^.done := FALSE; END ReadChar; PROCEDURE NextChar(InputText: Text): CHAR; BEGIN WITH InputText^ DO IF type # Preading THEN Error(Preading); END; IF NOT done THEN IF EndOfStream(stream, result) THEN eof := TRUE; ch := 0C; ELSE Read(stream, ch, result); done := TRUE; END; END; RETURN ch; END; END NextChar; PROCEDURE Get(InputText: Text); VAR dummy: CHAR; BEGIN ReadChar(InputText, dummy); END Get; PROCEDURE Eoln(InputText: Text): BOOLEAN; BEGIN RETURN NextChar(InputText) = 12C; END Eoln; PROCEDURE Eof(InputText: Text): BOOLEAN; BEGIN RETURN (NextChar(InputText) = 0C) AND InputText^.eof; END Eof; PROCEDURE ReadLn(InputText: Text); VAR ch: CHAR; BEGIN REPEAT ReadChar(InputText, ch) UNTIL ch = 12C; END ReadLn; PROCEDURE WriteChar(OutputText: Text; char: CHAR); BEGIN WITH OutputText^ DO IF type # Pwriting THEN Error(Pwriting); END; Write(stream, char, result); END; END WriteChar; PROCEDURE WriteLn(OutputText: Text); BEGIN WriteChar(OutputText, 12C); END WriteLn; PROCEDURE Page(OutputText: Text); BEGIN WriteChar(OutputText, 14C); END Page; PROCEDURE ReadInteger(InputText: Text; VAR int : INTEGER); CONST SAFELIMITDIV10 = MAX(INTEGER) DIV 10; SAFELIMITREM10 = MAX(INTEGER) MOD 10; VAR neg : BOOLEAN; safedigit: CARDINAL; ch: CHAR; chvalue: CARDINAL; BEGIN WHILE NextChar(InputText) IN spaces DO Get(InputText); END; ch := NextChar(InputText); IF ch = '-' THEN Get(InputText); ch := NextChar(InputText); neg := TRUE; ELSIF ch = '+' THEN Get(InputText); ch := NextChar(InputText); neg := FALSE; ELSE neg := FALSE END; safedigit := SAFELIMITREM10; IF neg THEN safedigit := safedigit + 1 END; int := 0; IF (ch >= '0') AND (ch <= '9') THEN WHILE (ch >= '0') & (ch <= '9') DO chvalue := ORD(ch) - ORD('0'); IF (int < -SAFELIMITDIV10) OR ( (int = -SAFELIMITDIV10) AND (chvalue > safedigit)) THEN Traps.Message("integer too large"); HALT; ELSE int := 10*int - VAL(INTEGER, chvalue); Get(InputText); ch := NextChar(InputText); END; END; IF NOT neg THEN int := -int END; ELSE Traps.Message("integer expected"); HALT; END; END ReadInteger; PROCEDURE ReadCardinal(InputText: Text; VAR card : CARDINAL); CONST SAFELIMITDIV10 = MAX(CARDINAL) DIV 10; SAFELIMITREM10 = MAX(CARDINAL) MOD 10; VAR ch : CHAR; safedigit: CARDINAL; chvalue: CARDINAL; BEGIN WHILE NextChar(InputText) IN spaces DO Get(InputText); END; ch := NextChar(InputText); safedigit := SAFELIMITREM10; card := 0; IF (ch >= '0') AND (ch <= '9') THEN WHILE (ch >= '0') & (ch <= '9') DO chvalue := ORD(ch) - ORD('0'); IF (card > SAFELIMITDIV10) OR ( (card = SAFELIMITDIV10) AND (chvalue > safedigit)) THEN Traps.Message("cardinal too large"); HALT; ELSE card := 10*card + chvalue; Get(InputText); ch := NextChar(InputText); END; END; ELSE Traps.Message("cardinal expected"); HALT; END; END ReadCardinal; PROCEDURE ReadReal(InputText: Text; VAR real: REAL); VAR buf: numbuf; ch: CHAR; ok: BOOLEAN; index: INTEGER; PROCEDURE inch(): CHAR; BEGIN buf[index] := ch; INC(index); Get(InputText); RETURN NextChar(InputText); END inch; BEGIN index := 0; ok := TRUE; WHILE NextChar(InputText) IN spaces DO Get(InputText); END; ch := NextChar(InputText); IF (ch ='+') OR (ch = '-') THEN ch := inch(); END; IF (ch >= '0') AND (ch <= '9') THEN WHILE (ch >= '0') AND (ch <= '9') DO ch := inch(); END; IF (ch = '.') THEN ch := inch(); IF (ch >= '0') AND (ch <= '9') THEN WHILE (ch >= '0') AND (ch <= '9') DO ch := inch(); END; ELSE ok := FALSE; END; END; IF ok AND (ch = 'E') THEN ch := inch(); IF (ch ='+') OR (ch = '-') THEN ch := inch(); END; IF (ch >= '0') AND (ch <= '9') THEN WHILE (ch >= '0') AND (ch <= '9') DO ch := inch(); END; ELSE ok := FALSE; END; END; ELSE ok := FALSE; END; IF ok THEN buf[index] := 0C; RealConversions.StringToReal(buf, real, ok); END; IF NOT ok THEN Traps.Message("Illegal real"); HALT; END; END ReadReal; PROCEDURE WriteCardinal(OutputText: Text; card: CARDINAL; width: CARDINAL); VAR buf : numbuf; BEGIN Conversions.ConvertCardinal(card, 1, buf); WriteString(OutputText, buf, width); END WriteCardinal; PROCEDURE WriteInteger(OutputText: Text; int: INTEGER; width: CARDINAL); VAR buf : numbuf; BEGIN Conversions.ConvertInteger(int, 1, buf); WriteString(OutputText, buf, width); END WriteInteger; PROCEDURE WriteBoolean(OutputText: Text; bool: BOOLEAN; width: CARDINAL); BEGIN IF bool THEN WriteString(OutputText, " TRUE", width); ELSE WriteString(OutputText, "FALSE", width); END; END WriteBoolean; PROCEDURE WriteReal(OutputText: Text; real: REAL; width, nfrac: CARDINAL); VAR buf: numbuf; ok: BOOLEAN; digits: INTEGER; BEGIN IF width > SIZE(buf) THEN width := SIZE(buf); END; IF nfrac > 0 THEN RealConversions.RealToString(real, width, nfrac, buf, ok); ELSE IF width < 9 THEN width := 9; END; IF real < 0.0 THEN digits := 7 - INTEGER(width); ELSE digits := 6 - INTEGER(width); END; RealConversions.RealToString(real, width, digits, buf, ok); END; WriteString(OutputText, buf, 0); END WriteReal; PROCEDURE WriteString(OutputText: Text; str: ARRAY OF CHAR; width: CARDINAL); VAR index: CARDINAL; BEGIN index := 0; WHILE (index <= HIGH(str)) AND (str[index] # Eos) DO INC(index); END; WHILE index < width DO WriteChar(OutputText, " "); INC(index); END; index := 0; WHILE (index <= HIGH(str)) AND (str[index] # Eos) DO WriteChar(OutputText, str[index]); INC(index); END; END WriteString; BEGIN (* PascalIO initialization *) WITH ibuf DO stream := InputStream; eof := FALSE; type := Preading; done := FALSE; END; WITH obuf DO stream := OutputStream; eof := FALSE; type := Pwriting; END; Notext := NIL; Input := ADR(ibuf); Output := ADR(obuf); Input^.next := Output; Output^.next := NIL; head := Input; END PascalIO.