stty.c 14 KB

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