summaryrefslogtreecommitdiff
path: root/coreutils/stty.c (plain)
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 */
243enum speed_setting {
244 input_speed, output_speed, both_speeds
245};
246
247/* Which member(s) of 'struct termios' a mode uses */
248enum {
249 control, input, output, local, combination
250};
251static 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 */
275struct 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
290enum {
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 */
320static 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
476static 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
630enum {
631 NUM_mode_info = ARRAY_SIZE(mode_info)
632};
633
634
635/* Control characters */
636struct control_info {
637 const uint8_t saneval; /* Value to set for 'stty sane' */
638 const uint8_t offset; /* Offset in c_cc */
639};
640
641enum {
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 */
683static 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
725static 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
765enum {
766 NUM_control_info = ARRAY_SIZE(control_info)
767};
768
769
770struct 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
784static 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
799static NORETURN void perror_on_device_and_die(const char *fmt)
800{
801 bb_perror_msg_and_die(fmt, G.device_name);
802}
803
804static 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 */
812static 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
841static void newline(void)
842{
843 if (G.current_col != 0)
844 wrapf("\n");
845}
846
847#ifdef TIOCGWINSZ
848static 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))
865bail:
866 perror_on_device("%s");
867}
868#endif
869
870static 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
885static const struct suffix_mult stty_suffixes[] = {
886 { "b", 512 },
887 { "k", 1024 },
888 { "B", 1024 },
889 { "", 0 }
890};
891
892static 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
898static 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
904enum {
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
916static 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
935static 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
965static 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
977static 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
994static 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
1062static 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
1090static 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
1224static 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
1250int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1251int 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