a bit more IPv6-ization work
[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         /* were using short here, but ppc32 was unhappy: */
165         tcflag_t mask;          /* Other bits to turn off for this mode */
166         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 *name;               /* Name given on command line */
328         unsigned char saneval;          /* Value to set for 'stty sane' */
329         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 speed_t string_to_baud_or_die(const char *arg)
429 {
430         return tty_value_to_baud(xatou(arg));
431 }
432
433 static void set_speed_or_die(enum speed_setting type, const char *arg,
434                                         struct termios *mode)
435 {
436         speed_t baud;
437
438         baud = string_to_baud_or_die(arg);
439
440         if (type != output_speed) {     /* either input or both */
441                 cfsetispeed(mode, baud);
442         }
443         if (type != input_speed) {      /* either output or both */
444                 cfsetospeed(mode, baud);
445         }
446 }
447
448 static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
449 {
450         bb_perror_msg_and_die(fmt, device_name);
451 }
452
453 static void perror_on_device(const char *fmt)
454 {
455         bb_perror_msg(fmt, device_name);
456 }
457
458 /* No, inline won't be as efficient (gcc 3.4.3) */
459 #define streq(a,b) (!strcmp((a),(b)))
460
461 /* Print format string MESSAGE and optional args.
462    Wrap to next line first if it won't fit.
463    Print a space first unless MESSAGE will start a new line */
464 static void wrapf(const char *message, ...)
465 {
466         char buf[128];
467         va_list args;
468         int buflen;
469
470         va_start(args, message);
471         vsnprintf(buf, sizeof(buf), message, args);
472         va_end(args);
473         buflen = strlen(buf);
474         if (!buflen) return;
475
476         if (current_col > 0) {
477                 current_col++;
478                 if (buf[0] != '\n') {
479                         if (current_col + buflen >= max_col) {
480                                 putchar('\n');
481                                 current_col = 0;
482                         } else
483                                 putchar(' ');
484                 }
485         }
486         fputs(buf, stdout);
487         current_col += buflen;
488         if (buf[buflen-1] == '\n')
489                 current_col = 0;
490 }
491
492 #ifdef TIOCGWINSZ
493
494 static int get_win_size(int fd, struct winsize *win)
495 {
496         return ioctl(fd, TIOCGWINSZ, (char *) win);
497 }
498
499 static void set_window_size(int rows, int cols)
500 {
501         struct winsize win;
502
503         if (get_win_size(STDIN_FILENO, &win)) {
504                 if (errno != EINVAL) {
505                         perror_on_device("%s");
506                         return;
507                 }
508                 memset(&win, 0, sizeof(win));
509         }
510
511         if (rows >= 0)
512                 win.ws_row = rows;
513         if (cols >= 0)
514                 win.ws_col = cols;
515
516 # ifdef TIOCSSIZE
517         /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
518            The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
519            This comment from sys/ttold.h describes Sun's twisted logic - a better
520            test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
521            At any rate, the problem is gone in Solaris 2.x */
522
523         if (win.ws_row == 0 || win.ws_col == 0) {
524                 struct ttysize ttysz;
525
526                 ttysz.ts_lines = win.ws_row;
527                 ttysz.ts_cols = win.ws_col;
528
529                 win.ws_row = win.ws_col = 1;
530
531                 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
532                 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
533                         perror_on_device("%s");
534                 }
535                 return;
536         }
537 # endif
538
539         if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
540                 perror_on_device("%s");
541 }
542
543 static void display_window_size(int fancy)
544 {
545         const char *fmt_str = "%s\0%s: no size information for this device";
546         struct winsize win;
547
548         if (get_win_size(STDIN_FILENO, &win)) {
549                 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
550                         perror_on_device(fmt_str);
551                 }
552         } else {
553                 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
554                                 win.ws_row, win.ws_col);
555         }
556 }
557
558 #else /* !TIOCGWINSZ */
559
560 static inline void display_window_size(int fancy) {}
561
562 #endif /* !TIOCGWINSZ */
563
564 static int screen_columns_or_die(void)
565 {
566         const char *s;
567
568 #ifdef TIOCGWINSZ
569         struct winsize win;
570
571         /* With Solaris 2.[123], this ioctl fails and errno is set to
572            EINVAL for telnet (but not rlogin) sessions.
573            On ISC 3.0, it fails for the console and the serial port
574            (but it works for ptys).
575            It can also fail on any system when stdout isn't a tty.
576            In case of any failure, just use the default */
577         if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
578                 return win.ws_col;
579 #endif
580
581         s = getenv("COLUMNS");
582         if (s)
583                 return xatoi_u(s);
584         return 80;
585 }
586
587 static const struct suffix_mult stty_suffixes[] = {
588         {"b",  512 },
589         {"k",  1024},
590         {"B",  1024},
591         {NULL, 0   }
592 };
593
594 static const struct mode_info *find_mode(const char *name)
595 {
596         int i;
597         for (i = 0; i < NUM_mode_info; ++i)
598                 if (streq(name, mode_info[i].name))
599                         return &mode_info[i];
600         return 0;
601 }
602
603 static const struct control_info *find_control(const char *name)
604 {
605         int i;
606         for (i = 0; i < NUM_control_info; ++i)
607                 if (streq(name, control_info[i].name))
608                         return &control_info[i];
609         return 0;
610 }
611
612 enum {
613         param_need_arg = 0x80,
614         param_line   = 1 | 0x80,
615         param_rows   = 2 | 0x80,
616         param_cols   = 3 | 0x80,
617         param_size   = 4,
618         param_speed  = 5,
619         param_ispeed = 6 | 0x80,
620         param_ospeed = 7 | 0x80,
621 };
622
623 static int find_param(const char *name)
624 {
625 #ifdef HAVE_C_LINE
626         if (streq(name, "line")) return param_line;
627 #endif
628 #ifdef TIOCGWINSZ
629         if (streq(name, "rows")) return param_rows;
630         if (streq(name, "cols")) return param_cols;
631         if (streq(name, "columns")) return param_cols;
632         if (streq(name, "size")) return param_size;
633 #endif
634         if (streq(name, "speed")) return param_speed;
635         if (streq(name, "ispeed")) return param_ispeed;
636         if (streq(name, "ospeed")) return param_ospeed;
637         return 0;
638 }
639
640
641 static int recover_mode(const char *arg, struct termios *mode);
642 static void set_mode(const struct mode_info *info,
643                                 int reversed, struct termios *mode);
644 static void display_all(const struct termios *mode);
645 static void display_changed(const struct termios *mode);
646 static void display_recoverable(const struct termios *mode);
647 static void display_speed(const struct termios *mode, int fancy);
648 static void sane_mode(struct termios *mode);
649 static void set_control_char_or_die(const struct control_info *info,
650                                 const char *arg, struct termios *mode);
651
652 int stty_main(int argc, char **argv)
653 {
654         struct termios mode;
655         void (*output_func)(const struct termios *);
656         const char *file_name = NULL;
657         int require_set_attr;
658         int speed_was_set;
659         int verbose_output;
660         int recoverable_output;
661         int noargs;
662         int k;
663
664         output_func = display_changed;
665         noargs = 1;
666         speed_was_set = 0;
667         require_set_attr = 0;
668         verbose_output = 0;
669         recoverable_output = 0;
670
671         /* First pass: only parse/verify command line params */
672         k = 0;
673         while (argv[++k]) {
674                 const struct mode_info *mp;
675                 const struct control_info *cp;
676                 const char *arg = argv[k];
677                 const char *argnext = argv[k+1];
678                 int param;
679
680                 if (arg[0] == '-') {
681                         int i;
682                         mp = find_mode(arg+1);
683                         if (mp) {
684                                 if (!(mp->flags & REV))
685                                         bb_error_msg_and_die("invalid argument '%s'", arg);
686                                 noargs = 0;
687                                 continue;
688                         }
689                         /* It is an option - parse it */
690                         i = 0;
691                         while (arg[++i]) {
692                                 switch (arg[i]) {
693                                 case 'a':
694                                         verbose_output = 1;
695                                         output_func = display_all;
696                                         break;
697                                 case 'g':
698                                         recoverable_output = 1;
699                                         output_func = display_recoverable;
700                                         break;
701                                 case 'F':
702                                         if (file_name)
703                                                 bb_error_msg_and_die("only one device may be specified");
704                                         file_name = &arg[i+1]; /* "-Fdevice" ? */
705                                         if (!file_name[0]) { /* nope, "-F device" */
706                                                 int p = k+1; /* argv[p] is argnext */
707                                                 file_name = argnext;
708                                                 if (!file_name)
709                                                         bb_error_msg_and_die(bb_msg_requires_arg, "-F");
710                                                 /* remove -F param from arg[vc] */
711                                                 --argc;
712                                                 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
713                                         }
714                                         goto end_option;
715                                 default:
716                                         bb_error_msg_and_die("invalid argument '%s'", arg);
717                                 }
718                         }
719 end_option:
720                         continue;
721                 }
722
723                 mp = find_mode(arg);
724                 if (mp) {
725                         noargs = 0;
726                         continue;
727                 }
728
729                 cp = find_control(arg);
730                 if (cp) {
731                         if (!argnext)
732                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
733                         /* called for the side effect of xfunc death only */
734                         set_control_char_or_die(cp, argnext, &mode);
735                         noargs = 0;
736                         ++k;
737                         continue;
738                 }
739
740                 param = find_param(arg);
741                 if (param & param_need_arg) {
742                         if (!argnext)
743                                 bb_error_msg_and_die(bb_msg_requires_arg, arg);
744                         ++k;
745                 }
746
747                 switch (param) {
748 #ifdef HAVE_C_LINE
749                 case param_line:
750 # ifndef TIOCGWINSZ
751                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
752                         break;
753 # endif /* else fall-through */
754 #endif
755 #ifdef TIOCGWINSZ
756                 case param_rows:
757                 case param_cols:
758                         xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
759                         break;
760                 case param_size:
761 #endif
762                 case param_speed:
763                         break;
764                 case param_ispeed:
765                         /* called for the side effect of xfunc death only */
766                         set_speed_or_die(input_speed, argnext, &mode);
767                         break;
768                 case param_ospeed:
769                         /* called for the side effect of xfunc death only */
770                         set_speed_or_die(output_speed, argnext, &mode);
771                         break;
772                 default:
773                         if (recover_mode(arg, &mode) == 1) break;
774                         if (string_to_baud_or_die(arg) != (speed_t) -1) break;
775                         bb_error_msg_and_die("invalid argument '%s'", arg);
776                 }
777                 noargs = 0;
778         }
779
780         /* Specifying both -a and -g is an error */
781         if (verbose_output && recoverable_output)
782                 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
783         /* Specifying -a or -g with non-options is an error */
784         if (!noargs && (verbose_output || recoverable_output))
785                 bb_error_msg_and_die("modes may not be set when specifying an output style");
786
787         /* Now it is safe to start doing things */
788         if (file_name) {
789                 int fd, fdflags;
790                 device_name = file_name;
791                 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
792                 if (fd != STDIN_FILENO) {
793                         dup2(fd, STDIN_FILENO);
794                         close(fd);
795                 }
796                 fdflags = fcntl(STDIN_FILENO, F_GETFL);
797                 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
798                         perror_on_device_and_die("%s: cannot reset non-blocking mode");
799         }
800
801         /* Initialize to all zeroes so there is no risk memcmp will report a
802            spurious difference in an uninitialized portion of the structure */
803         memset(&mode, 0, sizeof(mode));
804         if (tcgetattr(STDIN_FILENO, &mode))
805                 perror_on_device_and_die("%s");
806
807         if (verbose_output || recoverable_output || noargs) {
808                 max_col = screen_columns_or_die();
809                 output_func(&mode);
810                 return EXIT_SUCCESS;
811         }
812
813         /* Second pass: perform actions */
814         k = 0;
815         while (argv[++k]) {
816                 const struct mode_info *mp;
817                 const struct control_info *cp;
818                 const char *arg = argv[k];
819                 const char *argnext = argv[k+1];
820                 int param;
821
822                 if (arg[0] == '-') {
823                         mp = find_mode(arg+1);
824                         if (mp) {
825                                 set_mode(mp, 1 /* reversed */, &mode);
826                         }
827                         /* It is an option - already parsed. Skip it */
828                         continue;
829                 }
830
831                 mp = find_mode(arg);
832                 if (mp) {
833                         set_mode(mp, 0 /* non-reversed */, &mode);
834                         continue;
835                 }
836
837                 cp = find_control(arg);
838                 if (cp) {
839                         ++k;
840                         set_control_char_or_die(cp, argnext, &mode);
841                         continue;
842                 }
843
844                 param = find_param(arg);
845                 if (param & param_need_arg) {
846                         ++k;
847                 }
848
849                 switch (param) {
850 #ifdef HAVE_C_LINE
851                 case param_line:
852                         mode.c_line = xatoul_sfx(argnext, stty_suffixes);
853                         require_set_attr = 1;
854                         break;
855 #endif
856 #ifdef TIOCGWINSZ
857                 case param_cols:
858                         set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
859                         break;
860                 case param_size:
861                         display_window_size(0);
862                         break;
863                 case param_rows:
864                         set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
865                         break;
866 #endif
867                 case param_speed:
868                         display_speed(&mode, 0);
869                         break;
870                 case param_ispeed:
871                         set_speed_or_die(input_speed, argnext, &mode);
872                         speed_was_set = 1;
873                         require_set_attr = 1;
874                         break;
875                 case param_ospeed:
876                         set_speed_or_die(output_speed, argnext, &mode);
877                         speed_was_set = 1;
878                         require_set_attr = 1;
879                         break;
880                 default:
881                         if (recover_mode(arg, &mode) == 1)
882                                 require_set_attr = 1;
883                         else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
884                                 set_speed_or_die(both_speeds, arg, &mode);
885                                 speed_was_set = 1;
886                                 require_set_attr = 1;
887                         } /* else - impossible (caught in the first pass):
888                                 bb_error_msg_and_die("invalid argument '%s'", arg); */
889                 }
890         }
891
892         if (require_set_attr) {
893                 struct termios new_mode;
894
895                 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
896                         perror_on_device_and_die("%s");
897
898                 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
899                    it performs *any* of the requested operations.  This means it
900                    can report 'success' when it has actually failed to perform
901                    some proper subset of the requested operations.  To detect
902                    this partial failure, get the current terminal attributes and
903                    compare them to the requested ones */
904
905                 /* Initialize to all zeroes so there is no risk memcmp will report a
906                    spurious difference in an uninitialized portion of the structure */
907                 memset(&new_mode, 0, sizeof(new_mode));
908                 if (tcgetattr(STDIN_FILENO, &new_mode))
909                         perror_on_device_and_die("%s");
910
911                 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
912 #ifdef CIBAUD
913                         /* SunOS 4.1.3 (at least) has the problem that after this sequence,
914                            tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
915                            sometimes (m1 != m2).  The only difference is in the four bits
916                            of the c_cflag field corresponding to the baud rate.  To save
917                            Sun users a little confusion, don't report an error if this
918                            happens.  But suppress the error only if we haven't tried to
919                            set the baud rate explicitly -- otherwise we'd never give an
920                            error for a true failure to set the baud rate */
921
922                         new_mode.c_cflag &= (~CIBAUD);
923                         if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
924 #endif
925                                 perror_on_device_and_die("%s: cannot perform all requested operations");
926                 }
927         }
928
929         return EXIT_SUCCESS;
930 }
931
932 /* Save set_mode from #ifdef forest plague */
933 #ifndef ONLCR
934 #define ONLCR 0
935 #endif
936 #ifndef OCRNL
937 #define OCRNL 0
938 #endif
939 #ifndef ONLRET
940 #define ONLRET 0
941 #endif
942 #ifndef XCASE
943 #define XCASE 0
944 #endif
945 #ifndef IXANY
946 #define IXANY 0
947 #endif
948 #ifndef TABDLY
949 #define TABDLY 0
950 #endif
951 #ifndef OXTABS
952 #define OXTABS 0
953 #endif
954 #ifndef IUCLC
955 #define IUCLC 0
956 #endif
957 #ifndef OLCUC
958 #define OLCUC 0
959 #endif
960 #ifndef ECHOCTL
961 #define ECHOCTL 0
962 #endif
963 #ifndef ECHOKE
964 #define ECHOKE 0
965 #endif
966
967 static void set_mode(const struct mode_info *info, int reversed,
968                                         struct termios *mode)
969 {
970         tcflag_t *bitsp;
971
972         bitsp = mode_type_flag(info->type, mode);
973
974         if (bitsp) {
975                 if (reversed)
976                         *bitsp = *bitsp & ~info->mask & ~info->bits;
977                 else
978                         *bitsp = (*bitsp & ~info->mask) | info->bits;
979                 return;
980         }
981
982         /* Combination mode */
983         if (info->name == evenp || info->name == parity) {
984                 if (reversed)
985                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
986                 else
987                         mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
988         } else if (info->name == stty_oddp) {
989                 if (reversed)
990                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
991                 else
992                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
993         } else if (info->name == stty_nl) {
994                 if (reversed) {
995                         mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
996                         mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
997                 } else {
998                         mode->c_iflag = mode->c_iflag & ~ICRNL;
999                         if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1000                 }
1001         } else if (info->name == stty_ek) {
1002                 mode->c_cc[VERASE] = CERASE;
1003                 mode->c_cc[VKILL] = CKILL;
1004         } else if (info->name == stty_sane) {
1005                 sane_mode(mode);
1006         }
1007         else if (info->name == cbreak) {
1008                 if (reversed)
1009                         mode->c_lflag |= ICANON;
1010                 else
1011                         mode->c_lflag &= ~ICANON;
1012         } else if (info->name == stty_pass8) {
1013                 if (reversed) {
1014                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1015                         mode->c_iflag |= ISTRIP;
1016                 } else {
1017                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1018                         mode->c_iflag &= ~ISTRIP;
1019                 }
1020         } else if (info->name == litout) {
1021                 if (reversed) {
1022                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1023                         mode->c_iflag |= ISTRIP;
1024                         mode->c_oflag |= OPOST;
1025                 } else {
1026                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1027                         mode->c_iflag &= ~ISTRIP;
1028                         mode->c_oflag &= ~OPOST;
1029                 }
1030         } else if (info->name == raw || info->name == cooked) {
1031                 if ((info->name[0] == 'r' && reversed)
1032                         || (info->name[0] == 'c' && !reversed)) {
1033                         /* Cooked mode */
1034                         mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1035                         mode->c_oflag |= OPOST;
1036                         mode->c_lflag |= ISIG | ICANON;
1037 #if VMIN == VEOF
1038                         mode->c_cc[VEOF] = CEOF;
1039 #endif
1040 #if VTIME == VEOL
1041                         mode->c_cc[VEOL] = CEOL;
1042 #endif
1043                 } else {
1044                         /* Raw mode */
1045                         mode->c_iflag = 0;
1046                         mode->c_oflag &= ~OPOST;
1047                         mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1048                         mode->c_cc[VMIN] = 1;
1049                         mode->c_cc[VTIME] = 0;
1050                 }
1051         }
1052         else if (IXANY && info->name == decctlq) {
1053                 if (reversed)
1054                         mode->c_iflag |= IXANY;
1055                 else
1056                         mode->c_iflag &= ~IXANY;
1057         }
1058         else if (TABDLY && info->name == stty_tabs) {
1059                 if (reversed)
1060                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1061                 else
1062                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1063         }
1064         else if (OXTABS && info->name == stty_tabs) {
1065                 if (reversed)
1066                         mode->c_oflag |= OXTABS;
1067                 else
1068                         mode->c_oflag &= ~OXTABS;
1069         }
1070         else if (XCASE && IUCLC && OLCUC
1071         && (info->name == stty_lcase || info->name == stty_LCASE)) {
1072                 if (reversed) {
1073                         mode->c_lflag &= ~XCASE;
1074                         mode->c_iflag &= ~IUCLC;
1075                         mode->c_oflag &= ~OLCUC;
1076                 } else {
1077                         mode->c_lflag |= XCASE;
1078                         mode->c_iflag |= IUCLC;
1079                         mode->c_oflag |= OLCUC;
1080                 }
1081         }
1082         else if (info->name == stty_crt) {
1083                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1084         }
1085         else if (info->name == stty_dec) {
1086                 mode->c_cc[VINTR] = 3; /* ^C */
1087                 mode->c_cc[VERASE] = 127; /* DEL */
1088                 mode->c_cc[VKILL] = 21; /* ^U */
1089                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1090                 if (IXANY) mode->c_iflag &= ~IXANY;
1091         }
1092 }
1093
1094 static void set_control_char_or_die(const struct control_info *info,
1095                         const char *arg, struct termios *mode)
1096 {
1097         unsigned char value;
1098
1099         if (info->name == stty_min || info->name == stty_time)
1100                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1101         else if (arg[0] == '\0' || arg[1] == '\0')
1102                 value = arg[0];
1103         else if (streq(arg, "^-") || streq(arg, "undef"))
1104                 value = _POSIX_VDISABLE;
1105         else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1106                 value = arg[1] & 0x1f; /* Non-letters get weird results */
1107                 if (arg[1] == '?')
1108                         value = 127;
1109         } else
1110                 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1111         mode->c_cc[info->offset] = value;
1112 }
1113
1114 static void display_changed(const struct termios *mode)
1115 {
1116         int i;
1117         tcflag_t *bitsp;
1118         unsigned long mask;
1119         int prev_type = control;
1120
1121         display_speed(mode, 1);
1122 #ifdef HAVE_C_LINE
1123         wrapf("line = %d;\n", mode->c_line);
1124 #else
1125         wrapf("\n");
1126 #endif
1127
1128         for (i = 0; control_info[i].name != stty_min; ++i) {
1129                 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1130                         continue;
1131                 /* If swtch is the same as susp, don't print both */
1132 #if VSWTCH == VSUSP
1133                 if (control_info[i].name == stty_swtch)
1134                         continue;
1135 #endif
1136                 /* If eof uses the same slot as min, only print whichever applies */
1137 #if VEOF == VMIN
1138                 if ((mode->c_lflag & ICANON) == 0
1139                         && (control_info[i].name == stty_eof
1140                                 || control_info[i].name == stty_eol)) continue;
1141 #endif
1142                 wrapf("%s = %s;", control_info[i].name,
1143                           visible(mode->c_cc[control_info[i].offset]));
1144         }
1145         if ((mode->c_lflag & ICANON) == 0) {
1146                 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
1147                           (int) mode->c_cc[VTIME]);
1148         }
1149         if (current_col) wrapf("\n");
1150
1151         for (i = 0; i < NUM_mode_info; ++i) {
1152                 if (mode_info[i].flags & OMIT)
1153                         continue;
1154                 if (mode_info[i].type != prev_type) {
1155                         if (current_col) wrapf("\n");
1156                         prev_type = mode_info[i].type;
1157                 }
1158
1159                 bitsp = mode_type_flag(mode_info[i].type, mode);
1160                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1161                 if ((*bitsp & mask) == mode_info[i].bits) {
1162                         if (mode_info[i].flags & SANE_UNSET) {
1163                                 wrapf("%s", mode_info[i].name);
1164                         }
1165                 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
1166                         wrapf("-%s", mode_info[i].name);
1167                 }
1168         }
1169         if (current_col) wrapf("\n");
1170 }
1171
1172 static void display_all(const struct termios *mode)
1173 {
1174         int i;
1175         tcflag_t *bitsp;
1176         unsigned long mask;
1177         int prev_type = control;
1178
1179         display_speed(mode, 1);
1180         display_window_size(1);
1181 #ifdef HAVE_C_LINE
1182         wrapf("line = %d;\n", mode->c_line);
1183 #else
1184         wrapf("\n");
1185 #endif
1186
1187         for (i = 0; control_info[i].name != stty_min; ++i) {
1188                 /* If swtch is the same as susp, don't print both */
1189 #if VSWTCH == VSUSP
1190                 if (control_info[i].name == stty_swtch)
1191                         continue;
1192 #endif
1193                 /* If eof uses the same slot as min, only print whichever applies */
1194 #if VEOF == VMIN
1195                 if ((mode->c_lflag & ICANON) == 0
1196                         && (control_info[i].name == stty_eof
1197                                 || control_info[i].name == stty_eol)) continue;
1198 #endif
1199                 wrapf("%s = %s;", control_info[i].name,
1200                           visible(mode->c_cc[control_info[i].offset]));
1201         }
1202 #if VEOF == VMIN
1203         if ((mode->c_lflag & ICANON) == 0)
1204 #endif
1205                 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1206         if (current_col) wrapf("\n");
1207
1208         for (i = 0; i < NUM_mode_info; ++i) {
1209                 if (mode_info[i].flags & OMIT)
1210                         continue;
1211                 if (mode_info[i].type != prev_type) {
1212                         wrapf("\n");
1213                         prev_type = mode_info[i].type;
1214                 }
1215
1216                 bitsp = mode_type_flag(mode_info[i].type, mode);
1217                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1218                 if ((*bitsp & mask) == mode_info[i].bits)
1219                         wrapf("%s", mode_info[i].name);
1220                 else if (mode_info[i].flags & REV)
1221                         wrapf("-%s", mode_info[i].name);
1222         }
1223         if (current_col) wrapf("\n");
1224 }
1225
1226 static void display_speed(const struct termios *mode, int fancy)
1227 {
1228                              //01234567 8 9
1229         const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1230         unsigned long ispeed, ospeed;
1231
1232         ospeed = ispeed = cfgetispeed(mode);
1233         if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1234                 ispeed = ospeed;                /* in case ispeed was 0 */
1235                          //0123 4 5 6 7 8 9
1236                 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1237         }
1238         if (fancy) fmt_str += 9;
1239         wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1240 }
1241
1242 static void display_recoverable(const struct termios *mode)
1243 {
1244         int i;
1245         printf("%lx:%lx:%lx:%lx",
1246                    (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1247                    (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1248         for (i = 0; i < NCCS; ++i)
1249                 printf(":%x", (unsigned int) mode->c_cc[i]);
1250         putchar('\n');
1251 }
1252
1253 static int recover_mode(const char *arg, struct termios *mode)
1254 {
1255         int i, n;
1256         unsigned int chr;
1257         unsigned long iflag, oflag, cflag, lflag;
1258
1259         /* Scan into temporaries since it is too much trouble to figure out
1260            the right format for 'tcflag_t' */
1261         if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1262                            &iflag, &oflag, &cflag, &lflag, &n) != 4)
1263                 return 0;
1264         mode->c_iflag = iflag;
1265         mode->c_oflag = oflag;
1266         mode->c_cflag = cflag;
1267         mode->c_lflag = lflag;
1268         arg += n;
1269         for (i = 0; i < NCCS; ++i) {
1270                 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1271                         return 0;
1272                 mode->c_cc[i] = chr;
1273                 arg += n;
1274         }
1275
1276         /* Fail if there are too many fields */
1277         if (*arg != '\0')
1278                 return 0;
1279
1280         return 1;
1281 }
1282
1283 static void sane_mode(struct termios *mode)
1284 {
1285         int i;
1286         tcflag_t *bitsp;
1287
1288         for (i = 0; i < NUM_control_info; ++i) {
1289 #if VMIN == VEOF
1290                 if (control_info[i].name == stty_min)
1291                         break;
1292 #endif
1293                 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1294         }
1295
1296         for (i = 0; i < NUM_mode_info; ++i) {
1297                 if (mode_info[i].flags & SANE_SET) {
1298                         bitsp = mode_type_flag(mode_info[i].type, mode);
1299                         *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1300                                 | mode_info[i].bits;
1301                 } else if (mode_info[i].flags & SANE_UNSET) {
1302                         bitsp = mode_type_flag(mode_info[i].type, mode);
1303                         *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1304                                 & ~mode_info[i].bits;
1305                 }
1306         }
1307 }