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