Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/coreutils/stty.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (show annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 34260 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

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 #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 static unsigned max_col = 80; /* default */
379 /* Current position, to know when to wrap */
380 static unsigned current_col;
381 static const char *device_name = bb_msg_standard_input;
382
383 /* Return a string that is the printable representation of character CH */
384 /* Adapted from 'cat' by Torbjorn Granlund */
385 static const char *visible(unsigned int ch)
386 {
387 static char buf[10];
388 char *bpout = buf;
389
390 if (ch == _POSIX_VDISABLE)
391 return "<undef>";
392
393 if (ch >= 128) {
394 ch -= 128;
395 *bpout++ = 'M';
396 *bpout++ = '-';
397 }
398
399 if (ch < 32) {
400 *bpout++ = '^';
401 *bpout++ = ch + 64;
402 } else if (ch < 127) {
403 *bpout++ = ch;
404 } else {
405 *bpout++ = '^';
406 *bpout++ = '?';
407 }
408
409 *bpout = '\0';
410 return buf;
411 }
412
413 static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
414 {
415 static const unsigned char tcflag_offsets[] = {
416 offsetof(struct termios, c_cflag), /* control */
417 offsetof(struct termios, c_iflag), /* input */
418 offsetof(struct termios, c_oflag), /* output */
419 offsetof(struct termios, c_lflag), /* local */
420 };
421
422 if (type <= local) {
423 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
424 }
425 return NULL;
426 }
427
428 static void set_speed_or_die(enum speed_setting type, const char * const arg,
429 struct termios * const mode)
430 {
431 speed_t baud;
432
433 baud = tty_value_to_baud(xatou(arg));
434
435 if (type != output_speed) { /* either input or both */
436 cfsetispeed(mode, baud);
437 }
438 if (type != input_speed) { /* either output or both */
439 cfsetospeed(mode, baud);
440 }
441 }
442
443 static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
444 {
445 bb_perror_msg_and_die(fmt, device_name);
446 }
447
448 static void perror_on_device(const char *fmt)
449 {
450 bb_perror_msg(fmt, device_name);
451 }
452
453 /* Print format string MESSAGE and optional args.
454 Wrap to next line first if it won't fit.
455 Print a space first unless MESSAGE will start a new line */
456 static void wrapf(const char *message, ...)
457 {
458 char buf[128];
459 va_list args;
460 int buflen;
461
462 va_start(args, message);
463 buflen = vsnprintf(buf, sizeof(buf), message, args);
464 va_end(args);
465 /* We seem to be called only with suitable lengths, but check if
466 somebody failed to adhere to this assumption just to be sure. */
467 if (!buflen || buflen >= sizeof(buf)) return;
468
469 if (current_col > 0) {
470 current_col++;
471 if (buf[0] != '\n') {
472 if (current_col + buflen >= max_col) {
473 putchar('\n');
474 current_col = 0;
475 } else
476 putchar(' ');
477 }
478 }
479 fputs(buf, stdout);
480 current_col += buflen;
481 if (buf[buflen-1] == '\n')
482 current_col = 0;
483 }
484
485 static void set_window_size(const int rows, const int cols)
486 {
487 struct winsize win = { 0, 0, 0, 0};
488
489 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
490 if (errno != EINVAL) {
491 goto bail;
492 }
493 memset(&win, 0, sizeof(win));
494 }
495
496 if (rows >= 0)
497 win.ws_row = rows;
498 if (cols >= 0)
499 win.ws_col = cols;
500
501 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
502 bail:
503 perror_on_device("%s");
504 }
505
506 static void display_window_size(const int fancy)
507 {
508 const char *fmt_str = "%s\0%s: no size information for this device";
509 unsigned width, height;
510
511 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
512 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
513 perror_on_device(fmt_str);
514 }
515 } else {
516 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
517 height, width);
518 }
519 }
520
521 static const struct suffix_mult stty_suffixes[] = {
522 {"b", 512 },
523 {"k", 1024},
524 {"B", 1024},
525 {NULL, 0 }
526 };
527
528 static const struct mode_info *find_mode(const char *name)
529 {
530 int i;
531 for (i = 0; i < NUM_mode_info; ++i)
532 if (!strcmp(name, mode_info[i].name))
533 return &mode_info[i];
534 return 0;
535 }
536
537 static const struct control_info *find_control(const char *name)
538 {
539 int i;
540 for (i = 0; i < NUM_control_info; ++i)
541 if (!strcmp(name, control_info[i].name))
542 return &control_info[i];
543 return 0;
544 }
545
546 enum {
547 param_need_arg = 0x80,
548 param_line = 1 | 0x80,
549 param_rows = 2 | 0x80,
550 param_cols = 3 | 0x80,
551 param_size = 4,
552 param_speed = 5,
553 param_ispeed = 6 | 0x80,
554 param_ospeed = 7 | 0x80,
555 };
556
557 static int find_param(const char * const name)
558 {
559 const char * const params[] = {
560 "line",
561 "rows",
562 "cols",
563 "columns",
564 "size",
565 "speed",
566 "ispeed",
567 "ospeed",
568 NULL
569 };
570 int i = index_in_str_array(params, name);
571 if (i < 0)
572 return 0;
573 if (!(i == 4 || i == 5))
574 i |= 0x80;
575
576 return i;
577 }
578
579 static int recover_mode(const char *arg, struct termios *mode)
580 {
581 int i, n;
582 unsigned int chr;
583 unsigned long iflag, oflag, cflag, lflag;
584
585 /* Scan into temporaries since it is too much trouble to figure out
586 the right format for 'tcflag_t' */
587 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
588 &iflag, &oflag, &cflag, &lflag, &n) != 4)
589 return 0;
590 mode->c_iflag = iflag;
591 mode->c_oflag = oflag;
592 mode->c_cflag = cflag;
593 mode->c_lflag = lflag;
594 arg += n;
595 for (i = 0; i < NCCS; ++i) {
596 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
597 return 0;
598 mode->c_cc[i] = chr;
599 arg += n;
600 }
601
602 /* Fail if there are too many fields */
603 if (*arg != '\0')
604 return 0;
605
606 return 1;
607 }
608
609 static void display_recoverable(const struct termios *mode,
610 const int ATTRIBUTE_UNUSED dummy)
611 {
612 int i;
613 printf("%lx:%lx:%lx:%lx",
614 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
615 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
616 for (i = 0; i < NCCS; ++i)
617 printf(":%x", (unsigned int) mode->c_cc[i]);
618 putchar('\n');
619 }
620
621 static void display_speed(const struct termios *mode, int fancy)
622 {
623 //01234567 8 9
624 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
625 unsigned long ispeed, ospeed;
626
627 ospeed = ispeed = cfgetispeed(mode);
628 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
629 ispeed = ospeed; /* in case ispeed was 0 */
630 //0123 4 5 6 7 8 9
631 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
632 }
633 if (fancy) fmt_str += 9;
634 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
635 }
636
637 static void do_display(const struct termios *mode, const int all)
638 {
639 int i;
640 tcflag_t *bitsp;
641 unsigned long mask;
642 int prev_type = control;
643
644 display_speed(mode, 1);
645 if (all)
646 display_window_size(1);
647 #ifdef HAVE_C_LINE
648 wrapf("line = %d;\n", mode->c_line);
649 #else
650 wrapf("\n");
651 #endif
652
653 for (i = 0; control_info[i].name != stty_min; ++i) {
654 /* If swtch is the same as susp, don't print both */
655 #if VSWTCH == VSUSP
656 if (control_info[i].name == stty_swtch)
657 continue;
658 #endif
659 /* If eof uses the same slot as min, only print whichever applies */
660 #if VEOF == VMIN
661 if ((mode->c_lflag & ICANON) == 0
662 && (control_info[i].name == stty_eof
663 || control_info[i].name == stty_eol)) continue;
664 #endif
665 wrapf("%s = %s;", control_info[i].name,
666 visible(mode->c_cc[control_info[i].offset]));
667 }
668 #if VEOF == VMIN
669 if ((mode->c_lflag & ICANON) == 0)
670 #endif
671 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
672 if (current_col) wrapf("\n");
673
674 for (i = 0; i < NUM_mode_info; ++i) {
675 if (mode_info[i].flags & OMIT)
676 continue;
677 if (mode_info[i].type != prev_type) {
678 /* wrapf("\n"); */
679 if (current_col) wrapf("\n");
680 prev_type = mode_info[i].type;
681 }
682
683 bitsp = mode_type_flag(mode_info[i].type, mode);
684 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
685 if ((*bitsp & mask) == mode_info[i].bits) {
686 if (all || (mode_info[i].flags & SANE_UNSET))
687 wrapf("%s", mode_info[i].name);
688 } else {
689 if ((all && mode_info[i].flags & REV) ||
690 (!all &&
691 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
692 wrapf("-%s", mode_info[i].name);
693 }
694 }
695 if (current_col) wrapf("\n");
696 }
697
698 static void sane_mode(struct termios *mode)
699 {
700 int i;
701 tcflag_t *bitsp;
702
703 for (i = 0; i < NUM_control_info; ++i) {
704 #if VMIN == VEOF
705 if (control_info[i].name == stty_min)
706 break;
707 #endif
708 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
709 }
710
711 for (i = 0; i < NUM_mode_info; ++i) {
712 if (mode_info[i].flags & SANE_SET) {
713 bitsp = mode_type_flag(mode_info[i].type, mode);
714 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
715 | mode_info[i].bits;
716 } else if (mode_info[i].flags & SANE_UNSET) {
717 bitsp = mode_type_flag(mode_info[i].type, mode);
718 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
719 & ~mode_info[i].bits;
720 }
721 }
722 }
723
724 /* Save set_mode from #ifdef forest plague */
725 #ifndef ONLCR
726 #define ONLCR 0
727 #endif
728 #ifndef OCRNL
729 #define OCRNL 0
730 #endif
731 #ifndef ONLRET
732 #define ONLRET 0
733 #endif
734 #ifndef XCASE
735 #define XCASE 0
736 #endif
737 #ifndef IXANY
738 #define IXANY 0
739 #endif
740 #ifndef TABDLY
741 #define TABDLY 0
742 #endif
743 #ifndef OXTABS
744 #define OXTABS 0
745 #endif
746 #ifndef IUCLC
747 #define IUCLC 0
748 #endif
749 #ifndef OLCUC
750 #define OLCUC 0
751 #endif
752 #ifndef ECHOCTL
753 #define ECHOCTL 0
754 #endif
755 #ifndef ECHOKE
756 #define ECHOKE 0
757 #endif
758
759 static void set_mode(const struct mode_info *info, int reversed,
760 struct termios *mode)
761 {
762 tcflag_t *bitsp;
763
764 bitsp = mode_type_flag(info->type, mode);
765
766 if (bitsp) {
767 if (reversed)
768 *bitsp = *bitsp & ~info->mask & ~info->bits;
769 else
770 *bitsp = (*bitsp & ~info->mask) | info->bits;
771 return;
772 }
773
774 /* Combination mode */
775 if (info->name == evenp || info->name == parity) {
776 if (reversed)
777 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
778 else
779 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
780 } else if (info->name == stty_oddp) {
781 if (reversed)
782 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
783 else
784 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
785 } else if (info->name == stty_nl) {
786 if (reversed) {
787 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
788 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
789 } else {
790 mode->c_iflag = mode->c_iflag & ~ICRNL;
791 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
792 }
793 } else if (info->name == stty_ek) {
794 mode->c_cc[VERASE] = CERASE;
795 mode->c_cc[VKILL] = CKILL;
796 } else if (info->name == stty_sane) {
797 sane_mode(mode);
798 }
799 else if (info->name == cbreak) {
800 if (reversed)
801 mode->c_lflag |= ICANON;
802 else
803 mode->c_lflag &= ~ICANON;
804 } else if (info->name == stty_pass8) {
805 if (reversed) {
806 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
807 mode->c_iflag |= ISTRIP;
808 } else {
809 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
810 mode->c_iflag &= ~ISTRIP;
811 }
812 } else if (info->name == litout) {
813 if (reversed) {
814 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
815 mode->c_iflag |= ISTRIP;
816 mode->c_oflag |= OPOST;
817 } else {
818 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
819 mode->c_iflag &= ~ISTRIP;
820 mode->c_oflag &= ~OPOST;
821 }
822 } else if (info->name == raw || info->name == cooked) {
823 if ((info->name[0] == 'r' && reversed)
824 || (info->name[0] == 'c' && !reversed)) {
825 /* Cooked mode */
826 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
827 mode->c_oflag |= OPOST;
828 mode->c_lflag |= ISIG | ICANON;
829 #if VMIN == VEOF
830 mode->c_cc[VEOF] = CEOF;
831 #endif
832 #if VTIME == VEOL
833 mode->c_cc[VEOL] = CEOL;
834 #endif
835 } else {
836 /* Raw mode */
837 mode->c_iflag = 0;
838 mode->c_oflag &= ~OPOST;
839 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
840 mode->c_cc[VMIN] = 1;
841 mode->c_cc[VTIME] = 0;
842 }
843 }
844 else if (IXANY && info->name == decctlq) {
845 if (reversed)
846 mode->c_iflag |= IXANY;
847 else
848 mode->c_iflag &= ~IXANY;
849 }
850 else if (TABDLY && info->name == stty_tabs) {
851 if (reversed)
852 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
853 else
854 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
855 }
856 else if (OXTABS && info->name == stty_tabs) {
857 if (reversed)
858 mode->c_oflag |= OXTABS;
859 else
860 mode->c_oflag &= ~OXTABS;
861 }
862 else if (XCASE && IUCLC && OLCUC
863 && (info->name == stty_lcase || info->name == stty_LCASE)) {
864 if (reversed) {
865 mode->c_lflag &= ~XCASE;
866 mode->c_iflag &= ~IUCLC;
867 mode->c_oflag &= ~OLCUC;
868 } else {
869 mode->c_lflag |= XCASE;
870 mode->c_iflag |= IUCLC;
871 mode->c_oflag |= OLCUC;
872 }
873 }
874 else if (info->name == stty_crt) {
875 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
876 }
877 else if (info->name == stty_dec) {
878 mode->c_cc[VINTR] = 3; /* ^C */
879 mode->c_cc[VERASE] = 127; /* DEL */
880 mode->c_cc[VKILL] = 21; /* ^U */
881 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
882 if (IXANY) mode->c_iflag &= ~IXANY;
883 }
884 }
885
886 static void set_control_char_or_die(const struct control_info *info,
887 const char *arg, struct termios *mode)
888 {
889 unsigned char value;
890
891 if (info->name == stty_min || info->name == stty_time)
892 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
893 else if (arg[0] == '\0' || arg[1] == '\0')
894 value = arg[0];
895 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
896 value = _POSIX_VDISABLE;
897 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
898 value = arg[1] & 0x1f; /* Non-letters get weird results */
899 if (arg[1] == '?')
900 value = 127;
901 } else
902 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
903 mode->c_cc[info->offset] = value;
904 }
905
906 #define STTY_require_set_attr (1<<0)
907 #define STTY_speed_was_set (1<<1)
908 #define STTY_verbose_output (1<<2)
909 #define STTY_recoverable_output (1<<3)
910 #define STTY_noargs (1<<4)
911 int stty_main(int argc, char **argv)
912 {
913 struct termios mode;
914 void (*output_func)(const struct termios *, const int);
915 const char *file_name = NULL;
916 int display_all = 0;
917 int stty_state;
918 int k;
919
920 stty_state = STTY_noargs;
921 output_func = do_display;
922
923 /* First pass: only parse/verify command line params */
924 k = 0;
925 while (argv[++k]) {
926 const struct mode_info *mp;
927 const struct control_info *cp;
928 const char *arg = argv[k];
929 const char *argnext = argv[k+1];
930 int param;
931
932 if (arg[0] == '-') {
933 int i;
934 mp = find_mode(arg+1);
935 if (mp) {
936 if (!(mp->flags & REV))
937 goto invalid_argument;
938 stty_state &= ~STTY_noargs;
939 continue;
940 }
941 /* It is an option - parse it */
942 i = 0;
943 while (arg[++i]) {
944 switch (arg[i]) {
945 case 'a':
946 stty_state |= STTY_verbose_output;
947 output_func = do_display;
948 display_all = 1;
949 break;
950 case 'g':
951 stty_state |= STTY_recoverable_output;
952 output_func = display_recoverable;
953 break;
954 case 'F':
955 if (file_name)
956 bb_error_msg_and_die("only one device may be specified");
957 file_name = &arg[i+1]; /* "-Fdevice" ? */
958 if (!file_name[0]) { /* nope, "-F device" */
959 int p = k+1; /* argv[p] is argnext */
960 file_name = argnext;
961 if (!file_name)
962 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
963 /* remove -F param from arg[vc] */
964 --argc;
965 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
966 }
967 goto end_option;
968 default:
969 goto invalid_argument;
970 }
971 }
972 end_option:
973 continue;
974 }
975
976 mp = find_mode(arg);
977 if (mp) {
978 stty_state &= ~STTY_noargs;
979 continue;
980 }
981
982 cp = find_control(arg);
983 if (cp) {
984 if (!argnext)
985 bb_error_msg_and_die(bb_msg_requires_arg, arg);
986 /* called for the side effect of xfunc death only */
987 set_control_char_or_die(cp, argnext, &mode);
988 stty_state &= ~STTY_noargs;
989 ++k;
990 continue;
991 }
992
993 param = find_param(arg);
994 if (param & param_need_arg) {
995 if (!argnext)
996 bb_error_msg_and_die(bb_msg_requires_arg, arg);
997 ++k;
998 }
999
1000 switch (param) {
1001 #ifdef HAVE_C_LINE
1002 case param_line:
1003 # ifndef TIOCGWINSZ
1004 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1005 break;
1006 # endif /* else fall-through */
1007 #endif
1008 #ifdef TIOCGWINSZ
1009 case param_rows:
1010 case param_cols:
1011 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1012 break;
1013 case param_size:
1014 #endif
1015 case param_speed:
1016 break;
1017 case param_ispeed:
1018 /* called for the side effect of xfunc death only */
1019 set_speed_or_die(input_speed, argnext, &mode);
1020 break;
1021 case param_ospeed:
1022 /* called for the side effect of xfunc death only */
1023 set_speed_or_die(output_speed, argnext, &mode);
1024 break;
1025 default:
1026 if (recover_mode(arg, &mode) == 1) break;
1027 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1028 invalid_argument:
1029 bb_error_msg_and_die("invalid argument '%s'", arg);
1030 }
1031 stty_state &= ~STTY_noargs;
1032 }
1033
1034 /* Specifying both -a and -g is an error */
1035 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1036 (STTY_verbose_output | STTY_recoverable_output))
1037 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1038 /* Specifying -a or -g with non-options is an error */
1039 if (!(stty_state & STTY_noargs) &&
1040 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
1041 bb_error_msg_and_die("modes may not be set when specifying an output style");
1042
1043 /* Now it is safe to start doing things */
1044 if (file_name) {
1045 int fd, fdflags;
1046 device_name = file_name;
1047 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
1048 if (fd != STDIN_FILENO) {
1049 dup2(fd, STDIN_FILENO);
1050 close(fd);
1051 }
1052 fdflags = fcntl(STDIN_FILENO, F_GETFL);
1053 if (fdflags < 0 ||
1054 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
1055 perror_on_device_and_die("%s: cannot reset non-blocking mode");
1056 }
1057
1058 /* Initialize to all zeroes so there is no risk memcmp will report a
1059 spurious difference in an uninitialized portion of the structure */
1060 memset(&mode, 0, sizeof(mode));
1061 if (tcgetattr(STDIN_FILENO, &mode))
1062 perror_on_device_and_die("%s");
1063
1064 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1065 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
1066 output_func(&mode, display_all);
1067 return EXIT_SUCCESS;
1068 }
1069
1070 /* Second pass: perform actions */
1071 k = 0;
1072 while (argv[++k]) {
1073 const struct mode_info *mp;
1074 const struct control_info *cp;
1075 const char *arg = argv[k];
1076 const char *argnext = argv[k+1];
1077 int param;
1078
1079 if (arg[0] == '-') {
1080 mp = find_mode(arg+1);
1081 if (mp) {
1082 set_mode(mp, 1 /* reversed */, &mode);
1083 stty_state |= STTY_require_set_attr;
1084 }
1085 /* It is an option - already parsed. Skip it */
1086 continue;
1087 }
1088
1089 mp = find_mode(arg);
1090 if (mp) {
1091 set_mode(mp, 0 /* non-reversed */, &mode);
1092 stty_state |= STTY_require_set_attr;
1093 continue;
1094 }
1095
1096 cp = find_control(arg);
1097 if (cp) {
1098 ++k;
1099 set_control_char_or_die(cp, argnext, &mode);
1100 stty_state |= STTY_require_set_attr;
1101 continue;
1102 }
1103
1104 param = find_param(arg);
1105 if (param & param_need_arg) {
1106 ++k;
1107 }
1108
1109 switch (param) {
1110 #ifdef HAVE_C_LINE
1111 case param_line:
1112 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1113 stty_state |= STTY_require_set_attr;
1114 break;
1115 #endif
1116 #ifdef TIOCGWINSZ
1117 case param_cols:
1118 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1119 break;
1120 case param_size:
1121 display_window_size(0);
1122 break;
1123 case param_rows:
1124 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1125 break;
1126 #endif
1127 case param_speed:
1128 display_speed(&mode, 0);
1129 break;
1130 case param_ispeed:
1131 set_speed_or_die(input_speed, argnext, &mode);
1132 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1133 break;
1134 case param_ospeed:
1135 set_speed_or_die(output_speed, argnext, &mode);
1136 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1137 break;
1138 default:
1139 if (recover_mode(arg, &mode) == 1)
1140 stty_state |= STTY_require_set_attr;
1141 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1142 set_speed_or_die(both_speeds, arg, &mode);
1143 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1144 } /* else - impossible (caught in the first pass):
1145 bb_error_msg_and_die("invalid argument '%s'", arg); */
1146 }
1147 }
1148
1149 if (stty_state & STTY_require_set_attr) {
1150 struct termios new_mode;
1151
1152 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1153 perror_on_device_and_die("%s");
1154
1155 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1156 it performs *any* of the requested operations. This means it
1157 can report 'success' when it has actually failed to perform
1158 some proper subset of the requested operations. To detect
1159 this partial failure, get the current terminal attributes and
1160 compare them to the requested ones */
1161
1162 /* Initialize to all zeroes so there is no risk memcmp will report a
1163 spurious difference in an uninitialized portion of the structure */
1164 memset(&new_mode, 0, sizeof(new_mode));
1165 if (tcgetattr(STDIN_FILENO, &new_mode))
1166 perror_on_device_and_die("%s");
1167
1168 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1169 #ifdef CIBAUD
1170 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1171 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1172 sometimes (m1 != m2). The only difference is in the four bits
1173 of the c_cflag field corresponding to the baud rate. To save
1174 Sun users a little confusion, don't report an error if this
1175 happens. But suppress the error only if we haven't tried to
1176 set the baud rate explicitly -- otherwise we'd never give an
1177 error for a true failure to set the baud rate */
1178
1179 new_mode.c_cflag &= (~CIBAUD);
1180 if ((stty_state & STTY_speed_was_set)
1181 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1182 #endif
1183 perror_on_device_and_die("%s: cannot perform all requested operations");
1184 }
1185 }
1186
1187 return EXIT_SUCCESS;
1188 }