update copyrights for 1997
[platform/upstream/coreutils.git] / src / stty.c
1 /* stty -- change and print terminal line settings
2    Copyright (C) 90,91,92,93,94,95,96,1997 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Usage: stty [-ag] [--all] [--save] [setting...]
19
20    Options:
21    -a, --all    Write all current settings to stdout in human-readable form.
22    -g, --save   Write all current settings to stdout in stty-readable form.
23
24    If no args are given, write to stdout the baud rate and settings that
25    have been changed from their defaults.  Mode reading and changes
26    are done on stdin.
27
28    David MacKenzie <djm@gnu.ai.mit.edu> */
29
30 #include <config.h>
31
32 #ifdef TERMIOS_NEEDS_XOPEN_SOURCE
33 # define _XOPEN_SOURCE
34 #endif
35
36 #include <stdio.h>
37 #include <sys/types.h>
38 #if HAVE_TERMIOS_H
39 # include <termios.h>
40 #endif
41 #ifdef GWINSZ_IN_SYS_IOCTL
42 # include <sys/ioctl.h>
43 #endif
44 #ifdef WINSIZE_IN_PTEM
45 # include <sys/stream.h>
46 # include <sys/ptem.h>
47 #endif
48 #ifdef GWINSZ_IN_SYS_PTY
49 # include <sys/ioctl.h>
50 # include <sys/tty.h>
51 # include <sys/pty.h>
52 #endif
53 #include <getopt.h>
54 #if PROTOTYPES
55 # include <stdarg.h>
56 # define VA_START(args, lastarg) va_start(args, lastarg)
57 #else
58 # include <varargs.h>
59 # define VA_START(args, lastarg) va_start(args)
60 #endif
61
62 #include "system.h"
63 #include "long-options.h"
64 #include "error.h"
65 #include "xstrtol.h"
66
67 #ifndef _POSIX_VDISABLE
68 # define _POSIX_VDISABLE ((unsigned char) 0)
69 #endif
70
71 #define Control(c) ((c) & 0x1f)
72 /* Canonical values for control characters. */
73 #ifndef CINTR
74 # define CINTR Control ('c')
75 #endif
76 #ifndef CQUIT
77 # define CQUIT 28
78 #endif
79 #ifndef CERASE
80 # define CERASE 127
81 #endif
82 #ifndef CKILL
83 # define CKILL Control ('u')
84 #endif
85 #ifndef CEOF
86 # define CEOF Control ('d')
87 #endif
88 #ifndef CEOL
89 # define CEOL _POSIX_VDISABLE
90 #endif
91 #ifndef CSTART
92 # define CSTART Control ('q')
93 #endif
94 #ifndef CSTOP
95 # define CSTOP Control ('s')
96 #endif
97 #ifndef CSUSP
98 # define CSUSP Control ('z')
99 #endif
100 #if defined(VEOL2) && !defined(CEOL2)
101 # define CEOL2 _POSIX_VDISABLE
102 #endif
103 /* ISC renamed swtch to susp for termios, but we'll accept either name.  */
104 #if defined(VSUSP) && !defined(VSWTCH)
105 # define VSWTCH VSUSP
106 # define CSWTCH CSUSP
107 #endif
108 #if defined(VSWTCH) && !defined(CSWTCH)
109 # define CSWTCH _POSIX_VDISABLE
110 #endif
111
112 /* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
113    So the default is to disable `swtch.'  */
114 #if defined (__sparc__) && defined (__svr4__)
115 # undef CSWTCH
116 # define CSWTCH _POSIX_VDISABLE
117 #endif
118
119 #if defined(VWERSE) && !defined (VWERASE)       /* AIX-3.2.5 */
120 # define VWERASE VWERSE
121 #endif
122 #if defined(VDSUSP) && !defined (CDSUSP)
123 # define CDSUSP Control ('y')
124 #endif
125 #if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
126 # define VREPRINT VRPRNT
127 #endif
128 #if defined(VREPRINT) && !defined(CRPRNT)
129 # define CRPRNT Control ('r')
130 #endif
131 #if defined(VWERASE) && !defined(CWERASE)
132 # define CWERASE Control ('w')
133 #endif
134 #if defined(VLNEXT) && !defined(CLNEXT)
135 # define CLNEXT Control ('v')
136 #endif
137 #if defined(VDISCARD) && !defined(VFLUSHO)
138 # define VFLUSHO VDISCARD
139 #endif
140 #if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
141 # define VFLUSHO VFLUSH
142 #endif
143 #if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
144 # define ECHOCTL CTLECH
145 #endif
146 #if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
147 # define ECHOCTL TCTLECH
148 #endif
149 #if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
150 # define ECHOKE CRTKIL
151 #endif
152 #if defined(VFLUSHO) && !defined(CFLUSHO)
153 # define CFLUSHO Control ('o')
154 #endif
155 #if defined(VSTATUS) && !defined(CSTATUS)
156 # define CSTATUS Control ('t')
157 #endif
158
159 /* Which speeds to set.  */
160 enum speed_setting
161   {
162     input_speed, output_speed, both_speeds
163   };
164
165 /* What to output and how.  */
166 enum output_type
167   {
168     changed, all, recoverable   /* Default, -a, -g.  */
169   };
170
171 /* Which member(s) of `struct termios' a mode uses.  */
172 enum mode_type
173   {
174     control, input, output, local, combination
175   };
176
177 /* Flags for `struct mode_info'. */
178 #define SANE_SET 1              /* Set in `sane' mode. */
179 #define SANE_UNSET 2            /* Unset in `sane' mode. */
180 #define REV 4                   /* Can be turned off by prepending `-'. */
181 #define OMIT 8                  /* Don't display value. */
182
183 /* Each mode.  */
184 struct mode_info
185   {
186     const char *name;           /* Name given on command line.  */
187     enum mode_type type;        /* Which structure element to change. */
188     char flags;                 /* Setting and display options.  */
189     unsigned long bits;         /* Bits to set for this mode.  */
190     unsigned long mask;         /* Other bits to turn off for this mode.  */
191   };
192
193 static struct mode_info mode_info[] =
194 {
195   {"parenb", control, REV, PARENB, 0},
196   {"parodd", control, REV, PARODD, 0},
197   {"cs5", control, 0, CS5, CSIZE},
198   {"cs6", control, 0, CS6, CSIZE},
199   {"cs7", control, 0, CS7, CSIZE},
200   {"cs8", control, 0, CS8, CSIZE},
201   {"hupcl", control, REV, HUPCL, 0},
202   {"hup", control, REV | OMIT, HUPCL, 0},
203   {"cstopb", control, REV, CSTOPB, 0},
204   {"cread", control, SANE_SET | REV, CREAD, 0},
205   {"clocal", control, REV, CLOCAL, 0},
206 #ifdef CRTSCTS
207   {"crtscts", control, REV, CRTSCTS, 0},
208 #endif
209
210   {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0},
211   {"brkint", input, SANE_SET | REV, BRKINT, 0},
212   {"ignpar", input, REV, IGNPAR, 0},
213   {"parmrk", input, REV, PARMRK, 0},
214   {"inpck", input, REV, INPCK, 0},
215   {"istrip", input, REV, ISTRIP, 0},
216   {"inlcr", input, SANE_UNSET | REV, INLCR, 0},
217   {"igncr", input, SANE_UNSET | REV, IGNCR, 0},
218   {"icrnl", input, SANE_SET | REV, ICRNL, 0},
219   {"ixon", input, REV, IXON, 0},
220   {"ixoff", input, SANE_UNSET | REV, IXOFF, 0},
221   {"tandem", input, REV | OMIT, IXOFF, 0},
222 #ifdef IUCLC
223   {"iuclc", input, SANE_UNSET | REV, IUCLC, 0},
224 #endif
225 #ifdef IXANY
226   {"ixany", input, SANE_UNSET | REV, IXANY, 0},
227 #endif
228 #ifdef IMAXBEL
229   {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0},
230 #endif
231
232   {"opost", output, SANE_SET | REV, OPOST, 0},
233 #ifdef OLCUC
234   {"olcuc", output, SANE_UNSET | REV, OLCUC, 0},
235 #endif
236 #ifdef OCRNL
237   {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0},
238 #endif
239 #ifdef ONLCR
240   {"onlcr", output, SANE_SET | REV, ONLCR, 0},
241 #endif
242 #ifdef ONOCR
243   {"onocr", output, SANE_UNSET | REV, ONOCR, 0},
244 #endif
245 #ifdef ONLRET
246   {"onlret", output, SANE_UNSET | REV, ONLRET, 0},
247 #endif
248 #ifdef OFILL
249   {"ofill", output, SANE_UNSET | REV, OFILL, 0},
250 #endif
251 #ifdef OFDEL
252   {"ofdel", output, SANE_UNSET | REV, OFDEL, 0},
253 #endif
254 #ifdef NLDLY
255   {"nl1", output, SANE_UNSET, NL1, NLDLY},
256   {"nl0", output, SANE_SET, NL0, NLDLY},
257 #endif
258 #ifdef CRDLY
259   {"cr3", output, SANE_UNSET, CR3, CRDLY},
260   {"cr2", output, SANE_UNSET, CR2, CRDLY},
261   {"cr1", output, SANE_UNSET, CR1, CRDLY},
262   {"cr0", output, SANE_SET, CR0, CRDLY},
263 #endif
264 #ifdef TABDLY
265   {"tab3", output, SANE_UNSET, TAB3, TABDLY},
266   {"tab2", output, SANE_UNSET, TAB2, TABDLY},
267   {"tab1", output, SANE_UNSET, TAB1, TABDLY},
268   {"tab0", output, SANE_SET, TAB0, TABDLY},
269 #else
270 # ifdef OXTABS
271   {"tab3", output, SANE_UNSET, OXTABS, 0},
272 # endif
273 #endif
274 #ifdef BSDLY
275   {"bs1", output, SANE_UNSET, BS1, BSDLY},
276   {"bs0", output, SANE_SET, BS0, BSDLY},
277 #endif
278 #ifdef VTDLY
279   {"vt1", output, SANE_UNSET, VT1, VTDLY},
280   {"vt0", output, SANE_SET, VT0, VTDLY},
281 #endif
282 #ifdef FFDLY
283   {"ff1", output, SANE_UNSET, FF1, FFDLY},
284   {"ff0", output, SANE_SET, FF0, FFDLY},
285 #endif
286
287   {"isig", local, SANE_SET | REV, ISIG, 0},
288   {"icanon", local, SANE_SET | REV, ICANON, 0},
289 #ifdef IEXTEN
290   {"iexten", local, SANE_SET | REV, IEXTEN, 0},
291 #endif
292   {"echo", local, SANE_SET | REV, ECHO, 0},
293   {"echoe", local, SANE_SET | REV, ECHOE, 0},
294   {"crterase", local, REV | OMIT, ECHOE, 0},
295   {"echok", local, SANE_SET | REV, ECHOK, 0},
296   {"echonl", local, SANE_UNSET | REV, ECHONL, 0},
297   {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0},
298 #ifdef XCASE
299   {"xcase", local, SANE_UNSET | REV, XCASE, 0},
300 #endif
301 #ifdef TOSTOP
302   {"tostop", local, SANE_UNSET | REV, TOSTOP, 0},
303 #endif
304 #ifdef ECHOPRT
305   {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0},
306   {"prterase", local, REV | OMIT, ECHOPRT, 0},
307 #endif
308 #ifdef ECHOCTL
309   {"echoctl", local, SANE_SET | REV, ECHOCTL, 0},
310   {"ctlecho", local, REV | OMIT, ECHOCTL, 0},
311 #endif
312 #ifdef ECHOKE
313   {"echoke", local, SANE_SET | REV, ECHOKE, 0},
314   {"crtkill", local, REV | OMIT, ECHOKE, 0},
315 #endif
316
317   {"evenp", combination, REV | OMIT, 0, 0},
318   {"parity", combination, REV | OMIT, 0, 0},
319   {"oddp", combination, REV | OMIT, 0, 0},
320   {"nl", combination, REV | OMIT, 0, 0},
321   {"ek", combination, OMIT, 0, 0},
322   {"sane", combination, OMIT, 0, 0},
323   {"cooked", combination, REV | OMIT, 0, 0},
324   {"raw", combination, REV | OMIT, 0, 0},
325   {"pass8", combination, REV | OMIT, 0, 0},
326   {"litout", combination, REV | OMIT, 0, 0},
327   {"cbreak", combination, REV | OMIT, 0, 0},
328 #ifdef IXANY
329   {"decctlq", combination, REV | OMIT, 0, 0},
330 #endif
331 #if defined (TABDLY) || defined (OXTABS)
332   {"tabs", combination, REV | OMIT, 0, 0},
333 #endif
334 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
335   {"lcase", combination, REV | OMIT, 0, 0},
336   {"LCASE", combination, REV | OMIT, 0, 0},
337 #endif
338   {"crt", combination, OMIT, 0, 0},
339   {"dec", combination, OMIT, 0, 0},
340
341   {NULL, control, 0, 0, 0}
342 };
343
344 /* Control character settings.  */
345 struct control_info
346   {
347     const char *name;           /* Name given on command line.  */
348     unsigned char saneval;      /* Value to set for `stty sane'.  */
349     int offset;                 /* Offset in c_cc.  */
350   };
351
352 /* Control characters. */
353
354 static struct control_info control_info[] =
355 {
356   {"intr", CINTR, VINTR},
357   {"quit", CQUIT, VQUIT},
358   {"erase", CERASE, VERASE},
359   {"kill", CKILL, VKILL},
360   {"eof", CEOF, VEOF},
361   {"eol", CEOL, VEOL},
362 #ifdef VEOL2
363   {"eol2", CEOL2, VEOL2},
364 #endif
365 #ifdef VSWTCH
366   {"swtch", CSWTCH, VSWTCH},
367 #endif
368   {"start", CSTART, VSTART},
369   {"stop", CSTOP, VSTOP},
370   {"susp", CSUSP, VSUSP},
371 #ifdef VDSUSP
372   {"dsusp", CDSUSP, VDSUSP},
373 #endif
374 #ifdef VREPRINT
375   {"rprnt", CRPRNT, VREPRINT},
376 #endif
377 #ifdef VWERASE
378   {"werase", CWERASE, VWERASE},
379 #endif
380 #ifdef VLNEXT
381   {"lnext", CLNEXT, VLNEXT},
382 #endif
383 #ifdef VFLUSHO
384   {"flush", CFLUSHO, VFLUSHO},
385 #endif
386 #ifdef VSTATUS
387   {"status", CSTATUS, VSTATUS},
388 #endif
389
390   /* These must be last because of the display routines. */
391   {"min", 1, VMIN},
392   {"time", 0, VTIME},
393   {NULL, 0, 0}
394 };
395
396 static const char *visible __P ((unsigned int ch));
397 static unsigned long baud_to_value __P ((speed_t speed));
398 static int recover_mode __P ((char *arg, struct termios *mode));
399 static int screen_columns __P ((void));
400 static int set_mode __P ((struct mode_info *info, int reversed,
401                           struct termios *mode));
402 static long integer_arg __P ((const char *s));
403 static speed_t string_to_baud __P ((const char *arg));
404 static tcflag_t *mode_type_flag __P ((enum mode_type type,
405                                       struct termios *mode));
406 static void display_all __P ((struct termios *mode));
407 static void display_changed __P ((struct termios *mode));
408 static void display_recoverable __P ((struct termios *mode));
409 static void display_settings __P ((enum output_type output_type,
410                                    struct termios *mode));
411 static void display_speed __P ((struct termios *mode, int fancy));
412 static void display_window_size __P ((int fancy));
413 static void sane_mode __P ((struct termios *mode));
414 static void set_control_char __P ((struct control_info *info, const char *arg,
415                                    struct termios *mode));
416 static void set_speed __P ((enum speed_setting type, const char *arg,
417                             struct termios *mode));
418 static void set_window_size __P ((int rows, int cols));
419
420 /* The width of the screen, for output wrapping. */
421 static int max_col;
422
423 /* Current position, to know when to wrap. */
424 static int current_col;
425
426 static struct option longopts[] =
427 {
428   {"all", no_argument, NULL, 'a'},
429   {"save", no_argument, NULL, 'g'},
430   {NULL, 0, NULL, 0}
431 };
432
433 /* The name this program was run with. */
434 char *program_name;
435
436 /* Print format string MESSAGE and optional args.
437    Wrap to next line first if it won't fit.
438    Print a space first unless MESSAGE will start a new line. */
439
440 /* VARARGS */
441 static void
442 #if PROTOTYPES
443 wrapf (const char *message,...)
444 #else
445  wrapf (message, va_alist)
446      const char *message;
447      va_dcl
448 #endif
449 {
450   va_list args;
451   char buf[1024];               /* Plenty long for our needs. */
452   int buflen;
453
454   VA_START (args, message);
455   vsprintf (buf, message, args);
456   va_end (args);
457   buflen = strlen (buf);
458   if (current_col + (current_col > 0) + buflen >= max_col)
459     {
460       putchar ('\n');
461       current_col = 0;
462     }
463   if (current_col > 0)
464     {
465       putchar (' ');
466       current_col++;
467     }
468   fputs (buf, stdout);
469   current_col += buflen;
470 }
471
472 static void
473 usage (int status)
474 {
475   if (status != 0)
476     fprintf (stderr, _("Try `%s --help' for more information.\n"),
477              program_name);
478   else
479     {
480       printf (_("\
481 Usage: %s [SETTING]...\n\
482   or:  %s OPTION\n\
483 "),
484               program_name, program_name);
485       printf (_("\
486 Print or change terminal characteristics.\n\
487 \n\
488   -a, --all       print all current settings in human-readable form\n\
489   -g, --save      print all current settings in a stty-readable form\n\
490       --help      display this help and exit\n\
491       --version   output version information and exit\n\
492 \n\
493 Optional - before SETTING indicates negation.  An * marks non-POSIX\n\
494 settings.  The underlying system defines which settings are available.\n\
495 "));
496       printf (_("\
497 \n\
498 Special characters:\n\
499 * dsusp CHAR    CHAR will send a terminal stop signal once input flushed\n\
500   eof CHAR      CHAR will send an end of file (terminate the input)\n\
501   eol CHAR      CHAR will end the line\n\
502 * eol2 CHAR     alternate CHAR for ending the line\n\
503   erase CHAR    CHAR will erase the last character typed\n\
504   intr CHAR     CHAR will send an interrupt signal\n\
505   kill CHAR     CHAR will erase the current line\n\
506 * lnext CHAR    CHAR will enter the next character quoted\n\
507   quit CHAR     CHAR will send a quit signal\n\
508 * rprnt CHAR    CHAR will redraw the current line\n\
509   start CHAR    CHAR will restart the output after stopping it\n\
510   stop CHAR     CHAR will stop the output\n\
511   susp CHAR     CHAR will send a terminal stop signal\n\
512 * swtch CHAR    CHAR will switch to a different shell layer\n\
513 * werase CHAR   CHAR will erase the last word typed\n\
514 "));
515       printf (_("\
516 \n\
517 Special settings:\n\
518   N             set the input and output speeds to N bauds\n\
519 * cols N        tell the kernel that the terminal has N columns\n\
520 * columns N     same as cols N\n\
521   ispeed N      set the input speed to N\n\
522 * line N        use line discipline N\n\
523   min N         with -icanon, set N characters minimum for a completed read\n\
524   ospeed N      set the output speed to N\n\
525 * rows N        tell the kernel that the terminal has N rows\n\
526 * size          print the number of rows and columns according to the kernel\n\
527   speed         print the terminal speed\n\
528   time N        with -icanon, set read timeout of N tenths of a second\n\
529 "));
530       printf (_("\
531 \n\
532 Control settings:\n\
533   [-]clocal     disable modem control signals\n\
534   [-]cread      allow input to be received\n\
535 * [-]crtscts    enable RTS/CTS handshaking\n\
536   csN           set character size to N bits, N in [5..8]\n\
537   [-]cstopb     use two stop bits per character (one with `-')\n\
538   [-]hup        send a hangup signal when the last process closes the tty\n\
539   [-]hupcl      same as [-]hup\n\
540   [-]parenb     generate parity bit in output and expect parity bit in input\n\
541   [-]parodd     set odd parity (even with `-')\n\
542 "));
543       printf (_("\
544 \n\
545 Input settings:\n\
546   [-]brkint     breaks cause an interrupt signal\n\
547   [-]icrnl      translate carriage return to newline\n\
548   [-]ignbrk     ignore break characters\n\
549   [-]igncr      ignore carriage return\n\
550   [-]ignpar     ignore characters with parity errors\n\
551 * [-]imaxbel    beep and do not flush a full input buffer on a character\n\
552   [-]inlcr      translate newline to carriage return\n\
553   [-]inpck      enable input parity checking\n\
554   [-]istrip     clear high (8th) bit of input characters\n\
555 * [-]iuclc      translate uppercase characters to lowercase\n\
556 * [-]ixany      let any character restart output, not only start character\n\
557   [-]ixoff      enable sending of start/stop characters\n\
558   [-]ixon       enable XON/XOFF flow control\n\
559   [-]parmrk     mark parity errors (with a 255-0-character sequence)\n\
560   [-]tandem     same as [-]ixoff\n\
561 "));
562       printf (_("\
563 \n\
564 Output settings:\n\
565 * bsN           backspace delay style, N in [0..1]\n\
566 * crN           carriage return delay style, N in [0..3]\n\
567 * ffN           form feed delay style, N in [0..1]\n\
568 * nlN           newline delay style, N in [0..1]\n\
569 * [-]ocrnl      translate carriage return to newline\n\
570 * [-]ofdel      use delete characters for fill instead of null characters\n\
571 * [-]ofill      use fill (padding) characters instead of timing for delays\n\
572 * [-]olcuc      translate lowercase characters to uppercase\n\
573 * [-]onlcr      translate newline to carriage return-newline\n\
574 * [-]onlret     newline performs a carriage return\n\
575 * [-]onocr      do not print carriage returns in the first column\n\
576   [-]opost      postprocess output\n\
577 * tabN          horizontal tab delay style, N in [0..3]\n\
578 * tabs          same as tab0\n\
579 * -tabs         same as tab3\n\
580 * vtN           vertical tab delay style, N in [0..1]\n\
581 "));
582       printf (_("\
583 \n\
584 Local settings:\n\
585   [-]crterase   echo erase characters as backspace-space-backspace\n\
586 * crtkill       kill all line by obeying the echoprt and echoe settings\n\
587 * -crtkill      kill all line by obeying the echoctl and echok settings\n\
588 * [-]ctlecho    echo control characters in hat notation (`^c')\n\
589   [-]echo       echo input characters\n\
590 * [-]echoctl    same as [-]ctlecho\n\
591   [-]echoe      same as [-]crterase\n\
592   [-]echok      echo a newline after a kill character\n\
593 * [-]echoke     same as [-]crtkill\n\
594   [-]echonl     echo newline even if not echoing other characters\n\
595 * [-]echoprt    echo erased characters backward, between `\\' and '/'\n\
596   [-]icanon     enable erase, kill, werase, and rprnt special characters\n\
597   [-]iexten     enable non-POSIX special characters\n\
598   [-]isig       enable interrupt, quit, and suspend special characters\n\
599   [-]noflsh     disable flushing after interrupt and quit special characters\n\
600 * [-]prterase   same as [-]echoprt\n\
601 * [-]tostop     stop background jobs that try to write to the terminal\n\
602 * [-]xcase      with icanon, escape with `\\' for uppercase characters\n\
603 "));
604       printf (_("\
605 \n\
606 Combination settings:\n\
607 * [-]LCASE      same as [-]lcase\n\
608   cbreak        same as -icanon\n\
609   -cbreak       same as icanon\n\
610   cooked        same as brkint ignpar istrip icrnl ixon opost isig\n\
611                 icanon, eof and eol characters to their default values\n\
612   -cooked       same as raw\n\
613   crt           same as echoe echoctl echoke\n\
614   dec           same as echoe echoctl echoke -ixany intr ^c erase 0177\n\
615                 kill ^u\n\
616 * [-]decctlq    same as [-]ixany\n\
617   ek            erase and kill characters to their default values\n\
618   evenp         same as parenb -parodd cs7\n\
619   -evenp        same as -parenb cs8\n\
620 * [-]lcase      same as xcase iuclc olcuc\n\
621   litout        same as -parenb -istrip -opost cs8\n\
622   -litout       same as parenb istrip opost cs7\n\
623   nl            same as -icrnl -onlcr\n\
624   -nl           same as icrnl -inlcr -igncr onlcr -ocrnl -onlret\n\
625   oddp          same as parenb parodd cs7\n\
626   -oddp         same as -parenb cs8\n\
627   [-]parity     same as [-]evenp\n\
628   pass8         same as -parenb -istrip cs8\n\
629   -pass8        same as parenb istrip cs7\n\
630   raw           same as -ignbrk -brkint -ignpar -parmrk -inpck -istrip\n\
631                 -inlcr -igncr -icrnl  -ixon  -ixoff  -iuclc  -ixany\n\
632                 -imaxbel -opost -isig -icanon -xcase min 1 time 0\n\
633   -raw          same as cooked\n\
634   sane          same as cread -ignbrk brkint -inlcr -igncr icrnl\n\
635                 -ixoff -iuclc -ixany imaxbel opost -olcuc -ocrnl onlcr\n\
636                 -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0\n\
637                 isig icanon iexten echo echoe echok -echonl -noflsh\n\
638                 -xcase -tostop -echoprt echoctl echoke, all special\n\
639                 characters to their default values.\n\
640 "));
641       printf (_("\
642 \n\
643 Handle the tty line connected to standard input.  Without arguments,\n\
644 prints baud rate, line discipline, and deviations from stty sane.  In\n\
645 settings, CHAR is taken literally, or coded as in ^c, 0x37, 0177 or\n\
646 127; special values ^- or undef used to disable special characters.\n\
647 "));
648       puts (_("\nReport bugs to <sh-utils-bugs@gnu.ai.mit.edu>."));
649     }
650   exit (status);
651 }
652
653 int
654 main (int argc, char **argv)
655 {
656   struct termios mode;
657   enum output_type output_type;
658   int optc;
659   int require_set_attr;
660   int speed_was_set;
661   int verbose_output;
662   int recoverable_output;
663   int k;
664
665   program_name = argv[0];
666   setlocale (LC_ALL, "");
667   bindtextdomain (PACKAGE, LOCALEDIR);
668   textdomain (PACKAGE);
669
670   parse_long_options (argc, argv, "stty", GNU_PACKAGE, VERSION, usage);
671
672   output_type = changed;
673   verbose_output = 0;
674   recoverable_output = 0;
675
676   /* Recognize the long options only.  */
677   opterr = 0;
678   while ((optc = getopt_long_only (argc, argv, "ag", longopts, NULL)) != -1)
679     {
680       switch (optc)
681         {
682         case 'a':
683           verbose_output = 1;
684           output_type = all;
685           break;
686
687         case 'g':
688           recoverable_output = 1;
689           output_type = recoverable;
690           break;
691
692         default:
693           break;
694         }
695     }
696
697   /* Recognize short options and combinations: -a, -g, -ag, and -ga.
698      They need not precede non-options.  We cannot use GNU getopt because
699      it would treat -tabs and -ixany as uses of the -a option.  */
700   for (k = optind; k < argc; k++)
701     {
702       if (argv[k][0] == '-')
703         {
704           if (argv[k][1] == 'a'
705               && argv[k][2] == '\0')
706             {
707               ++optind;
708               verbose_output = 1;
709             }
710           else if (argv[k][1] == 'g'
711                    && argv[k][2] == '\0')
712             {
713               ++optind;
714               recoverable_output = 1;
715             }
716           else if ((argv[k][1] == 'g'
717                     && argv[k][2] == 'a'
718                     && argv[k][3] == '\0')
719                    || (argv[k][1] == 'a'
720                        && argv[k][2] == 'g'
721                        && argv[k][3] == '\0'))
722             {
723               ++optind;
724               verbose_output = 1;
725               recoverable_output = 1;
726             }
727         }
728     }
729
730   /* Specifying both -a and -g gets an error.  */
731   if (verbose_output && recoverable_output)
732     error (2, 0,
733            _("the options for verbose and stty-readable output styles are\n\
734 mutually exclusive"));
735
736   /* Specifying any other arguments with -a or -g gets an error.  */
737   if (argc - optind > 0 && (verbose_output || recoverable_output))
738     error (2, 0, _("when specifying an output style, modes may not be set"));
739
740   /* Initialize to all zeroes so there is no risk memcmp will report a
741      spurious difference in an uninitialized portion of the structure.  */
742   memset (&mode, 0, sizeof (mode));
743   if (tcgetattr (0, &mode))
744     error (1, errno, _("standard input"));
745
746   if (verbose_output || recoverable_output || argc == 1)
747     {
748       max_col = screen_columns ();
749       current_col = 0;
750       display_settings (output_type, &mode);
751       exit (0);
752     }
753
754   speed_was_set = 0;
755   require_set_attr = 0;
756   k = 1;
757   while (k < argc)
758     {
759       int match_found = 0;
760       int reversed = 0;
761       int i;
762
763       if (argv[k][0] == '-')
764         {
765           ++argv[k];
766           reversed = 1;
767         }
768       for (i = 0; mode_info[i].name != NULL; ++i)
769         {
770           if (!strcmp (argv[k], mode_info[i].name))
771             {
772               match_found = set_mode (&mode_info[i], reversed, &mode);
773               require_set_attr = 1;
774               break;
775             }
776         }
777       if (match_found == 0 && reversed)
778         {
779           error (0, 0, _("invalid argument `%s'"), --argv[k]);
780           usage (1);
781         }
782       if (match_found == 0)
783         {
784           for (i = 0; control_info[i].name != NULL; ++i)
785             {
786               if (!strcmp (argv[k], control_info[i].name))
787                 {
788                   if (k == argc - 1)
789                     {
790                       error (0, 0, _("missing argument to `%s'"), argv[k]);
791                       usage (1);
792                     }
793                   match_found = 1;
794                   ++k;
795                   set_control_char (&control_info[i], argv[k], &mode);
796                   require_set_attr = 1;
797                   break;
798                 }
799             }
800         }
801       if (match_found == 0)
802         {
803           if (!strcmp (argv[k], "ispeed"))
804             {
805               if (k == argc - 1)
806                 {
807                   error (0, 0, _("missing argument to `%s'"), argv[k]);
808                   usage (1);
809                 }
810               ++k;
811               set_speed (input_speed, argv[k], &mode);
812               speed_was_set = 1;
813               require_set_attr = 1;
814             }
815           else if (!strcmp (argv[k], "ospeed"))
816             {
817               if (k == argc - 1)
818                 {
819                   error (0, 0, _("missing argument to `%s'"), argv[k]);
820                   usage (1);
821                 }
822               ++k;
823               set_speed (output_speed, argv[k], &mode);
824               speed_was_set = 1;
825               require_set_attr = 1;
826             }
827 #ifdef TIOCGWINSZ
828           else if (!strcmp (argv[k], "rows"))
829             {
830               if (k == argc - 1)
831                 {
832                   error (0, 0, _("missing argument to `%s'"), argv[k]);
833                   usage (1);
834                 }
835               ++k;
836               set_window_size ((int) integer_arg (argv[k]), -1);
837             }
838           else if (!strcmp (argv[k], "cols")
839                    || !strcmp (argv[k], "columns"))
840             {
841               if (k == argc - 1)
842                 {
843                   error (0, 0, _("missing argument to `%s'"), argv[k]);
844                   usage (1);
845                 }
846               ++k;
847               set_window_size (-1, (int) integer_arg (argv[k]));
848             }
849           else if (!strcmp (argv[k], "size"))
850             {
851               max_col = screen_columns ();
852               current_col = 0;
853               display_window_size (0);
854             }
855 #endif
856 #ifdef HAVE_C_LINE
857           else if (!strcmp (argv[k], "line"))
858             {
859               if (k == argc - 1)
860                 {
861                   error (0, 0, _("missing argument to `%s'"), argv[k]);
862                   usage (1);
863                 }
864               ++k;
865               mode.c_line = integer_arg (argv[k]);
866               require_set_attr = 1;
867             }
868 #endif
869           else if (!strcmp (argv[k], "speed"))
870             {
871               max_col = screen_columns ();
872               display_speed (&mode, 0);
873             }
874           else if (string_to_baud (argv[k]) != (speed_t) -1)
875             {
876               set_speed (both_speeds, argv[k], &mode);
877               speed_was_set = 1;
878               require_set_attr = 1;
879             }
880           else
881             {
882               if (recover_mode (argv[k], &mode) == 0)
883                 {
884                   error (0, 0, _("invalid argument `%s'"), argv[k]);
885                   usage (1);
886                 }
887               require_set_attr = 1;
888             }
889         }
890       k++;
891     }
892
893   if (require_set_attr)
894     {
895       struct termios new_mode;
896
897       if (tcsetattr (0, TCSADRAIN, &mode))
898         error (1, errno, _("standard input"));
899
900       /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
901          it performs *any* of the requested operations.  This means it
902          can report `success' when it has actually failed to perform
903          some proper subset of the requested operations.  To detect
904          this partial failure, get the current terminal attributes and
905          compare them to the requested ones.  */
906
907       /* Initialize to all zeroes so there is no risk memcmp will report a
908          spurious difference in an uninitialized portion of the structure.  */
909       memset (&new_mode, 0, sizeof (new_mode));
910       if (tcgetattr (0, &new_mode))
911         error (1, errno, _("standard input"));
912
913       /* Normally, one shouldn't use memcmp to compare structures that
914          may have `holes' containing uninitialized data, but we have been
915          careful to initialize the storage of these two variables to all
916          zeroes.  One might think it more efficient simply to compare the
917          modified fields, but that would require enumerating those fields --
918          and not all systems have the same fields in this structure.  */
919
920       if (memcmp (&mode, &new_mode, sizeof (mode)) != 0)
921         {
922 #ifdef CIBAUD
923           /* SunOS 4.1.3 (at least) has the problem that after this sequence,
924              tcgetattr(&m1); tcsetattr(&m1); tcgetattr(&m2);
925              sometimes (m1 != m2).  The only difference is in the four bits
926              of the c_cflag field corresponding to the baud rate.  To save
927              Sun users a little confusion, don't report an error if this
928              happens.  But suppress the error only if we haven't tried to
929              set the baud rate explicitly -- otherwise we'd never give an
930              error for a true failure to set the baud rate.  */
931
932           new_mode.c_cflag &= (~CIBAUD);
933           if (speed_was_set || memcmp (&mode, &new_mode, sizeof (mode)) != 0)
934 #endif
935             {
936               size_t i;
937               error (1, 0,
938               _("standard input: unable to perform all requested operations"));
939               printf (_("new_mode: mode\n"));
940               for (i = 0; i < sizeof (new_mode); i++)
941                 printf ("0x%02x: 0x%02x\n",
942                         *(((unsigned char *) &new_mode) + i),
943                         *(((unsigned char *) &mode) + i));
944             }
945         }
946     }
947
948   exit (0);
949 }
950
951 /* Return 0 if not applied because not reversible; otherwise return 1. */
952
953 static int
954 set_mode (struct mode_info *info, int reversed, struct termios *mode)
955 {
956   tcflag_t *bitsp;
957
958   if (reversed && (info->flags & REV) == 0)
959     return 0;
960
961   bitsp = mode_type_flag (info->type, mode);
962
963   if (bitsp == NULL)
964     {
965       /* Combination mode. */
966       if (!strcmp (info->name, "evenp") || !strcmp (info->name, "parity"))
967         {
968           if (reversed)
969             mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
970           else
971             mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
972         }
973       else if (!strcmp (info->name, "oddp"))
974         {
975           if (reversed)
976             mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
977           else
978             mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
979         }
980       else if (!strcmp (info->name, "nl"))
981         {
982           if (reversed)
983             {
984               mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
985               mode->c_oflag = (mode->c_oflag
986 #ifdef ONLCR
987                                | ONLCR
988 #endif
989                 )
990 #ifdef OCRNL
991                 & ~OCRNL
992 #endif
993 #ifdef ONLRET
994                 & ~ONLRET
995 #endif
996                 ;
997             }
998           else
999             {
1000               mode->c_iflag = mode->c_iflag & ~ICRNL;
1001 #ifdef ONLCR
1002               mode->c_oflag = mode->c_oflag & ~ONLCR;
1003 #endif
1004             }
1005         }
1006       else if (!strcmp (info->name, "ek"))
1007         {
1008           mode->c_cc[VERASE] = CERASE;
1009           mode->c_cc[VKILL] = CKILL;
1010         }
1011       else if (!strcmp (info->name, "sane"))
1012         sane_mode (mode);
1013       else if (!strcmp (info->name, "cbreak"))
1014         {
1015           if (reversed)
1016             mode->c_lflag |= ICANON;
1017           else
1018             mode->c_lflag &= ~ICANON;
1019         }
1020       else if (!strcmp (info->name, "pass8"))
1021         {
1022           if (reversed)
1023             {
1024               mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1025               mode->c_iflag |= ISTRIP;
1026             }
1027           else
1028             {
1029               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1030               mode->c_iflag &= ~ISTRIP;
1031             }
1032         }
1033       else if (!strcmp (info->name, "litout"))
1034         {
1035           if (reversed)
1036             {
1037               mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1038               mode->c_iflag |= ISTRIP;
1039               mode->c_oflag |= OPOST;
1040             }
1041           else
1042             {
1043               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1044               mode->c_iflag &= ~ISTRIP;
1045               mode->c_oflag &= ~OPOST;
1046             }
1047         }
1048       else if (!strcmp (info->name, "raw") || !strcmp (info->name, "cooked"))
1049         {
1050           if ((info->name[0] == 'r' && reversed)
1051               || (info->name[0] == 'c' && !reversed))
1052             {
1053               /* Cooked mode. */
1054               mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1055               mode->c_oflag |= OPOST;
1056               mode->c_lflag |= ISIG | ICANON;
1057 #if VMIN == VEOF
1058               mode->c_cc[VEOF] = CEOF;
1059 #endif
1060 #if VTIME == VEOL
1061               mode->c_cc[VEOL] = CEOL;
1062 #endif
1063             }
1064           else
1065             {
1066               /* Raw mode. */
1067               mode->c_iflag = 0;
1068               mode->c_oflag &= ~OPOST;
1069               mode->c_lflag &= ~(ISIG | ICANON
1070 #ifdef XCASE
1071                                  | XCASE
1072 #endif
1073                 );
1074               mode->c_cc[VMIN] = 1;
1075               mode->c_cc[VTIME] = 0;
1076             }
1077         }
1078 #ifdef IXANY
1079       else if (!strcmp (info->name, "decctlq"))
1080         {
1081           if (reversed)
1082             mode->c_iflag |= IXANY;
1083           else
1084             mode->c_iflag &= ~IXANY;
1085         }
1086 #endif
1087 #ifdef TABDLY
1088       else if (!strcmp (info->name, "tabs"))
1089         {
1090           if (reversed)
1091             mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1092           else
1093             mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1094         }
1095 #else
1096 # ifdef OXTABS
1097       else if (!strcmp (info->name, "tabs"))
1098         {
1099           if (reversed)
1100             mode->c_oflag = mode->c_oflag | OXTABS;
1101           else
1102             mode->c_oflag = mode->c_oflag & ~OXTABS;
1103         }
1104 # endif
1105 #endif
1106 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
1107       else if (!strcmp (info->name, "lcase")
1108                || !strcmp (info->name, "LCASE"))
1109         {
1110           if (reversed)
1111             {
1112               mode->c_lflag &= ~XCASE;
1113               mode->c_iflag &= ~IUCLC;
1114               mode->c_oflag &= ~OLCUC;
1115             }
1116           else
1117             {
1118               mode->c_lflag |= XCASE;
1119               mode->c_iflag |= IUCLC;
1120               mode->c_oflag |= OLCUC;
1121             }
1122         }
1123 #endif
1124       else if (!strcmp (info->name, "crt"))
1125         mode->c_lflag |= ECHOE
1126 #ifdef ECHOCTL
1127           | ECHOCTL
1128 #endif
1129 #ifdef ECHOKE
1130           | ECHOKE
1131 #endif
1132           ;
1133       else if (!strcmp (info->name, "dec"))
1134         {
1135           mode->c_cc[VINTR] = 3;        /* ^C */
1136           mode->c_cc[VERASE] = 127;     /* DEL */
1137           mode->c_cc[VKILL] = 21;       /* ^U */
1138           mode->c_lflag |= ECHOE
1139 #ifdef ECHOCTL
1140             | ECHOCTL
1141 #endif
1142 #ifdef ECHOKE
1143             | ECHOKE
1144 #endif
1145             ;
1146 #ifdef IXANY
1147           mode->c_iflag &= ~IXANY;
1148 #endif
1149         }
1150     }
1151   else if (reversed)
1152     *bitsp = *bitsp & ~info->mask & ~info->bits;
1153   else
1154     *bitsp = (*bitsp & ~info->mask) | info->bits;
1155
1156   return 1;
1157 }
1158
1159 static void
1160 set_control_char (struct control_info *info, const char *arg,
1161                   struct termios *mode)
1162 {
1163   unsigned char value;
1164
1165   if (!strcmp (info->name, "min") || !strcmp (info->name, "time"))
1166     value = integer_arg (arg);
1167   else if (arg[0] == '\0' || arg[1] == '\0')
1168     value = arg[0];
1169   else if (!strcmp (arg, "^-") || !strcmp (arg, "undef"))
1170     value = _POSIX_VDISABLE;
1171   else if (arg[0] == '^' && arg[1] != '\0')     /* Ignore any trailing junk. */
1172     {
1173       if (arg[1] == '?')
1174         value = 127;
1175       else
1176         value = arg[1] & ~0140; /* Non-letters get weird results. */
1177     }
1178   else
1179     value = integer_arg (arg);
1180   mode->c_cc[info->offset] = value;
1181 }
1182
1183 static void
1184 set_speed (enum speed_setting type, const char *arg, struct termios *mode)
1185 {
1186   speed_t baud;
1187
1188   baud = string_to_baud (arg);
1189   if (type == input_speed || type == both_speeds)
1190     cfsetispeed (mode, baud);
1191   if (type == output_speed || type == both_speeds)
1192     cfsetospeed (mode, baud);
1193 }
1194
1195 #ifdef TIOCGWINSZ
1196
1197 static int
1198 get_win_size (int fd, struct winsize *win)
1199 {
1200   int err = ioctl (fd, TIOCGWINSZ, (char *) win);
1201   return err;
1202 }
1203
1204 static void
1205 set_window_size (int rows, int cols)
1206 {
1207   struct winsize win;
1208
1209   if (get_win_size (STDIN_FILENO, &win))
1210     {
1211       if (errno != EINVAL)
1212         error (1, errno, _("standard input"));
1213       memset (&win, 0, sizeof (win));
1214     }
1215
1216   if (rows >= 0)
1217     win.ws_row = rows;
1218   if (cols >= 0)
1219     win.ws_col = cols;
1220
1221 # ifdef TIOCSSIZE
1222   /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
1223      The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
1224      This comment from sys/ttold.h describes Sun's twisted logic - a better
1225      test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
1226      At any rate, the problem is gone in Solaris 2.x.
1227
1228      Unfortunately, the old TIOCSSIZE code does collide with TIOCSWINSZ,
1229      but they can be disambiguated by checking whether a "struct ttysize"
1230      structure's "ts_lines" field is greater than 64K or not.  If so,
1231      it's almost certainly a "struct winsize" instead.
1232
1233      At any rate, the bug manifests itself when ws_row == 0; the symptom is
1234      that ws_row is set to ws_col, and ws_col is set to (ws_xpixel<<16) +
1235      ws_ypixel.  Since GNU stty sets rows and columns separately, this bug
1236      caused "stty rows 0 cols 0" to set rows to cols and cols to 0, while
1237      "stty cols 0 rows 0" would do the right thing.  On a little-endian
1238      machine like the sun386i, the problem is the same, but for ws_col == 0.
1239
1240      The workaround is to do the ioctl once with row and col = 1 to set the
1241      pixel info, and then do it again using a TIOCSSIZE to set rows/cols.  */
1242
1243   if (win.ws_row == 0 || win.ws_col == 0)
1244     {
1245       struct ttysize ttysz;
1246
1247       ttysz.ts_lines = win.ws_row;
1248       ttysz.ts_cols = win.ws_col;
1249
1250       win.ws_row = 1;
1251       win.ws_col = 1;
1252
1253       if (ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win))
1254         error (1, errno, _("standard input"));
1255
1256       if (ioctl (STDIN_FILENO, TIOCSSIZE, (char *) &ttysz))
1257         error (1, errno, _("standard input"));
1258       return;
1259     }
1260 # endif
1261
1262   if (ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win))
1263     error (1, errno, _("standard input"));
1264 }
1265
1266 static void
1267 display_window_size (int fancy)
1268 {
1269   struct winsize win;
1270
1271   if (get_win_size (STDIN_FILENO, &win))
1272     {
1273       if (errno != EINVAL)
1274         error (1, errno, _("standard input"));
1275       if (!fancy)
1276         error (1, 0, _("no size information for this device"));
1277     }
1278   else
1279     {
1280       wrapf (fancy ? "rows %d; columns %d;" : "%d %d\n",
1281              win.ws_row, win.ws_col);
1282       if (!fancy)
1283         current_col = 0;
1284     }
1285 }
1286 #endif
1287
1288 static int
1289 screen_columns (void)
1290 {
1291 #ifdef TIOCGWINSZ
1292   struct winsize win;
1293
1294   /* With Solaris 2.[123], this ioctl fails and errno is set to
1295      EINVAL for telnet (but not rlogin) sessions.
1296      On ISC 3.0, it fails for the console and the serial port
1297      (but it works for ptys).
1298      It can also fail on any system when stdout isn't a tty.
1299      In case of any failure, just use the default.  */
1300   if (get_win_size (STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1301     return win.ws_col;
1302 #endif
1303   /* FIXME: use xstrtol */
1304   if (getenv ("COLUMNS"))
1305     return atoi (getenv ("COLUMNS"));
1306   return 80;
1307 }
1308
1309 static tcflag_t *
1310 mode_type_flag (enum mode_type type, struct termios *mode)
1311 {
1312   switch (type)
1313     {
1314     case control:
1315       return &mode->c_cflag;
1316
1317     case input:
1318       return &mode->c_iflag;
1319
1320     case output:
1321       return &mode->c_oflag;
1322
1323     case local:
1324       return &mode->c_lflag;
1325
1326     case combination:
1327       return NULL;
1328
1329     default:
1330       abort ();
1331     }
1332 }
1333
1334 static void
1335 display_settings (enum output_type output_type, struct termios *mode)
1336 {
1337   switch (output_type)
1338     {
1339     case changed:
1340       display_changed (mode);
1341       break;
1342
1343     case all:
1344       display_all (mode);
1345       break;
1346
1347     case recoverable:
1348       display_recoverable (mode);
1349       break;
1350     }
1351 }
1352
1353 static void
1354 display_changed (struct termios *mode)
1355 {
1356   int i;
1357   int empty_line;
1358   tcflag_t *bitsp;
1359   unsigned long mask;
1360   enum mode_type prev_type = control;
1361
1362   display_speed (mode, 1);
1363 #ifdef HAVE_C_LINE
1364   wrapf ("line = %d;", mode->c_line);
1365 #endif
1366   putchar ('\n');
1367   current_col = 0;
1368
1369   empty_line = 1;
1370   for (i = 0; strcmp (control_info[i].name, "min"); ++i)
1371     {
1372       if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1373         continue;
1374       /* If swtch is the same as susp, don't print both.  */
1375 #if VSWTCH == VSUSP
1376       if (strcmp (control_info[i].name, "swtch") == 0)
1377         continue;
1378 #endif
1379       /* If eof uses the same slot as min, only print whichever applies.  */
1380 #if VEOF == VMIN
1381       if ((mode->c_lflag & ICANON) == 0
1382           && (strcmp (control_info[i].name, "eof") == 0
1383               || strcmp(control_info[i].name, "eol") == 0))
1384         continue;
1385 #endif
1386
1387       empty_line = 0;
1388       wrapf ("%s = %s;", control_info[i].name,
1389              visible (mode->c_cc[control_info[i].offset]));
1390     }
1391   if ((mode->c_lflag & ICANON) == 0)
1392     {
1393       wrapf ("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1394              (int) mode->c_cc[VTIME]);
1395     }
1396   else if (empty_line == 0)
1397     putchar ('\n');
1398   current_col = 0;
1399
1400   empty_line = 1;
1401   for (i = 0; mode_info[i].name != NULL; ++i)
1402     {
1403       if (mode_info[i].flags & OMIT)
1404         continue;
1405       if (mode_info[i].type != prev_type)
1406         {
1407           if (empty_line == 0)
1408             {
1409               putchar ('\n');
1410               current_col = 0;
1411               empty_line = 1;
1412             }
1413           prev_type = mode_info[i].type;
1414         }
1415
1416       bitsp = mode_type_flag (mode_info[i].type, mode);
1417       mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1418       if ((*bitsp & mask) == mode_info[i].bits)
1419         {
1420           if (mode_info[i].flags & SANE_UNSET)
1421             {
1422               wrapf ("%s", mode_info[i].name);
1423               empty_line = 0;
1424             }
1425         }
1426       else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1427         {
1428           wrapf ("-%s", mode_info[i].name);
1429           empty_line = 0;
1430         }
1431     }
1432   if (empty_line == 0)
1433     putchar ('\n');
1434   current_col = 0;
1435 }
1436
1437 static void
1438 display_all (struct termios *mode)
1439 {
1440   int i;
1441   tcflag_t *bitsp;
1442   unsigned long mask;
1443   enum mode_type prev_type = control;
1444
1445   display_speed (mode, 1);
1446 #ifdef TIOCGWINSZ
1447   display_window_size (1);
1448 #endif
1449 #ifdef HAVE_C_LINE
1450   wrapf ("line = %d;", mode->c_line);
1451 #endif
1452   putchar ('\n');
1453   current_col = 0;
1454
1455   for (i = 0; strcmp (control_info[i].name, "min"); ++i)
1456     {
1457       /* If swtch is the same as susp, don't print both.  */
1458 #if VSWTCH == VSUSP
1459       if (strcmp (control_info[i].name, "swtch") == 0)
1460         continue;
1461 #endif
1462       /* If eof uses the same slot as min, only print whichever applies.  */
1463 #if VEOF == VMIN
1464       if ((mode->c_lflag & ICANON) == 0
1465           && (strcmp (control_info[i].name, "eof") == 0
1466               || strcmp(control_info[i].name, "eol") == 0))
1467         continue;
1468 #endif
1469       wrapf ("%s = %s;", control_info[i].name,
1470              visible (mode->c_cc[control_info[i].offset]));
1471     }
1472 #if VEOF == VMIN
1473   if ((mode->c_lflag & ICANON) == 0)
1474 #endif
1475     wrapf ("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1476   if (current_col != 0)
1477     putchar ('\n');
1478   current_col = 0;
1479
1480   for (i = 0; mode_info[i].name != NULL; ++i)
1481     {
1482       if (mode_info[i].flags & OMIT)
1483         continue;
1484       if (mode_info[i].type != prev_type)
1485         {
1486           putchar ('\n');
1487           current_col = 0;
1488           prev_type = mode_info[i].type;
1489         }
1490
1491       bitsp = mode_type_flag (mode_info[i].type, mode);
1492       mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1493       if ((*bitsp & mask) == mode_info[i].bits)
1494         wrapf ("%s", mode_info[i].name);
1495       else if (mode_info[i].flags & REV)
1496         wrapf ("-%s", mode_info[i].name);
1497     }
1498   putchar ('\n');
1499   current_col = 0;
1500 }
1501
1502 static void
1503 display_speed (struct termios *mode, int fancy)
1504 {
1505   if (cfgetispeed (mode) == 0 || cfgetispeed (mode) == cfgetospeed (mode))
1506     wrapf (fancy ? "speed %lu baud;" : "%lu\n",
1507            baud_to_value (cfgetospeed (mode)));
1508   else
1509     wrapf (fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
1510            baud_to_value (cfgetispeed (mode)),
1511            baud_to_value (cfgetospeed (mode)));
1512   if (!fancy)
1513     current_col = 0;
1514 }
1515
1516 static void
1517 display_recoverable (struct termios *mode)
1518 {
1519   int i;
1520
1521   printf ("%lx:%lx:%lx:%lx",
1522           (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1523           (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1524   for (i = 0; i < NCCS; ++i)
1525     printf (":%x", (unsigned int) mode->c_cc[i]);
1526   putchar ('\n');
1527 }
1528
1529 static int
1530 recover_mode (char *arg, struct termios *mode)
1531 {
1532   int i, n;
1533   unsigned int chr;
1534   unsigned long iflag, oflag, cflag, lflag;
1535
1536   /* Scan into temporaries since it is too much trouble to figure out
1537      the right format for `tcflag_t'.  */
1538   if (sscanf (arg, "%lx:%lx:%lx:%lx%n",
1539               &iflag, &oflag, &cflag, &lflag, &n) != 4)
1540     return 0;
1541   mode->c_iflag = iflag;
1542   mode->c_oflag = oflag;
1543   mode->c_cflag = cflag;
1544   mode->c_lflag = lflag;
1545   arg += n;
1546   for (i = 0; i < NCCS; ++i)
1547     {
1548       if (sscanf (arg, ":%x%n", &chr, &n) != 1)
1549         return 0;
1550       mode->c_cc[i] = chr;
1551       arg += n;
1552     }
1553
1554   /* Fail if there are too many fields.  */
1555   if (*arg != '\0')
1556     return 0;
1557
1558   return 1;
1559 }
1560
1561 struct speed_map
1562 {
1563   const char *string;           /* ASCII representation. */
1564   speed_t speed;                /* Internal form. */
1565   unsigned long value;          /* Numeric value. */
1566 };
1567
1568 struct speed_map speeds[] =
1569 {
1570   {"0", B0, 0},
1571   {"50", B50, 50},
1572   {"75", B75, 75},
1573   {"110", B110, 110},
1574   {"134", B134, 134},
1575   {"134.5", B134, 134},
1576   {"150", B150, 150},
1577   {"200", B200, 200},
1578   {"300", B300, 300},
1579   {"600", B600, 600},
1580   {"1200", B1200, 1200},
1581   {"1800", B1800, 1800},
1582   {"2400", B2400, 2400},
1583   {"4800", B4800, 4800},
1584   {"9600", B9600, 9600},
1585   {"19200", B19200, 19200},
1586   {"38400", B38400, 38400},
1587   {"exta", B19200, 19200},
1588   {"extb", B38400, 38400},
1589 #ifdef B57600
1590   {"57600", B57600, 57600},
1591 #endif
1592 #ifdef B115200
1593   {"115200", B115200, 115200},
1594 #endif
1595   {NULL, 0, 0}
1596 };
1597
1598 static speed_t
1599 string_to_baud (const char *arg)
1600 {
1601   int i;
1602
1603   for (i = 0; speeds[i].string != NULL; ++i)
1604     if (!strcmp (arg, speeds[i].string))
1605       return speeds[i].speed;
1606   return (speed_t) -1;
1607 }
1608
1609 static unsigned long
1610 baud_to_value (speed_t speed)
1611 {
1612   int i;
1613
1614   for (i = 0; speeds[i].string != NULL; ++i)
1615     if (speed == speeds[i].speed)
1616       return speeds[i].value;
1617   return 0;
1618 }
1619
1620 static void
1621 sane_mode (struct termios *mode)
1622 {
1623   int i;
1624   tcflag_t *bitsp;
1625
1626   for (i = 0; control_info[i].name; ++i)
1627     {
1628 #if VMIN == VEOF
1629       if (!strcmp (control_info[i].name, "min"))
1630         break;
1631 #endif
1632       mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1633     }
1634
1635   for (i = 0; mode_info[i].name != NULL; ++i)
1636     {
1637       if (mode_info[i].flags & SANE_SET)
1638         {
1639           bitsp = mode_type_flag (mode_info[i].type, mode);
1640           *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
1641         }
1642       else if (mode_info[i].flags & SANE_UNSET)
1643         {
1644           bitsp = mode_type_flag (mode_info[i].type, mode);
1645           *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
1646         }
1647     }
1648 }
1649
1650 /* Return a string that is the printable representation of character CH.  */
1651 /* Adapted from `cat' by Torbjorn Granlund.  */
1652
1653 static const char *
1654 visible (unsigned int ch)
1655 {
1656   static char buf[10];
1657   char *bpout = buf;
1658
1659   if (ch == _POSIX_VDISABLE)
1660     return _("<undef>");
1661
1662   if (ch >= 32)
1663     {
1664       if (ch < 127)
1665         *bpout++ = ch;
1666       else if (ch == 127)
1667         {
1668           *bpout++ = '^';
1669           *bpout++ = '?';
1670         }
1671       else
1672         {
1673           *bpout++ = 'M',
1674             *bpout++ = '-';
1675           if (ch >= 128 + 32)
1676             {
1677               if (ch < 128 + 127)
1678                 *bpout++ = ch - 128;
1679               else
1680                 {
1681                   *bpout++ = '^';
1682                   *bpout++ = '?';
1683                 }
1684             }
1685           else
1686             {
1687               *bpout++ = '^';
1688               *bpout++ = ch - 128 + 64;
1689             }
1690         }
1691     }
1692   else
1693     {
1694       *bpout++ = '^';
1695       *bpout++ = ch + 64;
1696     }
1697   *bpout = '\0';
1698   return (const char *) buf;
1699 }
1700
1701 /* Parse string S as an integer, using decimal radix by default,
1702    but allowing octal and hex numbers as in C.  */
1703 /* From `od' by Richard Stallman.  */
1704
1705 static long
1706 integer_arg (const char *s)
1707 {
1708   long value;
1709   if (xstrtol (s, NULL, 0, &value, "bB") != LONGINT_OK)
1710     {
1711       error (0, 0, _("invalid integer argument `%s'"), s);
1712       usage (1);
1713     }
1714   return value;
1715 }