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