]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/m2/gm2-libs-iso/TermFile.mod
Update copyright years.
[thirdparty/gcc.git] / gcc / m2 / gm2-libs-iso / TermFile.mod
CommitLineData
1eee94d3
GM
1(* TermFile.mod implement the ISO TermFile specification.
2
83ffe9cd 3Copyright (C) 2009-2023 Free Software Foundation, Inc.
1eee94d3
GM
4Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
5
6This file is part of GNU Modula-2.
7
8GNU Modula-2 is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 3, or (at your option)
11any later version.
12
13GNU Modula-2 is distributed in the hope that it will be useful, but
14WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16General Public License for more details.
17
18Under Section 7 of GPL version 3, you are granted additional
19permissions described in the GCC Runtime Library Exception, version
203.1, as published by the Free Software Foundation.
21
22You should have received a copy of the GNU General Public License and
23a copy of the GCC Runtime Library Exception along with this program;
24see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25<http://www.gnu.org/licenses/>. *)
26
27IMPLEMENTATION MODULE TermFile ;
28
29
30FROM ASCII IMPORT nul, lf, cr ;
31FROM ChanConsts IMPORT ChanFlags ;
32FROM RTio IMPORT GetDeviceId ;
33FROM RTgenif IMPORT GenDevIF, InitGenDevIF ;
34FROM RTdata IMPORT ModuleId, MakeModuleId, InitData, GetData, KillData ;
35FROM IOChan IMPORT ChanExceptions, InvalidChan, CurrentFlags ;
36FROM IOConsts IMPORT ReadResults ;
37FROM Strings IMPORT Assign ;
38
39FROM IOLink IMPORT DeviceId, DeviceTable, DeviceTablePtr, DeviceTablePtrValue, IsDevice,
40 AllocateDeviceId, RAISEdevException, MakeChan, UnMakeChan ;
41
42FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
43FROM Strings IMPORT Append ;
44
45
46FROM SYSTEM IMPORT ADDRESS, ADR, LOC ;
47FROM errno IMPORT geterrno ;
48FROM ErrnoCategory IMPORT GetOpenResults ;
49
50FROM RTgen IMPORT ChanDev, DeviceType, InitChanDev,
51 doLook, doSkip, doSkipLook, doWriteLn,
52 doReadText, doWriteText, doReadLocs, doWriteLocs,
53 checkErrno ;
54
55FROM DynamicStrings IMPORT String, InitStringCharStar, CopyOut,
56 KillString ;
57
58FROM termios IMPORT TERMIOS, InitTermios, KillTermios, tcgetattr,
59 tcsetattr, cfmakeraw, tcsnow ;
60
61
62IMPORT libc ;
63
64
65CONST
66 O_RDONLY = 0 ;
67 O_WRONLY = 1 ;
68
69TYPE
70 PtrToLoc = POINTER TO LOC ;
71 TermInfo = POINTER TO RECORD
72 fd : INTEGER ;
73 pushed : CHAR ;
74 pushBack: BOOLEAN ;
75 old, new: TERMIOS ;
76 END ;
77
78VAR
79 mid: ModuleId ;
80 did: DeviceId ;
81 dev: ChanDev ;
82
83
84(*
85 InitTermInfo - creates a new TermInfo and initializes fields,
86 fd, and, pushed.
87*)
88
89PROCEDURE InitTermInfo (fd: INTEGER) : TermInfo ;
90VAR
91 t: TermInfo ;
92BEGIN
93 NEW(t) ;
94 t^.fd := fd ;
95 t^.pushBack := FALSE ;
96 t^.new := InitTermios() ;
97 t^.old := InitTermios() ;
98 RETURN( t )
99END InitTermInfo ;
100
101
102(*
103 KillTermInfo - deallocates memory associated with, t.
104*)
105
106PROCEDURE KillTermInfo (t: TermInfo) : TermInfo ;
107BEGIN
108 WITH t^ DO
109 new := KillTermios(new) ;
110 old := KillTermios(old)
111 END ;
112 DISPOSE(t) ;
113 RETURN( NIL )
114END KillTermInfo ;
115
116
117(*
118 getFd - return the file descriptor associated with, t.
119*)
120
121PROCEDURE getFd (t: TermInfo) : INTEGER ;
122BEGIN
123 RETURN( t^.fd )
124END getFd ;
125
126
127(*
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
131 character.
132*)
133
134PROCEDURE getPushBackChar (t: TermInfo; VAR ch: CHAR) : BOOLEAN ;
135BEGIN
136 WITH t^ DO
137 IF pushBack
138 THEN
139 ch := pushed ;
140 pushBack := FALSE ;
141 RETURN( TRUE )
142 ELSE
143 RETURN( FALSE )
144 END
145 END
146END getPushBackChar ;
147
148
149(*
150 setPushBackChar - attempts to push back, ch. Only one character
151 may be pushed back consecutively.
152*)
153
154PROCEDURE setPushBackChar (t: TermInfo; ch: CHAR) : BOOLEAN ;
155BEGIN
156 WITH t^ DO
157 IF pushBack
158 THEN
159 RETURN( FALSE )
160 ELSE
161 pushed := ch ;
162 pushBack := TRUE ;
163 RETURN( TRUE )
164 END
165 END
166END setPushBackChar ;
167
168
169PROCEDURE look (d: DeviceTablePtr;
170 VAR ch: CHAR; VAR r: ReadResults) ;
171BEGIN
172 doLook(dev, d, ch, r)
173END look ;
174
175
176PROCEDURE skip (d: DeviceTablePtr) ;
177BEGIN
178 doSkip(dev, d)
179END skip ;
180
181
182PROCEDURE skiplook (d: DeviceTablePtr;
183 VAR ch: CHAR; VAR r: ReadResults) ;
184BEGIN
185 doSkipLook(dev, d, ch, r)
186END skiplook ;
187
188
189PROCEDURE lnwrite (d: DeviceTablePtr) ;
190BEGIN
191 doWriteLn(dev, d)
192END lnwrite ;
193
194
195PROCEDURE textread (d: DeviceTablePtr;
196 to: ADDRESS;
197 maxChars: CARDINAL;
198 VAR charsRead: CARDINAL) ;
199BEGIN
200 doReadText(dev, d, to, maxChars, charsRead)
201END textread ;
202
203
204PROCEDURE textwrite (d: DeviceTablePtr;
205 from: ADDRESS;
206 charsToWrite: CARDINAL);
207BEGIN
208 doWriteText(dev, d, from, charsToWrite)
209END textwrite ;
210
211
212PROCEDURE rawread (d: DeviceTablePtr;
213 to: ADDRESS;
214 maxLocs: CARDINAL;
215 VAR locsRead: CARDINAL) ;
216BEGIN
217 doReadLocs(dev, d, to, maxLocs, locsRead)
218END rawread ;
219
220
221PROCEDURE rawwrite (d: DeviceTablePtr;
222 from: ADDRESS;
223 locsToWrite: CARDINAL) ;
224BEGIN
225 doWriteLocs(dev, d, from, locsToWrite)
226END rawwrite ;
227
228
229(*
230 doreadchar - returns a CHAR from the file associated with, g.
231*)
232
233PROCEDURE doreadchar (g: GenDevIF; d: DeviceTablePtr) : CHAR ;
234VAR
235 i : INTEGER ;
236 fd: INTEGER ;
237 t : TermInfo ;
238 ch: CHAR ;
239BEGIN
240 t := GetData(d, mid) ;
241 WITH d^ DO
242 fd := getFd(t) ;
243 IF NOT getPushBackChar(t, ch)
244 THEN
245 REPEAT
246 i := libc.read(fd, ADR(ch), SIZE(ch))
247 UNTIL i#0 ;
248 IF i<0
249 THEN
250 errNum := geterrno()
251 END
252 END ;
253 RETURN( ch )
254 END
255END doreadchar ;
256
257
258(*
259 dounreadchar - pushes a CHAR back onto the file associated with, g.
260*)
261
262PROCEDURE dounreadchar (g: GenDevIF; d: DeviceTablePtr; ch: CHAR) : CHAR ;
263VAR
264 fd: INTEGER ;
265 t : TermInfo ;
266BEGIN
267 t := GetData(d, mid) ;
268 WITH d^ DO
269 fd := getFd(t) ;
270 IF NOT setPushBackChar(t, ch)
271 THEN
272 RAISEdevException(cid, did, notAvailable,
273 'TermFile.dounreadchar: cannot push back more than one character consecutively')
274 END ;
275 RETURN( ch )
276 END
277END dounreadchar ;
278
279
280(*
281 dogeterrno - returns the errno relating to the generic device.
282*)
283
284PROCEDURE dogeterrno (g: GenDevIF; d: DeviceTablePtr) : INTEGER ;
285BEGIN
286 RETURN geterrno()
287END dogeterrno ;
288
289
290(*
291 dorbytes - reads upto, max, bytes setting, actual, and
292 returning FALSE if an error (not due to eof)
293 occurred.
294*)
295
296PROCEDURE dorbytes (g: GenDevIF; d: DeviceTablePtr;
297 to: ADDRESS;
298 max: CARDINAL;
299 VAR actual: CARDINAL) : BOOLEAN ;
300VAR
301 fd: INTEGER ;
302 t : TermInfo ;
303 p : PtrToLoc ;
304 i : INTEGER ;
305BEGIN
306 t := GetData(d, mid) ;
307 WITH d^ DO
308 IF max>0
309 THEN
310 p := to ;
311 IF getPushBackChar(t, p^)
312 THEN
313 actual := 1 ;
314 RETURN( TRUE )
315 END ;
316 fd := getFd(t) ;
317 i := libc.read(fd, p, max) ;
318 IF i>=0
319 THEN
320 actual := i ;
321 RETURN( TRUE )
322 ELSE
323 errNum := geterrno() ;
324 actual := 0 ;
325 RETURN( FALSE )
326 END
327 END
328 END
329END dorbytes ;
330
331
332(*
333 dowbytes -
334*)
335
336PROCEDURE dowbytes (g: GenDevIF; d: DeviceTablePtr;
337 from: ADDRESS;
338 nBytes: CARDINAL;
339 VAR actual: CARDINAL) : BOOLEAN ;
340VAR
341 fd: INTEGER ;
342 t : TermInfo ;
343 i : INTEGER ;
344BEGIN
345 t := GetData(d, mid) ;
346 WITH d^ DO
347 fd := getFd(t) ;
348 i := libc.write(fd, from, nBytes) ;
349 IF i>=0
350 THEN
351 actual := i ;
352 RETURN( TRUE )
353 ELSE
354 errNum := geterrno() ;
355 actual := 0 ;
356 RETURN( FALSE )
357 END
358 END
359END dowbytes ;
360
361
362(*
363 dowriteln - attempt to write an end of line marker to the
364 file and returns TRUE if successful.
365*)
366
367PROCEDURE dowriteln (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ;
368VAR
369 a: ARRAY [0..1] OF CHAR ;
370 i: CARDINAL ;
371BEGIN
372 a[0] := cr ;
373 a[1] := lf ;
374 RETURN( dowbytes(g, d, ADR(a), SIZE(a), i) AND (i=SIZE(a)) )
375END dowriteln ;
376
377
378(*
379 iseof - returns TRUE if end of file is seen.
380*)
381
382PROCEDURE iseof (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ;
383VAR
384 ch: CHAR ;
385BEGIN
386 ch := doreadchar(g, d) ;
387 WITH d^ DO
388 IF errNum=0
389 THEN
390 ch := dounreadchar(g, d, ch) ;
391 RETURN( FALSE )
392 ELSE
393 RETURN( TRUE )
394 END
395 END
396END iseof ;
397
398
399(*
400 iseoln - returns TRUE if end of line is seen.
401*)
402
403PROCEDURE iseoln (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ;
404VAR
405 ch: CHAR ;
406BEGIN
407 ch := doreadchar(g, d) ;
408 WITH d^ DO
409 IF errNum=0
410 THEN
411 ch := dounreadchar(g, d, ch) ;
412 RETURN( ch=lf )
413 ELSE
414 RETURN( FALSE )
415 END
416 END
417END iseoln ;
418
419
420(*
421 iserror - returns TRUE if an error was seen on the device.
422*)
423
424PROCEDURE iserror (g: GenDevIF; d: DeviceTablePtr) : BOOLEAN ;
425BEGIN
426 RETURN( d^.errNum#0 )
427END iserror ;
428
429
430(*
431 getname - assigns, a, to the device name of the terminal.
432*)
433
434PROCEDURE getname (d: DeviceTablePtr;
435 VAR a: ARRAY OF CHAR) ;
436VAR
437 s: String ;
438BEGIN
439 s := InitStringCharStar(libc.ttyname(0)) ;
440 CopyOut(a, s) ;
441 s := KillString(s)
442END getname ;
443
444
445(*
446 freeData - disposes of, t.
447*)
448
449PROCEDURE freeData (t: TermInfo) ;
450BEGIN
451 t := KillTermInfo(t)
452END freeData ;
453
454
455(*
456 handlefree -
457*)
458
459PROCEDURE handlefree (d: DeviceTablePtr) ;
460VAR
461 t : TermInfo ;
462 fd: INTEGER ;
463 i : INTEGER ;
464BEGIN
465 t := GetData(d, mid) ;
466 fd := getFd(t) ;
467 i := libc.close(fd) ;
468 checkErrno(dev, d) ;
469 KillData(d, mid)
470END handlefree ;
471
472
473(*
474 termOpen - attempts to open up the terminal device. It fills
475 in any implied flags and returns a result depending
476 whether the open was successful.
477*)
478
479PROCEDURE termOpen (t: TermInfo; VAR flagset: FlagSet; VAR e: INTEGER) : OpenResults ;
480VAR
481 i: INTEGER ;
482BEGIN
483 WITH t^ DO
484 IF NOT (rawFlag IN flagset)
485 THEN
486 INCL(flagset, textFlag)
487 END ;
488 IF NOT (echoFlag IN flagset)
489 THEN
490 INCL(flagset, interactiveFlag)
491 END ;
492 IF NOT (writeFlag IN flagset)
493 THEN
494 INCL(flagset, readFlag)
495 END ;
496 IF writeFlag IN flagset
497 THEN
498 fd := libc.open(ADR("/dev/tty"), O_WRONLY, 0600B)
499 ELSE
500 fd := libc.open(ADR("/dev/tty"), O_RDONLY)
501 END ;
502 IF tcgetattr(fd, new)=0
503 THEN
504 END ;
505 IF tcgetattr(fd, old)=0
506 THEN
507 IF rawFlag IN flagset
508 THEN
509 cfmakeraw(new)
510 END ;
511 IF tcsetattr(fd, tcsnow(), new)=0
512 THEN
513 END
514 END ;
515 e := geterrno() ;
516 RETURN( GetOpenResults(e) )
517 END
518END termOpen ;
519
520
521(*
522 RestoreTerminalSettings -
523*)
524
525PROCEDURE RestoreTerminalSettings (cid: ChanId) ;
526VAR
527 d: DeviceTablePtr ;
528 t: TermInfo ;
529 e: INTEGER ;
530BEGIN
531 d := DeviceTablePtrValue(cid, did) ;
532 t := GetData(d, mid) ;
533 WITH t^ DO
534 IF tcsetattr(fd, tcsnow(), old)=0
535 THEN
536 END
537 END
538END RestoreTerminalSettings ;
539
540
541(*
542 Open - attempts to obtain and open a channel connected to
543 the terminal. Without the raw flag, text is implied.
544 Without the echo flag, line mode is requested,
545 otherwise single character mode is requested.
546 If successful, assigns to cid the identity of
547 the opened channel, and assigns the value opened to res.
548 If a channel cannot be opened as required, the value of
549 res indicates the reason, and cid identifies the
550 invalid channel.
551*)
552
553PROCEDURE Open (VAR cid: ChanId;
554 flagset: FlagSet; VAR res: OpenResults) ;
555VAR
556 d: DeviceTablePtr ;
557 t: TermInfo ;
558 e: INTEGER ;
559BEGIN
560 MakeChan(did, cid) ; (* create new channel *)
561 d := DeviceTablePtrValue(cid, did) ;
562 t := InitTermInfo(-1) ;
563 res := termOpen(t, flagset, e) ;
564 InitData(d, mid, t, freeData) ; (* attach memory to device and module *)
565 WITH d^ DO
566 flags := flagset ;
567 errNum := e ;
568 doLook := look ;
569 doSkip := skip ;
570 doSkipLook := skiplook ;
571 doLnWrite := lnwrite ;
572 doTextRead := textread ;
573 doTextWrite := textwrite ;
574 doRawRead := rawread ;
575 doRawWrite := rawwrite ;
576 doGetName := getname ;
577 doFree := handlefree
578 END
579END Open ;
580
581
582(*
583 IsTermFile - tests if the channel identified by cid is open to
584 the terminal.
585*)
586
587PROCEDURE IsTermFile (cid: ChanId) : BOOLEAN ;
588BEGIN
589 RETURN( (cid # NIL) AND (InvalidChan() # cid) AND
590 (IsDevice(cid, did)) AND
591 ((readFlag IN CurrentFlags(cid)) OR
592 (writeFlag IN CurrentFlags(cid))) )
593END IsTermFile ;
594
595
596(*
597 Close - if the channel identified by cid is not open to the
598 terminal, the exception wrongDevice is raised; otherwise
599 closes the channel, and assigns the value identifying
600 the invalid channel to cid.
601*)
602
603PROCEDURE Close (VAR cid: ChanId) ;
604BEGIN
605 IF IsTermFile(cid)
606 THEN
607 RestoreTerminalSettings(cid) ;
608 UnMakeChan(did, cid) ;
609 cid := InvalidChan()
610 ELSE
611 RAISEdevException(cid, did, wrongDevice,
612 'TermFile.' + __FUNCTION__ +
613 ': channel is opened to the terminal')
614 END
615END Close ;
616
617
618(*
619 Init -
620*)
621
622PROCEDURE Init ;
623VAR
624 gen: GenDevIF ;
625BEGIN
626 MakeModuleId(mid) ;
627 AllocateDeviceId(did) ;
628 gen := InitGenDevIF(did,
629 doreadchar, dounreadchar,
630 dogeterrno, dorbytes, dowbytes,
631 dowriteln,
632 iseof, iseoln, iserror) ;
633 dev := InitChanDev(term, did, gen)
634END Init ;
635
636
637BEGIN
638 Init
639END TermFile.