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