1 (* TermFile.mod implement the ISO TermFile specification.
3 Copyright (C) 2009-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 TermFile
;
30 FROM ASCII
IMPORT nul
, lf
, cr
;
31 FROM ChanConsts
IMPORT ChanFlags
;
32 FROM RTio
IMPORT GetDeviceId
;
33 FROM RTgenif
IMPORT GenDevIF
, InitGenDevIF
;
34 FROM RTdata
IMPORT ModuleId
, MakeModuleId
, InitData
, GetData
, KillData
;
35 FROM IOChan
IMPORT ChanExceptions
, InvalidChan
, CurrentFlags
;
36 FROM IOConsts
IMPORT ReadResults
;
37 FROM Strings
IMPORT Assign
;
39 FROM IOLink
IMPORT DeviceId
, DeviceTable
, DeviceTablePtr
, DeviceTablePtrValue
, IsDevice
,
40 AllocateDeviceId
, RAISEdevException
, MakeChan
, UnMakeChan
;
42 FROM Storage
IMPORT ALLOCATE
, DEALLOCATE
;
43 FROM Strings
IMPORT Append
;
46 FROM SYSTEM
IMPORT ADDRESS
, ADR
, LOC
;
47 FROM errno
IMPORT geterrno
;
48 FROM ErrnoCategory
IMPORT GetOpenResults
;
50 FROM RTgen
IMPORT ChanDev
, DeviceType
, InitChanDev
,
51 doLook
, doSkip
, doSkipLook
, doWriteLn
,
52 doReadText
, doWriteText
, doReadLocs
, doWriteLocs
,
55 FROM DynamicStrings
IMPORT String
, InitStringCharStar
, CopyOut
,
58 FROM termios
IMPORT TERMIOS
, InitTermios
, KillTermios
, tcgetattr
,
59 tcsetattr
, cfmakeraw
, tcsnow
;
70 PtrToLoc
= POINTER TO LOC
;
71 TermInfo
= POINTER TO RECORD
85 InitTermInfo - creates a new TermInfo and initializes fields,
89 PROCEDURE InitTermInfo (fd
: INTEGER) : TermInfo
;
95 t^.pushBack
:= FALSE ;
96 t^.new
:= InitTermios() ;
97 t^.old
:= InitTermios() ;
103 KillTermInfo - deallocates memory associated with, t.
106 PROCEDURE KillTermInfo (t
: TermInfo
) : TermInfo
;
109 new
:= KillTermios(new
) ;
110 old
:= KillTermios(old
)
118 getFd - return the file descriptor associated with, t.
121 PROCEDURE getFd (t
: TermInfo
) : INTEGER ;
128 getPushBackChar - returns TRUE if a previously pushed back
129 character is available. If TRUE then,
130 ch, will be assigned to the pushed back
134 PROCEDURE getPushBackChar (t
: TermInfo
; VAR ch
: CHAR) : BOOLEAN ;
146 END getPushBackChar
;
150 setPushBackChar - attempts to push back, ch. Only one character
151 may be pushed back consecutively.
154 PROCEDURE setPushBackChar (t
: TermInfo
; ch
: CHAR) : BOOLEAN ;
166 END setPushBackChar
;
169 PROCEDURE look (d
: DeviceTablePtr
;
170 VAR ch
: CHAR; VAR r
: ReadResults
) ;
172 doLook(dev
, d
, ch
, r
)
176 PROCEDURE skip (d
: DeviceTablePtr
) ;
182 PROCEDURE skiplook (d
: DeviceTablePtr
;
183 VAR ch
: CHAR; VAR r
: ReadResults
) ;
185 doSkipLook(dev
, d
, ch
, r
)
189 PROCEDURE lnwrite (d
: DeviceTablePtr
) ;
195 PROCEDURE textread (d
: DeviceTablePtr
;
198 VAR charsRead
: CARDINAL) ;
200 doReadText(dev
, d
, to
, maxChars
, charsRead
)
204 PROCEDURE textwrite (d
: DeviceTablePtr
;
206 charsToWrite
: CARDINAL);
208 doWriteText(dev
, d
, from
, charsToWrite
)
212 PROCEDURE rawread (d
: DeviceTablePtr
;
215 VAR locsRead
: CARDINAL) ;
217 doReadLocs(dev
, d
, to
, maxLocs
, locsRead
)
221 PROCEDURE rawwrite (d
: DeviceTablePtr
;
223 locsToWrite
: CARDINAL) ;
225 doWriteLocs(dev
, d
, from
, locsToWrite
)
230 doreadchar - returns a CHAR from the file associated with, g.
233 PROCEDURE doreadchar (g
: GenDevIF
; d
: DeviceTablePtr
) : CHAR ;
240 t
:= GetData(d
, mid
) ;
243 IF NOT getPushBackChar(t
, ch
)
246 i
:= libc.
read(fd
, ADR(ch
), SIZE(ch
))
259 dounreadchar - pushes a CHAR back onto the file associated with, g.
262 PROCEDURE dounreadchar (g
: GenDevIF
; d
: DeviceTablePtr
; ch
: CHAR) : CHAR ;
267 t
:= GetData(d
, mid
) ;
270 IF NOT setPushBackChar(t
, ch
)
272 RAISEdevException(cid
, did
, notAvailable
,
273 'TermFile.dounreadchar: cannot push back more than one character consecutively')
281 dogeterrno - returns the errno relating to the generic device.
284 PROCEDURE dogeterrno (g
: GenDevIF
; d
: DeviceTablePtr
) : INTEGER ;
291 dorbytes - reads upto, max, bytes setting, actual, and
292 returning FALSE if an error (not due to eof)
296 PROCEDURE dorbytes (g
: GenDevIF
; d
: DeviceTablePtr
;
299 VAR actual
: CARDINAL) : BOOLEAN ;
306 t
:= GetData(d
, mid
) ;
311 IF getPushBackChar(t
, p^
)
317 i
:= libc.
read(fd
, p
, max
) ;
323 errNum
:= geterrno() ;
334 dowbytes - attempts to write out nBytes. The actual
335 number of bytes written are returned.
336 If the actual number of bytes written is >= 0 then
337 the return result will be true. Failure to
338 write any bytes results in returning FALSE
339 errno set and the actual will be set to zero.
342 PROCEDURE dowbytes (g
: GenDevIF
; d
: DeviceTablePtr
;
345 VAR actual
: CARDINAL) : BOOLEAN ;
351 t
:= GetData(d
, mid
) ;
354 i
:= libc.
write(fd
, from
, nBytes
) ;
360 errNum
:= geterrno() ;
369 dowriteln - attempt to write an end of line marker to the
370 file and returns TRUE if successful.
373 PROCEDURE dowriteln (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
375 a
: ARRAY [0.
.1] OF CHAR ;
380 RETURN( dowbytes(g
, d
, ADR(a
), SIZE(a
), i
) AND (i
=SIZE(a
)) )
385 iseof - returns TRUE if end of file is seen.
388 PROCEDURE iseof (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
392 ch
:= doreadchar(g
, d
) ;
396 ch
:= dounreadchar(g
, d
, ch
) ;
406 iseoln - returns TRUE if end of line is seen.
409 PROCEDURE iseoln (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
413 ch
:= doreadchar(g
, d
) ;
417 ch
:= dounreadchar(g
, d
, ch
) ;
427 iserror - returns TRUE if an error was seen on the device.
430 PROCEDURE iserror (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
432 RETURN( d^.errNum#
0 )
437 getname - assigns, a, to the device name of the terminal.
440 PROCEDURE getname (d
: DeviceTablePtr
;
441 VAR a
: ARRAY OF CHAR) ;
445 s
:= InitStringCharStar(libc.
ttyname(0)) ;
452 freeData - disposes of, t.
455 PROCEDURE freeData (t
: TermInfo
) ;
465 PROCEDURE handlefree (d
: DeviceTablePtr
) ;
471 t
:= GetData(d
, mid
) ;
473 i
:= libc.
close(fd
) ;
480 termOpen - attempts to open up the terminal device. It fills
481 in any implied flags and returns a result depending
482 whether the open was successful.
485 PROCEDURE termOpen (t
: TermInfo
; VAR flagset
: FlagSet
; VAR e
: INTEGER) : OpenResults
;
490 IF NOT (rawFlag
IN flagset
)
492 INCL(flagset
, textFlag
)
494 IF NOT (echoFlag
IN flagset
)
496 INCL(flagset
, interactiveFlag
)
498 IF NOT (writeFlag
IN flagset
)
500 INCL(flagset
, readFlag
)
502 IF writeFlag
IN flagset
504 fd
:= libc.
open(ADR("/dev/tty"), O_WRONLY
, 0600B
)
506 fd
:= libc.
open(ADR("/dev/tty"), O_RDONLY
)
508 IF tcgetattr(fd
, new
)=0
511 IF tcgetattr(fd
, old
)=0
513 IF rawFlag
IN flagset
517 IF tcsetattr(fd
, tcsnow(), new
)=0
522 RETURN( GetOpenResults(e
) )
528 RestoreTerminalSettings -
531 PROCEDURE RestoreTerminalSettings (cid
: ChanId
) ;
537 d
:= DeviceTablePtrValue(cid
, did
) ;
538 t
:= GetData(d
, mid
) ;
540 IF tcsetattr(fd
, tcsnow(), old
)=0
544 END RestoreTerminalSettings
;
548 Open - attempts to obtain and open a channel connected to
549 the terminal. Without the raw flag, text is implied.
550 Without the echo flag, line mode is requested,
551 otherwise single character mode is requested.
552 If successful, assigns to cid the identity of
553 the opened channel, and assigns the value opened to res.
554 If a channel cannot be opened as required, the value of
555 res indicates the reason, and cid identifies the
559 PROCEDURE Open (VAR cid
: ChanId
;
560 flagset
: FlagSet
; VAR res
: OpenResults
) ;
566 MakeChan(did
, cid
) ; (* create new channel *)
567 d
:= DeviceTablePtrValue(cid
, did
) ;
568 t
:= InitTermInfo(-1) ;
569 res
:= termOpen(t
, flagset
, e
) ;
570 InitData(d
, mid
, t
, freeData
) ; (* attach memory to device and module *)
576 doSkipLook
:= skiplook
;
577 doLnWrite
:= lnwrite
;
578 doTextRead
:= textread
;
579 doTextWrite
:= textwrite
;
580 doRawRead
:= rawread
;
581 doRawWrite
:= rawwrite
;
582 doGetName
:= getname
;
589 IsTermFile - tests if the channel identified by cid is open to
593 PROCEDURE IsTermFile (cid
: ChanId
) : BOOLEAN ;
595 RETURN( (cid #
NIL) AND (InvalidChan() # cid
) AND
596 (IsDevice(cid
, did
)) AND
597 ((readFlag
IN CurrentFlags(cid
)) OR
598 (writeFlag
IN CurrentFlags(cid
))) )
603 Close - if the channel identified by cid is not open to the
604 terminal, the exception wrongDevice is raised; otherwise
605 closes the channel, and assigns the value identifying
606 the invalid channel to cid.
609 PROCEDURE Close (VAR cid
: ChanId
) ;
613 RestoreTerminalSettings(cid
) ;
614 UnMakeChan(did
, cid
) ;
617 RAISEdevException(cid
, did
, wrongDevice
,
618 'TermFile.' + __FUNCTION__
+
619 ': channel is opened to the terminal')
633 AllocateDeviceId(did
) ;
634 gen
:= InitGenDevIF(did
,
635 doreadchar
, dounreadchar
,
636 dogeterrno
, dorbytes
, dowbytes
,
638 iseof
, iseoln
, iserror
) ;
639 dev
:= InitChanDev(term
, did
, gen
)