1 /* vi: set sw=4 ts=4: */
2 /*
3  * stty -- change and print terminal line settings
4  * Copyright (C) 1990-1999 Free Software Foundation, Inc.
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7  */
8 /* David MacKenzie <djm@gnu.ai.mit.edu>
9  *
10  * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
11  */
12 //config:config STTY
13 //config:	bool "stty (8.9 kb)"
14 //config:	default y
15 //config:	help
16 //config:	stty is used to change and print terminal line settings.
17 
18 //applet:IF_STTY(APPLET_NOEXEC(stty, stty, BB_DIR_BIN, BB_SUID_DROP, stty))
19 
20 //kbuild:lib-$(CONFIG_STTY) += stty.o
21 
22 //usage:#define stty_trivial_usage
23 //usage:       "[-a|g] [-F DEVICE] [SETTING]..."
24 //usage:#define stty_full_usage "\n\n"
25 //usage:       "Without arguments, prints baud rate, line discipline,\n"
26 //usage:       "and deviations from stty sane\n"
27 //usage:     "\n	-F DEVICE	Open device instead of stdin"
28 //usage:     "\n	-a		Print all current settings in human-readable form"
29 //usage:     "\n	-g		Print in stty-readable form"
30 //usage:     "\n	[SETTING]	See manpage"
31 
32 /* If no args are given, write to stdout the baud rate and settings that
33  * have been changed from their defaults.  Mode reading and changes
34  * are done on the specified device, or stdin if none was specified.
35  */
36 
37 #include "libbb.h"
38 #include "common_bufsiz.h"
39 
40 #ifndef _POSIX_VDISABLE
41 # define _POSIX_VDISABLE ((unsigned char) 0)
42 #endif
43 
44 #define Control(c) ((c) & 0x1f)
45 /* Canonical values for control characters */
46 #ifndef CINTR
47 # define CINTR Control('c')
48 #endif
49 #ifndef CQUIT
50 # define CQUIT 28
51 #endif
52 #ifndef CERASE
53 # define CERASE 127
54 #endif
55 #ifndef CKILL
56 # define CKILL Control('u')
57 #endif
58 #ifndef CEOF
59 # define CEOF Control('d')
60 #endif
61 #ifndef CEOL
62 # define CEOL _POSIX_VDISABLE
63 #endif
64 #ifndef CSTART
65 # define CSTART Control('q')
66 #endif
67 #ifndef CSTOP
68 # define CSTOP Control('s')
69 #endif
70 #ifndef CSUSP
71 # define CSUSP Control('z')
72 #endif
73 #if defined(VEOL2) && !defined(CEOL2)
74 # define CEOL2 _POSIX_VDISABLE
75 #endif
76 /* glibc-2.12.1 uses only VSWTC name */
77 #if defined(VSWTC) && !defined(VSWTCH)
78 # define VSWTCH VSWTC
79 #endif
80 /* ISC renamed swtch to susp for termios, but we'll accept either name */
81 #if defined(VSUSP) && !defined(VSWTCH)
82 # define VSWTCH VSUSP
83 # define CSWTCH CSUSP
84 #endif
85 #if defined(VSWTCH) && !defined(CSWTCH)
86 # define CSWTCH _POSIX_VDISABLE
87 #endif
88 
89 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
90    So the default is to disable 'swtch.'  */
91 #if defined(__sparc__) && defined(__svr4__)
92 # undef CSWTCH
93 # define CSWTCH _POSIX_VDISABLE
94 #endif
95 
96 #if defined(VWERSE) && !defined(VWERASE)       /* AIX-3.2.5 */
97 # define VWERASE VWERSE
98 #endif
99 #if defined(VDSUSP) && !defined(CDSUSP)
100 # define CDSUSP Control('y')
101 #endif
102 #if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
103 # define VREPRINT VRPRNT
104 #endif
105 #if defined(VREPRINT) && !defined(CRPRNT)
106 # define CRPRNT Control('r')
107 #endif
108 #if defined(VWERASE) && !defined(CWERASE)
109 # define CWERASE Control('w')
110 #endif
111 #if defined(VLNEXT) && !defined(CLNEXT)
112 # define CLNEXT Control('v')
113 #endif
114 #if defined(VDISCARD) && !defined(VFLUSHO)
115 # define VFLUSHO VDISCARD
116 #endif
117 #if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
118 # define VFLUSHO VFLUSH
119 #endif
120 #if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
121 # define ECHOCTL CTLECH
122 #endif
123 #if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
124 # define ECHOCTL TCTLECH
125 #endif
126 #if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
127 # define ECHOKE CRTKIL
128 #endif
129 #if defined(VFLUSHO) && !defined(CFLUSHO)
130 # define CFLUSHO Control('o')
131 #endif
132 #if defined(VSTATUS) && !defined(CSTATUS)
133 # define CSTATUS Control('t')
134 #endif
135 
136 /* Save us from #ifdef forest plague */
137 #ifndef BSDLY
138 # define BSDLY 0
139 #endif
140 #ifndef CIBAUD
141 # define CIBAUD 0
142 #endif
143 #ifndef CRDLY
144 # define CRDLY 0
145 #endif
146 #ifndef CMSPAR
147 # define CMSPAR 0
148 #endif
149 #ifndef CRTSCTS
150 # define CRTSCTS 0
151 #endif
152 #ifndef ECHOCTL
153 # define ECHOCTL 0
154 #endif
155 #ifndef ECHOKE
156 # define ECHOKE 0
157 #endif
158 #ifndef ECHOPRT
159 # define ECHOPRT 0
160 #endif
161 #ifndef FFDLY
162 # define FFDLY 0
163 #endif
164 #ifndef IEXTEN
165 # define IEXTEN 0
166 #endif
167 #ifndef IMAXBEL
168 # define IMAXBEL 0
169 #endif
170 #ifndef IUCLC
171 # define IUCLC 0
172 #endif
173 #ifndef IXANY
174 # define IXANY 0
175 #endif
176 #ifndef NLDLY
177 # define NLDLY 0
178 #endif
179 #ifndef OCRNL
180 # define OCRNL 0
181 #endif
182 #ifndef OFDEL
183 # define OFDEL 0
184 #endif
185 #ifndef OFILL
186 # define OFILL 0
187 #endif
188 #ifndef OLCUC
189 # define OLCUC 0
190 #endif
191 #ifndef ONLCR
192 # define ONLCR 0
193 #endif
194 #ifndef ONLRET
195 # define ONLRET 0
196 #endif
197 #ifndef ONOCR
198 # define ONOCR 0
199 #endif
200 #ifndef OXTABS
201 # define OXTABS 0
202 #endif
203 #ifndef TABDLY
204 # define TABDLY 0
205 #endif
206 #ifndef TAB1
207 # define TAB1 0
208 #endif
209 #ifndef TAB2
210 # define TAB2 0
211 #endif
212 #ifndef TOSTOP
213 # define TOSTOP 0
214 #endif
215 #ifndef VDSUSP
216 # define VDSUSP 0
217 #endif
218 #ifndef VEOL2
219 # define VEOL2 0
220 #endif
221 #ifndef VFLUSHO
222 # define VFLUSHO 0
223 #endif
224 #ifndef VLNEXT
225 # define VLNEXT 0
226 #endif
227 #ifndef VREPRINT
228 # define VREPRINT 0
229 #endif
230 #ifndef VSTATUS
231 # define VSTATUS 0
232 #endif
233 #ifndef VSWTCH
234 # define VSWTCH 0
235 #endif
236 #ifndef VTDLY
237 # define VTDLY 0
238 #endif
239 #ifndef VWERASE
240 # define VWERASE 0
241 #endif
242 #ifndef XCASE
243 # define XCASE 0
244 #endif
245 #ifndef IUTF8
246 # define IUTF8 0
247 #endif
248 
249 /* Which speeds to set */
250 enum speed_setting {
251 	input_speed, output_speed, both_speeds
252 };
253 
254 /* Which member(s) of 'struct termios' a mode uses */
255 enum {
256 	control, input, output, local, combination
257 };
get_ptr_to_tcflag(unsigned type,const struct termios * mode)258 static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
259 {
260 	static const uint8_t tcflag_offsets[] ALIGN1 = {
261 		offsetof(struct termios, c_cflag), /* control */
262 		offsetof(struct termios, c_iflag), /* input */
263 		offsetof(struct termios, c_oflag), /* output */
264 		offsetof(struct termios, c_lflag)  /* local */
265 	};
266 	if (type <= local) {
267 		return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
268 	}
269 	return NULL;
270 }
271 
272 /* Flags for 'struct mode_info' */
273 #define SANE_SET 1              /* Set in 'sane' mode                  */
274 #define SANE_UNSET 2            /* Unset in 'sane' mode                */
275 #define REV 4                   /* Can be turned off by prepending '-' */
276 #define OMIT 8                  /* Don't display value                 */
277 
278 
279 /* Each mode.
280  * This structure should be kept as small as humanly possible.
281  */
282 struct mode_info {
283 	const uint8_t type;           /* Which structure element to change    */
284 	const uint8_t flags;          /* Setting and display options          */
285 	/* only these values are ever used, so... */
286 #if   (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
287 	const uint8_t mask;
288 #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
289 	const uint16_t mask;
290 #else
291 	const tcflag_t mask;          /* Other bits to turn off for this mode */
292 #endif
293 	/* was using short here, but ppc32 was unhappy */
294 	const tcflag_t bits;          /* Bits to set for this mode            */
295 };
296 
297 enum {
298 	/* Must match mode_name[] and mode_info[] order! */
299 	IDX_evenp = 0,
300 	IDX_parity,
301 	IDX_oddp,
302 	IDX_nl,
303 	IDX_ek,
304 	IDX_sane,
305 	IDX_cooked,
306 	IDX_raw,
307 	IDX_pass8,
308 	IDX_litout,
309 	IDX_cbreak,
310 	IDX_crt,
311 	IDX_dec,
312 #if IXANY
313 	IDX_decctlq,
314 #endif
315 #if TABDLY || OXTABS
316 	IDX_tabs,
317 #endif
318 #if XCASE && IUCLC && OLCUC
319 	IDX_lcase,
320 	IDX_LCASE,
321 #endif
322 };
323 
324 #define MI_ENTRY(N,T,F,B,M) N "\0"
325 
326 /* Mode names given on command line */
327 static const char mode_name[] ALIGN1 =
328 	MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
329 	MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
330 	MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
331 	MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
332 	MI_ENTRY("ek",       combination, OMIT,              0,          0 )
333 	MI_ENTRY("sane",     combination, OMIT,              0,          0 )
334 	MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
335 	MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
336 	MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
337 	MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
338 	MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
339 	MI_ENTRY("crt",      combination, OMIT,              0,          0 )
340 	MI_ENTRY("dec",      combination, OMIT,              0,          0 )
341 #if IXANY
342 	MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
343 #endif
344 #if TABDLY || OXTABS
345 	MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
346 #endif
347 #if XCASE && IUCLC && OLCUC
348 	MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
349 	MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
350 #endif
351 	MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
352 	MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
353 #if CMSPAR
354 	MI_ENTRY("cmspar",   control,     REV,               CMSPAR,     0 )
355 #endif
356 	MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
357 	MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
358 	MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
359 	MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
360 	MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
361 	MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
362 	MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
363 	MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
364 	MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
365 #if CRTSCTS
366 	MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
367 #endif
368 	MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
369 	MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
370 	MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
371 	MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
372 	MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
373 	MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
374 	MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
375 	MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
376 	MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
377 	MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
378 	MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
379 	MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
380 #if IUCLC
381 	MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
382 #endif
383 #if IXANY
384 	MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
385 #endif
386 #if IMAXBEL
387 	MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
388 #endif
389 #if IUTF8
390 	MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
391 #endif
392 	MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
393 #if OLCUC
394 	MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
395 #endif
396 #if OCRNL
397 	MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
398 #endif
399 #if ONLCR
400 	MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
401 #endif
402 #if ONOCR
403 	MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
404 #endif
405 #if ONLRET
406 	MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
407 #endif
408 #if OFILL
409 	MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
410 #endif
411 #if OFDEL
412 	MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
413 #endif
414 #if NLDLY
415 	MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
416 	MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
417 #endif
418 #if CRDLY
419 	MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
420 	MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
421 	MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
422 	MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
423 #endif
424 
425 #if TABDLY
426 	MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
427 # if TAB2
428 	MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
429 # endif
430 # if TAB1
431 	MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
432 # endif
433 	MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
434 #else
435 # if OXTABS
436 	MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
437 # endif
438 #endif
439 
440 #if BSDLY
441 	MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
442 	MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
443 #endif
444 #if VTDLY
445 	MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
446 	MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
447 #endif
448 #if FFDLY
449 	MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
450 	MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
451 #endif
452 	MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
453 	MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
454 #if IEXTEN
455 	MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
456 #endif
457 	MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
458 	MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
459 	MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
460 	MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
461 	MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
462 	MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
463 #if XCASE
464 	MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
465 #endif
466 #if TOSTOP
467 	MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
468 #endif
469 #if ECHOPRT
470 	MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
471 	MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
472 #endif
473 #if ECHOCTL
474 	MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
475 	MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
476 #endif
477 #if ECHOKE
478 	MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
479 	MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
480 #endif
481 	MI_ENTRY("flusho",   local,       SANE_UNSET | REV,  FLUSHO,     0 )
482 #ifdef EXTPROC
483 	MI_ENTRY("extproc",  local,       SANE_UNSET | REV,  EXTPROC,    0 )
484 #endif
485 	;
486 
487 #undef MI_ENTRY
488 #define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
489 
490 static const struct mode_info mode_info[] ALIGN4 = {
491 	/* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
492 	MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
493 	MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
494 	MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
495 	MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
496 	MI_ENTRY("ek",       combination, OMIT,              0,          0 )
497 	MI_ENTRY("sane",     combination, OMIT,              0,          0 )
498 	MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
499 	MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
500 	MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
501 	MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
502 	MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
503 	MI_ENTRY("crt",      combination, OMIT,              0,          0 )
504 	MI_ENTRY("dec",      combination, OMIT,              0,          0 )
505 #if IXANY
506 	MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
507 #endif
508 #if TABDLY || OXTABS
509 	MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
510 #endif
511 #if XCASE && IUCLC && OLCUC
512 	MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
513 	MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
514 #endif
515 	MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
516 	MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
517 #if CMSPAR
518 	MI_ENTRY("cmspar",   control,     REV,               CMSPAR,     0 )
519 #endif
520 	MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
521 	MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
522 	MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
523 	MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
524 	MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
525 	MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
526 	MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
527 	MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
528 	MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
529 #if CRTSCTS
530 	MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
531 #endif
532 	MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
533 	MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
534 	MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
535 	MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
536 	MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
537 	MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
538 	MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
539 	MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
540 	MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
541 	MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
542 	MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
543 	MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
544 #if IUCLC
545 	MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
546 #endif
547 #if IXANY
548 	MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
549 #endif
550 #if IMAXBEL
551 	MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
552 #endif
553 #if IUTF8
554 	MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
555 #endif
556 	MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
557 #if OLCUC
558 	MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
559 #endif
560 #if OCRNL
561 	MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
562 #endif
563 #if ONLCR
564 	MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
565 #endif
566 #if ONOCR
567 	MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
568 #endif
569 #if ONLRET
570 	MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
571 #endif
572 #if OFILL
573 	MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
574 #endif
575 #if OFDEL
576 	MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
577 #endif
578 #if NLDLY
579 	MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
580 	MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
581 #endif
582 #if CRDLY
583 	MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
584 	MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
585 	MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
586 	MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
587 #endif
588 
589 #if TABDLY
590 	MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
591 # if TAB2
592 	MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
593 # endif
594 # if TAB1
595 	MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
596 # endif
597 	MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
598 #else
599 # if OXTABS
600 	MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
601 # endif
602 #endif
603 
604 #if BSDLY
605 	MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
606 	MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
607 #endif
608 #if VTDLY
609 	MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
610 	MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
611 #endif
612 #if FFDLY
613 	MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
614 	MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
615 #endif
616 	MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
617 	MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
618 #if IEXTEN
619 	MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
620 #endif
621 	MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
622 	MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
623 	MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
624 	MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
625 	MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
626 	MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
627 #if XCASE
628 	MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
629 #endif
630 #if TOSTOP
631 	MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
632 #endif
633 #if ECHOPRT
634 	MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
635 	MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
636 #endif
637 #if ECHOCTL
638 	MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
639 	MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
640 #endif
641 #if ECHOKE
642 	MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
643 	MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
644 #endif
645 	MI_ENTRY("flusho",   local,       SANE_UNSET | REV,  FLUSHO,     0 )
646 #ifdef EXTPROC
647 	MI_ENTRY("extproc",  local,       SANE_UNSET | REV,  EXTPROC,    0 )
648 #endif
649 };
650 
651 enum {
652 	NUM_mode_info = ARRAY_SIZE(mode_info)
653 };
654 
655 
656 /* Control characters */
657 struct control_info {
658 	const uint8_t saneval;  /* Value to set for 'stty sane' */
659 	const uint8_t offset;   /* Offset in c_cc */
660 };
661 
662 enum {
663 	/* Must match control_name[] and control_info[] order! */
664 	CIDX_intr = 0,
665 	CIDX_quit,
666 	CIDX_erase,
667 	CIDX_kill,
668 	CIDX_eof,
669 	CIDX_eol,
670 #if VEOL2
671 	CIDX_eol2,
672 #endif
673 #if VSWTCH
674 	CIDX_swtch,
675 #endif
676 	CIDX_start,
677 	CIDX_stop,
678 	CIDX_susp,
679 #if VDSUSP
680 	CIDX_dsusp,
681 #endif
682 #if VREPRINT
683 	CIDX_rprnt,
684 #endif
685 #if VWERASE
686 	CIDX_werase,
687 #endif
688 #if VLNEXT
689 	CIDX_lnext,
690 #endif
691 #if VFLUSHO
692 	CIDX_flush,
693 #endif
694 #if VSTATUS
695 	CIDX_status,
696 #endif
697 	CIDX_min,
698 	CIDX_time,
699 };
700 
701 #define CI_ENTRY(n,s,o) n "\0"
702 
703 /* Name given on command line */
704 static const char control_name[] ALIGN1 =
705 	CI_ENTRY("intr",     CINTR,   VINTR   )
706 	CI_ENTRY("quit",     CQUIT,   VQUIT   )
707 	CI_ENTRY("erase",    CERASE,  VERASE  )
708 	CI_ENTRY("kill",     CKILL,   VKILL   )
709 	CI_ENTRY("eof",      CEOF,    VEOF    )
710 	CI_ENTRY("eol",      CEOL,    VEOL    )
711 #if VEOL2
712 	CI_ENTRY("eol2",     CEOL2,   VEOL2   )
713 #endif
714 #if VSWTCH
715 	CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
716 #endif
717 	CI_ENTRY("start",    CSTART,  VSTART  )
718 	CI_ENTRY("stop",     CSTOP,   VSTOP   )
719 	CI_ENTRY("susp",     CSUSP,   VSUSP   )
720 #if VDSUSP
721 	CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
722 #endif
723 #if VREPRINT
724 	CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
725 #endif
726 #if VWERASE
727 	CI_ENTRY("werase",   CWERASE, VWERASE )
728 #endif
729 #if VLNEXT
730 	CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
731 #endif
732 #if VFLUSHO
733 	CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
734 #endif
735 #if VSTATUS
736 	CI_ENTRY("status",   CSTATUS, VSTATUS )
737 #endif
738 	/* These must be last because of the display routines */
739 	CI_ENTRY("min",      1,       VMIN    )
740 	CI_ENTRY("time",     0,       VTIME   )
741 	;
742 
743 #undef CI_ENTRY
744 #define CI_ENTRY(n,s,o) { s, o },
745 
746 static const struct control_info control_info[] ALIGN2 = {
747 	/* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
748 	CI_ENTRY("intr",     CINTR,   VINTR   )
749 	CI_ENTRY("quit",     CQUIT,   VQUIT   )
750 	CI_ENTRY("erase",    CERASE,  VERASE  )
751 	CI_ENTRY("kill",     CKILL,   VKILL   )
752 	CI_ENTRY("eof",      CEOF,    VEOF    )
753 	CI_ENTRY("eol",      CEOL,    VEOL    )
754 #if VEOL2
755 	CI_ENTRY("eol2",     CEOL2,   VEOL2   )
756 #endif
757 #if VSWTCH
758 	CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
759 #endif
760 	CI_ENTRY("start",    CSTART,  VSTART  )
761 	CI_ENTRY("stop",     CSTOP,   VSTOP   )
762 	CI_ENTRY("susp",     CSUSP,   VSUSP   )
763 #if VDSUSP
764 	CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
765 #endif
766 #if VREPRINT
767 	CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
768 #endif
769 #if VWERASE
770 	CI_ENTRY("werase",   CWERASE, VWERASE )
771 #endif
772 #if VLNEXT
773 	CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
774 #endif
775 #if VFLUSHO
776 	CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
777 #endif
778 #if VSTATUS
779 	CI_ENTRY("status",   CSTATUS, VSTATUS )
780 #endif
781 	/* These must be last because of the display routines */
782 	CI_ENTRY("min",      1,       VMIN    )
783 	CI_ENTRY("time",     0,       VTIME   )
784 };
785 
786 enum {
787 	NUM_control_info = ARRAY_SIZE(control_info)
788 };
789 
790 
791 struct globals {
792 	const char *device_name;
793 	/* The width of the screen, for output wrapping */
794 	unsigned max_col;
795 	/* Current position, to know when to wrap */
796 	unsigned current_col;
797 } FIX_ALIASING;
798 #define G (*(struct globals*)bb_common_bufsiz1)
799 #define INIT_G() do { \
800 	setup_common_bufsiz(); \
801 	G.device_name = bb_msg_standard_input; \
802 	G.max_col = 80; \
803 	G.current_col = 0; /* we are noexec, must clear */ \
804 } while (0)
805 
set_speed_or_die(enum speed_setting type,const char * arg,struct termios * mode)806 static void set_speed_or_die(enum speed_setting type, const char *arg,
807 					struct termios *mode)
808 {
809 	speed_t baud;
810 
811 	baud = tty_value_to_baud(xatou(arg));
812 
813 	if (type != output_speed) {     /* either input or both */
814 		cfsetispeed(mode, baud);
815 	}
816 	if (type != input_speed) {      /* either output or both */
817 		cfsetospeed(mode, baud);
818 	}
819 }
820 
perror_on_device_and_die(const char * fmt)821 static NORETURN void perror_on_device_and_die(const char *fmt)
822 {
823 	bb_perror_msg_and_die(fmt, G.device_name);
824 }
825 
perror_on_device(const char * fmt)826 static void perror_on_device(const char *fmt)
827 {
828 	bb_perror_msg(fmt, G.device_name);
829 }
830 
831 /* Print format string MESSAGE and optional args.
832    Wrap to next line first if it won't fit.
833    Print a space first unless MESSAGE will start a new line */
wrapf(const char * message,...)834 static void wrapf(const char *message, ...)
835 {
836 	char buf[128];
837 	va_list args;
838 	unsigned buflen;
839 
840 	va_start(args, message);
841 	buflen = vsnprintf(buf, sizeof(buf), message, args);
842 	va_end(args);
843 	/* We seem to be called only with suitable lengths, but check if
844 	   somebody failed to adhere to this assumption just to be sure.  */
845 	if (!buflen || buflen >= sizeof(buf)) return;
846 
847 	if (G.current_col > 0) {
848 		G.current_col++;
849 		if (buf[0] != '\n') {
850 			if (G.current_col + buflen >= G.max_col) {
851 				G.current_col = 0;
852 				bb_putchar('\n');
853 			} else {
854 				bb_putchar(' ');
855 			}
856 		}
857 	}
858 	fputs_stdout(buf);
859 	G.current_col += buflen;
860 	if (buf[buflen-1] == '\n')
861 		G.current_col = 0;
862 }
863 
newline(void)864 static void newline(void)
865 {
866 	if (G.current_col != 0)
867 		wrapf("\n");
868 }
869 
870 #ifdef TIOCGWINSZ
set_window_size(int rows,int cols)871 static void set_window_size(int rows, int cols)
872 {
873 	struct winsize win = { 0, 0, 0, 0 };
874 
875 	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
876 		if (errno != EINVAL) {
877 			goto bail;
878 		}
879 		memset(&win, 0, sizeof(win));
880 	}
881 
882 	if (rows >= 0)
883 		win.ws_row = rows;
884 	if (cols >= 0)
885 		win.ws_col = cols;
886 
887 	if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
888 bail:
889 		perror_on_device("%s");
890 }
891 #endif
892 
display_window_size(int fancy)893 static void display_window_size(int fancy)
894 {
895 	const char *fmt_str = "%s\0%s: no size information for this device";
896 	unsigned width, height;
897 
898 	if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
899 		if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
900 			perror_on_device(fmt_str);
901 		}
902 	} else {
903 		wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
904 				height, width);
905 	}
906 }
907 
908 static const struct suffix_mult stty_suffixes[] ALIGN_SUFFIX = {
909 	{ "b",  512 },
910 	{ "k", 1024 },
911 	{ "B", 1024 },
912 	{ "", 0 }
913 };
914 
find_mode(const char * name)915 static const struct mode_info *find_mode(const char *name)
916 {
917 	int i = index_in_strings(mode_name, name);
918 	return i >= 0 ? &mode_info[i] : NULL;
919 }
920 
find_control(const char * name)921 static const struct control_info *find_control(const char *name)
922 {
923 	int i = index_in_strings(control_name, name);
924 	return i >= 0 ? &control_info[i] : NULL;
925 }
926 
927 enum {
928 	param_need_arg = 0x80,
929 	param_line    = 1 | 0x80,
930 	param_rows    = 2 | 0x80,
931 	param_cols    = 3 | 0x80,
932 	param_columns = 4 | 0x80,
933 	param_size    = 5,
934 	param_speed   = 6,
935 	param_ispeed  = 7 | 0x80,
936 	param_ospeed  = 8 | 0x80,
937 };
938 
find_param(const char * name)939 static int find_param(const char *name)
940 {
941 	static const char params[] ALIGN1 =
942 		"line\0"    /* 1 */
943 		"rows\0"    /* 2 */
944 		"cols\0"    /* 3 */
945 		"columns\0" /* 4 */
946 		"size\0"    /* 5 */
947 		"speed\0"   /* 6 */
948 		"ispeed\0"
949 		"ospeed\0";
950 	int i = index_in_strings(params, name) + 1;
951 	if (i == 0)
952 		return 0;
953 	if (i != 5 && i != 6)
954 		i |= 0x80;
955 	return i;
956 }
957 
recover_mode(const char * arg,struct termios * mode)958 static int recover_mode(const char *arg, struct termios *mode)
959 {
960 	int i, n;
961 	unsigned chr;
962 	unsigned long iflag, oflag, cflag, lflag;
963 
964 	/* Scan into temporaries since it is too much trouble to figure out
965 	   the right format for 'tcflag_t' */
966 	if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
967 			   &iflag, &oflag, &cflag, &lflag, &n) != 4)
968 		return 0;
969 	mode->c_iflag = iflag;
970 	mode->c_oflag = oflag;
971 	mode->c_cflag = cflag;
972 	mode->c_lflag = lflag;
973 	arg += n;
974 	for (i = 0; i < NCCS; ++i) {
975 		if (sscanf(arg, ":%x%n", &chr, &n) != 1)
976 			return 0;
977 		mode->c_cc[i] = chr;
978 		arg += n;
979 	}
980 
981 	/* Fail if there are too many fields */
982 	if (*arg != '\0')
983 		return 0;
984 
985 	return 1;
986 }
987 
display_recoverable(const struct termios * mode,int UNUSED_PARAM dummy)988 static void display_recoverable(const struct termios *mode,
989 				int UNUSED_PARAM dummy)
990 {
991 	int i;
992 	printf("%lx:%lx:%lx:%lx",
993 		   (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
994 		   (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
995 	for (i = 0; i < NCCS; ++i)
996 		printf(":%x", (unsigned int) mode->c_cc[i]);
997 	bb_putchar('\n');
998 }
999 
display_speed(const struct termios * mode,int fancy)1000 static void display_speed(const struct termios *mode, int fancy)
1001 {
1002 	//____________________ 01234567 8 9
1003 	const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1004 	unsigned long ispeed, ospeed;
1005 
1006 	ispeed = cfgetispeed(mode);
1007 	ospeed = cfgetospeed(mode);
1008 	if (ispeed == 0 || ispeed == ospeed) {
1009 		ispeed = ospeed;                /* in case ispeed was 0 */
1010 		//________ 0123 4 5 6 7 8 9
1011 		fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1012 	}
1013 	if (fancy) fmt_str += 9;
1014 	wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1015 }
1016 
do_display(const struct termios * mode,int all)1017 static void do_display(const struct termios *mode, int all)
1018 {
1019 	int i;
1020 	tcflag_t *bitsp;
1021 	unsigned long mask;
1022 	int prev_type = control;
1023 
1024 	display_speed(mode, 1);
1025 	if (all)
1026 		display_window_size(1);
1027 #ifdef __linux__
1028 	wrapf("line = %u;\n", mode->c_line);
1029 #else
1030 	newline();
1031 #endif
1032 
1033 	for (i = 0; i != CIDX_min; ++i) {
1034 		char ch;
1035 		char buf10[10];
1036 
1037 		/* If swtch is the same as susp, don't print both */
1038 #if VSWTCH == VSUSP
1039 		if (i == CIDX_swtch)
1040 			continue;
1041 #endif
1042 		/* If eof uses the same slot as min, only print whichever applies */
1043 #if VEOF == VMIN
1044 		if (!(mode->c_lflag & ICANON)
1045 		 && (i == CIDX_eof || i == CIDX_eol)
1046 		) {
1047 			continue;
1048 		}
1049 #endif
1050 		ch = mode->c_cc[control_info[i].offset];
1051 		if (ch == _POSIX_VDISABLE)
1052 			strcpy(buf10, "<undef>");
1053 		else
1054 			visible(ch, buf10, 0);
1055 		wrapf("%s = %s;", nth_string(control_name, i), buf10);
1056 	}
1057 #if VEOF == VMIN
1058 	if ((mode->c_lflag & ICANON) == 0)
1059 #endif
1060 		wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1061 	newline();
1062 
1063 	for (i = 0; i < NUM_mode_info; ++i) {
1064 		if (mode_info[i].flags & OMIT)
1065 			continue;
1066 		if (mode_info[i].type != prev_type) {
1067 			newline();
1068 			prev_type = mode_info[i].type;
1069 		}
1070 
1071 		bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1072 		mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1073 		if ((*bitsp & mask) == mode_info[i].bits) {
1074 			if (all || (mode_info[i].flags & SANE_UNSET))
1075 				wrapf("-%s"+1, nth_string(mode_name, i));
1076 		} else {
1077 			if ((all && mode_info[i].flags & REV)
1078 			 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1079 			) {
1080 				wrapf("-%s", nth_string(mode_name, i));
1081 			}
1082 		}
1083 	}
1084 	newline();
1085 }
1086 
sane_mode(struct termios * mode)1087 static void sane_mode(struct termios *mode)
1088 {
1089 	int i;
1090 
1091 	for (i = 0; i < NUM_control_info; ++i) {
1092 #if VMIN == VEOF
1093 		if (i == CIDX_min)
1094 			break;
1095 #endif
1096 		mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1097 	}
1098 
1099 	for (i = 0; i < NUM_mode_info; ++i) {
1100 		tcflag_t val;
1101 		tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
1102 
1103 		if (!bitsp)
1104 			continue;
1105 		val = *bitsp & ~((unsigned long)mode_info[i].mask);
1106 		if (mode_info[i].flags & SANE_SET) {
1107 			*bitsp = val | mode_info[i].bits;
1108 		} else
1109 		if (mode_info[i].flags & SANE_UNSET) {
1110 			*bitsp = val & ~mode_info[i].bits;
1111 		}
1112 	}
1113 }
1114 
set_mode(const struct mode_info * info,int reversed,struct termios * mode)1115 static void set_mode(const struct mode_info *info, int reversed,
1116 					struct termios *mode)
1117 {
1118 	tcflag_t *bitsp;
1119 
1120 	bitsp = get_ptr_to_tcflag(info->type, mode);
1121 
1122 	if (bitsp) {
1123 		tcflag_t val = *bitsp & ~info->mask;
1124 		if (reversed)
1125 			*bitsp = val & ~info->bits;
1126 		else
1127 			*bitsp = val | info->bits;
1128 		return;
1129 	}
1130 
1131 	/* !bitsp - it's a "combination" mode */
1132 	if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1133 		if (reversed)
1134 			mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1135 		else
1136 			mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1137 	} else if (info == &mode_info[IDX_oddp]) {
1138 		if (reversed)
1139 			mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1140 		else
1141 			mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1142 	} else if (info == &mode_info[IDX_nl]) {
1143 		if (reversed) {
1144 			mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1145 			mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1146 		} else {
1147 			mode->c_iflag = mode->c_iflag & ~ICRNL;
1148 			if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1149 		}
1150 	} else if (info == &mode_info[IDX_ek]) {
1151 		mode->c_cc[VERASE] = CERASE;
1152 		mode->c_cc[VKILL] = CKILL;
1153 	} else if (info == &mode_info[IDX_sane]) {
1154 		sane_mode(mode);
1155 	} else if (info == &mode_info[IDX_cbreak]) {
1156 		if (reversed)
1157 			mode->c_lflag |= ICANON;
1158 		else
1159 			mode->c_lflag &= ~ICANON;
1160 	} else if (info == &mode_info[IDX_pass8]) {
1161 		if (reversed) {
1162 			mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1163 			mode->c_iflag |= ISTRIP;
1164 		} else {
1165 			mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1166 			mode->c_iflag &= ~ISTRIP;
1167 		}
1168 	} else if (info == &mode_info[IDX_litout]) {
1169 		if (reversed) {
1170 			mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1171 			mode->c_iflag |= ISTRIP;
1172 			mode->c_oflag |= OPOST;
1173 		} else {
1174 			mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1175 			mode->c_iflag &= ~ISTRIP;
1176 			mode->c_oflag &= ~OPOST;
1177 		}
1178 	} else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1179 		if ((info == &mode_info[IDX_raw] && reversed)
1180 		 || (info == &mode_info[IDX_cooked] && !reversed)
1181 		) {
1182 			/* Cooked mode */
1183 			mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1184 			mode->c_oflag |= OPOST;
1185 			mode->c_lflag |= ISIG | ICANON;
1186 #if VMIN == VEOF
1187 			mode->c_cc[VEOF] = CEOF;
1188 #endif
1189 #if VTIME == VEOL
1190 			mode->c_cc[VEOL] = CEOL;
1191 #endif
1192 		} else {
1193 			/* Raw mode */
1194 			mode->c_iflag = 0;
1195 			mode->c_oflag &= ~OPOST;
1196 			mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1197 			mode->c_cc[VMIN] = 1;
1198 			mode->c_cc[VTIME] = 0;
1199 		}
1200 	}
1201 #if IXANY
1202 	else if (info == &mode_info[IDX_decctlq]) {
1203 		if (reversed)
1204 			mode->c_iflag |= IXANY;
1205 		else
1206 			mode->c_iflag &= ~IXANY;
1207 	}
1208 #endif
1209 #if TABDLY
1210 	else if (info == &mode_info[IDX_tabs]) {
1211 		if (reversed)
1212 			mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1213 		else
1214 			mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1215 	}
1216 #endif
1217 #if OXTABS
1218 	else if (info == &mode_info[IDX_tabs]) {
1219 		if (reversed)
1220 			mode->c_oflag |= OXTABS;
1221 		else
1222 			mode->c_oflag &= ~OXTABS;
1223 	}
1224 #endif
1225 #if XCASE && IUCLC && OLCUC
1226 	else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1227 		if (reversed) {
1228 			mode->c_lflag &= ~XCASE;
1229 			mode->c_iflag &= ~IUCLC;
1230 			mode->c_oflag &= ~OLCUC;
1231 		} else {
1232 			mode->c_lflag |= XCASE;
1233 			mode->c_iflag |= IUCLC;
1234 			mode->c_oflag |= OLCUC;
1235 		}
1236 	}
1237 #endif
1238 	else if (info == &mode_info[IDX_crt]) {
1239 		mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1240 	} else if (info == &mode_info[IDX_dec]) {
1241 		mode->c_cc[VINTR] = 3; /* ^C */
1242 		mode->c_cc[VERASE] = 127; /* DEL */
1243 		mode->c_cc[VKILL] = 21; /* ^U */
1244 		mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1245 		if (IXANY) mode->c_iflag &= ~IXANY;
1246 	}
1247 }
1248 
set_control_char_or_die(const struct control_info * info,const char * arg,struct termios * mode)1249 static void set_control_char_or_die(const struct control_info *info,
1250 			const char *arg, struct termios *mode)
1251 {
1252 	unsigned char value;
1253 
1254 	if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1255 		value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1256 	else if (arg[0] == '\0' || arg[1] == '\0')
1257 		value = arg[0];
1258 	else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1259 		value = _POSIX_VDISABLE;
1260 	else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1261 		value = arg[1] & 0x1f; /* Non-letters get weird results */
1262 		if (arg[1] == '?')
1263 			value = 127;
1264 	} else
1265 		value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1266 	mode->c_cc[info->offset] = value;
1267 }
1268 
1269 #define STTY_require_set_attr   (1 << 0)
1270 #define STTY_speed_was_set      (1 << 1)
1271 #define STTY_verbose_output     (1 << 2)
1272 #define STTY_recoverable_output (1 << 3)
1273 #define STTY_noargs             (1 << 4)
1274 
1275 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
stty_main(int argc UNUSED_PARAM,char ** argv)1276 int stty_main(int argc UNUSED_PARAM, char **argv)
1277 {
1278 	struct termios mode;
1279 	void (*output_func)(const struct termios *, int);
1280 	const char *file_name = NULL;
1281 	int display_all = 0;
1282 	int stty_state;
1283 	int k;
1284 
1285 	INIT_G();
1286 
1287 	stty_state = STTY_noargs;
1288 	output_func = do_display;
1289 
1290 	/* First pass: only parse/verify command line params */
1291 	k = 0;
1292 	while (argv[++k]) {
1293 		const struct mode_info *mp;
1294 		const struct control_info *cp;
1295 		const char *arg = argv[k];
1296 		const char *argnext = argv[k+1];
1297 		int param;
1298 
1299 		if (arg[0] == '-') {
1300 			int i;
1301 			mp = find_mode(arg+1);
1302 			if (mp) {
1303 				if (!(mp->flags & REV))
1304 					goto invalid_argument;
1305 				stty_state &= ~STTY_noargs;
1306 				continue;
1307 			}
1308 			/* It is an option - parse it */
1309 			i = 0;
1310 			while (arg[++i]) {
1311 				switch (arg[i]) {
1312 				case 'a':
1313 					stty_state |= STTY_verbose_output;
1314 					output_func = do_display;
1315 					display_all = 1;
1316 					break;
1317 				case 'g':
1318 					stty_state |= STTY_recoverable_output;
1319 					output_func = display_recoverable;
1320 					break;
1321 				case 'F':
1322 					if (file_name)
1323 						bb_simple_error_msg_and_die("only one device may be specified");
1324 					file_name = &arg[i+1]; /* "-Fdevice" ? */
1325 					if (!file_name[0]) { /* nope, "-F device" */
1326 						int p = k+1; /* argv[p] is argnext */
1327 						file_name = argnext;
1328 						if (!file_name)
1329 							bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1330 						/* remove -F param from arg[vc] */
1331 						while (argv[p]) {
1332 							argv[p] = argv[p+1];
1333 							++p;
1334 						}
1335 					}
1336 					goto end_option;
1337 				default:
1338 					goto invalid_argument;
1339 				}
1340 			}
1341  end_option:
1342 			continue;
1343 		}
1344 
1345 		mp = find_mode(arg);
1346 		if (mp) {
1347 			stty_state &= ~STTY_noargs;
1348 			continue;
1349 		}
1350 
1351 		cp = find_control(arg);
1352 		if (cp) {
1353 			if (!argnext)
1354 				bb_error_msg_and_die(bb_msg_requires_arg, arg);
1355 			/* called for the side effect of xfunc death only */
1356 			set_control_char_or_die(cp, argnext, &mode);
1357 			stty_state &= ~STTY_noargs;
1358 			++k;
1359 			continue;
1360 		}
1361 
1362 		param = find_param(arg);
1363 		if (param & param_need_arg) {
1364 			if (!argnext)
1365 				bb_error_msg_and_die(bb_msg_requires_arg, arg);
1366 			++k;
1367 		}
1368 
1369 		switch (param) {
1370 #ifdef __linux__
1371 		case param_line:
1372 # ifndef TIOCGWINSZ
1373 			xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1374 			break;
1375 # endif /* else fall-through */
1376 #endif
1377 #ifdef TIOCGWINSZ
1378 		case param_rows:
1379 		case param_cols:
1380 		case param_columns:
1381 			xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1382 			break;
1383 		case param_size:
1384 #endif
1385 		case param_speed:
1386 			break;
1387 		case param_ispeed:
1388 			/* called for the side effect of xfunc death only */
1389 			set_speed_or_die(input_speed, argnext, &mode);
1390 			break;
1391 		case param_ospeed:
1392 			/* called for the side effect of xfunc death only */
1393 			set_speed_or_die(output_speed, argnext, &mode);
1394 			break;
1395 		default:
1396 			if (recover_mode(arg, &mode) == 1) break;
1397 			if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1398  invalid_argument:
1399 			bb_error_msg_and_die("invalid argument '%s'", arg);
1400 		}
1401 		stty_state &= ~STTY_noargs;
1402 	}
1403 
1404 	/* Specifying both -a and -g is an error */
1405 	if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1406 		(STTY_verbose_output | STTY_recoverable_output)
1407 	) {
1408 		bb_simple_error_msg_and_die("-a and -g are mutually exclusive");
1409 	}
1410 	/* Specifying -a or -g with non-options is an error */
1411 	if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1412 	 && !(stty_state & STTY_noargs)
1413 	) {
1414 		bb_simple_error_msg_and_die("modes may not be set when -a or -g is used");
1415 	}
1416 
1417 	/* Now it is safe to start doing things */
1418 	if (file_name) {
1419 		G.device_name = file_name;
1420 		xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1421 		ndelay_off(STDIN_FILENO);
1422 	}
1423 
1424 	/* Initialize to all zeroes so there is no risk memcmp will report a
1425 	   spurious difference in an uninitialized portion of the structure */
1426 	memset(&mode, 0, sizeof(mode));
1427 	if (tcgetattr(STDIN_FILENO, &mode))
1428 		perror_on_device_and_die("%s");
1429 
1430 	if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1431 		G.max_col = get_terminal_width(STDOUT_FILENO);
1432 		output_func(&mode, display_all);
1433 		return EXIT_SUCCESS;
1434 	}
1435 
1436 	/* Second pass: perform actions */
1437 	k = 0;
1438 	while (argv[++k]) {
1439 		const struct mode_info *mp;
1440 		const struct control_info *cp;
1441 		const char *arg = argv[k];
1442 		const char *argnext = argv[k+1];
1443 		int param;
1444 
1445 		if (arg[0] == '-') {
1446 			mp = find_mode(arg+1);
1447 			if (mp) {
1448 				set_mode(mp, 1 /* reversed */, &mode);
1449 				stty_state |= STTY_require_set_attr;
1450 			}
1451 			/* It is an option - already parsed. Skip it */
1452 			continue;
1453 		}
1454 
1455 		mp = find_mode(arg);
1456 		if (mp) {
1457 			set_mode(mp, 0 /* non-reversed */, &mode);
1458 			stty_state |= STTY_require_set_attr;
1459 			continue;
1460 		}
1461 
1462 		cp = find_control(arg);
1463 		if (cp) {
1464 			++k;
1465 			set_control_char_or_die(cp, argnext, &mode);
1466 			stty_state |= STTY_require_set_attr;
1467 			continue;
1468 		}
1469 
1470 		param = find_param(arg);
1471 		if (param & param_need_arg) {
1472 			++k;
1473 		}
1474 
1475 		switch (param) {
1476 #ifdef __linux__
1477 		case param_line:
1478 			mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1479 			stty_state |= STTY_require_set_attr;
1480 			break;
1481 #endif
1482 #ifdef TIOCGWINSZ
1483 		case param_cols:
1484 		case param_columns:
1485 			set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1486 			break;
1487 		case param_size:
1488 			display_window_size(0);
1489 			break;
1490 		case param_rows:
1491 			set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1492 			break;
1493 #endif
1494 		case param_speed:
1495 			display_speed(&mode, 0);
1496 			break;
1497 		case param_ispeed:
1498 			set_speed_or_die(input_speed, argnext, &mode);
1499 			stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1500 			break;
1501 		case param_ospeed:
1502 			set_speed_or_die(output_speed, argnext, &mode);
1503 			stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1504 			break;
1505 		default:
1506 			if (recover_mode(arg, &mode) == 1)
1507 				stty_state |= STTY_require_set_attr;
1508 			else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1509 				set_speed_or_die(both_speeds, arg, &mode);
1510 				stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1511 			} /* else - impossible (caught in the first pass):
1512 				bb_error_msg_and_die("invalid argument '%s'", arg); */
1513 		}
1514 	}
1515 
1516 	if (stty_state & STTY_require_set_attr) {
1517 		struct termios new_mode;
1518 
1519 		if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1520 			perror_on_device_and_die("%s");
1521 
1522 		/* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1523 		   it performs *any* of the requested operations.  This means it
1524 		   can report 'success' when it has actually failed to perform
1525 		   some proper subset of the requested operations.  To detect
1526 		   this partial failure, get the current terminal attributes and
1527 		   compare them to the requested ones */
1528 
1529 		/* Initialize to all zeroes so there is no risk memcmp will report a
1530 		   spurious difference in an uninitialized portion of the structure */
1531 		memset(&new_mode, 0, sizeof(new_mode));
1532 		if (tcgetattr(STDIN_FILENO, &new_mode))
1533 			perror_on_device_and_die("%s");
1534 
1535 		if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1536 /*
1537  * I think the below chunk is not necessary on Linux.
1538  * If you are deleting it, also delete STTY_speed_was_set bit -
1539  * it is only ever checked here.
1540  */
1541 #if 0 /* was "if CIBAUD" */
1542 			/* SunOS 4.1.3 (at least) has the problem that after this sequence,
1543 			   tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1544 			   sometimes (m1 != m2).  The only difference is in the four bits
1545 			   of the c_cflag field corresponding to the baud rate.  To save
1546 			   Sun users a little confusion, don't report an error if this
1547 			   happens.  But suppress the error only if we haven't tried to
1548 			   set the baud rate explicitly -- otherwise we'd never give an
1549 			   error for a true failure to set the baud rate */
1550 
1551 			new_mode.c_cflag &= (~CIBAUD);
1552 			if ((stty_state & STTY_speed_was_set)
1553 			 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1554 #endif
1555 				perror_on_device_and_die("%s: cannot perform all requested operations");
1556 		}
1557 	}
1558 
1559 	return EXIT_SUCCESS;
1560 }
1561