blob: 06e3b0ed5ab692dabb8e077ec280496047c53a48 [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
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000024#include "libbb.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000025
Eric Andersen98e599c2001-02-14 18:47:33 +000026#ifndef _POSIX_VDISABLE
27# define _POSIX_VDISABLE ((unsigned char) 0)
28#endif
29
30#define Control(c) ((c) & 0x1f)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000031/* Canonical values for control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +000032#ifndef CINTR
Denis Vlasenko9efb0702006-09-19 14:17:10 +000033# define CINTR Control('c')
Eric Andersen98e599c2001-02-14 18:47:33 +000034#endif
35#ifndef CQUIT
36# define CQUIT 28
37#endif
38#ifndef CERASE
39# define CERASE 127
40#endif
41#ifndef CKILL
Denis Vlasenko9efb0702006-09-19 14:17:10 +000042# define CKILL Control('u')
Eric Andersen98e599c2001-02-14 18:47:33 +000043#endif
44#ifndef CEOF
Denis Vlasenko9efb0702006-09-19 14:17:10 +000045# define CEOF Control('d')
Eric Andersen98e599c2001-02-14 18:47:33 +000046#endif
47#ifndef CEOL
48# define CEOL _POSIX_VDISABLE
49#endif
50#ifndef CSTART
Denis Vlasenko9efb0702006-09-19 14:17:10 +000051# define CSTART Control('q')
Eric Andersen98e599c2001-02-14 18:47:33 +000052#endif
53#ifndef CSTOP
Denis Vlasenko9efb0702006-09-19 14:17:10 +000054# define CSTOP Control('s')
Eric Andersen98e599c2001-02-14 18:47:33 +000055#endif
56#ifndef CSUSP
Denis Vlasenko9efb0702006-09-19 14:17:10 +000057# define CSUSP Control('z')
Eric Andersen98e599c2001-02-14 18:47:33 +000058#endif
59#if defined(VEOL2) && !defined(CEOL2)
60# define CEOL2 _POSIX_VDISABLE
61#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +000062/* ISC renamed swtch to susp for termios, but we'll accept either name */
Eric Andersen98e599c2001-02-14 18:47:33 +000063#if defined(VSUSP) && !defined(VSWTCH)
64# define VSWTCH VSUSP
65# define CSWTCH CSUSP
66#endif
67#if defined(VSWTCH) && !defined(CSWTCH)
68# define CSWTCH _POSIX_VDISABLE
69#endif
70
Denis Vlasenko79deb662006-09-19 15:12:12 +000071/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
72 So the default is to disable 'swtch.' */
Denis Vlasenkoff131b92007-04-10 15:42:06 +000073#if defined(__sparc__) && defined(__svr4__)
Eric Andersen98e599c2001-02-14 18:47:33 +000074# undef CSWTCH
75# define CSWTCH _POSIX_VDISABLE
76#endif
77
Denis Vlasenkoff131b92007-04-10 15:42:06 +000078#if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000079# define VWERASE VWERSE
80#endif
Denis Vlasenkoff131b92007-04-10 15:42:06 +000081#if defined(VDSUSP) && !defined(CDSUSP)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000082# define CDSUSP Control('y')
Eric Andersen98e599c2001-02-14 18:47:33 +000083#endif
Mark Whitley446dd272001-03-02 20:00:54 +000084#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000085# define VREPRINT VRPRNT
86#endif
87#if defined(VREPRINT) && !defined(CRPRNT)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000088# define CRPRNT Control('r')
Eric Andersen98e599c2001-02-14 18:47:33 +000089#endif
90#if defined(VWERASE) && !defined(CWERASE)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000091# define CWERASE Control('w')
Eric Andersen98e599c2001-02-14 18:47:33 +000092#endif
93#if defined(VLNEXT) && !defined(CLNEXT)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000094# define CLNEXT Control('v')
Eric Andersen98e599c2001-02-14 18:47:33 +000095#endif
96#if defined(VDISCARD) && !defined(VFLUSHO)
97# define VFLUSHO VDISCARD
98#endif
Mark Whitley446dd272001-03-02 20:00:54 +000099#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000100# define VFLUSHO VFLUSH
101#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000102#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000103# define ECHOCTL CTLECH
104#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000105#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000106# define ECHOCTL TCTLECH
107#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000108#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000109# define ECHOKE CRTKIL
110#endif
111#if defined(VFLUSHO) && !defined(CFLUSHO)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000112# define CFLUSHO Control('o')
Eric Andersen98e599c2001-02-14 18:47:33 +0000113#endif
114#if defined(VSTATUS) && !defined(CSTATUS)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000115# define CSTATUS Control('t')
Eric Andersen98e599c2001-02-14 18:47:33 +0000116#endif
117
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000118/* Which speeds to set */
Eric Andersen98e599c2001-02-14 18:47:33 +0000119enum speed_setting {
120 input_speed, output_speed, both_speeds
121};
122
Denis Vlasenko79deb662006-09-19 15:12:12 +0000123/* Which member(s) of 'struct termios' a mode uses */
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000124enum {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000125 /* Do NOT change the order or values, as mode_type_flag()
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000126 * depends on them */
Eric Andersen98e599c2001-02-14 18:47:33 +0000127 control, input, output, local, combination
128};
129
Denis Vlasenko79deb662006-09-19 15:12:12 +0000130/* Flags for 'struct mode_info' */
131#define SANE_SET 1 /* Set in 'sane' mode */
132#define SANE_UNSET 2 /* Unset in 'sane' mode */
133#define REV 4 /* Can be turned off by prepending '-' */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000134#define OMIT 8 /* Don't display value */
Eric Andersen98e599c2001-02-14 18:47:33 +0000135
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000136/* Each mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000137struct mode_info {
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000138 const char name[9]; /* Name given on command line */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000139 const unsigned char type; /* Which structure element to change */
140 const unsigned char flags; /* Setting and display options */
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000141 /* were using short here, but ppc32 was unhappy: */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000142 const tcflag_t mask; /* Other bits to turn off for this mode */
143 const tcflag_t bits; /* Bits to set for this mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000144};
145
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000146enum {
147 /* Must match mode_info[] order! */
148 IDX_evenp = 0,
149 IDX_parity,
150 IDX_oddp,
151 IDX_nl,
152 IDX_ek,
153 IDX_sane,
154 IDX_cooked,
155 IDX_raw,
156 IDX_pass8,
157 IDX_litout,
158 IDX_cbreak,
159 IDX_crt,
160 IDX_dec,
161#ifdef IXANY
162 IDX_decctlq,
163#endif
164#if defined(TABDLY) || defined(OXTABS)
165 IDX_tabs,
166#endif
167#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
168 IDX_lcase,
169 IDX_LCASE,
170#endif
171};
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000172
Manuel Novoa III cad53642003-03-19 09:13:01 +0000173#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
174
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000175static const struct mode_info mode_info[] = {
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000176 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ),
177 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ),
178 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ),
179 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ),
180 MI_ENTRY("ek", combination, OMIT, 0, 0 ),
181 MI_ENTRY("sane", combination, OMIT, 0, 0 ),
182 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ),
183 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ),
184 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ),
185 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ),
186 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ),
187 MI_ENTRY("crt", combination, OMIT, 0, 0 ),
188 MI_ENTRY("dec", combination, OMIT, 0, 0 ),
189#ifdef IXANY
190 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ),
191#endif
192#if defined(TABDLY) || defined(OXTABS)
193 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ),
194#endif
195#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
196 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ),
197 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ),
198#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000199 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
200 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
201 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
202 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
203 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
204 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
205 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
206 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
207 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
208 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
209 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000210#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000211 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000212#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000213 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
214 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
215 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
216 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
217 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
218 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
219 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
220 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
221 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
222 MI_ENTRY("ixon", input, REV, IXON, 0 ),
223 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
224 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000225#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000226 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000227#endif
228#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000229 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000230#endif
231#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000232 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000233#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000234 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000235#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000236 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000237#endif
238#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000239 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000240#endif
241#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000242 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000243#endif
244#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000245 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000246#endif
247#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000248 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000249#endif
250#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000251 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000252#endif
253#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000254 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000255#endif
256#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000257 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
258 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000259#endif
260#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000261 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
262 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
263 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
264 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000265#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000266
Eric Andersen98e599c2001-02-14 18:47:33 +0000267#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000268 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
269 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
270 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
271 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000272#else
273# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000274 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000275# endif
276#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000277
Eric Andersen98e599c2001-02-14 18:47:33 +0000278#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000279 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
280 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000281#endif
282#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000283 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
284 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000285#endif
286#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000287 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
288 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000289#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000290 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
291 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000292#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000293 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000294#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000295 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
296 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
297 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
298 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
299 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
300 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000301#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000302 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000303#endif
304#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000305 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000306#endif
307#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000308 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
309 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000310#endif
311#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000312 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
313 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000314#endif
315#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000316 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
317 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000318#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000319};
320
Rob Landleybc68cd12006-03-10 19:22:06 +0000321enum {
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000322 NUM_mode_info = ARRAY_SIZE(mode_info)
Rob Landleybc68cd12006-03-10 19:22:06 +0000323};
Eric Andersen98e599c2001-02-14 18:47:33 +0000324
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000325/* Control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +0000326struct control_info {
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000327 const char name[7]; /* Name given on command line */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000328 const unsigned char saneval; /* Value to set for 'stty sane' */
329 const unsigned char offset; /* Offset in c_cc */
Eric Andersen98e599c2001-02-14 18:47:33 +0000330};
331
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000332enum {
333 /* Must match control_info[] order! */
334 CIDX_intr = 0,
335 CIDX_quit,
336 CIDX_erase,
337 CIDX_kill,
338 CIDX_eof,
339 CIDX_eol,
340#ifdef VEOL2
341 CIDX_eol2,
342#endif
343#ifdef VSWTCH
344 CIDX_swtch,
345#endif
346 CIDX_start,
347 CIDX_stop,
348 CIDX_susp,
349#ifdef VDSUSP
350 CIDX_dsusp,
351#endif
352#ifdef VREPRINT
353 CIDX_rprnt,
354#endif
355#ifdef VWERASE
356 CIDX_werase,
357#endif
358#ifdef VLNEXT
359 CIDX_lnext,
360#endif
361#ifdef VFLUSHO
362 CIDX_flush,
363#endif
364#ifdef VSTATUS
365 CIDX_status,
366#endif
367 CIDX_min,
368 CIDX_time,
369};
Eric Andersen98e599c2001-02-14 18:47:33 +0000370
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000371static const struct control_info control_info[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000372 {"intr", CINTR, VINTR},
373 {"quit", CQUIT, VQUIT},
374 {"erase", CERASE, VERASE},
375 {"kill", CKILL, VKILL},
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000376 {"eof", CEOF, VEOF},
377 {"eol", CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000378#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000379 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000380#endif
381#ifdef VSWTCH
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000382 {"swtch", CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000383#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000384 {"start", CSTART, VSTART},
385 {"stop", CSTOP, VSTOP},
386 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000387#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000388 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000389#endif
390#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000391 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000392#endif
393#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000394 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000395#endif
396#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000397 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000398#endif
399#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000400 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000401#endif
402#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000403 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000404#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000405 /* These must be last because of the display routines */
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000406 {"min", 1, VMIN},
407 {"time", 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000408};
409
Rob Landleybc68cd12006-03-10 19:22:06 +0000410enum {
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000411 NUM_control_info = ARRAY_SIZE(control_info)
Rob Landleybc68cd12006-03-10 19:22:06 +0000412};
Eric Andersen98e599c2001-02-14 18:47:33 +0000413
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000414/* The width of the screen, for output wrapping */
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000415unsigned max_col = 80; /* default */
416
417struct globals {
418 /* Current position, to know when to wrap */
419 unsigned current_col;
420 char buf[10];
421};
422#define G (*(struct globals*)&bb_common_bufsiz1)
423
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000424static const char *device_name = bb_msg_standard_input;
Eric Andersen8876fb22003-06-20 09:01:58 +0000425
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000426/* Return a string that is the printable representation of character CH */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000427/* Adapted from 'cat' by Torbjorn Granlund */
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000428static const char *visible(unsigned ch)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000429{
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000430 char *bpout = G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000431
432 if (ch == _POSIX_VDISABLE)
433 return "<undef>";
434
435 if (ch >= 128) {
436 ch -= 128;
437 *bpout++ = 'M';
438 *bpout++ = '-';
439 }
440
441 if (ch < 32) {
442 *bpout++ = '^';
443 *bpout++ = ch + 64;
444 } else if (ch < 127) {
445 *bpout++ = ch;
446 } else {
447 *bpout++ = '^';
448 *bpout++ = '?';
449 }
450
451 *bpout = '\0';
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000452 return G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000453}
454
455static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
456{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000457 static const unsigned char tcflag_offsets[] ALIGN1 = {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000458 offsetof(struct termios, c_cflag), /* control */
459 offsetof(struct termios, c_iflag), /* input */
460 offsetof(struct termios, c_oflag), /* output */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000461 offsetof(struct termios, c_lflag) /* local */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000462 };
463
464 if (type <= local) {
465 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
466 }
467 return NULL;
468}
469
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000470static void set_speed_or_die(enum speed_setting type, const char *const arg,
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000471 struct termios * const mode)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000472{
473 speed_t baud;
474
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000475 baud = tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000476
477 if (type != output_speed) { /* either input or both */
478 cfsetispeed(mode, baud);
479 }
480 if (type != input_speed) { /* either output or both */
481 cfsetospeed(mode, baud);
482 }
483}
484
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000485static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000486{
487 bb_perror_msg_and_die(fmt, device_name);
488}
489
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000490static void perror_on_device(const char *fmt)
491{
492 bb_perror_msg(fmt, device_name);
493}
494
Eric Andersen98e599c2001-02-14 18:47:33 +0000495/* Print format string MESSAGE and optional args.
496 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000497 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000498static void wrapf(const char *message, ...)
499{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000500 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000501 va_list args;
Eric Andersen98e599c2001-02-14 18:47:33 +0000502 int buflen;
503
504 va_start(args, message);
Bernhard Reutner-Fischer8eb05492007-01-17 19:46:33 +0000505 buflen = vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000506 va_end(args);
Bernhard Reutner-Fischer1a250d92007-01-18 08:41:22 +0000507 /* We seem to be called only with suitable lengths, but check if
508 somebody failed to adhere to this assumption just to be sure. */
509 if (!buflen || buflen >= sizeof(buf)) return;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000510
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000511 if (G.current_col > 0) {
512 G.current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000513 if (buf[0] != '\n') {
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000514 if (G.current_col + buflen >= max_col) {
Denis Vlasenko4daad902007-09-27 10:20:47 +0000515 bb_putchar('\n');
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000516 G.current_col = 0;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000517 } else
Denis Vlasenko4daad902007-09-27 10:20:47 +0000518 bb_putchar(' ');
Denis Vlasenko79deb662006-09-19 15:12:12 +0000519 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000520 }
521 fputs(buf, stdout);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000522 G.current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000523 if (buf[buflen-1] == '\n')
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000524 G.current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000525}
526
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000527static void set_window_size(const int rows, const int cols)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000528{
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000529 struct winsize win = { 0, 0, 0, 0};
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000530
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000531 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000532 if (errno != EINVAL) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000533 goto bail;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000534 }
535 memset(&win, 0, sizeof(win));
536 }
537
538 if (rows >= 0)
539 win.ws_row = rows;
540 if (cols >= 0)
541 win.ws_col = cols;
542
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000543 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000544bail:
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000545 perror_on_device("%s");
546}
547
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000548static void display_window_size(const int fancy)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000549{
550 const char *fmt_str = "%s\0%s: no size information for this device";
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000551 unsigned width, height;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000552
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000553 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000554 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
555 perror_on_device(fmt_str);
556 }
557 } else {
558 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000559 height, width);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000560 }
561}
562
Eric Andersen98e599c2001-02-14 18:47:33 +0000563static const struct suffix_mult stty_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000564 { "b", 512 },
565 { "k", 1024 },
566 { "B", 1024 },
567 { }
Eric Andersen98e599c2001-02-14 18:47:33 +0000568};
569
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000570static const struct mode_info *find_mode(const char *name)
571{
572 int i;
573 for (i = 0; i < NUM_mode_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000574 if (!strcmp(name, mode_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000575 return &mode_info[i];
576 return 0;
577}
578
579static const struct control_info *find_control(const char *name)
580{
581 int i;
582 for (i = 0; i < NUM_control_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000583 if (!strcmp(name, control_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000584 return &control_info[i];
585 return 0;
586}
587
588enum {
589 param_need_arg = 0x80,
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000590 param_line = 1 | 0x80,
591 param_rows = 2 | 0x80,
592 param_cols = 3 | 0x80,
593 param_columns = 4 | 0x80,
594 param_size = 5,
595 param_speed = 6,
596 param_ispeed = 7 | 0x80,
597 param_ospeed = 8 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000598};
599
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000600static int find_param(const char *const name)
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000601{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000602 static const char params[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000603 "line\0" /* 1 */
604 "rows\0" /* 2 */
605 "cols\0" /* 3 */
606 "columns\0" /* 4 */
607 "size\0" /* 5 */
608 "speed\0" /* 6 */
609 "ispeed\0"
610 "ospeed\0";
611 int i = index_in_strings(params, name) + 1;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000612 if (i == 0)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000613 return 0;
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000614 if (i != 5 && i != 6)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000615 i |= 0x80;
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000616 return i;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000617}
618
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000619static int recover_mode(const char *arg, struct termios *mode)
620{
621 int i, n;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000622 unsigned chr;
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000623 unsigned long iflag, oflag, cflag, lflag;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000624
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000625 /* Scan into temporaries since it is too much trouble to figure out
626 the right format for 'tcflag_t' */
627 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
628 &iflag, &oflag, &cflag, &lflag, &n) != 4)
629 return 0;
630 mode->c_iflag = iflag;
631 mode->c_oflag = oflag;
632 mode->c_cflag = cflag;
633 mode->c_lflag = lflag;
634 arg += n;
635 for (i = 0; i < NCCS; ++i) {
636 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
637 return 0;
638 mode->c_cc[i] = chr;
639 arg += n;
640 }
641
642 /* Fail if there are too many fields */
643 if (*arg != '\0')
644 return 0;
645
646 return 1;
647}
648
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000649static void display_recoverable(const struct termios *mode,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000650 int ATTRIBUTE_UNUSED dummy)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000651{
652 int i;
653 printf("%lx:%lx:%lx:%lx",
654 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
655 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
656 for (i = 0; i < NCCS; ++i)
657 printf(":%x", (unsigned int) mode->c_cc[i]);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000658 bb_putchar('\n');
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000659}
660
661static void display_speed(const struct termios *mode, int fancy)
662{
663 //01234567 8 9
664 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
665 unsigned long ispeed, ospeed;
666
667 ospeed = ispeed = cfgetispeed(mode);
668 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
669 ispeed = ospeed; /* in case ispeed was 0 */
Denis Vlasenko4b924f32007-05-30 00:29:55 +0000670 //0123 4 5 6 7 8 9
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000671 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
672 }
673 if (fancy) fmt_str += 9;
674 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
675}
676
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000677static void do_display(const struct termios *mode, const int all)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000678{
679 int i;
680 tcflag_t *bitsp;
681 unsigned long mask;
682 int prev_type = control;
683
684 display_speed(mode, 1);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000685 if (all)
686 display_window_size(1);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000687#ifdef HAVE_C_LINE
688 wrapf("line = %d;\n", mode->c_line);
689#else
690 wrapf("\n");
691#endif
692
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000693 for (i = 0; i != CIDX_min; ++i) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000694 /* If swtch is the same as susp, don't print both */
695#if VSWTCH == VSUSP
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000696 if (i == CIDX_swtch)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000697 continue;
698#endif
699 /* If eof uses the same slot as min, only print whichever applies */
700#if VEOF == VMIN
701 if ((mode->c_lflag & ICANON) == 0
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000702 && (i == CIDX_eof || i == CIDX_eol)
703 ) {
704 continue;
705 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000706#endif
707 wrapf("%s = %s;", control_info[i].name,
708 visible(mode->c_cc[control_info[i].offset]));
709 }
710#if VEOF == VMIN
711 if ((mode->c_lflag & ICANON) == 0)
712#endif
713 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000714 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000715
716 for (i = 0; i < NUM_mode_info; ++i) {
717 if (mode_info[i].flags & OMIT)
718 continue;
719 if (mode_info[i].type != prev_type) {
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000720 /* wrapf("\n"); */
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000721 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000722 prev_type = mode_info[i].type;
723 }
724
725 bitsp = mode_type_flag(mode_info[i].type, mode);
726 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000727 if ((*bitsp & mask) == mode_info[i].bits) {
728 if (all || (mode_info[i].flags & SANE_UNSET))
729 wrapf("%s", mode_info[i].name);
730 } else {
731 if ((all && mode_info[i].flags & REV) ||
732 (!all &&
733 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
734 wrapf("-%s", mode_info[i].name);
735 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000736 }
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000737 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000738}
739
740static void sane_mode(struct termios *mode)
741{
742 int i;
743 tcflag_t *bitsp;
744
745 for (i = 0; i < NUM_control_info; ++i) {
746#if VMIN == VEOF
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000747 if (i == CIDX_min)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000748 break;
749#endif
750 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
751 }
752
753 for (i = 0; i < NUM_mode_info; ++i) {
754 if (mode_info[i].flags & SANE_SET) {
755 bitsp = mode_type_flag(mode_info[i].type, mode);
756 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
757 | mode_info[i].bits;
758 } else if (mode_info[i].flags & SANE_UNSET) {
759 bitsp = mode_type_flag(mode_info[i].type, mode);
760 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
761 & ~mode_info[i].bits;
762 }
763 }
764}
765
766/* Save set_mode from #ifdef forest plague */
767#ifndef ONLCR
768#define ONLCR 0
769#endif
770#ifndef OCRNL
771#define OCRNL 0
772#endif
773#ifndef ONLRET
774#define ONLRET 0
775#endif
776#ifndef XCASE
777#define XCASE 0
778#endif
779#ifndef IXANY
780#define IXANY 0
781#endif
782#ifndef TABDLY
783#define TABDLY 0
784#endif
785#ifndef OXTABS
786#define OXTABS 0
787#endif
788#ifndef IUCLC
789#define IUCLC 0
790#endif
791#ifndef OLCUC
792#define OLCUC 0
793#endif
794#ifndef ECHOCTL
795#define ECHOCTL 0
796#endif
797#ifndef ECHOKE
798#define ECHOKE 0
799#endif
800
801static void set_mode(const struct mode_info *info, int reversed,
802 struct termios *mode)
803{
804 tcflag_t *bitsp;
805
806 bitsp = mode_type_flag(info->type, mode);
807
808 if (bitsp) {
809 if (reversed)
810 *bitsp = *bitsp & ~info->mask & ~info->bits;
811 else
812 *bitsp = (*bitsp & ~info->mask) | info->bits;
813 return;
814 }
815
816 /* Combination mode */
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000817 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000818 if (reversed)
819 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
820 else
821 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000822 } else if (info == &mode_info[IDX_oddp]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000823 if (reversed)
824 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
825 else
826 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000827 } else if (info == &mode_info[IDX_nl]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000828 if (reversed) {
829 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
830 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
831 } else {
832 mode->c_iflag = mode->c_iflag & ~ICRNL;
833 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
834 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000835 } else if (info == &mode_info[IDX_ek]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000836 mode->c_cc[VERASE] = CERASE;
837 mode->c_cc[VKILL] = CKILL;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000838 } else if (info == &mode_info[IDX_sane]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000839 sane_mode(mode);
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000840 } else if (info == &mode_info[IDX_cbreak]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000841 if (reversed)
842 mode->c_lflag |= ICANON;
843 else
844 mode->c_lflag &= ~ICANON;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000845 } else if (info == &mode_info[IDX_pass8]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000846 if (reversed) {
847 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
848 mode->c_iflag |= ISTRIP;
849 } else {
850 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
851 mode->c_iflag &= ~ISTRIP;
852 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000853 } else if (info == &mode_info[IDX_litout]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000854 if (reversed) {
855 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
856 mode->c_iflag |= ISTRIP;
857 mode->c_oflag |= OPOST;
858 } else {
859 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
860 mode->c_iflag &= ~ISTRIP;
861 mode->c_oflag &= ~OPOST;
862 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000863 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000864 if ((info->name[0] == 'r' && reversed)
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000865 || (info->name[0] == 'c' && !reversed)
866 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000867 /* Cooked mode */
868 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
869 mode->c_oflag |= OPOST;
870 mode->c_lflag |= ISIG | ICANON;
871#if VMIN == VEOF
872 mode->c_cc[VEOF] = CEOF;
873#endif
874#if VTIME == VEOL
875 mode->c_cc[VEOL] = CEOL;
876#endif
877 } else {
878 /* Raw mode */
879 mode->c_iflag = 0;
880 mode->c_oflag &= ~OPOST;
881 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
882 mode->c_cc[VMIN] = 1;
883 mode->c_cc[VTIME] = 0;
884 }
885 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000886 else if (IXANY && info == &mode_info[IDX_decctlq]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000887 if (reversed)
888 mode->c_iflag |= IXANY;
889 else
890 mode->c_iflag &= ~IXANY;
891 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000892 else if (TABDLY && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000893 if (reversed)
894 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
895 else
896 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
897 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000898 else if (OXTABS && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000899 if (reversed)
900 mode->c_oflag |= OXTABS;
901 else
902 mode->c_oflag &= ~OXTABS;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000903 } else
904 if (XCASE && IUCLC && OLCUC
905 && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
906 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000907 if (reversed) {
908 mode->c_lflag &= ~XCASE;
909 mode->c_iflag &= ~IUCLC;
910 mode->c_oflag &= ~OLCUC;
911 } else {
912 mode->c_lflag |= XCASE;
913 mode->c_iflag |= IUCLC;
914 mode->c_oflag |= OLCUC;
915 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000916 } else if (info == &mode_info[IDX_crt]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000917 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000918 } else if (info == &mode_info[IDX_dec]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000919 mode->c_cc[VINTR] = 3; /* ^C */
920 mode->c_cc[VERASE] = 127; /* DEL */
921 mode->c_cc[VKILL] = 21; /* ^U */
922 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
923 if (IXANY) mode->c_iflag &= ~IXANY;
924 }
925}
926
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000927static void set_control_char_or_die(const struct control_info *info,
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000928 const char *arg, struct termios *mode)
929{
930 unsigned char value;
931
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000932 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000933 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
934 else if (arg[0] == '\0' || arg[1] == '\0')
935 value = arg[0];
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000936 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000937 value = _POSIX_VDISABLE;
938 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
939 value = arg[1] & 0x1f; /* Non-letters get weird results */
940 if (arg[1] == '?')
941 value = 127;
942 } else
943 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
944 mode->c_cc[info->offset] = value;
945}
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000946
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000947#define STTY_require_set_attr (1<<0)
948#define STTY_speed_was_set (1<<1)
949#define STTY_verbose_output (1<<2)
950#define STTY_recoverable_output (1<<3)
951#define STTY_noargs (1<<4)
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000952int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Rob Landleydfba7412006-03-06 20:47:33 +0000953int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +0000954{
955 struct termios mode;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000956 void (*output_func)(const struct termios *, const int);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000957 const char *file_name = NULL;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000958 int display_all = 0;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000959 int stty_state;
960 int k;
961
962 stty_state = STTY_noargs;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000963 output_func = do_display;
Eric Andersen98e599c2001-02-14 18:47:33 +0000964
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000965 /* First pass: only parse/verify command line params */
966 k = 0;
967 while (argv[++k]) {
968 const struct mode_info *mp;
969 const struct control_info *cp;
970 const char *arg = argv[k];
971 const char *argnext = argv[k+1];
972 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000973
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000974 if (arg[0] == '-') {
975 int i;
976 mp = find_mode(arg+1);
977 if (mp) {
978 if (!(mp->flags & REV))
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +0000979 goto invalid_argument;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000980 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000981 continue;
982 }
983 /* It is an option - parse it */
984 i = 0;
985 while (arg[++i]) {
986 switch (arg[i]) {
987 case 'a':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000988 stty_state |= STTY_verbose_output;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000989 output_func = do_display;
990 display_all = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000991 break;
992 case 'g':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000993 stty_state |= STTY_recoverable_output;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000994 output_func = display_recoverable;
995 break;
996 case 'F':
997 if (file_name)
998 bb_error_msg_and_die("only one device may be specified");
999 file_name = &arg[i+1]; /* "-Fdevice" ? */
1000 if (!file_name[0]) { /* nope, "-F device" */
1001 int p = k+1; /* argv[p] is argnext */
1002 file_name = argnext;
1003 if (!file_name)
1004 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1005 /* remove -F param from arg[vc] */
1006 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +00001007 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001008 }
1009 goto end_option;
1010 default:
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001011 goto invalid_argument;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001012 }
1013 }
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001014 end_option:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001015 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001016 }
1017
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001018 mp = find_mode(arg);
1019 if (mp) {
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001020 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001021 continue;
1022 }
1023
1024 cp = find_control(arg);
1025 if (cp) {
1026 if (!argnext)
1027 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001028 /* called for the side effect of xfunc death only */
1029 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001030 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001031 ++k;
1032 continue;
1033 }
1034
1035 param = find_param(arg);
1036 if (param & param_need_arg) {
1037 if (!argnext)
1038 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1039 ++k;
1040 }
1041
1042 switch (param) {
1043#ifdef HAVE_C_LINE
1044 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001045# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +00001046 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001047 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001048# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001049#endif
1050#ifdef TIOCGWINSZ
1051 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001052 case param_cols:
Denis Vlasenko9ace6132007-04-19 19:55:54 +00001053 case param_columns:
Denis Vlasenko13858992006-10-08 12:49:22 +00001054 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001055 break;
1056 case param_size:
1057#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001058 case param_speed:
1059 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001060 case param_ispeed:
1061 /* called for the side effect of xfunc death only */
1062 set_speed_or_die(input_speed, argnext, &mode);
1063 break;
1064 case param_ospeed:
1065 /* called for the side effect of xfunc death only */
1066 set_speed_or_die(output_speed, argnext, &mode);
1067 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001068 default:
1069 if (recover_mode(arg, &mode) == 1) break;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001070 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001071 invalid_argument:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001072 bb_error_msg_and_die("invalid argument '%s'", arg);
1073 }
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001074 stty_state &= ~STTY_noargs;
Eric Andersen98e599c2001-02-14 18:47:33 +00001075 }
1076
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001077 /* Specifying both -a and -g is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001078 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001079 (STTY_verbose_output | STTY_recoverable_output))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001080 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1081 /* Specifying -a or -g with non-options is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001082 if (!(stty_state & STTY_noargs) &&
1083 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001084 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +00001085
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001086 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +00001087 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001088 int fd, fdflags;
Eric Andersen98e599c2001-02-14 18:47:33 +00001089 device_name = file_name;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001090 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +00001091 if (fd != STDIN_FILENO) {
1092 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001093 close(fd);
1094 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +00001095 fdflags = fcntl(STDIN_FILENO, F_GETFL);
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001096 if (fdflags < 0 ||
1097 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +00001098 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +00001099 }
1100
1101 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001102 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001103 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001104 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001105 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001106
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001107 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +00001108 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001109 output_func(&mode, display_all);
Eric Andersen98e599c2001-02-14 18:47:33 +00001110 return EXIT_SUCCESS;
1111 }
1112
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001113 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +00001114 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001115 while (argv[++k]) {
1116 const struct mode_info *mp;
1117 const struct control_info *cp;
1118 const char *arg = argv[k];
1119 const char *argnext = argv[k+1];
1120 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001121
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001122 if (arg[0] == '-') {
1123 mp = find_mode(arg+1);
1124 if (mp) {
1125 set_mode(mp, 1 /* reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001126 stty_state |= STTY_require_set_attr;
Eric Andersenfc059092002-06-06 11:35:29 +00001127 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001128 /* It is an option - already parsed. Skip it */
1129 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001130 }
Mark Whitley446dd272001-03-02 20:00:54 +00001131
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001132 mp = find_mode(arg);
1133 if (mp) {
1134 set_mode(mp, 0 /* non-reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001135 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001136 continue;
1137 }
Mark Whitley446dd272001-03-02 20:00:54 +00001138
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001139 cp = find_control(arg);
1140 if (cp) {
1141 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001142 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001143 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001144 continue;
1145 }
Mark Whitley446dd272001-03-02 20:00:54 +00001146
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001147 param = find_param(arg);
1148 if (param & param_need_arg) {
1149 ++k;
1150 }
1151
1152 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001153#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001154 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +00001155 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001156 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001157 break;
Eric Andersen98e599c2001-02-14 18:47:33 +00001158#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001159#ifdef TIOCGWINSZ
1160 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001161 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001162 break;
1163 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001164 display_window_size(0);
1165 break;
1166 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +00001167 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001168 break;
1169#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001170 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001171 display_speed(&mode, 0);
1172 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001173 case param_ispeed:
1174 set_speed_or_die(input_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001175 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001176 break;
1177 case param_ospeed:
1178 set_speed_or_die(output_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001179 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001180 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001181 default:
1182 if (recover_mode(arg, &mode) == 1)
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001183 stty_state |= STTY_require_set_attr;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001184 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001185 set_speed_or_die(both_speeds, arg, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001186 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001187 } /* else - impossible (caught in the first pass):
1188 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +00001189 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001190 }
1191
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001192 if (stty_state & STTY_require_set_attr) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001193 struct termios new_mode;
1194
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001195 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001196 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001197
1198 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1199 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +00001200 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +00001201 some proper subset of the requested operations. To detect
1202 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001203 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +00001204
1205 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001206 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001207 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001208 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001209 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001210
Eric Andersen98e599c2001-02-14 18:47:33 +00001211 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1212#ifdef CIBAUD
1213 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1214 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1215 sometimes (m1 != m2). The only difference is in the four bits
1216 of the c_cflag field corresponding to the baud rate. To save
1217 Sun users a little confusion, don't report an error if this
1218 happens. But suppress the error only if we haven't tried to
1219 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001220 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +00001221
1222 new_mode.c_cflag &= (~CIBAUD);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001223 if ((stty_state & STTY_speed_was_set)
1224 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
Eric Andersen98e599c2001-02-14 18:47:33 +00001225#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +00001226 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +00001227 }
1228 }
1229
1230 return EXIT_SUCCESS;
1231}