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