blob: 4e665bcb767dcd9274ed6293ef536caec58e7d72 [file] [log] [blame]
Eric Andersen98e599c2001-02-14 18:47:33 +00001/* vi: set sw=4 ts=4: */
2/* stty -- change and print terminal line settings
3 Copyright (C) 1990-1999 Free Software Foundation, Inc.
4
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +00005 Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
6*/
Eric Andersen98e599c2001-02-14 18:47:33 +00007/* Usage: stty [-ag] [-F device] [setting...]
8
9 Options:
10 -a Write all current settings to stdout in human-readable form.
11 -g Write all current settings to stdout in stty-readable form.
12 -F Open and use the specified device instead of stdin
13
14 If no args are given, write to stdout the baud rate and settings that
15 have been changed from their defaults. Mode reading and changes
16 are done on the specified device, or stdin if none was specified.
17
18 David MacKenzie <djm@gnu.ai.mit.edu>
19
Eric Andersen7467c8d2001-07-12 20:26:32 +000020 Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
Eric Andersen98e599c2001-02-14 18:47:33 +000021
22 */
23
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +000024#include "busybox.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000025
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.' */
Eric Andersen98e599c2001-02-14 18:47:33 +000073#if defined (__sparc__) && defined (__svr4__)
74# undef CSWTCH
75# define CSWTCH _POSIX_VDISABLE
76#endif
77
Mark Whitley446dd272001-03-02 20:00:54 +000078#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000079# define VWERASE VWERSE
80#endif
81#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
Mark Whitley446dd272001-03-02 20:00:54 +0000130static const char evenp [] = "evenp";
131static const char raw [] = "raw";
132static const char stty_min [] = "min";
133static const char stty_time [] = "time";
Eric Andersen98e599c2001-02-14 18:47:33 +0000134static const char stty_swtch[] = "swtch";
Mark Whitley446dd272001-03-02 20:00:54 +0000135static const char stty_eol [] = "eol";
136static const char stty_eof [] = "eof";
137static const char parity [] = "parity";
138static const char stty_oddp [] = "oddp";
139static const char stty_nl [] = "nl";
140static const char stty_ek [] = "ek";
141static const char stty_sane [] = "sane";
142static const char cbreak [] = "cbreak";
Eric Andersen98e599c2001-02-14 18:47:33 +0000143static const char stty_pass8[] = "pass8";
Mark Whitley446dd272001-03-02 20:00:54 +0000144static const char litout [] = "litout";
145static const char cooked [] = "cooked";
146static const char decctlq [] = "decctlq";
147static const char stty_tabs [] = "tabs";
Eric Andersen98e599c2001-02-14 18:47:33 +0000148static const char stty_lcase[] = "lcase";
149static const char stty_LCASE[] = "LCASE";
Mark Whitley446dd272001-03-02 20:00:54 +0000150static const char stty_crt [] = "crt";
151static const char stty_dec [] = "dec";
Eric Andersen98e599c2001-02-14 18:47:33 +0000152
Denis Vlasenko79deb662006-09-19 15:12:12 +0000153/* Flags for 'struct mode_info' */
154#define SANE_SET 1 /* Set in 'sane' mode */
155#define SANE_UNSET 2 /* Unset in 'sane' mode */
156#define REV 4 /* Can be turned off by prepending '-' */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000157#define OMIT 8 /* Don't display value */
Eric Andersen98e599c2001-02-14 18:47:33 +0000158
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000159/* Each mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000160struct mode_info {
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000161 const char * const name; /* Name given on command line */
162 const unsigned char type; /* Which structure element to change */
163 const unsigned char flags; /* Setting and display options */
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000164 /* were using short here, but ppc32 was unhappy: */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000165 const tcflag_t mask; /* Other bits to turn off for this mode */
166 const tcflag_t bits; /* Bits to set for this mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000167};
168
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000169/* We can optimize it further by using name[8] instead of char *name */
170/* but beware of "if (info->name == evenp)" checks! */
171/* Need to replace them with "if (info == &mode_info[EVENP_INDX])" */
172
Manuel Novoa III cad53642003-03-19 09:13:01 +0000173#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
174
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000175static const struct mode_info mode_info[] = {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000176 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
177 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
178 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
179 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
180 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
181 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
182 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
183 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
184 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
185 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
186 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000187#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000188 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000189#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000190 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
191 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
192 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
193 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
194 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
195 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
196 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
197 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
198 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
199 MI_ENTRY("ixon", input, REV, IXON, 0 ),
200 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
201 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000202#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000203 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000204#endif
205#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000206 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000207#endif
208#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000209 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000210#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000211 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000212#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000213 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000214#endif
215#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000216 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000217#endif
218#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000219 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000220#endif
221#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000222 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000223#endif
224#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000225 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000226#endif
227#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000228 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000229#endif
230#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000231 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000232#endif
233#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000234 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
235 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000236#endif
237#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000238 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
239 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
240 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
241 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000242#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000243
Eric Andersen98e599c2001-02-14 18:47:33 +0000244#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000245 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
246 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
247 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
248 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000249#else
250# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000251 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000252# endif
253#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000254
Eric Andersen98e599c2001-02-14 18:47:33 +0000255#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000256 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
257 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000258#endif
259#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000260 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
261 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000262#endif
263#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000264 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
265 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000266#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000267 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
268 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000269#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000270 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000271#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000272 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
273 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
274 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
275 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
276 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
277 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000278#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000279 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000280#endif
281#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000282 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000283#endif
284#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000285 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
286 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000287#endif
288#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000289 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
290 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000291#endif
292#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000293 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
294 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000295#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000296 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
297 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
298 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
299 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
300 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
301 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
302 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
303 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
304 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
305 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
306 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000307#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000308 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000309#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000310#if defined(TABDLY) || defined(OXTABS)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000311 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000312#endif
313#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000314 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
315 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000316#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000317 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
318 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000319};
320
Rob Landleybc68cd12006-03-10 19:22:06 +0000321enum {
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000322 NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
Rob Landleybc68cd12006-03-10 19:22:06 +0000323};
Eric Andersen98e599c2001-02-14 18:47:33 +0000324
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000325/* Control character settings */
Eric Andersen98e599c2001-02-14 18:47:33 +0000326struct control_info {
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000327 const char * const name; /* Name given on command line */
328 const unsigned char saneval; /* Value to set for 'stty sane' */
329 const unsigned char offset; /* Offset in c_cc */
Eric Andersen98e599c2001-02-14 18:47:33 +0000330};
331
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000332/* Control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +0000333
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000334static const struct control_info control_info[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000335 {"intr", CINTR, VINTR},
336 {"quit", CQUIT, VQUIT},
337 {"erase", CERASE, VERASE},
338 {"kill", CKILL, VKILL},
339 {stty_eof, CEOF, VEOF},
340 {stty_eol, CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000341#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000342 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000343#endif
344#ifdef VSWTCH
Mark Whitley446dd272001-03-02 20:00:54 +0000345 {stty_swtch, CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000346#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000347 {"start", CSTART, VSTART},
348 {"stop", CSTOP, VSTOP},
349 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000350#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000351 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000352#endif
353#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000354 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000355#endif
356#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000357 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000358#endif
359#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000360 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000361#endif
362#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000363 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000364#endif
365#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000366 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000367#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000368 /* These must be last because of the display routines */
Mark Whitley446dd272001-03-02 20:00:54 +0000369 {stty_min, 1, VMIN},
370 {stty_time, 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000371};
372
Rob Landleybc68cd12006-03-10 19:22:06 +0000373enum {
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000374 NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
Rob Landleybc68cd12006-03-10 19:22:06 +0000375};
Eric Andersen98e599c2001-02-14 18:47:33 +0000376
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000377/* The width of the screen, for output wrapping */
Denis Vlasenko13858992006-10-08 12:49:22 +0000378static unsigned max_col = 80; /* default */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000379/* Current position, to know when to wrap */
Denis Vlasenko13858992006-10-08 12:49:22 +0000380static unsigned current_col;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000381static const char *device_name = bb_msg_standard_input;
Eric Andersen8876fb22003-06-20 09:01:58 +0000382
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000383/* Return a string that is the printable representation of character CH */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000384/* Adapted from 'cat' by Torbjorn Granlund */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000385static const char *visible(unsigned int ch)
386{
387 static char buf[10];
388 char *bpout = buf;
389
390 if (ch == _POSIX_VDISABLE)
391 return "<undef>";
392
393 if (ch >= 128) {
394 ch -= 128;
395 *bpout++ = 'M';
396 *bpout++ = '-';
397 }
398
399 if (ch < 32) {
400 *bpout++ = '^';
401 *bpout++ = ch + 64;
402 } else if (ch < 127) {
403 *bpout++ = ch;
404 } else {
405 *bpout++ = '^';
406 *bpout++ = '?';
407 }
408
409 *bpout = '\0';
410 return buf;
411}
412
413static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
414{
415 static const unsigned char tcflag_offsets[] = {
416 offsetof(struct termios, c_cflag), /* control */
417 offsetof(struct termios, c_iflag), /* input */
418 offsetof(struct termios, c_oflag), /* output */
419 offsetof(struct termios, c_lflag), /* local */
420 };
421
422 if (type <= local) {
423 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
424 }
425 return NULL;
426}
427
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000428static void set_speed_or_die(enum speed_setting type, const char * const arg,
429 struct termios * const mode)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000430{
431 speed_t baud;
432
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000433 baud = tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000434
435 if (type != output_speed) { /* either input or both */
436 cfsetispeed(mode, baud);
437 }
438 if (type != input_speed) { /* either output or both */
439 cfsetospeed(mode, baud);
440 }
441}
442
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000443static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000444{
445 bb_perror_msg_and_die(fmt, device_name);
446}
447
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000448static void perror_on_device(const char *fmt)
449{
450 bb_perror_msg(fmt, device_name);
451}
452
Eric Andersen98e599c2001-02-14 18:47:33 +0000453/* Print format string MESSAGE and optional args.
454 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000455 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000456static void wrapf(const char *message, ...)
457{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000458 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000459 va_list args;
Eric Andersen98e599c2001-02-14 18:47:33 +0000460 int buflen;
461
462 va_start(args, message);
Bernhard Reutner-Fischer8eb05492007-01-17 19:46:33 +0000463 buflen = vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000464 va_end(args);
Bernhard Reutner-Fischer1a250d92007-01-18 08:41:22 +0000465 /* We seem to be called only with suitable lengths, but check if
466 somebody failed to adhere to this assumption just to be sure. */
467 if (!buflen || buflen >= sizeof(buf)) return;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000468
Eric Andersen98e599c2001-02-14 18:47:33 +0000469 if (current_col > 0) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000470 current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000471 if (buf[0] != '\n') {
472 if (current_col + buflen >= max_col) {
473 putchar('\n');
474 current_col = 0;
475 } else
476 putchar(' ');
477 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000478 }
479 fputs(buf, stdout);
480 current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000481 if (buf[buflen-1] == '\n')
482 current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000483}
484
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000485static void set_window_size(const int rows, const int cols)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000486{
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000487 struct winsize win = { 0, 0, 0, 0};
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000488
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000489 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000490 if (errno != EINVAL) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000491 goto bail;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000492 }
493 memset(&win, 0, sizeof(win));
494 }
495
496 if (rows >= 0)
497 win.ws_row = rows;
498 if (cols >= 0)
499 win.ws_col = cols;
500
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000501 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000502bail:
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000503 perror_on_device("%s");
504}
505
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000506static void display_window_size(const int fancy)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000507{
508 const char *fmt_str = "%s\0%s: no size information for this device";
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000509 unsigned width, height;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000510
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000511 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000512 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
513 perror_on_device(fmt_str);
514 }
515 } else {
516 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000517 height, width);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000518 }
519}
520
Eric Andersen98e599c2001-02-14 18:47:33 +0000521static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000522 {"b", 512 },
523 {"k", 1024},
524 {"B", 1024},
525 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000526};
527
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000528static const struct mode_info *find_mode(const char *name)
529{
530 int i;
531 for (i = 0; i < NUM_mode_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000532 if (!strcmp(name, mode_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000533 return &mode_info[i];
534 return 0;
535}
536
537static const struct control_info *find_control(const char *name)
538{
539 int i;
540 for (i = 0; i < NUM_control_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000541 if (!strcmp(name, control_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000542 return &control_info[i];
543 return 0;
544}
545
546enum {
547 param_need_arg = 0x80,
548 param_line = 1 | 0x80,
549 param_rows = 2 | 0x80,
550 param_cols = 3 | 0x80,
551 param_size = 4,
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000552 param_speed = 5,
553 param_ispeed = 6 | 0x80,
554 param_ospeed = 7 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000555};
556
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000557static int find_param(const char * const name)
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000558{
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000559 const char * const params[] = {
560 "line",
561 "rows",
562 "cols",
563 "columns",
564 "size",
565 "speed",
566 "ispeed",
567 "ospeed",
568 NULL
569 };
570 int i = index_in_str_array(params, name);
571 if (i) {
572 if (!(i == 4 || i == 5))
573 i |= 0x80;
574 }
575 return i;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000576}
577
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000578static int recover_mode(const char *arg, struct termios *mode)
579{
580 int i, n;
581 unsigned int chr;
582 unsigned long iflag, oflag, cflag, lflag;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000583
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000584 /* Scan into temporaries since it is too much trouble to figure out
585 the right format for 'tcflag_t' */
586 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
587 &iflag, &oflag, &cflag, &lflag, &n) != 4)
588 return 0;
589 mode->c_iflag = iflag;
590 mode->c_oflag = oflag;
591 mode->c_cflag = cflag;
592 mode->c_lflag = lflag;
593 arg += n;
594 for (i = 0; i < NCCS; ++i) {
595 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
596 return 0;
597 mode->c_cc[i] = chr;
598 arg += n;
599 }
600
601 /* Fail if there are too many fields */
602 if (*arg != '\0')
603 return 0;
604
605 return 1;
606}
607
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000608static void display_recoverable(const struct termios *mode,
609 const int ATTRIBUTE_UNUSED dummy)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000610{
611 int i;
612 printf("%lx:%lx:%lx:%lx",
613 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
614 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
615 for (i = 0; i < NCCS; ++i)
616 printf(":%x", (unsigned int) mode->c_cc[i]);
617 putchar('\n');
618}
619
620static void display_speed(const struct termios *mode, int fancy)
621{
622 //01234567 8 9
623 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
624 unsigned long ispeed, ospeed;
625
626 ospeed = ispeed = cfgetispeed(mode);
627 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
628 ispeed = ospeed; /* in case ispeed was 0 */
629 //0123 4 5 6 7 8 9
630 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
631 }
632 if (fancy) fmt_str += 9;
633 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
634}
635
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000636static void do_display(const struct termios *mode, const int all)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000637{
638 int i;
639 tcflag_t *bitsp;
640 unsigned long mask;
641 int prev_type = control;
642
643 display_speed(mode, 1);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000644 if (all)
645 display_window_size(1);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000646#ifdef HAVE_C_LINE
647 wrapf("line = %d;\n", mode->c_line);
648#else
649 wrapf("\n");
650#endif
651
652 for (i = 0; control_info[i].name != stty_min; ++i) {
653 /* If swtch is the same as susp, don't print both */
654#if VSWTCH == VSUSP
655 if (control_info[i].name == stty_swtch)
656 continue;
657#endif
658 /* If eof uses the same slot as min, only print whichever applies */
659#if VEOF == VMIN
660 if ((mode->c_lflag & ICANON) == 0
661 && (control_info[i].name == stty_eof
662 || control_info[i].name == stty_eol)) continue;
663#endif
664 wrapf("%s = %s;", control_info[i].name,
665 visible(mode->c_cc[control_info[i].offset]));
666 }
667#if VEOF == VMIN
668 if ((mode->c_lflag & ICANON) == 0)
669#endif
670 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
671 if (current_col) wrapf("\n");
672
673 for (i = 0; i < NUM_mode_info; ++i) {
674 if (mode_info[i].flags & OMIT)
675 continue;
676 if (mode_info[i].type != prev_type) {
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000677 /* wrapf("\n"); */
678 if (current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000679 prev_type = mode_info[i].type;
680 }
681
682 bitsp = mode_type_flag(mode_info[i].type, mode);
683 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000684 if ((*bitsp & mask) == mode_info[i].bits) {
685 if (all || (mode_info[i].flags & SANE_UNSET))
686 wrapf("%s", mode_info[i].name);
687 } else {
688 if ((all && mode_info[i].flags & REV) ||
689 (!all &&
690 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
691 wrapf("-%s", mode_info[i].name);
692 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000693 }
694 if (current_col) wrapf("\n");
695}
696
697static void sane_mode(struct termios *mode)
698{
699 int i;
700 tcflag_t *bitsp;
701
702 for (i = 0; i < NUM_control_info; ++i) {
703#if VMIN == VEOF
704 if (control_info[i].name == stty_min)
705 break;
706#endif
707 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
708 }
709
710 for (i = 0; i < NUM_mode_info; ++i) {
711 if (mode_info[i].flags & SANE_SET) {
712 bitsp = mode_type_flag(mode_info[i].type, mode);
713 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
714 | mode_info[i].bits;
715 } else if (mode_info[i].flags & SANE_UNSET) {
716 bitsp = mode_type_flag(mode_info[i].type, mode);
717 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
718 & ~mode_info[i].bits;
719 }
720 }
721}
722
723/* Save set_mode from #ifdef forest plague */
724#ifndef ONLCR
725#define ONLCR 0
726#endif
727#ifndef OCRNL
728#define OCRNL 0
729#endif
730#ifndef ONLRET
731#define ONLRET 0
732#endif
733#ifndef XCASE
734#define XCASE 0
735#endif
736#ifndef IXANY
737#define IXANY 0
738#endif
739#ifndef TABDLY
740#define TABDLY 0
741#endif
742#ifndef OXTABS
743#define OXTABS 0
744#endif
745#ifndef IUCLC
746#define IUCLC 0
747#endif
748#ifndef OLCUC
749#define OLCUC 0
750#endif
751#ifndef ECHOCTL
752#define ECHOCTL 0
753#endif
754#ifndef ECHOKE
755#define ECHOKE 0
756#endif
757
758static void set_mode(const struct mode_info *info, int reversed,
759 struct termios *mode)
760{
761 tcflag_t *bitsp;
762
763 bitsp = mode_type_flag(info->type, mode);
764
765 if (bitsp) {
766 if (reversed)
767 *bitsp = *bitsp & ~info->mask & ~info->bits;
768 else
769 *bitsp = (*bitsp & ~info->mask) | info->bits;
770 return;
771 }
772
773 /* Combination mode */
774 if (info->name == evenp || info->name == parity) {
775 if (reversed)
776 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
777 else
778 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
779 } else if (info->name == stty_oddp) {
780 if (reversed)
781 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
782 else
783 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
784 } else if (info->name == stty_nl) {
785 if (reversed) {
786 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
787 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
788 } else {
789 mode->c_iflag = mode->c_iflag & ~ICRNL;
790 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
791 }
792 } else if (info->name == stty_ek) {
793 mode->c_cc[VERASE] = CERASE;
794 mode->c_cc[VKILL] = CKILL;
795 } else if (info->name == stty_sane) {
796 sane_mode(mode);
797 }
798 else if (info->name == cbreak) {
799 if (reversed)
800 mode->c_lflag |= ICANON;
801 else
802 mode->c_lflag &= ~ICANON;
803 } else if (info->name == stty_pass8) {
804 if (reversed) {
805 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
806 mode->c_iflag |= ISTRIP;
807 } else {
808 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
809 mode->c_iflag &= ~ISTRIP;
810 }
811 } else if (info->name == litout) {
812 if (reversed) {
813 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
814 mode->c_iflag |= ISTRIP;
815 mode->c_oflag |= OPOST;
816 } else {
817 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
818 mode->c_iflag &= ~ISTRIP;
819 mode->c_oflag &= ~OPOST;
820 }
821 } else if (info->name == raw || info->name == cooked) {
822 if ((info->name[0] == 'r' && reversed)
823 || (info->name[0] == 'c' && !reversed)) {
824 /* Cooked mode */
825 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
826 mode->c_oflag |= OPOST;
827 mode->c_lflag |= ISIG | ICANON;
828#if VMIN == VEOF
829 mode->c_cc[VEOF] = CEOF;
830#endif
831#if VTIME == VEOL
832 mode->c_cc[VEOL] = CEOL;
833#endif
834 } else {
835 /* Raw mode */
836 mode->c_iflag = 0;
837 mode->c_oflag &= ~OPOST;
838 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
839 mode->c_cc[VMIN] = 1;
840 mode->c_cc[VTIME] = 0;
841 }
842 }
843 else if (IXANY && info->name == decctlq) {
844 if (reversed)
845 mode->c_iflag |= IXANY;
846 else
847 mode->c_iflag &= ~IXANY;
848 }
849 else if (TABDLY && info->name == stty_tabs) {
850 if (reversed)
851 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
852 else
853 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
854 }
855 else if (OXTABS && info->name == stty_tabs) {
856 if (reversed)
857 mode->c_oflag |= OXTABS;
858 else
859 mode->c_oflag &= ~OXTABS;
860 }
861 else if (XCASE && IUCLC && OLCUC
862 && (info->name == stty_lcase || info->name == stty_LCASE)) {
863 if (reversed) {
864 mode->c_lflag &= ~XCASE;
865 mode->c_iflag &= ~IUCLC;
866 mode->c_oflag &= ~OLCUC;
867 } else {
868 mode->c_lflag |= XCASE;
869 mode->c_iflag |= IUCLC;
870 mode->c_oflag |= OLCUC;
871 }
872 }
873 else if (info->name == stty_crt) {
874 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
875 }
876 else if (info->name == stty_dec) {
877 mode->c_cc[VINTR] = 3; /* ^C */
878 mode->c_cc[VERASE] = 127; /* DEL */
879 mode->c_cc[VKILL] = 21; /* ^U */
880 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
881 if (IXANY) mode->c_iflag &= ~IXANY;
882 }
883}
884
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000885static void set_control_char_or_die(const struct control_info *info,
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000886 const char *arg, struct termios *mode)
887{
888 unsigned char value;
889
890 if (info->name == stty_min || info->name == stty_time)
891 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
892 else if (arg[0] == '\0' || arg[1] == '\0')
893 value = arg[0];
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000894 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000895 value = _POSIX_VDISABLE;
896 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
897 value = arg[1] & 0x1f; /* Non-letters get weird results */
898 if (arg[1] == '?')
899 value = 127;
900 } else
901 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
902 mode->c_cc[info->offset] = value;
903}
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000904
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000905#define STTY_require_set_attr (1<<0)
906#define STTY_speed_was_set (1<<1)
907#define STTY_verbose_output (1<<2)
908#define STTY_recoverable_output (1<<3)
909#define STTY_noargs (1<<4)
Rob Landleydfba7412006-03-06 20:47:33 +0000910int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +0000911{
912 struct termios mode;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000913 void (*output_func)(const struct termios *, const int);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000914 const char *file_name = NULL;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000915 int display_all = 0;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000916 int stty_state;
917 int k;
918
919 stty_state = STTY_noargs;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000920 output_func = do_display;
Eric Andersen98e599c2001-02-14 18:47:33 +0000921
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000922 /* First pass: only parse/verify command line params */
923 k = 0;
924 while (argv[++k]) {
925 const struct mode_info *mp;
926 const struct control_info *cp;
927 const char *arg = argv[k];
928 const char *argnext = argv[k+1];
929 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000930
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000931 if (arg[0] == '-') {
932 int i;
933 mp = find_mode(arg+1);
934 if (mp) {
935 if (!(mp->flags & REV))
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +0000936 goto invalid_argument;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000937 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000938 continue;
939 }
940 /* It is an option - parse it */
941 i = 0;
942 while (arg[++i]) {
943 switch (arg[i]) {
944 case 'a':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000945 stty_state |= STTY_verbose_output;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000946 output_func = do_display;
947 display_all = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000948 break;
949 case 'g':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000950 stty_state |= STTY_recoverable_output;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000951 output_func = display_recoverable;
952 break;
953 case 'F':
954 if (file_name)
955 bb_error_msg_and_die("only one device may be specified");
956 file_name = &arg[i+1]; /* "-Fdevice" ? */
957 if (!file_name[0]) { /* nope, "-F device" */
958 int p = k+1; /* argv[p] is argnext */
959 file_name = argnext;
960 if (!file_name)
961 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
962 /* remove -F param from arg[vc] */
963 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000964 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000965 }
966 goto end_option;
967 default:
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +0000968 goto invalid_argument;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000969 }
970 }
971end_option:
972 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +0000973 }
974
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000975 mp = find_mode(arg);
976 if (mp) {
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000977 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000978 continue;
979 }
980
981 cp = find_control(arg);
982 if (cp) {
983 if (!argnext)
984 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000985 /* called for the side effect of xfunc death only */
986 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000987 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000988 ++k;
989 continue;
990 }
991
992 param = find_param(arg);
993 if (param & param_need_arg) {
994 if (!argnext)
995 bb_error_msg_and_die(bb_msg_requires_arg, arg);
996 ++k;
997 }
998
999 switch (param) {
1000#ifdef HAVE_C_LINE
1001 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001002# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +00001003 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001004 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001005# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001006#endif
1007#ifdef TIOCGWINSZ
1008 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001009 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001010 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001011 break;
1012 case param_size:
1013#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001014 case param_speed:
1015 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001016 case param_ispeed:
1017 /* called for the side effect of xfunc death only */
1018 set_speed_or_die(input_speed, argnext, &mode);
1019 break;
1020 case param_ospeed:
1021 /* called for the side effect of xfunc death only */
1022 set_speed_or_die(output_speed, argnext, &mode);
1023 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001024 default:
1025 if (recover_mode(arg, &mode) == 1) break;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001026 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001027invalid_argument:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001028 bb_error_msg_and_die("invalid argument '%s'", arg);
1029 }
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001030 stty_state &= ~STTY_noargs;
Eric Andersen98e599c2001-02-14 18:47:33 +00001031 }
1032
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001033 /* Specifying both -a and -g is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001034 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001035 (STTY_verbose_output | STTY_recoverable_output))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001036 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1037 /* Specifying -a or -g with non-options is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001038 if (!(stty_state & STTY_noargs) &&
1039 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001040 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +00001041
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001042 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +00001043 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001044 int fd, fdflags;
Eric Andersen98e599c2001-02-14 18:47:33 +00001045 device_name = file_name;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001046 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +00001047 if (fd != STDIN_FILENO) {
1048 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001049 close(fd);
1050 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +00001051 fdflags = fcntl(STDIN_FILENO, F_GETFL);
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001052 if (fdflags < 0 ||
1053 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +00001054 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +00001055 }
1056
1057 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001058 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001059 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001060 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001061 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001062
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001063 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +00001064 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001065 output_func(&mode, display_all);
Eric Andersen98e599c2001-02-14 18:47:33 +00001066 return EXIT_SUCCESS;
1067 }
1068
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001069 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +00001070 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001071 while (argv[++k]) {
1072 const struct mode_info *mp;
1073 const struct control_info *cp;
1074 const char *arg = argv[k];
1075 const char *argnext = argv[k+1];
1076 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001077
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001078 if (arg[0] == '-') {
1079 mp = find_mode(arg+1);
1080 if (mp) {
1081 set_mode(mp, 1 /* reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001082 stty_state |= STTY_require_set_attr;
Eric Andersenfc059092002-06-06 11:35:29 +00001083 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001084 /* It is an option - already parsed. Skip it */
1085 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001086 }
Mark Whitley446dd272001-03-02 20:00:54 +00001087
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001088 mp = find_mode(arg);
1089 if (mp) {
1090 set_mode(mp, 0 /* non-reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001091 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001092 continue;
1093 }
Mark Whitley446dd272001-03-02 20:00:54 +00001094
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001095 cp = find_control(arg);
1096 if (cp) {
1097 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001098 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001099 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001100 continue;
1101 }
Mark Whitley446dd272001-03-02 20:00:54 +00001102
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001103 param = find_param(arg);
1104 if (param & param_need_arg) {
1105 ++k;
1106 }
1107
1108 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001109#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001110 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +00001111 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001112 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001113 break;
Eric Andersen98e599c2001-02-14 18:47:33 +00001114#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001115#ifdef TIOCGWINSZ
1116 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001117 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001118 break;
1119 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001120 display_window_size(0);
1121 break;
1122 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +00001123 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001124 break;
1125#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001126 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001127 display_speed(&mode, 0);
1128 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001129 case param_ispeed:
1130 set_speed_or_die(input_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001131 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001132 break;
1133 case param_ospeed:
1134 set_speed_or_die(output_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001135 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001136 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001137 default:
1138 if (recover_mode(arg, &mode) == 1)
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001139 stty_state |= STTY_require_set_attr;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001140 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001141 set_speed_or_die(both_speeds, arg, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001142 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001143 } /* else - impossible (caught in the first pass):
1144 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +00001145 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001146 }
1147
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001148 if (stty_state & STTY_require_set_attr) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001149 struct termios new_mode;
1150
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001151 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001152 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001153
1154 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1155 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +00001156 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +00001157 some proper subset of the requested operations. To detect
1158 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001159 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +00001160
1161 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001162 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001163 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001164 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001165 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001166
Eric Andersen98e599c2001-02-14 18:47:33 +00001167 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1168#ifdef CIBAUD
1169 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1170 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1171 sometimes (m1 != m2). The only difference is in the four bits
1172 of the c_cflag field corresponding to the baud rate. To save
1173 Sun users a little confusion, don't report an error if this
1174 happens. But suppress the error only if we haven't tried to
1175 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001176 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +00001177
1178 new_mode.c_cflag &= (~CIBAUD);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001179 if ((stty_state & STTY_speed_was_set)
1180 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
Eric Andersen98e599c2001-02-14 18:47:33 +00001181#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +00001182 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +00001183 }
1184 }
1185
1186 return EXIT_SUCCESS;
1187}