1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Implementation of termios.h.
3 *@ FIXME everywhere: tcsetattr() generates SIGTTOU when we're not in
4 *@ FIXME foreground pgrp, and can fail with EINTR!!
5 *@ TODO . SIGINT during HANDS_OFF reaches us nonetheless.
6 *@ TODO . _HANDS_OFF as well as the stack based approach as such is nonsense.
7 *@ TODO It might work well for this MUA, but in general termios_ctx should
8 *@ TODO be a public struct with a lifetime, and activate/suspend methods.
9 *@ TODO It shall be up to the callers how this is managed, we can emit some
10 *@ TODO state events to let them decide for good.
11 *@ TODO Handling children which take over terminal via HANDS_OFF is bad:
12 *@ TODO instead, we need to have a notion of background and foreground,
13 *@ TODO and ensure the terminal is in normal mode when going backward.
14 *@ TODO What children do is up to them, managing them in stack: impossible
16 * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
17 * SPDX-License-Identifier: ISC
19 * Permission to use, copy, modify, and/or distribute this software for any
20 * purpose with or without fee is hereby granted, provided that the above
21 * copyright notice and this permission notice appear in all copies.
23 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
24 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
26 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 #define su_FILE termios
34 #define mx_SOURCE_TERMIOS
36 #ifndef mx_HAVE_AMALGAMATION
40 #include <sys/ioctl.h>
44 #include <su/icodec.h>
50 #include "mx/termios.h"
51 #include "su/code-in.h"
53 /* Problem: VAL_ configuration values are strings, we need numbers */
54 #define a_TERMIOS_DEFAULT_HEIGHT \
55 (VAL_HEIGHT[1] == '\0' ? (VAL_HEIGHT[0] - '0') \
56 : (VAL_HEIGHT[2] == '\0' \
57 ? ((VAL_HEIGHT[0] - '0') * 10 + (VAL_HEIGHT[1] - '0')) \
58 : (((VAL_HEIGHT[0] - '0') * 10 + (VAL_HEIGHT[1] - '0')) * 10 + \
59 (VAL_HEIGHT[2] - '0'))))
60 #define a_TERMIOS_DEFAULT_WIDTH \
61 (VAL_WIDTH[1] == '\0' ? (VAL_WIDTH[0] - '0') \
62 : (VAL_WIDTH[2] == '\0' \
63 ? ((VAL_WIDTH[0] - '0') * 10 + (VAL_WIDTH[1] - '0')) \
64 : (((VAL_WIDTH[0] - '0') * 10 + (VAL_WIDTH[1] - '0')) * 10 + \
65 (VAL_WIDTH[2] - '0'))))
68 # define a_TERMIOS_SIGWINCH SIGWINCH
70 # define a_TERMIOS_SIGWINCH -1
74 struct a_termios_env
*tiose_prev
;
77 boole tiose_suspended
;
79 /*s32 tiose_pgrp;*/ /* In HANDS_OFF mode */
80 su_64( u8 tiose__pad2
[4]; )
81 mx_termios_on_state_change tiose_on_state_change
;
83 struct termios tiose_state
;
87 struct a_termios_env
*tiosg_envp
;
88 /* If outermost == normal state; used as init switch, too */
89 struct a_termios_env
*tiosg_normal
;
90 struct a_termios_env
*tiosg_pend_free
;
93 n_sighdl_t tiosg_otstp
;
94 n_sighdl_t tiosg_ottin
;
95 n_sighdl_t tiosg_ottou
;
96 n_sighdl_t tiosg_ocont
;
97 #if a_TERMIOS_SIGWINCH != -1
98 n_sighdl_t tiosg_owinch
;
100 /* Remaining signals only when in password/raw mode */
101 n_sighdl_t tiosg_ohup
;
102 n_sighdl_t tiosg_oint
;
103 n_sighdl_t tiosg_oquit
;
104 n_sighdl_t tiosg_oterm
;
105 struct a_termios_env tiosg_env_base
;
108 static struct a_termios_g a_termios_g
;
110 struct mx_termios_dimension mx_termios_dimen
;
113 static void a_termios_sig_adjust(boole condome
);
116 static void a_termios_onsig(int sig
);
119 SINLINE boole
a_termios_norm_query(void);
121 /* Do the system-dependent dance on getting used to terminal dimension */
122 static void a_termios_dimen_query(struct mx_termios_dimension
*tiosdp
);
125 a_termios_sig_adjust(boole condome
){
129 if((a_termios_g
.tiosg_ohup
= safe_signal(SIGHUP
, SIG_IGN
)) != SIG_IGN
)
130 safe_signal(SIGHUP
, &a_termios_onsig
);
131 if((a_termios_g
.tiosg_oint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
132 safe_signal(SIGINT
, &a_termios_onsig
);
133 safe_signal(SIGQUIT
, &a_termios_onsig
);
134 safe_signal(SIGTERM
, &a_termios_onsig
);
136 if(a_termios_g
.tiosg_ohup
!= SIG_IGN
)
137 safe_signal(SIGHUP
, a_termios_g
.tiosg_ohup
);
138 if(a_termios_g
.tiosg_oint
!= SIG_IGN
)
139 safe_signal(SIGINT
, a_termios_g
.tiosg_oint
);
140 safe_signal(SIGQUIT
, a_termios_g
.tiosg_oquit
);
141 safe_signal(SIGTERM
, a_termios_g
.tiosg_oterm
);
147 a_termios_norm_query(void){
151 rv
= (tcgetattr(fileno(mx_tty_fp
),
152 &a_termios_g
.tiosg_normal
->tiose_state
) == 0);
153 /* XXX always set ECHO and ICANON in our "normal" canonical state */
154 a_termios_g
.tiosg_normal
->tiose_state
.c_lflag
|= ECHO
| ICANON
;
160 a_termios_onsig(int sig
){
161 n_sighdl_t oact
, myact
;
163 struct a_termios_env
*tiosep
;
165 NYD
; /* Signal handler */
167 if(sig
== a_TERMIOS_SIGWINCH
)
172 case SIG ## N: oact = a_termios_g.tiosg_o ## X; jobsig = Y; break;
176 a_X(TSTP
, tstp
, TRU1
)
177 a_X(TTIN
, ttin
, TRU1
)
178 a_X(TTOU
, ttou
, TRU1
)
179 a_X(CONT
, cont
, TRU1
)
182 a_X(QUIT
, quit
, FAL0
)
183 a_X(TERM
, term
, FAL0
)
189 tiosep
= a_termios_g
.tiosg_envp
;
191 if(!jobsig
|| sig
!= SIGCONT
){
192 if(!tiosep
->tiose_suspended
){
193 tiosep
->tiose_suspended
= TRU1
;
195 if(tiosep
->tiose_on_state_change
!= NIL
){
196 dopop
= (*tiosep
->tiose_on_state_change
)(
197 tiosep
->tiose_osc_cookie
,
198 (mx_TERMIOS_STATE_SUSPEND
| mx_TERMIOS_STATE_SIGNAL
|
199 (jobsig
? mx_TERMIOS_STATE_JOB_SIGNAL
: 0)), sig
);
201 a_termios_g
.tiosg_envp
= tiosep
->tiose_prev
;
204 if(tiosep
->tiose_cmd
!= mx_TERMIOS_CMD_NORMAL
)
205 (void)tcsetattr(fileno(mx_tty_fp
), TCSAFLUSH
,
206 &a_termios_g
.tiosg_normal
->tiose_state
);
210 /* If we shall pop this level link context in a list for later freeing in
211 * a more regular context */
213 tiosep
->tiose_prev
= a_termios_g
.tiosg_pend_free
;
214 a_termios_g
.tiosg_pend_free
= tiosep
;
217 if(jobsig
|| (tiosep
->tiose_cmd
!= mx_TERMIOS_CMD_HANDS_OFF
&&
218 oact
!= SIG_DFL
&& oact
!= SIG_IGN
&& oact
!= SIG_ERR
)){
219 myact
= safe_signal(sig
, oact
);
222 sigaddset(&nset
, sig
);
223 sigprocmask(SIG_UNBLOCK
, &nset
, NIL
);
225 sigprocmask(SIG_BLOCK
, &nset
, NIL
);
227 safe_signal(sig
, myact
);
229 /* When we come here we shall continue */
230 if(!dopop
&& tiosep
->tiose_suspended
){
231 tiosep
->tiose_suspended
= FAL0
;
233 if(tiosep
->tiose_cmd
!= mx_TERMIOS_CMD_HANDS_OFF
){
234 /* Requery our notion of what is "normal", so that possible user
235 * adjustments which happened in the meantime are kept */
236 a_termios_norm_query();
238 if(tiosep
->tiose_cmd
!= mx_TERMIOS_CMD_NORMAL
)
239 (void)tcsetattr(fileno(mx_tty_fp
), TCSADRAIN
,
240 &tiosep
->tiose_state
);
243 if(tiosep
->tiose_on_state_change
!= NIL
)
244 (*tiosep
->tiose_on_state_change
)(tiosep
->tiose_osc_cookie
,
245 (mx_TERMIOS_STATE_RESUME
| mx_TERMIOS_STATE_SIGNAL
|
246 (jobsig
? mx_TERMIOS_STATE_JOB_SIGNAL
: 0)), sig
);
249 #if a_TERMIOS_SIGWINCH == -1
259 if(n_psonce
& n_PSO_INTERACTIVE
){
260 a_termios_dimen_query(&mx_termios_dimen
);
261 if(mx_termios_dimen
.tiosd_width
> 1 &&
262 !(n_psonce
& n_PSO_TERMCAP_FULLWIDTH
))
263 --mx_termios_dimen
.tiosd_width
;
264 n_pstate
|= n_PS_SIGWINCH_PEND
;
270 a_termios_dimen_query(struct mx_termios_dimension
*tiosdp
){
272 #if defined mx_HAVE_TCGETWINSIZE || defined TIOCGWINSZ
274 #elif defined TIOCGSIZE
277 # error One of tcgetwinsize(3), TIOCGWINSZ and TIOCGSIZE
281 #ifdef mx_HAVE_TCGETWINSIZE
282 if(tcgetwinsize(fileno(mx_tty_fp
), &ws
) == -1)
283 ws
.ws_col
= ws
.ws_row
= 0;
284 #elif defined TIOCGWINSZ
285 if(ioctl(fileno(mx_tty_fp
), TIOCGWINSZ
, &ws
) == -1)
286 ws
.ws_col
= ws
.ws_row
= 0;
287 #elif defined TIOCGSIZE
288 if(ioctl(fileno(mx_tty_fp
), TIOCGSIZE
, &ws
) == -1)
289 ts
.ts_lines
= ts
.ts_cols
= 0;
292 #if defined mx_HAVE_TCGETWINSIZE || defined TIOCGWINSZ
294 tiosdp
->tiosd_height
= tiosdp
->tiosd_real_height
= ws
.ws_row
;
295 #elif defined TIOCGSIZE
297 tiosdp
->tiosd_height
= tiosdp
->tiosd_real_height
= ts
.ts_lines
;
302 /* We use the following algorithm for the fallback height:
303 * If baud rate < 1200, use 9
304 * If baud rate = 1200, use 14
305 * If baud rate > 1200, use VAL_HEIGHT */
306 ospeed
= ((tcgetattr(fileno(mx_tty_fp
), &tbuf
) == -1)
307 ? B9600
: cfgetospeed(&tbuf
));
310 tiosdp
->tiosd_height
= 9;
311 else if(ospeed
== B1200
)
312 tiosdp
->tiosd_height
= 14;
314 tiosdp
->tiosd_height
= a_TERMIOS_DEFAULT_HEIGHT
;
316 tiosdp
->tiosd_real_height
= a_TERMIOS_DEFAULT_HEIGHT
;
320 #if defined mx_HAVE_TCGETWINSIZE || defined TIOCGWINSZ
321 tiosdp
->tiosd_width
= tiosdp
->tiosd_real_width
= ws
.ws_col
322 #elif defined TIOCGSIZE
323 tiosdp
->tiosd_width
= tiosdp
->tiosd_real_width
= ts
.ts_cols
326 tiosdp
->tiosd_width
= tiosdp
->tiosd_real_width
= a_TERMIOS_DEFAULT_WIDTH
;
332 mx_termios_controller_setup(enum mx_termios_setup what
){
336 if(what
== mx_TERMIOS_SETUP_STARTUP
){
337 a_termios_g
.tiosg_envp
= &a_termios_g
.tiosg_env_base
;
340 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
342 a_termios_g
.tiosg_otstp
= safe_signal(SIGTSTP
, &a_termios_onsig
);
343 a_termios_g
.tiosg_ottin
= safe_signal(SIGTTIN
, &a_termios_onsig
);
344 a_termios_g
.tiosg_ottou
= safe_signal(SIGTTOU
, &a_termios_onsig
);
345 a_termios_g
.tiosg_ocont
= safe_signal(SIGCONT
, &a_termios_onsig
);
347 /* This assumes oset contains nothing but SIGCHLD, so to say */
348 sigdelset(&oset
, SIGTSTP
);
349 sigdelset(&oset
, SIGTTIN
);
350 sigdelset(&oset
, SIGTTOU
);
351 sigdelset(&oset
, SIGCONT
);
352 sigprocmask(SIG_SETMASK
, &oset
, NIL
);
354 /* Semantics are a bit hairy and cast in stone in the manual, and
355 * scattered all over the place, at least $COLUMNS, $LINES, -# */
356 if(!su_state_has(su_STATE_REPRODUCIBLE
) &&
357 ((n_psonce
& n_PSO_INTERACTIVE
) ||
358 ((n_psonce
& n_PSO_TTYANY
) && (n_poption
& n_PO_BATCH_FLAG
)))){
359 /* (Also) POSIX: LINES and COLUMNS always override. These variables
360 * are ensured to be positive numbers, so no checking */
365 ASSERT(mx_termios_dimen
.tiosd_height
== 0);
366 ASSERT(mx_termios_dimen
.tiosd_real_height
== 0);
367 ASSERT(mx_termios_dimen
.tiosd_width
== 0);
368 ASSERT(mx_termios_dimen
.tiosd_real_width
== 0);
369 if(n_psonce
& n_PSO_INTERACTIVE
){
370 /* XXX Yet WINCH after WINCH/CONT, but see POSIX TOSTOP flag */
371 #if a_TERMIOS_SIGWINCH != -1
372 if(safe_signal(SIGWINCH
, SIG_IGN
) != SIG_IGN
)
373 a_termios_g
.tiosg_owinch
= safe_signal(SIGWINCH
,
379 if((hadl
= ((cp
= ok_vlook(LINES
)) != NIL
)))
380 su_idec_u32_cp(&l
, cp
, 0, NIL
);
381 if((hadc
= ((cp
= ok_vlook(COLUMNS
)) != NIL
)))
382 su_idec_u32_cp(&c
, cp
, 0, NIL
);
384 if(l
== 0 || c
== 0){
385 /* In non-interactive mode, stop now, except for the documented
386 * case that both are set but not both have been usable */
387 if(!(n_psonce
& n_PSO_INTERACTIVE
) &&
388 !((n_psonce
& n_PSO_TTYANY
) &&
389 (n_poption
& n_PO_BATCH_FLAG
)) &&
391 goto jtermsize_default
;
393 a_termios_dimen_query(&mx_termios_dimen
);
397 mx_termios_dimen
.tiosd_real_height
=
398 mx_termios_dimen
.tiosd_height
= l
;
400 mx_termios_dimen
.tiosd_width
=
401 mx_termios_dimen
.tiosd_real_width
= c
;
404 /* $COLUMNS and $LINES defaults as documented in the manual! */
405 mx_termios_dimen
.tiosd_height
=
406 mx_termios_dimen
.tiosd_real_height
= a_TERMIOS_DEFAULT_HEIGHT
;
407 mx_termios_dimen
.tiosd_width
=
408 mx_termios_dimen
.tiosd_real_width
= a_TERMIOS_DEFAULT_WIDTH
;
411 /* Note: for this first invocation this will always trigger.
412 * If we have termcap support then termcap_init() will undo this if
413 * FULLWIDTH is set after termcap is initialized.
414 * We have to evaluate it now since cmds may run pre-termcap ... */
415 if(mx_termios_dimen
.tiosd_width
> 1)
416 --mx_termios_dimen
.tiosd_width
;
417 n_pstate
|= n_PS_SIGWINCH_PEND
;
424 mx_termios_on_state_change_set(mx_termios_on_state_change tiossc
, up cookie
){
426 ASSERT(a_termios_g
.tiosg_envp
->tiose_prev
!= NIL
); /* Not in base level */
428 a_termios_g
.tiosg_envp
->tiose_on_state_change
= tiossc
;
429 a_termios_g
.tiosg_envp
->tiose_osc_cookie
= cookie
;
434 mx_termios_cmd(u32 tiosc
, uz a1
){
435 /* xxx tcsetattr not correct says manual: would need to requery and check
436 * whether all desired changes made it instead! */
438 struct a_termios_env
*tiosep_2free
, *tiosep
;
444 ASSERT_NYD_EXEC((tiosc
& mx__TERMIOS_CMD_CTL_MASK
) ||
445 tiosc
== mx_TERMIOS_CMD_RESET
||
446 /*(tiosc == mx_TERMIOS_CMD_SET_PGRP &&
447 * a_termios_g.tiosg_envp->tiose_cmd == mx_TERMIOS_CMD_HANDS_OFF) ||*/
448 (a_termios_g
.tiosg_envp
->tiose_prev
!= NIL
&&
449 ((tiosc
& mx__TERMIOS_CMD_ACT_MASK
) == mx_TERMIOS_CMD_RAW
||
450 (tiosc
& mx__TERMIOS_CMD_ACT_MASK
) == mx_TERMIOS_CMD_RAW_TIMEOUT
) &&
451 (a_termios_g
.tiosg_envp
->tiose_cmd
== mx_TERMIOS_CMD_RAW
||
452 a_termios_g
.tiosg_envp
->tiose_cmd
== mx_TERMIOS_CMD_RAW_TIMEOUT
)),
454 ASSERT_NYD_EXEC(!(tiosc
& mx_TERMIOS_CMD_POP
) ||
455 a_termios_g
.tiosg_envp
->tiose_prev
!= NIL
, rv
= FAL0
);
456 ASSERT_NYD_EXEC((tiosc
& mx__TERMIOS_CMD_ACT_MASK
) ||
457 (tiosc
== mx_TERMIOS_CMD_RESET
/*|| tiosc == mx_TERMIOS_CMD_SET_PGRP*/),
460 if(a_termios_g
.tiosg_normal
== NIL
){
461 a_termios_g
.tiosg_normal
= a_termios_g
.tiosg_envp
;
462 a_termios_g
.tiosg_normal
->tiose_cmd
= mx_TERMIOS_CMD_NORMAL
;
463 /*rv =*/ a_termios_norm_query();
466 /* Note: RESET only called with signals blocked in main loop handler */
467 if(tiosc
== mx_TERMIOS_CMD_RESET
){
470 if((tiosep
= a_termios_g
.tiosg_envp
)->tiose_prev
== NIL
){
474 rv
= (tcsetattr(fileno(mx_tty_fp
), TCSADRAIN
, &tiosep
->tiose_state
477 for(isfirst
= TRU1
;; isfirst
= FAL0
){
478 a_termios_g
.tiosg_envp
= tiosep
->tiose_prev
;
480 if(isfirst
|| !tiosep
->tiose_suspended
){
481 if(tiosep
->tiose_on_state_change
!= NIL
)
482 (*tiosep
->tiose_on_state_change
)(tiosep
->tiose_osc_cookie
,
483 (isfirst
? mx_TERMIOS_STATE_SUSPEND
: 0
484 | mx_TERMIOS_STATE_POP
), 0);
489 if((tiosep
= a_termios_g
.tiosg_envp
)->tiose_prev
== NIL
)
493 a_termios_sig_adjust(FAL0
);
496 }else if(tiosc
== mx_TERMIOS_CMD_SET_PGRP
){
497 if(a_termios_g
.tiosg_pgrp
== 0)
498 a_termios_g
.tiosg_pgrp
= getpgrp();
499 rv
= (tcsetpgrp(fileno(mx_tty_fp
), S(pid_t
,a1
)) == 0);
502 }else if(a_termios_g
.tiosg_envp
->tiose_cmd
==
503 (tiosc
& mx__TERMIOS_CMD_ACT_MASK
) &&
504 !(tiosc
& mx__TERMIOS_CMD_CTL_MASK
)){
509 if(tiosc
& mx_TERMIOS_CMD_PUSH
){
510 tiosep
= su_TCALLOC(struct a_termios_env
, 1);
511 tiosep
->tiose_cmd
= (tiosc
& mx__TERMIOS_CMD_ACT_MASK
);
516 if(tiosc
& mx_TERMIOS_CMD_PUSH
){
517 if((tiosep
->tiose_prev
= a_termios_g
.tiosg_envp
)->tiose_prev
== NIL
)
518 a_termios_sig_adjust(TRU1
);
520 if(!a_termios_g
.tiosg_envp
->tiose_suspended
){
521 a_termios_g
.tiosg_envp
->tiose_suspended
= TRU1
;
522 if(a_termios_g
.tiosg_envp
->tiose_on_state_change
!= NIL
)
523 (*a_termios_g
.tiosg_envp
->tiose_on_state_change
)(
524 a_termios_g
.tiosg_envp
->tiose_osc_cookie
,
525 mx_TERMIOS_STATE_SUSPEND
, 0);
529 a_termios_g
.tiosg_envp
= tiosep
;
530 }else if(tiosc
& mx_TERMIOS_CMD_POP
){
531 tiosep_2free
= tiosep
= a_termios_g
.tiosg_envp
;
532 a_termios_g
.tiosg_envp
= tiosep
->tiose_prev
;
533 tiosep
->tiose_prev
= NIL
;
535 if(!tiosep
->tiose_suspended
&& tiosep
->tiose_on_state_change
!= NIL
)
536 (*tiosep
->tiose_on_state_change
)(tiosep
->tiose_osc_cookie
,
537 (mx_TERMIOS_STATE_SUSPEND
| mx_TERMIOS_STATE_POP
), 0);
539 if((tiosep
= a_termios_g
.tiosg_envp
)->tiose_prev
== NIL
)
540 a_termios_sig_adjust(FAL0
);
542 tiosc
= tiosep
->tiose_cmd
| mx_TERMIOS_CMD_POP
;
543 a1
= tiosep
->tiose_a1
;
545 tiosep
= a_termios_g
.tiosg_envp
;
547 if(tiosep
->tiose_prev
!= NIL
)
548 su_mem_copy(&tiosep
->tiose_state
, &a_termios_g
.tiosg_normal
->tiose_state
,
549 sizeof(tiosep
->tiose_state
));
551 ASSERT(tiosep
->tiose_cmd
== mx_TERMIOS_CMD_NORMAL
);
553 switch((tiosep
->tiose_cmd
= (tiosc
& mx__TERMIOS_CMD_ACT_MASK
))){
555 case mx_TERMIOS_CMD_HANDS_OFF
:
556 if(!(tiosc
& mx_TERMIOS_CMD_PUSH
)){
561 case mx_TERMIOS_CMD_NORMAL
:
562 rv
= (tcsetattr(fileno(mx_tty_fp
), TCSADRAIN
, &tiosep
->tiose_state
565 case mx_TERMIOS_CMD_PASSWORD
:
566 tiosep
->tiose_state
.c_iflag
&= ~(ISTRIP
);
567 tiosep
->tiose_state
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
568 rv
= (tcsetattr(fileno(mx_tty_fp
), TCSADRAIN
, &tiosep
->tiose_state
571 case mx_TERMIOS_CMD_RAW
:
572 case mx_TERMIOS_CMD_RAW_TIMEOUT
:
573 a1
= MIN(U8_MAX
, a1
);
574 tiosep
->tiose_a1
= S(u8
,a1
);
575 if((tiosc
& mx__TERMIOS_CMD_ACT_MASK
) == mx_TERMIOS_CMD_RAW
){
576 tiosep
->tiose_state
.c_cc
[VMIN
] = S(u8
,a1
);
577 tiosep
->tiose_state
.c_cc
[VTIME
] = 0;
579 tiosep
->tiose_state
.c_cc
[VMIN
] = 0;
580 tiosep
->tiose_state
.c_cc
[VTIME
] = S(u8
,a1
);
582 tiosep
->tiose_state
.c_iflag
&= ~(ISTRIP
| IGNCR
| IXON
| IXOFF
);
583 tiosep
->tiose_state
.c_lflag
&= ~(ECHO
/*| ECHOE | ECHONL */|
584 ICANON
| IEXTEN
| ISIG
);
585 rv
= (tcsetattr(fileno(mx_tty_fp
), TCSADRAIN
, &tiosep
->tiose_state
590 if(/*!(tiosc & mx__TERMIOS_CMD_CTL_MASK) &&*/ tiosep
->tiose_suspended
&&
591 tiosep
->tiose_on_state_change
!= NIL
)
592 (*tiosep
->tiose_on_state_change
)(tiosep
->tiose_osc_cookie
,
593 mx_TERMIOS_STATE_RESUME
, 0);
596 tiosep
->tiose_suspended
= FAL0
;
599 if(tiosep_2free
!= NIL
)
600 tiosep_2free
->tiose_prev
= a_termios_g
.tiosg_pend_free
;
602 tiosep_2free
= a_termios_g
.tiosg_pend_free
;
603 a_termios_g
.tiosg_pend_free
= NIL
;
608 while((tiosep
= tiosep_2free
) != NIL
){
609 tiosep_2free
= tiosep
->tiose_prev
;
617 #include "su/code-ou.h"