stty.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. * Copyright (c) 2021, Daniel Bertalan <dani@danielbertalan.dev>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #define __USE_MISC
  7. #define TTYDEFCHARS
  8. #include <AK/Array.h>
  9. #include <AK/Optional.h>
  10. #include <AK/Result.h>
  11. #include <AK/ScopeGuard.h>
  12. #include <AK/String.h>
  13. #include <AK/StringView.h>
  14. #include <AK/Vector.h>
  15. #include <ctype.h>
  16. #include <fcntl.h>
  17. #include <getopt.h>
  18. #include <stdio.h>
  19. #include <sys/ioctl.h>
  20. #include <sys/ttydefaults.h>
  21. #include <termios.h>
  22. #include <unistd.h>
  23. constexpr option long_options[] = {
  24. { "all", no_argument, 0, 'a' },
  25. { "save", no_argument, 0, 'g' },
  26. { "file", required_argument, 0, 'F' },
  27. { 0, 0, 0, 0 }
  28. };
  29. struct TermiosFlag {
  30. StringView name;
  31. tcflag_t value;
  32. tcflag_t mask;
  33. };
  34. struct BaudRate {
  35. speed_t speed;
  36. unsigned long numeric_value;
  37. };
  38. struct ControlCharacter {
  39. StringView name;
  40. unsigned index;
  41. };
  42. constexpr TermiosFlag all_iflags[] = {
  43. { "ignbrk", IGNBRK, IGNBRK },
  44. { "brkint", BRKINT, BRKINT },
  45. { "ignpar", IGNPAR, IGNPAR },
  46. { "parmer", PARMRK, PARMRK },
  47. { "inpck", INPCK, INPCK },
  48. { "istrip", ISTRIP, ISTRIP },
  49. { "inlcr", INLCR, INLCR },
  50. { "igncr", IGNCR, IGNCR },
  51. { "icrnl", ICRNL, ICRNL },
  52. { "iuclc", IUCLC, IUCLC },
  53. { "ixon", IXON, IXON },
  54. { "ixany", IXANY, IXANY },
  55. { "ixoff", IXOFF, IXOFF },
  56. { "imaxbel", IMAXBEL, IMAXBEL },
  57. { "iutf8", IUTF8, IUTF8 }
  58. };
  59. constexpr TermiosFlag all_oflags[] = {
  60. { "opost", OPOST, OPOST },
  61. { "olcuc", OLCUC, OPOST },
  62. { "onlcr", ONLCR, ONLCR },
  63. { "onlret", ONLRET, ONLRET },
  64. { "ofill", OFILL, OFILL },
  65. { "ofdel", OFDEL, OFDEL },
  66. };
  67. constexpr TermiosFlag all_cflags[] = {
  68. { "cs5", CS5, CSIZE },
  69. { "cs6", CS6, CSIZE },
  70. { "cs7", CS7, CSIZE },
  71. { "cs8", CS8, CSIZE },
  72. { "cstopb", CSTOPB, CSTOPB },
  73. { "cread", CREAD, CREAD },
  74. { "parenb", PARENB, PARENB },
  75. { "parodd", PARODD, PARODD },
  76. { "hupcl", HUPCL, HUPCL },
  77. { "clocal", CLOCAL, CLOCAL },
  78. };
  79. constexpr TermiosFlag all_lflags[] = {
  80. { "isig", ISIG, ISIG },
  81. { "icanon", ICANON, ICANON },
  82. { "echo", ECHO, ECHO },
  83. { "echoe", ECHOE, ECHOE },
  84. { "echok", ECHOK, ECHOK },
  85. { "echonl", ECHONL, ECHONL },
  86. { "noflsh", NOFLSH, NOFLSH },
  87. { "tostop", TOSTOP, TOSTOP },
  88. { "iexten", IEXTEN, IEXTEN }
  89. };
  90. constexpr BaudRate baud_rates[] = {
  91. { B0, 0 },
  92. { B50, 50 },
  93. { B75, 75 },
  94. { B110, 110 },
  95. { B134, 134 },
  96. { B150, 150 },
  97. { B200, 200 },
  98. { B300, 300 },
  99. { B600, 600 },
  100. { B1200, 1200 },
  101. { B1800, 1800 },
  102. { B2400, 2400 },
  103. { B4800, 4800 },
  104. { B9600, 9600 },
  105. { B19200, 19200 },
  106. { B38400, 38400 },
  107. { B57600, 57600 },
  108. { B115200, 115200 },
  109. { B230400, 230400 },
  110. { B460800, 460800 },
  111. { B500000, 500000 },
  112. { B576000, 576000 },
  113. { B921600, 921600 },
  114. { B1000000, 1000000 },
  115. { B1152000, 1152000 },
  116. { B1500000, 1500000 },
  117. { B2000000, 2000000 },
  118. { B2500000, 2500000 },
  119. { B3000000, 3000000 },
  120. { B3500000, 3500000 },
  121. { B4000000, 4000000 }
  122. };
  123. constexpr ControlCharacter control_characters[] = {
  124. { "intr", VINTR },
  125. { "quit", VQUIT },
  126. { "erase", VERASE },
  127. { "kill", VKILL },
  128. { "eof", VEOF },
  129. /* time and min are handled separately */
  130. { "swtc", VSWTC },
  131. { "start", VSTART },
  132. { "stop", VSTOP },
  133. { "susp", VSUSP },
  134. { "eol", VEOL },
  135. { "reprint", VREPRINT },
  136. { "discard", VDISCARD },
  137. { "werase", VWERASE },
  138. { "lnext", VLNEXT },
  139. { "eol2", VEOL2 }
  140. };
  141. Optional<speed_t> numeric_value_to_speed(unsigned long);
  142. Optional<unsigned long> speed_to_numeric_value(speed_t);
  143. void print_stty_readable(const termios&);
  144. void print_human_readable(const termios&, const winsize&, bool);
  145. Result<void, int> apply_stty_readable_modes(StringView, termios&);
  146. Result<void, int> apply_modes(size_t, char**, termios&, winsize&);
  147. Optional<speed_t> numeric_value_to_speed(unsigned long numeric_value)
  148. {
  149. for (auto rate : baud_rates) {
  150. if (rate.numeric_value == numeric_value)
  151. return rate.speed;
  152. }
  153. return {};
  154. }
  155. Optional<unsigned long> speed_to_numeric_value(speed_t speed)
  156. {
  157. for (auto rate : baud_rates) {
  158. if (rate.speed == speed)
  159. return rate.numeric_value;
  160. }
  161. return {};
  162. }
  163. void print_stty_readable(const termios& modes)
  164. {
  165. out("{:x}:{:x}:{:x}:{:x}", modes.c_iflag, modes.c_oflag, modes.c_cflag, modes.c_lflag);
  166. for (size_t i = 0; i < NCCS; ++i)
  167. out(":{:x}", modes.c_cc[i]);
  168. out(":{:x}:{:x}\n", modes.c_ispeed, modes.c_ospeed);
  169. }
  170. void print_human_readable(const termios& modes, const winsize& ws, bool verbose_mode)
  171. {
  172. auto print_speed = [&] {
  173. if (verbose_mode && modes.c_ispeed != modes.c_ospeed) {
  174. out("ispeed {} baud; ospeed {} baud;", speed_to_numeric_value(modes.c_ispeed).value(), speed_to_numeric_value(modes.c_ospeed).value());
  175. } else {
  176. out("speed {} baud;", speed_to_numeric_value(modes.c_ispeed).value());
  177. }
  178. };
  179. auto print_winsize = [&] {
  180. out("rows {}; columns {};", ws.ws_row, ws.ws_col);
  181. };
  182. auto escape_character = [&](u8 ch) {
  183. StringBuilder sb;
  184. if (ch <= 0x20) {
  185. sb.append("^");
  186. sb.append(ch + 0x40);
  187. } else if (ch == 0x7f) {
  188. sb.append("^?");
  189. } else {
  190. sb.append(ch);
  191. }
  192. return sb.to_string();
  193. };
  194. auto print_control_characters = [&] {
  195. bool first_in_line = true;
  196. for (auto cc : control_characters) {
  197. if (verbose_mode || modes.c_cc[cc.index] != ttydefchars[cc.index]) {
  198. out("{}{} = {};", (first_in_line) ? "" : " ", cc.name, escape_character(modes.c_cc[cc.index]));
  199. first_in_line = false;
  200. }
  201. }
  202. if (!first_in_line)
  203. out("\n");
  204. };
  205. auto print_flags_of_type = [&](const TermiosFlag flags[], size_t flag_count, tcflag_t field_value, tcflag_t field_default) {
  206. bool first_in_line = true;
  207. for (size_t i = 0; i < flag_count; ++i) {
  208. auto& flag = flags[i];
  209. if (verbose_mode || (field_value & flag.mask) != (field_default & flag.mask)) {
  210. bool set = (field_value & flag.mask) == flag.value;
  211. out("{}{}{}", first_in_line ? "" : " ", set ? "" : "-", flag.name);
  212. first_in_line = false;
  213. }
  214. }
  215. if (!first_in_line)
  216. out("\n");
  217. };
  218. auto print_flags = [&] {
  219. print_flags_of_type(all_cflags, sizeof(all_cflags) / sizeof(TermiosFlag), modes.c_cflag, TTYDEF_CFLAG);
  220. print_flags_of_type(all_oflags, sizeof(all_oflags) / sizeof(TermiosFlag), modes.c_oflag, TTYDEF_OFLAG);
  221. print_flags_of_type(all_iflags, sizeof(all_iflags) / sizeof(TermiosFlag), modes.c_iflag, TTYDEF_IFLAG);
  222. print_flags_of_type(all_lflags, sizeof(all_lflags) / sizeof(TermiosFlag), modes.c_lflag, TTYDEF_LFLAG);
  223. };
  224. print_speed();
  225. out(" ");
  226. print_winsize();
  227. out("\n");
  228. print_control_characters();
  229. print_flags();
  230. }
  231. Result<void, int> apply_stty_readable_modes(StringView mode_string, termios& t)
  232. {
  233. auto split = mode_string.split_view(':');
  234. if (split.size() != 4 + NCCS + 2) {
  235. warnln("Save string has an incorrect number of parameters");
  236. return 1;
  237. }
  238. auto parse_hex = [&](const StringView& v) {
  239. tcflag_t ret = 0;
  240. for (auto c : v) {
  241. c = tolower(c);
  242. ret *= 16;
  243. if (isdigit(c)) {
  244. ret += c - '0';
  245. } else {
  246. VERIFY(c >= 'a' && c <= 'f');
  247. ret += c - 'a';
  248. }
  249. }
  250. return ret;
  251. };
  252. t.c_iflag = parse_hex(split[0]);
  253. t.c_oflag = parse_hex(split[1]);
  254. t.c_cflag = parse_hex(split[2]);
  255. t.c_lflag = parse_hex(split[3]);
  256. for (size_t i = 0; i < NCCS; ++i) {
  257. t.c_cc[i] = (cc_t)parse_hex(split[4 + i]);
  258. }
  259. t.c_ispeed = parse_hex(split[4 + NCCS]);
  260. t.c_ospeed = parse_hex(split[4 + NCCS + 1]);
  261. return {};
  262. }
  263. Result<void, int> apply_modes(size_t parameter_count, char** raw_parameters, termios& t, winsize& w)
  264. {
  265. Vector<StringView> parameters;
  266. parameters.ensure_capacity(parameter_count);
  267. for (size_t i = 0; i < parameter_count; ++i)
  268. parameters.append(StringView(raw_parameters[i]));
  269. auto parse_baud = [&](size_t idx) -> Optional<speed_t> {
  270. auto maybe_numeric_value = parameters[idx].to_uint<uint32_t>();
  271. if (maybe_numeric_value.has_value())
  272. return numeric_value_to_speed(maybe_numeric_value.value());
  273. return {};
  274. };
  275. auto parse_number = [&](size_t idx) -> Optional<cc_t> {
  276. return parameters[idx].to_uint<cc_t>();
  277. };
  278. auto looks_like_stty_readable = [&](size_t idx) {
  279. bool contains_colon = false;
  280. for (auto c : parameters[idx]) {
  281. c = tolower(c);
  282. if (!isdigit(c) && !(c >= 'a' && c <= 'f') && c != ':')
  283. return false;
  284. if (c == ':')
  285. contains_colon = true;
  286. }
  287. return contains_colon;
  288. };
  289. auto parse_control_character = [&](size_t idx) -> Optional<cc_t> {
  290. VERIFY(!parameters[idx].is_empty());
  291. if (parameters[idx] == "^-" || parameters[idx] == "undef") {
  292. // FIXME: disabling characters is a bit wonky right now in TTY.
  293. // We should add the _POSIX_VDISABLE macro.
  294. return 0;
  295. } else if (parameters[idx][0] == '^' && parameters[idx].length() == 2) {
  296. return toupper(parameters[idx][1]) - 0x40;
  297. } else if (parameters[idx].starts_with("0x")) {
  298. cc_t value = 0;
  299. if (parameters[idx].length() == 2) {
  300. warnln("Invalid hexadecimal character code {}", parameters[idx]);
  301. return {};
  302. }
  303. for (size_t i = 2; i < parameters[idx].length(); ++i) {
  304. char ch = tolower(parameters[idx][i]);
  305. if (!isdigit(ch) && !(ch >= 'a' && ch <= 'f')) {
  306. warnln("Invalid hexadecimal character code {}", parameters[idx]);
  307. return {};
  308. }
  309. value = 16 * value + (isdigit(ch)) ? (ch - '0') : (ch - 'a');
  310. }
  311. return value;
  312. } else if (parameters[idx].starts_with("0")) {
  313. cc_t value = 0;
  314. for (size_t i = 1; i < parameters[idx].length(); ++i) {
  315. char ch = parameters[idx][i];
  316. if (!(ch >= '0' && ch <= '7')) {
  317. warnln("Invalid octal character code {}", parameters[idx]);
  318. return {};
  319. }
  320. value = 8 * value + (ch - '0');
  321. }
  322. return value;
  323. } else if (isdigit(parameters[idx][0])) {
  324. auto maybe_value = parameters[idx].to_uint<cc_t>();
  325. if (!maybe_value.has_value()) {
  326. warnln("Invalid decimal character code {}", parameters[idx]);
  327. return {};
  328. }
  329. return maybe_value.value();
  330. } else if (parameters[idx].length() == 1) {
  331. return parameters[idx][0];
  332. }
  333. warnln("Invalid control character {}", parameters[idx]);
  334. return {};
  335. };
  336. size_t parameter_idx = 0;
  337. auto parse_flag_or_char = [&]() -> Result<void, int> {
  338. if (parameters[parameter_idx][0] != '-') {
  339. if (parameters[parameter_idx] == "min") {
  340. auto maybe_number = parse_number(++parameter_idx);
  341. if (!maybe_number.has_value()) {
  342. warnln("Error parsing min: {} is not a number", parameters[parameter_idx]);
  343. return 1;
  344. }
  345. return {};
  346. } else if (parameters[parameter_idx] == "time") {
  347. auto maybe_number = parse_number(++parameter_idx);
  348. if (!maybe_number.has_value()) {
  349. warnln("Error parsing time: {} is not a number", parameters[parameter_idx]);
  350. return 1;
  351. }
  352. return {};
  353. } else {
  354. for (auto cc : control_characters) {
  355. if (cc.name == parameters[parameter_idx]) {
  356. if (parameter_idx == parameter_count - 1) {
  357. warnln("No control character specified for {}", cc.name);
  358. return 1;
  359. }
  360. auto maybe_control_character = parse_control_character(++parameter_idx);
  361. if (!maybe_control_character.has_value())
  362. return 1;
  363. t.c_cc[cc.index] = maybe_control_character.value();
  364. return {};
  365. }
  366. }
  367. }
  368. }
  369. // We fall through to here if what we are setting is not a control character.
  370. bool negate = false;
  371. if (parameters[parameter_idx][0] == '-') {
  372. negate = true;
  373. parameters[parameter_idx] = parameters[parameter_idx].substring_view(1);
  374. }
  375. auto perform_masking = [&](tcflag_t value, tcflag_t mask, tcflag_t& dest) {
  376. if (negate)
  377. dest &= ~mask;
  378. else
  379. dest = (dest & (~mask)) | value;
  380. };
  381. for (auto flag : all_iflags) {
  382. if (flag.name == parameters[parameter_idx]) {
  383. perform_masking(flag.value, flag.mask, t.c_iflag);
  384. return {};
  385. }
  386. }
  387. for (auto flag : all_oflags) {
  388. if (flag.name == parameters[parameter_idx]) {
  389. perform_masking(flag.value, flag.mask, t.c_oflag);
  390. return {};
  391. }
  392. }
  393. for (auto flag : all_cflags) {
  394. if (flag.name == parameters[parameter_idx]) {
  395. perform_masking(flag.value, flag.mask, t.c_cflag);
  396. return {};
  397. }
  398. }
  399. for (auto flag : all_lflags) {
  400. if (flag.name == parameters[parameter_idx]) {
  401. perform_masking(flag.value, flag.mask, t.c_lflag);
  402. return {};
  403. }
  404. }
  405. warnln("Invalid control flag or control character name {}", parameters[parameter_idx]);
  406. return 1;
  407. };
  408. while (parameter_idx < parameter_count) {
  409. if (looks_like_stty_readable(parameter_idx)) {
  410. auto maybe_error = apply_stty_readable_modes(parameters[parameter_idx], t);
  411. if (maybe_error.is_error())
  412. return maybe_error.error();
  413. } else if (isdigit(parameters[parameter_idx][0])) {
  414. auto new_baud = parse_baud(parameter_idx);
  415. if (!new_baud.has_value()) {
  416. warnln("Invalid baud rate {}", parameters[parameter_idx]);
  417. return 1;
  418. }
  419. t.c_ispeed = t.c_ospeed = new_baud.value();
  420. } else if (parameters[parameter_idx] == "ispeed") {
  421. if (parameter_idx == parameter_count - 1) {
  422. warnln("No baud rate specified for ispeed");
  423. return 1;
  424. }
  425. auto new_baud = parse_baud(++parameter_idx);
  426. if (!new_baud.has_value()) {
  427. warnln("Invalid input baud rate {}", parameters[parameter_idx]);
  428. return 1;
  429. }
  430. t.c_ispeed = new_baud.value();
  431. } else if (parameters[parameter_idx] == "ospeed") {
  432. if (parameter_idx == parameter_count - 1) {
  433. warnln("No baud rate specified for ospeed");
  434. return 1;
  435. }
  436. auto new_baud = parse_baud(++parameter_idx);
  437. if (!new_baud.has_value()) {
  438. warnln("Invalid output baud rate {}", parameters[parameter_idx]);
  439. return 1;
  440. }
  441. t.c_ospeed = new_baud.value();
  442. } else if (parameters[parameter_idx] == "columns" || parameters[parameter_idx] == "cols") {
  443. auto maybe_number = parse_number(++parameter_idx);
  444. if (!maybe_number.has_value()) {
  445. warnln("Invalid column count {}", parameters[parameter_idx]);
  446. return 1;
  447. }
  448. w.ws_col = maybe_number.value();
  449. } else if (parameters[parameter_idx] == "rows") {
  450. auto maybe_number = parse_number(++parameter_idx);
  451. if (!maybe_number.has_value()) {
  452. warnln("Invalid row count {}", parameters[parameter_idx]);
  453. return 1;
  454. }
  455. w.ws_row = maybe_number.value();
  456. } else if (parameters[parameter_idx] == "evenp" || parameters[parameter_idx] == "parity") {
  457. t.c_cflag &= ~(CSIZE | PARODD);
  458. t.c_cflag |= CS7 | PARENB;
  459. } else if (parameters[parameter_idx] == "oddp") {
  460. t.c_cflag &= ~(CSIZE);
  461. t.c_cflag |= CS7 | PARENB | PARODD;
  462. } else if (parameters[parameter_idx] == "-parity" || parameters[parameter_idx] == "-evenp" || parameters[parameter_idx] == "-oddp") {
  463. t.c_cflag &= ~(PARENB | CSIZE);
  464. t.c_cflag |= CS8;
  465. } else if (parameters[parameter_idx] == "raw") {
  466. cfmakeraw(&t);
  467. } else if (parameters[parameter_idx] == "nl") {
  468. t.c_iflag &= ~ICRNL;
  469. } else if (parameters[parameter_idx] == "-nl") {
  470. t.c_cflag &= ~(INLCR & IGNCR);
  471. t.c_iflag |= ICRNL;
  472. } else if (parameters[parameter_idx] == "ek") {
  473. t.c_cc[VERASE] = CERASE;
  474. t.c_cc[VKILL] = CKILL;
  475. } else if (parameters[parameter_idx] == "sane") {
  476. t.c_iflag = TTYDEF_IFLAG;
  477. t.c_oflag = TTYDEF_OFLAG;
  478. t.c_cflag = TTYDEF_CFLAG;
  479. t.c_lflag = TTYDEF_LFLAG;
  480. for (size_t i = 0; i < NCCS; ++i)
  481. t.c_cc[i] = ttydefchars[i];
  482. t.c_ispeed = t.c_ospeed = TTYDEF_SPEED;
  483. } else {
  484. auto maybe_error = parse_flag_or_char();
  485. if (maybe_error.is_error())
  486. return maybe_error.error();
  487. }
  488. ++parameter_idx;
  489. }
  490. return {};
  491. }
  492. int main(int argc, char** argv)
  493. {
  494. if (pledge("stdio tty rpath", nullptr) < 0) {
  495. perror("pledge");
  496. return 1;
  497. }
  498. if (unveil("/dev", "r") < 0) {
  499. perror("unveil");
  500. return 1;
  501. }
  502. if (unveil(nullptr, nullptr) < 0) {
  503. perror("unveil");
  504. return 1;
  505. }
  506. String device_file;
  507. bool stty_readable = false;
  508. bool all_settings = false;
  509. // Core::ArgsParser can't handle the weird syntax of stty, so we use getopt_long instead.
  510. opterr = 0; // We handle unknown flags gracefully by starting to parse the arguments in `apply_modes`.
  511. int optc;
  512. bool should_quit = false;
  513. while (!should_quit && ((optc = getopt_long(argc, argv, "-agF:", long_options, nullptr)) != -1)) {
  514. switch (optc) {
  515. case 'a':
  516. all_settings = true;
  517. break;
  518. case 'g':
  519. stty_readable = true;
  520. break;
  521. case 'F':
  522. if (!device_file.is_empty()) {
  523. warnln("Only one device may be specified");
  524. exit(1);
  525. }
  526. device_file = optarg;
  527. break;
  528. default:
  529. should_quit = true;
  530. break;
  531. }
  532. }
  533. if (stty_readable && all_settings) {
  534. warnln("Save mode and all-settings mode are mutually exclusive");
  535. exit(1);
  536. }
  537. int terminal_fd = STDIN_FILENO;
  538. if (!device_file.is_empty()) {
  539. if ((terminal_fd = open(device_file.characters(), O_RDONLY, 0)) < 0) {
  540. perror("open");
  541. exit(1);
  542. }
  543. }
  544. ScopeGuard file_close_guard = [&] { close(terminal_fd); };
  545. termios initial_termios;
  546. if (tcgetattr(terminal_fd, &initial_termios) < 0) {
  547. perror("tcgetattr");
  548. exit(1);
  549. }
  550. winsize initial_winsize;
  551. if (ioctl(terminal_fd, TIOCGWINSZ, &initial_winsize) < 0) {
  552. perror("ioctl(TIOCGWINSZ)");
  553. exit(1);
  554. }
  555. if (optind < argc) {
  556. if (stty_readable || all_settings) {
  557. warnln("Modes cannot be set when printing settings");
  558. exit(1);
  559. }
  560. auto result = apply_modes(argc - optind, argv + optind, initial_termios, initial_winsize);
  561. if (result.is_error())
  562. return result.error();
  563. if (tcsetattr(terminal_fd, TCSADRAIN, &initial_termios) < 0) {
  564. perror("tcsetattr");
  565. exit(1);
  566. }
  567. if (ioctl(terminal_fd, TIOCSWINSZ, &initial_winsize) < 0) {
  568. perror("ioctl(TIOCSWINSZ)");
  569. exit(1);
  570. }
  571. } else if (stty_readable) {
  572. print_stty_readable(initial_termios);
  573. } else {
  574. print_human_readable(initial_termios, initial_winsize, all_settings);
  575. }
  576. return 0;
  577. }