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