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