IMPLEMENTATION MODULE InOut ; IMPORT Unix; IMPORT Conversions; IMPORT Traps; FROM TTY IMPORT isatty; FROM SYSTEM IMPORT ADR; CONST BUFSIZ = 1024; (* Tunable *) TAB = 11C; TYPE IOBuf = RECORD fildes: INTEGER; cnt: INTEGER; maxcnt: INTEGER; bufferedcount: INTEGER; buf: ARRAY [1..BUFSIZ] OF CHAR; END; numbuf = ARRAY[0..255] OF CHAR; VAR ibuf, obuf: IOBuf; unread: BOOLEAN; unreadch: CHAR; PROCEDURE Read(VAR c : CHAR); BEGIN IF unread THEN unread := FALSE; c := unreadch; Done := TRUE; ELSE WITH ibuf DO IF cnt <= maxcnt THEN c := buf[cnt]; INC(cnt); Done := TRUE; ELSE c := FillBuf(ibuf); END; END; END; END Read; PROCEDURE UnRead(ch: CHAR); BEGIN unread := TRUE; unreadch := ch; END UnRead; PROCEDURE FillBuf(VAR ib: IOBuf) : CHAR; VAR c : CHAR; BEGIN WITH ib DO maxcnt := Unix.read(fildes, ADR(buf), bufferedcount); cnt := 2; Done := maxcnt > 0; IF NOT Done THEN c := 0C; ELSE c := buf[1]; END; END; RETURN c; END FillBuf; PROCEDURE Flush(VAR ob: IOBuf); VAR dummy: INTEGER; BEGIN WITH ob DO dummy := Unix.write(fildes, ADR(buf), cnt); cnt := 0; END; END Flush; PROCEDURE Write(c: CHAR); BEGIN WITH obuf DO INC(cnt); buf[cnt] := c; IF cnt >= bufferedcount THEN Flush(obuf); END; END; END Write; PROCEDURE OpenInput(defext: ARRAY OF CHAR); VAR namebuf : ARRAY [1..256] OF CHAR; BEGIN IF ibuf.fildes # 0 THEN CloseInput; END; MakeFileName("Name of input file: ", defext, namebuf); IF NOT Done THEN RETURN; END; IF (namebuf[1] = '-') AND (namebuf[2] = 0C) THEN ELSE WITH ibuf DO fildes := Unix.open(ADR(namebuf), 0); Done := fildes >= 0; maxcnt := 0; cnt := 1; END; END; END OpenInput; PROCEDURE OpenInputFile(filename: ARRAY OF CHAR); BEGIN IF ibuf.fildes # 0 THEN CloseInput; END; IF (filename[0] = '-') AND (filename[1] = 0C) THEN ELSE WITH ibuf DO fildes := Unix.open(ADR(filename), 0); Done := fildes >= 0; maxcnt := 0; cnt := 1; END; END; END OpenInputFile; PROCEDURE CloseInput; BEGIN WITH ibuf DO IF (fildes > 0) AND (Unix.close(fildes) < 0) THEN ; END; fildes := 0; maxcnt := 0; cnt := 1; END; END CloseInput; PROCEDURE OpenOutput(defext: ARRAY OF CHAR); VAR namebuf : ARRAY [1..256] OF CHAR; BEGIN IF obuf.fildes # 1 THEN CloseOutput; END; MakeFileName("Name of output file: ", defext, namebuf); IF NOT Done THEN RETURN; END; IF (namebuf[1] = '-') AND (namebuf[2] = 0C) THEN ELSE WITH obuf DO fildes := Unix.creat(ADR(namebuf), 666B); Done := fildes >= 0; bufferedcount := BUFSIZ; cnt := 0; END; END; END OpenOutput; PROCEDURE OpenOutputFile(filename: ARRAY OF CHAR); BEGIN IF obuf.fildes # 1 THEN CloseOutput; END; IF (filename[0] = '-') AND (filename[1] = 0C) THEN ELSE WITH obuf DO fildes := Unix.creat(ADR(filename), 666B); Done := fildes >= 0; bufferedcount := BUFSIZ; cnt := 0; END; END; END OpenOutputFile; PROCEDURE CloseOutput; BEGIN Flush(obuf); WITH obuf DO IF (fildes # 1) AND (Unix.close(fildes) < 0) THEN ; END; fildes := 1; bufferedcount := 1; cnt := 0; END; END CloseOutput; PROCEDURE MakeFileName(prompt, defext : ARRAY OF CHAR; VAR buf : ARRAY OF CHAR); VAR i : INTEGER; j : CARDINAL; ch: CHAR; BEGIN Done := TRUE; IF isatty(0) THEN XWriteString(prompt); END; XReadString(buf); i := 0; WHILE buf[i] # 0C DO i := i + 1 END; IF i # 0 THEN i := i - 1; IF buf[i] = '.' THEN FOR j := 0 TO HIGH(defext) DO i := i + 1; buf[i] := defext[j]; END; buf[i+1] := 0C; END; RETURN; END; Done := FALSE; END MakeFileName; PROCEDURE ReadInt(VAR integ : INTEGER); CONST SAFELIMITDIV10 = MAX(INTEGER) DIV 10; SAFELIMITREM10 = MAX(INTEGER) MOD 10; TYPE itype = [0..31]; ibuf = ARRAY itype OF CHAR; VAR int : INTEGER; neg : BOOLEAN; safedigit: [0 .. 9]; chvalue: CARDINAL; buf : ibuf; index : itype; BEGIN ReadString(buf); IF NOT Done THEN RETURN END; index := 0; IF buf[index] = '-' THEN neg := TRUE; INC(index); ELSIF buf[index] = '+' THEN neg := FALSE; INC(index); ELSE neg := FALSE END; safedigit := SAFELIMITREM10; IF neg THEN safedigit := safedigit + 1 END; int := 0; WHILE (buf[index] >= '0') & (buf[index] <= '9') DO chvalue := ORD(buf[index]) - 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); INC(index) END; END; IF neg THEN integ := -int ELSE integ := int END; IF buf[index] > " " THEN Traps.Message("illegal integer"); HALT; END; Done := TRUE; END ReadInt; PROCEDURE ReadCard(VAR card : CARDINAL); CONST SAFELIMITDIV10 = MAX(CARDINAL) DIV 10; SAFELIMITREM10 = MAX(CARDINAL) MOD 10; TYPE itype = [0..31]; ibuf = ARRAY itype OF CHAR; VAR int : CARDINAL; index : itype; buf : ibuf; safedigit: [0 .. 9]; chvalue: CARDINAL; BEGIN ReadString(buf); IF NOT Done THEN RETURN; END; index := 0; safedigit := SAFELIMITREM10; int := 0; WHILE (buf[index] >= '0') & (buf[index] <= '9') DO chvalue := ORD(buf[index]) - ORD('0'); IF (int > SAFELIMITDIV10) OR ( (int = SAFELIMITDIV10) AND (chvalue > safedigit)) THEN Traps.Message("cardinal too large"); HALT; ELSE int := 10*int + chvalue; INC(index); END; END; IF buf[index] > " " THEN Traps.Message("illegal cardinal"); HALT; END; card := int; Done := TRUE; END ReadCard; PROCEDURE ReadString(VAR s : ARRAY OF CHAR); TYPE charset = SET OF CHAR; VAR i : CARDINAL; ch : CHAR; BEGIN i := 0; REPEAT Read(ch); UNTIL NOT (ch IN charset{' ', TAB, 12C, 15C}); IF NOT Done THEN RETURN; END; REPEAT Read(ch); termCH := ch; IF i <= HIGH(s) THEN s[i] := ch; IF (NOT Done) OR (ch <= " ") THEN s[i] := 0C; END; END; INC(i); UNTIL (NOT Done) OR (ch <= " "); IF Done THEN UnRead(ch); END; END ReadString; PROCEDURE XReadString(VAR s : ARRAY OF CHAR); VAR i : INTEGER; j : CARDINAL; ch : CHAR; BEGIN j := 0; LOOP i := Unix.read(0, ADR(ch), 1); IF i < 0 THEN EXIT; END; IF ch <= " " THEN s[j] := 0C; EXIT; END; IF j < HIGH(s) THEN s[j] := ch; INC(j); END; END; END XReadString; PROCEDURE XWriteString(s: ARRAY OF CHAR); VAR i: CARDINAL; BEGIN i := 0; LOOP IF (i <= HIGH(s)) AND (s[i] # 0C) THEN INC(i); ELSE EXIT; END; END; IF Unix.write(1, ADR(s), i) < 0 THEN ; END; END XWriteString; PROCEDURE WriteCard(card, width : CARDINAL); VAR buf : numbuf; BEGIN Conversions.ConvertCardinal(card, width, buf); WriteString(buf); END WriteCard; PROCEDURE WriteInt(int : INTEGER; width : CARDINAL); VAR buf : numbuf; BEGIN Conversions.ConvertInteger(int, width, buf); WriteString(buf); END WriteInt; PROCEDURE WriteHex(card, width : CARDINAL); VAR buf : numbuf; BEGIN Conversions.ConvertHex(card, width, buf); WriteString(buf); END WriteHex; PROCEDURE WriteLn; BEGIN Write(EOL) END WriteLn; PROCEDURE WriteOct(card, width : CARDINAL); VAR buf : numbuf; BEGIN Conversions.ConvertOctal(card, width, buf); WriteString(buf); END WriteOct; PROCEDURE WriteString(str : ARRAY OF CHAR); VAR nbytes : CARDINAL; BEGIN nbytes := 0; WHILE (nbytes <= HIGH(str)) AND (str[nbytes] # 0C) DO Write(str[nbytes]); INC(nbytes) END; END WriteString; BEGIN (* InOut initialization *) WITH ibuf DO fildes := 0; bufferedcount := BUFSIZ; maxcnt := 0; cnt := 1; END; WITH obuf DO fildes := 1; bufferedcount := 1; cnt := 0; END; END InOut.