blob: c40d718affd27bdbd348d690a2daa3c19dbc32af [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 {
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100622 const char *device_name;
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000623 /* The width of the screen, for output wrapping */
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100624 unsigned max_col;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000625 /* Current position, to know when to wrap */
626 unsigned current_col;
627 char buf[10];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100628} FIX_ALIASING;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000629#define G (*(struct globals*)&bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000630#define INIT_G() do { \
631 G.device_name = bb_msg_standard_input; \
632 G.max_col = 80; \
633} while (0)
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000634
Eric Andersen8876fb22003-06-20 09:01:58 +0000635
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000636/* Return a string that is the printable representation of character CH */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000637/* Adapted from 'cat' by Torbjorn Granlund */
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000638static const char *visible(unsigned ch)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000639{
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000640 char *bpout = G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000641
642 if (ch == _POSIX_VDISABLE)
643 return "<undef>";
644
645 if (ch >= 128) {
646 ch -= 128;
647 *bpout++ = 'M';
648 *bpout++ = '-';
649 }
650
651 if (ch < 32) {
652 *bpout++ = '^';
653 *bpout++ = ch + 64;
654 } else if (ch < 127) {
655 *bpout++ = ch;
656 } else {
657 *bpout++ = '^';
658 *bpout++ = '?';
659 }
660
661 *bpout = '\0';
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000662 return G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000663}
664
665static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
666{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000667 static const uint8_t tcflag_offsets[] ALIGN1 = {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000668 offsetof(struct termios, c_cflag), /* control */
669 offsetof(struct termios, c_iflag), /* input */
670 offsetof(struct termios, c_oflag), /* output */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000671 offsetof(struct termios, c_lflag) /* local */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000672 };
673
674 if (type <= local) {
675 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
676 }
677 return NULL;
678}
679
Denis Vlasenkoc7cc5a92009-04-19 01:27:20 +0000680static void set_speed_or_die(enum speed_setting type, const char *arg,
681 struct termios *mode)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000682{
683 speed_t baud;
684
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000685 baud = tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000686
687 if (type != output_speed) { /* either input or both */
688 cfsetispeed(mode, baud);
689 }
690 if (type != input_speed) { /* either output or both */
691 cfsetospeed(mode, baud);
692 }
693}
694
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000695static NORETURN void perror_on_device_and_die(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000696{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000697 bb_perror_msg_and_die(fmt, G.device_name);
Eric Andersen8876fb22003-06-20 09:01:58 +0000698}
699
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000700static void perror_on_device(const char *fmt)
701{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000702 bb_perror_msg(fmt, G.device_name);
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000703}
704
Eric Andersen98e599c2001-02-14 18:47:33 +0000705/* Print format string MESSAGE and optional args.
706 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000707 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000708static void wrapf(const char *message, ...)
709{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000710 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000711 va_list args;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000712 unsigned buflen;
Eric Andersen98e599c2001-02-14 18:47:33 +0000713
714 va_start(args, message);
Bernhard Reutner-Fischer8eb05492007-01-17 19:46:33 +0000715 buflen = vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000716 va_end(args);
Bernhard Reutner-Fischer1a250d92007-01-18 08:41:22 +0000717 /* We seem to be called only with suitable lengths, but check if
718 somebody failed to adhere to this assumption just to be sure. */
719 if (!buflen || buflen >= sizeof(buf)) return;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000720
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000721 if (G.current_col > 0) {
722 G.current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000723 if (buf[0] != '\n') {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000724 if (G.current_col + buflen >= G.max_col) {
Denis Vlasenko4daad902007-09-27 10:20:47 +0000725 bb_putchar('\n');
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000726 G.current_col = 0;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000727 } else
Denis Vlasenko4daad902007-09-27 10:20:47 +0000728 bb_putchar(' ');
Denis Vlasenko79deb662006-09-19 15:12:12 +0000729 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000730 }
731 fputs(buf, stdout);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000732 G.current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000733 if (buf[buflen-1] == '\n')
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000734 G.current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000735}
736
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100737static void newline(void)
738{
739 if (G.current_col != 0)
740 wrapf("\n");
741}
742
743static void set_window_size(int rows, int cols)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000744{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000745 struct winsize win = { 0, 0, 0, 0 };
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000746
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000747 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000748 if (errno != EINVAL) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000749 goto bail;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000750 }
751 memset(&win, 0, sizeof(win));
752 }
753
754 if (rows >= 0)
755 win.ws_row = rows;
756 if (cols >= 0)
757 win.ws_col = cols;
758
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000759 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000760bail:
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000761 perror_on_device("%s");
762}
763
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100764static void display_window_size(int fancy)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000765{
766 const char *fmt_str = "%s\0%s: no size information for this device";
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000767 unsigned width, height;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000768
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000769 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000770 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
771 perror_on_device(fmt_str);
772 }
773 } else {
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100774 wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000775 height, width);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000776 }
777}
778
Eric Andersen98e599c2001-02-14 18:47:33 +0000779static const struct suffix_mult stty_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000780 { "b", 512 },
781 { "k", 1024 },
782 { "B", 1024 },
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200783 { "", 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000784};
785
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000786static const struct mode_info *find_mode(const char *name)
787{
Denis Vlasenkoda42bd52008-01-27 23:24:31 +0000788 int i = index_in_strings(mode_name, name);
789 return i >= 0 ? &mode_info[i] : NULL;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000790}
791
792static const struct control_info *find_control(const char *name)
793{
Denis Vlasenkoda42bd52008-01-27 23:24:31 +0000794 int i = index_in_strings(control_name, name);
795 return i >= 0 ? &control_info[i] : NULL;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000796}
797
798enum {
799 param_need_arg = 0x80,
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000800 param_line = 1 | 0x80,
801 param_rows = 2 | 0x80,
802 param_cols = 3 | 0x80,
803 param_columns = 4 | 0x80,
804 param_size = 5,
805 param_speed = 6,
806 param_ispeed = 7 | 0x80,
807 param_ospeed = 8 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000808};
809
Denis Vlasenkoc7cc5a92009-04-19 01:27:20 +0000810static int find_param(const char *name)
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000811{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000812 static const char params[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000813 "line\0" /* 1 */
814 "rows\0" /* 2 */
815 "cols\0" /* 3 */
816 "columns\0" /* 4 */
817 "size\0" /* 5 */
818 "speed\0" /* 6 */
819 "ispeed\0"
820 "ospeed\0";
821 int i = index_in_strings(params, name) + 1;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000822 if (i == 0)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000823 return 0;
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000824 if (i != 5 && i != 6)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000825 i |= 0x80;
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000826 return i;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000827}
828
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000829static int recover_mode(const char *arg, struct termios *mode)
830{
831 int i, n;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000832 unsigned chr;
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000833 unsigned long iflag, oflag, cflag, lflag;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000834
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000835 /* Scan into temporaries since it is too much trouble to figure out
836 the right format for 'tcflag_t' */
837 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
838 &iflag, &oflag, &cflag, &lflag, &n) != 4)
839 return 0;
840 mode->c_iflag = iflag;
841 mode->c_oflag = oflag;
842 mode->c_cflag = cflag;
843 mode->c_lflag = lflag;
844 arg += n;
845 for (i = 0; i < NCCS; ++i) {
846 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
847 return 0;
848 mode->c_cc[i] = chr;
849 arg += n;
850 }
851
852 /* Fail if there are too many fields */
853 if (*arg != '\0')
854 return 0;
855
856 return 1;
857}
858
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000859static void display_recoverable(const struct termios *mode,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000860 int UNUSED_PARAM dummy)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000861{
862 int i;
863 printf("%lx:%lx:%lx:%lx",
864 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
865 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
866 for (i = 0; i < NCCS; ++i)
867 printf(":%x", (unsigned int) mode->c_cc[i]);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000868 bb_putchar('\n');
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000869}
870
871static void display_speed(const struct termios *mode, int fancy)
872{
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100873 //____________________ 01234567 8 9
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000874 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
875 unsigned long ispeed, ospeed;
876
877 ospeed = ispeed = cfgetispeed(mode);
878 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
879 ispeed = ospeed; /* in case ispeed was 0 */
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100880 //________ 0123 4 5 6 7 8 9
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000881 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
882 }
883 if (fancy) fmt_str += 9;
884 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
885}
886
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100887static void do_display(const struct termios *mode, int all)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000888{
889 int i;
890 tcflag_t *bitsp;
891 unsigned long mask;
892 int prev_type = control;
893
894 display_speed(mode, 1);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000895 if (all)
896 display_window_size(1);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000897#ifdef HAVE_C_LINE
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100898 wrapf("line = %u;\n", mode->c_line);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000899#else
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100900 newline();
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000901#endif
902
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000903 for (i = 0; i != CIDX_min; ++i) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000904 /* If swtch is the same as susp, don't print both */
905#if VSWTCH == VSUSP
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000906 if (i == CIDX_swtch)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000907 continue;
908#endif
909 /* If eof uses the same slot as min, only print whichever applies */
910#if VEOF == VMIN
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100911 if (!(mode->c_lflag & ICANON)
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000912 && (i == CIDX_eof || i == CIDX_eol)
913 ) {
914 continue;
915 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000916#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000917 wrapf("%s = %s;", nth_string(control_name, i),
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000918 visible(mode->c_cc[control_info[i].offset]));
919 }
920#if VEOF == VMIN
921 if ((mode->c_lflag & ICANON) == 0)
922#endif
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100923 wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
924 newline();
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000925
926 for (i = 0; i < NUM_mode_info; ++i) {
927 if (mode_info[i].flags & OMIT)
928 continue;
929 if (mode_info[i].type != prev_type) {
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100930 newline();
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000931 prev_type = mode_info[i].type;
932 }
933
934 bitsp = mode_type_flag(mode_info[i].type, mode);
935 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000936 if ((*bitsp & mask) == mode_info[i].bits) {
937 if (all || (mode_info[i].flags & SANE_UNSET))
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000938 wrapf("-%s"+1, nth_string(mode_name, i));
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000939 } else {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000940 if ((all && mode_info[i].flags & REV)
941 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
942 ) {
943 wrapf("-%s", nth_string(mode_name, i));
944 }
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000945 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000946 }
Denys Vlasenko800ff7c2009-12-11 15:00:17 +0100947 newline();
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000948}
949
950static void sane_mode(struct termios *mode)
951{
952 int i;
953 tcflag_t *bitsp;
954
955 for (i = 0; i < NUM_control_info; ++i) {
956#if VMIN == VEOF
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000957 if (i == CIDX_min)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000958 break;
959#endif
960 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
961 }
962
963 for (i = 0; i < NUM_mode_info; ++i) {
964 if (mode_info[i].flags & SANE_SET) {
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 } else if (mode_info[i].flags & SANE_UNSET) {
969 bitsp = mode_type_flag(mode_info[i].type, mode);
970 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
971 & ~mode_info[i].bits;
972 }
973 }
974}
975
976/* Save set_mode from #ifdef forest plague */
977#ifndef ONLCR
978#define ONLCR 0
979#endif
980#ifndef OCRNL
981#define OCRNL 0
982#endif
983#ifndef ONLRET
984#define ONLRET 0
985#endif
986#ifndef XCASE
987#define XCASE 0
988#endif
989#ifndef IXANY
990#define IXANY 0
991#endif
992#ifndef TABDLY
993#define TABDLY 0
994#endif
995#ifndef OXTABS
996#define OXTABS 0
997#endif
998#ifndef IUCLC
999#define IUCLC 0
1000#endif
1001#ifndef OLCUC
1002#define OLCUC 0
1003#endif
1004#ifndef ECHOCTL
1005#define ECHOCTL 0
1006#endif
1007#ifndef ECHOKE
1008#define ECHOKE 0
1009#endif
1010
1011static void set_mode(const struct mode_info *info, int reversed,
1012 struct termios *mode)
1013{
1014 tcflag_t *bitsp;
1015
1016 bitsp = mode_type_flag(info->type, mode);
1017
1018 if (bitsp) {
1019 if (reversed)
1020 *bitsp = *bitsp & ~info->mask & ~info->bits;
1021 else
1022 *bitsp = (*bitsp & ~info->mask) | info->bits;
1023 return;
1024 }
1025
1026 /* Combination mode */
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001027 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001028 if (reversed)
1029 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1030 else
1031 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001032 } else if (info == &mode_info[IDX_oddp]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001033 if (reversed)
1034 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1035 else
1036 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001037 } else if (info == &mode_info[IDX_nl]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001038 if (reversed) {
1039 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1040 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1041 } else {
1042 mode->c_iflag = mode->c_iflag & ~ICRNL;
1043 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1044 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001045 } else if (info == &mode_info[IDX_ek]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001046 mode->c_cc[VERASE] = CERASE;
1047 mode->c_cc[VKILL] = CKILL;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001048 } else if (info == &mode_info[IDX_sane]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001049 sane_mode(mode);
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001050 } else if (info == &mode_info[IDX_cbreak]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001051 if (reversed)
1052 mode->c_lflag |= ICANON;
1053 else
1054 mode->c_lflag &= ~ICANON;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001055 } else if (info == &mode_info[IDX_pass8]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001056 if (reversed) {
1057 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1058 mode->c_iflag |= ISTRIP;
1059 } else {
1060 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1061 mode->c_iflag &= ~ISTRIP;
1062 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001063 } else if (info == &mode_info[IDX_litout]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001064 if (reversed) {
1065 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1066 mode->c_iflag |= ISTRIP;
1067 mode->c_oflag |= OPOST;
1068 } else {
1069 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1070 mode->c_iflag &= ~ISTRIP;
1071 mode->c_oflag &= ~OPOST;
1072 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001073 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001074 if ((info == &mode_info[IDX_raw] && reversed)
1075 || (info == &mode_info[IDX_cooked] && !reversed)
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001076 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001077 /* Cooked mode */
1078 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1079 mode->c_oflag |= OPOST;
1080 mode->c_lflag |= ISIG | ICANON;
1081#if VMIN == VEOF
1082 mode->c_cc[VEOF] = CEOF;
1083#endif
1084#if VTIME == VEOL
1085 mode->c_cc[VEOL] = CEOL;
1086#endif
1087 } else {
1088 /* Raw mode */
1089 mode->c_iflag = 0;
1090 mode->c_oflag &= ~OPOST;
1091 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1092 mode->c_cc[VMIN] = 1;
1093 mode->c_cc[VTIME] = 0;
1094 }
1095 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001096 else if (IXANY && info == &mode_info[IDX_decctlq]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001097 if (reversed)
1098 mode->c_iflag |= IXANY;
1099 else
1100 mode->c_iflag &= ~IXANY;
1101 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001102 else if (TABDLY && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001103 if (reversed)
1104 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1105 else
1106 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1107 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001108 else if (OXTABS && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001109 if (reversed)
1110 mode->c_oflag |= OXTABS;
1111 else
1112 mode->c_oflag &= ~OXTABS;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001113 } else
1114 if (XCASE && IUCLC && OLCUC
1115 && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
1116 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001117 if (reversed) {
1118 mode->c_lflag &= ~XCASE;
1119 mode->c_iflag &= ~IUCLC;
1120 mode->c_oflag &= ~OLCUC;
1121 } else {
1122 mode->c_lflag |= XCASE;
1123 mode->c_iflag |= IUCLC;
1124 mode->c_oflag |= OLCUC;
1125 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001126 } else if (info == &mode_info[IDX_crt]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001127 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001128 } else if (info == &mode_info[IDX_dec]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001129 mode->c_cc[VINTR] = 3; /* ^C */
1130 mode->c_cc[VERASE] = 127; /* DEL */
1131 mode->c_cc[VKILL] = 21; /* ^U */
1132 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1133 if (IXANY) mode->c_iflag &= ~IXANY;
1134 }
1135}
1136
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001137static void set_control_char_or_die(const struct control_info *info,
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001138 const char *arg, struct termios *mode)
1139{
1140 unsigned char value;
1141
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001142 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001143 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1144 else if (arg[0] == '\0' || arg[1] == '\0')
1145 value = arg[0];
Denys Vlasenko800ff7c2009-12-11 15:00:17 +01001146 else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001147 value = _POSIX_VDISABLE;
1148 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1149 value = arg[1] & 0x1f; /* Non-letters get weird results */
1150 if (arg[1] == '?')
1151 value = 127;
1152 } else
1153 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1154 mode->c_cc[info->offset] = value;
1155}
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001156
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001157#define STTY_require_set_attr (1 << 0)
1158#define STTY_speed_was_set (1 << 1)
1159#define STTY_verbose_output (1 << 2)
1160#define STTY_recoverable_output (1 << 3)
1161#define STTY_noargs (1 << 4)
1162
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001163int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denys Vlasenkoe992bae2009-11-28 15:18:53 +01001164int stty_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +00001165{
1166 struct termios mode;
Denys Vlasenko800ff7c2009-12-11 15:00:17 +01001167 void (*output_func)(const struct termios *, int);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001168 const char *file_name = NULL;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001169 int display_all = 0;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001170 int stty_state;
1171 int k;
1172
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001173 INIT_G();
1174
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001175 stty_state = STTY_noargs;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001176 output_func = do_display;
Eric Andersen98e599c2001-02-14 18:47:33 +00001177
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001178 /* First pass: only parse/verify command line params */
1179 k = 0;
1180 while (argv[++k]) {
1181 const struct mode_info *mp;
1182 const struct control_info *cp;
1183 const char *arg = argv[k];
1184 const char *argnext = argv[k+1];
1185 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001186
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001187 if (arg[0] == '-') {
1188 int i;
1189 mp = find_mode(arg+1);
1190 if (mp) {
1191 if (!(mp->flags & REV))
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001192 goto invalid_argument;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001193 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001194 continue;
1195 }
1196 /* It is an option - parse it */
1197 i = 0;
1198 while (arg[++i]) {
1199 switch (arg[i]) {
1200 case 'a':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001201 stty_state |= STTY_verbose_output;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001202 output_func = do_display;
1203 display_all = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001204 break;
1205 case 'g':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001206 stty_state |= STTY_recoverable_output;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001207 output_func = display_recoverable;
1208 break;
1209 case 'F':
1210 if (file_name)
1211 bb_error_msg_and_die("only one device may be specified");
1212 file_name = &arg[i+1]; /* "-Fdevice" ? */
1213 if (!file_name[0]) { /* nope, "-F device" */
1214 int p = k+1; /* argv[p] is argnext */
1215 file_name = argnext;
1216 if (!file_name)
1217 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1218 /* remove -F param from arg[vc] */
Denys Vlasenkoe992bae2009-11-28 15:18:53 +01001219 while (argv[p]) {
1220 argv[p] = argv[p+1];
1221 ++p;
1222 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001223 }
1224 goto end_option;
1225 default:
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001226 goto invalid_argument;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001227 }
1228 }
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001229 end_option:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001230 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001231 }
1232
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001233 mp = find_mode(arg);
1234 if (mp) {
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001235 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001236 continue;
1237 }
1238
1239 cp = find_control(arg);
1240 if (cp) {
1241 if (!argnext)
1242 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001243 /* called for the side effect of xfunc death only */
1244 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001245 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001246 ++k;
1247 continue;
1248 }
1249
1250 param = find_param(arg);
1251 if (param & param_need_arg) {
1252 if (!argnext)
1253 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1254 ++k;
1255 }
1256
1257 switch (param) {
1258#ifdef HAVE_C_LINE
1259 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001260# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +00001261 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001262 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001263# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001264#endif
1265#ifdef TIOCGWINSZ
1266 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001267 case param_cols:
Denis Vlasenko9ace6132007-04-19 19:55:54 +00001268 case param_columns:
Denis Vlasenko13858992006-10-08 12:49:22 +00001269 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001270 break;
1271 case param_size:
1272#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001273 case param_speed:
1274 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001275 case param_ispeed:
1276 /* called for the side effect of xfunc death only */
1277 set_speed_or_die(input_speed, argnext, &mode);
1278 break;
1279 case param_ospeed:
1280 /* called for the side effect of xfunc death only */
1281 set_speed_or_die(output_speed, argnext, &mode);
1282 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001283 default:
1284 if (recover_mode(arg, &mode) == 1) break;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001285 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001286 invalid_argument:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001287 bb_error_msg_and_die("invalid argument '%s'", arg);
1288 }
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001289 stty_state &= ~STTY_noargs;
Eric Andersen98e599c2001-02-14 18:47:33 +00001290 }
1291
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001292 /* Specifying both -a and -g is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001293 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001294 (STTY_verbose_output | STTY_recoverable_output))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001295 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1296 /* Specifying -a or -g with non-options is an error */
Denys Vlasenko800ff7c2009-12-11 15:00:17 +01001297 if (!(stty_state & STTY_noargs)
1298 && (stty_state & (STTY_verbose_output | STTY_recoverable_output))
1299 ) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001300 bb_error_msg_and_die("modes may not be set when specifying an output style");
Denys Vlasenko800ff7c2009-12-11 15:00:17 +01001301 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001302
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001303 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +00001304 if (file_name) {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001305 G.device_name = file_name;
Denys Vlasenkoab19ede2009-11-11 21:05:42 +01001306 xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1307 ndelay_off(STDIN_FILENO);
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:
Denys Vlasenko9f5a5772009-12-11 14:17:02 +01001370 case param_columns:
Denis Vlasenko13858992006-10-08 12:49:22 +00001371 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001372 break;
1373 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001374 display_window_size(0);
1375 break;
1376 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +00001377 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001378 break;
1379#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001380 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001381 display_speed(&mode, 0);
1382 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001383 case param_ispeed:
1384 set_speed_or_die(input_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001385 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001386 break;
1387 case param_ospeed:
1388 set_speed_or_die(output_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001389 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001390 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001391 default:
1392 if (recover_mode(arg, &mode) == 1)
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001393 stty_state |= STTY_require_set_attr;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001394 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001395 set_speed_or_die(both_speeds, arg, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001396 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001397 } /* else - impossible (caught in the first pass):
1398 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +00001399 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001400 }
1401
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001402 if (stty_state & STTY_require_set_attr) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001403 struct termios new_mode;
1404
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001405 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001406 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001407
1408 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1409 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +00001410 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +00001411 some proper subset of the requested operations. To detect
1412 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001413 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +00001414
1415 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001416 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001417 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001418 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001419 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001420
Eric Andersen98e599c2001-02-14 18:47:33 +00001421 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1422#ifdef CIBAUD
1423 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1424 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1425 sometimes (m1 != m2). The only difference is in the four bits
1426 of the c_cflag field corresponding to the baud rate. To save
1427 Sun users a little confusion, don't report an error if this
1428 happens. But suppress the error only if we haven't tried to
1429 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001430 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +00001431
1432 new_mode.c_cflag &= (~CIBAUD);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001433 if ((stty_state & STTY_speed_was_set)
1434 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
Eric Andersen98e599c2001-02-14 18:47:33 +00001435#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +00001436 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +00001437 }
1438 }
1439
1440 return EXIT_SUCCESS;
1441}