blob: d709bfc1eba66ddfcf6d7eac9b761aa6f72739b9 [file] [log] [blame]
Eric Andersen98e599c2001-02-14 18:47:33 +00001/* vi: set sw=4 ts=4: */
2/* stty -- change and print terminal line settings
3 Copyright (C) 1990-1999 Free Software Foundation, Inc.
4
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +00005 Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
6*/
Eric Andersen98e599c2001-02-14 18:47:33 +00007/* 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
Eric Andersen7467c8d2001-07-12 20:26:32 +000020 Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
Eric Andersen98e599c2001-02-14 18:47:33 +000021
22 */
23
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +000024#include "busybox.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000025
26#define STREQ(a, b) (strcmp ((a), (b)) == 0)
27
28
29#ifndef _POSIX_VDISABLE
30# define _POSIX_VDISABLE ((unsigned char) 0)
31#endif
32
33#define Control(c) ((c) & 0x1f)
34/* Canonical values for control characters. */
35#ifndef CINTR
36# define CINTR Control ('c')
37#endif
38#ifndef CQUIT
39# define CQUIT 28
40#endif
41#ifndef CERASE
42# define CERASE 127
43#endif
44#ifndef CKILL
45# define CKILL Control ('u')
46#endif
47#ifndef CEOF
48# define CEOF Control ('d')
49#endif
50#ifndef CEOL
51# define CEOL _POSIX_VDISABLE
52#endif
53#ifndef CSTART
54# define CSTART Control ('q')
55#endif
56#ifndef CSTOP
57# define CSTOP Control ('s')
58#endif
59#ifndef CSUSP
60# define CSUSP Control ('z')
61#endif
62#if defined(VEOL2) && !defined(CEOL2)
63# define CEOL2 _POSIX_VDISABLE
64#endif
65/* ISC renamed swtch to susp for termios, but we'll accept either name. */
66#if defined(VSUSP) && !defined(VSWTCH)
67# define VSWTCH VSUSP
68# define CSWTCH CSUSP
69#endif
70#if defined(VSWTCH) && !defined(CSWTCH)
71# define CSWTCH _POSIX_VDISABLE
72#endif
73
74/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
75 So the default is to disable `swtch.' */
76#if defined (__sparc__) && defined (__svr4__)
77# undef CSWTCH
78# define CSWTCH _POSIX_VDISABLE
79#endif
80
Mark Whitley446dd272001-03-02 20:00:54 +000081#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000082# define VWERASE VWERSE
83#endif
84#if defined(VDSUSP) && !defined (CDSUSP)
85# define CDSUSP Control ('y')
86#endif
Mark Whitley446dd272001-03-02 20:00:54 +000087#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000088# define VREPRINT VRPRNT
89#endif
90#if defined(VREPRINT) && !defined(CRPRNT)
91# define CRPRNT Control ('r')
92#endif
93#if defined(VWERASE) && !defined(CWERASE)
94# define CWERASE Control ('w')
95#endif
96#if defined(VLNEXT) && !defined(CLNEXT)
97# define CLNEXT Control ('v')
98#endif
99#if defined(VDISCARD) && !defined(VFLUSHO)
100# define VFLUSHO VDISCARD
101#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000102#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000103# define VFLUSHO VFLUSH
104#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000105#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000106# define ECHOCTL CTLECH
107#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000108#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000109# define ECHOCTL TCTLECH
110#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000111#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000112# define ECHOKE CRTKIL
113#endif
114#if defined(VFLUSHO) && !defined(CFLUSHO)
115# define CFLUSHO Control ('o')
116#endif
117#if defined(VSTATUS) && !defined(CSTATUS)
118# define CSTATUS Control ('t')
119#endif
120
121/* Which speeds to set. */
122enum speed_setting {
123 input_speed, output_speed, both_speeds
124};
125
Eric Andersen98e599c2001-02-14 18:47:33 +0000126/* Which member(s) of `struct termios' a mode uses. */
127enum mode_type {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000128 /* Do NOT change the order or values, as mode_type_flag()
129 * depends on them. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000130 control, input, output, local, combination
131};
132
133
Mark Whitley446dd272001-03-02 20:00:54 +0000134static const char evenp [] = "evenp";
135static const char raw [] = "raw";
136static const char stty_min [] = "min";
137static const char stty_time [] = "time";
Eric Andersen98e599c2001-02-14 18:47:33 +0000138static const char stty_swtch[] = "swtch";
Mark Whitley446dd272001-03-02 20:00:54 +0000139static const char stty_eol [] = "eol";
140static const char stty_eof [] = "eof";
141static const char parity [] = "parity";
142static const char stty_oddp [] = "oddp";
143static const char stty_nl [] = "nl";
144static const char stty_ek [] = "ek";
145static const char stty_sane [] = "sane";
146static const char cbreak [] = "cbreak";
Eric Andersen98e599c2001-02-14 18:47:33 +0000147static const char stty_pass8[] = "pass8";
Mark Whitley446dd272001-03-02 20:00:54 +0000148static const char litout [] = "litout";
149static const char cooked [] = "cooked";
150static const char decctlq [] = "decctlq";
151static const char stty_tabs [] = "tabs";
Eric Andersen98e599c2001-02-14 18:47:33 +0000152static const char stty_lcase[] = "lcase";
153static const char stty_LCASE[] = "LCASE";
Mark Whitley446dd272001-03-02 20:00:54 +0000154static const char stty_crt [] = "crt";
155static const char stty_dec [] = "dec";
Eric Andersen98e599c2001-02-14 18:47:33 +0000156
157
158/* Flags for `struct mode_info'. */
Mark Whitley446dd272001-03-02 20:00:54 +0000159#define SANE_SET 1 /* Set in `sane' mode. */
160#define SANE_UNSET 2 /* Unset in `sane' mode. */
161#define REV 4 /* Can be turned off by prepending `-'. */
162#define OMIT 8 /* Don't display value. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000163
164/* Each mode. */
165struct mode_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000166 const char *name; /* Name given on command line. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000167 /* enum mode_type type; */
168 char type; /* Which structure element to change. */
Mark Whitley446dd272001-03-02 20:00:54 +0000169 char flags; /* Setting and display options. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000170 unsigned short mask; /* Other bits to turn off for this mode. */
Mark Whitley446dd272001-03-02 20:00:54 +0000171 unsigned long bits; /* Bits to set for this mode. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000172};
173
Manuel Novoa III cad53642003-03-19 09:13:01 +0000174#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
175
Mark Whitley446dd272001-03-02 20:00:54 +0000176static const struct mode_info mode_info[] = {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000177 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
178 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
179 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
180 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
181 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
182 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
183 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
184 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
185 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
186 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
187 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000188#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000189 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000190#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000191 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
192 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
193 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
194 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
195 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
196 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
197 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
198 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
199 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
200 MI_ENTRY("ixon", input, REV, IXON, 0 ),
201 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
202 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000203#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000204 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000205#endif
206#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000207 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000208#endif
209#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000210 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000211#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000212 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000213#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000214 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000215#endif
216#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000217 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000218#endif
219#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000220 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000221#endif
222#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000223 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000224#endif
225#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000226 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000227#endif
228#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000229 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000230#endif
231#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000232 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000233#endif
234#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000235 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
236 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000237#endif
238#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000239 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
240 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
241 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
242 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000243#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000244
Eric Andersen98e599c2001-02-14 18:47:33 +0000245#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000246 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
247 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
248 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
249 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000250#else
251# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000252 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000253# endif
254#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000255
Eric Andersen98e599c2001-02-14 18:47:33 +0000256#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000257 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
258 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000259#endif
260#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000261 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
262 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000263#endif
264#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000265 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
266 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000267#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000268 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
269 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000270#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000271 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000272#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000273 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
274 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
275 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
276 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
277 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
278 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000279#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000280 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000281#endif
282#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000283 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000284#endif
285#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000286 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
287 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000288#endif
289#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000290 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
291 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000292#endif
293#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000294 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
295 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000296#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000297 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
298 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
299 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
300 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
301 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
302 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
303 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
304 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
305 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
306 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
307 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000308#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000309 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000310#endif
311#if defined (TABDLY) || defined (OXTABS)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000312 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000313#endif
314#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000315 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
316 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000317#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000318 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
319 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000320};
321
Rob Landleybc68cd12006-03-10 19:22:06 +0000322enum {
323 NUM_mode_info =
324 (sizeof(mode_info) / sizeof(struct mode_info))
325};
Eric Andersen98e599c2001-02-14 18:47:33 +0000326
327/* Control character settings. */
328struct control_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000329 const char *name; /* Name given on command line. */
330 unsigned char saneval; /* Value to set for `stty sane'. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000331 unsigned char offset; /* Offset in c_cc. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000332};
333
334/* Control characters. */
335
Mark Whitley446dd272001-03-02 20:00:54 +0000336static const struct control_info control_info[] = {
337 {"intr", CINTR, VINTR},
338 {"quit", CQUIT, VQUIT},
339 {"erase", CERASE, VERASE},
340 {"kill", CKILL, VKILL},
341 {stty_eof, CEOF, VEOF},
342 {stty_eol, CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000343#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000344 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000345#endif
346#ifdef VSWTCH
Mark Whitley446dd272001-03-02 20:00:54 +0000347 {stty_swtch, CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000348#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000349 {"start", CSTART, VSTART},
350 {"stop", CSTOP, VSTOP},
351 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000352#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000353 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000354#endif
355#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000356 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000357#endif
358#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000359 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000360#endif
361#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000362 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000363#endif
364#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000365 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000366#endif
367#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000368 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000369#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000370 /* These must be last because of the display routines. */
Mark Whitley446dd272001-03-02 20:00:54 +0000371 {stty_min, 1, VMIN},
372 {stty_time, 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000373};
374
Rob Landleybc68cd12006-03-10 19:22:06 +0000375enum {
376 NUM_control_info =
377 (sizeof(control_info) / sizeof(struct control_info))
378};
Eric Andersen98e599c2001-02-14 18:47:33 +0000379
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000380#define EMT(t) ((enum mode_type)(t))
Eric Andersen98e599c2001-02-14 18:47:33 +0000381
Mark Whitley446dd272001-03-02 20:00:54 +0000382static const char * visible(unsigned int ch);
Mark Whitley446dd272001-03-02 20:00:54 +0000383static int recover_mode(char *arg, struct termios *mode);
384static int screen_columns(void);
385static int set_mode(const struct mode_info *info,
386 int reversed, struct termios *mode);
387static speed_t string_to_baud(const char *arg);
388static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000389static void display_all(struct termios *mode);
390static void display_changed(struct termios *mode);
391static void display_recoverable(struct termios *mode);
Mark Whitley446dd272001-03-02 20:00:54 +0000392static void display_speed(struct termios *mode, int fancy);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000393static void display_window_size(int fancy);
Mark Whitley446dd272001-03-02 20:00:54 +0000394static void sane_mode(struct termios *mode);
395static void set_control_char(const struct control_info *info,
396 const char *arg, struct termios *mode);
397static void set_speed(enum speed_setting type,
398 const char *arg, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000399static void set_window_size(int rows, int cols);
Eric Andersen8876fb22003-06-20 09:01:58 +0000400
401static const char *device_name;
402
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000403static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000404{
405 bb_perror_msg_and_die(fmt, device_name);
406}
407
Eric Andersen98e599c2001-02-14 18:47:33 +0000408
409/* The width of the screen, for output wrapping. */
410static int max_col;
411
412/* Current position, to know when to wrap. */
413static int current_col;
414
415/* Print format string MESSAGE and optional args.
416 Wrap to next line first if it won't fit.
417 Print a space first unless MESSAGE will start a new line. */
418
419static void wrapf(const char *message, ...)
420{
421 va_list args;
Mark Whitley446dd272001-03-02 20:00:54 +0000422 char buf[1024]; /* Plenty long for our needs. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000423 int buflen;
424
425 va_start(args, message);
426 vsprintf(buf, message, args);
427 va_end(args);
428 buflen = strlen(buf);
429 if (current_col + (current_col > 0) + buflen >= max_col) {
430 putchar('\n');
431 current_col = 0;
432 }
433 if (current_col > 0) {
434 putchar(' ');
435 current_col++;
436 }
437 fputs(buf, stdout);
438 current_col += buflen;
439}
440
441static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000442 {"b", 512 },
443 {"k", 1024},
444 {"B", 1024},
445 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000446};
447
Rob Landleydfba7412006-03-06 20:47:33 +0000448int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +0000449{
450 struct termios mode;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000451 void (*output_func)(struct termios *);
Mark Whitley446dd272001-03-02 20:00:54 +0000452 int optc;
453 int require_set_attr;
454 int speed_was_set;
455 int verbose_output;
456 int recoverable_output;
457 int k;
458 int noargs = 1;
459 char * file_name = NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +0000460
Manuel Novoa III cad53642003-03-19 09:13:01 +0000461 output_func = display_changed;
Eric Andersen98e599c2001-02-14 18:47:33 +0000462 verbose_output = 0;
463 recoverable_output = 0;
464
465 /* Don't print error messages for unrecognized options. */
466 opterr = 0;
467
468 while ((optc = getopt(argc, argv, "agF:")) != -1) {
469 switch (optc) {
470 case 'a':
471 verbose_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000472 output_func = display_all;
Eric Andersen98e599c2001-02-14 18:47:33 +0000473 break;
474
475 case 'g':
476 recoverable_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000477 output_func = display_recoverable;
Eric Andersen98e599c2001-02-14 18:47:33 +0000478 break;
479
480 case 'F':
481 if (file_name)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000482 bb_error_msg_and_die("only one device may be specified");
Eric Andersen98e599c2001-02-14 18:47:33 +0000483 file_name = optarg;
484 break;
485
Mark Whitley446dd272001-03-02 20:00:54 +0000486 default: /* unrecognized option */
Eric Andersen98e599c2001-02-14 18:47:33 +0000487 noargs = 0;
488 break;
489 }
490
491 if (noargs == 0)
492 break;
493 }
494
495 if (optind < argc)
496 noargs = 0;
497
498 /* Specifying both -a and -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000499 if (verbose_output & recoverable_output)
500 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
Eric Andersen98e599c2001-02-14 18:47:33 +0000501
502 /* Specifying any other arguments with -a or -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000503 if (~noargs & (verbose_output | recoverable_output))
504 bb_error_msg_and_die ("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000505
506 /* FIXME: it'd be better not to open the file until we've verified
507 that all arguments are valid. Otherwise, we could end up doing
508 only some of the requested operations and then failing, probably
509 leaving things in an undesirable state. */
510
511 if (file_name) {
512 int fdflags;
513
514 device_name = file_name;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000515 fclose(stdin);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000516 xopen(device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +0000517 fdflags = fcntl(STDIN_FILENO, F_GETFL);
518 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Eric Andersen8876fb22003-06-20 09:01:58 +0000519 perror_on_device("%s: couldn't reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +0000520 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000521 device_name = bb_msg_standard_input;
Eric Andersen98e599c2001-02-14 18:47:33 +0000522 }
523
524 /* Initialize to all zeroes so there is no risk memcmp will report a
525 spurious difference in an uninitialized portion of the structure. */
526 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000527 if (tcgetattr(STDIN_FILENO, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000528 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000529
Manuel Novoa III cad53642003-03-19 09:13:01 +0000530 if (verbose_output | recoverable_output | noargs) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000531 max_col = screen_columns();
532 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000533 output_func(&mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000534 return EXIT_SUCCESS;
535 }
536
537 speed_was_set = 0;
538 require_set_attr = 0;
Eric Andersenfc059092002-06-06 11:35:29 +0000539 k = 0;
540 while (++k < argc) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000541 int match_found = 0;
542 int reversed = 0;
543 int i;
544
545 if (argv[k][0] == '-') {
Eric Andersenfc059092002-06-06 11:35:29 +0000546 char *find_dev_opt;
547
Eric Andersen98e599c2001-02-14 18:47:33 +0000548 ++argv[k];
Eric Andersenfc059092002-06-06 11:35:29 +0000549
550 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
551 Find the options that have been parsed. This is really
552 gross, but it's needed because stty SETTINGS look like options to
553 getopt(), so we need to work around things in a really horrible
554 way. If any new options are ever added to stty, the short option
555 MUST NOT be a letter which is the first letter of one of the
556 possible stty settings.
557 */
558 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
559 if(find_dev_opt) {
560 if(find_dev_opt[1]==0) /* -*F /dev/foo */
561 k++; /* skip /dev/foo */
562 continue; /* else -*F/dev/foo - no skip */
563 }
564 if(argv[k][0]=='a' || argv[k][0]=='g')
565 continue;
566 /* Is not options - is reverse params */
Eric Andersen98e599c2001-02-14 18:47:33 +0000567 reversed = 1;
568 }
Mark Whitley446dd272001-03-02 20:00:54 +0000569 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000570 if (STREQ(argv[k], mode_info[i].name)) {
571 match_found = set_mode(&mode_info[i], reversed, &mode);
572 require_set_attr = 1;
573 break;
574 }
Mark Whitley446dd272001-03-02 20:00:54 +0000575
576 if (match_found == 0 && reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000577 bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000578
579 if (match_found == 0)
580 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000581 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000582 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000583 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000584 match_found = 1;
585 ++k;
586 set_control_char(&control_info[i], argv[k], &mode);
587 require_set_attr = 1;
588 break;
589 }
Mark Whitley446dd272001-03-02 20:00:54 +0000590
Eric Andersen98e599c2001-02-14 18:47:33 +0000591 if (match_found == 0) {
592 if (STREQ(argv[k], "ispeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000593 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000594 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000595 ++k;
596 set_speed(input_speed, argv[k], &mode);
597 speed_was_set = 1;
598 require_set_attr = 1;
599 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000600 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000601 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000602 ++k;
603 set_speed(output_speed, argv[k], &mode);
604 speed_was_set = 1;
605 require_set_attr = 1;
606 }
607#ifdef TIOCGWINSZ
608 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000609 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000610 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000611 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000612 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000613 -1);
Eric Andersen98e599c2001-02-14 18:47:33 +0000614 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000615 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000616 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000617 ++k;
618 set_window_size(-1,
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000619 (int) bb_xparse_number(argv[k], stty_suffixes));
Eric Andersen98e599c2001-02-14 18:47:33 +0000620 } else if (STREQ(argv[k], "size")) {
621 max_col = screen_columns();
622 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000623 display_window_size(0);
Eric Andersen98e599c2001-02-14 18:47:33 +0000624 }
625#endif
626#ifdef HAVE_C_LINE
627 else if (STREQ(argv[k], "line")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000628 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000629 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000630 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000631 mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000632 require_set_attr = 1;
633 }
634#endif
635 else if (STREQ(argv[k], "speed")) {
636 max_col = screen_columns();
637 display_speed(&mode, 0);
Mark Whitley446dd272001-03-02 20:00:54 +0000638 } else if (recover_mode(argv[k], &mode) == 1)
639 require_set_attr = 1;
640 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000641 set_speed(both_speeds, argv[k], &mode);
642 speed_was_set = 1;
643 require_set_attr = 1;
Mark Whitley446dd272001-03-02 20:00:54 +0000644 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000645 bb_error_msg_and_die("invalid argument `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000646 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000647 }
648
649 if (require_set_attr) {
650 struct termios new_mode;
651
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000652 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000653 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000654
655 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
656 it performs *any* of the requested operations. This means it
657 can report `success' when it has actually failed to perform
658 some proper subset of the requested operations. To detect
659 this partial failure, get the current terminal attributes and
660 compare them to the requested ones. */
661
662 /* Initialize to all zeroes so there is no risk memcmp will report a
663 spurious difference in an uninitialized portion of the structure. */
664 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000665 if (tcgetattr(STDIN_FILENO, &new_mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000666 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000667
668 /* Normally, one shouldn't use memcmp to compare structures that
669 may have `holes' containing uninitialized data, but we have been
670 careful to initialize the storage of these two variables to all
671 zeroes. One might think it more efficient simply to compare the
672 modified fields, but that would require enumerating those fields --
673 and not all systems have the same fields in this structure. */
674
675 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
676#ifdef CIBAUD
677 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
678 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
679 sometimes (m1 != m2). The only difference is in the four bits
680 of the c_cflag field corresponding to the baud rate. To save
681 Sun users a little confusion, don't report an error if this
682 happens. But suppress the error only if we haven't tried to
683 set the baud rate explicitly -- otherwise we'd never give an
684 error for a true failure to set the baud rate. */
685
686 new_mode.c_cflag &= (~CIBAUD);
687 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
688#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000689 perror_on_device ("%s: unable to perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +0000690 }
691 }
692
693 return EXIT_SUCCESS;
694}
695
696/* Return 0 if not applied because not reversible; otherwise return 1. */
697
698static int
699set_mode(const struct mode_info *info, int reversed, struct termios *mode)
700{
701 tcflag_t *bitsp;
702
703 if (reversed && (info->flags & REV) == 0)
704 return 0;
705
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000706 bitsp = mode_type_flag(EMT(info->type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000707
708 if (bitsp == NULL) {
709 /* Combination mode. */
710 if (info->name == evenp || info->name == parity) {
711 if (reversed)
712 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
713 else
714 mode->c_cflag =
715 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
716 } else if (info->name == stty_oddp) {
717 if (reversed)
718 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
719 else
720 mode->c_cflag =
721 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
722 } else if (info->name == stty_nl) {
723 if (reversed) {
724 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
725 mode->c_oflag = (mode->c_oflag
726#ifdef ONLCR
727 | ONLCR
728#endif
729 )
730#ifdef OCRNL
731 & ~OCRNL
732#endif
733#ifdef ONLRET
734 & ~ONLRET
735#endif
736 ;
737 } else {
738 mode->c_iflag = mode->c_iflag & ~ICRNL;
739#ifdef ONLCR
740 mode->c_oflag = mode->c_oflag & ~ONLCR;
741#endif
742 }
743 } else if (info->name == stty_ek) {
744 mode->c_cc[VERASE] = CERASE;
745 mode->c_cc[VKILL] = CKILL;
746 } else if (info->name == stty_sane)
747 sane_mode(mode);
748 else if (info->name == cbreak) {
749 if (reversed)
750 mode->c_lflag |= ICANON;
751 else
752 mode->c_lflag &= ~ICANON;
753 } else if (info->name == stty_pass8) {
754 if (reversed) {
755 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
756 mode->c_iflag |= ISTRIP;
757 } else {
758 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
759 mode->c_iflag &= ~ISTRIP;
760 }
761 } else if (info->name == litout) {
762 if (reversed) {
763 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
764 mode->c_iflag |= ISTRIP;
765 mode->c_oflag |= OPOST;
766 } else {
767 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
768 mode->c_iflag &= ~ISTRIP;
769 mode->c_oflag &= ~OPOST;
770 }
771 } else if (info->name == raw || info->name == cooked) {
772 if ((info->name[0] == 'r' && reversed)
773 || (info->name[0] == 'c' && !reversed)) {
774 /* Cooked mode. */
775 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
776 mode->c_oflag |= OPOST;
777 mode->c_lflag |= ISIG | ICANON;
778#if VMIN == VEOF
779 mode->c_cc[VEOF] = CEOF;
780#endif
781#if VTIME == VEOL
782 mode->c_cc[VEOL] = CEOL;
783#endif
784 } else {
785 /* Raw mode. */
786 mode->c_iflag = 0;
787 mode->c_oflag &= ~OPOST;
788 mode->c_lflag &= ~(ISIG | ICANON
789#ifdef XCASE
790 | XCASE
791#endif
792 );
793 mode->c_cc[VMIN] = 1;
794 mode->c_cc[VTIME] = 0;
795 }
796 }
797#ifdef IXANY
798 else if (info->name == decctlq) {
799 if (reversed)
800 mode->c_iflag |= IXANY;
801 else
802 mode->c_iflag &= ~IXANY;
803 }
804#endif
805#ifdef TABDLY
806 else if (info->name == stty_tabs) {
807 if (reversed)
808 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
809 else
810 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
811 }
812#else
813# ifdef OXTABS
814 else if (info->name == stty_tabs) {
815 if (reversed)
816 mode->c_oflag = mode->c_oflag | OXTABS;
817 else
818 mode->c_oflag = mode->c_oflag & ~OXTABS;
819 }
820# endif
821#endif
822#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
823 else if (info->name == stty_lcase || info->name == stty_LCASE) {
824 if (reversed) {
825 mode->c_lflag &= ~XCASE;
826 mode->c_iflag &= ~IUCLC;
827 mode->c_oflag &= ~OLCUC;
828 } else {
829 mode->c_lflag |= XCASE;
830 mode->c_iflag |= IUCLC;
831 mode->c_oflag |= OLCUC;
832 }
833 }
834#endif
835 else if (info->name == stty_crt)
836 mode->c_lflag |= ECHOE
837#ifdef ECHOCTL
838 | ECHOCTL
839#endif
840#ifdef ECHOKE
841 | ECHOKE
842#endif
843 ;
844 else if (info->name == stty_dec) {
Mark Whitley446dd272001-03-02 20:00:54 +0000845 mode->c_cc[VINTR] = 3; /* ^C */
846 mode->c_cc[VERASE] = 127; /* DEL */
847 mode->c_cc[VKILL] = 21; /* ^U */
Eric Andersen98e599c2001-02-14 18:47:33 +0000848 mode->c_lflag |= ECHOE
849#ifdef ECHOCTL
850 | ECHOCTL
851#endif
852#ifdef ECHOKE
853 | ECHOKE
854#endif
855 ;
856#ifdef IXANY
857 mode->c_iflag &= ~IXANY;
858#endif
859 }
860 } else if (reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000861 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000862 else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000863 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000864
865 return 1;
866}
867
868static void
869set_control_char(const struct control_info *info, const char *arg,
870 struct termios *mode)
871{
872 unsigned char value;
873
874 if (info->name == stty_min || info->name == stty_time)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000875 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000876 else if (arg[0] == '\0' || arg[1] == '\0')
877 value = arg[0];
878 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
879 value = _POSIX_VDISABLE;
Mark Whitley446dd272001-03-02 20:00:54 +0000880 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000881 if (arg[1] == '?')
882 value = 127;
883 else
Mark Whitley446dd272001-03-02 20:00:54 +0000884 value = arg[1] & ~0140; /* Non-letters get weird results. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000885 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000886 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000887 mode->c_cc[info->offset] = value;
888}
889
890static void
891set_speed(enum speed_setting type, const char *arg, struct termios *mode)
892{
893 speed_t baud;
894
895 baud = string_to_baud(arg);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000896
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000897 if (type != output_speed) { /* either input or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000898 cfsetispeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000899 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000900 if (type != input_speed) { /* either output or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000901 cfsetospeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000902 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000903}
904
905#ifdef TIOCGWINSZ
906
907static int get_win_size(int fd, struct winsize *win)
908{
909 int err = ioctl(fd, TIOCGWINSZ, (char *) win);
910
911 return err;
912}
913
914static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000915set_window_size(int rows, int cols)
Eric Andersen98e599c2001-02-14 18:47:33 +0000916{
917 struct winsize win;
918
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000919 if (get_win_size(STDIN_FILENO, &win)) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000920 if (errno != EINVAL)
Eric Andersen8876fb22003-06-20 09:01:58 +0000921 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000922 memset(&win, 0, sizeof(win));
923 }
924
925 if (rows >= 0)
926 win.ws_row = rows;
927 if (cols >= 0)
928 win.ws_col = cols;
929
930# ifdef TIOCSSIZE
931 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
932 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
933 This comment from sys/ttold.h describes Sun's twisted logic - a better
934 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
935 At any rate, the problem is gone in Solaris 2.x. */
936
937 if (win.ws_row == 0 || win.ws_col == 0) {
938 struct ttysize ttysz;
939
940 ttysz.ts_lines = win.ws_row;
941 ttysz.ts_cols = win.ws_col;
942
Manuel Novoa III cad53642003-03-19 09:13:01 +0000943 win.ws_row = win.ws_col = 1;
Eric Andersen98e599c2001-02-14 18:47:33 +0000944
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000945 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
946 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000947 perror_on_device("%s");
Glenn L McGrathca1c1af2004-09-15 03:24:32 +0000948 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000949 return;
950 }
951# endif
952
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000953 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Eric Andersen8876fb22003-06-20 09:01:58 +0000954 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000955}
956
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000957static void display_window_size(int fancy)
Eric Andersen98e599c2001-02-14 18:47:33 +0000958{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000959 const char *fmt_str = "%s" "\0" "%s: no size information for this device";
Eric Andersen98e599c2001-02-14 18:47:33 +0000960 struct winsize win;
961
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000962 if (get_win_size(STDIN_FILENO, &win)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000963 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000964 perror_on_device(fmt_str);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000965 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000966 } else {
967 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
968 win.ws_row, win.ws_col);
969 if (!fancy)
970 current_col = 0;
971 }
972}
973#endif
974
975static int screen_columns(void)
976{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000977 int columns;
978 const char *s;
979
Eric Andersen98e599c2001-02-14 18:47:33 +0000980#ifdef TIOCGWINSZ
981 struct winsize win;
982
983 /* With Solaris 2.[123], this ioctl fails and errno is set to
984 EINVAL for telnet (but not rlogin) sessions.
985 On ISC 3.0, it fails for the console and the serial port
986 (but it works for ptys).
987 It can also fail on any system when stdout isn't a tty.
988 In case of any failure, just use the default. */
989 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
990 return win.ws_col;
991#endif
992
Manuel Novoa III cad53642003-03-19 09:13:01 +0000993 columns = 80;
994 if ((s = getenv("COLUMNS"))) {
995 columns = atoi(s);
996 }
997 return columns;
Eric Andersen98e599c2001-02-14 18:47:33 +0000998}
999
1000static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1001{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001002 static const unsigned char tcflag_offsets[] = {
1003 offsetof(struct termios, c_cflag), /* control */
1004 offsetof(struct termios, c_iflag), /* input */
1005 offsetof(struct termios, c_oflag), /* output */
1006 offsetof(struct termios, c_lflag) /* local */
1007 };
Eric Andersen98e599c2001-02-14 18:47:33 +00001008
Manuel Novoa III cad53642003-03-19 09:13:01 +00001009 if (((unsigned int) type) <= local) {
1010 return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
Eric Andersen98e599c2001-02-14 18:47:33 +00001011 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001012 return NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +00001013}
1014
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001015static void display_changed(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001016{
1017 int i;
1018 int empty_line;
1019 tcflag_t *bitsp;
1020 unsigned long mask;
1021 enum mode_type prev_type = control;
1022
1023 display_speed(mode, 1);
1024#ifdef HAVE_C_LINE
1025 wrapf("line = %d;", mode->c_line);
1026#endif
1027 putchar('\n');
1028 current_col = 0;
1029
1030 empty_line = 1;
1031 for (i = 0; control_info[i].name != stty_min; ++i) {
1032 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1033 continue;
1034 /* If swtch is the same as susp, don't print both. */
1035#if VSWTCH == VSUSP
1036 if (control_info[i].name == stty_swtch)
1037 continue;
1038#endif
1039 /* If eof uses the same slot as min, only print whichever applies. */
1040#if VEOF == VMIN
1041 if ((mode->c_lflag & ICANON) == 0
1042 && (control_info[i].name == stty_eof
1043 || control_info[i].name == stty_eol)) continue;
1044#endif
1045
1046 empty_line = 0;
1047 wrapf("%s = %s;", control_info[i].name,
1048 visible(mode->c_cc[control_info[i].offset]));
1049 }
1050 if ((mode->c_lflag & ICANON) == 0) {
1051 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1052 (int) mode->c_cc[VTIME]);
1053 } else if (empty_line == 0)
1054 putchar('\n');
1055 current_col = 0;
1056
1057 empty_line = 1;
1058 for (i = 0; i < NUM_mode_info; ++i) {
1059 if (mode_info[i].flags & OMIT)
1060 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001061 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001062 if (empty_line == 0) {
1063 putchar('\n');
1064 current_col = 0;
1065 empty_line = 1;
1066 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001067 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001068 }
1069
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001070 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001071 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1072 if ((*bitsp & mask) == mode_info[i].bits) {
1073 if (mode_info[i].flags & SANE_UNSET) {
1074 wrapf("%s", mode_info[i].name);
1075 empty_line = 0;
1076 }
1077 }
1078 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1079 (SANE_SET | REV)) {
1080 wrapf("-%s", mode_info[i].name);
1081 empty_line = 0;
1082 }
1083 }
1084 if (empty_line == 0)
1085 putchar('\n');
1086 current_col = 0;
1087}
1088
1089static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001090display_all(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001091{
1092 int i;
1093 tcflag_t *bitsp;
1094 unsigned long mask;
1095 enum mode_type prev_type = control;
1096
1097 display_speed(mode, 1);
1098#ifdef TIOCGWINSZ
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001099 display_window_size(1);
Eric Andersen98e599c2001-02-14 18:47:33 +00001100#endif
1101#ifdef HAVE_C_LINE
1102 wrapf("line = %d;", mode->c_line);
1103#endif
1104 putchar('\n');
1105 current_col = 0;
1106
1107 for (i = 0; control_info[i].name != stty_min; ++i) {
1108 /* If swtch is the same as susp, don't print both. */
1109#if VSWTCH == VSUSP
1110 if (control_info[i].name == stty_swtch)
1111 continue;
1112#endif
1113 /* If eof uses the same slot as min, only print whichever applies. */
1114#if VEOF == VMIN
1115 if ((mode->c_lflag & ICANON) == 0
1116 && (control_info[i].name == stty_eof
1117 || control_info[i].name == stty_eol)) continue;
1118#endif
1119 wrapf("%s = %s;", control_info[i].name,
1120 visible(mode->c_cc[control_info[i].offset]));
1121 }
1122#if VEOF == VMIN
1123 if ((mode->c_lflag & ICANON) == 0)
1124#endif
1125 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1126 if (current_col != 0)
1127 putchar('\n');
1128 current_col = 0;
1129
1130 for (i = 0; i < NUM_mode_info; ++i) {
1131 if (mode_info[i].flags & OMIT)
1132 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001133 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001134 putchar('\n');
1135 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001136 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001137 }
1138
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001139 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001140 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1141 if ((*bitsp & mask) == mode_info[i].bits)
1142 wrapf("%s", mode_info[i].name);
1143 else if (mode_info[i].flags & REV)
1144 wrapf("-%s", mode_info[i].name);
1145 }
1146 putchar('\n');
1147 current_col = 0;
1148}
1149
1150static void display_speed(struct termios *mode, int fancy)
1151{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001152 unsigned long ispeed, ospeed;
1153 const char *fmt_str =
1154 "%lu %lu\n\0" "ispeed %lu baud; ospeed %lu baud;\0"
1155 "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
1156
1157 ospeed = ispeed = cfgetispeed(mode);
1158 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001159 ispeed = ospeed; /* in case ispeed was 0 */
Manuel Novoa III cad53642003-03-19 09:13:01 +00001160 fmt_str += 43;
1161 }
1162 if (fancy) {
1163 fmt_str += 9;
1164 }
Rob Landley290fcb42006-06-18 23:59:03 +00001165 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001166 if (!fancy)
1167 current_col = 0;
1168}
1169
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001170static void display_recoverable(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001171{
1172 int i;
1173
1174 printf("%lx:%lx:%lx:%lx",
1175 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1176 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1177 for (i = 0; i < NCCS; ++i)
1178 printf(":%x", (unsigned int) mode->c_cc[i]);
1179 putchar('\n');
1180}
1181
1182static int recover_mode(char *arg, struct termios *mode)
1183{
1184 int i, n;
1185 unsigned int chr;
1186 unsigned long iflag, oflag, cflag, lflag;
1187
1188 /* Scan into temporaries since it is too much trouble to figure out
1189 the right format for `tcflag_t'. */
1190 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1191 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1192 return 0;
1193 mode->c_iflag = iflag;
1194 mode->c_oflag = oflag;
1195 mode->c_cflag = cflag;
1196 mode->c_lflag = lflag;
1197 arg += n;
1198 for (i = 0; i < NCCS; ++i) {
1199 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1200 return 0;
1201 mode->c_cc[i] = chr;
1202 arg += n;
1203 }
1204
1205 /* Fail if there are too many fields. */
1206 if (*arg != '\0')
1207 return 0;
1208
1209 return 1;
1210}
1211
Eric Andersen98e599c2001-02-14 18:47:33 +00001212static speed_t string_to_baud(const char *arg)
1213{
Rob Landley290fcb42006-06-18 23:59:03 +00001214 return tty_value_to_baud(bb_xparse_number(arg, 0));
Eric Andersen98e599c2001-02-14 18:47:33 +00001215}
1216
1217static void sane_mode(struct termios *mode)
1218{
1219 int i;
1220 tcflag_t *bitsp;
1221
1222 for (i = 0; i < NUM_control_info; ++i) {
1223#if VMIN == VEOF
1224 if (control_info[i].name == stty_min)
1225 break;
1226#endif
1227 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1228 }
1229
1230 for (i = 0; i < NUM_mode_info; ++i) {
1231 if (mode_info[i].flags & SANE_SET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001232 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001233 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1234 | mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001235 } else if (mode_info[i].flags & SANE_UNSET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001236 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001237 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1238 & ~mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001239 }
1240 }
1241}
1242
1243/* Return a string that is the printable representation of character CH. */
1244/* Adapted from `cat' by Torbjorn Granlund. */
1245
1246static const char *visible(unsigned int ch)
1247{
1248 static char buf[10];
1249 char *bpout = buf;
1250
Manuel Novoa III cad53642003-03-19 09:13:01 +00001251 if (ch == _POSIX_VDISABLE) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001252 return "<undef>";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001253 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001254
Manuel Novoa III cad53642003-03-19 09:13:01 +00001255 if (ch >= 128) {
1256 ch -= 128;
1257 *bpout++ = 'M';
1258 *bpout++ = '-';
1259 }
1260
1261 if (ch < 32) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001262 *bpout++ = '^';
1263 *bpout++ = ch + 64;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001264 } else if (ch < 127) {
1265 *bpout++ = ch;
1266 } else {
1267 *bpout++ = '^';
1268 *bpout++ = '?';
Eric Andersen98e599c2001-02-14 18:47:33 +00001269 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001270
Eric Andersen98e599c2001-02-14 18:47:33 +00001271 *bpout = '\0';
1272 return (const char *) buf;
1273}