blob: a17955a6a3bacb8e176f01271f8323efbe309fbb [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 Vlasenkodd8bbfd2007-11-24 04:32:49 +0000136
137/* Each mode.
138 * This structure should be kept as small as humanly possible.
139 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000140struct mode_info {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000141 const uint8_t type; /* Which structure element to change */
142 const uint8_t flags; /* Setting and display options */
143 /* only these values are ever used, so... */
144#if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
145 const uint8_t mask;
146#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
147 const uint16_t mask;
148#else
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000149 const tcflag_t mask; /* Other bits to turn off for this mode */
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000150#endif
151 /* was using short here, but ppc32 was unhappy */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000152 const tcflag_t bits; /* Bits to set for this mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000153};
154
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000155enum {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000156 /* Must match mode_name[] and mode_info[] order! */
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000157 IDX_evenp = 0,
158 IDX_parity,
159 IDX_oddp,
160 IDX_nl,
161 IDX_ek,
162 IDX_sane,
163 IDX_cooked,
164 IDX_raw,
165 IDX_pass8,
166 IDX_litout,
167 IDX_cbreak,
168 IDX_crt,
169 IDX_dec,
170#ifdef IXANY
171 IDX_decctlq,
172#endif
173#if defined(TABDLY) || defined(OXTABS)
174 IDX_tabs,
175#endif
176#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
177 IDX_lcase,
178 IDX_LCASE,
179#endif
180};
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000181
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000182#define MI_ENTRY(N,T,F,B,M) N "\0"
Manuel Novoa III cad53642003-03-19 09:13:01 +0000183
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000184/* Mode names given on command line */
185static const char mode_name[] =
186 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
187 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
188 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
189 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
190 MI_ENTRY("ek", combination, OMIT, 0, 0 )
191 MI_ENTRY("sane", combination, OMIT, 0, 0 )
192 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
193 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
194 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
195 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
196 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
197 MI_ENTRY("crt", combination, OMIT, 0, 0 )
198 MI_ENTRY("dec", combination, OMIT, 0, 0 )
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000199#ifdef IXANY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000200 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000201#endif
202#if defined(TABDLY) || defined(OXTABS)
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000203 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000204#endif
205#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000206 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
207 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000208#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000209 MI_ENTRY("parenb", control, REV, PARENB, 0 )
210 MI_ENTRY("parodd", control, REV, PARODD, 0 )
211 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
212 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
213 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
214 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
215 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
216 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
217 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
218 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
219 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000220#ifdef CRTSCTS
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000221 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000222#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000223 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
224 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
225 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
226 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
227 MI_ENTRY("inpck", input, REV, INPCK, 0 )
228 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
229 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
230 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
231 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
232 MI_ENTRY("ixon", input, REV, IXON, 0 )
233 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
234 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000235#ifdef IUCLC
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000236 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000237#endif
238#ifdef IXANY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000239 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000240#endif
241#ifdef IMAXBEL
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000242 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000243#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000244 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000245#ifdef OLCUC
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000246 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000247#endif
248#ifdef OCRNL
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000249 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000250#endif
251#ifdef ONLCR
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000252 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000253#endif
254#ifdef ONOCR
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000255 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000256#endif
257#ifdef ONLRET
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000258 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000259#endif
260#ifdef OFILL
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000261 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000262#endif
263#ifdef OFDEL
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000264 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000265#endif
266#ifdef NLDLY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000267 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
268 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
Eric Andersen98e599c2001-02-14 18:47:33 +0000269#endif
270#ifdef CRDLY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000271 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
272 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
273 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
274 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
Eric Andersen98e599c2001-02-14 18:47:33 +0000275#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000276
Eric Andersen98e599c2001-02-14 18:47:33 +0000277#ifdef TABDLY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000278 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
279 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
280 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
281 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
Eric Andersen98e599c2001-02-14 18:47:33 +0000282#else
283# ifdef OXTABS
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000284 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000285# endif
286#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000287
Eric Andersen98e599c2001-02-14 18:47:33 +0000288#ifdef BSDLY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000289 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
290 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
Eric Andersen98e599c2001-02-14 18:47:33 +0000291#endif
292#ifdef VTDLY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000293 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
294 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
Eric Andersen98e599c2001-02-14 18:47:33 +0000295#endif
296#ifdef FFDLY
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000297 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
298 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
Eric Andersen98e599c2001-02-14 18:47:33 +0000299#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000300 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
301 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000302#ifdef IEXTEN
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000303 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000304#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000305 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
306 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
307 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
308 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
309 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
310 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000311#ifdef XCASE
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000312 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000313#endif
314#ifdef TOSTOP
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000315 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000316#endif
317#ifdef ECHOPRT
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000318 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
319 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000320#endif
321#ifdef ECHOCTL
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000322 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
323 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000324#endif
325#ifdef ECHOKE
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000326 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
327 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
328#endif
329 ;
330
331#undef MI_ENTRY
332#define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
333
334static const struct mode_info mode_info[] = {
335 /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
336 MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
337 MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
338 MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
339 MI_ENTRY("nl", combination, REV | OMIT, 0, 0 )
340 MI_ENTRY("ek", combination, OMIT, 0, 0 )
341 MI_ENTRY("sane", combination, OMIT, 0, 0 )
342 MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 )
343 MI_ENTRY("raw", combination, REV | OMIT, 0, 0 )
344 MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 )
345 MI_ENTRY("litout", combination, REV | OMIT, 0, 0 )
346 MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 )
347 MI_ENTRY("crt", combination, OMIT, 0, 0 )
348 MI_ENTRY("dec", combination, OMIT, 0, 0 )
349#ifdef IXANY
350 MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 )
351#endif
352#if defined(TABDLY) || defined(OXTABS)
353 MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 )
354#endif
355#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
356 MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 )
357 MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 )
358#endif
359 MI_ENTRY("parenb", control, REV, PARENB, 0 )
360 MI_ENTRY("parodd", control, REV, PARODD, 0 )
361 MI_ENTRY("cs5", control, 0, CS5, CSIZE)
362 MI_ENTRY("cs6", control, 0, CS6, CSIZE)
363 MI_ENTRY("cs7", control, 0, CS7, CSIZE)
364 MI_ENTRY("cs8", control, 0, CS8, CSIZE)
365 MI_ENTRY("hupcl", control, REV, HUPCL, 0 )
366 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 )
367 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 )
368 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 )
369 MI_ENTRY("clocal", control, REV, CLOCAL, 0 )
370#ifdef CRTSCTS
371 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 )
372#endif
373 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 )
374 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 )
375 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 )
376 MI_ENTRY("parmrk", input, REV, PARMRK, 0 )
377 MI_ENTRY("inpck", input, REV, INPCK, 0 )
378 MI_ENTRY("istrip", input, REV, ISTRIP, 0 )
379 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 )
380 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 )
381 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 )
382 MI_ENTRY("ixon", input, REV, IXON, 0 )
383 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 )
384 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 )
385#ifdef IUCLC
386 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 )
387#endif
388#ifdef IXANY
389 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 )
390#endif
391#ifdef IMAXBEL
392 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 )
393#endif
394 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 )
395#ifdef OLCUC
396 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 )
397#endif
398#ifdef OCRNL
399 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 )
400#endif
401#ifdef ONLCR
402 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 )
403#endif
404#ifdef ONOCR
405 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 )
406#endif
407#ifdef ONLRET
408 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 )
409#endif
410#ifdef OFILL
411 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 )
412#endif
413#ifdef OFDEL
414 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 )
415#endif
416#ifdef NLDLY
417 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY)
418 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY)
419#endif
420#ifdef CRDLY
421 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY)
422 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY)
423 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY)
424 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY)
425#endif
426
427#ifdef TABDLY
428 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY)
429 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY)
430 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY)
431 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY)
432#else
433# ifdef OXTABS
434 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 )
435# endif
436#endif
437
438#ifdef BSDLY
439 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY)
440 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY)
441#endif
442#ifdef VTDLY
443 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY)
444 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY)
445#endif
446#ifdef FFDLY
447 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY)
448 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY)
449#endif
450 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 )
451 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 )
452#ifdef IEXTEN
453 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 )
454#endif
455 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 )
456 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 )
457 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 )
458 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 )
459 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 )
460 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 )
461#ifdef XCASE
462 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 )
463#endif
464#ifdef TOSTOP
465 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 )
466#endif
467#ifdef ECHOPRT
468 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 )
469 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 )
470#endif
471#ifdef ECHOCTL
472 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 )
473 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 )
474#endif
475#ifdef ECHOKE
476 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 )
477 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000478#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000479};
480
Rob Landleybc68cd12006-03-10 19:22:06 +0000481enum {
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000482 NUM_mode_info = ARRAY_SIZE(mode_info)
Rob Landleybc68cd12006-03-10 19:22:06 +0000483};
Eric Andersen98e599c2001-02-14 18:47:33 +0000484
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000485
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000486/* Control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +0000487struct control_info {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000488 const uint8_t saneval; /* Value to set for 'stty sane' */
489 const uint8_t offset; /* Offset in c_cc */
Eric Andersen98e599c2001-02-14 18:47:33 +0000490};
491
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000492enum {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000493 /* Must match control_name[] and control_info[] order! */
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000494 CIDX_intr = 0,
495 CIDX_quit,
496 CIDX_erase,
497 CIDX_kill,
498 CIDX_eof,
499 CIDX_eol,
500#ifdef VEOL2
501 CIDX_eol2,
502#endif
503#ifdef VSWTCH
504 CIDX_swtch,
505#endif
506 CIDX_start,
507 CIDX_stop,
508 CIDX_susp,
509#ifdef VDSUSP
510 CIDX_dsusp,
511#endif
512#ifdef VREPRINT
513 CIDX_rprnt,
514#endif
515#ifdef VWERASE
516 CIDX_werase,
517#endif
518#ifdef VLNEXT
519 CIDX_lnext,
520#endif
521#ifdef VFLUSHO
522 CIDX_flush,
523#endif
524#ifdef VSTATUS
525 CIDX_status,
526#endif
527 CIDX_min,
528 CIDX_time,
529};
Eric Andersen98e599c2001-02-14 18:47:33 +0000530
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000531#define CI_ENTRY(n,s,o) n "\0"
532
533/* Name given on command line */
534static const char control_name[] =
535 CI_ENTRY("intr", CINTR, VINTR )
536 CI_ENTRY("quit", CQUIT, VQUIT )
537 CI_ENTRY("erase", CERASE, VERASE )
538 CI_ENTRY("kill", CKILL, VKILL )
539 CI_ENTRY("eof", CEOF, VEOF )
540 CI_ENTRY("eol", CEOL, VEOL )
Eric Andersen98e599c2001-02-14 18:47:33 +0000541#ifdef VEOL2
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000542 CI_ENTRY("eol2", CEOL2, VEOL2 )
Eric Andersen98e599c2001-02-14 18:47:33 +0000543#endif
544#ifdef VSWTCH
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000545 CI_ENTRY("swtch", CSWTCH, VSWTCH )
Eric Andersen98e599c2001-02-14 18:47:33 +0000546#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000547 CI_ENTRY("start", CSTART, VSTART )
548 CI_ENTRY("stop", CSTOP, VSTOP )
549 CI_ENTRY("susp", CSUSP, VSUSP )
Eric Andersen98e599c2001-02-14 18:47:33 +0000550#ifdef VDSUSP
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000551 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
Eric Andersen98e599c2001-02-14 18:47:33 +0000552#endif
553#ifdef VREPRINT
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000554 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
Eric Andersen98e599c2001-02-14 18:47:33 +0000555#endif
556#ifdef VWERASE
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000557 CI_ENTRY("werase", CWERASE, VWERASE )
Eric Andersen98e599c2001-02-14 18:47:33 +0000558#endif
559#ifdef VLNEXT
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000560 CI_ENTRY("lnext", CLNEXT, VLNEXT )
Eric Andersen98e599c2001-02-14 18:47:33 +0000561#endif
562#ifdef VFLUSHO
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000563 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
Eric Andersen98e599c2001-02-14 18:47:33 +0000564#endif
565#ifdef VSTATUS
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000566 CI_ENTRY("status", CSTATUS, VSTATUS )
Eric Andersen98e599c2001-02-14 18:47:33 +0000567#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000568 /* These must be last because of the display routines */
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000569 CI_ENTRY("min", 1, VMIN )
570 CI_ENTRY("time", 0, VTIME )
571 ;
572
573#undef CI_ENTRY
574#define CI_ENTRY(n,s,o) { s, o },
575
576static const struct control_info control_info[] = {
577 /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
578 CI_ENTRY("intr", CINTR, VINTR )
579 CI_ENTRY("quit", CQUIT, VQUIT )
580 CI_ENTRY("erase", CERASE, VERASE )
581 CI_ENTRY("kill", CKILL, VKILL )
582 CI_ENTRY("eof", CEOF, VEOF )
583 CI_ENTRY("eol", CEOL, VEOL )
584#ifdef VEOL2
585 CI_ENTRY("eol2", CEOL2, VEOL2 )
586#endif
587#ifdef VSWTCH
588 CI_ENTRY("swtch", CSWTCH, VSWTCH )
589#endif
590 CI_ENTRY("start", CSTART, VSTART )
591 CI_ENTRY("stop", CSTOP, VSTOP )
592 CI_ENTRY("susp", CSUSP, VSUSP )
593#ifdef VDSUSP
594 CI_ENTRY("dsusp", CDSUSP, VDSUSP )
595#endif
596#ifdef VREPRINT
597 CI_ENTRY("rprnt", CRPRNT, VREPRINT)
598#endif
599#ifdef VWERASE
600 CI_ENTRY("werase", CWERASE, VWERASE )
601#endif
602#ifdef VLNEXT
603 CI_ENTRY("lnext", CLNEXT, VLNEXT )
604#endif
605#ifdef VFLUSHO
606 CI_ENTRY("flush", CFLUSHO, VFLUSHO )
607#endif
608#ifdef VSTATUS
609 CI_ENTRY("status", CSTATUS, VSTATUS )
610#endif
611 /* These must be last because of the display routines */
612 CI_ENTRY("min", 1, VMIN )
613 CI_ENTRY("time", 0, VTIME )
Eric Andersen98e599c2001-02-14 18:47:33 +0000614};
615
Rob Landleybc68cd12006-03-10 19:22:06 +0000616enum {
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000617 NUM_control_info = ARRAY_SIZE(control_info)
Rob Landleybc68cd12006-03-10 19:22:06 +0000618};
Eric Andersen98e599c2001-02-14 18:47:33 +0000619
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000620
621struct globals {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000622 const char *device_name; // = bb_msg_standard_input;
623 /* The width of the screen, for output wrapping */
624 unsigned max_col; // = 80;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000625 /* Current position, to know when to wrap */
626 unsigned current_col;
627 char buf[10];
628};
629#define G (*(struct globals*)&bb_common_bufsiz1)
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000630#define INIT_G() \
631 do { \
632 G.device_name = bb_msg_standard_input; \
633 G.max_col = 80; \
634 } while (0)
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000635
Eric Andersen8876fb22003-06-20 09:01:58 +0000636
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000637/* Return a string that is the printable representation of character CH */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000638/* Adapted from 'cat' by Torbjorn Granlund */
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000639static const char *visible(unsigned ch)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000640{
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000641 char *bpout = G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000642
643 if (ch == _POSIX_VDISABLE)
644 return "<undef>";
645
646 if (ch >= 128) {
647 ch -= 128;
648 *bpout++ = 'M';
649 *bpout++ = '-';
650 }
651
652 if (ch < 32) {
653 *bpout++ = '^';
654 *bpout++ = ch + 64;
655 } else if (ch < 127) {
656 *bpout++ = ch;
657 } else {
658 *bpout++ = '^';
659 *bpout++ = '?';
660 }
661
662 *bpout = '\0';
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000663 return G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000664}
665
666static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
667{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000668 static const uint8_t tcflag_offsets[] ALIGN1 = {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000669 offsetof(struct termios, c_cflag), /* control */
670 offsetof(struct termios, c_iflag), /* input */
671 offsetof(struct termios, c_oflag), /* output */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000672 offsetof(struct termios, c_lflag) /* local */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000673 };
674
675 if (type <= local) {
676 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
677 }
678 return NULL;
679}
680
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000681static void set_speed_or_die(enum speed_setting type, const char *const arg,
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000682 struct termios * const mode)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000683{
684 speed_t baud;
685
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000686 baud = tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000687
688 if (type != output_speed) { /* either input or both */
689 cfsetispeed(mode, baud);
690 }
691 if (type != input_speed) { /* either output or both */
692 cfsetospeed(mode, baud);
693 }
694}
695
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000696static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000697{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000698 bb_perror_msg_and_die(fmt, G.device_name);
Eric Andersen8876fb22003-06-20 09:01:58 +0000699}
700
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000701static void perror_on_device(const char *fmt)
702{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000703 bb_perror_msg(fmt, G.device_name);
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000704}
705
Eric Andersen98e599c2001-02-14 18:47:33 +0000706/* Print format string MESSAGE and optional args.
707 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000708 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000709static void wrapf(const char *message, ...)
710{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000711 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000712 va_list args;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000713 unsigned buflen;
Eric Andersen98e599c2001-02-14 18:47:33 +0000714
715 va_start(args, message);
Bernhard Reutner-Fischer8eb05492007-01-17 19:46:33 +0000716 buflen = vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000717 va_end(args);
Bernhard Reutner-Fischer1a250d92007-01-18 08:41:22 +0000718 /* We seem to be called only with suitable lengths, but check if
719 somebody failed to adhere to this assumption just to be sure. */
720 if (!buflen || buflen >= sizeof(buf)) return;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000721
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000722 if (G.current_col > 0) {
723 G.current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000724 if (buf[0] != '\n') {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000725 if (G.current_col + buflen >= G.max_col) {
Denis Vlasenko4daad902007-09-27 10:20:47 +0000726 bb_putchar('\n');
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000727 G.current_col = 0;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000728 } else
Denis Vlasenko4daad902007-09-27 10:20:47 +0000729 bb_putchar(' ');
Denis Vlasenko79deb662006-09-19 15:12:12 +0000730 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000731 }
732 fputs(buf, stdout);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000733 G.current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000734 if (buf[buflen-1] == '\n')
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000735 G.current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000736}
737
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000738static void set_window_size(const int rows, const int cols)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000739{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000740 struct winsize win = { 0, 0, 0, 0 };
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000741
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000742 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000743 if (errno != EINVAL) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000744 goto bail;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000745 }
746 memset(&win, 0, sizeof(win));
747 }
748
749 if (rows >= 0)
750 win.ws_row = rows;
751 if (cols >= 0)
752 win.ws_col = cols;
753
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000754 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000755bail:
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000756 perror_on_device("%s");
757}
758
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000759static void display_window_size(const int fancy)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000760{
761 const char *fmt_str = "%s\0%s: no size information for this device";
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000762 unsigned width, height;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000763
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000764 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000765 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
766 perror_on_device(fmt_str);
767 }
768 } else {
769 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000770 height, width);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000771 }
772}
773
Eric Andersen98e599c2001-02-14 18:47:33 +0000774static const struct suffix_mult stty_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000775 { "b", 512 },
776 { "k", 1024 },
777 { "B", 1024 },
778 { }
Eric Andersen98e599c2001-02-14 18:47:33 +0000779};
780
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000781static const struct mode_info *find_mode(const char *name)
782{
Denis Vlasenkoda42bd52008-01-27 23:24:31 +0000783 int i = index_in_strings(mode_name, name);
784 return i >= 0 ? &mode_info[i] : NULL;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000785}
786
787static const struct control_info *find_control(const char *name)
788{
Denis Vlasenkoda42bd52008-01-27 23:24:31 +0000789 int i = index_in_strings(control_name, name);
790 return i >= 0 ? &control_info[i] : NULL;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000791}
792
793enum {
794 param_need_arg = 0x80,
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000795 param_line = 1 | 0x80,
796 param_rows = 2 | 0x80,
797 param_cols = 3 | 0x80,
798 param_columns = 4 | 0x80,
799 param_size = 5,
800 param_speed = 6,
801 param_ispeed = 7 | 0x80,
802 param_ospeed = 8 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000803};
804
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000805static int find_param(const char *const name)
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000806{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000807 static const char params[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000808 "line\0" /* 1 */
809 "rows\0" /* 2 */
810 "cols\0" /* 3 */
811 "columns\0" /* 4 */
812 "size\0" /* 5 */
813 "speed\0" /* 6 */
814 "ispeed\0"
815 "ospeed\0";
816 int i = index_in_strings(params, name) + 1;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000817 if (i == 0)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000818 return 0;
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000819 if (i != 5 && i != 6)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000820 i |= 0x80;
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000821 return i;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000822}
823
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000824static int recover_mode(const char *arg, struct termios *mode)
825{
826 int i, n;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000827 unsigned chr;
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000828 unsigned long iflag, oflag, cflag, lflag;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000829
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000830 /* Scan into temporaries since it is too much trouble to figure out
831 the right format for 'tcflag_t' */
832 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
833 &iflag, &oflag, &cflag, &lflag, &n) != 4)
834 return 0;
835 mode->c_iflag = iflag;
836 mode->c_oflag = oflag;
837 mode->c_cflag = cflag;
838 mode->c_lflag = lflag;
839 arg += n;
840 for (i = 0; i < NCCS; ++i) {
841 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
842 return 0;
843 mode->c_cc[i] = chr;
844 arg += n;
845 }
846
847 /* Fail if there are too many fields */
848 if (*arg != '\0')
849 return 0;
850
851 return 1;
852}
853
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000854static void display_recoverable(const struct termios *mode,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000855 int ATTRIBUTE_UNUSED dummy)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000856{
857 int i;
858 printf("%lx:%lx:%lx:%lx",
859 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
860 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
861 for (i = 0; i < NCCS; ++i)
862 printf(":%x", (unsigned int) mode->c_cc[i]);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000863 bb_putchar('\n');
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000864}
865
866static void display_speed(const struct termios *mode, int fancy)
867{
868 //01234567 8 9
869 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
870 unsigned long ispeed, ospeed;
871
872 ospeed = ispeed = cfgetispeed(mode);
873 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
874 ispeed = ospeed; /* in case ispeed was 0 */
Denis Vlasenko4b924f32007-05-30 00:29:55 +0000875 //0123 4 5 6 7 8 9
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000876 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
877 }
878 if (fancy) fmt_str += 9;
879 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
880}
881
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000882static void do_display(const struct termios *mode, const int all)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000883{
884 int i;
885 tcflag_t *bitsp;
886 unsigned long mask;
887 int prev_type = control;
888
889 display_speed(mode, 1);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000890 if (all)
891 display_window_size(1);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000892#ifdef HAVE_C_LINE
893 wrapf("line = %d;\n", mode->c_line);
894#else
895 wrapf("\n");
896#endif
897
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000898 for (i = 0; i != CIDX_min; ++i) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000899 /* If swtch is the same as susp, don't print both */
900#if VSWTCH == VSUSP
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000901 if (i == CIDX_swtch)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000902 continue;
903#endif
904 /* If eof uses the same slot as min, only print whichever applies */
905#if VEOF == VMIN
906 if ((mode->c_lflag & ICANON) == 0
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000907 && (i == CIDX_eof || i == CIDX_eol)
908 ) {
909 continue;
910 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000911#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000912 wrapf("%s = %s;", nth_string(control_name, i),
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000913 visible(mode->c_cc[control_info[i].offset]));
914 }
915#if VEOF == VMIN
916 if ((mode->c_lflag & ICANON) == 0)
917#endif
918 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000919 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000920
921 for (i = 0; i < NUM_mode_info; ++i) {
922 if (mode_info[i].flags & OMIT)
923 continue;
924 if (mode_info[i].type != prev_type) {
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000925 /* wrapf("\n"); */
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000926 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000927 prev_type = mode_info[i].type;
928 }
929
930 bitsp = mode_type_flag(mode_info[i].type, mode);
931 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000932 if ((*bitsp & mask) == mode_info[i].bits) {
933 if (all || (mode_info[i].flags & SANE_UNSET))
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000934 wrapf("-%s"+1, nth_string(mode_name, i));
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000935 } else {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000936 if ((all && mode_info[i].flags & REV)
937 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
938 ) {
939 wrapf("-%s", nth_string(mode_name, i));
940 }
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000941 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000942 }
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000943 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000944}
945
946static void sane_mode(struct termios *mode)
947{
948 int i;
949 tcflag_t *bitsp;
950
951 for (i = 0; i < NUM_control_info; ++i) {
952#if VMIN == VEOF
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000953 if (i == CIDX_min)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000954 break;
955#endif
956 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
957 }
958
959 for (i = 0; i < NUM_mode_info; ++i) {
960 if (mode_info[i].flags & SANE_SET) {
961 bitsp = mode_type_flag(mode_info[i].type, mode);
962 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
963 | mode_info[i].bits;
964 } else if (mode_info[i].flags & SANE_UNSET) {
965 bitsp = mode_type_flag(mode_info[i].type, mode);
966 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
967 & ~mode_info[i].bits;
968 }
969 }
970}
971
972/* Save set_mode from #ifdef forest plague */
973#ifndef ONLCR
974#define ONLCR 0
975#endif
976#ifndef OCRNL
977#define OCRNL 0
978#endif
979#ifndef ONLRET
980#define ONLRET 0
981#endif
982#ifndef XCASE
983#define XCASE 0
984#endif
985#ifndef IXANY
986#define IXANY 0
987#endif
988#ifndef TABDLY
989#define TABDLY 0
990#endif
991#ifndef OXTABS
992#define OXTABS 0
993#endif
994#ifndef IUCLC
995#define IUCLC 0
996#endif
997#ifndef OLCUC
998#define OLCUC 0
999#endif
1000#ifndef ECHOCTL
1001#define ECHOCTL 0
1002#endif
1003#ifndef ECHOKE
1004#define ECHOKE 0
1005#endif
1006
1007static void set_mode(const struct mode_info *info, int reversed,
1008 struct termios *mode)
1009{
1010 tcflag_t *bitsp;
1011
1012 bitsp = mode_type_flag(info->type, mode);
1013
1014 if (bitsp) {
1015 if (reversed)
1016 *bitsp = *bitsp & ~info->mask & ~info->bits;
1017 else
1018 *bitsp = (*bitsp & ~info->mask) | info->bits;
1019 return;
1020 }
1021
1022 /* Combination mode */
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001023 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001024 if (reversed)
1025 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1026 else
1027 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001028 } else if (info == &mode_info[IDX_oddp]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001029 if (reversed)
1030 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1031 else
1032 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001033 } else if (info == &mode_info[IDX_nl]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001034 if (reversed) {
1035 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1036 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1037 } else {
1038 mode->c_iflag = mode->c_iflag & ~ICRNL;
1039 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1040 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001041 } else if (info == &mode_info[IDX_ek]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001042 mode->c_cc[VERASE] = CERASE;
1043 mode->c_cc[VKILL] = CKILL;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001044 } else if (info == &mode_info[IDX_sane]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001045 sane_mode(mode);
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001046 } else if (info == &mode_info[IDX_cbreak]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001047 if (reversed)
1048 mode->c_lflag |= ICANON;
1049 else
1050 mode->c_lflag &= ~ICANON;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001051 } else if (info == &mode_info[IDX_pass8]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001052 if (reversed) {
1053 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1054 mode->c_iflag |= ISTRIP;
1055 } else {
1056 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1057 mode->c_iflag &= ~ISTRIP;
1058 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001059 } else if (info == &mode_info[IDX_litout]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001060 if (reversed) {
1061 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1062 mode->c_iflag |= ISTRIP;
1063 mode->c_oflag |= OPOST;
1064 } else {
1065 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1066 mode->c_iflag &= ~ISTRIP;
1067 mode->c_oflag &= ~OPOST;
1068 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001069 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001070 if ((info == &mode_info[IDX_raw] && reversed)
1071 || (info == &mode_info[IDX_cooked] && !reversed)
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001072 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001073 /* Cooked mode */
1074 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1075 mode->c_oflag |= OPOST;
1076 mode->c_lflag |= ISIG | ICANON;
1077#if VMIN == VEOF
1078 mode->c_cc[VEOF] = CEOF;
1079#endif
1080#if VTIME == VEOL
1081 mode->c_cc[VEOL] = CEOL;
1082#endif
1083 } else {
1084 /* Raw mode */
1085 mode->c_iflag = 0;
1086 mode->c_oflag &= ~OPOST;
1087 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1088 mode->c_cc[VMIN] = 1;
1089 mode->c_cc[VTIME] = 0;
1090 }
1091 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001092 else if (IXANY && info == &mode_info[IDX_decctlq]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001093 if (reversed)
1094 mode->c_iflag |= IXANY;
1095 else
1096 mode->c_iflag &= ~IXANY;
1097 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001098 else if (TABDLY && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001099 if (reversed)
1100 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1101 else
1102 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1103 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001104 else if (OXTABS && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001105 if (reversed)
1106 mode->c_oflag |= OXTABS;
1107 else
1108 mode->c_oflag &= ~OXTABS;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001109 } else
1110 if (XCASE && IUCLC && OLCUC
1111 && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
1112 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001113 if (reversed) {
1114 mode->c_lflag &= ~XCASE;
1115 mode->c_iflag &= ~IUCLC;
1116 mode->c_oflag &= ~OLCUC;
1117 } else {
1118 mode->c_lflag |= XCASE;
1119 mode->c_iflag |= IUCLC;
1120 mode->c_oflag |= OLCUC;
1121 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001122 } else if (info == &mode_info[IDX_crt]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001123 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001124 } else if (info == &mode_info[IDX_dec]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001125 mode->c_cc[VINTR] = 3; /* ^C */
1126 mode->c_cc[VERASE] = 127; /* DEL */
1127 mode->c_cc[VKILL] = 21; /* ^U */
1128 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1129 if (IXANY) mode->c_iflag &= ~IXANY;
1130 }
1131}
1132
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001133static void set_control_char_or_die(const struct control_info *info,
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001134 const char *arg, struct termios *mode)
1135{
1136 unsigned char value;
1137
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001138 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001139 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1140 else if (arg[0] == '\0' || arg[1] == '\0')
1141 value = arg[0];
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +00001142 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001143 value = _POSIX_VDISABLE;
1144 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1145 value = arg[1] & 0x1f; /* Non-letters get weird results */
1146 if (arg[1] == '?')
1147 value = 127;
1148 } else
1149 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1150 mode->c_cc[info->offset] = value;
1151}
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001152
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001153#define STTY_require_set_attr (1 << 0)
1154#define STTY_speed_was_set (1 << 1)
1155#define STTY_verbose_output (1 << 2)
1156#define STTY_recoverable_output (1 << 3)
1157#define STTY_noargs (1 << 4)
1158
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001159int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Rob Landleydfba7412006-03-06 20:47:33 +00001160int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +00001161{
1162 struct termios mode;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001163 void (*output_func)(const struct termios *, const int);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001164 const char *file_name = NULL;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001165 int display_all = 0;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001166 int stty_state;
1167 int k;
1168
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001169 INIT_G();
1170
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001171 stty_state = STTY_noargs;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001172 output_func = do_display;
Eric Andersen98e599c2001-02-14 18:47:33 +00001173
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001174 /* First pass: only parse/verify command line params */
1175 k = 0;
1176 while (argv[++k]) {
1177 const struct mode_info *mp;
1178 const struct control_info *cp;
1179 const char *arg = argv[k];
1180 const char *argnext = argv[k+1];
1181 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001182
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001183 if (arg[0] == '-') {
1184 int i;
1185 mp = find_mode(arg+1);
1186 if (mp) {
1187 if (!(mp->flags & REV))
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001188 goto invalid_argument;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001189 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001190 continue;
1191 }
1192 /* It is an option - parse it */
1193 i = 0;
1194 while (arg[++i]) {
1195 switch (arg[i]) {
1196 case 'a':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001197 stty_state |= STTY_verbose_output;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001198 output_func = do_display;
1199 display_all = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001200 break;
1201 case 'g':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001202 stty_state |= STTY_recoverable_output;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001203 output_func = display_recoverable;
1204 break;
1205 case 'F':
1206 if (file_name)
1207 bb_error_msg_and_die("only one device may be specified");
1208 file_name = &arg[i+1]; /* "-Fdevice" ? */
1209 if (!file_name[0]) { /* nope, "-F device" */
1210 int p = k+1; /* argv[p] is argnext */
1211 file_name = argnext;
1212 if (!file_name)
1213 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1214 /* remove -F param from arg[vc] */
1215 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +00001216 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001217 }
1218 goto end_option;
1219 default:
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001220 goto invalid_argument;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001221 }
1222 }
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001223 end_option:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001224 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001225 }
1226
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001227 mp = find_mode(arg);
1228 if (mp) {
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001229 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001230 continue;
1231 }
1232
1233 cp = find_control(arg);
1234 if (cp) {
1235 if (!argnext)
1236 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001237 /* called for the side effect of xfunc death only */
1238 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001239 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001240 ++k;
1241 continue;
1242 }
1243
1244 param = find_param(arg);
1245 if (param & param_need_arg) {
1246 if (!argnext)
1247 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1248 ++k;
1249 }
1250
1251 switch (param) {
1252#ifdef HAVE_C_LINE
1253 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001254# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +00001255 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001256 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001257# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001258#endif
1259#ifdef TIOCGWINSZ
1260 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001261 case param_cols:
Denis Vlasenko9ace6132007-04-19 19:55:54 +00001262 case param_columns:
Denis Vlasenko13858992006-10-08 12:49:22 +00001263 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001264 break;
1265 case param_size:
1266#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001267 case param_speed:
1268 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001269 case param_ispeed:
1270 /* called for the side effect of xfunc death only */
1271 set_speed_or_die(input_speed, argnext, &mode);
1272 break;
1273 case param_ospeed:
1274 /* called for the side effect of xfunc death only */
1275 set_speed_or_die(output_speed, argnext, &mode);
1276 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001277 default:
1278 if (recover_mode(arg, &mode) == 1) break;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001279 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001280 invalid_argument:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001281 bb_error_msg_and_die("invalid argument '%s'", arg);
1282 }
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001283 stty_state &= ~STTY_noargs;
Eric Andersen98e599c2001-02-14 18:47:33 +00001284 }
1285
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001286 /* Specifying both -a and -g is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001287 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001288 (STTY_verbose_output | STTY_recoverable_output))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001289 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1290 /* Specifying -a or -g with non-options is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001291 if (!(stty_state & STTY_noargs) &&
1292 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001293 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +00001294
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001295 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +00001296 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001297 int fd, fdflags;
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001298 G.device_name = file_name;
1299 fd = xopen(G.device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +00001300 if (fd != STDIN_FILENO) {
1301 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001302 close(fd);
1303 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +00001304 fdflags = fcntl(STDIN_FILENO, F_GETFL);
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001305 if (fdflags < 0 ||
1306 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +00001307 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +00001308 }
1309
1310 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001311 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001312 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001313 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001314 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001315
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001316 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001317 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001318 output_func(&mode, display_all);
Eric Andersen98e599c2001-02-14 18:47:33 +00001319 return EXIT_SUCCESS;
1320 }
1321
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001322 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +00001323 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001324 while (argv[++k]) {
1325 const struct mode_info *mp;
1326 const struct control_info *cp;
1327 const char *arg = argv[k];
1328 const char *argnext = argv[k+1];
1329 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001330
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001331 if (arg[0] == '-') {
1332 mp = find_mode(arg+1);
1333 if (mp) {
1334 set_mode(mp, 1 /* reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001335 stty_state |= STTY_require_set_attr;
Eric Andersenfc059092002-06-06 11:35:29 +00001336 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001337 /* It is an option - already parsed. Skip it */
1338 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001339 }
Mark Whitley446dd272001-03-02 20:00:54 +00001340
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001341 mp = find_mode(arg);
1342 if (mp) {
1343 set_mode(mp, 0 /* non-reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001344 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001345 continue;
1346 }
Mark Whitley446dd272001-03-02 20:00:54 +00001347
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001348 cp = find_control(arg);
1349 if (cp) {
1350 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001351 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001352 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001353 continue;
1354 }
Mark Whitley446dd272001-03-02 20:00:54 +00001355
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001356 param = find_param(arg);
1357 if (param & param_need_arg) {
1358 ++k;
1359 }
1360
1361 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001362#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001363 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +00001364 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001365 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001366 break;
Eric Andersen98e599c2001-02-14 18:47:33 +00001367#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001368#ifdef TIOCGWINSZ
1369 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001370 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001371 break;
1372 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001373 display_window_size(0);
1374 break;
1375 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +00001376 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001377 break;
1378#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001379 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001380 display_speed(&mode, 0);
1381 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001382 case param_ispeed:
1383 set_speed_or_die(input_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001384 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001385 break;
1386 case param_ospeed:
1387 set_speed_or_die(output_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001388 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001389 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001390 default:
1391 if (recover_mode(arg, &mode) == 1)
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001392 stty_state |= STTY_require_set_attr;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001393 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001394 set_speed_or_die(both_speeds, arg, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001395 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001396 } /* else - impossible (caught in the first pass):
1397 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +00001398 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001399 }
1400
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001401 if (stty_state & STTY_require_set_attr) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001402 struct termios new_mode;
1403
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001404 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001405 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001406
1407 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1408 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +00001409 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +00001410 some proper subset of the requested operations. To detect
1411 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001412 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +00001413
1414 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001415 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001416 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001417 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001418 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001419
Eric Andersen98e599c2001-02-14 18:47:33 +00001420 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1421#ifdef CIBAUD
1422 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1423 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1424 sometimes (m1 != m2). The only difference is in the four bits
1425 of the c_cflag field corresponding to the baud rate. To save
1426 Sun users a little confusion, don't report an error if this
1427 happens. But suppress the error only if we haven't tried to
1428 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001429 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +00001430
1431 new_mode.c_cflag &= (~CIBAUD);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001432 if ((stty_state & STTY_speed_was_set)
1433 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
Eric Andersen98e599c2001-02-14 18:47:33 +00001434#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +00001435 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +00001436 }
1437 }
1438
1439 return EXIT_SUCCESS;
1440}