1 (* RTgen.mod implement a generic device interface used by ISO.
3 Copyright (C) 2008-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE RTgen
;
30 FROM IOChan
IMPORT ChanId
, InvalidChan
, ChanExceptions
;
32 FROM IOLink
IMPORT DeviceTablePtrValue
,
37 IMPORT ErrnoCategory
;
40 FROM RTgenif
IMPORT getDID
,
41 doReadChar
, doUnReadChar
, doGetErrno
,
42 doRBytes
, doWBytes
, doWrLn
,
43 isEOF
, isError
, isEOLN
;
45 FROM ChanConsts
IMPORT FlagSet
, readFlag
, writeFlag
, rawFlag
,
46 textFlag
, read
, write
, raw
, text
;
48 FROM Storage
IMPORT ALLOCATE
, DEALLOCATE
;
52 ChanDev
= POINTER TO RECORD
58 deviceExceptions
= ARRAY DeviceType
OF BOOLEAN ;
62 raiseEofInSkip
: deviceExceptions
;
66 InitChanDev - initialize and return a ChanDev.
69 PROCEDURE InitChanDev (t
: DeviceType
; d
: DeviceId
; g
: GenDevIF
) : ChanDev
;
84 KillChanDev - deallocates, g.
87 PROCEDURE KillChanDev (g
: GenDevIF
) : GenDevIF
;
94 (* internal routine to check whether we have a valid channel *)
96 PROCEDURE checkValid (g
: ChanDev
; d
: DeviceTablePtr
) ;
99 IF getDID(g^.genif
)#did
101 RAISEdevException(cid
, did
, wrongDevice
,
102 'operation attempted on an invalid channel')
104 IF (cid
=InvalidChan()) OR (cid
=NIL)
106 RAISEdevException(cid
, did
, wrongDevice
,
107 'operation attempted on an invalid channel')
109 IF d#
DeviceTablePtrValue(cid
, did
)
111 RAISEdevException(cid
, did
, wrongDevice
,
112 'operation attempted on an invalid channel')
119 checkErrno - checks a number of errno conditions and raises
120 appropriate ISO exceptions if they occur.
123 PROCEDURE checkErrno (g
: ChanDev
; d
: DeviceTablePtr
) ;
126 IF isError(g^.genif
, d
)
128 errNum
:= doGetErrno(g^.genif
, d
) ;
129 IF ErrnoCategory.
IsErrnoHard(errNum
)
131 RAISEdevException(cid
, did
, notAvailable
,
132 'unrecoverable (errno)')
133 ELSIF ErrnoCategory.
UnAvailable(errNum
)
135 RAISEdevException(cid
, did
, notAvailable
,
136 'unavailable (errno)')
139 RAISEdevException(cid
, did
, notAvailable
,
140 'recoverable (errno)')
147 PROCEDURE checkPreRead (g
: ChanDev
;
149 raise
, raw
: BOOLEAN) ;
152 IF isEOF(g^.genif
, d
)
154 result
:= IOConsts.endOfInput
;
157 RAISEdevException(cid
, did
, skipAtEnd
,
158 'attempting to read beyond end of file')
160 ELSIF (NOT raw
) AND isEOLN(g^.genif
, d
)
162 result
:= IOConsts.endOfLine
164 result
:= IOConsts.allRight
171 checkPostRead - checks whether an error occurred and sets
172 the result status. This must only be called
176 PROCEDURE checkPostRead (g
: ChanDev
; d
: DeviceTablePtr
) ;
187 PROCEDURE setReadResult (g
: ChanDev
; d
: DeviceTablePtr
) ;
190 IF isEOF(g^.genif
, d
)
192 result
:= IOConsts.endOfInput
193 ELSIF isEOLN(g^.genif
, d
)
195 result
:= IOConsts.endOfLine
197 result
:= IOConsts.allRight
203 PROCEDURE checkPreWrite (g
: ChanDev
; d
: DeviceTablePtr
) ;
209 PROCEDURE checkPostWrite (g
: ChanDev
; d
: DeviceTablePtr
) ;
216 checkFlags - checks read/write raw/text consistancy flags.
219 PROCEDURE checkFlags (f
: FlagSet
; d
: DeviceTablePtr
) ;
222 IF (readFlag
IN f
) AND (NOT (readFlag
IN flags
))
224 RAISEdevException(cid
, did
, wrongDevice
,
225 'attempting to read from a channel which was configured to write')
227 IF (writeFlag
IN f
) AND (NOT (writeFlag
IN flags
))
229 RAISEdevException(cid
, did
, wrongDevice
,
230 'attempting to write to a channel which was configured to read')
232 IF (rawFlag
IN f
) AND (NOT (rawFlag
IN flags
))
236 RAISEdevException(cid
, did
, notAvailable
,
237 'attempting to read raw LOCs from a channel which was configured to read text')
239 RAISEdevException(cid
, did
, notAvailable
,
240 'attempting to write raw LOCs from a channel which was configured to write text')
248 RaiseEOFinLook - returns TRUE if the Look procedure
249 should raise an exception if it
253 PROCEDURE RaiseEOFinLook (g
: ChanDev
) : BOOLEAN ;
255 RETURN( raiseEofInLook
[g^.type
] )
260 RaiseEOFinSkip - returns TRUE if the Skip procedure
261 should raise an exception if it
265 PROCEDURE RaiseEOFinSkip (g
: ChanDev
) : BOOLEAN ;
267 RETURN( raiseEofInSkip
[g^.type
] )
272 doLook - if there is a character as the next item in
273 the input stream then it assigns its value
274 to ch without removing it from the stream;
275 otherwise the value of ch is not defined.
276 r and result are set to the value allRight,
277 endOfLine, or endOfInput.
280 PROCEDURE doLook (g
: ChanDev
;
283 VAR r
: ReadResults
) ;
288 checkPreRead(g
, d
, RaiseEOFinLook(g
), ChanConsts.rawFlag
IN flags
) ;
289 IF (result
=IOConsts.allRight
) OR (result
=IOConsts.notKnown
) OR
290 (result
=IOConsts.endOfLine
)
292 ch
:= doReadChar(g^.genif
, d
) ;
293 setReadResult(g
, d
) ;
295 ch
:= doUnReadChar(g^.genif
, d
, ch
)
305 PROCEDURE doSkip (g
: ChanDev
;
312 checkPreRead(g
, d
, RaiseEOFinSkip(g
), ChanConsts.rawFlag
IN flags
) ;
313 ch
:= doReadChar(g^.genif
, d
) ;
320 doSkipLook - read a character, ignore it. Read another and unread it
321 return the new character.
324 PROCEDURE doSkipLook (g
: ChanDev
;
327 VAR r
: ReadResults
) ;
334 PROCEDURE doWriteLn (g
: ChanDev
;
339 checkPreWrite(g
, d
) ;
340 IF doWrLn(g^.genif
, d
)
348 PROCEDURE doReadText (g
: ChanDev
;
352 VAR charsRead
: CARDINAL) ;
357 checkFlags(read
+text
, d
) ;
361 INCL(flags
, textFlag
) ;
362 checkPreRead(g
, d
, FALSE, FALSE) ;
365 IF doRBytes(g^.genif
, d
, to
, maxChars
, i
)
372 (* if our target system does not support errno then we *)
373 RAISEdevException(cid
, did
, notAvailable
,
374 'textread unrecoverable errno')
376 UNTIL (maxChars
=0) OR isEOF(g^.genif
, d
) ;
383 PROCEDURE doWriteText (g
: ChanDev
;
386 charsToWrite
: CARDINAL) ;
391 checkFlags(write
+text
, d
) ;
393 checkPreWrite(g
, d
) ;
394 INCL(flags
, textFlag
) ;
395 WHILE (charsToWrite
>0) AND doWBytes(g^.genif
, d
, from
, charsToWrite
, i
) DO
399 IF isError(g^.genif
, d
)
402 (* if our target system does not support errno then we *)
403 RAISEdevException(cid
, did
, notAvailable
,
404 'textwrite unrecoverable errno')
411 PROCEDURE doReadLocs (g
: ChanDev
;
415 VAR locsRead
: CARDINAL) ;
420 checkFlags(read
+raw
, d
) ;
424 INCL(flags
, rawFlag
) ;
425 checkPreRead(g
, d
, FALSE, TRUE) ;
428 IF doRBytes(g^.genif
, d
, to
, maxLocs
, i
)
435 (* if our target system does not support errno then we *)
436 RAISEdevException(cid
, did
, notAvailable
,
437 'rawread unrecoverable errno')
439 UNTIL (maxLocs
=0) OR isEOF(g^.genif
, d
) ;
446 PROCEDURE doWriteLocs (g
: ChanDev
;
449 locsToWrite
: CARDINAL) ;
454 checkFlags(write
+raw
, d
) ;
456 checkPreWrite(g
, d
) ;
457 INCL(flags
, rawFlag
) ;
458 WHILE doWBytes(g^.genif
, d
, from
, locsToWrite
, i
) AND (i
<locsToWrite
) DO
462 IF isError(g^.genif
, d
)
465 (* if our target system does not support errno then we *)
466 RAISEdevException(cid
, did
, notAvailable
,
467 'rawwrite unrecoverable errno')
475 (* seqfile, streamfile, programargs, stdchans, term , socket, rndfile *)
476 raiseEofInLook
:= deviceExceptions
{ FALSE , FALSE , FALSE , FALSE , FALSE, FALSE , FALSE };
477 raiseEofInSkip
:= deviceExceptions
{ TRUE , TRUE , TRUE , TRUE , TRUE , TRUE , TRUE };