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