1 /**********************************************************
2 * tty/tio routines.
3 *
4 * Most part of this code is taken from mgetty package
5 * copyrighted by 1993 Gert Doering.
6 *
7 * I don't intend to make mgetty competent software based
8 * on the mgetty's source but just to use the well
9 * working code.
10 **********************************************************/
11
12 /*
13 * $Id: tty.c,v 1.27 2005/08/16 20:23:23 mitry Exp $
14 *
15 * $Log: tty.c,v $
16 * Revision 1.27 2005/08/16 20:23:23 mitry
17 * Pre 0.57.1: minor changes
18 *
19 * Revision 1.26 2005/08/16 10:32:19 mitry
20 * Fixed broken calls of loginscript
21 *
22 * Revision 1.25 2005/06/10 20:57:28 mitry
23 * Changed tty_bufblock()
24 *
25 * Revision 1.24 2005/05/17 19:51:11 mitry
26 * Code cleaning
27 *
28 * Revision 1.23 2005/05/17 18:17:43 mitry
29 * Removed system basename() usage.
30 * Added qbasename() implementation.
31 *
32 * Revision 1.22 2005/05/16 20:33:46 mitry
33 * Code cleaning
34 *
35 * Revision 1.21 2005/05/06 20:53:06 mitry
36 * Changed misc code
37 *
38 * Revision 1.20 2005/04/14 14:48:11 mitry
39 * Misc changes
40 *
41 * Revision 1.19 2005/04/08 18:15:05 mitry
42 * More proper sighup handling
43 *
44 * Revision 1.18 2005/04/04 19:43:56 mitry
45 * Added timeout arg to BUFFLUSH() - tty_bufflush()
46 *
47 * Revision 1.17 2005/03/31 20:39:20 mitry
48 * Added support for HUP_XXX and removed old code
49 *
50 * Revision 1.16 2005/03/29 20:41:27 mitry
51 * Restore saved tio settings before closing port
52 *
53 * Revision 1.15 2005/03/28 17:02:53 mitry
54 * Pre non-blocking i/o update. Mostly non working.
55 *
56 * Revision 1.14 2005/02/25 16:41:55 mitry
57 * Adding proper tio calls in progress
58 * Changed some code
59 *
60 * Revision 1.13 2005/02/23 21:56:26 mitry
61 * Changed tty_carrier() code. To be rewritten
62 *
63 * Revision 1.12 2005/02/22 16:15:39 mitry
64 * Code rewrite
65 *
66 * Revision 1.11 2005/02/21 18:14:15 mitry
67 * One more tty_hangedup rename
68 *
69 * Revision 1.10 2005/02/21 16:33:42 mitry
70 * Changed tty_hangedup to tty_online
71 *
72 * Revision 1.9 2005/02/18 20:39:34 mitry
73 * tty_block() commented out
74 * tty_close() now has parameter (may be temporarily) and rewritten
75 * Added debug messages
76 *
77 */
78
79 #include "headers.h"
80 #ifdef HAVE_SYS_IOCTL_H
81 #include <sys/ioctl.h>
82 #endif
83 #include "tty.h"
84
85
86 #ifdef POSIX_TERMIOS
87 static char tio_compilation_type[]="@(#)tty.c compiled with POSIX_TERMIOS";
88 #endif
89 #ifdef SYSV_TERMIO
90 static char tio_compilation_type[]="@(#)tty.c compiled with SYSV_TERMIO";
91 #endif
92 #ifdef BSD_SGTTY
93 static char tio_compilation_type[]="@(#)tty.c compiled with BSD_SGTTY";
94 #endif
95
96
97 #ifdef BSD
98 # ifndef IUCLC
99 # define IUCLC 0
100 # endif
101 # ifndef TAB3
102 # ifdef NeXT
103 # define TAB3 XTABS
104 # else
105 # define TAB3 OXTABS
106 # endif /* !NeXT */
107 # endif
108 #endif
109
110
111 #define DEBUG_SLEEP 0
112
113 #define IFGOTHUP \
114 if ( tty_gothup || (!is_ip && tty_online && !tty_dcd( tty_fd ))) \
115 { tty_status = TTY_HANGUP; return RCDO; }
116
117 #define IFLESSZERO \
118 if ( rc < 0 ) { \
119 IFGOTHUP; \
120 else return ERROR; \
121 }
122
123
124 char *tty_errs[] = {
125 "Ok", /* ME_OK 0 */
126 "tcget/setattr error", /* ME_ATTRS 1 */
127 "bad speed", /* ME_SPEED 2 */
128 "open error", /* ME_OPEN 3 */
129 "read error", /* ME_READ 4 */
130 "write error", /* ME_WRITE 5 */
131 "timeout", /* ME_TIMEOUT 6 */
132 "close error", /* ME_CLOSE 7 */
133 "can't lock port", /* ME_CANTLOCK 8 */
134 "can't set/get flags", /* ME_FLAGS 9 */
135 "not a tty", /* ME_NOTATT 10 */
136 "no DSR/CTS signals" /* ME_NODSR 11 */
137 };
138
139
140 /* baud rate table */
141 static struct speedtab {
142 #ifdef POSIX_TERMIOS
143 speed_t cbaud;
144 #else
145 unsigned short cbaud; /* baud rate, e.g. B9600 */
146 #endif
147 int nspeed; /* speed in numeric format */
148 char *speed; /* speed in display format */
149 } speedtab[] = {
150 { B50, 50, "50" },
151 { B75, 75, "75" },
152 { B110, 110, "110" },
153 { B134, 134, "134" },
154 { B150, 150, "150" },
155 { B200, 200, "200" },
156 { B300, 300, "300" },
157 { B600, 600, "600" },
158 #ifdef B900
159 { B900, 900, "900" },
160 #endif
161 { B1200, 1200, "1200" },
162 { B1800, 1800, "1800" },
163 { B2400, 2400, "2400" },
164 #ifdef B3600
165 { B3600, 3600, "3600" },
166 #endif
167 { B4800, 4800, "4800" },
168 #ifdef B7200
169 { B7200, 7200, "7200" },
170 #endif
171 { B9600, 9600, "9600" },
172 #ifdef B14400
173 { B14400, 14400, "14400" },
174 #endif
175 #ifdef B19200
176 { B19200, 19200, "19200" },
177 #endif /* B19200 */
178 #ifdef B28800
179 { B28800, 28800, "28800" },
180 #endif
181 #ifdef B38400
182 { B38400, 38400, "38400" },
183 #endif /* B38400 */
184 #ifdef EXTA
185 { EXTA, 19200, "EXTA" },
186 #endif
187 #ifdef EXTB
188 { EXTB, 38400, "EXTB" },
189 #endif
190 #ifdef B57600
191 { B57600, 57600, "57600" },
192 #endif
193 #ifdef B76800
194 { B76800, 76800, "76800" },
195 #endif
196 #ifdef B115200
197 { B115200,115200,"115200"},
198 #endif
199 #ifdef B230400
200 { B230400,230400,"230400"},
201 #endif
202 #ifdef B460800
203 { B460800,460800,"460800"},
204 #endif
205 { 0, 0, "" }
206 };
207
208
209 TIO tty_stio;
210
211 #if 0
212 #ifdef HYDRA8K16K
213 #define RX_BUF_SIZE (16384 + 1024)
214 #define TX_BUF_SIZE (16384 + 1024)
215 #else
216 #define RX_BUF_SIZE (4096 + 1024)
217 #define TX_BUF_SIZE (4096 + 1024)
218 #endif
219 #endif /* 0 */
220
221 #define RX_BUF_SIZE (0x8100)
222 #define TX_BUF_SIZE (0x8100)
223
224 static unsigned char tty_rx_buf[RX_BUF_SIZE];
225 static unsigned char tty_tx_buf[TX_BUF_SIZE];
226
227 /* tty_rx_left - number of bytes in receive buffer
228 * tty_rx_ptr - pointer to next byte in receive buffer
229 * tty_tx_ptr - pointer to next byte to send out in transmit buffer
230 * tty_tx_free - number of byte left in transmit buffer
231 *
232 * | <- tty_rx_left -> |
233 * +---------------------------+*********************+--------------------+
234 * |read data// tty_rx_ptr <- | new data | | RX_BUF_SIZE
235 * +---------------------------+*********************+--------------------+
236 *
237 * | <- tty_tx_free -> |
238 * +---------------------------+*********************+--------------------+
239 * |sent data// tty_tx_ptr -> | unsent data | <- new data | TX_BUF_SIZE
240 * +---------------------------+*********************+--------------------+
241 */
242 static int tty_rx_ptr = 0;
243 static int tty_rx_left = 0;
244 static int tty_tx_ptr = 0;
245 static int tty_tx_free = TX_BUF_SIZE;
246
247 int tty_fd = -1;
248 int tty_error = 0;
249 int tty_status = TTY_SUCCESS;
250
251
252 #define FDS(f) ( (f) ? ( *(f) ? "true" : "false" ) : "not set" )
253
254 /*
255 * Checks out whether or not stdin and/or stdout are ready to receive/send data
256 *
257 * Parameters:
258 * rd - if not NULL & TRUE - check read (stdin) fd
259 * wd - if not NULL & TRUE - check write (stdout) fd
260 * tval - a pointer to struct timeval to wait for event. If tval:
261 * NULL - wait for at least one fd is ready or forever
262 * {0,0} - do not wait at all, just return fds state
263 * other - wait for specified time
264 *
265 * Return values: number of fds are ready or -1 on error (errno in tty_error).
266 * rd - true if stdin is ready
267 * wd - true if stdout is ready
268 */
tty_select(boolean * rd,boolean * wd,struct timeval * tval)269 int tty_select(boolean *rd, boolean *wd, struct timeval *tval)
270 {
271 fd_set rfd, wfd;
272 int rc;
273
274 DEBUG(('T',2,"tty_select"));
275
276 FD_ZERO( &rfd );
277 FD_ZERO( &wfd );
278 if ( rd && *rd ) {
279 FD_SET( tty_fd, &rfd );
280 *rd = FALSE;
281 }
282 if ( wd && *wd ) {
283 FD_SET( tty_fd, &wfd );
284 *wd = FALSE;
285 }
286
287 tty_error = 0;
288 rc = select( tty_fd + 1, &rfd, &wfd, NULL, ( tval ? tval : NULL ));
289
290 tty_error = errno;
291 tty_status = TTY_SUCCESS;
292 if ( rc < 0 ) {
293 if ( EWBOEA() ) {
294 tty_status = TTY_TIMEOUT;
295 } else if ( errno == EINTR ) {
296 tty_status = ( tty_online && tty_gothup ) ? TTY_HANGUP : TTY_TIMEOUT;
297 } else if ( errno == EPIPE ) {
298 tty_gothup = HUP_LINE;
299 tty_status = TTY_HANGUP;
300 } else
301 tty_status = TTY_ERROR;
302 } else if ( rc == 0 )
303 tty_status = TTY_TIMEOUT;
304 else {
305 if ( rd && FD_ISSET( tty_fd, &rfd ))
306 *rd = TRUE;
307 if ( wd && FD_ISSET( tty_fd, &wfd ))
308 *wd = TRUE;
309 }
310
311 DEBUG(('T',2,"tty_select: fd=%d rc=%i (rd=%s, wd=%s)", tty_fd, rc, FDS( rd ), FDS( wd )));
312
313 return rc;
314 }
315
316
317 /*
318 * Returns whether the rx_buf has data.
319 */
tty_hasinbuf(void)320 int tty_hasinbuf(void)
321 {
322 return ( tty_rx_left > 0 );
323 }
324
325
326 /*
327 * Returns whether the in stream has data.
328 */
tty_hasdata(int sec,int usec)329 int tty_hasdata(int sec, int usec)
330 {
331 boolean rd = TRUE;
332 struct timeval tv;
333
334 DEBUG(('T',5,"tty_hasdata"));
335
336 if ( tty_hasinbuf() )
337 return 1;
338
339 tv.tv_sec = sec;
340 tv.tv_usec = usec;
341 return ( tty_select( &rd, NULL, &tv ) > 0 && rd );
342 }
343
344
tty_hasdata_timed(int * timeout)345 int tty_hasdata_timed(int *timeout)
346 {
347 int rc;
348 time_t t = time( NULL );
349
350 rc = tty_hasdata( *timeout, 0 );
351 *timeout -= (time( NULL ) - t);
352
353 return rc;
354 }
355
356
357
358 /*
359 * Attempts to write nbytes of data from the buffer pointed to by buf to stdout.
360 *
361 * Return values:
362 * Upon successful completion the number of bytes which were written is
363 * returned. Otherwise a value < 0 is returned and the tty_error and tty_status
364 * are set to indicate the error.
365 */
tty_write(const void * buf,size_t nbytes)366 int tty_write(const void *buf, size_t nbytes)
367 {
368 int rc;
369
370 #ifdef NEED_DEBUG
371 byte *bbuf = (byte *) buf;
372 int i;
373 #endif
374
375 DEBUG(('T',5,"tty_write"));
376
377 IFGOTHUP;
378
379 if ( nbytes == 0 )
380 return 0;
381
382 rc = write( tty_fd, buf, nbytes );
383
384 tty_error = errno;
385 IFGOTHUP;
386
387 if ( rc < 0 ) {
388 if ( EWBOEA() ) {
389 tty_status = TTY_TIMEOUT;
390 } else if ( errno == EINTR ) {
391 if ( tty_online && tty_gothup )
392 tty_status = TTY_HANGUP;
393 else
394 tty_status = TTY_TIMEOUT;
395 } else if ( errno == EPIPE ) {
396 tty_gothup = HUP_LINE;
397 tty_status = TTY_HANGUP;
398 } else
399 tty_status = TTY_ERROR;
400 } else if ( rc == 0 ) {
401 tty_status = TTY_ERROR;
402 } else /* rc > 0 */
403 tty_status = TTY_SUCCESS;
404
405 DEBUG(('T',6,"tty_write: rc = %d", rc));
406
407 #ifdef NEED_DEBUG
408 if ( rc > 0 )
409 for( i = 0; i < rc; i++ )
410 DEBUG(('T',9,"tty_write: '%c' (%d)", C0(bbuf[i]), bbuf[i]));
411 #endif
412
413 return ( tty_status == TTY_SUCCESS ? rc : tty_status );
414 }
415
416
417 /*
418 * Attempts to read nbytes of data from the stdin into the buffer pointed to
419 * by buf.
420 *
421 * Return values:
422 * Upon successful completion the number of bytes which were read is
423 * returned. Otherwise a value < 0 is returned and the tty_error and tty_status
424 * are set to indicate the error.
425 */
tty_read(void * buf,size_t nbytes)426 int tty_read(void *buf, size_t nbytes)
427 {
428 int rc;
429
430 #ifdef NEED_DEBUG
431 byte *bbuf = (byte *) buf;
432 int i;
433 #endif
434
435 DEBUG(('T',5,"tty_read"));
436
437 IFGOTHUP;
438
439 rc = read( tty_fd, buf, nbytes );
440 IFGOTHUP;
441
442 tty_error = errno;
443 if ( rc < 0 ) {
444 if ( EWBOEA() ) {
445 tty_status = TTY_TIMEOUT;
446 } else if ( errno == EINTR ) {
447 if ( tty_online && tty_gothup )
448 tty_status = TTY_HANGUP;
449 else
450 tty_status = TTY_TIMEOUT;
451 } else if ( errno == EPIPE ) {
452 tty_gothup = HUP_LINE;
453 tty_status = TTY_HANGUP;
454 } else
455 tty_status = TTY_ERROR;
456 } else if ( rc == 0 ) {
457 tty_status = TTY_ERROR;
458 } else /* rc > 0 */
459 tty_status = TTY_SUCCESS;
460
461 DEBUG(('T',6,"tty_read: rc = %d", rc));
462
463 #ifdef NEED_DEBUG
464 if ( rc > 0 )
465 for( i = 0; i < rc; i++ )
466 DEBUG(('T',9,"tty_read: '%c' (%d)", C0(bbuf[i]), bbuf[i]));
467 #endif
468
469 return ( tty_status == TTY_SUCCESS ? rc : tty_status );
470 }
471
472
tty_sighup(int sig)473 RETSIGTYPE tty_sighup(int sig)
474 {
475 DEBUG(('T',1,"tty_sighup: got SIG%s signal, %d", sigs[sig], tty_gothup));
476
477 getevt();
478 DEBUG(('T',2,"tty_sighup: %d", tty_gothup));
479 if (( is_ip && sig == SIGPIPE ) || ( !is_ip && !tty_dcd( tty_fd )))
480 tty_gothup = HUP_LINE;
481 else
482 tty_gothup = HUP_OPERATOR;
483 }
484
485
baseport(const char * p)486 char *baseport(const char *p)
487 {
488 char *q;
489 static char pn[MAX_PATH + 5];
490
491 q = qbasename( p );
492 if ( !q || !*q )
493 return NULL;
494
495 xstrcpy( pn, q, MAX_PATH );
496 if (( q = strrchr( pn, ':' )))
497 *q = 0;
498 return pn;
499 }
500
501
tty_isfree(const char * port,const char * nodial)502 int tty_isfree(const char *port, const char *nodial)
503 {
504 int pid = 0;
505 FILE *f;
506 struct stat s;
507 char lckname[MAX_PATH + 5];
508
509 if ( !port )
510 return 0;
511
512 if ( nodial ) {
513 snprintf( lckname, MAX_PATH, "%s.%s", nodial, port );
514 if ( !stat( lckname, &s ))
515 return 0;
516 }
517
518 LCK_NAME(lckname, port);
519 if (( f = fopen( lckname, "r" ))) {
520 fscanf( f, "%d", &pid );
521 fclose( f );
522 if ( pid && kill( pid, 0 ) && ( errno == ESRCH )) {
523 lunlink( lckname );
524 return 1;
525 }
526 return 0;
527 }
528 return 1;
529 }
530
531
tty_findport(slist_t * ports,const char * nodial)532 char *tty_findport(slist_t *ports, const char *nodial)
533 {
534 for(; ports; ports = ports->next )
535 if( tty_isfree( baseport( ports->str ), nodial ))
536 return ports->str;
537 return NULL;
538 }
539
540
tty_lock(const char * port)541 int tty_lock(const char *port)
542 {
543 int rc = -1;
544 char lckname[MAX_PATH + 5];
545 const char *p;
546
547 DEBUG(('M',1,"tty_lock"));
548 if ( !( p = strrchr( port, '/' )))
549 p = port;
550 else
551 p++;
552
553 LCK_NAME(lckname, p);
554 rc = lockpid( lckname );
555 return rc ? 0 : -1;
556 }
557
558
tty_unlock(const char * port)559 void tty_unlock(const char *port)
560 {
561 long pid;
562 char lckname[MAX_PATH + 5];
563 const char *p;
564 FILE *f;
565
566 DEBUG(('M',1,"tty_unlock"));
567 if ( !( p = strrchr( port, '/' )))
568 p = port;
569 else
570 p++;
571
572 LCK_NAME(lckname, p);
573 if (( f = fopen( lckname, "r" ))) {
574 fscanf( f, "%ld", &pid );
575 fclose( f );
576 }
577
578 if ( pid == getpid())
579 lunlink( lckname );
580 }
581
582
tty_close(void)583 int tty_close(void)
584 {
585 if ( !tty_port )
586 return ME_CLOSE;
587
588 DEBUG(('M',2,"tty_close"));
589
590 tio_flush_queue( tty_fd, TIO_Q_BOTH );
591 tio_set( tty_fd, &tty_stio );
592
593 (void) close( tty_fd );
594
595 tty_unlock( tty_port );
596 xfree( tty_port );
597 tty_online = FALSE;
598
599 return ME_OK;
600 }
601
602
tty_local(TIO * tio,int local)603 int tty_local(TIO *tio, int local)
604 {
605 signal( SIGHUP, local ? SIG_IGN : tty_sighup );
606 signal( SIGPIPE, local ? SIG_IGN : tty_sighup );
607
608 if ( !isatty( tty_fd ))
609 return ME_NOTATT;
610
611 tio_local_mode( tio, local );
612 tio_set_flow_control( tty_fd, tio, local ? FLOW_NONE : FLOW_HARD );
613
614 if ( local )
615 tty_gothup = FALSE;
616 return 1;
617 }
618
619
tty_bufclear(void)620 void tty_bufclear(void)
621 {
622 tty_tx_ptr = 0;
623 tty_tx_free = TX_BUF_SIZE;
624 }
625
626
tty_bufflush(int tsec)627 int tty_bufflush(int tsec)
628 {
629 int rc = OK, restsize = TX_BUF_SIZE - tty_tx_free - tty_tx_ptr;
630 boolean wd;
631 struct timeval tv;
632 time_t tm;
633
634 tm = timer_set( tsec );
635 while( TX_BUF_SIZE != tty_tx_free ) {
636 wd = true;
637 tv.tv_sec = timer_rest( tm );
638 tv.tv_usec = 0;
639
640 if (( rc = tty_select( NULL, &wd, &tv )) > 0 && wd ) {
641 rc = tty_write( tty_tx_buf + tty_tx_ptr, restsize );
642
643 if ( rc == restsize ) {
644 tty_bufclear();
645 } else if ( rc > 0 ) {
646 tty_tx_ptr += rc;
647 restsize -= rc;
648 } else if ( rc < 0 && tty_status != TTY_TIMEOUT )
649 return ERROR;
650 } else
651 return rc;
652
653 if ( timer_expired( tm ))
654 return ERROR;
655 }
656 return rc;
657 }
658
659
tty_bufblock(const void * data,size_t nbytes)660 int tty_bufblock(const void *data, size_t nbytes)
661 {
662 int rc = OK, txptr = TX_BUF_SIZE - tty_tx_free;
663 char *nptr = (char *)data;
664
665 DEBUG(('T',8,"tty_bufblock: tty_tx_ptr=%d, tty_tx_free=%d, need2buf=%d",
666 tty_tx_ptr,tty_tx_free,nbytes));
667
668 tty_status = TTY_SUCCESS;
669
670 while ( nbytes ) {
671 if ( nbytes > (size_t) tty_tx_free ) {
672 do {
673 tty_bufflush( 5 );
674 if ( tty_status == TTY_SUCCESS ) {
675 size_t n = MIN( (size_t) tty_tx_free, nbytes);
676 memcpy( tty_tx_buf, nptr, n );
677 tty_tx_free -= n;
678 nbytes -= n;
679 nptr += n;
680 }
681 } while ( tty_status != TTY_SUCCESS );
682 } else {
683 memcpy( (void *) (tty_tx_buf + txptr), nptr, nbytes );
684 tty_tx_free -= nbytes;
685 nbytes = 0;
686 }
687 }
688 return rc;
689 }
690
691
tty_bufc(char ch)692 int tty_bufc(char ch)
693 {
694 return tty_bufblock( &ch, 1 );
695 }
696
697
tty_putc(char ch)698 int tty_putc(char ch)
699 {
700 tty_bufblock( &ch, 1 );
701 return tty_bufflush( 5 );
702 }
703
704
tty_getc(int timeout)705 int tty_getc(int timeout)
706 {
707
708 DEBUG(('T',8,"tty_getc: tty_rx_ptr=%d, tty_rx_left=%d",tty_rx_ptr,tty_rx_left));
709 if ( tty_rx_left == 0 ) {
710 int rc = tty_hasdata( timeout, 0 );
711 /*
712 int rc = ( timeout > 0 ) ? tty_hasdata( timeout, 0 ) : 1;
713 */
714
715 IFGOTHUP;
716
717 if ( rc > 0 ) {
718 if (( rc = tty_read( tty_rx_buf, RX_BUF_SIZE )) < 0 ) {
719 IFGOTHUP;
720 return ( EWBOEA()) ? TTY_TIMEOUT : ERROR;
721 } else if ( rc == 0 )
722 return ERROR;
723 tty_rx_ptr = 0;
724 tty_rx_left = rc;
725 } else
726 return ( tty_gothup ? TTY_HANGUP : TTY_TIMEOUT );
727 }
728
729 tty_rx_left--;
730 return tty_rx_buf[tty_rx_ptr++];
731 }
732
733
tty_getc_timed(int * timeout)734 int tty_getc_timed(int *timeout)
735 {
736 int rc;
737 time_t t = time( NULL );
738
739 rc = tty_getc( *timeout );
740 *timeout -= (time( NULL ) - t);
741 return rc;
742 }
743
744
tty_purge(void)745 void tty_purge(void) {
746 DEBUG(('M',3,"tty_purge"));
747
748 tty_rx_ptr = tty_rx_left = 0;
749 if ( isatty( tty_fd ))
750 tio_flush_queue( tty_fd, TIO_Q_IN );
751 }
752
753
tty_purgeout(void)754 void tty_purgeout(void) {
755 DEBUG(('M',3,"tty_purgeout"));
756
757 tty_bufclear();
758 if ( isatty( tty_fd ))
759 tio_flush_queue( tty_fd, TIO_Q_OUT );
760 }
761
762
tty_send_break(void)763 int tty_send_break(void)
764 {
765 #ifdef POSIX_TERMIOS
766 if ( tcsendbreak( tty_fd, 0 ) < 0 ) {
767 DEBUG(('M',3,"tcsendbreak() failed"));
768 return -1;
769 }
770 #endif
771 #ifdef SYSV_TERMIO
772 if ( ioctl( tty_fd, TCSBRK, 0 ) < 0 ) {
773 DEBUG(('M',3,"ioctl( TCSBRK ) failed"));
774 return -1;
775 }
776 #endif
777 #ifdef BSD_SGTTY
778 if ( ioctl( tty_fd, TIOCSBRK, 0 ) < 0 ) {
779 DEBUG(('M',3,"ioctl( TIOCSBRK ) failed"));
780 return -1;
781 }
782 qsleep( 1000 );
783 if ( ioctl( tty_fd, TIOCCBRK, 0 ) < 0 ) {
784 DEBUG(('M',3,"ioctl( TIOCCBRK ) failed"));
785 return -1;
786 }
787 #endif
788
789 return 0;
790 }
791
792
793 /* Return the amount of remaining bytes to be transmitted */
tty_hasout(void)794 int tty_hasout(void)
795 {
796 return (tty_tx_free != TX_BUF_SIZE);
797 }
798
799
800 /*
801 * Returns whether CD line of given tty is on or off
802 */
tty_dcd(int fd)803 int tty_dcd(int fd)
804 {
805 int rs_lines = tio_get_rs232_lines( fd );
806
807 DEBUG(('M',2,"tty_dcd: CD is %s",(rs_lines & TIO_F_DCD ? "On" : "Off")));
808
809 return ( rs_lines & TIO_F_DCD );
810 }
811
812
813 /*
814 * Close all FDs >= a specified value
815 */
fd_close_all(int startfd)816 static void fd_close_all(int startfd)
817 {
818 int fdlimit = sysconf(_SC_OPEN_MAX);
819
820 while( startfd < fdlimit )
821 close( startfd++ );
822 }
823
824
825 /*
826 * Make file descriptor stdin/stdout/stderr
827 */
fd_make_stddev(int fd)828 int fd_make_stddev(int fd)
829 {
830
831 fflush( stdin );
832 fflush( stdout );
833 fflush( stderr );
834
835 if ( fd > 0 ) {
836 (void) close( 0 );
837 if ( dup( fd ) != 0 ) {
838 DEBUG(('T',2,"fd_make_stddev: can't dup(fd=%d) to stdin", fd));
839 return ERROR;
840 }
841 close( fd );
842 }
843
844 (void) close( 1 );
845 if ( dup( 0 ) != 1 ) {
846 DEBUG(('T',2,"fd_make_stddev: can't dup(fd=%d) to stdout", fd));
847 return ERROR;
848 }
849
850 (void) close( 2 );
851 if ( dup( 0 ) != 2 ) {
852 DEBUG(('T',2,"fd_make_stddev: can't dup(0) to stderr"));
853 return ERROR;
854 }
855
856 setbuf( stdin, (char *) NULL );
857 setbuf( stdout, (char *) NULL );
858 setbuf( stderr, (char *) NULL );
859
860 clearerr( stdin );
861 clearerr( stdout );
862 clearerr( stderr );
863
864 return OK;
865 }
866
867
868 /*
869 * Set the O_NONBLOCK flag of desc if value is nonzero,
870 * or clear the flag if value is 0.
871 * Return 0 on success, or -1 on error with errno set.
872 */
fd_set_nonblock(int fd,int mode)873 int fd_set_nonblock(int fd, int mode)
874 {
875 int misc;
876
877 #if defined(FIONBIO)
878
879 /*
880 * Set non-blocking I/O mode on sockets
881 */
882 misc = 1;
883 if ( ioctl( fd, FIONBIO, &misc, sizeof( misc )) == -1 ) {
884 DEBUG(('T',1,"ioctl: can't set %sblocking mode on fd %d: %s",
885 (mode ? "non-" : ""), fd, strerror( errno )));
886 return -1;
887 }
888 #endif
889
890 misc = fcntl( fd, F_GETFL, 0);
891 if ( misc == -1 ) {
892 DEBUG(('T',1,"fnctl: can't get flags on fd %d: %s",
893 fd, strerror( errno )));
894 return -1;
895 }
896
897 if ( mode )
898 misc |= O_NONBLOCK;
899 else
900 misc &= ~O_NONBLOCK;
901
902 if (( misc = fcntl( fd, F_SETFL, misc )) == -1 ) {
903 DEBUG(('T',1,"fnctl: can't set %sblocking mode on fd %d: %s",
904 (mode ? "non-" : ""), fd, strerror( errno )));
905 }
906 return misc;
907 }
908
909
910
911 /*
912 * tio part
913 */
914
915 /* get current tio settings for given filedescriptor */
tio_get(int fd,TIO * t)916 int tio_get(int fd, TIO *t)
917 {
918 #ifdef SYSV_TERMIO
919 if ( ioctl( fd, TCGETA, t ) < 0 )
920 {
921 DEBUG(('T',3,"TCGETA failed"));
922 return ERROR;
923 }
924 #endif
925 #ifdef POSIX_TERMIOS
926 if ( tcgetattr( fd, t ) < 0 )
927 {
928 DEBUG(('T',3,"tcgetattr failed"));
929 return ERROR;
930 }
931 #endif
932 #ifdef BSD_SGTTY
933 if ( gtty( fd, t ) < 0 )
934 {
935 DEBUG(('T',3,"gtty failed"));
936 return ERROR;
937 }
938 #endif
939 DEBUG(('T',4,"tio_get: c_iflag=%08x, c_oflag=%08x, c_cflag=%08x, c_lflag=%08x",
940 t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag));
941 return OK;
942 }
943
944
945 /* set current tio settings for given filedescriptor */
tio_set(int fd,TIO * t)946 int tio_set(int fd, TIO *t)
947 {
948 #ifdef SYSV_TERMIO
949 if ( ioctl( fd, TCSETA, t ) < 0 )
950 {
951 DEBUG(('T',3,"ioctl TCSETA failed"));
952 return ERROR;
953 }
954 #endif
955 #ifdef POSIX_TERMIOS
956 if ( tcsetattr( fd, TCSANOW, t ) < 0 )
957 {
958 DEBUG(('T',3,"tcsetattr failed"));
959 return ERROR;
960 }
961 #endif /* posix_termios */
962
963 #ifdef BSD_SGTTY
964 if ( stty( fd, t ) < 0 )
965 {
966 DEBUG(('T',3,"stty failed"));
967 return ERROR;
968 }
969 #endif
970 DEBUG(('T',4,"tio_set: c_iflag=%08x, c_oflag=%08x, c_cflag=%08x, c_lflag=%08x",
971 t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag));
972 return OK;
973 }
974
975
976 /*
977 * Set speed, do not touch the other flags
978 * "speed" is given as numeric baud rate, not as Bxxx constant
979 */
tio_set_speed(TIO * t,unsigned int speed)980 int tio_set_speed(TIO *t, unsigned int speed)
981 {
982 int i, symspeed = 0;
983
984 for( i = 0; speedtab[i].cbaud != 0; i++ )
985 {
986 if ( speedtab[i].nspeed == speed )
987 { symspeed = speedtab[i].cbaud; break; }
988 }
989
990 if ( symspeed == 0 ) {
991 errno = EINVAL;
992 DEBUG(('T',2,"tss: unknown/unsupported bit rate: %d", speed));
993 return ERROR;
994 }
995
996 DEBUG(('T',2,"tss: set speed to %d (%03o)", speed, symspeed));
997
998 #ifdef SYSV_TERMIO
999 t->c_cflag = ( t->c_cflag & ~CBAUD) | symspeed;
1000 #endif
1001 #ifdef POSIX_TERMIOS
1002 cfsetospeed( t, symspeed );
1003 cfsetispeed( t, symspeed );
1004 #endif
1005 #ifdef BSD_SGTTY
1006 t->sg_ispeed = t->sg_ospeed = symspeed;
1007 #endif
1008 return OK;
1009 }
1010
1011 /*
1012 * Get port speed. Return integer value, not symbolic constant
1013 */
tio_get_speed(TIO * t)1014 int tio_get_speed(TIO *t)
1015 {
1016 #ifdef SYSV_TERMIO
1017 ushort cbaud = t->c_cflag & CBAUD;
1018 #endif
1019 #ifdef POSIX_TERMIOS
1020 speed_t cbaud = cfgetospeed( t );
1021 #endif
1022 #ifdef BSD_SGTTY
1023 int cbaud = t->sg_ospeed;
1024 #endif
1025 struct speedtab *st;
1026
1027 for( st = speedtab; st->nspeed != 0; st++ ) {
1028 if ( st->cbaud == cbaud )
1029 break;
1030 }
1031 return st->nspeed;
1032 }
1033
1034
1035 /* set "sane" mode, usable for login, ...
1036 * unlike the other tio_mode_* functions, this function initializes
1037 * all flags, and should be called before calling any other function
1038 */
tio_local_mode(TIO * t,int local)1039 void tio_local_mode(TIO * t, int local)
1040 {
1041 if ( local )
1042 #if defined(SYSV_TERMIO) || defined( POSIX_TERMIOS )
1043 t->c_cflag |= CLOCAL;
1044 else
1045 t->c_cflag &= ~CLOCAL;
1046 #else /* BSD_SGTTY (tested only on NeXT yet, but should work) */
1047 t->sg_flags &= ~LNOHANG;
1048 else
1049 t->sg_flags |= LNOHANG ;
1050 #endif
1051 }
1052
1053
1054 /*
1055 */
1056
1057 #define CFLAGS_TO_SET (CREAD | HUPCL)
1058 #define CFLAGS_TO_CLEAR (CSTOPB | PARENB | PARODD | CLOCAL)
1059
tio_raw_mode(TIO * t)1060 void tio_raw_mode(TIO * t)
1061 {
1062 #if defined(SYSV_TERMIO) || defined( POSIX_TERMIOS)
1063 #if 0
1064 t->c_iflag &= ( IXON | IXOFF | IXANY ); /* clear all flags except */
1065 /* xon / xoff handshake */
1066 t->c_oflag = 0; /* no output processing */
1067 t->c_lflag = 0; /* no signals, no echo */
1068 #endif /* 0 */
1069 t->c_cc[VMIN] = 1; /* disable line buffering */
1070 t->c_cc[VTIME] = 0;
1071
1072 /*
1073 t->c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP
1074 | INLCR | IGNCR | ICRNL | IXON );
1075 */
1076 t->c_iflag = 0;
1077 t->c_oflag = 0;
1078 t->c_lflag = 0;
1079 t->c_cflag &= ~( CSIZE | CFLAGS_TO_CLEAR );
1080 t->c_cflag |= ( CS8 | CFLAGS_TO_SET );
1081
1082 #else
1083 t->sg_flags = RAW;
1084 #endif
1085 }
1086
1087
1088 /*
1089 * initialize all c_cc fields (for POSIX and SYSV) to proper start
1090 * values (normally, the serial driver should do this, but there are
1091 * numerous systems where some of the more esoteric (VDSUSP...) flags
1092 * are plain wrong (e.g. set to "m" or so)
1093 *
1094 * do /not/ initialize VERASE and VINTR, since some systems use
1095 * ^H / DEL here, others DEL / ^C.
1096 */
tio_default_cc(TIO * t)1097 void tio_default_cc(TIO *t)
1098 {
1099 #ifdef BSD_SGTTY
1100 t->sg_erase = 0x7f; /* erase character */
1101 t->sg_kill = 0x25; /* kill character, ^u */
1102
1103 #else /* posix or sysv */
1104 t->c_cc[VQUIT] = CQUIT;
1105 t->c_cc[VKILL] = CKILL;
1106 t->c_cc[VEOF] = CEOF;
1107 #if defined(VEOL) && VEOL < TIONCC
1108 t->c_cc[VEOL] = CEOL;
1109 #endif
1110 #if defined(VSTART) && VSTART < TIONCC
1111 t->c_cc[VSTART] = CSTART;
1112 #endif
1113 #if defined(VSTOP) && VSTOP < TIONCC
1114 t->c_cc[VSTOP] = CSTOP;
1115 #endif
1116 #if defined(VSUSP) && VSUSP < TIONCC
1117 t->c_cc[VSUSP] = CSUSP;
1118 #endif
1119 #if defined(VSWTCH) && VSWTCH < TIONCC
1120 t->c_cc[VSWTCH] = CSWTCH;
1121 #endif
1122 /* the following are for SVR4.2 (and higher) */
1123 #if defined(VDSUSP) && VDSUSP < TIONCC
1124 t->c_cc[VDSUSP] = CDSUSP;
1125 #endif
1126 #if defined(VREPRINT) && VREPRINT < TIONCC
1127 t->c_cc[VREPRINT] = CRPRNT;
1128 #endif
1129 #if defined(VDISCARD) && VDISCARD < TIONCC
1130 t->c_cc[VDISCARD] = CFLUSH;
1131 #endif
1132 #if defined(VWERASE) && VWERASE < TIONCC
1133 t->c_cc[VWERASE] = CWERASE;
1134 #endif
1135 #if defined(VLNEXT) && VLNEXT < TIONCC
1136 t->c_cc[VLNEXT] = CLNEXT;
1137 #endif
1138
1139 #endif /* bsd <-> posix + sysv */
1140 }
1141
1142
1143 /*
1144 * set flow control according to the <type> parameter. It can be any
1145 * combination of
1146 * FLOW_XON_IN - use Xon/Xoff on incoming data
1147 * FLOW_XON_OUT- respect Xon/Xoff on outgoing data
1148 * FLOW_HARD - use RTS/respect CTS line for hardware handshake
1149 * (not every combination will work on every system)
1150 *
1151 * WARNING: for most systems, this function will not touch the tty
1152 * settings, only modify the TIO structure.
1153 */
tio_set_flow_control(int fd,TIO * t,int type)1154 int tio_set_flow_control(int fd, TIO *t, int type)
1155 {
1156 #ifdef USE_TERMIOX
1157 struct termiox tix;
1158 #endif
1159
1160 DEBUG(('T',2,"tio_set_flow_control(%s%s%s%s )",
1161 type & FLOW_HARD ? " HARD": "",
1162 type & FLOW_XON_IN ? " XON_IN": "",
1163 type & FLOW_XON_OUT? " XON_OUT": "",
1164 type == FLOW_NONE ? " NONE" : "" ));
1165
1166 #if defined( SYSV_TERMIO ) || defined( POSIX_TERMIOS )
1167 t->c_cflag &= ~HARDW_HS;
1168 t->c_iflag &= ~( IXON | IXOFF | IXANY );
1169
1170 if ( type & FLOW_HARD )
1171 t->c_cflag |= HARDW_HS;
1172 if ( type & FLOW_XON_IN )
1173 t->c_iflag |= IXOFF;
1174 if ( type & FLOW_XON_OUT ) {
1175 t->c_iflag |= IXON;
1176 if ( type & FLOW_XON_IXANY )
1177 t->c_iflag |= IXANY;
1178 }
1179 #else
1180 # ifdef NEXTSGTTY
1181 DEBUG(('T',3,"tio_set_flow_control: not yet implemented"));
1182 # else
1183 # error "not yet implemented"
1184 # endif
1185 #endif
1186
1187 /* SVR4 came up with a new method of setting h/w flow control */
1188 #ifdef USE_TERMIOX
1189 DEBUG(('T',3,"tio_set_flow_control: using termiox"));
1190
1191 if ( ioctl(fd, TCGETX, &tix ) < 0) {
1192 DEBUG(('T',3,"ioctl TCGETX"));
1193 return ERROR;
1194 }
1195 if ( type & FLOW_HARD )
1196 tix.x_hflag |= (RTSXOFF | CTSXON);
1197 else
1198 tix.x_hflag &= ~(RTSXOFF | CTSXON);
1199
1200 if ( ioctl( fd, TCSETX, &tix ) < 0 ) {
1201 DEBUG(('T',3,"ioctl TCSETX" ));
1202 return ERROR;
1203 }
1204 #endif
1205
1206 return OK;
1207 }
1208
1209
1210 /*
1211 * Returns 0 if `speed' was not found in speedtab[] or
1212 * symbolic speed otherwise.
1213 */
tio_trans_speed(unsigned int speed)1214 int tio_trans_speed(unsigned int speed)
1215 {
1216 struct speedtab *st;
1217
1218 for( st = speedtab; st->nspeed != 0; st++ ) {
1219 if ( st->nspeed == speed )
1220 break;
1221 }
1222 return st->cbaud;
1223 }
1224
1225
1226 /*
1227 * Toggle dtr with delay for msec milliseconds
1228 */
tio_toggle_dtr(int fd,int msec)1229 int tio_toggle_dtr(int fd, int msec)
1230 {
1231 #if defined(TIOCMBIS) && \
1232 ( defined(sun) || defined(SVR4) || defined(NeXT) || defined(linux) )
1233
1234 int mctl = TIOCM_DTR;
1235
1236 DEBUG(('T',2,"tio_toggle_dtr: %d msec", msec));
1237 #if !defined( TIOCM_VALUE )
1238 if ( ioctl( fd, TIOCMBIC, &mctl ) < 0 )
1239 #else
1240 if ( ioctl( fd, TIOCMBIC, (char *) mctl ) < 0 )
1241 #endif
1242 {
1243 DEBUG(('T',2,"tio_toggle_dtr: TIOCMBIC failed"));
1244 return ERROR;
1245 }
1246
1247 qsleep( msec );
1248
1249 #if !defined( TIOCM_VALUE)
1250 if ( ioctl( fd, TIOCMBIS, &mctl ) < 0 )
1251 #else
1252 if ( ioctl( fd, TIOCMBIS, (char *) mctl ) < 0 )
1253 #endif
1254 {
1255 DEBUG(('T',2,"tio_toggle_dtr: TIOCMBIS failed"));
1256 return ERROR;
1257 }
1258 return OK;
1259 #else /* !TIOCMBI* */
1260
1261 /* On HP/UX, lowering DTR by setting the port speed to B0 will
1262 * leave it there. So, do it via HP/UX's special ioctl()'s...
1263 */
1264 #if defined(_HPUX_SOURCE) || defined(MCGETA)
1265 unsigned long mflag = 0L;
1266
1267 DEBUG(('T',2,"tio_toggle_dtr: %d msec", msec));
1268 if ( ioctl( fd, MCSETAF, &mflag ) < 0 ) {
1269 DEBUG(('T',2,"tio_toggle_dtr: MCSETAF failed"));
1270 return ERROR;
1271 }
1272
1273 qsleep( msec );
1274
1275 if ( ioctl( fd, MCGETA, &mflag ) < 0 ) {
1276 DEBUG(('T',2,"tio_toggle_dtr: MCGETA failed"));
1277 return ERROR;
1278 }
1279 mflag = MRTS | MDTR;
1280 if ( ioctl( fd, MCSETAF, &mflag ) < 0 ) {
1281 DEBUG(('T',2,"tio_toggle_dtr: MCSETAF failed"));
1282 return ERROR;
1283 }
1284 return OK;
1285
1286 #else /* !MCGETA */
1287
1288 /* The "standard" way of doing things - via speed = B0
1289 */
1290 TIO t, save_t;
1291 int result;
1292
1293 DEBUG(('T',2,"tio_toggle_dtr: %d msec", msec));
1294 if ( tio_get( fd, &t ) == ERROR ) {
1295 DEBUG(('T',2,"tio_toggle_dtr: tio_get failed"));
1296 return ERROR;
1297 }
1298
1299 save_t = t;
1300
1301 #ifdef SYSV_TERMIO
1302 t.c_cflag = ( t.c_cflag & ~CBAUD ) | B0; /* speed = 0 */
1303 #endif
1304 #ifdef POSIX_TERMIOS
1305 cfsetospeed( &t, B0 );
1306 cfsetispeed( &t, B0 );
1307 #endif
1308 #ifdef BSD_SGTTY
1309 t.sg_ispeed = t.sg_ospeed = B0
1310 #endif
1311
1312 tio_set( fd, &t );
1313 qsleep( msec );
1314 result = tio_set( fd, &save_t );
1315
1316 DEBUG(('T',2,"tio_toggle_dtr: result %d", result));
1317 return result;
1318 #endif /* !MCSETA */
1319 #endif /* !SVR4 */
1320 }
1321
1322
1323 /*
1324 * Flush input or output data queue
1325 *
1326 * "queue" is one of the TIO_Q* values from tio.h
1327 */
tio_flush_queue(int fd,int queue)1328 int tio_flush_queue(int fd, int queue)
1329 {
1330 int r = OK;
1331 #ifdef POSIX_TERMIOS
1332 switch( queue ) {
1333 case TIO_Q_IN: r = tcflush( fd, TCIFLUSH ); break;
1334 case TIO_Q_OUT: r = tcflush( fd, TCOFLUSH ); break;
1335 case TIO_Q_BOTH: r = tcflush( fd, TCIOFLUSH );break;
1336 default:
1337 DEBUG(('T',2,"tio_flush_queue: invalid ``queue'' argument (%d)", queue ));
1338 return ERROR;
1339 }
1340 #endif
1341 #ifdef SYSV_TERMIO
1342 switch ( queue ) {
1343 case TIO_Q_IN: r = ioctl( fd, TCFLSH, 0 ); break;
1344 case TIO_Q_OUT: r = ioctl( fd, TCFLSH, 1 ); break;
1345 case TIO_Q_BOTH: r = ioctl( fd, TCFLSH, 2 ); break;
1346 default:
1347 DEBUG(('T',2,"tio_flush_queue: invalid ``queue'' argument (%d)", queue ));
1348 return ERROR;
1349 }
1350 #endif
1351 #ifdef BSD_SGTTY
1352 int arg;
1353
1354 switch ( queue ) {
1355 case TIO_Q_IN: arg = FREAD; break;
1356 case TIO_Q_OUT: arg = FWRITE; break;
1357 case TIO_Q_BOTH: arg = FREAD | FWRITE; break;
1358 default:
1359 DEBUG(('T',2,"tio_flush_queue: invalid ``queue'' argument (%d)", queue ));
1360 return ERROR;
1361 }
1362 r = ioctl( fd, TIOCFLUSH, (char *) &arg );
1363 #endif
1364 if ( r != 0 )
1365 DEBUG(('T',2,"tio: cannot flush queue" ));
1366
1367 return r;
1368 }
1369
1370
1371 /*
1372 * tio_drain(fd): wait for output queue to drain
1373 */
tio_drain_output(int fd)1374 int tio_drain_output(int fd)
1375 {
1376 #ifdef POSIX_TERMIOS
1377 if ( tcdrain( fd ) == ERROR ) {
1378 DEBUG(('T',2,"tio_drain: tcdrain" ));
1379 return ERROR;
1380 }
1381 #else
1382 # ifdef SYSV_TERMIO
1383 if ( ioctl( fd, TCSBRK, 1 ) == ERROR ) {
1384 DEBUG(('T',2,"tio_drain: TCSBRK/1" ));
1385 return ERROR;
1386 }
1387 # else /* no way to wait for data to drain with BSD_SGTTY */
1388 DEBUG(('T',2,"tio_drain: expect spurious failures" ));
1389 # endif
1390 #endif
1391 return OK;
1392 }
1393
1394
1395 /* tio_get_rs232_lines()
1396 *
1397 * get the status of all RS232 status lines
1398 * (coded by the TIO_F_* flags. On systems that have the BSD TIOCM_*
1399 * flags, we use them, on others we may have to do some other tricks)
1400 *
1401 * "-1" can mean "error" or "not supported on this system" (e.g. SCO).
1402 */
tio_get_rs232_lines(int fd)1403 int tio_get_rs232_lines(int fd)
1404 {
1405 int flags;
1406 #ifdef TIO_F_SYSTEM_DEFS
1407 if ( ioctl(fd, TIOCMGET, &flags ) < 0 )
1408 DEBUG(('M',3,"tio_get_rs232_lines: TIOCMGET failed"));
1409
1410 #else /* !TIO_F_SYSTEM_DEFS */
1411 flags=-1;
1412 #endif
1413
1414 if ( flags != -1 )
1415 {
1416 DEBUG(('M',3,"tio_get_rs232_lines: status: [%s][%s][%s][%s][%s][%s]",
1417 ( flags & TIO_F_RTS ) ? "RTS" : "rts",
1418 ( flags & TIO_F_CTS ) ? "CTS" : "cts",
1419 ( flags & TIO_F_DSR ) ? "DSR" : "dsr",
1420 ( flags & TIO_F_DTR ) ? "DTR" : "dtr",
1421 ( flags & TIO_F_DCD ) ? "DCD" : "dcd",
1422 ( flags & TIO_F_RI ) ? "RI" : "ri" ));
1423 }
1424 return flags;
1425 }
1426
1427