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