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