3
0

stty.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. /* vi: set sw=4 ts=4: */
  2. /* stty -- change and print terminal line settings
  3. Copyright (C) 1990-1999 Free Software Foundation, Inc.
  4. Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  5. */
  6. /* Usage: stty [-ag] [-F device] [setting...]
  7. Options:
  8. -a Write all current settings to stdout in human-readable form.
  9. -g Write all current settings to stdout in stty-readable form.
  10. -F Open and use the specified device instead of stdin
  11. If no args are given, write to stdout the baud rate and settings that
  12. have been changed from their defaults. Mode reading and changes
  13. are done on the specified device, or stdin if none was specified.
  14. David MacKenzie <djm@gnu.ai.mit.edu>
  15. Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
  16. */
  17. #include "busybox.h"
  18. #ifndef _POSIX_VDISABLE
  19. # define _POSIX_VDISABLE ((unsigned char) 0)
  20. #endif
  21. #define Control(c) ((c) & 0x1f)
  22. /* Canonical values for control characters */
  23. #ifndef CINTR
  24. # define CINTR Control('c')
  25. #endif
  26. #ifndef CQUIT
  27. # define CQUIT 28
  28. #endif
  29. #ifndef CERASE
  30. # define CERASE 127
  31. #endif
  32. #ifndef CKILL
  33. # define CKILL Control('u')
  34. #endif
  35. #ifndef CEOF
  36. # define CEOF Control('d')
  37. #endif
  38. #ifndef CEOL
  39. # define CEOL _POSIX_VDISABLE
  40. #endif
  41. #ifndef CSTART
  42. # define CSTART Control('q')
  43. #endif
  44. #ifndef CSTOP
  45. # define CSTOP Control('s')
  46. #endif
  47. #ifndef CSUSP
  48. # define CSUSP Control('z')
  49. #endif
  50. #if defined(VEOL2) && !defined(CEOL2)
  51. # define CEOL2 _POSIX_VDISABLE
  52. #endif
  53. /* ISC renamed swtch to susp for termios, but we'll accept either name */
  54. #if defined(VSUSP) && !defined(VSWTCH)
  55. # define VSWTCH VSUSP
  56. # define CSWTCH CSUSP
  57. #endif
  58. #if defined(VSWTCH) && !defined(CSWTCH)
  59. # define CSWTCH _POSIX_VDISABLE
  60. #endif
  61. /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
  62. So the default is to disable 'swtch.' */
  63. #if defined (__sparc__) && defined (__svr4__)
  64. # undef CSWTCH
  65. # define CSWTCH _POSIX_VDISABLE
  66. #endif
  67. #if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
  68. # define VWERASE VWERSE
  69. #endif
  70. #if defined(VDSUSP) && !defined (CDSUSP)
  71. # define CDSUSP Control('y')
  72. #endif
  73. #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
  74. # define VREPRINT VRPRNT
  75. #endif
  76. #if defined(VREPRINT) && !defined(CRPRNT)
  77. # define CRPRNT Control('r')
  78. #endif
  79. #if defined(VWERASE) && !defined(CWERASE)
  80. # define CWERASE Control('w')
  81. #endif
  82. #if defined(VLNEXT) && !defined(CLNEXT)
  83. # define CLNEXT Control('v')
  84. #endif
  85. #if defined(VDISCARD) && !defined(VFLUSHO)
  86. # define VFLUSHO VDISCARD
  87. #endif
  88. #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
  89. # define VFLUSHO VFLUSH
  90. #endif
  91. #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
  92. # define ECHOCTL CTLECH
  93. #endif
  94. #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
  95. # define ECHOCTL TCTLECH
  96. #endif
  97. #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
  98. # define ECHOKE CRTKIL
  99. #endif
  100. #if defined(VFLUSHO) && !defined(CFLUSHO)
  101. # define CFLUSHO Control('o')
  102. #endif
  103. #if defined(VSTATUS) && !defined(CSTATUS)
  104. # define CSTATUS Control('t')
  105. #endif
  106. /* Which speeds to set */
  107. enum speed_setting {
  108. input_speed, output_speed, both_speeds
  109. };
  110. /* Which member(s) of 'struct termios' a mode uses */
  111. enum {
  112. /* Do NOT change the order or values, as mode_type_flag()
  113. * depends on them */
  114. control, input, output, local, combination
  115. };
  116. static const char evenp [] = "evenp";
  117. static const char raw [] = "raw";
  118. static const char stty_min [] = "min";
  119. static const char stty_time [] = "time";
  120. static const char stty_swtch[] = "swtch";
  121. static const char stty_eol [] = "eol";
  122. static const char stty_eof [] = "eof";
  123. static const char parity [] = "parity";
  124. static const char stty_oddp [] = "oddp";
  125. static const char stty_nl [] = "nl";
  126. static const char stty_ek [] = "ek";
  127. static const char stty_sane [] = "sane";
  128. static const char cbreak [] = "cbreak";
  129. static const char stty_pass8[] = "pass8";
  130. static const char litout [] = "litout";
  131. static const char cooked [] = "cooked";
  132. static const char decctlq [] = "decctlq";
  133. static const char stty_tabs [] = "tabs";
  134. static const char stty_lcase[] = "lcase";
  135. static const char stty_LCASE[] = "LCASE";
  136. static const char stty_crt [] = "crt";
  137. static const char stty_dec [] = "dec";
  138. /* Flags for 'struct mode_info' */
  139. #define SANE_SET 1 /* Set in 'sane' mode */
  140. #define SANE_UNSET 2 /* Unset in 'sane' mode */
  141. #define REV 4 /* Can be turned off by prepending '-' */
  142. #define OMIT 8 /* Don't display value */
  143. /* Each mode */
  144. struct mode_info {
  145. const char * const name; /* Name given on command line */
  146. const unsigned char type; /* Which structure element to change */
  147. const unsigned char flags; /* Setting and display options */
  148. /* were using short here, but ppc32 was unhappy: */
  149. const tcflag_t mask; /* Other bits to turn off for this mode */
  150. const tcflag_t bits; /* Bits to set for this mode */
  151. };
  152. /* We can optimize it further by using name[8] instead of char *name */
  153. /* but beware of "if (info->name == evenp)" checks! */
  154. /* Need to replace them with "if (info == &mode_info[EVENP_INDX])" */
  155. #define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
  156. static const struct mode_info mode_info[] = {
  157. MI_ENTRY("parenb", control, REV, PARENB, 0 ),
  158. MI_ENTRY("parodd", control, REV, PARODD, 0 ),
  159. MI_ENTRY("cs5", control, 0, CS5, CSIZE),
  160. MI_ENTRY("cs6", control, 0, CS6, CSIZE),
  161. MI_ENTRY("cs7", control, 0, CS7, CSIZE),
  162. MI_ENTRY("cs8", control, 0, CS8, CSIZE),
  163. MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
  164. MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
  165. MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
  166. MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
  167. MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
  168. #ifdef CRTSCTS
  169. MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
  170. #endif
  171. MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
  172. MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
  173. MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
  174. MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
  175. MI_ENTRY("inpck", input, REV, INPCK, 0 ),
  176. MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
  177. MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
  178. MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
  179. MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
  180. MI_ENTRY("ixon", input, REV, IXON, 0 ),
  181. MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
  182. MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
  183. #ifdef IUCLC
  184. MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
  185. #endif
  186. #ifdef IXANY
  187. MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
  188. #endif
  189. #ifdef IMAXBEL
  190. MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
  191. #endif
  192. MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
  193. #ifdef OLCUC
  194. MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
  195. #endif
  196. #ifdef OCRNL
  197. MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
  198. #endif
  199. #ifdef ONLCR
  200. MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
  201. #endif
  202. #ifdef ONOCR
  203. MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
  204. #endif
  205. #ifdef ONLRET
  206. MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
  207. #endif
  208. #ifdef OFILL
  209. MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
  210. #endif
  211. #ifdef OFDEL
  212. MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
  213. #endif
  214. #ifdef NLDLY
  215. MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
  216. MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
  217. #endif
  218. #ifdef CRDLY
  219. MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
  220. MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
  221. MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
  222. MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
  223. #endif
  224. #ifdef TABDLY
  225. MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
  226. MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
  227. MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
  228. MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
  229. #else
  230. # ifdef OXTABS
  231. MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
  232. # endif
  233. #endif
  234. #ifdef BSDLY
  235. MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
  236. MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
  237. #endif
  238. #ifdef VTDLY
  239. MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
  240. MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
  241. #endif
  242. #ifdef FFDLY
  243. MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
  244. MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
  245. #endif
  246. MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
  247. MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
  248. #ifdef IEXTEN
  249. MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
  250. #endif
  251. MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
  252. MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
  253. MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
  254. MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
  255. MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
  256. MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
  257. #ifdef XCASE
  258. MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
  259. #endif
  260. #ifdef TOSTOP
  261. MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
  262. #endif
  263. #ifdef ECHOPRT
  264. MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
  265. MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
  266. #endif
  267. #ifdef ECHOCTL
  268. MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
  269. MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
  270. #endif
  271. #ifdef ECHOKE
  272. MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
  273. MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
  274. #endif
  275. MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
  276. MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
  277. MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
  278. MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
  279. MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
  280. MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
  281. MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
  282. MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
  283. MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
  284. MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
  285. MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
  286. #ifdef IXANY
  287. MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
  288. #endif
  289. #if defined(TABDLY) || defined(OXTABS)
  290. MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
  291. #endif
  292. #if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
  293. MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
  294. MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
  295. #endif
  296. MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
  297. MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
  298. };
  299. enum {
  300. NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
  301. };
  302. /* Control character settings */
  303. struct control_info {
  304. const char * const name; /* Name given on command line */
  305. const unsigned char saneval; /* Value to set for 'stty sane' */
  306. const unsigned char offset; /* Offset in c_cc */
  307. };
  308. /* Control characters */
  309. static const struct control_info control_info[] = {
  310. {"intr", CINTR, VINTR},
  311. {"quit", CQUIT, VQUIT},
  312. {"erase", CERASE, VERASE},
  313. {"kill", CKILL, VKILL},
  314. {stty_eof, CEOF, VEOF},
  315. {stty_eol, CEOL, VEOL},
  316. #ifdef VEOL2
  317. {"eol2", CEOL2, VEOL2},
  318. #endif
  319. #ifdef VSWTCH
  320. {stty_swtch, CSWTCH, VSWTCH},
  321. #endif
  322. {"start", CSTART, VSTART},
  323. {"stop", CSTOP, VSTOP},
  324. {"susp", CSUSP, VSUSP},
  325. #ifdef VDSUSP
  326. {"dsusp", CDSUSP, VDSUSP},
  327. #endif
  328. #ifdef VREPRINT
  329. {"rprnt", CRPRNT, VREPRINT},
  330. #endif
  331. #ifdef VWERASE
  332. {"werase", CWERASE, VWERASE},
  333. #endif
  334. #ifdef VLNEXT
  335. {"lnext", CLNEXT, VLNEXT},
  336. #endif
  337. #ifdef VFLUSHO
  338. {"flush", CFLUSHO, VFLUSHO},
  339. #endif
  340. #ifdef VSTATUS
  341. {"status", CSTATUS, VSTATUS},
  342. #endif
  343. /* These must be last because of the display routines */
  344. {stty_min, 1, VMIN},
  345. {stty_time, 0, VTIME},
  346. };
  347. enum {
  348. NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
  349. };
  350. /* The width of the screen, for output wrapping */
  351. static unsigned max_col = 80; /* default */
  352. /* Current position, to know when to wrap */
  353. static unsigned current_col;
  354. static const char *device_name = bb_msg_standard_input;
  355. /* Return a string that is the printable representation of character CH */
  356. /* Adapted from 'cat' by Torbjorn Granlund */
  357. static const char *visible(unsigned int ch)
  358. {
  359. static char buf[10];
  360. char *bpout = buf;
  361. if (ch == _POSIX_VDISABLE)
  362. return "<undef>";
  363. if (ch >= 128) {
  364. ch -= 128;
  365. *bpout++ = 'M';
  366. *bpout++ = '-';
  367. }
  368. if (ch < 32) {
  369. *bpout++ = '^';
  370. *bpout++ = ch + 64;
  371. } else if (ch < 127) {
  372. *bpout++ = ch;
  373. } else {
  374. *bpout++ = '^';
  375. *bpout++ = '?';
  376. }
  377. *bpout = '\0';
  378. return buf;
  379. }
  380. static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
  381. {
  382. static const unsigned char tcflag_offsets[] = {
  383. offsetof(struct termios, c_cflag), /* control */
  384. offsetof(struct termios, c_iflag), /* input */
  385. offsetof(struct termios, c_oflag), /* output */
  386. offsetof(struct termios, c_lflag), /* local */
  387. };
  388. if (type <= local) {
  389. return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
  390. }
  391. return NULL;
  392. }
  393. static void set_speed_or_die(enum speed_setting type, const char * const arg,
  394. struct termios * const mode)
  395. {
  396. speed_t baud;
  397. baud = tty_value_to_baud(xatou(arg));
  398. if (type != output_speed) { /* either input or both */
  399. cfsetispeed(mode, baud);
  400. }
  401. if (type != input_speed) { /* either output or both */
  402. cfsetospeed(mode, baud);
  403. }
  404. }
  405. static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
  406. {
  407. bb_perror_msg_and_die(fmt, device_name);
  408. }
  409. static void perror_on_device(const char *fmt)
  410. {
  411. bb_perror_msg(fmt, device_name);
  412. }
  413. /* Print format string MESSAGE and optional args.
  414. Wrap to next line first if it won't fit.
  415. Print a space first unless MESSAGE will start a new line */
  416. static void wrapf(const char *message, ...)
  417. {
  418. char buf[128];
  419. va_list args;
  420. int buflen;
  421. va_start(args, message);
  422. buflen = vsnprintf(buf, sizeof(buf), message, args);
  423. va_end(args);
  424. /* We seem to be called only with suitable lengths, but check if
  425. somebody failed to adhere to this assumption just to be sure. */
  426. if (!buflen || buflen >= sizeof(buf)) return;
  427. if (current_col > 0) {
  428. current_col++;
  429. if (buf[0] != '\n') {
  430. if (current_col + buflen >= max_col) {
  431. putchar('\n');
  432. current_col = 0;
  433. } else
  434. putchar(' ');
  435. }
  436. }
  437. fputs(buf, stdout);
  438. current_col += buflen;
  439. if (buf[buflen-1] == '\n')
  440. current_col = 0;
  441. }
  442. static void set_window_size(const int rows, const int cols)
  443. {
  444. struct winsize win = { 0, 0, 0, 0};
  445. if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
  446. if (errno != EINVAL) {
  447. goto bail;
  448. }
  449. memset(&win, 0, sizeof(win));
  450. }
  451. if (rows >= 0)
  452. win.ws_row = rows;
  453. if (cols >= 0)
  454. win.ws_col = cols;
  455. if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
  456. bail:
  457. perror_on_device("%s");
  458. }
  459. static void display_window_size(const int fancy)
  460. {
  461. const char *fmt_str = "%s\0%s: no size information for this device";
  462. unsigned width, height;
  463. if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
  464. if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
  465. perror_on_device(fmt_str);
  466. }
  467. } else {
  468. wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
  469. height, width);
  470. }
  471. }
  472. static const struct suffix_mult stty_suffixes[] = {
  473. {"b", 512 },
  474. {"k", 1024},
  475. {"B", 1024},
  476. {NULL, 0 }
  477. };
  478. static const struct mode_info *find_mode(const char *name)
  479. {
  480. int i;
  481. for (i = 0; i < NUM_mode_info; ++i)
  482. if (!strcmp(name, mode_info[i].name))
  483. return &mode_info[i];
  484. return 0;
  485. }
  486. static const struct control_info *find_control(const char *name)
  487. {
  488. int i;
  489. for (i = 0; i < NUM_control_info; ++i)
  490. if (!strcmp(name, control_info[i].name))
  491. return &control_info[i];
  492. return 0;
  493. }
  494. enum {
  495. param_need_arg = 0x80,
  496. param_line = 1 | 0x80,
  497. param_rows = 2 | 0x80,
  498. param_cols = 3 | 0x80,
  499. param_size = 4,
  500. param_speed = 5,
  501. param_ispeed = 6 | 0x80,
  502. param_ospeed = 7 | 0x80,
  503. };
  504. static int find_param(const char * const name)
  505. {
  506. const char * const params[] = {
  507. "line",
  508. "rows",
  509. "cols",
  510. "columns",
  511. "size",
  512. "speed",
  513. "ispeed",
  514. "ospeed",
  515. NULL
  516. };
  517. int i = index_in_str_array(params, name);
  518. if (i < 0)
  519. return 0;
  520. if (!(i == 4 || i == 5))
  521. i |= 0x80;
  522. return i;
  523. }
  524. static int recover_mode(const char *arg, struct termios *mode)
  525. {
  526. int i, n;
  527. unsigned int chr;
  528. unsigned long iflag, oflag, cflag, lflag;
  529. /* Scan into temporaries since it is too much trouble to figure out
  530. the right format for 'tcflag_t' */
  531. if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
  532. &iflag, &oflag, &cflag, &lflag, &n) != 4)
  533. return 0;
  534. mode->c_iflag = iflag;
  535. mode->c_oflag = oflag;
  536. mode->c_cflag = cflag;
  537. mode->c_lflag = lflag;
  538. arg += n;
  539. for (i = 0; i < NCCS; ++i) {
  540. if (sscanf(arg, ":%x%n", &chr, &n) != 1)
  541. return 0;
  542. mode->c_cc[i] = chr;
  543. arg += n;
  544. }
  545. /* Fail if there are too many fields */
  546. if (*arg != '\0')
  547. return 0;
  548. return 1;
  549. }
  550. static void display_recoverable(const struct termios *mode,
  551. const int ATTRIBUTE_UNUSED dummy)
  552. {
  553. int i;
  554. printf("%lx:%lx:%lx:%lx",
  555. (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
  556. (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
  557. for (i = 0; i < NCCS; ++i)
  558. printf(":%x", (unsigned int) mode->c_cc[i]);
  559. putchar('\n');
  560. }
  561. static void display_speed(const struct termios *mode, int fancy)
  562. {
  563. //01234567 8 9
  564. const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
  565. unsigned long ispeed, ospeed;
  566. ospeed = ispeed = cfgetispeed(mode);
  567. if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
  568. ispeed = ospeed; /* in case ispeed was 0 */
  569. //0123 4 5 6 7 8 9
  570. fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
  571. }
  572. if (fancy) fmt_str += 9;
  573. wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
  574. }
  575. static void do_display(const struct termios *mode, const int all)
  576. {
  577. int i;
  578. tcflag_t *bitsp;
  579. unsigned long mask;
  580. int prev_type = control;
  581. display_speed(mode, 1);
  582. if (all)
  583. display_window_size(1);
  584. #ifdef HAVE_C_LINE
  585. wrapf("line = %d;\n", mode->c_line);
  586. #else
  587. wrapf("\n");
  588. #endif
  589. for (i = 0; control_info[i].name != stty_min; ++i) {
  590. /* If swtch is the same as susp, don't print both */
  591. #if VSWTCH == VSUSP
  592. if (control_info[i].name == stty_swtch)
  593. continue;
  594. #endif
  595. /* If eof uses the same slot as min, only print whichever applies */
  596. #if VEOF == VMIN
  597. if ((mode->c_lflag & ICANON) == 0
  598. && (control_info[i].name == stty_eof
  599. || control_info[i].name == stty_eol)) continue;
  600. #endif
  601. wrapf("%s = %s;", control_info[i].name,
  602. visible(mode->c_cc[control_info[i].offset]));
  603. }
  604. #if VEOF == VMIN
  605. if ((mode->c_lflag & ICANON) == 0)
  606. #endif
  607. wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
  608. if (current_col) wrapf("\n");
  609. for (i = 0; i < NUM_mode_info; ++i) {
  610. if (mode_info[i].flags & OMIT)
  611. continue;
  612. if (mode_info[i].type != prev_type) {
  613. /* wrapf("\n"); */
  614. if (current_col) wrapf("\n");
  615. prev_type = mode_info[i].type;
  616. }
  617. bitsp = mode_type_flag(mode_info[i].type, mode);
  618. mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
  619. if ((*bitsp & mask) == mode_info[i].bits) {
  620. if (all || (mode_info[i].flags & SANE_UNSET))
  621. wrapf("%s", mode_info[i].name);
  622. } else {
  623. if ((all && mode_info[i].flags & REV) ||
  624. (!all &&
  625. (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
  626. wrapf("-%s", mode_info[i].name);
  627. }
  628. }
  629. if (current_col) wrapf("\n");
  630. }
  631. static void sane_mode(struct termios *mode)
  632. {
  633. int i;
  634. tcflag_t *bitsp;
  635. for (i = 0; i < NUM_control_info; ++i) {
  636. #if VMIN == VEOF
  637. if (control_info[i].name == stty_min)
  638. break;
  639. #endif
  640. mode->c_cc[control_info[i].offset] = control_info[i].saneval;
  641. }
  642. for (i = 0; i < NUM_mode_info; ++i) {
  643. if (mode_info[i].flags & SANE_SET) {
  644. bitsp = mode_type_flag(mode_info[i].type, mode);
  645. *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
  646. | mode_info[i].bits;
  647. } else if (mode_info[i].flags & SANE_UNSET) {
  648. bitsp = mode_type_flag(mode_info[i].type, mode);
  649. *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
  650. & ~mode_info[i].bits;
  651. }
  652. }
  653. }
  654. /* Save set_mode from #ifdef forest plague */
  655. #ifndef ONLCR
  656. #define ONLCR 0
  657. #endif
  658. #ifndef OCRNL
  659. #define OCRNL 0
  660. #endif
  661. #ifndef ONLRET
  662. #define ONLRET 0
  663. #endif
  664. #ifndef XCASE
  665. #define XCASE 0
  666. #endif
  667. #ifndef IXANY
  668. #define IXANY 0
  669. #endif
  670. #ifndef TABDLY
  671. #define TABDLY 0
  672. #endif
  673. #ifndef OXTABS
  674. #define OXTABS 0
  675. #endif
  676. #ifndef IUCLC
  677. #define IUCLC 0
  678. #endif
  679. #ifndef OLCUC
  680. #define OLCUC 0
  681. #endif
  682. #ifndef ECHOCTL
  683. #define ECHOCTL 0
  684. #endif
  685. #ifndef ECHOKE
  686. #define ECHOKE 0
  687. #endif
  688. static void set_mode(const struct mode_info *info, int reversed,
  689. struct termios *mode)
  690. {
  691. tcflag_t *bitsp;
  692. bitsp = mode_type_flag(info->type, mode);
  693. if (bitsp) {
  694. if (reversed)
  695. *bitsp = *bitsp & ~info->mask & ~info->bits;
  696. else
  697. *bitsp = (*bitsp & ~info->mask) | info->bits;
  698. return;
  699. }
  700. /* Combination mode */
  701. if (info->name == evenp || info->name == parity) {
  702. if (reversed)
  703. mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
  704. else
  705. mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
  706. } else if (info->name == stty_oddp) {
  707. if (reversed)
  708. mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
  709. else
  710. mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
  711. } else if (info->name == stty_nl) {
  712. if (reversed) {
  713. mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
  714. mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
  715. } else {
  716. mode->c_iflag = mode->c_iflag & ~ICRNL;
  717. if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
  718. }
  719. } else if (info->name == stty_ek) {
  720. mode->c_cc[VERASE] = CERASE;
  721. mode->c_cc[VKILL] = CKILL;
  722. } else if (info->name == stty_sane) {
  723. sane_mode(mode);
  724. }
  725. else if (info->name == cbreak) {
  726. if (reversed)
  727. mode->c_lflag |= ICANON;
  728. else
  729. mode->c_lflag &= ~ICANON;
  730. } else if (info->name == stty_pass8) {
  731. if (reversed) {
  732. mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
  733. mode->c_iflag |= ISTRIP;
  734. } else {
  735. mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
  736. mode->c_iflag &= ~ISTRIP;
  737. }
  738. } else if (info->name == litout) {
  739. if (reversed) {
  740. mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
  741. mode->c_iflag |= ISTRIP;
  742. mode->c_oflag |= OPOST;
  743. } else {
  744. mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
  745. mode->c_iflag &= ~ISTRIP;
  746. mode->c_oflag &= ~OPOST;
  747. }
  748. } else if (info->name == raw || info->name == cooked) {
  749. if ((info->name[0] == 'r' && reversed)
  750. || (info->name[0] == 'c' && !reversed)) {
  751. /* Cooked mode */
  752. mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
  753. mode->c_oflag |= OPOST;
  754. mode->c_lflag |= ISIG | ICANON;
  755. #if VMIN == VEOF
  756. mode->c_cc[VEOF] = CEOF;
  757. #endif
  758. #if VTIME == VEOL
  759. mode->c_cc[VEOL] = CEOL;
  760. #endif
  761. } else {
  762. /* Raw mode */
  763. mode->c_iflag = 0;
  764. mode->c_oflag &= ~OPOST;
  765. mode->c_lflag &= ~(ISIG | ICANON | XCASE);
  766. mode->c_cc[VMIN] = 1;
  767. mode->c_cc[VTIME] = 0;
  768. }
  769. }
  770. else if (IXANY && info->name == decctlq) {
  771. if (reversed)
  772. mode->c_iflag |= IXANY;
  773. else
  774. mode->c_iflag &= ~IXANY;
  775. }
  776. else if (TABDLY && info->name == stty_tabs) {
  777. if (reversed)
  778. mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
  779. else
  780. mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
  781. }
  782. else if (OXTABS && info->name == stty_tabs) {
  783. if (reversed)
  784. mode->c_oflag |= OXTABS;
  785. else
  786. mode->c_oflag &= ~OXTABS;
  787. }
  788. else if (XCASE && IUCLC && OLCUC
  789. && (info->name == stty_lcase || info->name == stty_LCASE)) {
  790. if (reversed) {
  791. mode->c_lflag &= ~XCASE;
  792. mode->c_iflag &= ~IUCLC;
  793. mode->c_oflag &= ~OLCUC;
  794. } else {
  795. mode->c_lflag |= XCASE;
  796. mode->c_iflag |= IUCLC;
  797. mode->c_oflag |= OLCUC;
  798. }
  799. }
  800. else if (info->name == stty_crt) {
  801. mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
  802. }
  803. else if (info->name == stty_dec) {
  804. mode->c_cc[VINTR] = 3; /* ^C */
  805. mode->c_cc[VERASE] = 127; /* DEL */
  806. mode->c_cc[VKILL] = 21; /* ^U */
  807. mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
  808. if (IXANY) mode->c_iflag &= ~IXANY;
  809. }
  810. }
  811. static void set_control_char_or_die(const struct control_info *info,
  812. const char *arg, struct termios *mode)
  813. {
  814. unsigned char value;
  815. if (info->name == stty_min || info->name == stty_time)
  816. value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
  817. else if (arg[0] == '\0' || arg[1] == '\0')
  818. value = arg[0];
  819. else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
  820. value = _POSIX_VDISABLE;
  821. else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
  822. value = arg[1] & 0x1f; /* Non-letters get weird results */
  823. if (arg[1] == '?')
  824. value = 127;
  825. } else
  826. value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
  827. mode->c_cc[info->offset] = value;
  828. }
  829. #define STTY_require_set_attr (1<<0)
  830. #define STTY_speed_was_set (1<<1)
  831. #define STTY_verbose_output (1<<2)
  832. #define STTY_recoverable_output (1<<3)
  833. #define STTY_noargs (1<<4)
  834. int stty_main(int argc, char **argv)
  835. {
  836. struct termios mode;
  837. void (*output_func)(const struct termios *, const int);
  838. const char *file_name = NULL;
  839. int display_all = 0;
  840. int stty_state;
  841. int k;
  842. stty_state = STTY_noargs;
  843. output_func = do_display;
  844. /* First pass: only parse/verify command line params */
  845. k = 0;
  846. while (argv[++k]) {
  847. const struct mode_info *mp;
  848. const struct control_info *cp;
  849. const char *arg = argv[k];
  850. const char *argnext = argv[k+1];
  851. int param;
  852. if (arg[0] == '-') {
  853. int i;
  854. mp = find_mode(arg+1);
  855. if (mp) {
  856. if (!(mp->flags & REV))
  857. goto invalid_argument;
  858. stty_state &= ~STTY_noargs;
  859. continue;
  860. }
  861. /* It is an option - parse it */
  862. i = 0;
  863. while (arg[++i]) {
  864. switch (arg[i]) {
  865. case 'a':
  866. stty_state |= STTY_verbose_output;
  867. output_func = do_display;
  868. display_all = 1;
  869. break;
  870. case 'g':
  871. stty_state |= STTY_recoverable_output;
  872. output_func = display_recoverable;
  873. break;
  874. case 'F':
  875. if (file_name)
  876. bb_error_msg_and_die("only one device may be specified");
  877. file_name = &arg[i+1]; /* "-Fdevice" ? */
  878. if (!file_name[0]) { /* nope, "-F device" */
  879. int p = k+1; /* argv[p] is argnext */
  880. file_name = argnext;
  881. if (!file_name)
  882. bb_error_msg_and_die(bb_msg_requires_arg, "-F");
  883. /* remove -F param from arg[vc] */
  884. --argc;
  885. while (argv[p]) { argv[p] = argv[p+1]; ++p; }
  886. }
  887. goto end_option;
  888. default:
  889. goto invalid_argument;
  890. }
  891. }
  892. end_option:
  893. continue;
  894. }
  895. mp = find_mode(arg);
  896. if (mp) {
  897. stty_state &= ~STTY_noargs;
  898. continue;
  899. }
  900. cp = find_control(arg);
  901. if (cp) {
  902. if (!argnext)
  903. bb_error_msg_and_die(bb_msg_requires_arg, arg);
  904. /* called for the side effect of xfunc death only */
  905. set_control_char_or_die(cp, argnext, &mode);
  906. stty_state &= ~STTY_noargs;
  907. ++k;
  908. continue;
  909. }
  910. param = find_param(arg);
  911. if (param & param_need_arg) {
  912. if (!argnext)
  913. bb_error_msg_and_die(bb_msg_requires_arg, arg);
  914. ++k;
  915. }
  916. switch (param) {
  917. #ifdef HAVE_C_LINE
  918. case param_line:
  919. # ifndef TIOCGWINSZ
  920. xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
  921. break;
  922. # endif /* else fall-through */
  923. #endif
  924. #ifdef TIOCGWINSZ
  925. case param_rows:
  926. case param_cols:
  927. xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
  928. break;
  929. case param_size:
  930. #endif
  931. case param_speed:
  932. break;
  933. case param_ispeed:
  934. /* called for the side effect of xfunc death only */
  935. set_speed_or_die(input_speed, argnext, &mode);
  936. break;
  937. case param_ospeed:
  938. /* called for the side effect of xfunc death only */
  939. set_speed_or_die(output_speed, argnext, &mode);
  940. break;
  941. default:
  942. if (recover_mode(arg, &mode) == 1) break;
  943. if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
  944. invalid_argument:
  945. bb_error_msg_and_die("invalid argument '%s'", arg);
  946. }
  947. stty_state &= ~STTY_noargs;
  948. }
  949. /* Specifying both -a and -g is an error */
  950. if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
  951. (STTY_verbose_output | STTY_recoverable_output))
  952. bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
  953. /* Specifying -a or -g with non-options is an error */
  954. if (!(stty_state & STTY_noargs) &&
  955. (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
  956. bb_error_msg_and_die("modes may not be set when specifying an output style");
  957. /* Now it is safe to start doing things */
  958. if (file_name) {
  959. int fd, fdflags;
  960. device_name = file_name;
  961. fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
  962. if (fd != STDIN_FILENO) {
  963. dup2(fd, STDIN_FILENO);
  964. close(fd);
  965. }
  966. fdflags = fcntl(STDIN_FILENO, F_GETFL);
  967. if (fdflags < 0 ||
  968. fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
  969. perror_on_device_and_die("%s: cannot reset non-blocking mode");
  970. }
  971. /* Initialize to all zeroes so there is no risk memcmp will report a
  972. spurious difference in an uninitialized portion of the structure */
  973. memset(&mode, 0, sizeof(mode));
  974. if (tcgetattr(STDIN_FILENO, &mode))
  975. perror_on_device_and_die("%s");
  976. if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
  977. get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
  978. output_func(&mode, display_all);
  979. return EXIT_SUCCESS;
  980. }
  981. /* Second pass: perform actions */
  982. k = 0;
  983. while (argv[++k]) {
  984. const struct mode_info *mp;
  985. const struct control_info *cp;
  986. const char *arg = argv[k];
  987. const char *argnext = argv[k+1];
  988. int param;
  989. if (arg[0] == '-') {
  990. mp = find_mode(arg+1);
  991. if (mp) {
  992. set_mode(mp, 1 /* reversed */, &mode);
  993. stty_state |= STTY_require_set_attr;
  994. }
  995. /* It is an option - already parsed. Skip it */
  996. continue;
  997. }
  998. mp = find_mode(arg);
  999. if (mp) {
  1000. set_mode(mp, 0 /* non-reversed */, &mode);
  1001. stty_state |= STTY_require_set_attr;
  1002. continue;
  1003. }
  1004. cp = find_control(arg);
  1005. if (cp) {
  1006. ++k;
  1007. set_control_char_or_die(cp, argnext, &mode);
  1008. stty_state |= STTY_require_set_attr;
  1009. continue;
  1010. }
  1011. param = find_param(arg);
  1012. if (param & param_need_arg) {
  1013. ++k;
  1014. }
  1015. switch (param) {
  1016. #ifdef HAVE_C_LINE
  1017. case param_line:
  1018. mode.c_line = xatoul_sfx(argnext, stty_suffixes);
  1019. stty_state |= STTY_require_set_attr;
  1020. break;
  1021. #endif
  1022. #ifdef TIOCGWINSZ
  1023. case param_cols:
  1024. set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
  1025. break;
  1026. case param_size:
  1027. display_window_size(0);
  1028. break;
  1029. case param_rows:
  1030. set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
  1031. break;
  1032. #endif
  1033. case param_speed:
  1034. display_speed(&mode, 0);
  1035. break;
  1036. case param_ispeed:
  1037. set_speed_or_die(input_speed, argnext, &mode);
  1038. stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
  1039. break;
  1040. case param_ospeed:
  1041. set_speed_or_die(output_speed, argnext, &mode);
  1042. stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
  1043. break;
  1044. default:
  1045. if (recover_mode(arg, &mode) == 1)
  1046. stty_state |= STTY_require_set_attr;
  1047. else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
  1048. set_speed_or_die(both_speeds, arg, &mode);
  1049. stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
  1050. } /* else - impossible (caught in the first pass):
  1051. bb_error_msg_and_die("invalid argument '%s'", arg); */
  1052. }
  1053. }
  1054. if (stty_state & STTY_require_set_attr) {
  1055. struct termios new_mode;
  1056. if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
  1057. perror_on_device_and_die("%s");
  1058. /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
  1059. it performs *any* of the requested operations. This means it
  1060. can report 'success' when it has actually failed to perform
  1061. some proper subset of the requested operations. To detect
  1062. this partial failure, get the current terminal attributes and
  1063. compare them to the requested ones */
  1064. /* Initialize to all zeroes so there is no risk memcmp will report a
  1065. spurious difference in an uninitialized portion of the structure */
  1066. memset(&new_mode, 0, sizeof(new_mode));
  1067. if (tcgetattr(STDIN_FILENO, &new_mode))
  1068. perror_on_device_and_die("%s");
  1069. if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
  1070. #ifdef CIBAUD
  1071. /* SunOS 4.1.3 (at least) has the problem that after this sequence,
  1072. tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
  1073. sometimes (m1 != m2). The only difference is in the four bits
  1074. of the c_cflag field corresponding to the baud rate. To save
  1075. Sun users a little confusion, don't report an error if this
  1076. happens. But suppress the error only if we haven't tried to
  1077. set the baud rate explicitly -- otherwise we'd never give an
  1078. error for a true failure to set the baud rate */
  1079. new_mode.c_cflag &= (~CIBAUD);
  1080. if ((stty_state & STTY_speed_was_set)
  1081. || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
  1082. #endif
  1083. perror_on_device_and_die("%s: cannot perform all requested operations");
  1084. }
  1085. }
  1086. return EXIT_SUCCESS;
  1087. }