attempt to regularize atoi mess.
[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 *name;       /* Name given on command line            */
162         char type;              /* Which structure element to change     */
163         char flags;             /* Setting and display options           */
164         unsigned short mask;    /* Other bits to turn off for this mode */
165         unsigned long bits;     /* Bits to set for this mode             */
166 };
167
168 #define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
169
170 static const struct mode_info mode_info[] = {
171         MI_ENTRY("parenb",   control,     REV,               PARENB,     0 ),
172         MI_ENTRY("parodd",   control,     REV,               PARODD,     0 ),
173         MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE),
174         MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE),
175         MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE),
176         MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE),
177         MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 ),
178         MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 ),
179         MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 ),
180         MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 ),
181         MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 ),
182 #ifdef CRTSCTS
183         MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 ),
184 #endif
185         MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 ),
186         MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 ),
187         MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 ),
188         MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 ),
189         MI_ENTRY("inpck",    input,       REV,               INPCK,      0 ),
190         MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 ),
191         MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 ),
192         MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 ),
193         MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 ),
194         MI_ENTRY("ixon",     input,       REV,               IXON,       0 ),
195         MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 ),
196         MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 ),
197 #ifdef IUCLC
198         MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 ),
199 #endif
200 #ifdef IXANY
201         MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 ),
202 #endif
203 #ifdef IMAXBEL
204         MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 ),
205 #endif
206         MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 ),
207 #ifdef OLCUC
208         MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 ),
209 #endif
210 #ifdef OCRNL
211         MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 ),
212 #endif
213 #ifdef ONLCR
214         MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 ),
215 #endif
216 #ifdef ONOCR
217         MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 ),
218 #endif
219 #ifdef ONLRET
220         MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 ),
221 #endif
222 #ifdef OFILL
223         MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 ),
224 #endif
225 #ifdef OFDEL
226         MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 ),
227 #endif
228 #ifdef NLDLY
229         MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY),
230         MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY),
231 #endif
232 #ifdef CRDLY
233         MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY),
234         MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY),
235         MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY),
236         MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY),
237 #endif
238
239 #ifdef TABDLY
240         MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY),
241         MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY),
242         MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY),
243         MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY),
244 #else
245 # ifdef OXTABS
246         MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 ),
247 # endif
248 #endif
249
250 #ifdef BSDLY
251         MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY),
252         MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY),
253 #endif
254 #ifdef VTDLY
255         MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY),
256         MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY),
257 #endif
258 #ifdef FFDLY
259         MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY),
260         MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY),
261 #endif
262         MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 ),
263         MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 ),
264 #ifdef IEXTEN
265         MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 ),
266 #endif
267         MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 ),
268         MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 ),
269         MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 ),
270         MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 ),
271         MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 ),
272         MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 ),
273 #ifdef XCASE
274         MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 ),
275 #endif
276 #ifdef TOSTOP
277         MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 ),
278 #endif
279 #ifdef ECHOPRT
280         MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 ),
281         MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 ),
282 #endif
283 #ifdef ECHOCTL
284         MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 ),
285         MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 ),
286 #endif
287 #ifdef ECHOKE
288         MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 ),
289         MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 ),
290 #endif
291         MI_ENTRY(evenp,      combination, REV        | OMIT, 0,          0 ),
292         MI_ENTRY(parity,     combination, REV        | OMIT, 0,          0 ),
293         MI_ENTRY(stty_oddp,  combination, REV        | OMIT, 0,          0 ),
294         MI_ENTRY(stty_nl,    combination, REV        | OMIT, 0,          0 ),
295         MI_ENTRY(stty_ek,    combination, OMIT,              0,          0 ),
296         MI_ENTRY(stty_sane,  combination, OMIT,              0,          0 ),
297         MI_ENTRY(cooked,     combination, REV        | OMIT, 0,          0 ),
298         MI_ENTRY(raw,        combination, REV        | OMIT, 0,          0 ),
299         MI_ENTRY(stty_pass8, combination, REV        | OMIT, 0,          0 ),
300         MI_ENTRY(litout,     combination, REV        | OMIT, 0,          0 ),
301         MI_ENTRY(cbreak,     combination, REV        | OMIT, 0,          0 ),
302 #ifdef IXANY
303         MI_ENTRY(decctlq,    combination, REV        | OMIT, 0,          0 ),
304 #endif
305 #if defined(TABDLY) || defined(OXTABS)
306         MI_ENTRY(stty_tabs,  combination, REV        | OMIT, 0,          0 ),
307 #endif
308 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
309         MI_ENTRY(stty_lcase, combination, REV        | OMIT, 0,          0 ),
310         MI_ENTRY(stty_LCASE, combination, REV        | OMIT, 0,          0 ),
311 #endif
312         MI_ENTRY(stty_crt,   combination, OMIT,              0,          0 ),
313         MI_ENTRY(stty_dec,   combination, OMIT,              0,          0 ),
314 };
315
316 enum {
317         NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
318 };
319
320 /* Control character settings */
321 struct control_info {
322         const char *name;                       /* Name given on command line */
323         unsigned char saneval;          /* Value to set for 'stty sane' */
324         unsigned char offset;                           /* Offset in c_cc */
325 };
326
327 /* Control characters */
328
329 static const struct control_info control_info[] = {
330         {"intr",     CINTR,   VINTR},
331         {"quit",     CQUIT,   VQUIT},
332         {"erase",    CERASE,  VERASE},
333         {"kill",     CKILL,   VKILL},
334         {stty_eof,   CEOF,    VEOF},
335         {stty_eol,   CEOL,    VEOL},
336 #ifdef VEOL2
337         {"eol2",     CEOL2,   VEOL2},
338 #endif
339 #ifdef VSWTCH
340         {stty_swtch, CSWTCH,  VSWTCH},
341 #endif
342         {"start",    CSTART,  VSTART},
343         {"stop",     CSTOP,   VSTOP},
344         {"susp",     CSUSP,   VSUSP},
345 #ifdef VDSUSP
346         {"dsusp",    CDSUSP,  VDSUSP},
347 #endif
348 #ifdef VREPRINT
349         {"rprnt",    CRPRNT,  VREPRINT},
350 #endif
351 #ifdef VWERASE
352         {"werase",   CWERASE, VWERASE},
353 #endif
354 #ifdef VLNEXT
355         {"lnext",    CLNEXT,  VLNEXT},
356 #endif
357 #ifdef VFLUSHO
358         {"flush",    CFLUSHO, VFLUSHO},
359 #endif
360 #ifdef VSTATUS
361         {"status",   CSTATUS, VSTATUS},
362 #endif
363         /* These must be last because of the display routines */
364         {stty_min,   1,       VMIN},
365         {stty_time,  0,       VTIME},
366 };
367
368 enum {
369         NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
370 };
371
372 /* The width of the screen, for output wrapping */
373 static unsigned max_col = 80; /* default */
374 /* Current position, to know when to wrap */
375 static unsigned current_col;
376 static const char *device_name = bb_msg_standard_input;
377
378 /* Return a string that is the printable representation of character CH */
379 /* Adapted from 'cat' by Torbjorn Granlund */
380 static const char *visible(unsigned int ch)
381 {
382         static char buf[10];
383         char *bpout = buf;
384
385         if (ch == _POSIX_VDISABLE)
386                 return "<undef>";
387
388         if (ch >= 128) {
389                 ch -= 128;
390                 *bpout++ = 'M';
391                 *bpout++ = '-';
392         }
393
394         if (ch < 32) {
395                 *bpout++ = '^';
396                 *bpout++ = ch + 64;
397         } else if (ch < 127) {
398                 *bpout++ = ch;
399         } else {
400                 *bpout++ = '^';
401                 *bpout++ = '?';
402         }
403
404         *bpout = '\0';
405         return buf;
406 }
407
408 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
409 {
410         static const unsigned char tcflag_offsets[] = {
411                 offsetof(struct termios, c_cflag), /* control */
412                 offsetof(struct termios, c_iflag), /* input */
413                 offsetof(struct termios, c_oflag), /* output */
414                 offsetof(struct termios, c_lflag), /* local */
415         };
416
417         if (type <= local) {
418                 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
419         }
420         return NULL;
421 }
422
423 static speed_t string_to_baud_or_die(const char *arg)
424 {
425         return tty_value_to_baud(xatou(arg));
426 }
427
428 static void set_speed_or_die(enum speed_setting type, const char *arg,
429                                         struct termios *mode)
430 {
431         speed_t baud;
432
433         baud = string_to_baud_or_die(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 /* No, inline won't be as efficient (gcc 3.4.3) */
454 #define streq(a,b) (!strcmp((a),(b)))
455
456 /* Print format string MESSAGE and optional args.
457    Wrap to next line first if it won't fit.
458    Print a space first unless MESSAGE will start a new line */
459 static void wrapf(const char *message, ...)
460 {
461         char buf[128];
462         va_list args;
463         int buflen;
464
465         va_start(args, message);
466         vsnprintf(buf, sizeof(buf), message, args);
467         va_end(args);
468         buflen = strlen(buf);
469         if (!buflen) return;
470
471         if (current_col > 0) {
472                 current_col++;
473                 if (buf[0] != '\n') {
474                         if (current_col + buflen >= max_col) {
475                                 putchar('\n');
476                                 current_col = 0;
477                         } else
478                                 putchar(' ');
479                 }
480         }
481         fputs(buf, stdout);
482         current_col += buflen;
483         if (buf[buflen-1] == '\n')
484                 current_col = 0;
485 }
486
487 #ifdef TIOCGWINSZ
488
489 static int get_win_size(int fd, struct winsize *win)
490 {
491         return ioctl(fd, TIOCGWINSZ, (char *) win);
492 }
493
494 static void set_window_size(int rows, int cols)
495 {
496         struct winsize win;
497
498         if (get_win_size(STDIN_FILENO, &win)) {
499                 if (errno != EINVAL) {
500                         perror_on_device("%s");
501                         return;
502                 }
503                 memset(&win, 0, sizeof(win));
504         }
505
506         if (rows >= 0)
507                 win.ws_row = rows;
508         if (cols >= 0)
509                 win.ws_col = cols;
510
511 # ifdef TIOCSSIZE
512         /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
513            The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
514            This comment from sys/ttold.h describes Sun's twisted logic - a better
515            test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
516            At any rate, the problem is gone in Solaris 2.x */
517
518         if (win.ws_row == 0 || win.ws_col == 0) {
519                 struct ttysize ttysz;
520
521                 ttysz.ts_lines = win.ws_row;
522                 ttysz.ts_cols = win.ws_col;
523
524                 win.ws_row = win.ws_col = 1;
525
526                 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
527                 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
528                         perror_on_device("%s");
529                 }
530                 return;
531         }
532 # endif
533
534         if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
535                 perror_on_device("%s");
536 }
537
538 static void display_window_size(int fancy)
539 {
540         const char *fmt_str = "%s\0%s: no size information for this device";
541         struct winsize win;
542
543         if (get_win_size(STDIN_FILENO, &win)) {
544                 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
545                         perror_on_device(fmt_str);
546                 }
547         } else {
548                 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
549                                 win.ws_row, win.ws_col);
550         }
551 }
552
553 #else /* !TIOCGWINSZ */
554
555 static inline void display_window_size(int fancy) {}
556
557 #endif /* !TIOCGWINSZ */
558
559 static int screen_columns_or_die(void)
560 {
561         const char *s;
562
563 #ifdef TIOCGWINSZ
564         struct winsize win;
565
566         /* With Solaris 2.[123], this ioctl fails and errno is set to
567            EINVAL for telnet (but not rlogin) sessions.
568            On ISC 3.0, it fails for the console and the serial port
569            (but it works for ptys).
570            It can also fail on any system when stdout isn't a tty.
571            In case of any failure, just use the default */
572         if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
573                 return win.ws_col;
574 #endif
575
576         s = getenv("COLUMNS");
577         if (s)
578                 return xatoi_u(s);
579         return 80;
580 }
581
582 static const struct suffix_mult stty_suffixes[] = {
583         {"b",  512 },
584         {"k",  1024},
585         {"B",  1024},
586         {NULL, 0   }
587 };
588
589 static const struct mode_info *find_mode(const char *name)
590 {
591         int i;
592         for (i = 0; i < NUM_mode_info; ++i)
593                 if (streq(name, mode_info[i].name))
594                         return &mode_info[i];
595         return 0;
596 }
597
598 static const struct control_info *find_control(const char *name)
599 {
600         int i;
601         for (i = 0; i < NUM_control_info; ++i)
602                 if (streq(name, control_info[i].name))
603                         return &control_info[i];
604         return 0;
605 }
606
607 enum {
608         param_need_arg = 0x80,
609         param_line   = 1 | 0x80,
610         param_rows   = 2 | 0x80,
611         param_cols   = 3 | 0x80,
612         param_size   = 4,
613         param_speed  = 5,
614         param_ispeed = 6 | 0x80,
615         param_ospeed = 7 | 0x80,
616 };
617
618 static int find_param(const char *name)
619 {
620 #ifdef HAVE_C_LINE
621         if (streq(name, "line")) return param_line;
622 #endif
623 #ifdef TIOCGWINSZ
624         if (streq(name, "rows")) return param_rows;
625         if (streq(name, "cols")) return param_cols;
626         if (streq(name, "columns")) return param_cols;
627         if (streq(name, "size")) return param_size;
628 #endif
629         if (streq(name, "speed")) return param_speed;
630         if (streq(name, "ispeed")) return param_ispeed;
631         if (streq(name, "ospeed")) return param_ospeed;
632         return 0;
633 }
634
635
636 static int recover_mode(const char *arg, struct termios *mode);
637 static void set_mode(const struct mode_info *info,
638                                 int reversed, struct termios *mode);
639 static void display_all(const struct termios *mode);
640 static void display_changed(const struct termios *mode);
641 static void display_recoverable(const struct termios *mode);
642 static void display_speed(const struct termios *mode, int fancy);
643 static void sane_mode(struct termios *mode);
644 static void set_control_char_or_die(const struct control_info *info,
645                                 const char *arg, struct termios *mode);
646
647 int stty_main(int argc, char **argv)
648 {
649         struct termios mode;
650         void (*output_func)(const struct termios *);
651         const char *file_name = NULL;
652         int require_set_attr;
653         int speed_was_set;
654         int verbose_output;
655         int recoverable_output;
656         int noargs;
657         int k;
658
659         output_func = display_changed;
660         noargs = 1;
661         speed_was_set = 0;
662         require_set_attr = 0;
663         verbose_output = 0;
664         recoverable_output = 0;
665
666         /* First pass: only parse/verify command line params */
667         k = 0;
668         while (argv[++k]) {
669                 const struct mode_info *mp;
670                 const struct control_info *cp;
671                 const char *arg = argv[k];
672                 const char *argnext = argv[k+1];
673                 int param;
674
675                 if (arg[0] == '-') {
676                         int i;
677                         mp = find_mode(arg+1);
678                         if (mp) {
679                                 if (!(mp->flags & REV))
680                                         bb_error_msg_and_die("invalid argument '%s'", arg);
681                                 noargs = 0;
682                                 continue;
683                         }
684                         /* It is an option - parse it */
685                         i = 0;
686                         while (arg[++i]) {
687                                 switch (arg[i]) {
688                                 case 'a':
689                                         verbose_output = 1;
690                                         output_func = display_all;
691                                         break;
692                                 case 'g':
693                                         recoverable_output = 1;
694                                         output_func = display_recoverable;
695                                         break;
696                                 case 'F':
697                                         if (file_name)
698                                                 bb_error_msg_and_die("only one device may be specified");
699                                         file_name = &arg[i+1]; /* "-Fdevice" ? */
700                                         if (!file_name[0]) { /* nope, "-F device" */
701                                                 int p = k+1; /* argv[p] is argnext */
702                                                 file_name = argnext;
703                                                 if (!file_name)
704                                                         bb_error_msg_and_die(bb_msg_requires_arg, "-F");
705                                                 /* remove -F param from arg[vc] */
706                                                 --argc;
707                                                 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
708                                         }
709                                         goto end_option;
710                                 default:
711                                         bb_error_msg_and_die("invalid argument '%s'", arg);
712                                 }
713                         }
714 end_option:
715                         continue;
716                 }
717
718                 mp = find_mode(arg);
719                 if (mp) {
720                         noargs = 0;
721                         continue;
722                 }
723
724                 cp = find_control(arg);
725                 if (cp) {
726                         if (!argnext)
727                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
728                         /* called for the side effect of xfunc death only */
729                         set_control_char_or_die(cp, argnext, &mode);
730                         noargs = 0;
731                         ++k;
732                         continue;
733                 }
734
735                 param = find_param(arg);
736                 if (param & param_need_arg) {
737                         if (!argnext)
738                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
739                         ++k;
740                 }
741
742                 switch (param) {
743 #ifdef HAVE_C_LINE
744                 case param_line:
745 # ifndef TIOCGWINSZ
746                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
747                         break;
748 # endif /* else fall-through */
749 #endif
750 #ifdef TIOCGWINSZ
751                 case param_rows:
752                 case param_cols:
753                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
754                         break;
755                 case param_size:
756 #endif
757                 case param_speed:
758                         break;
759                 case param_ispeed:
760                         /* called for the side effect of xfunc death only */
761                         set_speed_or_die(input_speed, argnext, &mode);
762                         break;
763                 case param_ospeed:
764                         /* called for the side effect of xfunc death only */
765                         set_speed_or_die(output_speed, argnext, &mode);
766                         break;
767                 default:
768                         if (recover_mode(arg, &mode) == 1) break;
769                         if (string_to_baud_or_die(arg) != (speed_t) -1) break;
770                         bb_error_msg_and_die("invalid argument '%s'", arg);
771                 }
772                 noargs = 0;
773         }
774
775         /* Specifying both -a and -g is an error */
776         if (verbose_output && recoverable_output)
777                 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
778         /* Specifying -a or -g with non-options is an error */
779         if (!noargs && (verbose_output || recoverable_output))
780                 bb_error_msg_and_die("modes may not be set when specifying an output style");
781
782         /* Now it is safe to start doing things */
783         if (file_name) {
784                 int fd, fdflags;
785                 device_name = file_name;
786                 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
787                 if (fd != STDIN_FILENO) {
788                         dup2(fd, STDIN_FILENO);
789                         close(fd);
790                 }
791                 fdflags = fcntl(STDIN_FILENO, F_GETFL);
792                 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
793                         perror_on_device_and_die("%s: cannot reset non-blocking mode");
794         }
795
796         /* Initialize to all zeroes so there is no risk memcmp will report a
797            spurious difference in an uninitialized portion of the structure */
798         memset(&mode, 0, sizeof(mode));
799         if (tcgetattr(STDIN_FILENO, &mode))
800                 perror_on_device_and_die("%s");
801
802         if (verbose_output || recoverable_output || noargs) {
803                 max_col = screen_columns_or_die();
804                 output_func(&mode);
805                 return EXIT_SUCCESS;
806         }
807
808         /* Second pass: perform actions */
809         k = 0;
810         while (argv[++k]) {
811                 const struct mode_info *mp;
812                 const struct control_info *cp;
813                 const char *arg = argv[k];
814                 const char *argnext = argv[k+1];
815                 int param;
816
817                 if (arg[0] == '-') {
818                         mp = find_mode(arg+1);
819                         if (mp) {
820                                 set_mode(mp, 1 /* reversed */, &mode);
821                         }
822                         /* It is an option - already parsed. Skip it */
823                         continue;
824                 }
825
826                 mp = find_mode(arg);
827                 if (mp) {
828                         set_mode(mp, 0 /* non-reversed */, &mode);
829                         continue;
830                 }
831
832                 cp = find_control(arg);
833                 if (cp) {
834                         ++k;
835                         set_control_char_or_die(cp, argnext, &mode);
836                         continue;
837                 }
838
839                 param = find_param(arg);
840                 if (param & param_need_arg) {
841                         ++k;
842                 }
843
844                 switch (param) {
845 #ifdef HAVE_C_LINE
846                 case param_line:
847                         mode.c_line = xatoul_sfx(argnext, stty_suffixes);
848                         require_set_attr = 1;
849                         break;
850 #endif
851 #ifdef TIOCGWINSZ
852                 case param_cols:
853                         set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
854                         break;
855                 case param_size:
856                         display_window_size(0);
857                         break;
858                 case param_rows:
859                         set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
860                         break;
861 #endif
862                 case param_speed:
863                         display_speed(&mode, 0);
864                         break;
865                 case param_ispeed:
866                         set_speed_or_die(input_speed, argnext, &mode);
867                         speed_was_set = 1;
868                         require_set_attr = 1;
869                         break;
870                 case param_ospeed:
871                         set_speed_or_die(output_speed, argnext, &mode);
872                         speed_was_set = 1;
873                         require_set_attr = 1;
874                         break;
875                 default:
876                         if (recover_mode(arg, &mode) == 1)
877                                 require_set_attr = 1;
878                         else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
879                                 set_speed_or_die(both_speeds, arg, &mode);
880                                 speed_was_set = 1;
881                                 require_set_attr = 1;
882                         } /* else - impossible (caught in the first pass):
883                                 bb_error_msg_and_die("invalid argument '%s'", arg); */
884                 }
885         }
886
887         if (require_set_attr) {
888                 struct termios new_mode;
889
890                 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
891                         perror_on_device_and_die("%s");
892
893                 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
894                    it performs *any* of the requested operations.  This means it
895                    can report 'success' when it has actually failed to perform
896                    some proper subset of the requested operations.  To detect
897                    this partial failure, get the current terminal attributes and
898                    compare them to the requested ones */
899
900                 /* Initialize to all zeroes so there is no risk memcmp will report a
901                    spurious difference in an uninitialized portion of the structure */
902                 memset(&new_mode, 0, sizeof(new_mode));
903                 if (tcgetattr(STDIN_FILENO, &new_mode))
904                         perror_on_device_and_die("%s");
905
906                 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
907 #ifdef CIBAUD
908                         /* SunOS 4.1.3 (at least) has the problem that after this sequence,
909                            tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
910                            sometimes (m1 != m2).  The only difference is in the four bits
911                            of the c_cflag field corresponding to the baud rate.  To save
912                            Sun users a little confusion, don't report an error if this
913                            happens.  But suppress the error only if we haven't tried to
914                            set the baud rate explicitly -- otherwise we'd never give an
915                            error for a true failure to set the baud rate */
916
917                         new_mode.c_cflag &= (~CIBAUD);
918                         if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
919 #endif
920                                 perror_on_device_and_die ("%s: unable to perform all requested operations");
921                 }
922         }
923
924         return EXIT_SUCCESS;
925 }
926
927 /* Save set_mode from #ifdef forest plague */
928 #ifndef ONLCR
929 #define ONLCR 0
930 #endif
931 #ifndef OCRNL
932 #define OCRNL 0
933 #endif
934 #ifndef ONLRET
935 #define ONLRET 0
936 #endif
937 #ifndef XCASE
938 #define XCASE 0
939 #endif
940 #ifndef IXANY
941 #define IXANY 0
942 #endif
943 #ifndef TABDLY
944 #define TABDLY 0
945 #endif
946 #ifndef OXTABS
947 #define OXTABS 0
948 #endif
949 #ifndef IUCLC
950 #define IUCLC 0
951 #endif
952 #ifndef OLCUC
953 #define OLCUC 0
954 #endif
955 #ifndef ECHOCTL
956 #define ECHOCTL 0
957 #endif
958 #ifndef ECHOKE
959 #define ECHOKE 0
960 #endif
961
962 static void set_mode(const struct mode_info *info, int reversed,
963                                         struct termios *mode)
964 {
965         tcflag_t *bitsp;
966
967         bitsp = mode_type_flag(info->type, mode);
968
969         if (bitsp) {
970                 if (reversed)
971                         *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
972                 else
973                         *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
974                 return;
975         }
976
977         /* Combination mode */
978         if (info->name == evenp || info->name == parity) {
979                 if (reversed)
980                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
981                 else
982                         mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
983         } else if (info->name == stty_oddp) {
984                 if (reversed)
985                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
986                 else
987                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
988         } else if (info->name == stty_nl) {
989                 if (reversed) {
990                         mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
991                         mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
992                 } else {
993                         mode->c_iflag = mode->c_iflag & ~ICRNL;
994                         if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
995                 }
996         } else if (info->name == stty_ek) {
997                 mode->c_cc[VERASE] = CERASE;
998                 mode->c_cc[VKILL] = CKILL;
999         } else if (info->name == stty_sane) {
1000                 sane_mode(mode);
1001         }
1002         else if (info->name == cbreak) {
1003                 if (reversed)
1004                         mode->c_lflag |= ICANON;
1005                 else
1006                         mode->c_lflag &= ~ICANON;
1007         } else if (info->name == stty_pass8) {
1008                 if (reversed) {
1009                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1010                         mode->c_iflag |= ISTRIP;
1011                 } else {
1012                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1013                         mode->c_iflag &= ~ISTRIP;
1014                 }
1015         } else if (info->name == litout) {
1016                 if (reversed) {
1017                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1018                         mode->c_iflag |= ISTRIP;
1019                         mode->c_oflag |= OPOST;
1020                 } else {
1021                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1022                         mode->c_iflag &= ~ISTRIP;
1023                         mode->c_oflag &= ~OPOST;
1024                 }
1025         } else if (info->name == raw || info->name == cooked) {
1026                 if ((info->name[0] == 'r' && reversed)
1027                         || (info->name[0] == 'c' && !reversed)) {
1028                         /* Cooked mode */
1029                         mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1030                         mode->c_oflag |= OPOST;
1031                         mode->c_lflag |= ISIG | ICANON;
1032 #if VMIN == VEOF
1033                         mode->c_cc[VEOF] = CEOF;
1034 #endif
1035 #if VTIME == VEOL
1036                         mode->c_cc[VEOL] = CEOL;
1037 #endif
1038                 } else {
1039                         /* Raw mode */
1040                         mode->c_iflag = 0;
1041                         mode->c_oflag &= ~OPOST;
1042                         mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1043                         mode->c_cc[VMIN] = 1;
1044                         mode->c_cc[VTIME] = 0;
1045                 }
1046         }
1047         else if (IXANY && info->name == decctlq) {
1048                 if (reversed)
1049                         mode->c_iflag |= IXANY;
1050                 else
1051                         mode->c_iflag &= ~IXANY;
1052         }
1053         else if (TABDLY && info->name == stty_tabs) {
1054                 if (reversed)
1055                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1056                 else
1057                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1058         }
1059         else if (OXTABS && info->name == stty_tabs) {
1060                 if (reversed)
1061                         mode->c_oflag |= OXTABS;
1062                 else
1063                         mode->c_oflag &= ~OXTABS;
1064         }
1065         else if (XCASE && IUCLC && OLCUC
1066         && (info->name == stty_lcase || info->name == stty_LCASE)) {
1067                 if (reversed) {
1068                         mode->c_lflag &= ~XCASE;
1069                         mode->c_iflag &= ~IUCLC;
1070                         mode->c_oflag &= ~OLCUC;
1071                 } else {
1072                         mode->c_lflag |= XCASE;
1073                         mode->c_iflag |= IUCLC;
1074                         mode->c_oflag |= OLCUC;
1075                 }
1076         }
1077         else if (info->name == stty_crt) {
1078                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1079         }
1080         else if (info->name == stty_dec) {
1081                 mode->c_cc[VINTR] = 3; /* ^C */
1082                 mode->c_cc[VERASE] = 127; /* DEL */
1083                 mode->c_cc[VKILL] = 21; /* ^U */
1084                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1085                 if (IXANY) mode->c_iflag &= ~IXANY;
1086         }
1087 }
1088
1089 static void set_control_char_or_die(const struct control_info *info,
1090                         const char *arg, struct termios *mode)
1091 {
1092         unsigned char value;
1093
1094         if (info->name == stty_min || info->name == stty_time)
1095                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1096         else if (arg[0] == '\0' || arg[1] == '\0')
1097                 value = arg[0];
1098         else if (streq(arg, "^-") || streq(arg, "undef"))
1099                 value = _POSIX_VDISABLE;
1100         else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1101                 value = arg[1] & 0x1f; /* Non-letters get weird results */
1102                 if (arg[1] == '?')
1103                         value = 127;
1104         } else
1105                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1106         mode->c_cc[info->offset] = value;
1107 }
1108
1109 static void display_changed(const struct termios *mode)
1110 {
1111         int i;
1112         tcflag_t *bitsp;
1113         unsigned long mask;
1114         int prev_type = control;
1115
1116         display_speed(mode, 1);
1117 #ifdef HAVE_C_LINE
1118         wrapf("line = %d;\n", mode->c_line);
1119 #else
1120         wrapf("\n");
1121 #endif
1122
1123         for (i = 0; control_info[i].name != stty_min; ++i) {
1124                 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1125                         continue;
1126                 /* If swtch is the same as susp, don't print both */
1127 #if VSWTCH == VSUSP
1128                 if (control_info[i].name == stty_swtch)
1129                         continue;
1130 #endif
1131                 /* If eof uses the same slot as min, only print whichever applies */
1132 #if VEOF == VMIN
1133                 if ((mode->c_lflag & ICANON) == 0
1134                         && (control_info[i].name == stty_eof
1135                                 || control_info[i].name == stty_eol)) continue;
1136 #endif
1137                 wrapf("%s = %s;", control_info[i].name,
1138                           visible(mode->c_cc[control_info[i].offset]));
1139         }
1140         if ((mode->c_lflag & ICANON) == 0) {
1141                 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
1142                           (int) mode->c_cc[VTIME]);
1143         }
1144         if (current_col) wrapf("\n");
1145
1146         for (i = 0; i < NUM_mode_info; ++i) {
1147                 if (mode_info[i].flags & OMIT)
1148                         continue;
1149                 if (mode_info[i].type != prev_type) {
1150                         if (current_col) wrapf("\n");
1151                         prev_type = mode_info[i].type;
1152                 }
1153
1154                 bitsp = mode_type_flag(mode_info[i].type, mode);
1155                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1156                 if ((*bitsp & mask) == mode_info[i].bits) {
1157                         if (mode_info[i].flags & SANE_UNSET) {
1158                                 wrapf("%s", mode_info[i].name);
1159                         }
1160                 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
1161                         wrapf("-%s", mode_info[i].name);
1162                 }
1163         }
1164         if (current_col) wrapf("\n");
1165 }
1166
1167 static void display_all(const struct termios *mode)
1168 {
1169         int i;
1170         tcflag_t *bitsp;
1171         unsigned long mask;
1172         int prev_type = control;
1173
1174         display_speed(mode, 1);
1175         display_window_size(1);
1176 #ifdef HAVE_C_LINE
1177         wrapf("line = %d;\n", mode->c_line);
1178 #else
1179         wrapf("\n");
1180 #endif
1181
1182         for (i = 0; control_info[i].name != stty_min; ++i) {
1183                 /* If swtch is the same as susp, don't print both */
1184 #if VSWTCH == VSUSP
1185                 if (control_info[i].name == stty_swtch)
1186                         continue;
1187 #endif
1188                 /* If eof uses the same slot as min, only print whichever applies */
1189 #if VEOF == VMIN
1190                 if ((mode->c_lflag & ICANON) == 0
1191                         && (control_info[i].name == stty_eof
1192                                 || control_info[i].name == stty_eol)) continue;
1193 #endif
1194                 wrapf("%s = %s;", control_info[i].name,
1195                           visible(mode->c_cc[control_info[i].offset]));
1196         }
1197 #if VEOF == VMIN
1198         if ((mode->c_lflag & ICANON) == 0)
1199 #endif
1200                 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1201         if (current_col) wrapf("\n");
1202
1203         for (i = 0; i < NUM_mode_info; ++i) {
1204                 if (mode_info[i].flags & OMIT)
1205                         continue;
1206                 if (mode_info[i].type != prev_type) {
1207                         wrapf("\n");
1208                         prev_type = mode_info[i].type;
1209                 }
1210
1211                 bitsp = mode_type_flag(mode_info[i].type, mode);
1212                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1213                 if ((*bitsp & mask) == mode_info[i].bits)
1214                         wrapf("%s", mode_info[i].name);
1215                 else if (mode_info[i].flags & REV)
1216                         wrapf("-%s", mode_info[i].name);
1217         }
1218         if (current_col) wrapf("\n");
1219 }
1220
1221 static void display_speed(const struct termios *mode, int fancy)
1222 {
1223                              //01234567 8 9
1224         const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1225         unsigned long ispeed, ospeed;
1226
1227         ospeed = ispeed = cfgetispeed(mode);
1228         if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1229                 ispeed = ospeed;                /* in case ispeed was 0 */
1230                          //0123 4 5 6 7 8 9
1231                 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1232         }
1233         if (fancy) fmt_str += 9;
1234         wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1235 }
1236
1237 static void display_recoverable(const struct termios *mode)
1238 {
1239         int i;
1240         printf("%lx:%lx:%lx:%lx",
1241                    (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1242                    (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1243         for (i = 0; i < NCCS; ++i)
1244                 printf(":%x", (unsigned int) mode->c_cc[i]);
1245         putchar('\n');
1246 }
1247
1248 static int recover_mode(const char *arg, struct termios *mode)
1249 {
1250         int i, n;
1251         unsigned int chr;
1252         unsigned long iflag, oflag, cflag, lflag;
1253
1254         /* Scan into temporaries since it is too much trouble to figure out
1255            the right format for 'tcflag_t' */
1256         if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1257                            &iflag, &oflag, &cflag, &lflag, &n) != 4)
1258                 return 0;
1259         mode->c_iflag = iflag;
1260         mode->c_oflag = oflag;
1261         mode->c_cflag = cflag;
1262         mode->c_lflag = lflag;
1263         arg += n;
1264         for (i = 0; i < NCCS; ++i) {
1265                 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1266                         return 0;
1267                 mode->c_cc[i] = chr;
1268                 arg += n;
1269         }
1270
1271         /* Fail if there are too many fields */
1272         if (*arg != '\0')
1273                 return 0;
1274
1275         return 1;
1276 }
1277
1278 static void sane_mode(struct termios *mode)
1279 {
1280         int i;
1281         tcflag_t *bitsp;
1282
1283         for (i = 0; i < NUM_control_info; ++i) {
1284 #if VMIN == VEOF
1285                 if (control_info[i].name == stty_min)
1286                         break;
1287 #endif
1288                 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1289         }
1290
1291         for (i = 0; i < NUM_mode_info; ++i) {
1292                 if (mode_info[i].flags & SANE_SET) {
1293                         bitsp = mode_type_flag(mode_info[i].type, mode);
1294                         *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1295                                 | mode_info[i].bits;
1296                 } else if (mode_info[i].flags & SANE_UNSET) {
1297                         bitsp = mode_type_flag(mode_info[i].type, mode);
1298                         *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1299                                 & ~mode_info[i].bits;
1300                 }
1301         }
1302 }