blob: c9f11a8dab3e51ab975789cb362938609cafcba3 [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 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 Vlasenko6ca409e2007-08-12 20:58:27 +0000680static void set_speed_or_die(enum speed_setting type, const char *const arg,
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000681 struct termios * const 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 Vlasenko20b253d2006-09-19 14:24:23 +0000695static ATTRIBUTE_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
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000737static void set_window_size(const int rows, const int cols)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000738{
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000739 struct winsize win = { 0, 0, 0, 0 };
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000740
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000741 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000742 if (errno != EINVAL) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000743 goto bail;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000744 }
745 memset(&win, 0, sizeof(win));
746 }
747
748 if (rows >= 0)
749 win.ws_row = rows;
750 if (cols >= 0)
751 win.ws_col = cols;
752
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000753 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000754bail:
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000755 perror_on_device("%s");
756}
757
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000758static void display_window_size(const int fancy)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000759{
760 const char *fmt_str = "%s\0%s: no size information for this device";
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000761 unsigned width, height;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000762
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000763 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000764 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
765 perror_on_device(fmt_str);
766 }
767 } else {
768 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000769 height, width);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000770 }
771}
772
Eric Andersen98e599c2001-02-14 18:47:33 +0000773static const struct suffix_mult stty_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000774 { "b", 512 },
775 { "k", 1024 },
776 { "B", 1024 },
777 { }
Eric Andersen98e599c2001-02-14 18:47:33 +0000778};
779
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000780static const struct mode_info *find_mode(const char *name)
781{
Denis Vlasenkoda42bd52008-01-27 23:24:31 +0000782 int i = index_in_strings(mode_name, name);
783 return i >= 0 ? &mode_info[i] : NULL;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000784}
785
786static const struct control_info *find_control(const char *name)
787{
Denis Vlasenkoda42bd52008-01-27 23:24:31 +0000788 int i = index_in_strings(control_name, name);
789 return i >= 0 ? &control_info[i] : NULL;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000790}
791
792enum {
793 param_need_arg = 0x80,
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000794 param_line = 1 | 0x80,
795 param_rows = 2 | 0x80,
796 param_cols = 3 | 0x80,
797 param_columns = 4 | 0x80,
798 param_size = 5,
799 param_speed = 6,
800 param_ispeed = 7 | 0x80,
801 param_ospeed = 8 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000802};
803
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000804static int find_param(const char *const name)
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000805{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000806 static const char params[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000807 "line\0" /* 1 */
808 "rows\0" /* 2 */
809 "cols\0" /* 3 */
810 "columns\0" /* 4 */
811 "size\0" /* 5 */
812 "speed\0" /* 6 */
813 "ispeed\0"
814 "ospeed\0";
815 int i = index_in_strings(params, name) + 1;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000816 if (i == 0)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000817 return 0;
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000818 if (i != 5 && i != 6)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000819 i |= 0x80;
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000820 return i;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000821}
822
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000823static int recover_mode(const char *arg, struct termios *mode)
824{
825 int i, n;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000826 unsigned chr;
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000827 unsigned long iflag, oflag, cflag, lflag;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000828
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000829 /* Scan into temporaries since it is too much trouble to figure out
830 the right format for 'tcflag_t' */
831 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
832 &iflag, &oflag, &cflag, &lflag, &n) != 4)
833 return 0;
834 mode->c_iflag = iflag;
835 mode->c_oflag = oflag;
836 mode->c_cflag = cflag;
837 mode->c_lflag = lflag;
838 arg += n;
839 for (i = 0; i < NCCS; ++i) {
840 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
841 return 0;
842 mode->c_cc[i] = chr;
843 arg += n;
844 }
845
846 /* Fail if there are too many fields */
847 if (*arg != '\0')
848 return 0;
849
850 return 1;
851}
852
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000853static void display_recoverable(const struct termios *mode,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000854 int ATTRIBUTE_UNUSED dummy)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000855{
856 int i;
857 printf("%lx:%lx:%lx:%lx",
858 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
859 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
860 for (i = 0; i < NCCS; ++i)
861 printf(":%x", (unsigned int) mode->c_cc[i]);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000862 bb_putchar('\n');
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000863}
864
865static void display_speed(const struct termios *mode, int fancy)
866{
867 //01234567 8 9
868 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
869 unsigned long ispeed, ospeed;
870
871 ospeed = ispeed = cfgetispeed(mode);
872 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
873 ispeed = ospeed; /* in case ispeed was 0 */
Denis Vlasenko4b924f32007-05-30 00:29:55 +0000874 //0123 4 5 6 7 8 9
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000875 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
876 }
877 if (fancy) fmt_str += 9;
878 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
879}
880
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000881static void do_display(const struct termios *mode, const int all)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000882{
883 int i;
884 tcflag_t *bitsp;
885 unsigned long mask;
886 int prev_type = control;
887
888 display_speed(mode, 1);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000889 if (all)
890 display_window_size(1);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000891#ifdef HAVE_C_LINE
892 wrapf("line = %d;\n", mode->c_line);
893#else
894 wrapf("\n");
895#endif
896
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000897 for (i = 0; i != CIDX_min; ++i) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000898 /* If swtch is the same as susp, don't print both */
899#if VSWTCH == VSUSP
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000900 if (i == CIDX_swtch)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000901 continue;
902#endif
903 /* If eof uses the same slot as min, only print whichever applies */
904#if VEOF == VMIN
905 if ((mode->c_lflag & ICANON) == 0
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000906 && (i == CIDX_eof || i == CIDX_eol)
907 ) {
908 continue;
909 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000910#endif
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000911 wrapf("%s = %s;", nth_string(control_name, i),
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000912 visible(mode->c_cc[control_info[i].offset]));
913 }
914#if VEOF == VMIN
915 if ((mode->c_lflag & ICANON) == 0)
916#endif
917 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000918 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000919
920 for (i = 0; i < NUM_mode_info; ++i) {
921 if (mode_info[i].flags & OMIT)
922 continue;
923 if (mode_info[i].type != prev_type) {
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000924 /* wrapf("\n"); */
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000925 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000926 prev_type = mode_info[i].type;
927 }
928
929 bitsp = mode_type_flag(mode_info[i].type, mode);
930 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000931 if ((*bitsp & mask) == mode_info[i].bits) {
932 if (all || (mode_info[i].flags & SANE_UNSET))
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000933 wrapf("-%s"+1, nth_string(mode_name, i));
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000934 } else {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +0000935 if ((all && mode_info[i].flags & REV)
936 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
937 ) {
938 wrapf("-%s", nth_string(mode_name, i));
939 }
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000940 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000941 }
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000942 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000943}
944
945static void sane_mode(struct termios *mode)
946{
947 int i;
948 tcflag_t *bitsp;
949
950 for (i = 0; i < NUM_control_info; ++i) {
951#if VMIN == VEOF
Denis Vlasenko2ea8c402007-10-11 16:02:36 +0000952 if (i == CIDX_min)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000953 break;
954#endif
955 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
956 }
957
958 for (i = 0; i < NUM_mode_info; ++i) {
959 if (mode_info[i].flags & SANE_SET) {
960 bitsp = mode_type_flag(mode_info[i].type, mode);
961 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
962 | mode_info[i].bits;
963 } else if (mode_info[i].flags & SANE_UNSET) {
964 bitsp = mode_type_flag(mode_info[i].type, mode);
965 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
966 & ~mode_info[i].bits;
967 }
968 }
969}
970
971/* Save set_mode from #ifdef forest plague */
972#ifndef ONLCR
973#define ONLCR 0
974#endif
975#ifndef OCRNL
976#define OCRNL 0
977#endif
978#ifndef ONLRET
979#define ONLRET 0
980#endif
981#ifndef XCASE
982#define XCASE 0
983#endif
984#ifndef IXANY
985#define IXANY 0
986#endif
987#ifndef TABDLY
988#define TABDLY 0
989#endif
990#ifndef OXTABS
991#define OXTABS 0
992#endif
993#ifndef IUCLC
994#define IUCLC 0
995#endif
996#ifndef OLCUC
997#define OLCUC 0
998#endif
999#ifndef ECHOCTL
1000#define ECHOCTL 0
1001#endif
1002#ifndef ECHOKE
1003#define ECHOKE 0
1004#endif
1005
1006static void set_mode(const struct mode_info *info, int reversed,
1007 struct termios *mode)
1008{
1009 tcflag_t *bitsp;
1010
1011 bitsp = mode_type_flag(info->type, mode);
1012
1013 if (bitsp) {
1014 if (reversed)
1015 *bitsp = *bitsp & ~info->mask & ~info->bits;
1016 else
1017 *bitsp = (*bitsp & ~info->mask) | info->bits;
1018 return;
1019 }
1020
1021 /* Combination mode */
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001022 if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001023 if (reversed)
1024 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1025 else
1026 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001027 } else if (info == &mode_info[IDX_oddp]) {
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 & ~CSIZE) | CS7 | PARODD | PARENB;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001032 } else if (info == &mode_info[IDX_nl]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001033 if (reversed) {
1034 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1035 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1036 } else {
1037 mode->c_iflag = mode->c_iflag & ~ICRNL;
1038 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1039 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001040 } else if (info == &mode_info[IDX_ek]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001041 mode->c_cc[VERASE] = CERASE;
1042 mode->c_cc[VKILL] = CKILL;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001043 } else if (info == &mode_info[IDX_sane]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001044 sane_mode(mode);
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001045 } else if (info == &mode_info[IDX_cbreak]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001046 if (reversed)
1047 mode->c_lflag |= ICANON;
1048 else
1049 mode->c_lflag &= ~ICANON;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001050 } else if (info == &mode_info[IDX_pass8]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001051 if (reversed) {
1052 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1053 mode->c_iflag |= ISTRIP;
1054 } else {
1055 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1056 mode->c_iflag &= ~ISTRIP;
1057 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001058 } else if (info == &mode_info[IDX_litout]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001059 if (reversed) {
1060 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1061 mode->c_iflag |= ISTRIP;
1062 mode->c_oflag |= OPOST;
1063 } else {
1064 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1065 mode->c_iflag &= ~ISTRIP;
1066 mode->c_oflag &= ~OPOST;
1067 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001068 } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001069 if ((info == &mode_info[IDX_raw] && reversed)
1070 || (info == &mode_info[IDX_cooked] && !reversed)
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001071 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001072 /* Cooked mode */
1073 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1074 mode->c_oflag |= OPOST;
1075 mode->c_lflag |= ISIG | ICANON;
1076#if VMIN == VEOF
1077 mode->c_cc[VEOF] = CEOF;
1078#endif
1079#if VTIME == VEOL
1080 mode->c_cc[VEOL] = CEOL;
1081#endif
1082 } else {
1083 /* Raw mode */
1084 mode->c_iflag = 0;
1085 mode->c_oflag &= ~OPOST;
1086 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1087 mode->c_cc[VMIN] = 1;
1088 mode->c_cc[VTIME] = 0;
1089 }
1090 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001091 else if (IXANY && info == &mode_info[IDX_decctlq]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001092 if (reversed)
1093 mode->c_iflag |= IXANY;
1094 else
1095 mode->c_iflag &= ~IXANY;
1096 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001097 else if (TABDLY && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001098 if (reversed)
1099 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1100 else
1101 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1102 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001103 else if (OXTABS && info == &mode_info[IDX_tabs]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001104 if (reversed)
1105 mode->c_oflag |= OXTABS;
1106 else
1107 mode->c_oflag &= ~OXTABS;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001108 } else
1109 if (XCASE && IUCLC && OLCUC
1110 && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
1111 ) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001112 if (reversed) {
1113 mode->c_lflag &= ~XCASE;
1114 mode->c_iflag &= ~IUCLC;
1115 mode->c_oflag &= ~OLCUC;
1116 } else {
1117 mode->c_lflag |= XCASE;
1118 mode->c_iflag |= IUCLC;
1119 mode->c_oflag |= OLCUC;
1120 }
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001121 } else if (info == &mode_info[IDX_crt]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001122 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001123 } else if (info == &mode_info[IDX_dec]) {
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001124 mode->c_cc[VINTR] = 3; /* ^C */
1125 mode->c_cc[VERASE] = 127; /* DEL */
1126 mode->c_cc[VKILL] = 21; /* ^U */
1127 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1128 if (IXANY) mode->c_iflag &= ~IXANY;
1129 }
1130}
1131
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001132static void set_control_char_or_die(const struct control_info *info,
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001133 const char *arg, struct termios *mode)
1134{
1135 unsigned char value;
1136
Denis Vlasenko2ea8c402007-10-11 16:02:36 +00001137 if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001138 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1139 else if (arg[0] == '\0' || arg[1] == '\0')
1140 value = arg[0];
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +00001141 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
Denis Vlasenkof8abc102007-01-12 21:02:04 +00001142 value = _POSIX_VDISABLE;
1143 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1144 value = arg[1] & 0x1f; /* Non-letters get weird results */
1145 if (arg[1] == '?')
1146 value = 127;
1147 } else
1148 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1149 mode->c_cc[info->offset] = value;
1150}
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001151
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001152#define STTY_require_set_attr (1 << 0)
1153#define STTY_speed_was_set (1 << 1)
1154#define STTY_verbose_output (1 << 2)
1155#define STTY_recoverable_output (1 << 3)
1156#define STTY_noargs (1 << 4)
1157
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001158int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Rob Landleydfba7412006-03-06 20:47:33 +00001159int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +00001160{
1161 struct termios mode;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001162 void (*output_func)(const struct termios *, const int);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001163 const char *file_name = NULL;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001164 int display_all = 0;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001165 int stty_state;
1166 int k;
1167
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001168 INIT_G();
1169
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001170 stty_state = STTY_noargs;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001171 output_func = do_display;
Eric Andersen98e599c2001-02-14 18:47:33 +00001172
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001173 /* First pass: only parse/verify command line params */
1174 k = 0;
1175 while (argv[++k]) {
1176 const struct mode_info *mp;
1177 const struct control_info *cp;
1178 const char *arg = argv[k];
1179 const char *argnext = argv[k+1];
1180 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001181
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001182 if (arg[0] == '-') {
1183 int i;
1184 mp = find_mode(arg+1);
1185 if (mp) {
1186 if (!(mp->flags & REV))
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001187 goto invalid_argument;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001188 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001189 continue;
1190 }
1191 /* It is an option - parse it */
1192 i = 0;
1193 while (arg[++i]) {
1194 switch (arg[i]) {
1195 case 'a':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001196 stty_state |= STTY_verbose_output;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001197 output_func = do_display;
1198 display_all = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001199 break;
1200 case 'g':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001201 stty_state |= STTY_recoverable_output;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001202 output_func = display_recoverable;
1203 break;
1204 case 'F':
1205 if (file_name)
1206 bb_error_msg_and_die("only one device may be specified");
1207 file_name = &arg[i+1]; /* "-Fdevice" ? */
1208 if (!file_name[0]) { /* nope, "-F device" */
1209 int p = k+1; /* argv[p] is argnext */
1210 file_name = argnext;
1211 if (!file_name)
1212 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1213 /* remove -F param from arg[vc] */
1214 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +00001215 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001216 }
1217 goto end_option;
1218 default:
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001219 goto invalid_argument;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001220 }
1221 }
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001222 end_option:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001223 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001224 }
1225
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001226 mp = find_mode(arg);
1227 if (mp) {
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001228 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001229 continue;
1230 }
1231
1232 cp = find_control(arg);
1233 if (cp) {
1234 if (!argnext)
1235 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001236 /* called for the side effect of xfunc death only */
1237 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001238 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001239 ++k;
1240 continue;
1241 }
1242
1243 param = find_param(arg);
1244 if (param & param_need_arg) {
1245 if (!argnext)
1246 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1247 ++k;
1248 }
1249
1250 switch (param) {
1251#ifdef HAVE_C_LINE
1252 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001253# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +00001254 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001255 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001256# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001257#endif
1258#ifdef TIOCGWINSZ
1259 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001260 case param_cols:
Denis Vlasenko9ace6132007-04-19 19:55:54 +00001261 case param_columns:
Denis Vlasenko13858992006-10-08 12:49:22 +00001262 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001263 break;
1264 case param_size:
1265#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001266 case param_speed:
1267 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001268 case param_ispeed:
1269 /* called for the side effect of xfunc death only */
1270 set_speed_or_die(input_speed, argnext, &mode);
1271 break;
1272 case param_ospeed:
1273 /* called for the side effect of xfunc death only */
1274 set_speed_or_die(output_speed, argnext, &mode);
1275 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001276 default:
1277 if (recover_mode(arg, &mode) == 1) break;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001278 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001279 invalid_argument:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001280 bb_error_msg_and_die("invalid argument '%s'", arg);
1281 }
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001282 stty_state &= ~STTY_noargs;
Eric Andersen98e599c2001-02-14 18:47:33 +00001283 }
1284
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001285 /* Specifying both -a and -g is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001286 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001287 (STTY_verbose_output | STTY_recoverable_output))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001288 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1289 /* Specifying -a or -g with non-options is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001290 if (!(stty_state & STTY_noargs) &&
1291 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001292 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +00001293
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001294 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +00001295 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001296 int fd, fdflags;
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001297 G.device_name = file_name;
1298 fd = xopen(G.device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +00001299 if (fd != STDIN_FILENO) {
1300 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001301 close(fd);
1302 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +00001303 fdflags = fcntl(STDIN_FILENO, F_GETFL);
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001304 if (fdflags < 0 ||
1305 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +00001306 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +00001307 }
1308
1309 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001310 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001311 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001312 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001313 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001314
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001315 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
Denis Vlasenkodd8bbfd2007-11-24 04:32:49 +00001316 get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001317 output_func(&mode, display_all);
Eric Andersen98e599c2001-02-14 18:47:33 +00001318 return EXIT_SUCCESS;
1319 }
1320
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001321 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +00001322 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001323 while (argv[++k]) {
1324 const struct mode_info *mp;
1325 const struct control_info *cp;
1326 const char *arg = argv[k];
1327 const char *argnext = argv[k+1];
1328 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001329
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001330 if (arg[0] == '-') {
1331 mp = find_mode(arg+1);
1332 if (mp) {
1333 set_mode(mp, 1 /* reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001334 stty_state |= STTY_require_set_attr;
Eric Andersenfc059092002-06-06 11:35:29 +00001335 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001336 /* It is an option - already parsed. Skip it */
1337 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001338 }
Mark Whitley446dd272001-03-02 20:00:54 +00001339
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001340 mp = find_mode(arg);
1341 if (mp) {
1342 set_mode(mp, 0 /* non-reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001343 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001344 continue;
1345 }
Mark Whitley446dd272001-03-02 20:00:54 +00001346
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001347 cp = find_control(arg);
1348 if (cp) {
1349 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001350 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001351 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001352 continue;
1353 }
Mark Whitley446dd272001-03-02 20:00:54 +00001354
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001355 param = find_param(arg);
1356 if (param & param_need_arg) {
1357 ++k;
1358 }
1359
1360 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001361#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001362 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +00001363 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001364 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001365 break;
Eric Andersen98e599c2001-02-14 18:47:33 +00001366#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001367#ifdef TIOCGWINSZ
1368 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001369 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001370 break;
1371 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001372 display_window_size(0);
1373 break;
1374 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +00001375 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001376 break;
1377#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001378 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001379 display_speed(&mode, 0);
1380 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001381 case param_ispeed:
1382 set_speed_or_die(input_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001383 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001384 break;
1385 case param_ospeed:
1386 set_speed_or_die(output_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001387 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001388 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001389 default:
1390 if (recover_mode(arg, &mode) == 1)
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001391 stty_state |= STTY_require_set_attr;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001392 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001393 set_speed_or_die(both_speeds, arg, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001394 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001395 } /* else - impossible (caught in the first pass):
1396 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +00001397 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001398 }
1399
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001400 if (stty_state & STTY_require_set_attr) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001401 struct termios new_mode;
1402
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001403 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001404 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001405
1406 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1407 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +00001408 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +00001409 some proper subset of the requested operations. To detect
1410 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001411 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +00001412
1413 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001414 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001415 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001416 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001417 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001418
Eric Andersen98e599c2001-02-14 18:47:33 +00001419 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1420#ifdef CIBAUD
1421 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1422 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1423 sometimes (m1 != m2). The only difference is in the four bits
1424 of the c_cflag field corresponding to the baud rate. To save
1425 Sun users a little confusion, don't report an error if this
1426 happens. But suppress the error only if we haven't tried to
1427 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001428 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +00001429
1430 new_mode.c_cflag &= (~CIBAUD);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001431 if ((stty_state & STTY_speed_was_set)
1432 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
Eric Andersen98e599c2001-02-14 18:47:33 +00001433#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +00001434 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +00001435 }
1436 }
1437
1438 return EXIT_SUCCESS;
1439}