pidfile.c: not used anymore
[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         /* We seem to be called only with suitable lengths, but check if
466            somebody failed to adhere to this assumption just to be sure.  */
467         if (!buflen || buflen >= sizeof(buf)) return;
468
469         if (current_col > 0) {
470                 current_col++;
471                 if (buf[0] != '\n') {
472                         if (current_col + buflen >= max_col) {
473                                 putchar('\n');
474                                 current_col = 0;
475                         } else
476                                 putchar(' ');
477                 }
478         }
479         fputs(buf, stdout);
480         current_col += buflen;
481         if (buf[buflen-1] == '\n')
482                 current_col = 0;
483 }
484
485 static void set_window_size(const int rows, const int cols)
486 {
487         struct winsize win = { 0, 0, 0, 0};
488
489         if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
490                 if (errno != EINVAL) {
491                         goto bail;
492                 }
493                 memset(&win, 0, sizeof(win));
494         }
495
496         if (rows >= 0)
497                 win.ws_row = rows;
498         if (cols >= 0)
499                 win.ws_col = cols;
500
501         if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
502 bail:
503                 perror_on_device("%s");
504 }
505
506 static void display_window_size(const int fancy)
507 {
508         const char *fmt_str = "%s\0%s: no size information for this device";
509         unsigned width, height;
510
511         if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
512                 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
513                         perror_on_device(fmt_str);
514                 }
515         } else {
516                 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
517                                 height, width);
518         }
519 }
520
521 static const struct suffix_mult stty_suffixes[] = {
522         {"b",  512 },
523         {"k",  1024},
524         {"B",  1024},
525         {NULL, 0   }
526 };
527
528 static const struct mode_info *find_mode(const char *name)
529 {
530         int i;
531         for (i = 0; i < NUM_mode_info; ++i)
532                 if (!strcmp(name, mode_info[i].name))
533                         return &mode_info[i];
534         return 0;
535 }
536
537 static const struct control_info *find_control(const char *name)
538 {
539         int i;
540         for (i = 0; i < NUM_control_info; ++i)
541                 if (!strcmp(name, control_info[i].name))
542                         return &control_info[i];
543         return 0;
544 }
545
546 enum {
547         param_need_arg = 0x80,
548         param_line   = 1 | 0x80,
549         param_rows   = 2 | 0x80,
550         param_cols   = 3 | 0x80,
551         param_size   = 4,
552         param_speed  = 5,
553         param_ispeed = 6 | 0x80,
554         param_ospeed = 7 | 0x80,
555 };
556
557 static int find_param(const char * const name)
558 {
559         const char * const params[] = {
560                 "line",
561                 "rows",
562                 "cols",
563                 "columns",
564                 "size",
565                 "speed",
566                 "ispeed",
567                 "ospeed",
568                 NULL
569         };
570         int i = index_in_str_array(params, name);
571         if (i < 0)
572                 return 0;
573         if (!(i == 4 || i == 5))
574                 i |= 0x80;
575
576         return i;
577 }
578
579 static int recover_mode(const char *arg, struct termios *mode)
580 {
581         int i, n;
582         unsigned int chr;
583         unsigned long iflag, oflag, cflag, lflag;
584
585         /* Scan into temporaries since it is too much trouble to figure out
586            the right format for 'tcflag_t' */
587         if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
588                            &iflag, &oflag, &cflag, &lflag, &n) != 4)
589                 return 0;
590         mode->c_iflag = iflag;
591         mode->c_oflag = oflag;
592         mode->c_cflag = cflag;
593         mode->c_lflag = lflag;
594         arg += n;
595         for (i = 0; i < NCCS; ++i) {
596                 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
597                         return 0;
598                 mode->c_cc[i] = chr;
599                 arg += n;
600         }
601
602         /* Fail if there are too many fields */
603         if (*arg != '\0')
604                 return 0;
605
606         return 1;
607 }
608
609 static void display_recoverable(const struct termios *mode,
610                                                                 const int ATTRIBUTE_UNUSED dummy)
611 {
612         int i;
613         printf("%lx:%lx:%lx:%lx",
614                    (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
615                    (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
616         for (i = 0; i < NCCS; ++i)
617                 printf(":%x", (unsigned int) mode->c_cc[i]);
618         putchar('\n');
619 }
620
621 static void display_speed(const struct termios *mode, int fancy)
622 {
623                              //01234567 8 9
624         const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
625         unsigned long ispeed, ospeed;
626
627         ospeed = ispeed = cfgetispeed(mode);
628         if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
629                 ispeed = ospeed;                /* in case ispeed was 0 */
630                          //0123 4 5 6 7 8 9
631                 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
632         }
633         if (fancy) fmt_str += 9;
634         wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
635 }
636
637 static void do_display(const struct termios *mode, const int all)
638 {
639         int i;
640         tcflag_t *bitsp;
641         unsigned long mask;
642         int prev_type = control;
643
644         display_speed(mode, 1);
645         if (all)
646                 display_window_size(1);
647 #ifdef HAVE_C_LINE
648         wrapf("line = %d;\n", mode->c_line);
649 #else
650         wrapf("\n");
651 #endif
652
653         for (i = 0; control_info[i].name != stty_min; ++i) {
654                 /* If swtch is the same as susp, don't print both */
655 #if VSWTCH == VSUSP
656                 if (control_info[i].name == stty_swtch)
657                         continue;
658 #endif
659                 /* If eof uses the same slot as min, only print whichever applies */
660 #if VEOF == VMIN
661                 if ((mode->c_lflag & ICANON) == 0
662                         && (control_info[i].name == stty_eof
663                                 || control_info[i].name == stty_eol)) continue;
664 #endif
665                 wrapf("%s = %s;", control_info[i].name,
666                           visible(mode->c_cc[control_info[i].offset]));
667         }
668 #if VEOF == VMIN
669         if ((mode->c_lflag & ICANON) == 0)
670 #endif
671                 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
672         if (current_col) wrapf("\n");
673
674         for (i = 0; i < NUM_mode_info; ++i) {
675                 if (mode_info[i].flags & OMIT)
676                         continue;
677                 if (mode_info[i].type != prev_type) {
678                         /* wrapf("\n"); */
679                         if (current_col) wrapf("\n");
680                         prev_type = mode_info[i].type;
681                 }
682
683                 bitsp = mode_type_flag(mode_info[i].type, mode);
684                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
685                 if ((*bitsp & mask) == mode_info[i].bits) {
686                         if (all || (mode_info[i].flags & SANE_UNSET))
687                                 wrapf("%s", mode_info[i].name);
688                 } else {
689                         if ((all && mode_info[i].flags & REV) ||
690                                  (!all &&
691                                   (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
692                                 wrapf("-%s", mode_info[i].name);
693                 }
694         }
695         if (current_col) wrapf("\n");
696 }
697
698 static void sane_mode(struct termios *mode)
699 {
700         int i;
701         tcflag_t *bitsp;
702
703         for (i = 0; i < NUM_control_info; ++i) {
704 #if VMIN == VEOF
705                 if (control_info[i].name == stty_min)
706                         break;
707 #endif
708                 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
709         }
710
711         for (i = 0; i < NUM_mode_info; ++i) {
712                 if (mode_info[i].flags & SANE_SET) {
713                         bitsp = mode_type_flag(mode_info[i].type, mode);
714                         *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
715                                 | mode_info[i].bits;
716                 } else if (mode_info[i].flags & SANE_UNSET) {
717                         bitsp = mode_type_flag(mode_info[i].type, mode);
718                         *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
719                                 & ~mode_info[i].bits;
720                 }
721         }
722 }
723
724 /* Save set_mode from #ifdef forest plague */
725 #ifndef ONLCR
726 #define ONLCR 0
727 #endif
728 #ifndef OCRNL
729 #define OCRNL 0
730 #endif
731 #ifndef ONLRET
732 #define ONLRET 0
733 #endif
734 #ifndef XCASE
735 #define XCASE 0
736 #endif
737 #ifndef IXANY
738 #define IXANY 0
739 #endif
740 #ifndef TABDLY
741 #define TABDLY 0
742 #endif
743 #ifndef OXTABS
744 #define OXTABS 0
745 #endif
746 #ifndef IUCLC
747 #define IUCLC 0
748 #endif
749 #ifndef OLCUC
750 #define OLCUC 0
751 #endif
752 #ifndef ECHOCTL
753 #define ECHOCTL 0
754 #endif
755 #ifndef ECHOKE
756 #define ECHOKE 0
757 #endif
758
759 static void set_mode(const struct mode_info *info, int reversed,
760                                         struct termios *mode)
761 {
762         tcflag_t *bitsp;
763
764         bitsp = mode_type_flag(info->type, mode);
765
766         if (bitsp) {
767                 if (reversed)
768                         *bitsp = *bitsp & ~info->mask & ~info->bits;
769                 else
770                         *bitsp = (*bitsp & ~info->mask) | info->bits;
771                 return;
772         }
773
774         /* Combination mode */
775         if (info->name == evenp || info->name == parity) {
776                 if (reversed)
777                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
778                 else
779                         mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
780         } else if (info->name == stty_oddp) {
781                 if (reversed)
782                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
783                 else
784                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
785         } else if (info->name == stty_nl) {
786                 if (reversed) {
787                         mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
788                         mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
789                 } else {
790                         mode->c_iflag = mode->c_iflag & ~ICRNL;
791                         if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
792                 }
793         } else if (info->name == stty_ek) {
794                 mode->c_cc[VERASE] = CERASE;
795                 mode->c_cc[VKILL] = CKILL;
796         } else if (info->name == stty_sane) {
797                 sane_mode(mode);
798         }
799         else if (info->name == cbreak) {
800                 if (reversed)
801                         mode->c_lflag |= ICANON;
802                 else
803                         mode->c_lflag &= ~ICANON;
804         } else if (info->name == stty_pass8) {
805                 if (reversed) {
806                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
807                         mode->c_iflag |= ISTRIP;
808                 } else {
809                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
810                         mode->c_iflag &= ~ISTRIP;
811                 }
812         } else if (info->name == litout) {
813                 if (reversed) {
814                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
815                         mode->c_iflag |= ISTRIP;
816                         mode->c_oflag |= OPOST;
817                 } else {
818                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
819                         mode->c_iflag &= ~ISTRIP;
820                         mode->c_oflag &= ~OPOST;
821                 }
822         } else if (info->name == raw || info->name == cooked) {
823                 if ((info->name[0] == 'r' && reversed)
824                         || (info->name[0] == 'c' && !reversed)) {
825                         /* Cooked mode */
826                         mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
827                         mode->c_oflag |= OPOST;
828                         mode->c_lflag |= ISIG | ICANON;
829 #if VMIN == VEOF
830                         mode->c_cc[VEOF] = CEOF;
831 #endif
832 #if VTIME == VEOL
833                         mode->c_cc[VEOL] = CEOL;
834 #endif
835                 } else {
836                         /* Raw mode */
837                         mode->c_iflag = 0;
838                         mode->c_oflag &= ~OPOST;
839                         mode->c_lflag &= ~(ISIG | ICANON | XCASE);
840                         mode->c_cc[VMIN] = 1;
841                         mode->c_cc[VTIME] = 0;
842                 }
843         }
844         else if (IXANY && info->name == decctlq) {
845                 if (reversed)
846                         mode->c_iflag |= IXANY;
847                 else
848                         mode->c_iflag &= ~IXANY;
849         }
850         else if (TABDLY && info->name == stty_tabs) {
851                 if (reversed)
852                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
853                 else
854                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
855         }
856         else if (OXTABS && info->name == stty_tabs) {
857                 if (reversed)
858                         mode->c_oflag |= OXTABS;
859                 else
860                         mode->c_oflag &= ~OXTABS;
861         }
862         else if (XCASE && IUCLC && OLCUC
863         && (info->name == stty_lcase || info->name == stty_LCASE)) {
864                 if (reversed) {
865                         mode->c_lflag &= ~XCASE;
866                         mode->c_iflag &= ~IUCLC;
867                         mode->c_oflag &= ~OLCUC;
868                 } else {
869                         mode->c_lflag |= XCASE;
870                         mode->c_iflag |= IUCLC;
871                         mode->c_oflag |= OLCUC;
872                 }
873         }
874         else if (info->name == stty_crt) {
875                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
876         }
877         else if (info->name == stty_dec) {
878                 mode->c_cc[VINTR] = 3; /* ^C */
879                 mode->c_cc[VERASE] = 127; /* DEL */
880                 mode->c_cc[VKILL] = 21; /* ^U */
881                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
882                 if (IXANY) mode->c_iflag &= ~IXANY;
883         }
884 }
885
886 static void set_control_char_or_die(const struct control_info *info,
887                         const char *arg, struct termios *mode)
888 {
889         unsigned char value;
890
891         if (info->name == stty_min || info->name == stty_time)
892                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
893         else if (arg[0] == '\0' || arg[1] == '\0')
894                 value = arg[0];
895         else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
896                 value = _POSIX_VDISABLE;
897         else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
898                 value = arg[1] & 0x1f; /* Non-letters get weird results */
899                 if (arg[1] == '?')
900                         value = 127;
901         } else
902                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
903         mode->c_cc[info->offset] = value;
904 }
905
906 #define STTY_require_set_attr   (1<<0)
907 #define STTY_speed_was_set      (1<<1)
908 #define STTY_verbose_output     (1<<2)
909 #define STTY_recoverable_output (1<<3)
910 #define STTY_noargs             (1<<4)
911 int stty_main(int argc, char **argv);
912 int stty_main(int argc, char **argv)
913 {
914         struct termios mode;
915         void (*output_func)(const struct termios *, const int);
916         const char *file_name = NULL;
917         int display_all = 0;
918         int stty_state;
919         int k;
920
921         stty_state = STTY_noargs;
922         output_func = do_display;
923
924         /* First pass: only parse/verify command line params */
925         k = 0;
926         while (argv[++k]) {
927                 const struct mode_info *mp;
928                 const struct control_info *cp;
929                 const char *arg = argv[k];
930                 const char *argnext = argv[k+1];
931                 int param;
932
933                 if (arg[0] == '-') {
934                         int i;
935                         mp = find_mode(arg+1);
936                         if (mp) {
937                                 if (!(mp->flags & REV))
938                                         goto invalid_argument;
939                                 stty_state &= ~STTY_noargs;
940                                 continue;
941                         }
942                         /* It is an option - parse it */
943                         i = 0;
944                         while (arg[++i]) {
945                                 switch (arg[i]) {
946                                 case 'a':
947                                         stty_state |= STTY_verbose_output;
948                                         output_func = do_display;
949                                         display_all = 1;
950                                         break;
951                                 case 'g':
952                                         stty_state |= STTY_recoverable_output;
953                                         output_func = display_recoverable;
954                                         break;
955                                 case 'F':
956                                         if (file_name)
957                                                 bb_error_msg_and_die("only one device may be specified");
958                                         file_name = &arg[i+1]; /* "-Fdevice" ? */
959                                         if (!file_name[0]) { /* nope, "-F device" */
960                                                 int p = k+1; /* argv[p] is argnext */
961                                                 file_name = argnext;
962                                                 if (!file_name)
963                                                         bb_error_msg_and_die(bb_msg_requires_arg, "-F");
964                                                 /* remove -F param from arg[vc] */
965                                                 --argc;
966                                                 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
967                                         }
968                                         goto end_option;
969                                 default:
970                                         goto invalid_argument;
971                                 }
972                         }
973 end_option:
974                         continue;
975                 }
976
977                 mp = find_mode(arg);
978                 if (mp) {
979                         stty_state &= ~STTY_noargs;
980                         continue;
981                 }
982
983                 cp = find_control(arg);
984                 if (cp) {
985                         if (!argnext)
986                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
987                         /* called for the side effect of xfunc death only */
988                         set_control_char_or_die(cp, argnext, &mode);
989                         stty_state &= ~STTY_noargs;
990                         ++k;
991                         continue;
992                 }
993
994                 param = find_param(arg);
995                 if (param & param_need_arg) {
996                         if (!argnext)
997                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
998                         ++k;
999                 }
1000
1001                 switch (param) {
1002 #ifdef HAVE_C_LINE
1003                 case param_line:
1004 # ifndef TIOCGWINSZ
1005                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1006                         break;
1007 # endif /* else fall-through */
1008 #endif
1009 #ifdef TIOCGWINSZ
1010                 case param_rows:
1011                 case param_cols:
1012                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1013                         break;
1014                 case param_size:
1015 #endif
1016                 case param_speed:
1017                         break;
1018                 case param_ispeed:
1019                         /* called for the side effect of xfunc death only */
1020                         set_speed_or_die(input_speed, argnext, &mode);
1021                         break;
1022                 case param_ospeed:
1023                         /* called for the side effect of xfunc death only */
1024                         set_speed_or_die(output_speed, argnext, &mode);
1025                         break;
1026                 default:
1027                         if (recover_mode(arg, &mode) == 1) break;
1028                         if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1029 invalid_argument:
1030                         bb_error_msg_and_die("invalid argument '%s'", arg);
1031                 }
1032                 stty_state &= ~STTY_noargs;
1033         }
1034
1035         /* Specifying both -a and -g is an error */
1036         if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1037                 (STTY_verbose_output | STTY_recoverable_output))
1038                 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1039         /* Specifying -a or -g with non-options is an error */
1040         if (!(stty_state & STTY_noargs) &&
1041                 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1042                 bb_error_msg_and_die("modes may not be set when specifying an output style");
1043
1044         /* Now it is safe to start doing things */
1045         if (file_name) {
1046                 int fd, fdflags;
1047                 device_name = file_name;
1048                 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
1049                 if (fd != STDIN_FILENO) {
1050                         dup2(fd, STDIN_FILENO);
1051                         close(fd);
1052                 }
1053                 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1054                 if (fdflags < 0 ||
1055                         fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1056                         perror_on_device_and_die("%s: cannot reset non-blocking mode");
1057         }
1058
1059         /* Initialize to all zeroes so there is no risk memcmp will report a
1060            spurious difference in an uninitialized portion of the structure */
1061         memset(&mode, 0, sizeof(mode));
1062         if (tcgetattr(STDIN_FILENO, &mode))
1063                 perror_on_device_and_die("%s");
1064
1065         if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1066                 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
1067                 output_func(&mode, display_all);
1068                 return EXIT_SUCCESS;
1069         }
1070
1071         /* Second pass: perform actions */
1072         k = 0;
1073         while (argv[++k]) {
1074                 const struct mode_info *mp;
1075                 const struct control_info *cp;
1076                 const char *arg = argv[k];
1077                 const char *argnext = argv[k+1];
1078                 int param;
1079
1080                 if (arg[0] == '-') {
1081                         mp = find_mode(arg+1);
1082                         if (mp) {
1083                                 set_mode(mp, 1 /* reversed */, &mode);
1084                                 stty_state |= STTY_require_set_attr;
1085                         }
1086                         /* It is an option - already parsed. Skip it */
1087                         continue;
1088                 }
1089
1090                 mp = find_mode(arg);
1091                 if (mp) {
1092                         set_mode(mp, 0 /* non-reversed */, &mode);
1093                         stty_state |= STTY_require_set_attr;
1094                         continue;
1095                 }
1096
1097                 cp = find_control(arg);
1098                 if (cp) {
1099                         ++k;
1100                         set_control_char_or_die(cp, argnext, &mode);
1101                         stty_state |= STTY_require_set_attr;
1102                         continue;
1103                 }
1104
1105                 param = find_param(arg);
1106                 if (param & param_need_arg) {
1107                         ++k;
1108                 }
1109
1110                 switch (param) {
1111 #ifdef HAVE_C_LINE
1112                 case param_line:
1113                         mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1114                         stty_state |= STTY_require_set_attr;
1115                         break;
1116 #endif
1117 #ifdef TIOCGWINSZ
1118                 case param_cols:
1119                         set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1120                         break;
1121                 case param_size:
1122                         display_window_size(0);
1123                         break;
1124                 case param_rows:
1125                         set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1126                         break;
1127 #endif
1128                 case param_speed:
1129                         display_speed(&mode, 0);
1130                         break;
1131                 case param_ispeed:
1132                         set_speed_or_die(input_speed, argnext, &mode);
1133                         stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1134                         break;
1135                 case param_ospeed:
1136                         set_speed_or_die(output_speed, argnext, &mode);
1137                         stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1138                         break;
1139                 default:
1140                         if (recover_mode(arg, &mode) == 1)
1141                                 stty_state |= STTY_require_set_attr;
1142                         else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1143                                 set_speed_or_die(both_speeds, arg, &mode);
1144                                 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1145                         } /* else - impossible (caught in the first pass):
1146                                 bb_error_msg_and_die("invalid argument '%s'", arg); */
1147                 }
1148         }
1149
1150         if (stty_state & STTY_require_set_attr) {
1151                 struct termios new_mode;
1152
1153                 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1154                         perror_on_device_and_die("%s");
1155
1156                 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1157                    it performs *any* of the requested operations.  This means it
1158                    can report 'success' when it has actually failed to perform
1159                    some proper subset of the requested operations.  To detect
1160                    this partial failure, get the current terminal attributes and
1161                    compare them to the requested ones */
1162
1163                 /* Initialize to all zeroes so there is no risk memcmp will report a
1164                    spurious difference in an uninitialized portion of the structure */
1165                 memset(&new_mode, 0, sizeof(new_mode));
1166                 if (tcgetattr(STDIN_FILENO, &new_mode))
1167                         perror_on_device_and_die("%s");
1168
1169                 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1170 #ifdef CIBAUD
1171                         /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1172                            tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1173                            sometimes (m1 != m2).  The only difference is in the four bits
1174                            of the c_cflag field corresponding to the baud rate.  To save
1175                            Sun users a little confusion, don't report an error if this
1176                            happens.  But suppress the error only if we haven't tried to
1177                            set the baud rate explicitly -- otherwise we'd never give an
1178                            error for a true failure to set the baud rate */
1179
1180                         new_mode.c_cflag &= (~CIBAUD);
1181                         if ((stty_state & STTY_speed_was_set)
1182                          || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1183 #endif
1184                                 perror_on_device_and_die("%s: cannot perform all requested operations");
1185                 }
1186         }
1187
1188         return EXIT_SUCCESS;
1189 }