Home | History | Annotate | Download | only in pending
      1 /* stty.c - Get/set terminal configuration.
      2  *
      3  * Copyright 2017 The Android Open Source Project.
      4  *
      5  * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/stty.html
      6 
      7 USE_STTY(NEWTOY(stty, "?aF:g[!ag]", TOYFLAG_BIN))
      8 
      9 config STTY
     10   bool "stty"
     11   default n
     12   help
     13     usage: stty [-ag] [-F device] SETTING...
     14 
     15     Get/set terminal configuration.
     16 
     17     -F	Open device instead of stdin
     18     -a	Show all current settings (default differences from "sane").
     19     -g	Show all current settings usable as input to stty.
     20 
     21     Special characters (syntax ^c or undef): intr quit erase kill eof eol eol2
     22     swtch start stop susp rprnt werase lnext discard
     23 
     24     Control/input/output/local settings as shown by -a, '-' prefix to disable
     25 
     26     Combo settings: cooked/raw, evenp/oddp/parity, nl, ek, sane
     27 
     28     N	set input and output speed (ispeed N or ospeed N for just one)
     29     cols N	set number of columns
     30     rows N	set number of rows
     31     line N	set line discipline
     32     min N	set minimum chars per read
     33     time N	set read timeout
     34     speed	show speed only
     35     size	show size only
     36 */
     37 
     38 #define FOR_stty
     39 #include "toys.h"
     40 
     41 #include <linux/tty.h>
     42 
     43 GLOBALS(
     44   char *device;
     45 
     46   int fd, col;
     47   unsigned output_cols;
     48 )
     49 
     50 static const int bauds[] = {
     51   0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600,
     52   19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600,
     53   1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000
     54 };
     55 
     56 static int baud(speed_t speed)
     57 {
     58   if (speed&CBAUDEX) speed=(speed&~CBAUDEX)+15;
     59   return bauds[speed];
     60 }
     61 
     62 static speed_t speed(int baud)
     63 {
     64   int i;
     65 
     66   for (i=0;i<ARRAY_LEN(bauds);i++) if (bauds[i] == baud) break;
     67   if (i == ARRAY_LEN(bauds)) error_exit("unknown speed: %d", baud);
     68   return i+4081*(i>16);
     69 }
     70 
     71 struct flag {
     72   char *name;
     73   int value;
     74   int mask;
     75 };
     76 
     77 static const struct flag chars[] = {
     78   { "intr", VINTR }, { "quit", VQUIT }, { "erase", VERASE }, { "kill", VKILL },
     79   { "eof", VEOF }, { "eol", VEOL }, { "eol2", VEOL2 }, { "swtch", VSWTC },
     80   { "start", VSTART }, { "stop", VSTOP }, { "susp", VSUSP },
     81   { "rprnt", VREPRINT }, { "werase", VWERASE }, { "lnext", VLNEXT },
     82   { "discard", VDISCARD }, { "min", VMIN }, { "time", VTIME },
     83 };
     84 
     85 static const struct flag cflags[] = {
     86   { "parenb", PARENB }, { "parodd", PARODD }, { "cmspar", CMSPAR },
     87   { "cs5", CS5, CSIZE }, { "cs6", CS6, CSIZE }, { "cs7", CS7, CSIZE },
     88   { "cs8", CS8, CSIZE }, { "hupcl", HUPCL }, { "cstopb", CSTOPB },
     89   { "cread", CREAD }, { "clocal", CLOCAL }, { "crtscts", CRTSCTS },
     90 };
     91 
     92 static const struct flag iflags[] = {
     93   { "ignbrk", IGNBRK }, { "brkint", BRKINT }, { "ignpar", IGNPAR },
     94   { "parmrk", PARMRK }, { "inpck", INPCK }, { "istrip", ISTRIP },
     95   { "inlcr", INLCR }, { "igncr", IGNCR }, { "icrnl", ICRNL }, { "ixon", IXON },
     96   { "ixoff", IXOFF }, { "iuclc", IUCLC }, { "ixany", IXANY },
     97   { "imaxbel", IMAXBEL }, { "iutf8", IUTF8 },
     98 };
     99 
    100 static const struct flag oflags[] = {
    101   { "opost", OPOST }, { "olcuc", OLCUC }, { "ocrnl", OCRNL },
    102   { "onlcr", ONLCR }, { "onocr", ONOCR }, { "onlret", ONLRET },
    103   { "ofill", OFILL }, { "ofdel", OFDEL }, { "nl0", NL0, NLDLY },
    104   { "nl1", NL1, NLDLY }, { "cr0", CR0, CRDLY }, { "cr1", CR1, CRDLY },
    105   { "cr2", CR2, CRDLY }, { "cr3", CR3, CRDLY }, { "tab0", TAB0, TABDLY },
    106   { "tab1", TAB1, TABDLY }, { "tab2", TAB2, TABDLY }, { "tab3", TAB3, TABDLY },
    107   { "bs0", BS0, BSDLY }, { "bs1", BS1, BSDLY }, { "vt0", VT0, VTDLY },
    108   { "vt1", VT1, VTDLY }, { "ff0", FF0, FFDLY }, { "ff1", FF1, FFDLY },
    109 };
    110 
    111 static const struct flag lflags[] = {
    112   { "isig", ISIG }, { "icanon", ICANON }, { "iexten", IEXTEN },
    113   { "echo", ECHO }, { "echoe", ECHOE }, { "echok", ECHOK },
    114   { "echonl", ECHONL }, { "noflsh", NOFLSH }, { "xcase", XCASE },
    115   { "tostop", TOSTOP }, { "echoprt", ECHOPRT }, { "echoctl", ECHOCTL },
    116   { "echoke", ECHOKE }, { "flusho", FLUSHO }, { "extproc", EXTPROC },
    117 };
    118 
    119 static const struct synonym {
    120   char *from;
    121   char *to;
    122 } synonyms[] = {
    123   { "cbreak", "-icanon" }, { "-cbreak", "icanon" }, { "-cooked", "raw" },
    124   { "crterase", "echoe" }, { "-crterase", "-echoe" }, { "crtkill", "echoke" },
    125   { "-crtkill", "-echoke" }, { "ctlecho", "echoctl" }, { "-tandem", "-ixoff" },
    126   { "-ctlecho", "-echoctl" }, { "hup", "hupcl" }, { "-hup", "-hupcl" },
    127   { "prterase", "echoprt" }, { "-prterase", "-echoprt" }, { "-raw", "cooked" },
    128   { "tabs", "tab0" }, { "-tabs", "tab3" }, { "tandem", "ixoff" },
    129 };
    130 
    131 static void out(const char *fmt, ...)
    132 {
    133   va_list va;
    134   int len;
    135   char *prefix = " ";
    136 
    137   va_start(va, fmt);
    138   len = vsnprintf(toybuf, sizeof(toybuf), fmt, va);
    139   va_end(va);
    140 
    141   if (TT.output_cols == 0) {
    142     TT.output_cols = 80;
    143     terminal_size(&TT.output_cols, NULL);
    144   }
    145 
    146   if (TT.col == 0 || *fmt == '\n') prefix = "";
    147   else if (TT.col + 1 + len >= TT.output_cols) {
    148     prefix = "\n";
    149     TT.col = 0;
    150   }
    151   xprintf("%s%s", prefix, toybuf);
    152 
    153   if (toybuf[len-1] == '\n') TT.col = 0;
    154   else TT.col += strlen(prefix) + len;
    155 }
    156 
    157 static void show_flags(tcflag_t actual, tcflag_t sane,
    158                        const struct flag *flags, int len)
    159 {
    160   int i, j, value, mask;
    161 
    162   // Implement -a by ensuring that sane != actual so we'll show everything.
    163   if (toys.optflags&FLAG_a) sane = ~actual;
    164 
    165   for (i=j=0;i<len;i++) {
    166     value = flags[i].value;
    167     if ((mask = flags[i].mask)) {
    168       if ((actual&mask)==value && (sane&mask)!=value) {
    169         out("%s", flags[i].name);
    170         j++;
    171       }
    172     } else {
    173       if ((actual&value) != (sane&value)) {
    174         out("%s%s", actual&value?"":"-", flags[i].name);
    175         j++;
    176       }
    177     }
    178   }
    179   if (j) out("\n");
    180 }
    181 
    182 static void show_size(int verbose)
    183 {
    184   struct winsize ws;
    185 
    186   if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.device);
    187   out(verbose ? "rows %d; columns %d;" : "%d %d\n", ws.ws_row, ws.ws_col);
    188 }
    189 
    190 static void show_speed(struct termios *t, int verbose)
    191 {
    192   int ispeed = baud(cfgetispeed(t)), ospeed = baud(cfgetospeed(t));
    193   char *fmt = verbose ? "ispeed %d baud; ospeed %d baud;" : "%d %d\n";
    194 
    195   if (ispeed == ospeed) fmt += (verbose ? 17 : 3);
    196   out(fmt, ispeed, ospeed);
    197 }
    198 
    199 static int get_arg(int *i, long long low, long long high)
    200 {
    201   (*i)++;
    202   if (!toys.optargs[*i]) error_exit("missing arg");
    203   return atolx_range(toys.optargs[*i], low, high);
    204 }
    205 
    206 static int set_flag(tcflag_t *f, const struct flag *flags, int len,
    207                     char *name, int on)
    208 {
    209   int i;
    210 
    211   for (i=0;i<len;i++) {
    212     if (!strcmp(flags[i].name, name)) {
    213       if (on) {
    214         *f &= ~flags[i].mask;
    215         *f |= flags[i].value;
    216       } else {
    217         if (flags[i].mask) error_exit("%s isn't a boolean", name);
    218         *f &= ~flags[i].value;
    219       }
    220       return 1;
    221     }
    222   }
    223   return 0;
    224 }
    225 
    226 static void set_option(struct termios *new, char *option)
    227 {
    228   int on = (*option != '-');
    229 
    230   if (!on) option++;
    231   if (!set_flag(&new->c_cflag, cflags, ARRAY_LEN(cflags), option, on) &&
    232       !set_flag(&new->c_iflag, iflags, ARRAY_LEN(iflags), option, on) &&
    233       !set_flag(&new->c_oflag, oflags, ARRAY_LEN(oflags), option, on) &&
    234       !set_flag(&new->c_lflag, lflags, ARRAY_LEN(lflags), option, on))
    235     error_exit("unknown option: %s", option);
    236 }
    237 
    238 static void set_options(struct termios* new, ...)
    239 {
    240   va_list va;
    241   char *option;
    242 
    243   va_start(va, new);
    244   while ((option = va_arg(va, char *))) set_option(new, option);
    245   va_end(va);
    246 }
    247 
    248 static void set_size(int is_rows, unsigned short value)
    249 {
    250   struct winsize ws;
    251 
    252   if (ioctl(TT.fd, TIOCGWINSZ, &ws)) perror_exit("TIOCGWINSZ %s", TT.device);
    253   if (is_rows) ws.ws_row = value;
    254   else ws.ws_col = value;
    255   if (ioctl(TT.fd, TIOCSWINSZ, &ws)) perror_exit("TIOCSWINSZ %s", TT.device);
    256 }
    257 
    258 static int set_special_character(struct termios *new, int *i, char *char_name)
    259 {
    260   int j;
    261 
    262   // The -2 is to ignore VMIN and VTIME, which are just unsigned integers.
    263   for (j=0;j<ARRAY_LEN(chars)-2;j++) {
    264     if (!strcmp(chars[j].name, char_name)) {
    265       char *arg = toys.optargs[++(*i)];
    266       cc_t ch;
    267 
    268       if (!arg) error_exit("missing arg");
    269       if (!strcmp(arg, "^-") || !strcmp(arg, "undef")) ch = _POSIX_VDISABLE;
    270       else if (!strcmp(arg, "^?")) ch = 0x7f;
    271       else if (arg[0] == '^' && arg[2] == 0) ch = (toupper(arg[1])-'@');
    272       else if (!arg[1]) ch = arg[0];
    273       else error_exit("invalid arg: %s", arg);
    274       xprintf("setting %s to %s (%02x)\n", char_name, arg, ch);
    275       new->c_cc[chars[j].value] = ch;
    276       return 1;
    277     }
    278   }
    279   return 0;
    280 }
    281 
    282 static void make_sane(struct termios *t)
    283 {
    284   // POSIX has no opinion about what "sane" means. From "man stty".
    285   // "cs8" is missing from the man page, but needed to get identical results.
    286   set_options(t, "cread", "-ignbrk", "brkint", "-inlcr", "-igncr", "icrnl",
    287     "icanon", "iexten", "echo", "echoe", "echok", "-echonl", "-noflsh",
    288     "-ixoff", "-iutf8", "-iuclc", "-ixany", "imaxbel", "-xcase", "-olcuc",
    289     "-ocrnl", "opost", "-ofill", "onlcr", "-onocr", "-onlret", "nl0", "cr0",
    290     "tab0", "bs0", "vt0", "ff0", "isig", "-tostop", "-ofdel", "-echoprt",
    291     "echoctl", "echoke", "-extproc", "-flusho", "cs8", NULL);
    292   memset(t->c_cc, 0, NCCS);
    293   t->c_cc[VINTR] = 0x3;
    294   t->c_cc[VQUIT] = 0x1c;
    295   t->c_cc[VERASE] = 0x7f;
    296   t->c_cc[VKILL] = 0x15;
    297   t->c_cc[VEOF] = 0x4;
    298   t->c_cc[VTIME] = 0;
    299   t->c_cc[VMIN] = 1;
    300   t->c_cc[VSWTC] = 0;
    301   t->c_cc[VSTART] = 0x11;
    302   t->c_cc[VSTOP] = 0x13;
    303   t->c_cc[VSUSP] = 0x1a;
    304   t->c_cc[VEOL] = 0;
    305   t->c_cc[VREPRINT] = 0x12;
    306   t->c_cc[VDISCARD] = 0xf;
    307   t->c_cc[VWERASE] = 0x17;
    308   t->c_cc[VLNEXT] = 0x16;
    309   t->c_cc[VEOL2] = 0;
    310 }
    311 
    312 static void xtcgetattr(struct termios *t)
    313 {
    314   if (tcgetattr(TT.fd, t)) perror_exit("tcgetattr %s", TT.device);
    315 }
    316 
    317 static void do_stty()
    318 {
    319   struct termios old, sane;
    320   int i, j, n;
    321 
    322   xtcgetattr(&old);
    323 
    324   if (*toys.optargs) {
    325     struct termios new = old;
    326 
    327     for (i=0; toys.optargs[i]; i++) {
    328       char *arg = toys.optargs[i];
    329 
    330       if (!strcmp(arg, "size")) show_size(0);
    331       else if (!strcmp(arg, "speed")) show_speed(&old, 0);
    332       else if (!strcmp(arg, "line")) new.c_line = get_arg(&i, N_TTY, NR_LDISCS);
    333       else if (!strcmp(arg, "min")) new.c_cc[VMIN] = get_arg(&i, 0, 255);
    334       else if (!strcmp(arg, "time")) new.c_cc[VTIME] = get_arg(&i, 0, 255);
    335       else if (atoi(arg) > 0) {
    336         int new_speed = speed(atolx_range(arg, 0, 4000000));
    337 
    338         cfsetispeed(&new, new_speed);
    339         cfsetospeed(&new, new_speed);
    340       } else if (!strcmp(arg, "ispeed"))
    341         cfsetispeed(&new, speed(get_arg(&i, 0, 4000000)));
    342       else if (!strcmp(arg, "ospeed"))
    343         cfsetospeed(&new, speed(get_arg(&i, 0, 4000000)));
    344       else if (!strcmp(arg, "rows")) set_size(1, get_arg(&i, 0, USHRT_MAX));
    345       else if (!strcmp(arg, "cols") || !strcmp(arg, "columns"))
    346         set_size(0, get_arg(&i, 0, USHRT_MAX));
    347       else if (sscanf(arg, "%x:%x:%x:%x:%n", &new.c_iflag, &new.c_oflag,
    348                         &new.c_cflag, &new.c_lflag, &n) == 4)
    349       {
    350         int value;
    351 
    352         arg += n;
    353         for (j=0;j<NCCS;j++) {
    354           if (sscanf(arg, "%x%n", &value, &n) != 1) error_exit("bad -g string");
    355           new.c_cc[j] = value;
    356           arg += n+1;
    357         }
    358       } else if (set_special_character(&new, &i, arg));
    359         // Already done as a side effect.
    360       else if (!strcmp(arg, "cooked"))
    361         set_options(&new, "brkint", "ignpar", "istrip", "icrnl", "ixon",
    362           "opost", "isig", "icanon", NULL);
    363       else if (!strcmp(arg, "evenp") || !strcmp(arg, "parity"))
    364         set_options(&new, "parenb", "cs7", "-parodd", NULL);
    365       else if (!strcmp(arg, "oddp"))
    366         set_options(&new, "parenb", "cs7", "parodd", NULL);
    367       else if (!strcmp(arg, "-parity") || !strcmp(arg, "-evenp") ||
    368                  !strcmp(arg, "-oddp")) {
    369         set_options(&new, "-parenb", "cs8", NULL);
    370       } else if (!strcmp(arg, "raw")) {
    371         // POSIX and "man stty" differ wildly. This is "man stty".
    372         set_options(&new, "-ignbrk", "-brkint", "-ignpar", "-parmrk", "-inpck",
    373           "-istrip", "-inlcr", "-igncr", "-icrnl", "-ixon", "-ixoff", "-iuclc",
    374           "-ixany", "-imaxbel", "-opost", "-isig", "-icanon", "-xcase", NULL);
    375         new.c_cc[VMIN] = 1;
    376         new.c_cc[VTIME] = 0;
    377       } else if (!strcmp(arg, "nl"))
    378         set_options(&new, "-icrnl", "-ocrnl", NULL);
    379       else if (!strcmp(arg, "-nl"))
    380         set_options(&new, "icrnl", "ocrnl", "-inlcr", "-igncr", NULL);
    381       else if (!strcmp(arg, "ek")) {
    382         new.c_cc[VERASE] = 0x7f;
    383         new.c_cc[VKILL] = 0x15;
    384       } else if (!strcmp(arg, "sane")) make_sane(&new);
    385       else {
    386         // Translate historical cruft into canonical forms.
    387         for (j=0;j<ARRAY_LEN(synonyms);j++) {
    388           if (!strcmp(synonyms[j].from, arg)) {
    389             arg = synonyms[j].to;
    390             break;
    391           }
    392         }
    393         set_option(&new, arg);
    394       }
    395     }
    396     tcsetattr(TT.fd, TCSAFLUSH, &new);
    397     xtcgetattr(&old);
    398     if (memcmp(&old, &new, sizeof(old)))
    399       error_exit("unable to perform all requested operations on %s", TT.device);
    400 
    401     return;
    402   }
    403 
    404   if (toys.optflags&FLAG_g) {
    405     xprintf("%x:%x:%x:%x:", old.c_iflag, old.c_oflag, old.c_cflag, old.c_lflag);
    406     for (i=0;i<NCCS;i++) xprintf("%x%c", old.c_cc[i], i==NCCS-1?'\n':':');
    407     return;
    408   }
    409 
    410   // Without arguments, "stty" only shows the speed, the line discipline,
    411   // special characters and any flags that differ from the "sane" settings.
    412   make_sane(&sane);
    413   show_speed(&old, 1);
    414   if (toys.optflags&FLAG_a) show_size(1);
    415   out("line = %d;\n", old.c_line);
    416 
    417   for (i=j=0;i<ARRAY_LEN(chars);i++) {
    418     char vis[16] = {};
    419     cc_t ch = old.c_cc[chars[i].value];
    420 
    421     if (ch == sane.c_cc[chars[i].value] && (toys.optflags&FLAG_a)==0)
    422       continue;
    423 
    424     if (chars[i].value == VMIN || chars[i].value == VTIME) {
    425       snprintf(vis, sizeof(vis), "%u", ch);
    426     } else if (ch == _POSIX_VDISABLE) {
    427       strcat(vis, "<undef>");
    428     } else {
    429       if (ch > 0x7f) {
    430         strcat(vis, "M-");
    431         ch -= 128;
    432       }
    433       if (ch < ' ') sprintf(vis+strlen(vis), "^%c", (ch+'@'));
    434       else if (ch == 0x7f) strcat(vis, "^?");
    435       else sprintf(vis+strlen(vis), "%c", ch);
    436     }
    437     out("%s = %s;", chars[i].name, vis);
    438     j++;
    439   }
    440   if (j) out("\n");
    441 
    442   show_flags(old.c_cflag, sane.c_cflag, cflags, ARRAY_LEN(cflags));
    443   show_flags(old.c_iflag, sane.c_iflag, iflags, ARRAY_LEN(iflags));
    444   show_flags(old.c_oflag, sane.c_oflag, oflags, ARRAY_LEN(oflags));
    445   show_flags(old.c_lflag, sane.c_lflag, lflags, ARRAY_LEN(lflags));
    446 }
    447 
    448 void stty_main(void)
    449 {
    450   if (toys.optflags&(FLAG_a|FLAG_g) && *toys.optargs)
    451     error_exit("can't make settings with -a/-g");
    452 
    453   if (!TT.device) TT.device = "standard input";
    454   else TT.fd=xopen(TT.device, (O_RDWR*!!*toys.optargs)|O_NOCTTY|O_NONBLOCK);
    455 
    456   do_stty();
    457 
    458   if (CFG_TOYBOX_FREE && TT.device) close(TT.fd);
    459 }
    460