blob: e818d579c6446e84563239ce7b5fc9de67fa5544
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 | |
5 | Licensed under GPLv2 or later, see file LICENSE in this source tree. |
6 | */ |
7 | /* Usage: stty [-ag] [-F device] [setting...] |
8 | |
9 | Options: |
10 | -a Write all current settings to stdout in human-readable form. |
11 | -g Write all current settings to stdout in stty-readable form. |
12 | -F Open and use the specified device instead of stdin |
13 | |
14 | If no args are given, write to stdout the baud rate and settings that |
15 | have been changed from their defaults. Mode reading and changes |
16 | are done on the specified device, or stdin if none was specified. |
17 | |
18 | David MacKenzie <djm@gnu.ai.mit.edu> |
19 | |
20 | Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001 |
21 | */ |
22 | //config:config STTY |
23 | //config: bool "stty" |
24 | //config: default y |
25 | //config: help |
26 | //config: stty is used to change and print terminal line settings. |
27 | |
28 | //applet:IF_STTY(APPLET(stty, BB_DIR_BIN, BB_SUID_DROP)) |
29 | |
30 | //kbuild:lib-$(CONFIG_STTY) += stty.o |
31 | |
32 | //usage:#define stty_trivial_usage |
33 | //usage: "[-a|g] [-F DEVICE] [SETTING]..." |
34 | //usage:#define stty_full_usage "\n\n" |
35 | //usage: "Without arguments, prints baud rate, line discipline,\n" |
36 | //usage: "and deviations from stty sane\n" |
37 | //usage: "\n -F DEVICE Open device instead of stdin" |
38 | //usage: "\n -a Print all current settings in human-readable form" |
39 | //usage: "\n -g Print in stty-readable form" |
40 | //usage: "\n [SETTING] See manpage" |
41 | |
42 | #include "libbb.h" |
43 | #include "common_bufsiz.h" |
44 | |
45 | #ifndef _POSIX_VDISABLE |
46 | # define _POSIX_VDISABLE ((unsigned char) 0) |
47 | #endif |
48 | |
49 | #define Control(c) ((c) & 0x1f) |
50 | /* Canonical values for control characters */ |
51 | #ifndef CINTR |
52 | # define CINTR Control('c') |
53 | #endif |
54 | #ifndef CQUIT |
55 | # define CQUIT 28 |
56 | #endif |
57 | #ifndef CERASE |
58 | # define CERASE 127 |
59 | #endif |
60 | #ifndef CKILL |
61 | # define CKILL Control('u') |
62 | #endif |
63 | #ifndef CEOF |
64 | # define CEOF Control('d') |
65 | #endif |
66 | #ifndef CEOL |
67 | # define CEOL _POSIX_VDISABLE |
68 | #endif |
69 | #ifndef CSTART |
70 | # define CSTART Control('q') |
71 | #endif |
72 | #ifndef CSTOP |
73 | # define CSTOP Control('s') |
74 | #endif |
75 | #ifndef CSUSP |
76 | # define CSUSP Control('z') |
77 | #endif |
78 | #if defined(VEOL2) && !defined(CEOL2) |
79 | # define CEOL2 _POSIX_VDISABLE |
80 | #endif |
81 | /* glibc-2.12.1 uses only VSWTC name */ |
82 | #if defined(VSWTC) && !defined(VSWTCH) |
83 | # define VSWTCH VSWTC |
84 | #endif |
85 | /* ISC renamed swtch to susp for termios, but we'll accept either name */ |
86 | #if defined(VSUSP) && !defined(VSWTCH) |
87 | # define VSWTCH VSUSP |
88 | # define CSWTCH CSUSP |
89 | #endif |
90 | #if defined(VSWTCH) && !defined(CSWTCH) |
91 | # define CSWTCH _POSIX_VDISABLE |
92 | #endif |
93 | |
94 | /* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'. |
95 | So the default is to disable 'swtch.' */ |
96 | #if defined(__sparc__) && defined(__svr4__) |
97 | # undef CSWTCH |
98 | # define CSWTCH _POSIX_VDISABLE |
99 | #endif |
100 | |
101 | #if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */ |
102 | # define VWERASE VWERSE |
103 | #endif |
104 | #if defined(VDSUSP) && !defined(CDSUSP) |
105 | # define CDSUSP Control('y') |
106 | #endif |
107 | #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */ |
108 | # define VREPRINT VRPRNT |
109 | #endif |
110 | #if defined(VREPRINT) && !defined(CRPRNT) |
111 | # define CRPRNT Control('r') |
112 | #endif |
113 | #if defined(VWERASE) && !defined(CWERASE) |
114 | # define CWERASE Control('w') |
115 | #endif |
116 | #if defined(VLNEXT) && !defined(CLNEXT) |
117 | # define CLNEXT Control('v') |
118 | #endif |
119 | #if defined(VDISCARD) && !defined(VFLUSHO) |
120 | # define VFLUSHO VDISCARD |
121 | #endif |
122 | #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */ |
123 | # define VFLUSHO VFLUSH |
124 | #endif |
125 | #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */ |
126 | # define ECHOCTL CTLECH |
127 | #endif |
128 | #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */ |
129 | # define ECHOCTL TCTLECH |
130 | #endif |
131 | #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */ |
132 | # define ECHOKE CRTKIL |
133 | #endif |
134 | #if defined(VFLUSHO) && !defined(CFLUSHO) |
135 | # define CFLUSHO Control('o') |
136 | #endif |
137 | #if defined(VSTATUS) && !defined(CSTATUS) |
138 | # define CSTATUS Control('t') |
139 | #endif |
140 | |
141 | /* Save us from #ifdef forest plague */ |
142 | #ifndef BSDLY |
143 | # define BSDLY 0 |
144 | #endif |
145 | #ifndef CIBAUD |
146 | # define CIBAUD 0 |
147 | #endif |
148 | #ifndef CRDLY |
149 | # define CRDLY 0 |
150 | #endif |
151 | #ifndef CRTSCTS |
152 | # define CRTSCTS 0 |
153 | #endif |
154 | #ifndef ECHOCTL |
155 | # define ECHOCTL 0 |
156 | #endif |
157 | #ifndef ECHOKE |
158 | # define ECHOKE 0 |
159 | #endif |
160 | #ifndef ECHOPRT |
161 | # define ECHOPRT 0 |
162 | #endif |
163 | #ifndef FFDLY |
164 | # define FFDLY 0 |
165 | #endif |
166 | #ifndef IEXTEN |
167 | # define IEXTEN 0 |
168 | #endif |
169 | #ifndef IMAXBEL |
170 | # define IMAXBEL 0 |
171 | #endif |
172 | #ifndef IUCLC |
173 | # define IUCLC 0 |
174 | #endif |
175 | #ifndef IXANY |
176 | # define IXANY 0 |
177 | #endif |
178 | #ifndef NLDLY |
179 | # define NLDLY 0 |
180 | #endif |
181 | #ifndef OCRNL |
182 | # define OCRNL 0 |
183 | #endif |
184 | #ifndef OFDEL |
185 | # define OFDEL 0 |
186 | #endif |
187 | #ifndef OFILL |
188 | # define OFILL 0 |
189 | #endif |
190 | #ifndef OLCUC |
191 | # define OLCUC 0 |
192 | #endif |
193 | #ifndef ONLCR |
194 | # define ONLCR 0 |
195 | #endif |
196 | #ifndef ONLRET |
197 | # define ONLRET 0 |
198 | #endif |
199 | #ifndef ONOCR |
200 | # define ONOCR 0 |
201 | #endif |
202 | #ifndef OXTABS |
203 | # define OXTABS 0 |
204 | #endif |
205 | #ifndef TABDLY |
206 | # define TABDLY 0 |
207 | #endif |
208 | #ifndef TAB1 |
209 | # define TAB1 0 |
210 | #endif |
211 | #ifndef TAB2 |
212 | # define TAB2 0 |
213 | #endif |
214 | #ifndef TOSTOP |
215 | # define TOSTOP 0 |
216 | #endif |
217 | #ifndef VDSUSP |
218 | # define VDSUSP 0 |
219 | #endif |
220 | #ifndef VEOL2 |
221 | # define VEOL2 0 |
222 | #endif |
223 | #ifndef VFLUSHO |
224 | # define VFLUSHO 0 |
225 | #endif |
226 | #ifndef VLNEXT |
227 | # define VLNEXT 0 |
228 | #endif |
229 | #ifndef VREPRINT |
230 | # define VREPRINT 0 |
231 | #endif |
232 | #ifndef VSTATUS |
233 | # define VSTATUS 0 |
234 | #endif |
235 | #ifndef VSWTCH |
236 | # define VSWTCH 0 |
237 | #endif |
238 | #ifndef VTDLY |
239 | # define VTDLY 0 |
240 | #endif |
241 | #ifndef VWERASE |
242 | # define VWERASE 0 |
243 | #endif |
244 | #ifndef XCASE |
245 | # define XCASE 0 |
246 | #endif |
247 | #ifndef IUTF8 |
248 | # define IUTF8 0 |
249 | #endif |
250 | |
251 | /* Which speeds to set */ |
252 | enum speed_setting { |
253 | input_speed, output_speed, both_speeds |
254 | }; |
255 | |
256 | /* Which member(s) of 'struct termios' a mode uses */ |
257 | enum { |
258 | control, input, output, local, combination |
259 | }; |
260 | static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode) |
261 | { |
262 | static const uint8_t tcflag_offsets[] ALIGN1 = { |
263 | offsetof(struct termios, c_cflag), /* control */ |
264 | offsetof(struct termios, c_iflag), /* input */ |
265 | offsetof(struct termios, c_oflag), /* output */ |
266 | offsetof(struct termios, c_lflag) /* local */ |
267 | }; |
268 | if (type <= local) { |
269 | return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); |
270 | } |
271 | return NULL; |
272 | } |
273 | |
274 | /* Flags for 'struct mode_info' */ |
275 | #define SANE_SET 1 /* Set in 'sane' mode */ |
276 | #define SANE_UNSET 2 /* Unset in 'sane' mode */ |
277 | #define REV 4 /* Can be turned off by prepending '-' */ |
278 | #define OMIT 8 /* Don't display value */ |
279 | |
280 | |
281 | /* Each mode. |
282 | * This structure should be kept as small as humanly possible. |
283 | */ |
284 | struct mode_info { |
285 | const uint8_t type; /* Which structure element to change */ |
286 | const uint8_t flags; /* Setting and display options */ |
287 | /* only these values are ever used, so... */ |
288 | #if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100 |
289 | const uint8_t mask; |
290 | #elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000 |
291 | const uint16_t mask; |
292 | #else |
293 | const tcflag_t mask; /* Other bits to turn off for this mode */ |
294 | #endif |
295 | /* was using short here, but ppc32 was unhappy */ |
296 | const tcflag_t bits; /* Bits to set for this mode */ |
297 | }; |
298 | |
299 | enum { |
300 | /* Must match mode_name[] and mode_info[] order! */ |
301 | IDX_evenp = 0, |
302 | IDX_parity, |
303 | IDX_oddp, |
304 | IDX_nl, |
305 | IDX_ek, |
306 | IDX_sane, |
307 | IDX_cooked, |
308 | IDX_raw, |
309 | IDX_pass8, |
310 | IDX_litout, |
311 | IDX_cbreak, |
312 | IDX_crt, |
313 | IDX_dec, |
314 | #if IXANY |
315 | IDX_decctlq, |
316 | #endif |
317 | #if TABDLY || OXTABS |
318 | IDX_tabs, |
319 | #endif |
320 | #if XCASE && IUCLC && OLCUC |
321 | IDX_lcase, |
322 | IDX_LCASE, |
323 | #endif |
324 | }; |
325 | |
326 | #define MI_ENTRY(N,T,F,B,M) N "\0" |
327 | |
328 | /* Mode names given on command line */ |
329 | static const char mode_name[] ALIGN1 = |
330 | MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) |
331 | MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) |
332 | MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) |
333 | MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) |
334 | MI_ENTRY("ek", combination, OMIT, 0, 0 ) |
335 | MI_ENTRY("sane", combination, OMIT, 0, 0 ) |
336 | MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) |
337 | MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) |
338 | MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) |
339 | MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) |
340 | MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) |
341 | MI_ENTRY("crt", combination, OMIT, 0, 0 ) |
342 | MI_ENTRY("dec", combination, OMIT, 0, 0 ) |
343 | #if IXANY |
344 | MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) |
345 | #endif |
346 | #if TABDLY || OXTABS |
347 | MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) |
348 | #endif |
349 | #if XCASE && IUCLC && OLCUC |
350 | MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) |
351 | MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) |
352 | #endif |
353 | MI_ENTRY("parenb", control, REV, PARENB, 0 ) |
354 | MI_ENTRY("parodd", control, REV, PARODD, 0 ) |
355 | MI_ENTRY("cs5", control, 0, CS5, CSIZE) |
356 | MI_ENTRY("cs6", control, 0, CS6, CSIZE) |
357 | MI_ENTRY("cs7", control, 0, CS7, CSIZE) |
358 | MI_ENTRY("cs8", control, 0, CS8, CSIZE) |
359 | MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) |
360 | MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) |
361 | MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) |
362 | MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) |
363 | MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) |
364 | #if CRTSCTS |
365 | MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) |
366 | #endif |
367 | MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) |
368 | MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) |
369 | MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) |
370 | MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) |
371 | MI_ENTRY("inpck", input, REV, INPCK, 0 ) |
372 | MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) |
373 | MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) |
374 | MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) |
375 | MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) |
376 | MI_ENTRY("ixon", input, REV, IXON, 0 ) |
377 | MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) |
378 | MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) |
379 | #if IUCLC |
380 | MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) |
381 | #endif |
382 | #if IXANY |
383 | MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) |
384 | #endif |
385 | #if IMAXBEL |
386 | MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) |
387 | #endif |
388 | #if IUTF8 |
389 | MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) |
390 | #endif |
391 | MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) |
392 | #if OLCUC |
393 | MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) |
394 | #endif |
395 | #if OCRNL |
396 | MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) |
397 | #endif |
398 | #if ONLCR |
399 | MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) |
400 | #endif |
401 | #if ONOCR |
402 | MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) |
403 | #endif |
404 | #if ONLRET |
405 | MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) |
406 | #endif |
407 | #if OFILL |
408 | MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) |
409 | #endif |
410 | #if OFDEL |
411 | MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) |
412 | #endif |
413 | #if NLDLY |
414 | MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) |
415 | MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) |
416 | #endif |
417 | #if CRDLY |
418 | MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) |
419 | MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) |
420 | MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) |
421 | MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) |
422 | #endif |
423 | |
424 | #if TABDLY |
425 | MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) |
426 | # if TAB2 |
427 | MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) |
428 | # endif |
429 | # if TAB1 |
430 | MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) |
431 | # endif |
432 | MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) |
433 | #else |
434 | # if OXTABS |
435 | MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) |
436 | # endif |
437 | #endif |
438 | |
439 | #if BSDLY |
440 | MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) |
441 | MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) |
442 | #endif |
443 | #if VTDLY |
444 | MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) |
445 | MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) |
446 | #endif |
447 | #if FFDLY |
448 | MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) |
449 | MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) |
450 | #endif |
451 | MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) |
452 | MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) |
453 | #if IEXTEN |
454 | MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) |
455 | #endif |
456 | MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) |
457 | MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) |
458 | MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) |
459 | MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) |
460 | MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) |
461 | MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) |
462 | #if XCASE |
463 | MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) |
464 | #endif |
465 | #if TOSTOP |
466 | MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) |
467 | #endif |
468 | #if ECHOPRT |
469 | MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) |
470 | MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) |
471 | #endif |
472 | #if ECHOCTL |
473 | MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) |
474 | MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) |
475 | #endif |
476 | #if ECHOKE |
477 | MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) |
478 | MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) |
479 | #endif |
480 | ; |
481 | |
482 | #undef MI_ENTRY |
483 | #define MI_ENTRY(N,T,F,B,M) { T, F, M, B }, |
484 | |
485 | static const struct mode_info mode_info[] = { |
486 | /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ |
487 | MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) |
488 | MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) |
489 | MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) |
490 | MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) |
491 | MI_ENTRY("ek", combination, OMIT, 0, 0 ) |
492 | MI_ENTRY("sane", combination, OMIT, 0, 0 ) |
493 | MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) |
494 | MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) |
495 | MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) |
496 | MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) |
497 | MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) |
498 | MI_ENTRY("crt", combination, OMIT, 0, 0 ) |
499 | MI_ENTRY("dec", combination, OMIT, 0, 0 ) |
500 | #if IXANY |
501 | MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) |
502 | #endif |
503 | #if TABDLY || OXTABS |
504 | MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) |
505 | #endif |
506 | #if XCASE && IUCLC && OLCUC |
507 | MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) |
508 | MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) |
509 | #endif |
510 | MI_ENTRY("parenb", control, REV, PARENB, 0 ) |
511 | MI_ENTRY("parodd", control, REV, PARODD, 0 ) |
512 | MI_ENTRY("cs5", control, 0, CS5, CSIZE) |
513 | MI_ENTRY("cs6", control, 0, CS6, CSIZE) |
514 | MI_ENTRY("cs7", control, 0, CS7, CSIZE) |
515 | MI_ENTRY("cs8", control, 0, CS8, CSIZE) |
516 | MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) |
517 | MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) |
518 | MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) |
519 | MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) |
520 | MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) |
521 | #if CRTSCTS |
522 | MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) |
523 | #endif |
524 | MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) |
525 | MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) |
526 | MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) |
527 | MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) |
528 | MI_ENTRY("inpck", input, REV, INPCK, 0 ) |
529 | MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) |
530 | MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) |
531 | MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) |
532 | MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) |
533 | MI_ENTRY("ixon", input, REV, IXON, 0 ) |
534 | MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) |
535 | MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) |
536 | #if IUCLC |
537 | MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) |
538 | #endif |
539 | #if IXANY |
540 | MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) |
541 | #endif |
542 | #if IMAXBEL |
543 | MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) |
544 | #endif |
545 | #if IUTF8 |
546 | MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) |
547 | #endif |
548 | MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) |
549 | #if OLCUC |
550 | MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) |
551 | #endif |
552 | #if OCRNL |
553 | MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) |
554 | #endif |
555 | #if ONLCR |
556 | MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) |
557 | #endif |
558 | #if ONOCR |
559 | MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) |
560 | #endif |
561 | #if ONLRET |
562 | MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) |
563 | #endif |
564 | #if OFILL |
565 | MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) |
566 | #endif |
567 | #if OFDEL |
568 | MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) |
569 | #endif |
570 | #if NLDLY |
571 | MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) |
572 | MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) |
573 | #endif |
574 | #if CRDLY |
575 | MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) |
576 | MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) |
577 | MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) |
578 | MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) |
579 | #endif |
580 | |
581 | #if TABDLY |
582 | MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) |
583 | # if TAB2 |
584 | MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) |
585 | # endif |
586 | # if TAB1 |
587 | MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) |
588 | # endif |
589 | MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) |
590 | #else |
591 | # if OXTABS |
592 | MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) |
593 | # endif |
594 | #endif |
595 | |
596 | #if BSDLY |
597 | MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) |
598 | MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) |
599 | #endif |
600 | #if VTDLY |
601 | MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) |
602 | MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) |
603 | #endif |
604 | #if FFDLY |
605 | MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) |
606 | MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) |
607 | #endif |
608 | MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) |
609 | MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) |
610 | #if IEXTEN |
611 | MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) |
612 | #endif |
613 | MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) |
614 | MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) |
615 | MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) |
616 | MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) |
617 | MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) |
618 | MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) |
619 | #if XCASE |
620 | MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) |
621 | #endif |
622 | #if TOSTOP |
623 | MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) |
624 | #endif |
625 | #if ECHOPRT |
626 | MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) |
627 | MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) |
628 | #endif |
629 | #if ECHOCTL |
630 | MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) |
631 | MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) |
632 | #endif |
633 | #if ECHOKE |
634 | MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) |
635 | MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) |
636 | #endif |
637 | }; |
638 | |
639 | enum { |
640 | NUM_mode_info = ARRAY_SIZE(mode_info) |
641 | }; |
642 | |
643 | |
644 | /* Control characters */ |
645 | struct control_info { |
646 | const uint8_t saneval; /* Value to set for 'stty sane' */ |
647 | const uint8_t offset; /* Offset in c_cc */ |
648 | }; |
649 | |
650 | enum { |
651 | /* Must match control_name[] and control_info[] order! */ |
652 | CIDX_intr = 0, |
653 | CIDX_quit, |
654 | CIDX_erase, |
655 | CIDX_kill, |
656 | CIDX_eof, |
657 | CIDX_eol, |
658 | #if VEOL2 |
659 | CIDX_eol2, |
660 | #endif |
661 | #if VSWTCH |
662 | CIDX_swtch, |
663 | #endif |
664 | CIDX_start, |
665 | CIDX_stop, |
666 | CIDX_susp, |
667 | #if VDSUSP |
668 | CIDX_dsusp, |
669 | #endif |
670 | #if VREPRINT |
671 | CIDX_rprnt, |
672 | #endif |
673 | #if VWERASE |
674 | CIDX_werase, |
675 | #endif |
676 | #if VLNEXT |
677 | CIDX_lnext, |
678 | #endif |
679 | #if VFLUSHO |
680 | CIDX_flush, |
681 | #endif |
682 | #if VSTATUS |
683 | CIDX_status, |
684 | #endif |
685 | CIDX_min, |
686 | CIDX_time, |
687 | }; |
688 | |
689 | #define CI_ENTRY(n,s,o) n "\0" |
690 | |
691 | /* Name given on command line */ |
692 | static const char control_name[] ALIGN1 = |
693 | CI_ENTRY("intr", CINTR, VINTR ) |
694 | CI_ENTRY("quit", CQUIT, VQUIT ) |
695 | CI_ENTRY("erase", CERASE, VERASE ) |
696 | CI_ENTRY("kill", CKILL, VKILL ) |
697 | CI_ENTRY("eof", CEOF, VEOF ) |
698 | CI_ENTRY("eol", CEOL, VEOL ) |
699 | #if VEOL2 |
700 | CI_ENTRY("eol2", CEOL2, VEOL2 ) |
701 | #endif |
702 | #if VSWTCH |
703 | CI_ENTRY("swtch", CSWTCH, VSWTCH ) |
704 | #endif |
705 | CI_ENTRY("start", CSTART, VSTART ) |
706 | CI_ENTRY("stop", CSTOP, VSTOP ) |
707 | CI_ENTRY("susp", CSUSP, VSUSP ) |
708 | #if VDSUSP |
709 | CI_ENTRY("dsusp", CDSUSP, VDSUSP ) |
710 | #endif |
711 | #if VREPRINT |
712 | CI_ENTRY("rprnt", CRPRNT, VREPRINT) |
713 | #endif |
714 | #if VWERASE |
715 | CI_ENTRY("werase", CWERASE, VWERASE ) |
716 | #endif |
717 | #if VLNEXT |
718 | CI_ENTRY("lnext", CLNEXT, VLNEXT ) |
719 | #endif |
720 | #if VFLUSHO |
721 | CI_ENTRY("flush", CFLUSHO, VFLUSHO ) |
722 | #endif |
723 | #if VSTATUS |
724 | CI_ENTRY("status", CSTATUS, VSTATUS ) |
725 | #endif |
726 | /* These must be last because of the display routines */ |
727 | CI_ENTRY("min", 1, VMIN ) |
728 | CI_ENTRY("time", 0, VTIME ) |
729 | ; |
730 | |
731 | #undef CI_ENTRY |
732 | #define CI_ENTRY(n,s,o) { s, o }, |
733 | |
734 | static const struct control_info control_info[] ALIGN2 = { |
735 | /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */ |
736 | CI_ENTRY("intr", CINTR, VINTR ) |
737 | CI_ENTRY("quit", CQUIT, VQUIT ) |
738 | CI_ENTRY("erase", CERASE, VERASE ) |
739 | CI_ENTRY("kill", CKILL, VKILL ) |
740 | CI_ENTRY("eof", CEOF, VEOF ) |
741 | CI_ENTRY("eol", CEOL, VEOL ) |
742 | #if VEOL2 |
743 | CI_ENTRY("eol2", CEOL2, VEOL2 ) |
744 | #endif |
745 | #if VSWTCH |
746 | CI_ENTRY("swtch", CSWTCH, VSWTCH ) |
747 | #endif |
748 | CI_ENTRY("start", CSTART, VSTART ) |
749 | CI_ENTRY("stop", CSTOP, VSTOP ) |
750 | CI_ENTRY("susp", CSUSP, VSUSP ) |
751 | #if VDSUSP |
752 | CI_ENTRY("dsusp", CDSUSP, VDSUSP ) |
753 | #endif |
754 | #if VREPRINT |
755 | CI_ENTRY("rprnt", CRPRNT, VREPRINT) |
756 | #endif |
757 | #if VWERASE |
758 | CI_ENTRY("werase", CWERASE, VWERASE ) |
759 | #endif |
760 | #if VLNEXT |
761 | CI_ENTRY("lnext", CLNEXT, VLNEXT ) |
762 | #endif |
763 | #if VFLUSHO |
764 | CI_ENTRY("flush", CFLUSHO, VFLUSHO ) |
765 | #endif |
766 | #if VSTATUS |
767 | CI_ENTRY("status", CSTATUS, VSTATUS ) |
768 | #endif |
769 | /* These must be last because of the display routines */ |
770 | CI_ENTRY("min", 1, VMIN ) |
771 | CI_ENTRY("time", 0, VTIME ) |
772 | }; |
773 | |
774 | enum { |
775 | NUM_control_info = ARRAY_SIZE(control_info) |
776 | }; |
777 | |
778 | |
779 | struct globals { |
780 | const char *device_name; |
781 | /* The width of the screen, for output wrapping */ |
782 | unsigned max_col; |
783 | /* Current position, to know when to wrap */ |
784 | unsigned current_col; |
785 | char buf[10]; |
786 | } FIX_ALIASING; |
787 | #define G (*(struct globals*)bb_common_bufsiz1) |
788 | #define INIT_G() do { \ |
789 | G.device_name = bb_msg_standard_input; \ |
790 | G.max_col = 80; \ |
791 | } while (0) |
792 | |
793 | static void set_speed_or_die(enum speed_setting type, const char *arg, |
794 | struct termios *mode) |
795 | { |
796 | speed_t baud; |
797 | |
798 | baud = tty_value_to_baud(xatou(arg)); |
799 | |
800 | if (type != output_speed) { /* either input or both */ |
801 | cfsetispeed(mode, baud); |
802 | } |
803 | if (type != input_speed) { /* either output or both */ |
804 | cfsetospeed(mode, baud); |
805 | } |
806 | } |
807 | |
808 | static NORETURN void perror_on_device_and_die(const char *fmt) |
809 | { |
810 | bb_perror_msg_and_die(fmt, G.device_name); |
811 | } |
812 | |
813 | static void perror_on_device(const char *fmt) |
814 | { |
815 | bb_perror_msg(fmt, G.device_name); |
816 | } |
817 | |
818 | /* Print format string MESSAGE and optional args. |
819 | Wrap to next line first if it won't fit. |
820 | Print a space first unless MESSAGE will start a new line */ |
821 | static void wrapf(const char *message, ...) |
822 | { |
823 | char buf[128]; |
824 | va_list args; |
825 | unsigned buflen; |
826 | |
827 | va_start(args, message); |
828 | buflen = vsnprintf(buf, sizeof(buf), message, args); |
829 | va_end(args); |
830 | /* We seem to be called only with suitable lengths, but check if |
831 | somebody failed to adhere to this assumption just to be sure. */ |
832 | if (!buflen || buflen >= sizeof(buf)) return; |
833 | |
834 | if (G.current_col > 0) { |
835 | G.current_col++; |
836 | if (buf[0] != '\n') { |
837 | if (G.current_col + buflen >= G.max_col) { |
838 | bb_putchar('\n'); |
839 | G.current_col = 0; |
840 | } else |
841 | bb_putchar(' '); |
842 | } |
843 | } |
844 | fputs(buf, stdout); |
845 | G.current_col += buflen; |
846 | if (buf[buflen-1] == '\n') |
847 | G.current_col = 0; |
848 | } |
849 | |
850 | static void newline(void) |
851 | { |
852 | if (G.current_col != 0) |
853 | wrapf("\n"); |
854 | } |
855 | |
856 | #ifdef TIOCGWINSZ |
857 | static void set_window_size(int rows, int cols) |
858 | { |
859 | struct winsize win = { 0, 0, 0, 0 }; |
860 | |
861 | if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) { |
862 | if (errno != EINVAL) { |
863 | goto bail; |
864 | } |
865 | memset(&win, 0, sizeof(win)); |
866 | } |
867 | |
868 | if (rows >= 0) |
869 | win.ws_row = rows; |
870 | if (cols >= 0) |
871 | win.ws_col = cols; |
872 | |
873 | if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win)) |
874 | bail: |
875 | perror_on_device("%s"); |
876 | } |
877 | #endif |
878 | |
879 | static void display_window_size(int fancy) |
880 | { |
881 | const char *fmt_str = "%s\0%s: no size information for this device"; |
882 | unsigned width, height; |
883 | |
884 | if (get_terminal_width_height(STDIN_FILENO, &width, &height)) { |
885 | if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) { |
886 | perror_on_device(fmt_str); |
887 | } |
888 | } else { |
889 | wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n", |
890 | height, width); |
891 | } |
892 | } |
893 | |
894 | static const struct suffix_mult stty_suffixes[] = { |
895 | { "b", 512 }, |
896 | { "k", 1024 }, |
897 | { "B", 1024 }, |
898 | { "", 0 } |
899 | }; |
900 | |
901 | static const struct mode_info *find_mode(const char *name) |
902 | { |
903 | int i = index_in_strings(mode_name, name); |
904 | return i >= 0 ? &mode_info[i] : NULL; |
905 | } |
906 | |
907 | static const struct control_info *find_control(const char *name) |
908 | { |
909 | int i = index_in_strings(control_name, name); |
910 | return i >= 0 ? &control_info[i] : NULL; |
911 | } |
912 | |
913 | enum { |
914 | param_need_arg = 0x80, |
915 | param_line = 1 | 0x80, |
916 | param_rows = 2 | 0x80, |
917 | param_cols = 3 | 0x80, |
918 | param_columns = 4 | 0x80, |
919 | param_size = 5, |
920 | param_speed = 6, |
921 | param_ispeed = 7 | 0x80, |
922 | param_ospeed = 8 | 0x80, |
923 | }; |
924 | |
925 | static int find_param(const char *name) |
926 | { |
927 | static const char params[] ALIGN1 = |
928 | "line\0" /* 1 */ |
929 | "rows\0" /* 2 */ |
930 | "cols\0" /* 3 */ |
931 | "columns\0" /* 4 */ |
932 | "size\0" /* 5 */ |
933 | "speed\0" /* 6 */ |
934 | "ispeed\0" |
935 | "ospeed\0"; |
936 | int i = index_in_strings(params, name) + 1; |
937 | if (i == 0) |
938 | return 0; |
939 | if (i != 5 && i != 6) |
940 | i |= 0x80; |
941 | return i; |
942 | } |
943 | |
944 | static int recover_mode(const char *arg, struct termios *mode) |
945 | { |
946 | int i, n; |
947 | unsigned chr; |
948 | unsigned long iflag, oflag, cflag, lflag; |
949 | |
950 | /* Scan into temporaries since it is too much trouble to figure out |
951 | the right format for 'tcflag_t' */ |
952 | if (sscanf(arg, "%lx:%lx:%lx:%lx%n", |
953 | &iflag, &oflag, &cflag, &lflag, &n) != 4) |
954 | return 0; |
955 | mode->c_iflag = iflag; |
956 | mode->c_oflag = oflag; |
957 | mode->c_cflag = cflag; |
958 | mode->c_lflag = lflag; |
959 | arg += n; |
960 | for (i = 0; i < NCCS; ++i) { |
961 | if (sscanf(arg, ":%x%n", &chr, &n) != 1) |
962 | return 0; |
963 | mode->c_cc[i] = chr; |
964 | arg += n; |
965 | } |
966 | |
967 | /* Fail if there are too many fields */ |
968 | if (*arg != '\0') |
969 | return 0; |
970 | |
971 | return 1; |
972 | } |
973 | |
974 | static void display_recoverable(const struct termios *mode, |
975 | int UNUSED_PARAM dummy) |
976 | { |
977 | int i; |
978 | printf("%lx:%lx:%lx:%lx", |
979 | (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag, |
980 | (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag); |
981 | for (i = 0; i < NCCS; ++i) |
982 | printf(":%x", (unsigned int) mode->c_cc[i]); |
983 | bb_putchar('\n'); |
984 | } |
985 | |
986 | static void display_speed(const struct termios *mode, int fancy) |
987 | { |
988 | //____________________ 01234567 8 9 |
989 | const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;"; |
990 | unsigned long ispeed, ospeed; |
991 | |
992 | ispeed = cfgetispeed(mode); |
993 | ospeed = cfgetospeed(mode); |
994 | if (ispeed == 0 || ispeed == ospeed) { |
995 | ispeed = ospeed; /* in case ispeed was 0 */ |
996 | //________ 0123 4 5 6 7 8 9 |
997 | fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;"; |
998 | } |
999 | if (fancy) fmt_str += 9; |
1000 | wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); |
1001 | } |
1002 | |
1003 | static void do_display(const struct termios *mode, int all) |
1004 | { |
1005 | int i; |
1006 | tcflag_t *bitsp; |
1007 | unsigned long mask; |
1008 | int prev_type = control; |
1009 | |
1010 | display_speed(mode, 1); |
1011 | if (all) |
1012 | display_window_size(1); |
1013 | #ifdef __linux__ |
1014 | wrapf("line = %u;\n", mode->c_line); |
1015 | #else |
1016 | newline(); |
1017 | #endif |
1018 | |
1019 | for (i = 0; i != CIDX_min; ++i) { |
1020 | char ch; |
1021 | /* If swtch is the same as susp, don't print both */ |
1022 | #if VSWTCH == VSUSP |
1023 | if (i == CIDX_swtch) |
1024 | continue; |
1025 | #endif |
1026 | /* If eof uses the same slot as min, only print whichever applies */ |
1027 | #if VEOF == VMIN |
1028 | if (!(mode->c_lflag & ICANON) |
1029 | && (i == CIDX_eof || i == CIDX_eol) |
1030 | ) { |
1031 | continue; |
1032 | } |
1033 | #endif |
1034 | ch = mode->c_cc[control_info[i].offset]; |
1035 | if (ch == _POSIX_VDISABLE) |
1036 | strcpy(G.buf, "<undef>"); |
1037 | else |
1038 | visible(ch, G.buf, 0); |
1039 | wrapf("%s = %s;", nth_string(control_name, i), G.buf); |
1040 | } |
1041 | #if VEOF == VMIN |
1042 | if ((mode->c_lflag & ICANON) == 0) |
1043 | #endif |
1044 | wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]); |
1045 | newline(); |
1046 | |
1047 | for (i = 0; i < NUM_mode_info; ++i) { |
1048 | if (mode_info[i].flags & OMIT) |
1049 | continue; |
1050 | if (mode_info[i].type != prev_type) { |
1051 | newline(); |
1052 | prev_type = mode_info[i].type; |
1053 | } |
1054 | |
1055 | bitsp = get_ptr_to_tcflag(mode_info[i].type, mode); |
1056 | mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; |
1057 | if ((*bitsp & mask) == mode_info[i].bits) { |
1058 | if (all || (mode_info[i].flags & SANE_UNSET)) |
1059 | wrapf("-%s"+1, nth_string(mode_name, i)); |
1060 | } else { |
1061 | if ((all && mode_info[i].flags & REV) |
1062 | || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) |
1063 | ) { |
1064 | wrapf("-%s", nth_string(mode_name, i)); |
1065 | } |
1066 | } |
1067 | } |
1068 | newline(); |
1069 | } |
1070 | |
1071 | static void sane_mode(struct termios *mode) |
1072 | { |
1073 | int i; |
1074 | |
1075 | for (i = 0; i < NUM_control_info; ++i) { |
1076 | #if VMIN == VEOF |
1077 | if (i == CIDX_min) |
1078 | break; |
1079 | #endif |
1080 | mode->c_cc[control_info[i].offset] = control_info[i].saneval; |
1081 | } |
1082 | |
1083 | for (i = 0; i < NUM_mode_info; ++i) { |
1084 | tcflag_t val; |
1085 | tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode); |
1086 | |
1087 | if (!bitsp) |
1088 | continue; |
1089 | val = *bitsp & ~((unsigned long)mode_info[i].mask); |
1090 | if (mode_info[i].flags & SANE_SET) { |
1091 | *bitsp = val | mode_info[i].bits; |
1092 | } else |
1093 | if (mode_info[i].flags & SANE_UNSET) { |
1094 | *bitsp = val & ~mode_info[i].bits; |
1095 | } |
1096 | } |
1097 | } |
1098 | |
1099 | static void set_mode(const struct mode_info *info, int reversed, |
1100 | struct termios *mode) |
1101 | { |
1102 | tcflag_t *bitsp; |
1103 | |
1104 | bitsp = get_ptr_to_tcflag(info->type, mode); |
1105 | |
1106 | if (bitsp) { |
1107 | tcflag_t val = *bitsp & ~info->mask; |
1108 | if (reversed) |
1109 | *bitsp = val & ~info->bits; |
1110 | else |
1111 | *bitsp = val | info->bits; |
1112 | return; |
1113 | } |
1114 | |
1115 | /* !bitsp - it's a "combination" mode */ |
1116 | if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { |
1117 | if (reversed) |
1118 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; |
1119 | else |
1120 | mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7; |
1121 | } else if (info == &mode_info[IDX_oddp]) { |
1122 | if (reversed) |
1123 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; |
1124 | else |
1125 | mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB; |
1126 | } else if (info == &mode_info[IDX_nl]) { |
1127 | if (reversed) { |
1128 | mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR; |
1129 | mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET; |
1130 | } else { |
1131 | mode->c_iflag = mode->c_iflag & ~ICRNL; |
1132 | if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR; |
1133 | } |
1134 | } else if (info == &mode_info[IDX_ek]) { |
1135 | mode->c_cc[VERASE] = CERASE; |
1136 | mode->c_cc[VKILL] = CKILL; |
1137 | } else if (info == &mode_info[IDX_sane]) { |
1138 | sane_mode(mode); |
1139 | } else if (info == &mode_info[IDX_cbreak]) { |
1140 | if (reversed) |
1141 | mode->c_lflag |= ICANON; |
1142 | else |
1143 | mode->c_lflag &= ~ICANON; |
1144 | } else if (info == &mode_info[IDX_pass8]) { |
1145 | if (reversed) { |
1146 | mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; |
1147 | mode->c_iflag |= ISTRIP; |
1148 | } else { |
1149 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; |
1150 | mode->c_iflag &= ~ISTRIP; |
1151 | } |
1152 | } else if (info == &mode_info[IDX_litout]) { |
1153 | if (reversed) { |
1154 | mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; |
1155 | mode->c_iflag |= ISTRIP; |
1156 | mode->c_oflag |= OPOST; |
1157 | } else { |
1158 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; |
1159 | mode->c_iflag &= ~ISTRIP; |
1160 | mode->c_oflag &= ~OPOST; |
1161 | } |
1162 | } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { |
1163 | if ((info == &mode_info[IDX_raw] && reversed) |
1164 | || (info == &mode_info[IDX_cooked] && !reversed) |
1165 | ) { |
1166 | /* Cooked mode */ |
1167 | mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; |
1168 | mode->c_oflag |= OPOST; |
1169 | mode->c_lflag |= ISIG | ICANON; |
1170 | #if VMIN == VEOF |
1171 | mode->c_cc[VEOF] = CEOF; |
1172 | #endif |
1173 | #if VTIME == VEOL |
1174 | mode->c_cc[VEOL] = CEOL; |
1175 | #endif |
1176 | } else { |
1177 | /* Raw mode */ |
1178 | mode->c_iflag = 0; |
1179 | mode->c_oflag &= ~OPOST; |
1180 | mode->c_lflag &= ~(ISIG | ICANON | XCASE); |
1181 | mode->c_cc[VMIN] = 1; |
1182 | mode->c_cc[VTIME] = 0; |
1183 | } |
1184 | } |
1185 | #if IXANY |
1186 | else if (info == &mode_info[IDX_decctlq]) { |
1187 | if (reversed) |
1188 | mode->c_iflag |= IXANY; |
1189 | else |
1190 | mode->c_iflag &= ~IXANY; |
1191 | } |
1192 | #endif |
1193 | #if TABDLY |
1194 | else if (info == &mode_info[IDX_tabs]) { |
1195 | if (reversed) |
1196 | mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; |
1197 | else |
1198 | mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; |
1199 | } |
1200 | #endif |
1201 | #if OXTABS |
1202 | else if (info == &mode_info[IDX_tabs]) { |
1203 | if (reversed) |
1204 | mode->c_oflag |= OXTABS; |
1205 | else |
1206 | mode->c_oflag &= ~OXTABS; |
1207 | } |
1208 | #endif |
1209 | #if XCASE && IUCLC && OLCUC |
1210 | else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) { |
1211 | if (reversed) { |
1212 | mode->c_lflag &= ~XCASE; |
1213 | mode->c_iflag &= ~IUCLC; |
1214 | mode->c_oflag &= ~OLCUC; |
1215 | } else { |
1216 | mode->c_lflag |= XCASE; |
1217 | mode->c_iflag |= IUCLC; |
1218 | mode->c_oflag |= OLCUC; |
1219 | } |
1220 | } |
1221 | #endif |
1222 | else if (info == &mode_info[IDX_crt]) { |
1223 | mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; |
1224 | } else if (info == &mode_info[IDX_dec]) { |
1225 | mode->c_cc[VINTR] = 3; /* ^C */ |
1226 | mode->c_cc[VERASE] = 127; /* DEL */ |
1227 | mode->c_cc[VKILL] = 21; /* ^U */ |
1228 | mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; |
1229 | if (IXANY) mode->c_iflag &= ~IXANY; |
1230 | } |
1231 | } |
1232 | |
1233 | static void set_control_char_or_die(const struct control_info *info, |
1234 | const char *arg, struct termios *mode) |
1235 | { |
1236 | unsigned char value; |
1237 | |
1238 | if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time]) |
1239 | value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); |
1240 | else if (arg[0] == '\0' || arg[1] == '\0') |
1241 | value = arg[0]; |
1242 | else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0) |
1243 | value = _POSIX_VDISABLE; |
1244 | else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */ |
1245 | value = arg[1] & 0x1f; /* Non-letters get weird results */ |
1246 | if (arg[1] == '?') |
1247 | value = 127; |
1248 | } else |
1249 | value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); |
1250 | mode->c_cc[info->offset] = value; |
1251 | } |
1252 | |
1253 | #define STTY_require_set_attr (1 << 0) |
1254 | #define STTY_speed_was_set (1 << 1) |
1255 | #define STTY_verbose_output (1 << 2) |
1256 | #define STTY_recoverable_output (1 << 3) |
1257 | #define STTY_noargs (1 << 4) |
1258 | |
1259 | int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1260 | int stty_main(int argc UNUSED_PARAM, char **argv) |
1261 | { |
1262 | struct termios mode; |
1263 | void (*output_func)(const struct termios *, int); |
1264 | const char *file_name = NULL; |
1265 | int display_all = 0; |
1266 | int stty_state; |
1267 | int k; |
1268 | |
1269 | INIT_G(); |
1270 | |
1271 | stty_state = STTY_noargs; |
1272 | output_func = do_display; |
1273 | |
1274 | /* First pass: only parse/verify command line params */ |
1275 | k = 0; |
1276 | while (argv[++k]) { |
1277 | const struct mode_info *mp; |
1278 | const struct control_info *cp; |
1279 | const char *arg = argv[k]; |
1280 | const char *argnext = argv[k+1]; |
1281 | int param; |
1282 | |
1283 | if (arg[0] == '-') { |
1284 | int i; |
1285 | mp = find_mode(arg+1); |
1286 | if (mp) { |
1287 | if (!(mp->flags & REV)) |
1288 | goto invalid_argument; |
1289 | stty_state &= ~STTY_noargs; |
1290 | continue; |
1291 | } |
1292 | /* It is an option - parse it */ |
1293 | i = 0; |
1294 | while (arg[++i]) { |
1295 | switch (arg[i]) { |
1296 | case 'a': |
1297 | stty_state |= STTY_verbose_output; |
1298 | output_func = do_display; |
1299 | display_all = 1; |
1300 | break; |
1301 | case 'g': |
1302 | stty_state |= STTY_recoverable_output; |
1303 | output_func = display_recoverable; |
1304 | break; |
1305 | case 'F': |
1306 | if (file_name) |
1307 | bb_error_msg_and_die("only one device may be specified"); |
1308 | file_name = &arg[i+1]; /* "-Fdevice" ? */ |
1309 | if (!file_name[0]) { /* nope, "-F device" */ |
1310 | int p = k+1; /* argv[p] is argnext */ |
1311 | file_name = argnext; |
1312 | if (!file_name) |
1313 | bb_error_msg_and_die(bb_msg_requires_arg, "-F"); |
1314 | /* remove -F param from arg[vc] */ |
1315 | while (argv[p]) { |
1316 | argv[p] = argv[p+1]; |
1317 | ++p; |
1318 | } |
1319 | } |
1320 | goto end_option; |
1321 | default: |
1322 | goto invalid_argument; |
1323 | } |
1324 | } |
1325 | end_option: |
1326 | continue; |
1327 | } |
1328 | |
1329 | mp = find_mode(arg); |
1330 | if (mp) { |
1331 | stty_state &= ~STTY_noargs; |
1332 | continue; |
1333 | } |
1334 | |
1335 | cp = find_control(arg); |
1336 | if (cp) { |
1337 | if (!argnext) |
1338 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
1339 | /* called for the side effect of xfunc death only */ |
1340 | set_control_char_or_die(cp, argnext, &mode); |
1341 | stty_state &= ~STTY_noargs; |
1342 | ++k; |
1343 | continue; |
1344 | } |
1345 | |
1346 | param = find_param(arg); |
1347 | if (param & param_need_arg) { |
1348 | if (!argnext) |
1349 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
1350 | ++k; |
1351 | } |
1352 | |
1353 | switch (param) { |
1354 | #ifdef __linux__ |
1355 | case param_line: |
1356 | # ifndef TIOCGWINSZ |
1357 | xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); |
1358 | break; |
1359 | # endif /* else fall-through */ |
1360 | #endif |
1361 | #ifdef TIOCGWINSZ |
1362 | case param_rows: |
1363 | case param_cols: |
1364 | case param_columns: |
1365 | xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); |
1366 | break; |
1367 | case param_size: |
1368 | #endif |
1369 | case param_speed: |
1370 | break; |
1371 | case param_ispeed: |
1372 | /* called for the side effect of xfunc death only */ |
1373 | set_speed_or_die(input_speed, argnext, &mode); |
1374 | break; |
1375 | case param_ospeed: |
1376 | /* called for the side effect of xfunc death only */ |
1377 | set_speed_or_die(output_speed, argnext, &mode); |
1378 | break; |
1379 | default: |
1380 | if (recover_mode(arg, &mode) == 1) break; |
1381 | if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; |
1382 | invalid_argument: |
1383 | bb_error_msg_and_die("invalid argument '%s'", arg); |
1384 | } |
1385 | stty_state &= ~STTY_noargs; |
1386 | } |
1387 | |
1388 | /* Specifying both -a and -g is an error */ |
1389 | if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == |
1390 | (STTY_verbose_output | STTY_recoverable_output) |
1391 | ) { |
1392 | bb_error_msg_and_die("-a and -g are mutually exclusive"); |
1393 | } |
1394 | /* Specifying -a or -g with non-options is an error */ |
1395 | if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) |
1396 | && !(stty_state & STTY_noargs) |
1397 | ) { |
1398 | bb_error_msg_and_die("modes may not be set when -a or -g is used"); |
1399 | } |
1400 | |
1401 | /* Now it is safe to start doing things */ |
1402 | if (file_name) { |
1403 | G.device_name = file_name; |
1404 | xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); |
1405 | ndelay_off(STDIN_FILENO); |
1406 | } |
1407 | |
1408 | /* Initialize to all zeroes so there is no risk memcmp will report a |
1409 | spurious difference in an uninitialized portion of the structure */ |
1410 | memset(&mode, 0, sizeof(mode)); |
1411 | if (tcgetattr(STDIN_FILENO, &mode)) |
1412 | perror_on_device_and_die("%s"); |
1413 | |
1414 | if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { |
1415 | G.max_col = get_terminal_width(STDOUT_FILENO); |
1416 | output_func(&mode, display_all); |
1417 | return EXIT_SUCCESS; |
1418 | } |
1419 | |
1420 | /* Second pass: perform actions */ |
1421 | k = 0; |
1422 | while (argv[++k]) { |
1423 | const struct mode_info *mp; |
1424 | const struct control_info *cp; |
1425 | const char *arg = argv[k]; |
1426 | const char *argnext = argv[k+1]; |
1427 | int param; |
1428 | |
1429 | if (arg[0] == '-') { |
1430 | mp = find_mode(arg+1); |
1431 | if (mp) { |
1432 | set_mode(mp, 1 /* reversed */, &mode); |
1433 | stty_state |= STTY_require_set_attr; |
1434 | } |
1435 | /* It is an option - already parsed. Skip it */ |
1436 | continue; |
1437 | } |
1438 | |
1439 | mp = find_mode(arg); |
1440 | if (mp) { |
1441 | set_mode(mp, 0 /* non-reversed */, &mode); |
1442 | stty_state |= STTY_require_set_attr; |
1443 | continue; |
1444 | } |
1445 | |
1446 | cp = find_control(arg); |
1447 | if (cp) { |
1448 | ++k; |
1449 | set_control_char_or_die(cp, argnext, &mode); |
1450 | stty_state |= STTY_require_set_attr; |
1451 | continue; |
1452 | } |
1453 | |
1454 | param = find_param(arg); |
1455 | if (param & param_need_arg) { |
1456 | ++k; |
1457 | } |
1458 | |
1459 | switch (param) { |
1460 | #ifdef __linux__ |
1461 | case param_line: |
1462 | mode.c_line = xatoul_sfx(argnext, stty_suffixes); |
1463 | stty_state |= STTY_require_set_attr; |
1464 | break; |
1465 | #endif |
1466 | #ifdef TIOCGWINSZ |
1467 | case param_cols: |
1468 | case param_columns: |
1469 | set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); |
1470 | break; |
1471 | case param_size: |
1472 | display_window_size(0); |
1473 | break; |
1474 | case param_rows: |
1475 | set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); |
1476 | break; |
1477 | #endif |
1478 | case param_speed: |
1479 | display_speed(&mode, 0); |
1480 | break; |
1481 | case param_ispeed: |
1482 | set_speed_or_die(input_speed, argnext, &mode); |
1483 | stty_state |= (STTY_require_set_attr | STTY_speed_was_set); |
1484 | break; |
1485 | case param_ospeed: |
1486 | set_speed_or_die(output_speed, argnext, &mode); |
1487 | stty_state |= (STTY_require_set_attr | STTY_speed_was_set); |
1488 | break; |
1489 | default: |
1490 | if (recover_mode(arg, &mode) == 1) |
1491 | stty_state |= STTY_require_set_attr; |
1492 | else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ |
1493 | set_speed_or_die(both_speeds, arg, &mode); |
1494 | stty_state |= (STTY_require_set_attr | STTY_speed_was_set); |
1495 | } /* else - impossible (caught in the first pass): |
1496 | bb_error_msg_and_die("invalid argument '%s'", arg); */ |
1497 | } |
1498 | } |
1499 | |
1500 | if (stty_state & STTY_require_set_attr) { |
1501 | struct termios new_mode; |
1502 | |
1503 | if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) |
1504 | perror_on_device_and_die("%s"); |
1505 | |
1506 | /* POSIX (according to Zlotnick's book) tcsetattr returns zero if |
1507 | it performs *any* of the requested operations. This means it |
1508 | can report 'success' when it has actually failed to perform |
1509 | some proper subset of the requested operations. To detect |
1510 | this partial failure, get the current terminal attributes and |
1511 | compare them to the requested ones */ |
1512 | |
1513 | /* Initialize to all zeroes so there is no risk memcmp will report a |
1514 | spurious difference in an uninitialized portion of the structure */ |
1515 | memset(&new_mode, 0, sizeof(new_mode)); |
1516 | if (tcgetattr(STDIN_FILENO, &new_mode)) |
1517 | perror_on_device_and_die("%s"); |
1518 | |
1519 | if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { |
1520 | /* |
1521 | * I think the below chunk is not necessary on Linux. |
1522 | * If you are deleting it, also delete STTY_speed_was_set bit - |
1523 | * it is only ever checked here. |
1524 | */ |
1525 | #if 0 /* was "if CIBAUD" */ |
1526 | /* SunOS 4.1.3 (at least) has the problem that after this sequence, |
1527 | tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); |
1528 | sometimes (m1 != m2). The only difference is in the four bits |
1529 | of the c_cflag field corresponding to the baud rate. To save |
1530 | Sun users a little confusion, don't report an error if this |
1531 | happens. But suppress the error only if we haven't tried to |
1532 | set the baud rate explicitly -- otherwise we'd never give an |
1533 | error for a true failure to set the baud rate */ |
1534 | |
1535 | new_mode.c_cflag &= (~CIBAUD); |
1536 | if ((stty_state & STTY_speed_was_set) |
1537 | || memcmp(&mode, &new_mode, sizeof(mode)) != 0) |
1538 | #endif |
1539 | perror_on_device_and_die("%s: cannot perform all requested operations"); |
1540 | } |
1541 | } |
1542 | |
1543 | return EXIT_SUCCESS; |
1544 | } |
1545 |