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