source: MondoRescue/branches/3.3/mindi-busybox/coreutils/stty.c@ 3621

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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