Modified so that it "works" for archs other than i386... arm in particular.
[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 <dzo@simtreas.ru> 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 = 0;
576         while (++k < argc) {
577                 int match_found = 0;
578                 int reversed = 0;
579                 int i;
580
581                 if (argv[k][0] == '-') {
582                         char *find_dev_opt;
583
584                         ++argv[k];
585
586      /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
587         Find the options that have been parsed.  This is really
588         gross, but it's needed because stty SETTINGS look like options to
589         getopt(), so we need to work around things in a really horrible
590         way.  If any new options are ever added to stty, the short option
591         MUST NOT be a letter which is the first letter of one of the
592         possible stty settings.
593      */
594                         find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
595                         if(find_dev_opt) {
596                                 if(find_dev_opt[1]==0)  /* -*F   /dev/foo */
597                                         k++;            /* skip  /dev/foo */
598                                 continue;   /* else -*F/dev/foo - no skip */
599                         }
600                         if(argv[k][0]=='a' || argv[k][0]=='g')
601                                 continue;
602                         /* Is not options - is reverse params */
603                         reversed = 1;
604                 }
605                 for (i = 0; i < NUM_mode_info; ++i)
606                         if (STREQ(argv[k], mode_info[i].name)) {
607                                 match_found = set_mode(&mode_info[i], reversed, &mode);
608                                 require_set_attr = 1;
609                                 break;
610                         }
611
612                 if (match_found == 0 && reversed)
613                         error_msg_and_die("invalid argument `%s'", --argv[k]);
614
615                 if (match_found == 0)
616                         for (i = 0; i < NUM_control_info; ++i)
617                                 if (STREQ(argv[k], control_info[i].name)) {
618                                         if (k == argc - 1)
619                                             error_msg_and_die("missing argument to `%s'", argv[k]);
620                                         match_found = 1;
621                                         ++k;
622                                         set_control_char(&control_info[i], argv[k], &mode);
623                                         require_set_attr = 1;
624                                         break;
625                                 }
626
627                 if (match_found == 0) {
628                         if (STREQ(argv[k], "ispeed")) {
629                                 if (k == argc - 1)
630                                     error_msg_and_die("missing argument to `%s'", argv[k]);
631                                 ++k;
632                                 set_speed(input_speed, argv[k], &mode);
633                                 speed_was_set = 1;
634                                 require_set_attr = 1;
635                         } else if (STREQ(argv[k], "ospeed")) {
636                                 if (k == argc - 1)
637                                     error_msg_and_die("missing argument to `%s'", argv[k]);
638                                 ++k;
639                                 set_speed(output_speed, argv[k], &mode);
640                                 speed_was_set = 1;
641                                 require_set_attr = 1;
642                         }
643 #ifdef TIOCGWINSZ
644                         else if (STREQ(argv[k], "rows")) {
645                                 if (k == argc - 1)
646                                     error_msg_and_die("missing argument to `%s'", argv[k]);
647                                 ++k;
648                                 set_window_size((int) parse_number(argv[k], stty_suffixes),
649                                                                 -1, fd, device_name);
650                         } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
651                                 if (k == argc - 1)
652                                     error_msg_and_die("missing argument to `%s'", argv[k]);
653                                 ++k;
654                                 set_window_size(-1,
655                                                 (int) parse_number(argv[k], stty_suffixes),
656                                                 fd, device_name);
657                         } else if (STREQ(argv[k], "size")) {
658                                 max_col = screen_columns();
659                                 current_col = 0;
660                                 display_window_size(0, fd, device_name);
661                         }
662 #endif
663 #ifdef HAVE_C_LINE
664                         else if (STREQ(argv[k], "line")) {
665                                 if (k == argc - 1)
666                                         error_msg_and_die("missing argument to `%s'", argv[k]);
667                                 ++k;
668                                 mode.c_line = parse_number(argv[k], stty_suffixes);
669                                 require_set_attr = 1;
670                         }
671 #endif
672                         else if (STREQ(argv[k], "speed")) {
673                                 max_col = screen_columns();
674                                 display_speed(&mode, 0);
675                         } else if (recover_mode(argv[k], &mode) == 1)
676                                 require_set_attr = 1;
677                         else if (string_to_baud(argv[k]) != (speed_t) - 1) {
678                                 set_speed(both_speeds, argv[k], &mode);
679                                 speed_was_set = 1;
680                                 require_set_attr = 1;
681                         } else
682                                 error_msg_and_die("invalid argument `%s'", argv[k]);
683                 }
684         }
685
686         if (require_set_attr) {
687                 struct termios new_mode;
688
689                 if (tcsetattr(fd, TCSADRAIN, &mode))
690                         perror_msg_and_die("%s", device_name);
691
692                 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
693                    it performs *any* of the requested operations.  This means it
694                    can report `success' when it has actually failed to perform
695                    some proper subset of the requested operations.  To detect
696                    this partial failure, get the current terminal attributes and
697                    compare them to the requested ones.  */
698
699                 /* Initialize to all zeroes so there is no risk memcmp will report a
700                    spurious difference in an uninitialized portion of the structure.  */
701                 memset(&new_mode, 0, sizeof(new_mode));
702                 if (tcgetattr(fd, &new_mode))
703                         perror_msg_and_die("%s", device_name);
704
705                 /* Normally, one shouldn't use memcmp to compare structures that
706                    may have `holes' containing uninitialized data, but we have been
707                    careful to initialize the storage of these two variables to all
708                    zeroes.  One might think it more efficient simply to compare the
709                    modified fields, but that would require enumerating those fields --
710                    and not all systems have the same fields in this structure.  */
711
712                 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
713 #ifdef CIBAUD
714                         /* SunOS 4.1.3 (at least) has the problem that after this sequence,
715                            tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
716                            sometimes (m1 != m2).  The only difference is in the four bits
717                            of the c_cflag field corresponding to the baud rate.  To save
718                            Sun users a little confusion, don't report an error if this
719                            happens.  But suppress the error only if we haven't tried to
720                            set the baud rate explicitly -- otherwise we'd never give an
721                            error for a true failure to set the baud rate.  */
722
723                         new_mode.c_cflag &= (~CIBAUD);
724                         if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
725 #endif
726                                 error_msg_and_die ("%s: unable to perform all requested operations",
727                                          device_name);
728                 }
729         }
730
731         return EXIT_SUCCESS;
732 }
733
734 /* Return 0 if not applied because not reversible; otherwise return 1.  */
735
736 static int
737 set_mode(const struct mode_info *info, int reversed, struct termios *mode)
738 {
739         tcflag_t *bitsp;
740
741         if (reversed && (info->flags & REV) == 0)
742                 return 0;
743
744         bitsp = mode_type_flag(info->type, mode);
745
746         if (bitsp == NULL) {
747                 /* Combination mode. */
748                 if (info->name == evenp || info->name == parity) {
749                         if (reversed)
750                                 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
751                         else
752                                 mode->c_cflag =
753                                         (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
754                 } else if (info->name == stty_oddp) {
755                         if (reversed)
756                                 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
757                         else
758                                 mode->c_cflag =
759                                         (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
760                 } else if (info->name == stty_nl) {
761                         if (reversed) {
762                                 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
763                                 mode->c_oflag = (mode->c_oflag
764 #ifdef ONLCR
765                                                                  | ONLCR
766 #endif
767                                         )
768 #ifdef OCRNL
769                                         & ~OCRNL
770 #endif
771 #ifdef ONLRET
772                                         & ~ONLRET
773 #endif
774                                         ;
775                         } else {
776                                 mode->c_iflag = mode->c_iflag & ~ICRNL;
777 #ifdef ONLCR
778                                 mode->c_oflag = mode->c_oflag & ~ONLCR;
779 #endif
780                         }
781                 } else if (info->name == stty_ek) {
782                         mode->c_cc[VERASE] = CERASE;
783                         mode->c_cc[VKILL] = CKILL;
784                 } else if (info->name == stty_sane)
785                         sane_mode(mode);
786                 else if (info->name == cbreak) {
787                         if (reversed)
788                                 mode->c_lflag |= ICANON;
789                         else
790                                 mode->c_lflag &= ~ICANON;
791                 } else if (info->name == stty_pass8) {
792                         if (reversed) {
793                                 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
794                                 mode->c_iflag |= ISTRIP;
795                         } else {
796                                 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
797                                 mode->c_iflag &= ~ISTRIP;
798                         }
799                 } else if (info->name == litout) {
800                         if (reversed) {
801                                 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
802                                 mode->c_iflag |= ISTRIP;
803                                 mode->c_oflag |= OPOST;
804                         } else {
805                                 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
806                                 mode->c_iflag &= ~ISTRIP;
807                                 mode->c_oflag &= ~OPOST;
808                         }
809                 } else if (info->name == raw || info->name == cooked) {
810                         if ((info->name[0] == 'r' && reversed)
811                                 || (info->name[0] == 'c' && !reversed)) {
812                                 /* Cooked mode. */
813                                 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
814                                 mode->c_oflag |= OPOST;
815                                 mode->c_lflag |= ISIG | ICANON;
816 #if VMIN == VEOF
817                                 mode->c_cc[VEOF] = CEOF;
818 #endif
819 #if VTIME == VEOL
820                                 mode->c_cc[VEOL] = CEOL;
821 #endif
822                         } else {
823                                 /* Raw mode. */
824                                 mode->c_iflag = 0;
825                                 mode->c_oflag &= ~OPOST;
826                                 mode->c_lflag &= ~(ISIG | ICANON
827 #ifdef XCASE
828                                                                    | XCASE
829 #endif
830                                         );
831                                 mode->c_cc[VMIN] = 1;
832                                 mode->c_cc[VTIME] = 0;
833                         }
834                 }
835 #ifdef IXANY
836                 else if (info->name == decctlq) {
837                         if (reversed)
838                                 mode->c_iflag |= IXANY;
839                         else
840                                 mode->c_iflag &= ~IXANY;
841                 }
842 #endif
843 #ifdef TABDLY
844                 else if (info->name == stty_tabs) {
845                         if (reversed)
846                                 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
847                         else
848                                 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
849                 }
850 #else
851 # ifdef OXTABS
852                 else if (info->name == stty_tabs) {
853                         if (reversed)
854                                 mode->c_oflag = mode->c_oflag | OXTABS;
855                         else
856                                 mode->c_oflag = mode->c_oflag & ~OXTABS;
857                 }
858 # endif
859 #endif
860 #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
861                 else if (info->name == stty_lcase || info->name == stty_LCASE) {
862                         if (reversed) {
863                                 mode->c_lflag &= ~XCASE;
864                                 mode->c_iflag &= ~IUCLC;
865                                 mode->c_oflag &= ~OLCUC;
866                         } else {
867                                 mode->c_lflag |= XCASE;
868                                 mode->c_iflag |= IUCLC;
869                                 mode->c_oflag |= OLCUC;
870                         }
871                 }
872 #endif
873                 else if (info->name == stty_crt)
874                         mode->c_lflag |= ECHOE
875 #ifdef ECHOCTL
876                                 | ECHOCTL
877 #endif
878 #ifdef ECHOKE
879                                 | ECHOKE
880 #endif
881                                 ;
882                 else if (info->name == stty_dec) {
883                         mode->c_cc[VINTR] = 3;  /* ^C */
884                         mode->c_cc[VERASE] = 127;       /* DEL */
885                         mode->c_cc[VKILL] = 21; /* ^U */
886                         mode->c_lflag |= ECHOE
887 #ifdef ECHOCTL
888                                 | ECHOCTL
889 #endif
890 #ifdef ECHOKE
891                                 | ECHOKE
892 #endif
893                                 ;
894 #ifdef IXANY
895                         mode->c_iflag &= ~IXANY;
896 #endif
897                 }
898         } else if (reversed)
899                 *bitsp = *bitsp & ~info->mask & ~info->bits;
900         else
901                 *bitsp = (*bitsp & ~info->mask) | info->bits;
902
903         return 1;
904 }
905
906 static void
907 set_control_char(const struct control_info *info, const char *arg,
908                                  struct termios *mode)
909 {
910         unsigned char value;
911
912         if (info->name == stty_min || info->name == stty_time)
913                 value = parse_number(arg, stty_suffixes);
914         else if (arg[0] == '\0' || arg[1] == '\0')
915                 value = arg[0];
916         else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
917                 value = _POSIX_VDISABLE;
918         else if (arg[0] == '^' && arg[1] != '\0') {     /* Ignore any trailing junk. */
919                 if (arg[1] == '?')
920                         value = 127;
921                 else
922                         value = arg[1] & ~0140; /* Non-letters get weird results. */
923         } else
924                 value = parse_number(arg, stty_suffixes);
925         mode->c_cc[info->offset] = value;
926 }
927
928 static void
929 set_speed(enum speed_setting type, const char *arg, struct termios *mode)
930 {
931         speed_t baud;
932
933         baud = string_to_baud(arg);
934         if (type == input_speed || type == both_speeds)
935                 cfsetispeed(mode, baud);
936         if (type == output_speed || type == both_speeds)
937                 cfsetospeed(mode, baud);
938 }
939
940 #ifdef TIOCGWINSZ
941
942 static int get_win_size(int fd, struct winsize *win)
943 {
944         int err = ioctl(fd, TIOCGWINSZ, (char *) win);
945
946         return err;
947 }
948
949 static void
950 set_window_size(int rows, int cols, int fd, const char *device_name)
951 {
952         struct winsize win;
953
954         if (get_win_size(fd, &win)) {
955                 if (errno != EINVAL)
956                         perror_msg_and_die("%s", device_name);
957                 memset(&win, 0, sizeof(win));
958         }
959
960         if (rows >= 0)
961                 win.ws_row = rows;
962         if (cols >= 0)
963                 win.ws_col = cols;
964
965 # ifdef TIOCSSIZE
966         /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
967            The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
968            This comment from sys/ttold.h describes Sun's twisted logic - a better
969            test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
970            At any rate, the problem is gone in Solaris 2.x. */
971
972         if (win.ws_row == 0 || win.ws_col == 0) {
973                 struct ttysize ttysz;
974
975                 ttysz.ts_lines = win.ws_row;
976                 ttysz.ts_cols = win.ws_col;
977
978                 win.ws_row = 1;
979                 win.ws_col = 1;
980
981                 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
982                         perror_msg_and_die("%s", device_name);
983
984                 if (ioctl(fd, TIOCSSIZE, (char *) &ttysz))
985                         perror_msg_and_die("%s", device_name);
986                 return;
987         }
988 # endif
989
990         if (ioctl(fd, TIOCSWINSZ, (char *) &win))
991                 perror_msg_and_die("%s", device_name);
992 }
993
994 static void display_window_size(int fancy, int fd, const char *device_name)
995 {
996         struct winsize win;
997
998         if (get_win_size(fd, &win)) {
999                 if (errno != EINVAL)
1000                         perror_msg_and_die("%s", device_name);
1001                 if (!fancy)
1002                         perror_msg_and_die("%s: no size information for this device",
1003                                                            device_name);
1004         } else {
1005                 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
1006                           win.ws_row, win.ws_col);
1007                 if (!fancy)
1008                         current_col = 0;
1009         }
1010 }
1011 #endif
1012
1013 static int screen_columns(void)
1014 {
1015 #ifdef TIOCGWINSZ
1016         struct winsize win;
1017
1018         /* With Solaris 2.[123], this ioctl fails and errno is set to
1019            EINVAL for telnet (but not rlogin) sessions.
1020            On ISC 3.0, it fails for the console and the serial port
1021            (but it works for ptys).
1022            It can also fail on any system when stdout isn't a tty.
1023            In case of any failure, just use the default.  */
1024         if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1025                 return win.ws_col;
1026 #endif
1027
1028         if (getenv("COLUMNS"))
1029                 return atoi(getenv("COLUMNS"));
1030         return 80;
1031 }
1032
1033 static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1034 {
1035         switch (type) {
1036         case control:
1037                 return &mode->c_cflag;
1038
1039         case input:
1040                 return &mode->c_iflag;
1041
1042         case output:
1043                 return &mode->c_oflag;
1044
1045         case local:
1046                 return &mode->c_lflag;
1047
1048         default:                                        /* combination: */
1049                 return NULL;
1050         }
1051 }
1052
1053 static void
1054 display_settings(enum output_type output_type, struct termios *mode,
1055                                  int fd, const char *device_name)
1056 {
1057         switch (output_type) {
1058         case changed:
1059                 display_changed(mode);
1060                 break;
1061
1062         case all:
1063                 display_all(mode, fd, device_name);
1064                 break;
1065
1066         case recoverable:
1067                 display_recoverable(mode);
1068                 break;
1069         }
1070 }
1071
1072 static void display_changed(struct termios *mode)
1073 {
1074         int i;
1075         int empty_line;
1076         tcflag_t *bitsp;
1077         unsigned long mask;
1078         enum mode_type prev_type = control;
1079
1080         display_speed(mode, 1);
1081 #ifdef HAVE_C_LINE
1082         wrapf("line = %d;", mode->c_line);
1083 #endif
1084         putchar('\n');
1085         current_col = 0;
1086
1087         empty_line = 1;
1088         for (i = 0; control_info[i].name != stty_min; ++i) {
1089                 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1090                         continue;
1091                 /* If swtch is the same as susp, don't print both.  */
1092 #if VSWTCH == VSUSP
1093                 if (control_info[i].name == stty_swtch)
1094                         continue;
1095 #endif
1096                 /* If eof uses the same slot as min, only print whichever applies.  */
1097 #if VEOF == VMIN
1098                 if ((mode->c_lflag & ICANON) == 0
1099                         && (control_info[i].name == stty_eof
1100                                 || control_info[i].name == stty_eol)) continue;
1101 #endif
1102
1103                 empty_line = 0;
1104                 wrapf("%s = %s;", control_info[i].name,
1105                           visible(mode->c_cc[control_info[i].offset]));
1106         }
1107         if ((mode->c_lflag & ICANON) == 0) {
1108                 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1109                           (int) mode->c_cc[VTIME]);
1110         } else if (empty_line == 0)
1111                 putchar('\n');
1112         current_col = 0;
1113
1114         empty_line = 1;
1115         for (i = 0; i < NUM_mode_info; ++i) {
1116                 if (mode_info[i].flags & OMIT)
1117                         continue;
1118                 if (mode_info[i].type != prev_type) {
1119                         if (empty_line == 0) {
1120                                 putchar('\n');
1121                                 current_col = 0;
1122                                 empty_line = 1;
1123                         }
1124                         prev_type = mode_info[i].type;
1125                 }
1126
1127                 bitsp = mode_type_flag(mode_info[i].type, mode);
1128                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1129                 if ((*bitsp & mask) == mode_info[i].bits) {
1130                         if (mode_info[i].flags & SANE_UNSET) {
1131                                 wrapf("%s", mode_info[i].name);
1132                                 empty_line = 0;
1133                         }
1134                 }
1135                         else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1136                                          (SANE_SET | REV)) {
1137                         wrapf("-%s", mode_info[i].name);
1138                         empty_line = 0;
1139                 }
1140         }
1141         if (empty_line == 0)
1142                 putchar('\n');
1143         current_col = 0;
1144 }
1145
1146 static void
1147 display_all(struct termios *mode, int fd, const char *device_name)
1148 {
1149         int i;
1150         tcflag_t *bitsp;
1151         unsigned long mask;
1152         enum mode_type prev_type = control;
1153
1154         display_speed(mode, 1);
1155 #ifdef TIOCGWINSZ
1156         display_window_size(1, fd, device_name);
1157 #endif
1158 #ifdef HAVE_C_LINE
1159         wrapf("line = %d;", mode->c_line);
1160 #endif
1161         putchar('\n');
1162         current_col = 0;
1163
1164         for (i = 0; control_info[i].name != stty_min; ++i) {
1165                 /* If swtch is the same as susp, don't print both.  */
1166 #if VSWTCH == VSUSP
1167                 if (control_info[i].name == stty_swtch)
1168                         continue;
1169 #endif
1170                 /* If eof uses the same slot as min, only print whichever applies.  */
1171 #if VEOF == VMIN
1172                 if ((mode->c_lflag & ICANON) == 0
1173                         && (control_info[i].name == stty_eof
1174                                 || control_info[i].name == stty_eol)) continue;
1175 #endif
1176                 wrapf("%s = %s;", control_info[i].name,
1177                           visible(mode->c_cc[control_info[i].offset]));
1178         }
1179 #if VEOF == VMIN
1180         if ((mode->c_lflag & ICANON) == 0)
1181 #endif
1182                 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1183         if (current_col != 0)
1184                 putchar('\n');
1185         current_col = 0;
1186
1187         for (i = 0; i < NUM_mode_info; ++i) {
1188                 if (mode_info[i].flags & OMIT)
1189                         continue;
1190                 if (mode_info[i].type != prev_type) {
1191                         putchar('\n');
1192                         current_col = 0;
1193                         prev_type = mode_info[i].type;
1194                 }
1195
1196                 bitsp = mode_type_flag(mode_info[i].type, mode);
1197                 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1198                 if ((*bitsp & mask) == mode_info[i].bits)
1199                         wrapf("%s", mode_info[i].name);
1200                 else if (mode_info[i].flags & REV)
1201                         wrapf("-%s", mode_info[i].name);
1202         }
1203         putchar('\n');
1204         current_col = 0;
1205 }
1206
1207 static void display_speed(struct termios *mode, int fancy)
1208 {
1209         if (cfgetispeed(mode) == 0 || cfgetispeed(mode) == cfgetospeed(mode))
1210                 wrapf(fancy ? "speed %lu baud;" : "%lu\n",
1211                           baud_to_value(cfgetospeed(mode)));
1212         else
1213                 wrapf(fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
1214                           baud_to_value(cfgetispeed(mode)),
1215                           baud_to_value(cfgetospeed(mode)));
1216         if (!fancy)
1217                 current_col = 0;
1218 }
1219
1220 static void display_recoverable(struct termios *mode)
1221 {
1222         int i;
1223
1224         printf("%lx:%lx:%lx:%lx",
1225                    (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1226                    (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1227         for (i = 0; i < NCCS; ++i)
1228                 printf(":%x", (unsigned int) mode->c_cc[i]);
1229         putchar('\n');
1230 }
1231
1232 static int recover_mode(char *arg, struct termios *mode)
1233 {
1234         int i, n;
1235         unsigned int chr;
1236         unsigned long iflag, oflag, cflag, lflag;
1237
1238         /* Scan into temporaries since it is too much trouble to figure out
1239            the right format for `tcflag_t'.  */
1240         if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1241                            &iflag, &oflag, &cflag, &lflag, &n) != 4)
1242                 return 0;
1243         mode->c_iflag = iflag;
1244         mode->c_oflag = oflag;
1245         mode->c_cflag = cflag;
1246         mode->c_lflag = lflag;
1247         arg += n;
1248         for (i = 0; i < NCCS; ++i) {
1249                 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1250                         return 0;
1251                 mode->c_cc[i] = chr;
1252                 arg += n;
1253         }
1254
1255         /* Fail if there are too many fields.  */
1256         if (*arg != '\0')
1257                 return 0;
1258
1259         return 1;
1260 }
1261
1262 struct speed_map {
1263         speed_t speed;                          /* Internal form. */
1264         unsigned long value;            /* Numeric value. */
1265 };
1266
1267 static const struct speed_map speeds[] = {
1268         {B0, 0},
1269         {B50, 50},
1270         {B75, 75},
1271         {B110, 110},
1272         {B134, 134},
1273         {B150, 150},
1274         {B200, 200},
1275         {B300, 300},
1276         {B600, 600},
1277         {B1200, 1200},
1278         {B1800, 1800},
1279         {B2400, 2400},
1280         {B4800, 4800},
1281         {B9600, 9600},
1282         {B19200, 19200},
1283         {B38400, 38400},
1284 #ifdef B57600
1285         {B57600, 57600},
1286 #endif
1287 #ifdef B115200
1288         {B115200, 115200},
1289 #endif
1290 #ifdef B230400
1291         {B230400, 230400},
1292 #endif
1293 #ifdef B460800
1294         {B460800, 460800},
1295 #endif
1296 };
1297
1298 static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map));
1299
1300 static speed_t string_to_baud(const char *arg)
1301 {
1302         int i;
1303
1304         for (i = 0; i < NUM_SPEEDS; ++i)
1305                 if (parse_number(arg, 0) == speeds[i].value)
1306                         return speeds[i].speed;
1307         return (speed_t) - 1;
1308 }
1309
1310 static unsigned long baud_to_value(speed_t speed)
1311 {
1312         int i;
1313
1314         for (i = 0; i < NUM_SPEEDS; ++i)
1315                 if (speed == speeds[i].speed)
1316                         return speeds[i].value;
1317         return 0;
1318 }
1319
1320 static void sane_mode(struct termios *mode)
1321 {
1322         int i;
1323         tcflag_t *bitsp;
1324
1325         for (i = 0; i < NUM_control_info; ++i) {
1326 #if VMIN == VEOF
1327                 if (control_info[i].name == stty_min)
1328                         break;
1329 #endif
1330                 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1331         }
1332
1333         for (i = 0; i < NUM_mode_info; ++i) {
1334                 if (mode_info[i].flags & SANE_SET) {
1335                         bitsp = mode_type_flag(mode_info[i].type, mode);
1336                         *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
1337                 } else if (mode_info[i].flags & SANE_UNSET) {
1338                         bitsp = mode_type_flag(mode_info[i].type, mode);
1339                         *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
1340                 }
1341         }
1342 }
1343
1344 /* Return a string that is the printable representation of character CH.  */
1345 /* Adapted from `cat' by Torbjorn Granlund.  */
1346
1347 static const char *visible(unsigned int ch)
1348 {
1349         static char buf[10];
1350         char *bpout = buf;
1351
1352         if (ch == _POSIX_VDISABLE)
1353                 return "<undef>";
1354
1355         if (ch >= 32) {
1356                 if (ch < 127)
1357                         *bpout++ = ch;
1358                 else if (ch == 127) {
1359                         *bpout++ = '^';
1360                         *bpout++ = '?';
1361                 } else {
1362                         *bpout++ = 'M', *bpout++ = '-';
1363                         if (ch >= 128 + 32) {
1364                                 if (ch < 128 + 127)
1365                                         *bpout++ = ch - 128;
1366                                 else {
1367                                         *bpout++ = '^';
1368                                         *bpout++ = '?';
1369                                 }
1370                         } else {
1371                                 *bpout++ = '^';
1372                                 *bpout++ = ch - 128 + 64;
1373                         }
1374                 }
1375         } else {
1376                 *bpout++ = '^';
1377                 *bpout++ = ch + 64;
1378         }
1379         *bpout = '\0';
1380         return (const char *) buf;
1381 }
1382
1383 #ifdef TEST
1384
1385 const char *applet_name = "stty";
1386
1387 #endif
1388
1389 /*
1390 Local Variables:
1391 c-file-style: "linux"
1392 c-basic-offset: 4
1393 tab-width: 4
1394 End:
1395 */