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