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