Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/coreutils/stty.c

Parent Directory Parent Directory | Revision Log Revision Log


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