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