stop using global variable needlessly
[oweals/busybox.git] / coreutils / stty.c
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 the GPL v2 or later, see the file LICENSE in this tarball.
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    */
23
24 #include "busybox.h"
25
26 #ifndef _POSIX_VDISABLE
27 # define _POSIX_VDISABLE ((unsigned char) 0)
28 #endif
29
30 #define Control(c) ((c) & 0x1f)
31 /* Canonical values for control characters */
32 #ifndef CINTR
33 # define CINTR Control('c')
34 #endif
35 #ifndef CQUIT
36 # define CQUIT 28
37 #endif
38 #ifndef CERASE
39 # define CERASE 127
40 #endif
41 #ifndef CKILL
42 # define CKILL Control('u')
43 #endif
44 #ifndef CEOF
45 # define CEOF Control('d')
46 #endif
47 #ifndef CEOL
48 # define CEOL _POSIX_VDISABLE
49 #endif
50 #ifndef CSTART
51 # define CSTART Control('q')
52 #endif
53 #ifndef CSTOP
54 # define CSTOP Control('s')
55 #endif
56 #ifndef CSUSP
57 # define CSUSP Control('z')
58 #endif
59 #if defined(VEOL2) && !defined(CEOL2)
60 # define CEOL2 _POSIX_VDISABLE
61 #endif
62 /* ISC renamed swtch to susp for termios, but we'll accept either name */
63 #if defined(VSUSP) && !defined(VSWTCH)
64 # define VSWTCH VSUSP
65 # define CSWTCH CSUSP
66 #endif
67 #if defined(VSWTCH) && !defined(CSWTCH)
68 # define CSWTCH _POSIX_VDISABLE
69 #endif
70
71 /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
72    So the default is to disable 'swtch.'  */
73 #if defined (__sparc__) && defined (__svr4__)
74 # undef CSWTCH
75 # define CSWTCH _POSIX_VDISABLE
76 #endif
77
78 #if defined(VWERSE) && !defined (VWERASE)       /* AIX-3.2.5 */
79 # define VWERASE VWERSE
80 #endif
81 #if defined(VDSUSP) && !defined (CDSUSP)
82 # define CDSUSP Control('y')
83 #endif
84 #if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
85 # define VREPRINT VRPRNT
86 #endif
87 #if defined(VREPRINT) && !defined(CRPRNT)
88 # define CRPRNT Control('r')
89 #endif
90 #if defined(VWERASE) && !defined(CWERASE)
91 # define CWERASE Control('w')
92 #endif
93 #if defined(VLNEXT) && !defined(CLNEXT)
94 # define CLNEXT Control('v')
95 #endif
96 #if defined(VDISCARD) && !defined(VFLUSHO)
97 # define VFLUSHO VDISCARD
98 #endif
99 #if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
100 # define VFLUSHO VFLUSH
101 #endif
102 #if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
103 # define ECHOCTL CTLECH
104 #endif
105 #if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
106 # define ECHOCTL TCTLECH
107 #endif
108 #if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
109 # define ECHOKE CRTKIL
110 #endif
111 #if defined(VFLUSHO) && !defined(CFLUSHO)
112 # define CFLUSHO Control('o')
113 #endif
114 #if defined(VSTATUS) && !defined(CSTATUS)
115 # define CSTATUS Control('t')
116 #endif
117
118 /* Which speeds to set */
119 enum speed_setting {
120         input_speed, output_speed, both_speeds
121 };
122
123 /* Which member(s) of 'struct termios' a mode uses */
124 enum {
125         /* Do NOT change the order or values, as mode_type_flag()
126          * depends on them */
127         control, input, output, local, combination
128 };
129
130 static const char evenp     [] = "evenp";
131 static const char raw       [] = "raw";
132 static const char stty_min  [] = "min";
133 static const char stty_time [] = "time";
134 static const char stty_swtch[] = "swtch";
135 static const char stty_eol  [] = "eol";
136 static const char stty_eof  [] = "eof";
137 static const char parity    [] = "parity";
138 static const char stty_oddp [] = "oddp";
139 static const char stty_nl   [] = "nl";
140 static const char stty_ek   [] = "ek";
141 static const char stty_sane [] = "sane";
142 static const char cbreak    [] = "cbreak";
143 static const char stty_pass8[] = "pass8";
144 static const char litout    [] = "litout";
145 static const char cooked    [] = "cooked";
146 static const char decctlq   [] = "decctlq";
147 static const char stty_tabs [] = "tabs";
148 static const char stty_lcase[] = "lcase";
149 static const char stty_LCASE[] = "LCASE";
150 static const char stty_crt  [] = "crt";
151 static const char stty_dec  [] = "dec";
152
153 /* Flags for 'struct mode_info' */
154 #define SANE_SET 1              /* Set in 'sane' mode                  */
155 #define SANE_UNSET 2            /* Unset in 'sane' mode                */
156 #define REV 4                   /* Can be turned off by prepending '-' */
157 #define OMIT 8                  /* Don't display value                 */
158
159 /* Each mode */
160 struct mode_info {
161         const char * const name;      /* Name given on command line           */
162         const unsigned char type;     /* Which structure element to change    */
163         const unsigned char flags;    /* Setting and display options          */
164         /* were using short here, but ppc32 was unhappy: */
165         const tcflag_t mask;          /* Other bits to turn off for this mode */
166         const tcflag_t bits;          /* Bits to set for this mode            */
167 };
168
169 /* We can optimize it further by using name[8] instead of char *name */
170 /* but beware of "if (info->name == evenp)" checks! */
171 /* Need to replace them with "if (info == &mode_info[EVENP_INDX])" */
172
173 #define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
174
175 static const struct mode_info mode_info[] = {
176         MI_ENTRY("parenb",   control,     REV,               PARENB,     0 ),
177         MI_ENTRY("parodd",   control,     REV,               PARODD,     0 ),
178         MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE),
179         MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE),
180         MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE),
181         MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE),
182         MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 ),
183         MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 ),
184         MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 ),
185         MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 ),
186         MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 ),
187 #ifdef CRTSCTS
188         MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 ),
189 #endif
190         MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 ),
191         MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 ),
192         MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 ),
193         MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 ),
194         MI_ENTRY("inpck",    input,       REV,               INPCK,      0 ),
195         MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 ),
196         MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 ),
197         MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 ),
198         MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 ),
199         MI_ENTRY("ixon",     input,       REV,               IXON,       0 ),
200         MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 ),
201         MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 ),
202 #ifdef IUCLC
203         MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 ),
204 #endif
205 #ifdef IXANY
206         MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 ),
207 #endif
208 #ifdef IMAXBEL
209         MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 ),
210 #endif
211         MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 ),
212 #ifdef OLCUC
213         MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 ),
214 #endif
215 #ifdef OCRNL
216         MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 ),
217 #endif
218 #ifdef ONLCR
219         MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 ),
220 #endif
221 #ifdef ONOCR
222         MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 ),
223 #endif
224 #ifdef ONLRET
225         MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 ),
226 #endif
227 #ifdef OFILL
228         MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 ),
229 #endif
230 #ifdef OFDEL
231         MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 ),
232 #endif
233 #ifdef NLDLY
234         MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY),
235         MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY),
236 #endif
237 #ifdef CRDLY
238         MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY),
239         MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY),
240         MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY),
241         MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY),
242 #endif
243
244 #ifdef TABDLY
245         MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY),
246         MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY),
247         MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY),
248         MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY),
249 #else
250 # ifdef OXTABS
251         MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 ),
252 # endif
253 #endif
254
255 #ifdef BSDLY
256         MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY),
257         MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY),
258 #endif
259 #ifdef VTDLY
260         MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY),
261         MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY),
262 #endif
263 #ifdef FFDLY
264         MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY),
265         MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY),
266 #endif
267         MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 ),
268         MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 ),
269 #ifdef IEXTEN
270         MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 ),
271 #endif
272         MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 ),
273         MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 ),
274         MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 ),
275         MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 ),
276         MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 ),
277         MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 ),
278 #ifdef XCASE
279         MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 ),
280 #endif
281 #ifdef TOSTOP
282         MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 ),
283 #endif
284 #ifdef ECHOPRT
285         MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 ),
286         MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 ),
287 #endif
288 #ifdef ECHOCTL
289         MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 ),
290         MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 ),
291 #endif
292 #ifdef ECHOKE
293         MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 ),
294         MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 ),
295 #endif
296         MI_ENTRY(evenp,      combination, REV        | OMIT, 0,          0 ),
297         MI_ENTRY(parity,     combination, REV        | OMIT, 0,          0 ),
298         MI_ENTRY(stty_oddp,  combination, REV        | OMIT, 0,          0 ),
299         MI_ENTRY(stty_nl,    combination, REV        | OMIT, 0,          0 ),
300         MI_ENTRY(stty_ek,    combination, OMIT,              0,          0 ),
301         MI_ENTRY(stty_sane,  combination, OMIT,              0,          0 ),
302         MI_ENTRY(cooked,     combination, REV        | OMIT, 0,          0 ),
303         MI_ENTRY(raw,        combination, REV        | OMIT, 0,          0 ),
304         MI_ENTRY(stty_pass8, combination, REV        | OMIT, 0,          0 ),
305         MI_ENTRY(litout,     combination, REV        | OMIT, 0,          0 ),
306         MI_ENTRY(cbreak,     combination, REV        | OMIT, 0,          0 ),
307 #ifdef IXANY
308         MI_ENTRY(decctlq,    combination, REV        | OMIT, 0,          0 ),
309 #endif
310 #if defined(TABDLY) || defined(OXTABS)
311         MI_ENTRY(stty_tabs,  combination, REV        | OMIT, 0,          0 ),
312 #endif
313 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
314         MI_ENTRY(stty_lcase, combination, REV        | OMIT, 0,          0 ),
315         MI_ENTRY(stty_LCASE, combination, REV        | OMIT, 0,          0 ),
316 #endif
317         MI_ENTRY(stty_crt,   combination, OMIT,              0,          0 ),
318         MI_ENTRY(stty_dec,   combination, OMIT,              0,          0 ),
319 };
320
321 enum {
322         NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
323 };
324
325 /* Control character settings */
326 struct control_info {
327         const char * const name;               /* Name given on command line */
328         const unsigned char saneval;          /* Value to set for 'stty sane' */
329         const unsigned char offset;           /* Offset in c_cc */
330 };
331
332 /* Control characters */
333
334 static const struct control_info control_info[] = {
335         {"intr",     CINTR,   VINTR},
336         {"quit",     CQUIT,   VQUIT},
337         {"erase",    CERASE,  VERASE},
338         {"kill",     CKILL,   VKILL},
339         {stty_eof,   CEOF,    VEOF},
340         {stty_eol,   CEOL,    VEOL},
341 #ifdef VEOL2
342         {"eol2",     CEOL2,   VEOL2},
343 #endif
344 #ifdef VSWTCH
345         {stty_swtch, CSWTCH,  VSWTCH},
346 #endif
347         {"start",    CSTART,  VSTART},
348         {"stop",     CSTOP,   VSTOP},
349         {"susp",     CSUSP,   VSUSP},
350 #ifdef VDSUSP
351         {"dsusp",    CDSUSP,  VDSUSP},
352 #endif
353 #ifdef VREPRINT
354         {"rprnt",    CRPRNT,  VREPRINT},
355 #endif
356 #ifdef VWERASE
357         {"werase",   CWERASE, VWERASE},
358 #endif
359 #ifdef VLNEXT
360         {"lnext",    CLNEXT,  VLNEXT},
361 #endif
362 #ifdef VFLUSHO
363         {"flush",    CFLUSHO, VFLUSHO},
364 #endif
365 #ifdef VSTATUS
366         {"status",   CSTATUS, VSTATUS},
367 #endif
368         /* These must be last because of the display routines */
369         {stty_min,   1,       VMIN},
370         {stty_time,  0,       VTIME},
371 };
372
373 enum {
374         NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
375 };
376
377 /* The width of the screen, for output wrapping */
378 static unsigned max_col = 80; /* default */
379 /* Current position, to know when to wrap */
380 static unsigned current_col;
381 static const char *device_name = bb_msg_standard_input;
382
383 /* Return a string that is the printable representation of character CH */
384 /* Adapted from 'cat' by Torbjorn Granlund */
385 static const char *visible(unsigned int ch)
386 {
387         static char buf[10];
388         char *bpout = buf;
389
390         if (ch == _POSIX_VDISABLE)
391                 return "<undef>";
392
393         if (ch >= 128) {
394                 ch -= 128;
395                 *bpout++ = 'M';
396                 *bpout++ = '-';
397         }
398
399         if (ch < 32) {
400                 *bpout++ = '^';
401                 *bpout++ = ch + 64;
402         } else if (ch < 127) {
403                 *bpout++ = ch;
404         } else {
405                 *bpout++ = '^';
406                 *bpout++ = '?';
407         }
408
409         *bpout = '\0';
410         return buf;
411 }
412
413 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
414 {
415         static const unsigned char tcflag_offsets[] = {
416                 offsetof(struct termios, c_cflag), /* control */
417                 offsetof(struct termios, c_iflag), /* input */
418                 offsetof(struct termios, c_oflag), /* output */
419                 offsetof(struct termios, c_lflag), /* local */
420         };
421
422         if (type <= local) {
423                 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
424         }
425         return NULL;
426 }
427
428 static void set_speed_or_die(enum speed_setting type, const char * const arg,
429                                         struct termios * const mode)
430 {
431         speed_t baud;
432
433         baud = tty_value_to_baud(xatou(arg));
434
435         if (type != output_speed) {     /* either input or both */
436                 cfsetispeed(mode, baud);
437         }
438         if (type != input_speed) {      /* either output or both */
439                 cfsetospeed(mode, baud);
440         }
441 }
442
443 static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
444 {
445         bb_perror_msg_and_die(fmt, device_name);
446 }
447
448 static void perror_on_device(const char *fmt)
449 {
450         bb_perror_msg(fmt, device_name);
451 }
452
453 /* Print format string MESSAGE and optional args.
454    Wrap to next line first if it won't fit.
455    Print a space first unless MESSAGE will start a new line */
456 static void wrapf(const char *message, ...)
457 {
458         char buf[128];
459         va_list args;
460         int buflen;
461
462         va_start(args, message);
463         buflen = vsnprintf(buf, sizeof(buf), message, args);
464         va_end(args);
465         /* buflen = strlen(buf); cheaper not to pull in strlen */
466         if (!buflen /*|| buflen >= sizeof(buf)*/) return;
467
468         if (current_col > 0) {
469                 current_col++;
470                 if (buf[0] != '\n') {
471                         if (current_col + buflen >= max_col) {
472                                 putchar('\n');
473                                 current_col = 0;
474                         } else
475                                 putchar(' ');
476                 }
477         }
478         fputs(buf, stdout);
479         current_col += buflen;
480         if (buf[buflen-1] == '\n')
481                 current_col = 0;
482 }
483
484 static void set_window_size(const int rows, const int cols)
485 {
486         struct winsize win = { 0, 0, 0, 0};
487
488         if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
489                 if (errno != EINVAL) {
490                         goto bail;
491                 }
492                 memset(&win, 0, sizeof(win));
493         }
494
495         if (rows >= 0)
496                 win.ws_row = rows;
497         if (cols >= 0)
498                 win.ws_col = cols;
499
500         if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
501 bail:
502                 perror_on_device("%s");
503 }
504
505 static void display_window_size(const int fancy)
506 {
507         const char *fmt_str = "%s\0%s: no size information for this device";
508         unsigned width, height;
509
510         if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
511                 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
512                         perror_on_device(fmt_str);
513                 }
514         } else {
515                 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
516                                 height, width);
517         }
518 }
519
520 static const struct suffix_mult stty_suffixes[] = {
521         {"b",  512 },
522         {"k",  1024},
523         {"B",  1024},
524         {NULL, 0   }
525 };
526
527 static const struct mode_info *find_mode(const char *name)
528 {
529         int i;
530         for (i = 0; i < NUM_mode_info; ++i)
531                 if (!strcmp(name, mode_info[i].name))
532                         return &mode_info[i];
533         return 0;
534 }
535
536 static const struct control_info *find_control(const char *name)
537 {
538         int i;
539         for (i = 0; i < NUM_control_info; ++i)
540                 if (!strcmp(name, control_info[i].name))
541                         return &control_info[i];
542         return 0;
543 }
544
545 enum {
546         param_need_arg = 0x80,
547         param_line   = 1 | 0x80,
548         param_rows   = 2 | 0x80,
549         param_cols   = 3 | 0x80,
550         param_size   = 4,
551         param_speed  = 5,
552         param_ispeed = 6 | 0x80,
553         param_ospeed = 7 | 0x80,
554 };
555
556 static int find_param(const char * const name)
557 {
558         const char * const params[] = {
559                 "line",
560                 "rows",
561                 "cols",
562                 "columns",
563                 "size",
564                 "speed",
565                 "ispeed",
566                 "ospeed",
567                 NULL
568         };
569         int i = index_in_str_array(params, name);
570         if (i) {
571                 if (!(i == 4 || i == 5))
572                         i |= 0x80;
573         }
574         return i;
575 }
576
577 static int recover_mode(const char *arg, struct termios *mode)
578 {
579         int i, n;
580         unsigned int chr;
581         unsigned long iflag, oflag, cflag, lflag;
582
583         /* Scan into temporaries since it is too much trouble to figure out
584            the right format for 'tcflag_t' */
585         if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
586                            &iflag, &oflag, &cflag, &lflag, &n) != 4)
587                 return 0;
588         mode->c_iflag = iflag;
589         mode->c_oflag = oflag;
590         mode->c_cflag = cflag;
591         mode->c_lflag = lflag;
592         arg += n;
593         for (i = 0; i < NCCS; ++i) {
594                 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
595                         return 0;
596                 mode->c_cc[i] = chr;
597                 arg += n;
598         }
599
600         /* Fail if there are too many fields */
601         if (*arg != '\0')
602                 return 0;
603
604         return 1;
605 }
606
607 static void display_recoverable(const struct termios *mode,
608                                                                 const int ATTRIBUTE_UNUSED dummy)
609 {
610         int i;
611         printf("%lx:%lx:%lx:%lx",
612                    (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
613                    (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
614         for (i = 0; i < NCCS; ++i)
615                 printf(":%x", (unsigned int) mode->c_cc[i]);
616         putchar('\n');
617 }
618
619 static void display_speed(const struct termios *mode, int fancy)
620 {
621                              //01234567 8 9
622         const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
623         unsigned long ispeed, ospeed;
624
625         ospeed = ispeed = cfgetispeed(mode);
626         if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
627                 ispeed = ospeed;                /* in case ispeed was 0 */
628                          //0123 4 5 6 7 8 9
629                 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
630         }
631         if (fancy) fmt_str += 9;
632         wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
633 }
634
635 static void do_display(const struct termios *mode, const int all)
636 {
637         int i;
638         tcflag_t *bitsp;
639         unsigned long mask;
640         int prev_type = control;
641
642         display_speed(mode, 1);
643         if (all)
644                 display_window_size(1);
645 #ifdef HAVE_C_LINE
646         wrapf("line = %d;\n", mode->c_line);
647 #else
648         wrapf("\n");
649 #endif
650
651         for (i = 0; control_info[i].name != stty_min; ++i) {
652                 /* If swtch is the same as susp, don't print both */
653 #if VSWTCH == VSUSP
654                 if (control_info[i].name == stty_swtch)
655                         continue;
656 #endif
657                 /* If eof uses the same slot as min, only print whichever applies */
658 #if VEOF == VMIN
659                 if ((mode->c_lflag & ICANON) == 0
660                         && (control_info[i].name == stty_eof
661                                 || control_info[i].name == stty_eol)) continue;
662 #endif
663                 wrapf("%s = %s;", control_info[i].name,
664                           visible(mode->c_cc[control_info[i].offset]));
665         }
666 #if VEOF == VMIN
667         if ((mode->c_lflag & ICANON) == 0)
668 #endif
669                 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
670         if (current_col) wrapf("\n");
671
672         for (i = 0; i < NUM_mode_info; ++i) {
673                 if (mode_info[i].flags & OMIT)
674                         continue;
675                 if (mode_info[i].type != prev_type) {
676                         /* wrapf("\n"); */
677                         if (current_col) wrapf("\n");
678                         prev_type = mode_info[i].type;
679                 }
680
681                 bitsp = mode_type_flag(mode_info[i].type, mode);
682                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
683                 if ((*bitsp & mask) == mode_info[i].bits) {
684                         if (all || (mode_info[i].flags & SANE_UNSET))
685                                 wrapf("%s", mode_info[i].name);
686                 } else {
687                         if ((all && mode_info[i].flags & REV) ||
688                                  (!all &&
689                                   (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
690                                 wrapf("-%s", mode_info[i].name);
691                 }
692         }
693         if (current_col) wrapf("\n");
694 }
695
696 static void sane_mode(struct termios *mode)
697 {
698         int i;
699         tcflag_t *bitsp;
700
701         for (i = 0; i < NUM_control_info; ++i) {
702 #if VMIN == VEOF
703                 if (control_info[i].name == stty_min)
704                         break;
705 #endif
706                 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
707         }
708
709         for (i = 0; i < NUM_mode_info; ++i) {
710                 if (mode_info[i].flags & SANE_SET) {
711                         bitsp = mode_type_flag(mode_info[i].type, mode);
712                         *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
713                                 | mode_info[i].bits;
714                 } else if (mode_info[i].flags & SANE_UNSET) {
715                         bitsp = mode_type_flag(mode_info[i].type, mode);
716                         *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
717                                 & ~mode_info[i].bits;
718                 }
719         }
720 }
721
722 /* Save set_mode from #ifdef forest plague */
723 #ifndef ONLCR
724 #define ONLCR 0
725 #endif
726 #ifndef OCRNL
727 #define OCRNL 0
728 #endif
729 #ifndef ONLRET
730 #define ONLRET 0
731 #endif
732 #ifndef XCASE
733 #define XCASE 0
734 #endif
735 #ifndef IXANY
736 #define IXANY 0
737 #endif
738 #ifndef TABDLY
739 #define TABDLY 0
740 #endif
741 #ifndef OXTABS
742 #define OXTABS 0
743 #endif
744 #ifndef IUCLC
745 #define IUCLC 0
746 #endif
747 #ifndef OLCUC
748 #define OLCUC 0
749 #endif
750 #ifndef ECHOCTL
751 #define ECHOCTL 0
752 #endif
753 #ifndef ECHOKE
754 #define ECHOKE 0
755 #endif
756
757 static void set_mode(const struct mode_info *info, int reversed,
758                                         struct termios *mode)
759 {
760         tcflag_t *bitsp;
761
762         bitsp = mode_type_flag(info->type, mode);
763
764         if (bitsp) {
765                 if (reversed)
766                         *bitsp = *bitsp & ~info->mask & ~info->bits;
767                 else
768                         *bitsp = (*bitsp & ~info->mask) | info->bits;
769                 return;
770         }
771
772         /* Combination mode */
773         if (info->name == evenp || info->name == parity) {
774                 if (reversed)
775                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
776                 else
777                         mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
778         } else if (info->name == stty_oddp) {
779                 if (reversed)
780                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
781                 else
782                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
783         } else if (info->name == stty_nl) {
784                 if (reversed) {
785                         mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
786                         mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
787                 } else {
788                         mode->c_iflag = mode->c_iflag & ~ICRNL;
789                         if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
790                 }
791         } else if (info->name == stty_ek) {
792                 mode->c_cc[VERASE] = CERASE;
793                 mode->c_cc[VKILL] = CKILL;
794         } else if (info->name == stty_sane) {
795                 sane_mode(mode);
796         }
797         else if (info->name == cbreak) {
798                 if (reversed)
799                         mode->c_lflag |= ICANON;
800                 else
801                         mode->c_lflag &= ~ICANON;
802         } else if (info->name == stty_pass8) {
803                 if (reversed) {
804                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
805                         mode->c_iflag |= ISTRIP;
806                 } else {
807                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
808                         mode->c_iflag &= ~ISTRIP;
809                 }
810         } else if (info->name == litout) {
811                 if (reversed) {
812                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
813                         mode->c_iflag |= ISTRIP;
814                         mode->c_oflag |= OPOST;
815                 } else {
816                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
817                         mode->c_iflag &= ~ISTRIP;
818                         mode->c_oflag &= ~OPOST;
819                 }
820         } else if (info->name == raw || info->name == cooked) {
821                 if ((info->name[0] == 'r' && reversed)
822                         || (info->name[0] == 'c' && !reversed)) {
823                         /* Cooked mode */
824                         mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
825                         mode->c_oflag |= OPOST;
826                         mode->c_lflag |= ISIG | ICANON;
827 #if VMIN == VEOF
828                         mode->c_cc[VEOF] = CEOF;
829 #endif
830 #if VTIME == VEOL
831                         mode->c_cc[VEOL] = CEOL;
832 #endif
833                 } else {
834                         /* Raw mode */
835                         mode->c_iflag = 0;
836                         mode->c_oflag &= ~OPOST;
837                         mode->c_lflag &= ~(ISIG | ICANON | XCASE);
838                         mode->c_cc[VMIN] = 1;
839                         mode->c_cc[VTIME] = 0;
840                 }
841         }
842         else if (IXANY && info->name == decctlq) {
843                 if (reversed)
844                         mode->c_iflag |= IXANY;
845                 else
846                         mode->c_iflag &= ~IXANY;
847         }
848         else if (TABDLY && info->name == stty_tabs) {
849                 if (reversed)
850                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
851                 else
852                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
853         }
854         else if (OXTABS && info->name == stty_tabs) {
855                 if (reversed)
856                         mode->c_oflag |= OXTABS;
857                 else
858                         mode->c_oflag &= ~OXTABS;
859         }
860         else if (XCASE && IUCLC && OLCUC
861         && (info->name == stty_lcase || info->name == stty_LCASE)) {
862                 if (reversed) {
863                         mode->c_lflag &= ~XCASE;
864                         mode->c_iflag &= ~IUCLC;
865                         mode->c_oflag &= ~OLCUC;
866                 } else {
867                         mode->c_lflag |= XCASE;
868                         mode->c_iflag |= IUCLC;
869                         mode->c_oflag |= OLCUC;
870                 }
871         }
872         else if (info->name == stty_crt) {
873                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
874         }
875         else if (info->name == stty_dec) {
876                 mode->c_cc[VINTR] = 3; /* ^C */
877                 mode->c_cc[VERASE] = 127; /* DEL */
878                 mode->c_cc[VKILL] = 21; /* ^U */
879                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
880                 if (IXANY) mode->c_iflag &= ~IXANY;
881         }
882 }
883
884 static void set_control_char_or_die(const struct control_info *info,
885                         const char *arg, struct termios *mode)
886 {
887         unsigned char value;
888
889         if (info->name == stty_min || info->name == stty_time)
890                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
891         else if (arg[0] == '\0' || arg[1] == '\0')
892                 value = arg[0];
893         else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
894                 value = _POSIX_VDISABLE;
895         else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
896                 value = arg[1] & 0x1f; /* Non-letters get weird results */
897                 if (arg[1] == '?')
898                         value = 127;
899         } else
900                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
901         mode->c_cc[info->offset] = value;
902 }
903
904 #define STTY_require_set_attr   (1<<0)
905 #define STTY_speed_was_set      (1<<1)
906 #define STTY_verbose_output     (1<<2)
907 #define STTY_recoverable_output (1<<3)
908 #define STTY_noargs             (1<<4)
909 int stty_main(int argc, char **argv)
910 {
911         struct termios mode;
912         void (*output_func)(const struct termios *, const int);
913         const char *file_name = NULL;
914         int display_all = 0;
915         int stty_state;
916         int k;
917
918         stty_state = STTY_noargs;
919         output_func = do_display;
920
921         /* First pass: only parse/verify command line params */
922         k = 0;
923         while (argv[++k]) {
924                 const struct mode_info *mp;
925                 const struct control_info *cp;
926                 const char *arg = argv[k];
927                 const char *argnext = argv[k+1];
928                 int param;
929
930                 if (arg[0] == '-') {
931                         int i;
932                         mp = find_mode(arg+1);
933                         if (mp) {
934                                 if (!(mp->flags & REV))
935                                         goto invalid_argument;
936                                 stty_state &= ~STTY_noargs;
937                                 continue;
938                         }
939                         /* It is an option - parse it */
940                         i = 0;
941                         while (arg[++i]) {
942                                 switch (arg[i]) {
943                                 case 'a':
944                                         stty_state |= STTY_verbose_output;
945                                         output_func = do_display;
946                                         display_all = 1;
947                                         break;
948                                 case 'g':
949                                         stty_state |= STTY_recoverable_output;
950                                         output_func = display_recoverable;
951                                         break;
952                                 case 'F':
953                                         if (file_name)
954                                                 bb_error_msg_and_die("only one device may be specified");
955                                         file_name = &arg[i+1]; /* "-Fdevice" ? */
956                                         if (!file_name[0]) { /* nope, "-F device" */
957                                                 int p = k+1; /* argv[p] is argnext */
958                                                 file_name = argnext;
959                                                 if (!file_name)
960                                                         bb_error_msg_and_die(bb_msg_requires_arg, "-F");
961                                                 /* remove -F param from arg[vc] */
962                                                 --argc;
963                                                 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
964                                         }
965                                         goto end_option;
966                                 default:
967                                         goto invalid_argument;
968                                 }
969                         }
970 end_option:
971                         continue;
972                 }
973
974                 mp = find_mode(arg);
975                 if (mp) {
976                         stty_state &= ~STTY_noargs;
977                         continue;
978                 }
979
980                 cp = find_control(arg);
981                 if (cp) {
982                         if (!argnext)
983                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
984                         /* called for the side effect of xfunc death only */
985                         set_control_char_or_die(cp, argnext, &mode);
986                         stty_state &= ~STTY_noargs;
987                         ++k;
988                         continue;
989                 }
990
991                 param = find_param(arg);
992                 if (param & param_need_arg) {
993                         if (!argnext)
994                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
995                         ++k;
996                 }
997
998                 switch (param) {
999 #ifdef HAVE_C_LINE
1000                 case param_line:
1001 # ifndef TIOCGWINSZ
1002                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1003                         break;
1004 # endif /* else fall-through */
1005 #endif
1006 #ifdef TIOCGWINSZ
1007                 case param_rows:
1008                 case param_cols:
1009                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1010                         break;
1011                 case param_size:
1012 #endif
1013                 case param_speed:
1014                         break;
1015                 case param_ispeed:
1016                         /* called for the side effect of xfunc death only */
1017                         set_speed_or_die(input_speed, argnext, &mode);
1018                         break;
1019                 case param_ospeed:
1020                         /* called for the side effect of xfunc death only */
1021                         set_speed_or_die(output_speed, argnext, &mode);
1022                         break;
1023                 default:
1024                         if (recover_mode(arg, &mode) == 1) break;
1025                         if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1026 invalid_argument:
1027                         bb_error_msg_and_die("invalid argument '%s'", arg);
1028                 }
1029                 stty_state &= ~STTY_noargs;
1030         }
1031
1032         /* Specifying both -a and -g is an error */
1033         if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1034                 (STTY_verbose_output | STTY_recoverable_output))
1035                 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1036         /* Specifying -a or -g with non-options is an error */
1037         if (!(stty_state & STTY_noargs) &&
1038                 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1039                 bb_error_msg_and_die("modes may not be set when specifying an output style");
1040
1041         /* Now it is safe to start doing things */
1042         if (file_name) {
1043                 int fd, fdflags;
1044                 device_name = file_name;
1045                 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
1046                 if (fd != STDIN_FILENO) {
1047                         dup2(fd, STDIN_FILENO);
1048                         close(fd);
1049                 }
1050                 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1051                 if (fdflags < 0 ||
1052                         fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1053                         perror_on_device_and_die("%s: cannot reset non-blocking mode");
1054         }
1055
1056         /* Initialize to all zeroes so there is no risk memcmp will report a
1057            spurious difference in an uninitialized portion of the structure */
1058         memset(&mode, 0, sizeof(mode));
1059         if (tcgetattr(STDIN_FILENO, &mode))
1060                 perror_on_device_and_die("%s");
1061
1062         if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1063                 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
1064                 output_func(&mode, display_all);
1065                 return EXIT_SUCCESS;
1066         }
1067
1068         /* Second pass: perform actions */
1069         k = 0;
1070         while (argv[++k]) {
1071                 const struct mode_info *mp;
1072                 const struct control_info *cp;
1073                 const char *arg = argv[k];
1074                 const char *argnext = argv[k+1];
1075                 int param;
1076
1077                 if (arg[0] == '-') {
1078                         mp = find_mode(arg+1);
1079                         if (mp) {
1080                                 set_mode(mp, 1 /* reversed */, &mode);
1081                                 stty_state |= STTY_require_set_attr;
1082                         }
1083                         /* It is an option - already parsed. Skip it */
1084                         continue;
1085                 }
1086
1087                 mp = find_mode(arg);
1088                 if (mp) {
1089                         set_mode(mp, 0 /* non-reversed */, &mode);
1090                         stty_state |= STTY_require_set_attr;
1091                         continue;
1092                 }
1093
1094                 cp = find_control(arg);
1095                 if (cp) {
1096                         ++k;
1097                         set_control_char_or_die(cp, argnext, &mode);
1098                         stty_state |= STTY_require_set_attr;
1099                         continue;
1100                 }
1101
1102                 param = find_param(arg);
1103                 if (param & param_need_arg) {
1104                         ++k;
1105                 }
1106
1107                 switch (param) {
1108 #ifdef HAVE_C_LINE
1109                 case param_line:
1110                         mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1111                         stty_state |= STTY_require_set_attr;
1112                         break;
1113 #endif
1114 #ifdef TIOCGWINSZ
1115                 case param_cols:
1116                         set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1117                         break;
1118                 case param_size:
1119                         display_window_size(0);
1120                         break;
1121                 case param_rows:
1122                         set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1123                         break;
1124 #endif
1125                 case param_speed:
1126                         display_speed(&mode, 0);
1127                         break;
1128                 case param_ispeed:
1129                         set_speed_or_die(input_speed, argnext, &mode);
1130                         stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1131                         break;
1132                 case param_ospeed:
1133                         set_speed_or_die(output_speed, argnext, &mode);
1134                         stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1135                         break;
1136                 default:
1137                         if (recover_mode(arg, &mode) == 1)
1138                                 stty_state |= STTY_require_set_attr;
1139                         else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1140                                 set_speed_or_die(both_speeds, arg, &mode);
1141                                 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1142                         } /* else - impossible (caught in the first pass):
1143                                 bb_error_msg_and_die("invalid argument '%s'", arg); */
1144                 }
1145         }
1146
1147         if (stty_state & STTY_require_set_attr) {
1148                 struct termios new_mode;
1149
1150                 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1151                         perror_on_device_and_die("%s");
1152
1153                 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1154                    it performs *any* of the requested operations.  This means it
1155                    can report 'success' when it has actually failed to perform
1156                    some proper subset of the requested operations.  To detect
1157                    this partial failure, get the current terminal attributes and
1158                    compare them to the requested ones */
1159
1160                 /* Initialize to all zeroes so there is no risk memcmp will report a
1161                    spurious difference in an uninitialized portion of the structure */
1162                 memset(&new_mode, 0, sizeof(new_mode));
1163                 if (tcgetattr(STDIN_FILENO, &new_mode))
1164                         perror_on_device_and_die("%s");
1165
1166                 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1167 #ifdef CIBAUD
1168                         /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1169                            tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1170                            sometimes (m1 != m2).  The only difference is in the four bits
1171                            of the c_cflag field corresponding to the baud rate.  To save
1172                            Sun users a little confusion, don't report an error if this
1173                            happens.  But suppress the error only if we haven't tried to
1174                            set the baud rate explicitly -- otherwise we'd never give an
1175                            error for a true failure to set the baud rate */
1176
1177                         new_mode.c_cflag &= (~CIBAUD);
1178                         if ((stty_state & STTY_speed_was_set)
1179                          || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1180 #endif
1181                                 perror_on_device_and_die("%s: cannot perform all requested operations");
1182                 }
1183         }
1184
1185         return EXIT_SUCCESS;
1186 }