stty: reorder code, reducing need in forward declarations.
[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 int max_col;
374 /* Current position, to know when to wrap */
375 static int 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(bb_xparse_number(arg, 0));
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 (current_col + buflen >= max_col) {
474                         putchar('\n');
475                         current_col = 0;
476                 } else
477                         if (buf[0] != '\n') putchar(' ');
478         }
479         fputs(buf, stdout);
480         current_col += buflen;
481         if (buf[buflen-1] == '\n')
482                 current_col = 0;
483 }
484
485 #ifdef TIOCGWINSZ
486
487 static int get_win_size(struct winsize *win)
488 {
489         return ioctl(STDIN_FILENO, TIOCGWINSZ, (char *) win);
490 }
491
492 static void set_window_size(int rows, int cols)
493 {
494         struct winsize win;
495
496         if (get_win_size(&win)) {
497                 if (errno != EINVAL) {
498                         perror_on_device("%s");
499                         return;
500                 }
501                 memset(&win, 0, sizeof(win));
502         }
503
504         if (rows >= 0)
505                 win.ws_row = rows;
506         if (cols >= 0)
507                 win.ws_col = cols;
508
509 # ifdef TIOCSSIZE
510         /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
511            The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
512            This comment from sys/ttold.h describes Sun's twisted logic - a better
513            test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
514            At any rate, the problem is gone in Solaris 2.x */
515
516         if (win.ws_row == 0 || win.ws_col == 0) {
517                 struct ttysize ttysz;
518
519                 ttysz.ts_lines = win.ws_row;
520                 ttysz.ts_cols = win.ws_col;
521
522                 win.ws_row = win.ws_col = 1;
523
524                 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
525                 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
526                         perror_on_device("%s");
527                 }
528                 return;
529         }
530 # endif
531
532         if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
533                 perror_on_device("%s");
534 }
535
536 static void display_window_size(int fancy)
537 {
538         const char *fmt_str = "%s\0%s: no size information for this device";
539         struct winsize win;
540
541         if (get_win_size(&win)) {
542                 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
543                         perror_on_device(fmt_str);
544                 }
545         } else {
546                 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
547                                 win.ws_row, win.ws_col);
548         }
549 }
550
551 #else /* !TIOCGWINSZ */
552
553 static inline void display_window_size(int fancy) {}
554
555 #endif /* !TIOCGWINSZ */
556
557 static int screen_columns(void)
558 {
559         int columns;
560         const char *s;
561
562 #ifdef TIOCGWINSZ
563         struct winsize win;
564
565         /* With Solaris 2.[123], this ioctl fails and errno is set to
566            EINVAL for telnet (but not rlogin) sessions.
567            On ISC 3.0, it fails for the console and the serial port
568            (but it works for ptys).
569            It can also fail on any system when stdout isn't a tty.
570            In case of any failure, just use the default */
571         if (get_win_size(&win) == 0 && win.ws_col > 0)
572                 return win.ws_col;
573 #endif
574
575         columns = 80;
576         if ((s = getenv("COLUMNS"))) {
577                 columns = atoi(s);
578         }
579         return columns;
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+1]) { 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                         bb_xparse_number(argnext, stty_suffixes);
747                         break;
748 # endif /* else fall-through */
749 #endif
750 #ifdef TIOCGWINSZ
751                 case param_rows:
752                 case param_cols:
753                         bb_xparse_number(argnext, 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 != 0) {
788                         dup2(fd, 0);
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: couldn't 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();
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 = bb_xparse_number(argnext, stty_suffixes);
848                         require_set_attr = 1;
849                         break;
850 #endif
851 #ifdef TIOCGWINSZ
852                 case param_cols:
853                         set_window_size(-1, (int) bb_xparse_number(argnext, stty_suffixes));
854                         break;
855                 case param_size:
856                         max_col = screen_columns();
857                         display_window_size(0);
858                         break;
859                 case param_rows:
860                         set_window_size((int) bb_xparse_number(argnext, stty_suffixes), -1);
861                         break;
862 #endif
863                 case param_speed:
864                         max_col = screen_columns();
865                         display_speed(&mode, 0);
866                         break;
867                 case param_ispeed:
868                         set_speed_or_die(input_speed, argnext, &mode);
869                         speed_was_set = 1;
870                         require_set_attr = 1;
871                         break;
872                 case param_ospeed:
873                         set_speed_or_die(output_speed, argnext, &mode);
874                         speed_was_set = 1;
875                         require_set_attr = 1;
876                         break;
877                 default:
878                         if (recover_mode(arg, &mode) == 1)
879                                 require_set_attr = 1;
880                         else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
881                                 set_speed_or_die(both_speeds, arg, &mode);
882                                 speed_was_set = 1;
883                                 require_set_attr = 1;
884                         } /* else - impossible (caught in the first pass):
885                                 bb_error_msg_and_die("invalid argument '%s'", arg); */
886                 }
887         }
888
889         if (require_set_attr) {
890                 struct termios new_mode;
891
892                 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
893                         perror_on_device_and_die("%s");
894
895                 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
896                    it performs *any* of the requested operations.  This means it
897                    can report `success' when it has actually failed to perform
898                    some proper subset of the requested operations.  To detect
899                    this partial failure, get the current terminal attributes and
900                    compare them to the requested ones */
901
902                 /* Initialize to all zeroes so there is no risk memcmp will report a
903                    spurious difference in an uninitialized portion of the structure */
904                 memset(&new_mode, 0, sizeof(new_mode));
905                 if (tcgetattr(STDIN_FILENO, &new_mode))
906                         perror_on_device_and_die("%s");
907
908                 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
909 #ifdef CIBAUD
910                         /* SunOS 4.1.3 (at least) has the problem that after this sequence,
911                            tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
912                            sometimes (m1 != m2).  The only difference is in the four bits
913                            of the c_cflag field corresponding to the baud rate.  To save
914                            Sun users a little confusion, don't report an error if this
915                            happens.  But suppress the error only if we haven't tried to
916                            set the baud rate explicitly -- otherwise we'd never give an
917                            error for a true failure to set the baud rate */
918
919                         new_mode.c_cflag &= (~CIBAUD);
920                         if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
921 #endif
922                                 perror_on_device_and_die ("%s: unable to perform all requested operations");
923                 }
924         }
925
926         return EXIT_SUCCESS;
927 }
928
929 /* Save set_mode from #ifdef forest plague */
930 #ifndef ONLCR
931 #define ONLCR 0
932 #endif
933 #ifndef OCRNL
934 #define OCRNL 0
935 #endif
936 #ifndef ONLRET
937 #define ONLRET 0
938 #endif
939 #ifndef XCASE
940 #define XCASE 0
941 #endif
942 #ifndef IXANY
943 #define IXANY 0
944 #endif
945 #ifndef TABDLY
946 #define TABDLY 0
947 #endif
948 #ifndef OXTABS
949 #define OXTABS 0
950 #endif
951 #ifndef IUCLC
952 #define IUCLC 0
953 #endif
954 #ifndef OLCUC
955 #define OLCUC 0
956 #endif
957 #ifndef ECHOCTL
958 #define ECHOCTL 0
959 #endif
960 #ifndef ECHOKE
961 #define ECHOKE 0
962 #endif
963
964 static void set_mode(const struct mode_info *info, int reversed,
965                                         struct termios *mode)
966 {
967         tcflag_t *bitsp;
968
969         bitsp = mode_type_flag(info->type, mode);
970
971         if (bitsp) {
972                 if (reversed)
973                         *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
974                 else
975                         *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
976                 return;
977         }
978
979         /* Combination mode */
980         if (info->name == evenp || info->name == parity) {
981                 if (reversed)
982                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
983                 else
984                         mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
985         } else if (info->name == stty_oddp) {
986                 if (reversed)
987                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
988                 else
989                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
990         } else if (info->name == stty_nl) {
991                 if (reversed) {
992                         mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
993                         mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
994                 } else {
995                         mode->c_iflag = mode->c_iflag & ~ICRNL;
996                         if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
997                 }
998         } else if (info->name == stty_ek) {
999                 mode->c_cc[VERASE] = CERASE;
1000                 mode->c_cc[VKILL] = CKILL;
1001         } else if (info->name == stty_sane) {
1002                 sane_mode(mode);
1003         }
1004         else if (info->name == cbreak) {
1005                 if (reversed)
1006                         mode->c_lflag |= ICANON;
1007                 else
1008                         mode->c_lflag &= ~ICANON;
1009         } else if (info->name == stty_pass8) {
1010                 if (reversed) {
1011                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1012                         mode->c_iflag |= ISTRIP;
1013                 } else {
1014                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1015                         mode->c_iflag &= ~ISTRIP;
1016                 }
1017         } else if (info->name == litout) {
1018                 if (reversed) {
1019                         mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1020                         mode->c_iflag |= ISTRIP;
1021                         mode->c_oflag |= OPOST;
1022                 } else {
1023                         mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1024                         mode->c_iflag &= ~ISTRIP;
1025                         mode->c_oflag &= ~OPOST;
1026                 }
1027         } else if (info->name == raw || info->name == cooked) {
1028                 if ((info->name[0] == 'r' && reversed)
1029                         || (info->name[0] == 'c' && !reversed)) {
1030                         /* Cooked mode */
1031                         mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1032                         mode->c_oflag |= OPOST;
1033                         mode->c_lflag |= ISIG | ICANON;
1034 #if VMIN == VEOF
1035                         mode->c_cc[VEOF] = CEOF;
1036 #endif
1037 #if VTIME == VEOL
1038                         mode->c_cc[VEOL] = CEOL;
1039 #endif
1040                 } else {
1041                         /* Raw mode */
1042                         mode->c_iflag = 0;
1043                         mode->c_oflag &= ~OPOST;
1044                         mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1045                         mode->c_cc[VMIN] = 1;
1046                         mode->c_cc[VTIME] = 0;
1047                 }
1048         }
1049         else if (IXANY && info->name == decctlq) {
1050                 if (reversed)
1051                         mode->c_iflag |= IXANY;
1052                 else
1053                         mode->c_iflag &= ~IXANY;
1054         }
1055         else if (TABDLY && info->name == stty_tabs) {
1056                 if (reversed)
1057                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1058                 else
1059                         mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1060         }
1061         else if (OXTABS && info->name == stty_tabs) {
1062                 if (reversed)
1063                         mode->c_oflag |= OXTABS;
1064                 else
1065                         mode->c_oflag &= ~OXTABS;
1066         }
1067         else if (XCASE && IUCLC && OLCUC
1068         && (info->name == stty_lcase || info->name == stty_LCASE)) {
1069                 if (reversed) {
1070                         mode->c_lflag &= ~XCASE;
1071                         mode->c_iflag &= ~IUCLC;
1072                         mode->c_oflag &= ~OLCUC;
1073                 } else {
1074                         mode->c_lflag |= XCASE;
1075                         mode->c_iflag |= IUCLC;
1076                         mode->c_oflag |= OLCUC;
1077                 }
1078         }
1079         else if (info->name == stty_crt) {
1080                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1081         }
1082         else if (info->name == stty_dec) {
1083                 mode->c_cc[VINTR] = 3; /* ^C */
1084                 mode->c_cc[VERASE] = 127; /* DEL */
1085                 mode->c_cc[VKILL] = 21; /* ^U */
1086                 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1087                 if (IXANY) mode->c_iflag &= ~IXANY;
1088         }
1089 }
1090
1091 static void set_control_char_or_die(const struct control_info *info,
1092                         const char *arg, struct termios *mode)
1093 {
1094         unsigned char value;
1095
1096         if (info->name == stty_min || info->name == stty_time)
1097                 value = bb_xparse_number(arg, stty_suffixes);
1098         else if (arg[0] == '\0' || arg[1] == '\0')
1099                 value = arg[0];
1100         else if (streq(arg, "^-") || streq(arg, "undef"))
1101                 value = _POSIX_VDISABLE;
1102         else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1103                 value = arg[1] & 0x1f; /* Non-letters get weird results */
1104                 if (arg[1] == '?')
1105                         value = 127;
1106         } else
1107                 value = bb_xparse_number(arg, stty_suffixes);
1108         mode->c_cc[info->offset] = value;
1109 }
1110
1111 static void display_changed(const struct termios *mode)
1112 {
1113         int i;
1114         tcflag_t *bitsp;
1115         unsigned long mask;
1116         int prev_type = control;
1117
1118         display_speed(mode, 1);
1119 #ifdef HAVE_C_LINE
1120         wrapf("line = %d;\n", mode->c_line);
1121 #else
1122         wrapf("\n");
1123 #endif
1124
1125         for (i = 0; control_info[i].name != stty_min; ++i) {
1126                 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1127                         continue;
1128                 /* If swtch is the same as susp, don't print both */
1129 #if VSWTCH == VSUSP
1130                 if (control_info[i].name == stty_swtch)
1131                         continue;
1132 #endif
1133                 /* If eof uses the same slot as min, only print whichever applies */
1134 #if VEOF == VMIN
1135                 if ((mode->c_lflag & ICANON) == 0
1136                         && (control_info[i].name == stty_eof
1137                                 || control_info[i].name == stty_eol)) continue;
1138 #endif
1139                 wrapf("%s = %s;", control_info[i].name,
1140                           visible(mode->c_cc[control_info[i].offset]));
1141         }
1142         if ((mode->c_lflag & ICANON) == 0) {
1143                 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
1144                           (int) mode->c_cc[VTIME]);
1145         }
1146         if (current_col) wrapf("\n");
1147
1148         for (i = 0; i < NUM_mode_info; ++i) {
1149                 if (mode_info[i].flags & OMIT)
1150                         continue;
1151                 if (mode_info[i].type != prev_type) {
1152                         if (current_col) wrapf("\n");
1153                         prev_type = mode_info[i].type;
1154                 }
1155
1156                 bitsp = mode_type_flag(mode_info[i].type, mode);
1157                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1158                 if ((*bitsp & mask) == mode_info[i].bits) {
1159                         if (mode_info[i].flags & SANE_UNSET) {
1160                                 wrapf("%s", mode_info[i].name);
1161                         }
1162                 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
1163                         wrapf("-%s", mode_info[i].name);
1164                 }
1165         }
1166         if (current_col) wrapf("\n");
1167 }
1168
1169 static void display_all(const struct termios *mode)
1170 {
1171         int i;
1172         tcflag_t *bitsp;
1173         unsigned long mask;
1174         int prev_type = control;
1175
1176         display_speed(mode, 1);
1177         display_window_size(1);
1178 #ifdef HAVE_C_LINE
1179         wrapf("line = %d;\n", mode->c_line);
1180 #else
1181         wrapf("\n");
1182 #endif
1183
1184         for (i = 0; control_info[i].name != stty_min; ++i) {
1185                 /* If swtch is the same as susp, don't print both */
1186 #if VSWTCH == VSUSP
1187                 if (control_info[i].name == stty_swtch)
1188                         continue;
1189 #endif
1190                 /* If eof uses the same slot as min, only print whichever applies */
1191 #if VEOF == VMIN
1192                 if ((mode->c_lflag & ICANON) == 0
1193                         && (control_info[i].name == stty_eof
1194                                 || control_info[i].name == stty_eol)) continue;
1195 #endif
1196                 wrapf("%s = %s;", control_info[i].name,
1197                           visible(mode->c_cc[control_info[i].offset]));
1198         }
1199 #if VEOF == VMIN
1200         if ((mode->c_lflag & ICANON) == 0)
1201 #endif
1202                 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1203         if (current_col) wrapf("\n");
1204
1205         for (i = 0; i < NUM_mode_info; ++i) {
1206                 if (mode_info[i].flags & OMIT)
1207                         continue;
1208                 if (mode_info[i].type != prev_type) {
1209                         wrapf("\n");
1210                         prev_type = mode_info[i].type;
1211                 }
1212
1213                 bitsp = mode_type_flag(mode_info[i].type, mode);
1214                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1215                 if ((*bitsp & mask) == mode_info[i].bits)
1216                         wrapf("%s", mode_info[i].name);
1217                 else if (mode_info[i].flags & REV)
1218                         wrapf("-%s", mode_info[i].name);
1219         }
1220         if (current_col) wrapf("\n");
1221 }
1222
1223 static void display_speed(const struct termios *mode, int fancy)
1224 {
1225                              //01234567 8 9
1226         const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1227         unsigned long ispeed, ospeed;
1228
1229         ospeed = ispeed = cfgetispeed(mode);
1230         if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1231                 ispeed = ospeed;                /* in case ispeed was 0 */
1232                          //0123 4 5 6 7 8 9
1233                 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1234         }
1235         if (fancy) fmt_str += 9;
1236         wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1237 }
1238
1239 static void display_recoverable(const struct termios *mode)
1240 {
1241         int i;
1242         printf("%lx:%lx:%lx:%lx",
1243                    (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1244                    (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1245         for (i = 0; i < NCCS; ++i)
1246                 printf(":%x", (unsigned int) mode->c_cc[i]);
1247         putchar('\n');
1248 }
1249
1250 static int recover_mode(const char *arg, struct termios *mode)
1251 {
1252         int i, n;
1253         unsigned int chr;
1254         unsigned long iflag, oflag, cflag, lflag;
1255
1256         /* Scan into temporaries since it is too much trouble to figure out
1257            the right format for `tcflag_t' */
1258         if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1259                            &iflag, &oflag, &cflag, &lflag, &n) != 4)
1260                 return 0;
1261         mode->c_iflag = iflag;
1262         mode->c_oflag = oflag;
1263         mode->c_cflag = cflag;
1264         mode->c_lflag = lflag;
1265         arg += n;
1266         for (i = 0; i < NCCS; ++i) {
1267                 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1268                         return 0;
1269                 mode->c_cc[i] = chr;
1270                 arg += n;
1271         }
1272
1273         /* Fail if there are too many fields */
1274         if (*arg != '\0')
1275                 return 0;
1276
1277         return 1;
1278 }
1279
1280 static void sane_mode(struct termios *mode)
1281 {
1282         int i;
1283         tcflag_t *bitsp;
1284
1285         for (i = 0; i < NUM_control_info; ++i) {
1286 #if VMIN == VEOF
1287                 if (control_info[i].name == stty_min)
1288                         break;
1289 #endif
1290                 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1291         }
1292
1293         for (i = 0; i < NUM_mode_info; ++i) {
1294                 if (mode_info[i].flags & SANE_SET) {
1295                         bitsp = mode_type_flag(mode_info[i].type, mode);
1296                         *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1297                                 | mode_info[i].bits;
1298                 } else if (mode_info[i].flags & SANE_UNSET) {
1299                         bitsp = mode_type_flag(mode_info[i].type, mode);
1300                         *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1301                                 & ~mode_info[i].bits;
1302                 }
1303         }
1304 }