summaryrefslogtreecommitdiff
path: root/src/stty.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stty.c')
-rw-r--r--src/stty.c1892
1 files changed, 1892 insertions, 0 deletions
diff --git a/src/stty.c b/src/stty.c
new file mode 100644
index 0000000..33a821d
--- /dev/null
+++ b/src/stty.c
@@ -0,0 +1,1892 @@
+/* stty -- change and print terminal line settings
+ Copyright (C) 1990-2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Usage: stty [-ag] [--all] [--save] [-F device] [--file=device] [setting...]
+
+ Options:
+ -a, --all Write all current settings to stdout in human-readable form.
+ -g, --save Write all current settings to stdout in stty-readable form.
+ -F, --file Open and use the specified device instead of stdin
+
+ If no args are given, write to stdout the baud rate and settings that
+ have been changed from their defaults. Mode reading and changes
+ are done on the specified device, or stdin if none was specified.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <config.h>
+
+#ifdef TERMIOS_NEEDS_XOPEN_SOURCE
+# define _XOPEN_SOURCE
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#if HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+#if HAVE_STROPTS_H
+# include <stropts.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef WINSIZE_IN_PTEM
+# include <sys/stream.h>
+# include <sys/ptem.h>
+#endif
+#ifdef GWINSZ_IN_SYS_PTY
+# include <sys/tty.h>
+# include <sys/pty.h>
+#endif
+#include <getopt.h>
+#include <stdarg.h>
+
+#include "system.h"
+#include "error.h"
+#include "fd-reopen.h"
+#include "quote.h"
+#include "vasprintf.h"
+#include "xstrtol.h"
+
+/* The official name of this program (e.g., no `g' prefix). */
+#define PROGRAM_NAME "stty"
+
+#define AUTHORS "David MacKenzie"
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE 0
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters. */
+#ifndef CINTR
+# define CINTR Control ('c')
+#endif
+#ifndef CQUIT
+# define CQUIT 28
+#endif
+#ifndef CERASE
+# define CERASE 127
+#endif
+#ifndef CKILL
+# define CKILL Control ('u')
+#endif
+#ifndef CEOF
+# define CEOF Control ('d')
+#endif
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+# define CSTART Control ('q')
+#endif
+#ifndef CSTOP
+# define CSTOP Control ('s')
+#endif
+#ifndef CSUSP
+# define CSUSP Control ('z')
+#endif
+#if defined VEOL2 && !defined CEOL2
+# define CEOL2 _POSIX_VDISABLE
+#endif
+/* Some platforms have VSWTC, others VSWTCH. In both cases, this control
+ character is initialized by CSWTCH, if present. */
+#if defined VSWTC && !defined VSWTCH
+# define VSWTCH VSWTC
+#endif
+/* ISC renamed swtch to susp for termios, but we'll accept either name. */
+#if defined VSUSP && !defined VSWTCH
+# define VSWTCH VSUSP
+# if defined CSUSP && !defined CSWTCH
+# define CSWTCH CSUSP
+# endif
+#endif
+#if defined VSWTCH && !defined CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
+ So the default is to disable `swtch.' */
+#if defined __sparc__ && defined __svr4__
+# undef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+#if defined VWERSE && !defined VWERASE /* AIX-3.2.5 */
+# define VWERASE VWERSE
+#endif
+#if defined VDSUSP && !defined CDSUSP
+# define CDSUSP Control ('y')
+#endif
+#if !defined VREPRINT && defined VRPRNT /* Irix 4.0.5 */
+# define VREPRINT VRPRNT
+#endif
+#if defined VREPRINT && !defined CRPRNT
+# define CRPRNT Control ('r')
+#endif
+#if defined CREPRINT && !defined CRPRNT
+# define CRPRNT Control ('r')
+#endif
+#if defined VWERASE && !defined CWERASE
+# define CWERASE Control ('w')
+#endif
+#if defined VLNEXT && !defined CLNEXT
+# define CLNEXT Control ('v')
+#endif
+#if defined VDISCARD && !defined VFLUSHO
+# define VFLUSHO VDISCARD
+#endif
+#if defined VFLUSH && !defined VFLUSHO /* Ultrix 4.2 */
+# define VFLUSHO VFLUSH
+#endif
+#if defined CTLECH && !defined ECHOCTL /* Ultrix 4.3 */
+# define ECHOCTL CTLECH
+#endif
+#if defined TCTLECH && !defined ECHOCTL /* Ultrix 4.2 */
+# define ECHOCTL TCTLECH
+#endif
+#if defined CRTKIL && !defined ECHOKE /* Ultrix 4.2 and 4.3 */
+# define ECHOKE CRTKIL
+#endif
+#if defined VFLUSHO && !defined CFLUSHO
+# define CFLUSHO Control ('o')
+#endif
+#if defined VSTATUS && !defined CSTATUS
+# define CSTATUS Control ('t')
+#endif
+
+/* Which speeds to set. */
+enum speed_setting
+ {
+ input_speed, output_speed, both_speeds
+ };
+
+/* What to output and how. */
+enum output_type
+ {
+ changed, all, recoverable /* Default, -a, -g. */
+ };
+
+/* Which member(s) of `struct termios' a mode uses. */
+enum mode_type
+ {
+ control, input, output, local, combination
+ };
+
+/* Flags for `struct mode_info'. */
+#define SANE_SET 1 /* Set in `sane' mode. */
+#define SANE_UNSET 2 /* Unset in `sane' mode. */
+#define REV 4 /* Can be turned off by prepending `-'. */
+#define OMIT 8 /* Don't display value. */
+
+/* Each mode. */
+struct mode_info
+ {
+ const char *name; /* Name given on command line. */
+ enum mode_type type; /* Which structure element to change. */
+ char flags; /* Setting and display options. */
+ unsigned long bits; /* Bits to set for this mode. */
+ unsigned long mask; /* Other bits to turn off for this mode. */
+ };
+
+static struct mode_info mode_info[] =
+{
+ {"parenb", control, REV, PARENB, 0},
+ {"parodd", control, REV, PARODD, 0},
+ {"cs5", control, 0, CS5, CSIZE},
+ {"cs6", control, 0, CS6, CSIZE},
+ {"cs7", control, 0, CS7, CSIZE},
+ {"cs8", control, 0, CS8, CSIZE},
+ {"hupcl", control, REV, HUPCL, 0},
+ {"hup", control, REV | OMIT, HUPCL, 0},
+ {"cstopb", control, REV, CSTOPB, 0},
+ {"cread", control, SANE_SET | REV, CREAD, 0},
+ {"clocal", control, REV, CLOCAL, 0},
+#ifdef CRTSCTS
+ {"crtscts", control, REV, CRTSCTS, 0},
+#endif
+
+ {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0},
+ {"brkint", input, SANE_SET | REV, BRKINT, 0},
+ {"ignpar", input, REV, IGNPAR, 0},
+ {"parmrk", input, REV, PARMRK, 0},
+ {"inpck", input, REV, INPCK, 0},
+ {"istrip", input, REV, ISTRIP, 0},
+ {"inlcr", input, SANE_UNSET | REV, INLCR, 0},
+ {"igncr", input, SANE_UNSET | REV, IGNCR, 0},
+ {"icrnl", input, SANE_SET | REV, ICRNL, 0},
+ {"ixon", input, REV, IXON, 0},
+ {"ixoff", input, SANE_UNSET | REV, IXOFF, 0},
+ {"tandem", input, REV | OMIT, IXOFF, 0},
+#ifdef IUCLC
+ {"iuclc", input, SANE_UNSET | REV, IUCLC, 0},
+#endif
+#ifdef IXANY
+ {"ixany", input, SANE_UNSET | REV, IXANY, 0},
+#endif
+#ifdef IMAXBEL
+ {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0},
+#endif
+#ifdef IUTF8
+ {"iutf8", input, SANE_UNSET | REV, IUTF8, 0},
+#endif
+
+ {"opost", output, SANE_SET | REV, OPOST, 0},
+#ifdef OLCUC
+ {"olcuc", output, SANE_UNSET | REV, OLCUC, 0},
+#endif
+#ifdef OCRNL
+ {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0},
+#endif
+#ifdef ONLCR
+ {"onlcr", output, SANE_SET | REV, ONLCR, 0},
+#endif
+#ifdef ONOCR
+ {"onocr", output, SANE_UNSET | REV, ONOCR, 0},
+#endif
+#ifdef ONLRET
+ {"onlret", output, SANE_UNSET | REV, ONLRET, 0},
+#endif
+#ifdef OFILL
+ {"ofill", output, SANE_UNSET | REV, OFILL, 0},
+#endif
+#ifdef OFDEL
+ {"ofdel", output, SANE_UNSET | REV, OFDEL, 0},
+#endif
+#ifdef NLDLY
+ {"nl1", output, SANE_UNSET, NL1, NLDLY},
+ {"nl0", output, SANE_SET, NL0, NLDLY},
+#endif
+#ifdef CRDLY
+ {"cr3", output, SANE_UNSET, CR3, CRDLY},
+ {"cr2", output, SANE_UNSET, CR2, CRDLY},
+ {"cr1", output, SANE_UNSET, CR1, CRDLY},
+ {"cr0", output, SANE_SET, CR0, CRDLY},
+#endif
+#ifdef TABDLY
+ {"tab3", output, SANE_UNSET, TAB3, TABDLY},
+ {"tab2", output, SANE_UNSET, TAB2, TABDLY},
+ {"tab1", output, SANE_UNSET, TAB1, TABDLY},
+ {"tab0", output, SANE_SET, TAB0, TABDLY},
+#else
+# ifdef OXTABS
+ {"tab3", output, SANE_UNSET, OXTABS, 0},
+# endif
+#endif
+#ifdef BSDLY
+ {"bs1", output, SANE_UNSET, BS1, BSDLY},
+ {"bs0", output, SANE_SET, BS0, BSDLY},
+#endif
+#ifdef VTDLY
+ {"vt1", output, SANE_UNSET, VT1, VTDLY},
+ {"vt0", output, SANE_SET, VT0, VTDLY},
+#endif
+#ifdef FFDLY
+ {"ff1", output, SANE_UNSET, FF1, FFDLY},
+ {"ff0", output, SANE_SET, FF0, FFDLY},
+#endif
+
+ {"isig", local, SANE_SET | REV, ISIG, 0},
+ {"icanon", local, SANE_SET | REV, ICANON, 0},
+#ifdef IEXTEN
+ {"iexten", local, SANE_SET | REV, IEXTEN, 0},
+#endif
+ {"echo", local, SANE_SET | REV, ECHO, 0},
+ {"echoe", local, SANE_SET | REV, ECHOE, 0},
+ {"crterase", local, REV | OMIT, ECHOE, 0},
+ {"echok", local, SANE_SET | REV, ECHOK, 0},
+ {"echonl", local, SANE_UNSET | REV, ECHONL, 0},
+ {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0},
+#ifdef XCASE
+ {"xcase", local, SANE_UNSET | REV, XCASE, 0},
+#endif
+#ifdef TOSTOP
+ {"tostop", local, SANE_UNSET | REV, TOSTOP, 0},
+#endif
+#ifdef ECHOPRT
+ {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0},
+ {"prterase", local, REV | OMIT, ECHOPRT, 0},
+#endif
+#ifdef ECHOCTL
+ {"echoctl", local, SANE_SET | REV, ECHOCTL, 0},
+ {"ctlecho", local, REV | OMIT, ECHOCTL, 0},
+#endif
+#ifdef ECHOKE
+ {"echoke", local, SANE_SET | REV, ECHOKE, 0},
+ {"crtkill", local, REV | OMIT, ECHOKE, 0},
+#endif
+
+ {"evenp", combination, REV | OMIT, 0, 0},
+ {"parity", combination, REV | OMIT, 0, 0},
+ {"oddp", combination, REV | OMIT, 0, 0},
+ {"nl", combination, REV | OMIT, 0, 0},
+ {"ek", combination, OMIT, 0, 0},
+ {"sane", combination, OMIT, 0, 0},
+ {"cooked", combination, REV | OMIT, 0, 0},
+ {"raw", combination, REV | OMIT, 0, 0},
+ {"pass8", combination, REV | OMIT, 0, 0},
+ {"litout", combination, REV | OMIT, 0, 0},
+ {"cbreak", combination, REV | OMIT, 0, 0},
+#ifdef IXANY
+ {"decctlq", combination, REV | OMIT, 0, 0},
+#endif
+#if defined TABDLY || defined OXTABS
+ {"tabs", combination, REV | OMIT, 0, 0},
+#endif
+#if defined XCASE && defined IUCLC && defined OLCUC
+ {"lcase", combination, REV | OMIT, 0, 0},
+ {"LCASE", combination, REV | OMIT, 0, 0},
+#endif
+ {"crt", combination, OMIT, 0, 0},
+ {"dec", combination, OMIT, 0, 0},
+
+ {NULL, control, 0, 0, 0}
+};
+
+/* Control character settings. */
+struct control_info
+ {
+ const char *name; /* Name given on command line. */
+ cc_t saneval; /* Value to set for `stty sane'. */
+ size_t offset; /* Offset in c_cc. */
+ };
+
+/* Control characters. */
+
+static struct control_info control_info[] =
+{
+ {"intr", CINTR, VINTR},
+ {"quit", CQUIT, VQUIT},
+ {"erase", CERASE, VERASE},
+ {"kill", CKILL, VKILL},
+ {"eof", CEOF, VEOF},
+ {"eol", CEOL, VEOL},
+#ifdef VEOL2
+ {"eol2", CEOL2, VEOL2},
+#endif
+#ifdef VSWTCH
+ {"swtch", CSWTCH, VSWTCH},
+#endif
+ {"start", CSTART, VSTART},
+ {"stop", CSTOP, VSTOP},
+ {"susp", CSUSP, VSUSP},
+#ifdef VDSUSP
+ {"dsusp", CDSUSP, VDSUSP},
+#endif
+#ifdef VREPRINT
+ {"rprnt", CRPRNT, VREPRINT},
+#else
+# ifdef CREPRINT /* HPUX 10.20 needs this */
+ {"rprnt", CRPRNT, CREPRINT},
+# endif
+#endif
+#ifdef VWERASE
+ {"werase", CWERASE, VWERASE},
+#endif
+#ifdef VLNEXT
+ {"lnext", CLNEXT, VLNEXT},
+#endif
+#ifdef VFLUSHO
+ {"flush", CFLUSHO, VFLUSHO},
+#endif
+#ifdef VSTATUS
+ {"status", CSTATUS, VSTATUS},
+#endif
+
+ /* These must be last because of the display routines. */
+ {"min", 1, VMIN},
+ {"time", 0, VTIME},
+ {NULL, 0, 0}
+};
+
+static char const *visible (cc_t ch);
+static unsigned long int baud_to_value (speed_t speed);
+static bool recover_mode (char const *arg, struct termios *mode);
+static int screen_columns (void);
+static bool set_mode (struct mode_info *info, bool reversed,
+ struct termios *mode);
+static unsigned long int integer_arg (const char *s, unsigned long int max);
+static speed_t string_to_baud (const char *arg);
+static tcflag_t *mode_type_flag (enum mode_type type, struct termios *mode);
+static void display_all (struct termios *mode, char const *device_name);
+static void display_changed (struct termios *mode);
+static void display_recoverable (struct termios *mode);
+static void display_settings (enum output_type output_type,
+ struct termios *mode,
+ const char *device_name);
+static void display_speed (struct termios *mode, bool fancy);
+static void display_window_size (bool fancy, char const *device_name);
+static void sane_mode (struct termios *mode);
+static void set_control_char (struct control_info *info,
+ const char *arg,
+ struct termios *mode);
+static void set_speed (enum speed_setting type, const char *arg,
+ struct termios *mode);
+static void set_window_size (int rows, int cols, char const *device_name);
+
+/* The width of the screen, for output wrapping. */
+static int max_col;
+
+/* Current position, to know when to wrap. */
+static int current_col;
+
+static struct option longopts[] =
+{
+ {"all", no_argument, NULL, 'a'},
+ {"save", no_argument, NULL, 'g'},
+ {"file", required_argument, NULL, 'F'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+/* The name this program was run with. */
+char *program_name;
+
+static void wrapf (const char *message, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+
+/* Print format string MESSAGE and optional args.
+ Wrap to next line first if it won't fit.
+ Print a space first unless MESSAGE will start a new line. */
+
+static void
+wrapf (const char *message,...)
+{
+ va_list args;
+ char *buf;
+ int buflen;
+
+ va_start (args, message);
+ buflen = vasprintf (&buf, message, args);
+ va_end (args);
+
+ if (buflen < 0)
+ xalloc_die ();
+
+ if (0 < current_col)
+ {
+ if (max_col - current_col < buflen)
+ {
+ putchar ('\n');
+ current_col = 0;
+ }
+ else
+ {
+ putchar (' ');
+ current_col++;
+ }
+ }
+
+ fputs (buf, stdout);
+ free (buf);
+ current_col += buflen;
+}
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [-F DEVICE] [--file=DEVICE] [SETTING]...\n\
+ or: %s [-F DEVICE] [--file=DEVICE] [-a|--all]\n\
+ or: %s [-F DEVICE] [--file=DEVICE] [-g|--save]\n\
+"),
+ program_name, program_name, program_name);
+ fputs (_("\
+Print or change terminal characteristics.\n\
+\n\
+ -a, --all print all current settings in human-readable form\n\
+ -g, --save print all current settings in a stty-readable form\n\
+ -F, --file=DEVICE open and use the specified DEVICE instead of stdin\n\
+"), stdout);
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ fputs (_("\
+\n\
+Optional - before SETTING indicates negation. An * marks non-POSIX\n\
+settings. The underlying system defines which settings are available.\n\
+"), stdout);
+ fputs (_("\
+\n\
+Special characters:\n\
+ * dsusp CHAR CHAR will send a terminal stop signal once input flushed\n\
+ eof CHAR CHAR will send an end of file (terminate the input)\n\
+ eol CHAR CHAR will end the line\n\
+"), stdout);
+ fputs (_("\
+ * eol2 CHAR alternate CHAR for ending the line\n\
+ erase CHAR CHAR will erase the last character typed\n\
+ intr CHAR CHAR will send an interrupt signal\n\
+ kill CHAR CHAR will erase the current line\n\
+"), stdout);
+ fputs (_("\
+ * lnext CHAR CHAR will enter the next character quoted\n\
+ quit CHAR CHAR will send a quit signal\n\
+ * rprnt CHAR CHAR will redraw the current line\n\
+ start CHAR CHAR will restart the output after stopping it\n\
+"), stdout);
+ fputs (_("\
+ stop CHAR CHAR will stop the output\n\
+ susp CHAR CHAR will send a terminal stop signal\n\
+ * swtch CHAR CHAR will switch to a different shell layer\n\
+ * werase CHAR CHAR will erase the last word typed\n\
+"), stdout);
+ fputs (_("\
+\n\
+Special settings:\n\
+ N set the input and output speeds to N bauds\n\
+ * cols N tell the kernel that the terminal has N columns\n\
+ * columns N same as cols N\n\
+"), stdout);
+ fputs (_("\
+ ispeed N set the input speed to N\n\
+ * line N use line discipline N\n\
+ min N with -icanon, set N characters minimum for a completed read\n\
+ ospeed N set the output speed to N\n\
+"), stdout);
+ fputs (_("\
+ * rows N tell the kernel that the terminal has N rows\n\
+ * size print the number of rows and columns according to the kernel\n\
+ speed print the terminal speed\n\
+ time N with -icanon, set read timeout of N tenths of a second\n\
+"), stdout);
+ fputs (_("\
+\n\
+Control settings:\n\
+ [-]clocal disable modem control signals\n\
+ [-]cread allow input to be received\n\
+ * [-]crtscts enable RTS/CTS handshaking\n\
+ csN set character size to N bits, N in [5..8]\n\
+"), stdout);
+ fputs (_("\
+ [-]cstopb use two stop bits per character (one with `-')\n\
+ [-]hup send a hangup signal when the last process closes the tty\n\
+ [-]hupcl same as [-]hup\n\
+ [-]parenb generate parity bit in output and expect parity bit in input\n\
+ [-]parodd set odd parity (even with `-')\n\
+"), stdout);
+ fputs (_("\
+\n\
+Input settings:\n\
+ [-]brkint breaks cause an interrupt signal\n\
+ [-]icrnl translate carriage return to newline\n\
+ [-]ignbrk ignore break characters\n\
+ [-]igncr ignore carriage return\n\
+"), stdout);
+ fputs (_("\
+ [-]ignpar ignore characters with parity errors\n\
+ * [-]imaxbel beep and do not flush a full input buffer on a character\n\
+ [-]inlcr translate newline to carriage return\n\
+ [-]inpck enable input parity checking\n\
+ [-]istrip clear high (8th) bit of input characters\n\
+"), stdout);
+ fputs (_("\
+ * [-]iutf8 assume input characters are UTF-8 encoded\n\
+"), stdout);
+ fputs (_("\
+ * [-]iuclc translate uppercase characters to lowercase\n\
+ * [-]ixany let any character restart output, not only start character\n\
+ [-]ixoff enable sending of start/stop characters\n\
+ [-]ixon enable XON/XOFF flow control\n\
+ [-]parmrk mark parity errors (with a 255-0-character sequence)\n\
+ [-]tandem same as [-]ixoff\n\
+"), stdout);
+ fputs (_("\
+\n\
+Output settings:\n\
+ * bsN backspace delay style, N in [0..1]\n\
+ * crN carriage return delay style, N in [0..3]\n\
+ * ffN form feed delay style, N in [0..1]\n\
+ * nlN newline delay style, N in [0..1]\n\
+"), stdout);
+ fputs (_("\
+ * [-]ocrnl translate carriage return to newline\n\
+ * [-]ofdel use delete characters for fill instead of null characters\n\
+ * [-]ofill use fill (padding) characters instead of timing for delays\n\
+ * [-]olcuc translate lowercase characters to uppercase\n\
+ * [-]onlcr translate newline to carriage return-newline\n\
+ * [-]onlret newline performs a carriage return\n\
+"), stdout);
+ fputs (_("\
+ * [-]onocr do not print carriage returns in the first column\n\
+ [-]opost postprocess output\n\
+ * tabN horizontal tab delay style, N in [0..3]\n\
+ * tabs same as tab0\n\
+ * -tabs same as tab3\n\
+ * vtN vertical tab delay style, N in [0..1]\n\
+"), stdout);
+ fputs (_("\
+\n\
+Local settings:\n\
+ [-]crterase echo erase characters as backspace-space-backspace\n\
+ * crtkill kill all line by obeying the echoprt and echoe settings\n\
+ * -crtkill kill all line by obeying the echoctl and echok settings\n\
+"), stdout);
+ fputs (_("\
+ * [-]ctlecho echo control characters in hat notation (`^c')\n\
+ [-]echo echo input characters\n\
+ * [-]echoctl same as [-]ctlecho\n\
+ [-]echoe same as [-]crterase\n\
+ [-]echok echo a newline after a kill character\n\
+"), stdout);
+ fputs (_("\
+ * [-]echoke same as [-]crtkill\n\
+ [-]echonl echo newline even if not echoing other characters\n\
+ * [-]echoprt echo erased characters backward, between `\\' and '/'\n\
+ [-]icanon enable erase, kill, werase, and rprnt special characters\n\
+ [-]iexten enable non-POSIX special characters\n\
+"), stdout);
+ fputs (_("\
+ [-]isig enable interrupt, quit, and suspend special characters\n\
+ [-]noflsh disable flushing after interrupt and quit special characters\n\
+ * [-]prterase same as [-]echoprt\n\
+ * [-]tostop stop background jobs that try to write to the terminal\n\
+ * [-]xcase with icanon, escape with `\\' for uppercase characters\n\
+"), stdout);
+ fputs (_("\
+\n\
+Combination settings:\n\
+ * [-]LCASE same as [-]lcase\n\
+ cbreak same as -icanon\n\
+ -cbreak same as icanon\n\
+"), stdout);
+ fputs (_("\
+ cooked same as brkint ignpar istrip icrnl ixon opost isig\n\
+ icanon, eof and eol characters to their default values\n\
+ -cooked same as raw\n\
+ crt same as echoe echoctl echoke\n\
+"), stdout);
+ fputs (_("\
+ dec same as echoe echoctl echoke -ixany intr ^c erase 0177\n\
+ kill ^u\n\
+ * [-]decctlq same as [-]ixany\n\
+ ek erase and kill characters to their default values\n\
+ evenp same as parenb -parodd cs7\n\
+"), stdout);
+ fputs (_("\
+ -evenp same as -parenb cs8\n\
+ * [-]lcase same as xcase iuclc olcuc\n\
+ litout same as -parenb -istrip -opost cs8\n\
+ -litout same as parenb istrip opost cs7\n\
+ nl same as -icrnl -onlcr\n\
+ -nl same as icrnl -inlcr -igncr onlcr -ocrnl -onlret\n\
+"), stdout);
+ fputs (_("\
+ oddp same as parenb parodd cs7\n\
+ -oddp same as -parenb cs8\n\
+ [-]parity same as [-]evenp\n\
+ pass8 same as -parenb -istrip cs8\n\
+ -pass8 same as parenb istrip cs7\n\
+"), stdout);
+ fputs (_("\
+ raw same as -ignbrk -brkint -ignpar -parmrk -inpck -istrip\n\
+ -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany\n\
+ -imaxbel -opost -isig -icanon -xcase min 1 time 0\n\
+ -raw same as cooked\n\
+"), stdout);
+ fputs (_("\
+ sane same as cread -ignbrk brkint -inlcr -igncr icrnl -iutf8\n\
+ -ixoff -iuclc -ixany imaxbel opost -olcuc -ocrnl onlcr\n\
+ -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0\n\
+ isig icanon iexten echo echoe echok -echonl -noflsh\n\
+ -xcase -tostop -echoprt echoctl echoke, all special\n\
+ characters to their default values.\n\
+"), stdout);
+ fputs (_("\
+\n\
+Handle the tty line connected to standard input. Without arguments,\n\
+prints baud rate, line discipline, and deviations from stty sane. In\n\
+settings, CHAR is taken literally, or coded as in ^c, 0x37, 0177 or\n\
+127; special values ^- or undef used to disable special characters.\n\
+"), stdout);
+ printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ }
+ exit (status);
+}
+
+int
+main (int argc, char **argv)
+{
+ /* Initialize to all zeroes so there is no risk memcmp will report a
+ spurious difference in an uninitialized portion of the structure. */
+ struct termios mode = { 0, };
+
+ enum output_type output_type;
+ int optc;
+ int argi = 0;
+ int opti = 1;
+ bool require_set_attr;
+ bool speed_was_set;
+ bool verbose_output;
+ bool recoverable_output;
+ int k;
+ bool noargs = true;
+ char *file_name = NULL;
+ const char *device_name;
+
+ initialize_main (&argc, &argv);
+ program_name = argv[0];
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ output_type = changed;
+ verbose_output = false;
+ recoverable_output = false;
+
+ /* Don't print error messages for unrecognized options. */
+ opterr = 0;
+
+ /* If any new options are ever added to stty, the short options MUST
+ NOT allow any ambiguity with the stty settings. For example, the
+ stty setting "-gagFork" would not be feasible, since it will be
+ parsed as "-g -a -g -F ork". If you change anything about how
+ stty parses options, be sure it still works with combinations of
+ short and long options, --, POSIXLY_CORRECT, etc. */
+
+ while ((optc = getopt_long (argc - argi, argv + argi, "-agF:",
+ longopts, NULL))
+ != -1)
+ {
+ switch (optc)
+ {
+ case 'a':
+ verbose_output = true;
+ output_type = all;
+ break;
+
+ case 'g':
+ recoverable_output = true;
+ output_type = recoverable;
+ break;
+
+ case 'F':
+ if (file_name)
+ error (EXIT_FAILURE, 0, _("only one device may be specified"));
+ file_name = optarg;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ noargs = false;
+
+ /* Skip the argument containing this unrecognized option;
+ the 2nd pass will analyze it. */
+ argi += opti;
+
+ /* Restart getopt_long from the first unskipped argument. */
+ opti = 1;
+ optind = 0;
+
+ break;
+ }
+
+ /* Clear fully-parsed arguments, so they don't confuse the 2nd pass. */
+ while (opti < optind)
+ argv[argi + opti++] = NULL;
+ }
+
+ /* Specifying both -a and -g gets an error. */
+ if (verbose_output & recoverable_output)
+ error (EXIT_FAILURE, 0,
+ _("the options for verbose and stty-readable output styles are\n"
+ "mutually exclusive"));
+
+ /* Specifying any other arguments with -a or -g gets an error. */
+ if (!noargs & (verbose_output | recoverable_output))
+ error (EXIT_FAILURE, 0,
+ _("when specifying an output style, modes may not be set"));
+
+ /* FIXME: it'd be better not to open the file until we've verified
+ that all arguments are valid. Otherwise, we could end up doing
+ only some of the requested operations and then failing, probably
+ leaving things in an undesirable state. */
+
+ if (file_name)
+ {
+ int fdflags;
+ device_name = file_name;
+ if (fd_reopen (STDIN_FILENO, device_name, O_RDONLY | O_NONBLOCK, 0) < 0)
+ error (EXIT_FAILURE, errno, "%s", device_name);
+ if ((fdflags = fcntl (STDIN_FILENO, F_GETFL)) == -1
+ || fcntl (STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ error (EXIT_FAILURE, errno, _("%s: couldn't reset non-blocking mode"),
+ device_name);
+ }
+ else
+ device_name = _("standard input");
+
+ if (tcgetattr (STDIN_FILENO, &mode))
+ error (EXIT_FAILURE, errno, "%s", device_name);
+
+ if (verbose_output | recoverable_output | noargs)
+ {
+ max_col = screen_columns ();
+ current_col = 0;
+ display_settings (output_type, &mode, device_name);
+ exit (EXIT_SUCCESS);
+ }
+
+ speed_was_set = false;
+ require_set_attr = false;
+ for (k = 1; k < argc; k++)
+ {
+ char const *arg = argv[k];
+ bool match_found = false;
+ bool reversed = false;
+ int i;
+
+ if (! arg)
+ continue;
+
+ if (arg[0] == '-')
+ {
+ ++arg;
+ reversed = true;
+ }
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (STREQ (arg, mode_info[i].name))
+ {
+ match_found = set_mode (&mode_info[i], reversed, &mode);
+ require_set_attr = true;
+ break;
+ }
+ }
+ if (!match_found & reversed)
+ {
+ error (0, 0, _("invalid argument %s"), quote (arg - 1));
+ usage (EXIT_FAILURE);
+ }
+ if (!match_found)
+ {
+ for (i = 0; control_info[i].name != NULL; ++i)
+ {
+ if (STREQ (arg, control_info[i].name))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ match_found = true;
+ ++k;
+ set_control_char (&control_info[i], argv[k], &mode);
+ require_set_attr = true;
+ break;
+ }
+ }
+ }
+ if (!match_found)
+ {
+ if (STREQ (arg, "ispeed"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_speed (input_speed, argv[k], &mode);
+ speed_was_set = true;
+ require_set_attr = true;
+ }
+ else if (STREQ (arg, "ospeed"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_speed (output_speed, argv[k], &mode);
+ speed_was_set = true;
+ require_set_attr = true;
+ }
+#ifdef TIOCGWINSZ
+ else if (STREQ (arg, "rows"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_window_size (integer_arg (argv[k], INT_MAX), -1,
+ device_name);
+ }
+ else if (STREQ (arg, "cols")
+ || STREQ (arg, "columns"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_window_size (-1, integer_arg (argv[k], INT_MAX),
+ device_name);
+ }
+ else if (STREQ (arg, "size"))
+ {
+ max_col = screen_columns ();
+ current_col = 0;
+ display_window_size (false, device_name);
+ }
+#endif
+#ifdef HAVE_C_LINE
+ else if (STREQ (arg, "line"))
+ {
+ unsigned long int value;
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ mode.c_line = value = integer_arg (argv[k], ULONG_MAX);
+ if (mode.c_line != value)
+ error (0, 0, _("invalid line discipline %s"), quote (argv[k]));
+ require_set_attr = true;
+ }
+#endif
+ else if (STREQ (arg, "speed"))
+ {
+ max_col = screen_columns ();
+ display_speed (&mode, false);
+ }
+ else if (string_to_baud (arg) != (speed_t) -1)
+ {
+ set_speed (both_speeds, arg, &mode);
+ speed_was_set = true;
+ require_set_attr = true;
+ }
+ else
+ {
+ if (! recover_mode (arg, &mode))
+ {
+ error (0, 0, _("invalid argument %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ require_set_attr = true;
+ }
+ }
+ }
+
+ if (require_set_attr)
+ {
+ /* Initialize to all zeroes so there is no risk memcmp will report a
+ spurious difference in an uninitialized portion of the structure. */
+ struct termios new_mode = { 0, };
+
+ if (tcsetattr (STDIN_FILENO, TCSADRAIN, &mode))
+ error (EXIT_FAILURE, errno, "%s", device_name);
+
+ /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
+ it performs *any* of the requested operations. This means it
+ can report `success' when it has actually failed to perform
+ some proper subset of the requested operations. To detect
+ this partial failure, get the current terminal attributes and
+ compare them to the requested ones. */
+
+ if (tcgetattr (STDIN_FILENO, &new_mode))
+ error (EXIT_FAILURE, errno, "%s", device_name);
+
+ /* Normally, one shouldn't use memcmp to compare structures that
+ may have `holes' containing uninitialized data, but we have been
+ careful to initialize the storage of these two variables to all
+ zeroes. One might think it more efficient simply to compare the
+ modified fields, but that would require enumerating those fields --
+ and not all systems have the same fields in this structure. */
+
+ if (memcmp (&mode, &new_mode, sizeof (mode)) != 0)
+ {
+#ifdef CIBAUD
+ /* SunOS 4.1.3 (at least) has the problem that after this sequence,
+ tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
+ sometimes (m1 != m2). The only difference is in the four bits
+ of the c_cflag field corresponding to the baud rate. To save
+ Sun users a little confusion, don't report an error if this
+ happens. But suppress the error only if we haven't tried to
+ set the baud rate explicitly -- otherwise we'd never give an
+ error for a true failure to set the baud rate. */
+
+ new_mode.c_cflag &= (~CIBAUD);
+ if (speed_was_set || memcmp (&mode, &new_mode, sizeof (mode)) != 0)
+#endif
+ {
+ error (EXIT_FAILURE, 0,
+ _("%s: unable to perform all requested operations"),
+ device_name);
+#ifdef TESTING
+ {
+ size_t i;
+ printf (_("new_mode: mode\n"));
+ for (i = 0; i < sizeof (new_mode); i++)
+ printf ("0x%02x: 0x%02x\n",
+ *(((unsigned char *) &new_mode) + i),
+ *(((unsigned char *) &mode) + i));
+ }
+#endif
+ }
+ }
+ }
+
+ exit (EXIT_SUCCESS);
+}
+
+/* Return false if not applied because not reversible; otherwise
+ return true. */
+
+static bool
+set_mode (struct mode_info *info, bool reversed, struct termios *mode)
+{
+ tcflag_t *bitsp;
+
+ if (reversed && (info->flags & REV) == 0)
+ return false;
+
+ bitsp = mode_type_flag (info->type, mode);
+
+ if (bitsp == NULL)
+ {
+ /* Combination mode. */
+ if (STREQ (info->name, "evenp") || STREQ (info->name, "parity"))
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+ }
+ else if (STREQ (info->name, "oddp"))
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+ }
+ else if (STREQ (info->name, "nl"))
+ {
+ if (reversed)
+ {
+ mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
+ mode->c_oflag = (mode->c_oflag
+#ifdef ONLCR
+ | ONLCR
+#endif
+ )
+#ifdef OCRNL
+ & ~OCRNL
+#endif
+#ifdef ONLRET
+ & ~ONLRET
+#endif
+ ;
+ }
+ else
+ {
+ mode->c_iflag = mode->c_iflag & ~ICRNL;
+#ifdef ONLCR
+ mode->c_oflag = mode->c_oflag & ~ONLCR;
+#endif
+ }
+ }
+ else if (STREQ (info->name, "ek"))
+ {
+ mode->c_cc[VERASE] = CERASE;
+ mode->c_cc[VKILL] = CKILL;
+ }
+ else if (STREQ (info->name, "sane"))
+ sane_mode (mode);
+ else if (STREQ (info->name, "cbreak"))
+ {
+ if (reversed)
+ mode->c_lflag |= ICANON;
+ else
+ mode->c_lflag &= ~ICANON;
+ }
+ else if (STREQ (info->name, "pass8"))
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ }
+ }
+ else if (STREQ (info->name, "litout"))
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ mode->c_oflag |= OPOST;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ mode->c_oflag &= ~OPOST;
+ }
+ }
+ else if (STREQ (info->name, "raw") || STREQ (info->name, "cooked"))
+ {
+ if ((info->name[0] == 'r' && reversed)
+ || (info->name[0] == 'c' && !reversed))
+ {
+ /* Cooked mode. */
+ mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+ mode->c_oflag |= OPOST;
+ mode->c_lflag |= ISIG | ICANON;
+#if VMIN == VEOF
+ mode->c_cc[VEOF] = CEOF;
+#endif
+#if VTIME == VEOL
+ mode->c_cc[VEOL] = CEOL;
+#endif
+ }
+ else
+ {
+ /* Raw mode. */
+ mode->c_iflag = 0;
+ mode->c_oflag &= ~OPOST;
+ mode->c_lflag &= ~(ISIG | ICANON
+#ifdef XCASE
+ | XCASE
+#endif
+ );
+ mode->c_cc[VMIN] = 1;
+ mode->c_cc[VTIME] = 0;
+ }
+ }
+#ifdef IXANY
+ else if (STREQ (info->name, "decctlq"))
+ {
+ if (reversed)
+ mode->c_iflag |= IXANY;
+ else
+ mode->c_iflag &= ~IXANY;
+ }
+#endif
+#ifdef TABDLY
+ else if (STREQ (info->name, "tabs"))
+ {
+ if (reversed)
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+ else
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+ }
+#else
+# ifdef OXTABS
+ else if (STREQ (info->name, "tabs"))
+ {
+ if (reversed)
+ mode->c_oflag = mode->c_oflag | OXTABS;
+ else
+ mode->c_oflag = mode->c_oflag & ~OXTABS;
+ }
+# endif
+#endif
+#if defined XCASE && defined IUCLC && defined OLCUC
+ else if (STREQ (info->name, "lcase")
+ || STREQ (info->name, "LCASE"))
+ {
+ if (reversed)
+ {
+ mode->c_lflag &= ~XCASE;
+ mode->c_iflag &= ~IUCLC;
+ mode->c_oflag &= ~OLCUC;
+ }
+ else
+ {
+ mode->c_lflag |= XCASE;
+ mode->c_iflag |= IUCLC;
+ mode->c_oflag |= OLCUC;
+ }
+ }
+#endif
+ else if (STREQ (info->name, "crt"))
+ mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ ;
+ else if (STREQ (info->name, "dec"))
+ {
+ mode->c_cc[VINTR] = 3; /* ^C */
+ mode->c_cc[VERASE] = 127; /* DEL */
+ mode->c_cc[VKILL] = 21; /* ^U */
+ mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ ;
+#ifdef IXANY
+ mode->c_iflag &= ~IXANY;
+#endif
+ }
+ }
+ else if (reversed)
+ *bitsp = *bitsp & ~info->mask & ~info->bits;
+ else
+ *bitsp = (*bitsp & ~info->mask) | info->bits;
+
+ return true;
+}
+
+static void
+set_control_char (struct control_info *info, const char *arg,
+ struct termios *mode)
+{
+ unsigned long int value;
+
+ if (STREQ (info->name, "min") || STREQ (info->name, "time"))
+ value = integer_arg (arg, TYPE_MAXIMUM (cc_t));
+ else if (arg[0] == '\0' || arg[1] == '\0')
+ value = to_uchar (arg[0]);
+ else if (STREQ (arg, "^-") || STREQ (arg, "undef"))
+ value = _POSIX_VDISABLE;
+ else if (arg[0] == '^' && arg[1] != '\0') /* Ignore any trailing junk. */
+ {
+ if (arg[1] == '?')
+ value = 127;
+ else
+ value = to_uchar (arg[1]) & ~0140; /* Non-letters get weird results. */
+ }
+ else
+ value = integer_arg (arg, TYPE_MAXIMUM (cc_t));
+ mode->c_cc[info->offset] = value;
+}
+
+static void
+set_speed (enum speed_setting type, const char *arg, struct termios *mode)
+{
+ speed_t baud;
+
+ baud = string_to_baud (arg);
+ if (type == input_speed || type == both_speeds)
+ cfsetispeed (mode, baud);
+ if (type == output_speed || type == both_speeds)
+ cfsetospeed (mode, baud);
+}
+
+#ifdef TIOCGWINSZ
+
+static int
+get_win_size (int fd, struct winsize *win)
+{
+ int err = ioctl (fd, TIOCGWINSZ, (char *) win);
+ return err;
+}
+
+static void
+set_window_size (int rows, int cols, char const *device_name)
+{
+ struct winsize win;
+
+ if (get_win_size (STDIN_FILENO, &win))
+ {
+ if (errno != EINVAL)
+ error (EXIT_FAILURE, errno, "%s", device_name);
+ memset (&win, 0, sizeof (win));
+ }
+
+ if (rows >= 0)
+ win.ws_row = rows;
+ if (cols >= 0)
+ win.ws_col = cols;
+
+# ifdef TIOCSSIZE
+ /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
+ The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
+ This comment from sys/ttold.h describes Sun's twisted logic - a better
+ test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
+ At any rate, the problem is gone in Solaris 2.x.
+
+ Unfortunately, the old TIOCSSIZE code does collide with TIOCSWINSZ,
+ but they can be disambiguated by checking whether a "struct ttysize"
+ structure's "ts_lines" field is greater than 64K or not. If so,
+ it's almost certainly a "struct winsize" instead.
+
+ At any rate, the bug manifests itself when ws_row == 0; the symptom is
+ that ws_row is set to ws_col, and ws_col is set to (ws_xpixel<<16) +
+ ws_ypixel. Since GNU stty sets rows and columns separately, this bug
+ caused "stty rows 0 cols 0" to set rows to cols and cols to 0, while
+ "stty cols 0 rows 0" would do the right thing. On a little-endian
+ machine like the sun386i, the problem is the same, but for ws_col == 0.
+
+ The workaround is to do the ioctl once with row and col = 1 to set the
+ pixel info, and then do it again using a TIOCSSIZE to set rows/cols. */
+
+ if (win.ws_row == 0 || win.ws_col == 0)
+ {
+ struct ttysize ttysz;
+
+ ttysz.ts_lines = win.ws_row;
+ ttysz.ts_cols = win.ws_col;
+
+ win.ws_row = 1;
+ win.ws_col = 1;
+
+ if (ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win))
+ error (EXIT_FAILURE, errno, "%s", device_name);
+
+ if (ioctl (STDIN_FILENO, TIOCSSIZE, (char *) &ttysz))
+ error (EXIT_FAILURE, errno, "%s", device_name);
+ return;
+ }
+# endif
+
+ if (ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win))
+ error (EXIT_FAILURE, errno, "%s", device_name);
+}
+
+static void
+display_window_size (bool fancy, char const *device_name)
+{
+ struct winsize win;
+
+ if (get_win_size (STDIN_FILENO, &win))
+ {
+ if (errno != EINVAL)
+ error (EXIT_FAILURE, errno, "%s", device_name);
+ if (!fancy)
+ error (EXIT_FAILURE, 0,
+ _("%s: no size information for this device"), device_name);
+ }
+ else
+ {
+ wrapf (fancy ? "rows %d; columns %d;" : "%d %d\n",
+ win.ws_row, win.ws_col);
+ if (!fancy)
+ current_col = 0;
+ }
+}
+#endif
+
+static int
+screen_columns (void)
+{
+#ifdef TIOCGWINSZ
+ struct winsize win;
+
+ /* With Solaris 2.[123], this ioctl fails and errno is set to
+ EINVAL for telnet (but not rlogin) sessions.
+ On ISC 3.0, it fails for the console and the serial port
+ (but it works for ptys).
+ It can also fail on any system when stdout isn't a tty.
+ In case of any failure, just use the default. */
+ if (get_win_size (STDOUT_FILENO, &win) == 0 && 0 < win.ws_col)
+ return win.ws_col;
+#endif
+ {
+ /* Use $COLUMNS if it's in [1..INT_MAX]. */
+ char *col_string = getenv ("COLUMNS");
+ long int n_columns;
+ if (!(col_string != NULL
+ && xstrtol (col_string, NULL, 0, &n_columns, "") == LONGINT_OK
+ && 0 < n_columns
+ && n_columns <= INT_MAX))
+ n_columns = 80;
+ return n_columns;
+ }
+}
+
+static tcflag_t *
+mode_type_flag (enum mode_type type, struct termios *mode)
+{
+ switch (type)
+ {
+ case control:
+ return &mode->c_cflag;
+
+ case input:
+ return &mode->c_iflag;
+
+ case output:
+ return &mode->c_oflag;
+
+ case local:
+ return &mode->c_lflag;
+
+ case combination:
+ return NULL;
+
+ default:
+ abort ();
+ }
+}
+
+static void
+display_settings (enum output_type output_type, struct termios *mode,
+ char const *device_name)
+{
+ switch (output_type)
+ {
+ case changed:
+ display_changed (mode);
+ break;
+
+ case all:
+ display_all (mode, device_name);
+ break;
+
+ case recoverable:
+ display_recoverable (mode);
+ break;
+ }
+}
+
+static void
+display_changed (struct termios *mode)
+{
+ int i;
+ bool empty_line;
+ tcflag_t *bitsp;
+ unsigned long mask;
+ enum mode_type prev_type = control;
+
+ display_speed (mode, true);
+#ifdef HAVE_C_LINE
+ wrapf ("line = %d;", mode->c_line);
+#endif
+ putchar ('\n');
+ current_col = 0;
+
+ empty_line = true;
+ for (i = 0; !STREQ (control_info[i].name, "min"); ++i)
+ {
+ if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
+ continue;
+ /* If swtch is the same as susp, don't print both. */
+#if VSWTCH == VSUSP
+ if (STREQ (control_info[i].name, "swtch"))
+ continue;
+#endif
+ /* If eof uses the same slot as min, only print whichever applies. */
+#if VEOF == VMIN
+ if ((mode->c_lflag & ICANON) == 0
+ && (STREQ (control_info[i].name, "eof")
+ || STREQ (control_info[i].name, "eol")))
+ continue;
+#endif
+
+ empty_line = false;
+ wrapf ("%s = %s;", control_info[i].name,
+ visible (mode->c_cc[control_info[i].offset]));
+ }
+ if ((mode->c_lflag & ICANON) == 0)
+ {
+ wrapf ("min = %lu; time = %lu;\n",
+ (unsigned long int) mode->c_cc[VMIN],
+ (unsigned long int) mode->c_cc[VTIME]);
+ }
+ else if (!empty_line)
+ putchar ('\n');
+ current_col = 0;
+
+ empty_line = true;
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & OMIT)
+ continue;
+ if (mode_info[i].type != prev_type)
+ {
+ if (!empty_line)
+ {
+ putchar ('\n');
+ current_col = 0;
+ empty_line = true;
+ }
+ prev_type = mode_info[i].type;
+ }
+
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ if ((*bitsp & mask) == mode_info[i].bits)
+ {
+ if (mode_info[i].flags & SANE_UNSET)
+ {
+ wrapf ("%s", mode_info[i].name);
+ empty_line = false;
+ }
+ }
+ else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
+ {
+ wrapf ("-%s", mode_info[i].name);
+ empty_line = false;
+ }
+ }
+ if (!empty_line)
+ putchar ('\n');
+ current_col = 0;
+}
+
+static void
+display_all (struct termios *mode, char const *device_name)
+{
+ int i;
+ tcflag_t *bitsp;
+ unsigned long mask;
+ enum mode_type prev_type = control;
+
+ display_speed (mode, true);
+#ifdef TIOCGWINSZ
+ display_window_size (true, device_name);
+#endif
+#ifdef HAVE_C_LINE
+ wrapf ("line = %d;", mode->c_line);
+#endif
+ putchar ('\n');
+ current_col = 0;
+
+ for (i = 0; ! STREQ (control_info[i].name, "min"); ++i)
+ {
+ /* If swtch is the same as susp, don't print both. */
+#if VSWTCH == VSUSP
+ if (STREQ (control_info[i].name, "swtch"))
+ continue;
+#endif
+ /* If eof uses the same slot as min, only print whichever applies. */
+#if VEOF == VMIN
+ if ((mode->c_lflag & ICANON) == 0
+ && (STREQ (control_info[i].name, "eof")
+ || STREQ (control_info[i].name, "eol")))
+ continue;
+#endif
+ wrapf ("%s = %s;", control_info[i].name,
+ visible (mode->c_cc[control_info[i].offset]));
+ }
+#if VEOF == VMIN
+ if ((mode->c_lflag & ICANON) == 0)
+#endif
+ wrapf ("min = %lu; time = %lu;",
+ (unsigned long int) mode->c_cc[VMIN],
+ (unsigned long int) mode->c_cc[VTIME]);
+ if (current_col != 0)
+ putchar ('\n');
+ current_col = 0;
+
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & OMIT)
+ continue;
+ if (mode_info[i].type != prev_type)
+ {
+ putchar ('\n');
+ current_col = 0;
+ prev_type = mode_info[i].type;
+ }
+
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ if ((*bitsp & mask) == mode_info[i].bits)
+ wrapf ("%s", mode_info[i].name);
+ else if (mode_info[i].flags & REV)
+ wrapf ("-%s", mode_info[i].name);
+ }
+ putchar ('\n');
+ current_col = 0;
+}
+
+static void
+display_speed (struct termios *mode, bool fancy)
+{
+ if (cfgetispeed (mode) == 0 || cfgetispeed (mode) == cfgetospeed (mode))
+ wrapf (fancy ? "speed %lu baud;" : "%lu\n",
+ baud_to_value (cfgetospeed (mode)));
+ else
+ wrapf (fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
+ baud_to_value (cfgetispeed (mode)),
+ baud_to_value (cfgetospeed (mode)));
+ if (!fancy)
+ current_col = 0;
+}
+
+static void
+display_recoverable (struct termios *mode)
+{
+ size_t i;
+
+ printf ("%lx:%lx:%lx:%lx",
+ (unsigned long int) mode->c_iflag,
+ (unsigned long int) mode->c_oflag,
+ (unsigned long int) mode->c_cflag,
+ (unsigned long int) mode->c_lflag);
+ for (i = 0; i < NCCS; ++i)
+ printf (":%lx", (unsigned long int) mode->c_cc[i]);
+ putchar ('\n');
+}
+
+static bool
+recover_mode (char const *arg, struct termios *mode)
+{
+ size_t i;
+ int n;
+ unsigned long int chr;
+ unsigned long int iflag, oflag, cflag, lflag;
+
+ /* Scan into temporaries since it is too much trouble to figure out
+ the right format for `tcflag_t'. */
+ if (sscanf (arg, "%lx:%lx:%lx:%lx%n",
+ &iflag, &oflag, &cflag, &lflag, &n) != 4)
+ return false;
+ mode->c_iflag = iflag;
+ mode->c_oflag = oflag;
+ mode->c_cflag = cflag;
+ mode->c_lflag = lflag;
+ if (mode->c_iflag != iflag
+ || mode->c_oflag != oflag
+ || mode->c_cflag != cflag
+ || mode->c_lflag != lflag)
+ return false;
+ arg += n;
+ for (i = 0; i < NCCS; ++i)
+ {
+ if (sscanf (arg, ":%lx%n", &chr, &n) != 1)
+ return false;
+ mode->c_cc[i] = chr;
+ if (mode->c_cc[i] != chr)
+ return false;
+ arg += n;
+ }
+
+ /* Fail if there are too many fields. */
+ if (*arg != '\0')
+ return false;
+
+ return true;
+}
+
+struct speed_map
+{
+ const char *string; /* ASCII representation. */
+ speed_t speed; /* Internal form. */
+ unsigned long int value; /* Numeric value. */
+};
+
+static struct speed_map speeds[] =
+{
+ {"0", B0, 0},
+ {"50", B50, 50},
+ {"75", B75, 75},
+ {"110", B110, 110},
+ {"134", B134, 134},
+ {"134.5", B134, 134},
+ {"150", B150, 150},
+ {"200", B200, 200},
+ {"300", B300, 300},
+ {"600", B600, 600},
+ {"1200", B1200, 1200},
+ {"1800", B1800, 1800},
+ {"2400", B2400, 2400},
+ {"4800", B4800, 4800},
+ {"9600", B9600, 9600},
+ {"19200", B19200, 19200},
+ {"38400", B38400, 38400},
+ {"exta", B19200, 19200},
+ {"extb", B38400, 38400},
+#ifdef B57600
+ {"57600", B57600, 57600},
+#endif
+#ifdef B115200
+ {"115200", B115200, 115200},
+#endif
+#ifdef B230400
+ {"230400", B230400, 230400},
+#endif
+#ifdef B460800
+ {"460800", B460800, 460800},
+#endif
+#ifdef B500000
+ {"500000", B500000, 500000},
+#endif
+#ifdef B576000
+ {"576000", B576000, 576000},
+#endif
+#ifdef B921600
+ {"921600", B921600, 921600},
+#endif
+#ifdef B1000000
+ {"1000000", B1000000, 1000000},
+#endif
+#ifdef B1152000
+ {"1152000", B1152000, 1152000},
+#endif
+#ifdef B1500000
+ {"1500000", B1500000, 1500000},
+#endif
+#ifdef B2000000
+ {"2000000", B2000000, 2000000},
+#endif
+#ifdef B2500000
+ {"2500000", B2500000, 2500000},
+#endif
+#ifdef B3000000
+ {"3000000", B3000000, 3000000},
+#endif
+#ifdef B3500000
+ {"3500000", B3500000, 3500000},
+#endif
+#ifdef B4000000
+ {"4000000", B4000000, 4000000},
+#endif
+ {NULL, 0, 0}
+};
+
+static speed_t
+string_to_baud (const char *arg)
+{
+ int i;
+
+ for (i = 0; speeds[i].string != NULL; ++i)
+ if (STREQ (arg, speeds[i].string))
+ return speeds[i].speed;
+ return (speed_t) -1;
+}
+
+static unsigned long int
+baud_to_value (speed_t speed)
+{
+ int i;
+
+ for (i = 0; speeds[i].string != NULL; ++i)
+ if (speed == speeds[i].speed)
+ return speeds[i].value;
+ return 0;
+}
+
+static void
+sane_mode (struct termios *mode)
+{
+ int i;
+ tcflag_t *bitsp;
+
+ for (i = 0; control_info[i].name; ++i)
+ {
+#if VMIN == VEOF
+ if (STREQ (control_info[i].name, "min"))
+ break;
+#endif
+ mode->c_cc[control_info[i].offset] = control_info[i].saneval;
+ }
+
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & SANE_SET)
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
+ }
+ else if (mode_info[i].flags & SANE_UNSET)
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
+ }
+ }
+}
+
+/* Return a string that is the printable representation of character CH. */
+/* Adapted from `cat' by Torbjorn Granlund. */
+
+static const char *
+visible (cc_t ch)
+{
+ static char buf[10];
+ char *bpout = buf;
+
+ if (ch == _POSIX_VDISABLE)
+ return "<undef>";
+
+ if (ch >= 32)
+ {
+ if (ch < 127)
+ *bpout++ = ch;
+ else if (ch == 127)
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ else
+ {
+ *bpout++ = 'M',
+ *bpout++ = '-';
+ if (ch >= 128 + 32)
+ {
+ if (ch < 128 + 127)
+ *bpout++ = ch - 128;
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch - 128 + 64;
+ }
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch + 64;
+ }
+ *bpout = '\0';
+ return (const char *) buf;
+}
+
+/* Parse string S as an integer, using decimal radix by default,
+ but allowing octal and hex numbers as in C. Reject values
+ larger than MAXVAL. */
+
+static unsigned long int
+integer_arg (const char *s, unsigned long int maxval)
+{
+ unsigned long int value;
+ if (xstrtoul (s, NULL, 0, &value, "bB") != LONGINT_OK
+ || maxval < value)
+ {
+ error (0, 0, _("invalid integer argument %s"), quote (s));
+ usage (EXIT_FAILURE);
+ }
+ return value;
+}