OSDN Git Service

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