Signed-off-by: Mattias Andrée <[email protected]>
---
 Makefile |   1 +
 TODO     |   1 +
 stty.c   | 762 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 764 insertions(+)
 create mode 100644 stty.c

diff --git a/Makefile b/Makefile
index 59616a4..dc85510 100644
--- a/Makefile
+++ b/Makefile
@@ -74,6 +74,7 @@ BIN = \
        respawn           \
        rmmod             \
        stat              \
+       stty              \
        su                \
        swaplabel         \
        swapoff           \
diff --git a/TODO b/TODO
index 3e89e68..3f273b2 100644
--- a/TODO
+++ b/TODO
@@ -24,6 +24,7 @@ rfkill
 rmgroup
 rmuser
 setcap
+stty manpage
 tabs
 taskset
 top
diff --git a/stty.c b/stty.c
new file mode 100644
index 0000000..4f38256
--- /dev/null
+++ b/stty.c
@@ -0,0 +1,762 @@
+/* See LICENSE file for copyright and license details. */
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "util.h"
+
+/*
+ * Petty POSIX violations:
+ * 
+ * -  XBD 12.2 is not honoured precisely. This is for
+ *    convenience and compatibility with other implementations.
+ */
+
+#define CC_MAX  255
+
+static int output_size_requested = 0;
+static int output_speed_requested = 0;
+static int drain_requested = 1;
+
+static void sane(int, struct termios *);
+static void setwinsize(long, long);
+static void ispeed(char *, struct termios *);
+static void ospeed(char *, struct termios *);
+
+static void
+raw(int unset, struct termios *m)
+{
+       if (!unset) {
+               m->c_iflag = 0;
+               m->c_lflag &= ~XCASE;
+               m->c_cc[VMIN] = 1;
+               m->c_cc[VTIME] = 0;
+       } else {
+               m->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+       }
+}
+
+static void
+evenp(int unset, struct termios *m)
+{
+       m->c_oflag &= ~CSIZE;
+       m->c_oflag &= ~(unset ? PARENB : PARODD);
+       m->c_oflag |= unset ? CS8 : (CS7 | PARENB);
+}
+
+
+static void
+dec(int unset, struct termios *m)
+{
+       m->c_cc[VINTR] = CINTR;
+       m->c_cc[VKILL] = CKILL;
+       m->c_cc[VERASE] = CERASE;
+       (void) unset;
+}
+
+static void
+ek(int unset, struct termios *m)
+{
+       m->c_cc[VKILL] = CKILL;
+       m->c_cc[VERASE] = CERASE;
+       (void) unset;
+}
+
+static void
+nl(int unset, struct termios *m)
+{
+       if (unset) {
+               m->c_iflag &= ~(INLCR | IGNCR);
+               m->c_oflag &= ~(OCRNL | ONLRET);
+       }
+}
+
+static void
+oddp(int unset, struct termios *m)
+{
+       m->c_oflag &= ~CSIZE;
+       m->c_oflag &= ~(unset ? PARENB : 0);
+       m->c_oflag |= unset ? CS8 : (CS7 | PARODD | PARENB);
+}
+
+static void drain(int unset, struct termios *m)  { drain_requested = !unset; 
(void) m; }
+static void cooked(int unset, struct termios *m) { raw(!unset, m); }
+static void pass8(int unset, struct termios *m)  { m->c_cflag &= ~CSIZE, 
m->c_cflag |= unset ? CS7 : CS8; }
+static void size(int unset, struct termios *m)   { output_size_requested = 1; 
(void) m; (void) unset; }
+static void speed(int unset, struct termios *m)  { output_speed_requested = 1; 
(void) m; (void) unset; }
+static void tabs(int unset, struct termios *m)   { m->c_oflag &= ~TABDLY, 
m->c_oflag |= unset ? TAB3 : TAB0; }
+static void cols(char *arg, struct termios *m)   { setwinsize(-1, 
estrtonum(arg, 0, USHRT_MAX)); (void) m; }
+static void line(char *arg, struct termios *m)   { m->c_line = estrtonum(arg, 
0, 255); }
+static void min(char *arg, struct termios *m)    { m->c_cc[VMIN] = 
estrtonum(arg, 0, CC_MAX); }
+static void rows(char *arg, struct termios *m)   { setwinsize(estrtonum(arg, 
0, USHRT_MAX), -1); (void) m; }
+static void stime(char *arg, struct termios *m)  { m->c_cc[VTIME] = 
estrtonum(arg, 0, CC_MAX); }
+
+enum type { CTRL, IN, OUT, LOCAL, COMB, SPEC };
+enum {
+       BOOL = 1,
+       DUP = 2,
+       SANE = 4,
+       INSANE = 8,
+       CBREAK = 16,
+       DECCTLQ = 32,
+       LCASE = 64,
+       PASS8 = 128,
+       LITOUT = 256,
+       CRT = 1024,
+       DEC = 2048,
+       NL = 4096,
+       COOKED = 8192
+};
+
+struct mode {
+       const char *op;
+       enum type type;
+       tcflag_t set;
+       tcflag_t clear;
+       void (*fun)(int, struct termios *);
+       int flags;
+};
+
+struct key {
+       const char *op;
+       size_t index;
+       cc_t sanevalue;
+};
+
+struct intvalued {
+       const char *op;
+       void (*fun)(char *, struct termios *);
+};
+
+struct speed {
+       const char *str;
+       speed_t speed;
+};
+
+static const struct mode modes[] = {
+       {"clocal",   CTRL,  CLOCAL,  0,       0,      BOOL},
+       {"cmspar",   CTRL,  CMSPAR,  0,       0,      BOOL},
+       {"cread",    CTRL,  CREAD,   0,       0,      BOOL | SANE},
+       {"crtscts",  CTRL,  CRTSCTS, 0,       0,      BOOL},
+       {"cs5",      CTRL,  CS5,     CSIZE,   0,      0},
+       {"cs6",      CTRL,  CS6,     CSIZE,   0,      0},
+       {"cs7",      CTRL,  CS7,     CSIZE,   0,      0},
+       {"cs8",      CTRL,  CS8,     CSIZE,   0,      0},
+       {"cstopb",   CTRL,  CSTOPB,  0,       0,      BOOL},
+       {"hup",      CTRL,  HUPCL,   0,       0,      BOOL | DUP},
+       {"hupcl",    CTRL,  HUPCL,   0,       0,      BOOL},
+       {"parenb",   CTRL,  PARENB,  0,       0,      BOOL | PASS8 | LITOUT},
+       {"parodd",   CTRL,  PARODD,  0,       0,      BOOL},
+
+       {"brkint",   IN,    BRKINT,  0,       0,      BOOL | SANE},
+       {"icrnl",    IN,    ICRNL,   0,       0,      BOOL | SANE | NL},
+       {"ignbrk",   IN,    IGNBRK,  0,       0,      BOOL | INSANE},
+       {"igncr",    IN,    IGNCR,   0,       0,      BOOL | INSANE},
+       {"ignpar",   IN,    IGNPAR,  0,       0,      BOOL},
+       {"imaxbel",  IN,    IMAXBEL, 0,       0,      BOOL | SANE},
+       {"inlcr",    IN,    INLCR,   0,       0,      BOOL | INSANE},
+       {"inpck",    IN,    INPCK,   0,       0,      BOOL},
+       {"istrip",   IN,    ISTRIP,  0,       0,      BOOL | PASS8 | LITOUT},
+       {"iuclc",    IN,    IUCLC,   0,       0,      BOOL | INSANE | LCASE},
+       {"iutf8",    IN,    IUTF8,   0,       0,      BOOL | SANE},
+       {"ixany",    IN,    IXANY,   0,       0,      BOOL | INSANE | DECCTLQ},
+       {"ixoff",    IN,    IXOFF,   0,       0,      BOOL | INSANE},
+       {"ixon",     IN,    IXON,    0,       0,      BOOL},
+       {"parmrk",   IN,    PARMRK,  0,       0,      BOOL},
+       {"tandem",   IN,    IXOFF,   0,       0,      BOOL | DUP},
+
+       {"bs0",      OUT,   BS0,     BSDLY,   0,      SANE},
+       {"bs1",      OUT,   BS1,     BSDLY,   0,      INSANE},
+       {"cr0",      OUT,   CR0,     CRDLY,   0,      SANE},
+       {"cr1",      OUT,   CR1,     CRDLY,   0,      INSANE},
+       {"cr2",      OUT,   CR2,     CRDLY,   0,      INSANE},
+       {"cr3",      OUT,   CR3,     CRDLY,   0,      INSANE},
+       {"ff0",      OUT,   FF0,     FFDLY,   0,      SANE},
+       {"ff1",      OUT,   FF1,     FFDLY,   0,      INSANE},
+       {"nl0",      OUT,   NL0,     NLDLY,   0,      SANE},
+       {"nl1",      OUT,   NL1,     NLDLY,   0,      INSANE},
+       {"ocrnl",    OUT,   OCRNL,   0,       0,      BOOL | INSANE},
+       {"ofdel",    OUT,   OFDEL,   0,       0,      BOOL | INSANE},
+       {"ofill",    OUT,   OFILL,   0,       0,      BOOL | INSANE},
+       {"olcuc",    OUT,   OLCUC,   0,       0,      BOOL | INSANE | LCASE},
+       {"onlcr",    OUT,   ONLCR,   0,       0,      BOOL | SANE | NL},
+       {"onlret",   OUT,   ONLRET,  0,       0,      BOOL | INSANE},
+       {"onocr",    OUT,   ONOCR,   0,       0,      BOOL | INSANE},
+       {"opost",    OUT,   OPOST,   0,       0,      BOOL | SANE | LITOUT | 
COOKED},
+       {"tab0",     OUT,   TAB0,    TABDLY,  0,      SANE},
+       {"tab1",     OUT,   TAB1,    TABDLY,  0,      INSANE},
+       {"tab2",     OUT,   TAB2,    TABDLY,  0,      INSANE},
+       {"tab3",     OUT,   TAB3,    TABDLY,  0,      INSANE},
+       {"vt0",      OUT,   VT0,     VTDLY,   0,      SANE},
+       {"vt1",      OUT,   VT1,     VTDLY,   0,      INSANE},
+
+       {"crterase", LOCAL, ECHOE,   0,       0,      BOOL | DUP},
+       {"crtkill",  LOCAL, ECHOKE,  0,       0,      BOOL | DUP},
+       {"ctlecho",  LOCAL, ECHOCTL, 0,       0,      BOOL | DUP},
+       {"echo",     LOCAL, ECHO,    0,       0,      BOOL | SANE},
+       {"echoctl",  LOCAL, ECHOCTL, 0,       0,      BOOL | SANE | CRT | DEC},
+       {"echoe",    LOCAL, ECHOE,   0,       0,      BOOL | SANE | CRT | DEC},
+       {"echok",    LOCAL, ECHOK,   0,       0,      BOOL | SANE},
+       {"echoke",   LOCAL, ECHOKE,  0,       0,      BOOL | SANE | CRT | DEC},
+       {"echonl",   LOCAL, ECHONL,  0,       0,      BOOL | INSANE},
+       {"echoprt",  LOCAL, ECHOPRT, 0,       0,      BOOL | INSANE},
+       {"extproc",  LOCAL, EXTPROC, 0,       0,      BOOL | INSANE},
+       {"flusho",   LOCAL, FLUSHO,  0,       0,      BOOL | INSANE},
+       {"icanon",   LOCAL, ICANON,  0,       0,      BOOL | SANE | CBREAK | 
COOKED},
+       {"iexten",   LOCAL, IEXTEN,  0,       0,      BOOL | SANE},
+       {"isig",     LOCAL, ISIG,    0,       0,      BOOL | SANE | COOKED},
+       {"noflsh",   LOCAL, NOFLSH,  0,       0,      BOOL | INSANE},
+       {"prterase", LOCAL, ECHOPRT, 0,       0,      BOOL | DUP},
+       {"tostop",   LOCAL, TOSTOP,  0,       0,      BOOL | INSANE},
+       {"xcase",    LOCAL, XCASE,   0,       0,      BOOL | INSANE | LCASE},
+
+       {"cbreak",   COMB,  0,       CBREAK,  0,      BOOL | DUP},
+       {"cooked",   COMB,  COOKED,  0,       cooked, BOOL | DUP},
+       {"crt",      COMB,  CRT,     0,       0,      DUP},
+       {"dec",      COMB,  DEC,     DECCTLQ, dec,    DUP},
+       {"decctlq",  COMB,  0,       DECCTLQ, 0,      BOOL | DUP},
+       {"ek",       COMB,  0,       0,       ek,     DUP},
+       {"evenp",    COMB,  0,       0,       evenp,  BOOL | DUP},
+       {"LCASE",    COMB,  LCASE,   0,       0,      BOOL | DUP},
+       {"lcase",    COMB,  LCASE,   0,       0,      BOOL | DUP},
+       {"litout",   COMB,  0,       LITOUT,  pass8,  BOOL | DUP},
+       {"nl",       COMB,  0,       NL,      nl,     BOOL | DUP},
+       {"oddp",     COMB,  0,       0,       oddp,   BOOL | DUP},
+       {"parity",   COMB,  0,       0,       evenp,  BOOL | DUP},
+       {"pass8",    COMB,  0,       PASS8,   pass8,  BOOL | DUP},
+       {"raw",      COMB,  0,       COOKED,  raw,    BOOL | DUP},
+       {"sane",     COMB,  SANE,    INSANE,  sane,   DUP},
+       {"tabs",     COMB,  0,       0,       tabs,   BOOL | DUP},
+
+       {"size",     SPEC,  0,       0,       size,   DUP},
+       {"speed",    SPEC,  0,       0,       speed,  DUP},
+       {"drain",    SPEC,  0,       0,       drain,  BOOL | DUP},
+
+       {0, 0, 0, 0, 0, 0}
+};
+
+static const struct key keys[] = {
+       {"discard", VDISCARD, CDISCARD},
+       {"eof",     VEOF,     CEOF},
+       {"eol",     VEOL,     CEOL},
+       {"eol2",    VEOL2,    _POSIX_VDISABLE},
+       {"erase",   VERASE,   CERASE},
+       {"intr",    VINTR,    CINTR},
+       {"kill",    VKILL,    CKILL},
+       {"lnext",   VLNEXT,   CLNEXT},
+       {"quit",    VQUIT,    CQUIT},
+       {"rprnt",   VREPRINT, CRPRNT},
+       {"start",   VSTART,   CSTART},
+       {"stop",    VSTOP,    CSTOP},
+       {"susp",    VSUSP,    CSUSP},
+       {"swtch",   VSWTC,    _POSIX_VDISABLE},
+       {"werase",  VWERASE,  CWERASE},
+       {0, 0, 0}
+};
+
+static const struct intvalued ints[] = {
+       {"cols",    cols},
+       {"columns", cols},
+       {"line",    line},
+       {"min",     min},
+       {"rows",    rows},
+       {"time",    stime},
+       {"ispeed",  ispeed},
+       {"ospeed",  ospeed},
+       {0, 0}
+};
+
+#define B(baud) {#baud, B##baud}
+static const struct speed speeds[] = {
+       B(0),       B(50),      B(75),      B(110),     B(134),     B(150),     
B(200),     B(300),
+        B(600),     B(1200),    B(1800),    B(2400),    B(4800),    B(9600),   
 B(19200),   B(38400),
+        B(57600),   B(115200),  B(230400),  B(460800),  B(500000),  B(576000), 
 B(921600),  B(1000000),
+        B(1152000), B(1500000), B(2000000), B(2500000), B(3000000), 
B(3500000), B(4000000),
+       {"134.5", B134},
+       {"exta",  B19200},
+       {"extb",  B38400},
+       {0, 0}
+};
+#undef B
+
+static void
+sane(int unset, struct termios *m)
+{
+       const struct key *op = keys;
+       for (; op->op; op++)
+               m->c_cc[op->index] = op->sanevalue;
+       m->c_cc[VMIN] = 1;
+       m->c_cc[VTIME] = 0;
+       (void) unset;
+}
+
+static int
+isxnumber(char* str)
+{
+       if (!*str)
+               return 0;
+       for (; *str; str++)
+               if (!isxdigit(*str))
+                       return 0;
+       return 1;
+}
+
+static void
+decodehex(char *dest, char* src)
+{
+       while (*src) {
+               char hi = *src++;
+               char lo = *src++;
+               hi = (hi & 15) + 9 * !isdigit(hi);
+               lo = (lo & 15) + 9 * !isdigit(lo);
+               *dest++ = (hi << 4) | lo;
+       }
+}
+
+static void
+setwinsize(long y, long x)
+{
+       struct winsize winsize;
+       if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize))
+               eprintf("TIOCGWINSZ <stdin>:");
+       if (y >= 0)
+               winsize.ws_row = y;
+       if (x >= 0)
+               winsize.ws_col = x;
+       if (ioctl(STDIN_FILENO, TIOCSWINSZ, &winsize))
+               eprintf("TIOCSWINSZ <stdin>:");
+}
+
+static void
+setoperand_mode(int unset, const struct mode *op, struct termios *mode)
+{
+       tcflag_t *bitsp = 0;
+
+       switch (op->type) {
+       case CTRL:  bitsp = &mode->c_cflag; break;
+       case IN:    bitsp = &mode->c_iflag; break;
+       case OUT:   bitsp = &mode->c_oflag; break;
+       case LOCAL: bitsp = &mode->c_lflag; break;
+       case SPEC:  break;
+       default:    abort();
+       }
+
+       if (bitsp) {
+               *bitsp &= ~op->clear;
+               if (!unset)
+                       *bitsp |= op->set;
+               else
+                       *bitsp &= ~op->set;
+       }
+
+       if (op->fun)
+               op->fun(unset, mode);
+}
+
+static int
+parseoperand_mode(char *arg, struct termios *mode)
+{
+       const struct mode *op = modes;
+       const struct mode *op_proper;
+       int unset = *arg == '-';
+       int flags_set, flags_unset;
+
+       arg += unset;
+       while (op->op && strcmp(arg, op->op))
+               op++;
+       if (!op->op)
+               return -1;
+       if (unset && !(op->flags & BOOL))
+               return -1;
+
+       switch (op->type) {
+        case CTRL:
+        case IN:
+        case OUT:
+        case LOCAL:
+        case SPEC:
+               setoperand_mode(unset, op, mode);
+               return 0;
+        case COMB:
+               break;
+        default:
+               abort();
+       }
+
+       flags_set = (int)(op->set);
+       flags_unset = (int)(op->clear);
+       op_proper = op;
+
+       if (flags_unset || flags_set) {
+               for (op = modes; op->op; op++) {
+                       if (op->type == COMB)
+                               continue;
+                       if (flags_unset && (op->flags & flags_unset))
+                               setoperand_mode(!unset, op, mode);
+                       if (flags_set && (op->flags & flags_set))
+                               setoperand_mode(unset, op, mode);
+               }
+       }
+
+       if (op_proper->fun)
+               op_proper->fun(unset, mode);
+
+       return 0;
+}
+
+static long long
+estrtonum_radix(const char *numstr, long long minval, long long maxval, int 
radix)
+{
+       long long ll = 0;
+       char *ep;
+       errno = 0;
+       ll = strtoll(numstr, &ep, radix);
+       if (numstr == ep || *ep != '\0')
+               eprintf("estrtonum_radix %s: invalid\n", numstr);
+       else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+               eprintf("estrtonum_radix %s: too small\n", numstr);
+       else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+               eprintf("estrtonum_radix %s: too large\n", numstr);
+       return ll;
+}
+
+static int
+parseoperand_key(char *arg0, char *arg1, struct termios *mode)
+{
+       const struct key *op = keys;
+       cc_t value;
+
+       while (op->op && strcmp(arg0, op->op))
+               op++;
+       if (!op->op)
+               return -1;
+
+       if (!arg1)
+               eprintf("missing argument for operand: %s\n", arg0);
+
+       if (!strcmp(arg1, "^-") || !strcmp(arg1, "undef"))
+               value = _POSIX_VDISABLE;
+       else if (!strcmp(arg1, "^?"))
+               value = 127;
+       else if (!arg1[0] || !arg1[1])
+               value = arg1[0];
+       else if (arg1[0] == '^')
+               value = (cc_t)(arg1[1]) & ~0x60;
+       else if (strstr(arg1, "0x") == arg1)
+               value = estrtonum_radix(arg1 + 2, 0, CC_MAX, 16);
+       else if (arg1[0] == '0' && arg1[1])
+               value = estrtonum_radix(arg1 + 1, 0, CC_MAX, 8);
+       else
+               value = estrtonum_radix(arg1 + 0, 0, CC_MAX, 10);
+
+       mode->c_cc[op->index] = value;
+       return 0;
+}
+
+static int
+parseoperand_int(char *arg0, char *arg1, struct termios *mode)
+{
+       const struct intvalued *op = ints;
+
+       while (op->op && strcmp(arg0, op->op))
+               op++;
+       if (!op->op)
+               return -1;
+
+       if (!arg1)
+               eprintf("missing argument for operand: %s\n", arg0);
+
+       op->fun(arg1, mode);
+       return 0;
+}
+
+static const char *
+baudtostr(speed_t baud)
+{
+       const struct speed *speed = speeds;
+       while (speed->str && speed->speed != baud)
+               speed++;
+       return speed->str ? speed->str : "0";
+}
+
+static int
+parsespeed(char *arg, struct speed *ret)
+{
+       const struct speed *speed = speeds;
+       while (speed->str && strcmp(arg, speed->str))
+               speed++;
+       if (!speed->str)
+               return -1;
+       *ret = *speed;
+       return 0;
+}
+
+static void
+eparsespeed(char *arg, struct speed *ret)
+{
+       if (parsespeed(arg, ret))
+               eprintf("invalid speed parameter: %s\n", arg);
+}
+
+static void
+ispeed(char *arg, struct termios *m)
+{
+       struct speed speed;
+       eparsespeed(arg, &speed);
+       if (cfsetispeed(m, speed.speed))
+               eprintf("cfsetispeed %s:", speed.str);
+}
+
+static void
+ospeed(char *arg, struct termios *m)
+{
+       struct speed speed;
+       eparsespeed(arg, &speed);
+       if (cfsetospeed(m, speed.speed))
+               eprintf("cfsetospeed %s:", speed.str);
+}
+
+static void
+printtoken(const char *fmt, ...)
+{
+       static size_t width = 0;
+       static size_t pos = 0;
+       static char buf[BUFSIZ];
+       va_list ap;
+       int len;
+
+       if (!width) {
+               struct winsize winsize;
+               if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize))
+                       if (winsize.ws_col > 40)
+                               width = winsize.ws_col;
+               if (!width)
+                       width = SIZE_MAX;
+       }
+
+       if (!strcmp(fmt, "\n")) {
+               if (pos)
+                       printf("\n");
+               pos = 0;
+               return;
+       }
+
+       va_start(ap, fmt);
+       len = vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+       if (len < 0 || (size_t)len >= sizeof(buf))
+               eprintf("vsnprintf:");
+
+       if (pos + !!pos + len > width) {
+               printf("\n");
+               pos = 0;
+       } else if (pos) {
+               printf(" ");
+               pos++;
+       }
+
+       printf("%s", buf);
+       pos += len;
+}
+
+static const char*
+keytostr(cc_t key)
+{
+       static char buf[5];
+       int r;
+       if (key == _POSIX_VDISABLE)
+               return "undef";
+       else if (key < (cc_t)' ')
+               r = snprintf(buf, sizeof(buf), "^%c", key + '@');
+       else if (key < 127)
+               r = snprintf(buf, sizeof(buf), "%c", key);
+       else if (key == 127)
+               r = snprintf(buf, sizeof(buf), "^?");
+       else if (key < 128 + ' ')
+               r = snprintf(buf, sizeof(buf), "M-^%c", key - 128 + '@');
+       else if (key == 128 + 127)
+               r = snprintf(buf, sizeof(buf), "M-^?");
+       else
+               r = snprintf(buf, sizeof(buf), "M-%c", key - 128);
+       if (r < 0 || (size_t)r >= sizeof(buf))
+               eprintf("snprintf:");
+       return buf;
+}
+
+static void
+displaysettings(struct termios *m, int all)
+{
+       const struct key *kbd = keys;
+       const struct mode *mod = modes;
+       struct winsize winsize;
+       speed_t in, out;
+       tcflag_t *bitsp, mask;
+
+       in = cfgetispeed(m);
+       out = cfgetospeed(m);
+       if (!in || in == out) {
+               if (all || out != B38400)
+                       printtoken("speed %s baud;", baudtostr(out));
+       } else {
+               printtoken("ispeed %s baud;", baudtostr(in));
+               printtoken("ospeed %s baud;", baudtostr(out));
+       }
+
+       if (all) {
+               if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize))
+                       eprintf("TIOCGWINSZ <stdin>:");
+               printtoken("rows %u;", winsize.ws_row);
+               printtoken("columns %u;", winsize.ws_col);
+       }
+       printtoken("\n");
+
+       if (all || m->c_line != 0)
+               printtoken("line = %u;", (unsigned long)(m->c_line));
+       if (all || (m->c_cc[VMIN] != 1 && !(m->c_lflag & ICANON)))
+               printtoken("min = %u;", (unsigned long)(m->c_cc[VMIN]));
+       if (all || (m->c_cc[VTIME] != 0 && !(m->c_lflag & ICANON)))
+               printtoken("time = %u;", (unsigned long)(m->c_cc[VTIME]));
+       printtoken("\n");
+
+       for (; kbd->op; kbd++)
+               if (all || m->c_cc[kbd->index] != kbd->sanevalue)
+                       printtoken("%s = %s;", kbd->op, 
keytostr(m->c_cc[kbd->index]));
+       printtoken("\n");
+
+       for (; mod->op; mod++) {
+               switch (mod->type) {
+               case CTRL:  bitsp = &m->c_cflag; break;
+               case IN:    bitsp = &m->c_iflag; break;
+               case OUT:   bitsp = &m->c_oflag; break;
+               case LOCAL: bitsp = &m->c_lflag; break;
+               default:    bitsp = 0;           break;
+               }
+               if (!bitsp || (mod->flags & DUP))
+                       continue;
+               mask = mod->clear ? mod->clear : mod->set;
+               if ((*bitsp & mask) == mod->set) {
+                       if (all || (mod->flags & INSANE) || !(mod->flags & 
SANE))
+                               printtoken("%s", mod->op);
+               }
+               else if (mod->flags & BOOL) {
+                       if (all || (mod->flags & SANE) || !(mod->flags & 
INSANE))
+                               printtoken("-%s", mod->op);
+               }
+       }
+       printtoken("\n");
+}
+
+static void
+usage(void)
+{
+       eprintf("usage: %s [-a | -g] [operand ...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct termios mode;
+       struct termios mode2;
+       struct winsize winsize;
+       struct speed speed;
+       int aflag = 0;
+       int gflag = 0;
+       size_t n;
+       unsigned char *buf;
+       char *p;
+       speed_t in, out;
+
+       for (argv0 = *argv++, argc--; argc; argv++, argc--) {
+               if (!strcmp(*argv, "-ag") || !strcmp(*argv, "-ga")) {
+                       aflag = gflag = 1;
+               } else if (!strcmp(*argv, "-g")) {
+                       gflag = 1;
+               } else if (!strcmp(*argv, "-a")) {
+                       aflag = 1;
+               } else if (!strcmp(*argv, "--")) {
+                       argv++, argc--;
+                       break;
+               } else {
+                       break;
+               }
+       }
+
+       if (aflag && gflag)
+               usage();
+
+       memset(&mode, 0, sizeof(mode));
+       if (tcgetattr(STDIN_FILENO, &mode))
+               eprintf("tcgetattr <stdin>:");
+       memcpy(&mode2, &mode, sizeof(mode));
+
+       for (; *argv; argv++) {
+               if (**argv == '=') {
+                       p = *argv + 1;
+                       if (strlen(p) != sizeof(mode) * 2 || !isxnumber(p))
+                               goto invalid;
+                       decodehex((char *)&mode, p);
+               } else if (!parseoperand_mode(*argv, &mode)) {
+                       /* do nothing. */
+               } else if (!parseoperand_key(argv[0], argv[1], &mode)) {
+                       argv++;
+               } else if (!parseoperand_int(argv[0], argv[1], &mode)) {
+                       argv++;
+               } else if (!parsespeed(*argv, &speed)) {
+                       if (cfsetispeed(&mode, speed.speed))
+                               eprintf("cfsetispeed %s:", speed.str);
+                       if (cfsetospeed(&mode, speed.speed))
+                               eprintf("cfsetospeed %s:", speed.str);
+               } else {
+                       goto invalid;
+               }
+       }
+
+       if (memcmp(&mode, &mode2, sizeof(mode))) {
+               memset(&mode2, 0, sizeof(mode2));
+               if (tcsetattr(STDIN_FILENO, drain_requested ? TCSADRAIN : 
TCSANOW, &mode))
+                       eprintf("tcsetattr <stdin>:");
+               if (tcgetattr(STDIN_FILENO, &mode2))
+                       eprintf("tcgetattr <stdin>:");
+               if (memcmp(&mode, &mode2, sizeof(mode)))
+                       eprintf("tcsetattr <stdin>: unable to apply all 
operands\n");
+       }
+
+       if (gflag) {
+               buf = (unsigned char *)&mode;
+               printf("=");
+               for (n = sizeof(mode); n--; buf++)
+                       printf("%02x", *buf);
+               printf("\n");
+       }
+
+       if (output_size_requested) {
+               if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize))
+                       eprintf("TIOCGWINSZ <stdin>:");
+               printf("%u %u\n", winsize.ws_row, winsize.ws_col);
+       }
+
+       if (output_speed_requested) {
+               in = cfgetispeed(&mode);
+               out = cfgetospeed(&mode);
+               if (!in || in == out)
+                       printf("%s\n", baudtostr(out));
+               else
+                       printf("%s %s\n", baudtostr(in), baudtostr(out));
+       }
+
+       if ((aflag || !argc) && !gflag)
+               displaysettings(&mode, aflag);
+
+       return 0;
+
+invalid:
+       eprintf("invalid operand: %s\n", *argv);
+}
-- 
2.7.4


Reply via email to