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