blob: b489414297fb6442b7ffb41c7d5ca67ce73437b1 [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 {
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000161 const char *name; /* Name given on command line */
162 char type; /* Which structure element to change */
163 char flags; /* Setting and display options */
164 /* were using short here, but ppc32 was unhappy: */
165 tcflag_t mask; /* Other bits to turn off for this mode */
166 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 {
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000327 const char *name; /* Name given on command line */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000328 unsigned char saneval; /* Value to set for 'stty sane' */
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000329 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
428static speed_t string_to_baud_or_die(const char *arg)
429{
Denis Vlasenko13858992006-10-08 12:49:22 +0000430 return tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000431}
432
433static void set_speed_or_die(enum speed_setting type, const char *arg,
434 struct termios *mode)
435{
436 speed_t baud;
437
438 baud = string_to_baud_or_die(arg);
439
440 if (type != output_speed) { /* either input or both */
441 cfsetispeed(mode, baud);
442 }
443 if (type != input_speed) { /* either output or both */
444 cfsetospeed(mode, baud);
445 }
446}
447
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000448static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000449{
450 bb_perror_msg_and_die(fmt, device_name);
451}
452
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000453static void perror_on_device(const char *fmt)
454{
455 bb_perror_msg(fmt, device_name);
456}
457
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000458/* No, inline won't be as efficient (gcc 3.4.3) */
459#define streq(a,b) (!strcmp((a),(b)))
Eric Andersen98e599c2001-02-14 18:47:33 +0000460
461/* Print format string MESSAGE and optional args.
462 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000463 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000464static void wrapf(const char *message, ...)
465{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000466 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000467 va_list args;
Eric Andersen98e599c2001-02-14 18:47:33 +0000468 int buflen;
469
470 va_start(args, message);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000471 vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000472 va_end(args);
473 buflen = strlen(buf);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000474 if (!buflen) return;
475
Eric Andersen98e599c2001-02-14 18:47:33 +0000476 if (current_col > 0) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000477 current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000478 if (buf[0] != '\n') {
479 if (current_col + buflen >= max_col) {
480 putchar('\n');
481 current_col = 0;
482 } else
483 putchar(' ');
484 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000485 }
486 fputs(buf, stdout);
487 current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000488 if (buf[buflen-1] == '\n')
489 current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000490}
491
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000492#ifdef TIOCGWINSZ
493
Denis Vlasenko79deb662006-09-19 15:12:12 +0000494static int get_win_size(int fd, struct winsize *win)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000495{
Denis Vlasenko79deb662006-09-19 15:12:12 +0000496 return ioctl(fd, TIOCGWINSZ, (char *) win);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000497}
498
499static void set_window_size(int rows, int cols)
500{
501 struct winsize win;
502
Denis Vlasenko79deb662006-09-19 15:12:12 +0000503 if (get_win_size(STDIN_FILENO, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000504 if (errno != EINVAL) {
505 perror_on_device("%s");
506 return;
507 }
508 memset(&win, 0, sizeof(win));
509 }
510
511 if (rows >= 0)
512 win.ws_row = rows;
513 if (cols >= 0)
514 win.ws_col = cols;
515
516# ifdef TIOCSSIZE
517 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
518 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
519 This comment from sys/ttold.h describes Sun's twisted logic - a better
520 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
521 At any rate, the problem is gone in Solaris 2.x */
522
523 if (win.ws_row == 0 || win.ws_col == 0) {
524 struct ttysize ttysz;
525
526 ttysz.ts_lines = win.ws_row;
527 ttysz.ts_cols = win.ws_col;
528
529 win.ws_row = win.ws_col = 1;
530
531 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
532 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
533 perror_on_device("%s");
534 }
535 return;
536 }
537# endif
538
539 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
540 perror_on_device("%s");
541}
542
543static void display_window_size(int fancy)
544{
545 const char *fmt_str = "%s\0%s: no size information for this device";
546 struct winsize win;
547
Denis Vlasenko79deb662006-09-19 15:12:12 +0000548 if (get_win_size(STDIN_FILENO, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000549 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
550 perror_on_device(fmt_str);
551 }
552 } else {
553 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
554 win.ws_row, win.ws_col);
555 }
556}
557
558#else /* !TIOCGWINSZ */
559
560static inline void display_window_size(int fancy) {}
561
562#endif /* !TIOCGWINSZ */
563
Denis Vlasenko13858992006-10-08 12:49:22 +0000564static int screen_columns_or_die(void)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000565{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000566 const char *s;
567
568#ifdef TIOCGWINSZ
569 struct winsize win;
570
571 /* With Solaris 2.[123], this ioctl fails and errno is set to
572 EINVAL for telnet (but not rlogin) sessions.
573 On ISC 3.0, it fails for the console and the serial port
574 (but it works for ptys).
575 It can also fail on any system when stdout isn't a tty.
576 In case of any failure, just use the default */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000577 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000578 return win.ws_col;
579#endif
580
Denis Vlasenko13858992006-10-08 12:49:22 +0000581 s = getenv("COLUMNS");
582 if (s)
583 return xatoi_u(s);
584 return 80;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000585}
586
Eric Andersen98e599c2001-02-14 18:47:33 +0000587static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000588 {"b", 512 },
589 {"k", 1024},
590 {"B", 1024},
591 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000592};
593
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000594static const struct mode_info *find_mode(const char *name)
595{
596 int i;
597 for (i = 0; i < NUM_mode_info; ++i)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000598 if (streq(name, mode_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000599 return &mode_info[i];
600 return 0;
601}
602
603static const struct control_info *find_control(const char *name)
604{
605 int i;
606 for (i = 0; i < NUM_control_info; ++i)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000607 if (streq(name, control_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000608 return &control_info[i];
609 return 0;
610}
611
612enum {
613 param_need_arg = 0x80,
614 param_line = 1 | 0x80,
615 param_rows = 2 | 0x80,
616 param_cols = 3 | 0x80,
617 param_size = 4,
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000618 param_speed = 5,
619 param_ispeed = 6 | 0x80,
620 param_ospeed = 7 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000621};
622
623static int find_param(const char *name)
624{
625#ifdef HAVE_C_LINE
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000626 if (streq(name, "line")) return param_line;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000627#endif
628#ifdef TIOCGWINSZ
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000629 if (streq(name, "rows")) return param_rows;
630 if (streq(name, "cols")) return param_cols;
631 if (streq(name, "columns")) return param_cols;
632 if (streq(name, "size")) return param_size;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000633#endif
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000634 if (streq(name, "speed")) return param_speed;
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000635 if (streq(name, "ispeed")) return param_ispeed;
636 if (streq(name, "ospeed")) return param_ospeed;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000637 return 0;
638}
639
640
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000641static int recover_mode(const char *arg, struct termios *mode);
642static void set_mode(const struct mode_info *info,
643 int reversed, struct termios *mode);
644static void display_all(const struct termios *mode);
645static void display_changed(const struct termios *mode);
646static void display_recoverable(const struct termios *mode);
647static void display_speed(const struct termios *mode, int fancy);
648static void sane_mode(struct termios *mode);
649static void set_control_char_or_die(const struct control_info *info,
650 const char *arg, struct termios *mode);
651
Rob Landleydfba7412006-03-06 20:47:33 +0000652int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +0000653{
654 struct termios mode;
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000655 void (*output_func)(const struct termios *);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000656 const char *file_name = NULL;
657 int require_set_attr;
658 int speed_was_set;
659 int verbose_output;
660 int recoverable_output;
661 int noargs;
662 int k;
Eric Andersen98e599c2001-02-14 18:47:33 +0000663
Manuel Novoa III cad53642003-03-19 09:13:01 +0000664 output_func = display_changed;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000665 noargs = 1;
666 speed_was_set = 0;
667 require_set_attr = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000668 verbose_output = 0;
669 recoverable_output = 0;
670
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000671 /* First pass: only parse/verify command line params */
672 k = 0;
673 while (argv[++k]) {
674 const struct mode_info *mp;
675 const struct control_info *cp;
676 const char *arg = argv[k];
677 const char *argnext = argv[k+1];
678 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000679
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000680 if (arg[0] == '-') {
681 int i;
682 mp = find_mode(arg+1);
683 if (mp) {
684 if (!(mp->flags & REV))
685 bb_error_msg_and_die("invalid argument '%s'", arg);
686 noargs = 0;
687 continue;
688 }
689 /* It is an option - parse it */
690 i = 0;
691 while (arg[++i]) {
692 switch (arg[i]) {
693 case 'a':
694 verbose_output = 1;
695 output_func = display_all;
696 break;
697 case 'g':
698 recoverable_output = 1;
699 output_func = display_recoverable;
700 break;
701 case 'F':
702 if (file_name)
703 bb_error_msg_and_die("only one device may be specified");
704 file_name = &arg[i+1]; /* "-Fdevice" ? */
705 if (!file_name[0]) { /* nope, "-F device" */
706 int p = k+1; /* argv[p] is argnext */
707 file_name = argnext;
708 if (!file_name)
709 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
710 /* remove -F param from arg[vc] */
711 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000712 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000713 }
714 goto end_option;
715 default:
716 bb_error_msg_and_die("invalid argument '%s'", arg);
717 }
718 }
719end_option:
720 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +0000721 }
722
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000723 mp = find_mode(arg);
724 if (mp) {
725 noargs = 0;
726 continue;
727 }
728
729 cp = find_control(arg);
730 if (cp) {
731 if (!argnext)
732 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000733 /* called for the side effect of xfunc death only */
734 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000735 noargs = 0;
736 ++k;
737 continue;
738 }
739
740 param = find_param(arg);
741 if (param & param_need_arg) {
742 if (!argnext)
743 bb_error_msg_and_die(bb_msg_requires_arg, arg);
744 ++k;
745 }
746
747 switch (param) {
748#ifdef HAVE_C_LINE
749 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000750# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +0000751 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000752 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000753# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000754#endif
755#ifdef TIOCGWINSZ
756 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000757 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +0000758 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000759 break;
760 case param_size:
761#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000762 case param_speed:
763 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000764 case param_ispeed:
765 /* called for the side effect of xfunc death only */
766 set_speed_or_die(input_speed, argnext, &mode);
767 break;
768 case param_ospeed:
769 /* called for the side effect of xfunc death only */
770 set_speed_or_die(output_speed, argnext, &mode);
771 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000772 default:
773 if (recover_mode(arg, &mode) == 1) break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000774 if (string_to_baud_or_die(arg) != (speed_t) -1) break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000775 bb_error_msg_and_die("invalid argument '%s'", arg);
776 }
777 noargs = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000778 }
779
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000780 /* Specifying both -a and -g is an error */
781 if (verbose_output && recoverable_output)
782 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
783 /* Specifying -a or -g with non-options is an error */
784 if (!noargs && (verbose_output || recoverable_output))
785 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000786
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000787 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +0000788 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000789 int fd, fdflags;
Eric Andersen98e599c2001-02-14 18:47:33 +0000790 device_name = file_name;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000791 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +0000792 if (fd != STDIN_FILENO) {
793 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000794 close(fd);
795 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +0000796 fdflags = fcntl(STDIN_FILENO, F_GETFL);
797 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +0000798 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +0000799 }
800
801 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000802 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +0000803 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000804 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000805 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000806
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000807 if (verbose_output || recoverable_output || noargs) {
Denis Vlasenko13858992006-10-08 12:49:22 +0000808 max_col = screen_columns_or_die();
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000809 output_func(&mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000810 return EXIT_SUCCESS;
811 }
812
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000813 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +0000814 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000815 while (argv[++k]) {
816 const struct mode_info *mp;
817 const struct control_info *cp;
818 const char *arg = argv[k];
819 const char *argnext = argv[k+1];
820 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000821
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000822 if (arg[0] == '-') {
823 mp = find_mode(arg+1);
824 if (mp) {
825 set_mode(mp, 1 /* reversed */, &mode);
Eric Andersenfc059092002-06-06 11:35:29 +0000826 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000827 /* It is an option - already parsed. Skip it */
828 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +0000829 }
Mark Whitley446dd272001-03-02 20:00:54 +0000830
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000831 mp = find_mode(arg);
832 if (mp) {
833 set_mode(mp, 0 /* non-reversed */, &mode);
834 continue;
835 }
Mark Whitley446dd272001-03-02 20:00:54 +0000836
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000837 cp = find_control(arg);
838 if (cp) {
839 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000840 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000841 continue;
842 }
Mark Whitley446dd272001-03-02 20:00:54 +0000843
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000844 param = find_param(arg);
845 if (param & param_need_arg) {
846 ++k;
847 }
848
849 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000850#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000851 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +0000852 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000853 require_set_attr = 1;
854 break;
Eric Andersen98e599c2001-02-14 18:47:33 +0000855#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000856#ifdef TIOCGWINSZ
857 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +0000858 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000859 break;
860 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000861 display_window_size(0);
862 break;
863 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +0000864 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000865 break;
866#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000867 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000868 display_speed(&mode, 0);
869 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000870 case param_ispeed:
871 set_speed_or_die(input_speed, argnext, &mode);
872 speed_was_set = 1;
873 require_set_attr = 1;
874 break;
875 case param_ospeed:
876 set_speed_or_die(output_speed, argnext, &mode);
877 speed_was_set = 1;
878 require_set_attr = 1;
879 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000880 default:
881 if (recover_mode(arg, &mode) == 1)
Mark Whitley446dd272001-03-02 20:00:54 +0000882 require_set_attr = 1;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000883 else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
884 set_speed_or_die(both_speeds, arg, &mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000885 speed_was_set = 1;
886 require_set_attr = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000887 } /* else - impossible (caught in the first pass):
888 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +0000889 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000890 }
891
892 if (require_set_attr) {
893 struct termios new_mode;
894
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000895 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000896 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000897
898 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
899 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +0000900 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +0000901 some proper subset of the requested operations. To detect
902 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000903 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +0000904
905 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000906 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +0000907 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000908 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000909 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000910
Eric Andersen98e599c2001-02-14 18:47:33 +0000911 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
912#ifdef CIBAUD
913 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
914 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
915 sometimes (m1 != m2). The only difference is in the four bits
916 of the c_cflag field corresponding to the baud rate. To save
917 Sun users a little confusion, don't report an error if this
918 happens. But suppress the error only if we haven't tried to
919 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000920 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +0000921
922 new_mode.c_cflag &= (~CIBAUD);
923 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
924#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +0000925 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +0000926 }
927 }
928
929 return EXIT_SUCCESS;
930}
931
Denis Vlasenko8971cda2006-09-19 14:20:22 +0000932/* Save set_mode from #ifdef forest plague */
933#ifndef ONLCR
934#define ONLCR 0
935#endif
936#ifndef OCRNL
937#define OCRNL 0
938#endif
939#ifndef ONLRET
940#define ONLRET 0
941#endif
942#ifndef XCASE
943#define XCASE 0
944#endif
945#ifndef IXANY
946#define IXANY 0
947#endif
948#ifndef TABDLY
949#define TABDLY 0
950#endif
951#ifndef OXTABS
952#define OXTABS 0
953#endif
954#ifndef IUCLC
955#define IUCLC 0
956#endif
957#ifndef OLCUC
958#define OLCUC 0
959#endif
960#ifndef ECHOCTL
961#define ECHOCTL 0
962#endif
963#ifndef ECHOKE
964#define ECHOKE 0
965#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000966
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000967static void set_mode(const struct mode_info *info, int reversed,
968 struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +0000969{
970 tcflag_t *bitsp;
971
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000972 bitsp = mode_type_flag(info->type, mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000973
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000974 if (bitsp) {
975 if (reversed)
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000976 *bitsp = *bitsp & ~info->mask & ~info->bits;
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000977 else
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000978 *bitsp = (*bitsp & ~info->mask) | info->bits;
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000979 return;
980 }
981
982 /* Combination mode */
983 if (info->name == evenp || info->name == parity) {
984 if (reversed)
985 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
986 else
987 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
988 } else if (info->name == stty_oddp) {
989 if (reversed)
990 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
991 else
992 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
993 } else if (info->name == stty_nl) {
994 if (reversed) {
995 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
996 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
997 } else {
998 mode->c_iflag = mode->c_iflag & ~ICRNL;
999 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1000 }
1001 } else if (info->name == stty_ek) {
1002 mode->c_cc[VERASE] = CERASE;
1003 mode->c_cc[VKILL] = CKILL;
1004 } else if (info->name == stty_sane) {
1005 sane_mode(mode);
1006 }
1007 else if (info->name == cbreak) {
1008 if (reversed)
1009 mode->c_lflag |= ICANON;
1010 else
1011 mode->c_lflag &= ~ICANON;
1012 } else if (info->name == stty_pass8) {
1013 if (reversed) {
1014 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1015 mode->c_iflag |= ISTRIP;
1016 } else {
1017 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1018 mode->c_iflag &= ~ISTRIP;
1019 }
1020 } else if (info->name == litout) {
1021 if (reversed) {
1022 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1023 mode->c_iflag |= ISTRIP;
1024 mode->c_oflag |= OPOST;
1025 } else {
1026 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1027 mode->c_iflag &= ~ISTRIP;
1028 mode->c_oflag &= ~OPOST;
1029 }
1030 } else if (info->name == raw || info->name == cooked) {
1031 if ((info->name[0] == 'r' && reversed)
1032 || (info->name[0] == 'c' && !reversed)) {
1033 /* Cooked mode */
1034 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1035 mode->c_oflag |= OPOST;
1036 mode->c_lflag |= ISIG | ICANON;
Eric Andersen98e599c2001-02-14 18:47:33 +00001037#if VMIN == VEOF
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001038 mode->c_cc[VEOF] = CEOF;
Eric Andersen98e599c2001-02-14 18:47:33 +00001039#endif
1040#if VTIME == VEOL
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001041 mode->c_cc[VEOL] = CEOL;
Eric Andersen98e599c2001-02-14 18:47:33 +00001042#endif
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001043 } else {
1044 /* Raw mode */
1045 mode->c_iflag = 0;
1046 mode->c_oflag &= ~OPOST;
1047 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1048 mode->c_cc[VMIN] = 1;
1049 mode->c_cc[VTIME] = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +00001050 }
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001051 }
1052 else if (IXANY && info->name == decctlq) {
1053 if (reversed)
1054 mode->c_iflag |= IXANY;
1055 else
1056 mode->c_iflag &= ~IXANY;
1057 }
1058 else if (TABDLY && info->name == stty_tabs) {
1059 if (reversed)
1060 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1061 else
1062 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1063 }
1064 else if (OXTABS && info->name == stty_tabs) {
1065 if (reversed)
1066 mode->c_oflag |= OXTABS;
1067 else
1068 mode->c_oflag &= ~OXTABS;
1069 }
1070 else if (XCASE && IUCLC && OLCUC
1071 && (info->name == stty_lcase || info->name == stty_LCASE)) {
1072 if (reversed) {
1073 mode->c_lflag &= ~XCASE;
1074 mode->c_iflag &= ~IUCLC;
1075 mode->c_oflag &= ~OLCUC;
1076 } else {
1077 mode->c_lflag |= XCASE;
1078 mode->c_iflag |= IUCLC;
1079 mode->c_oflag |= OLCUC;
Eric Andersen98e599c2001-02-14 18:47:33 +00001080 }
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001081 }
1082 else if (info->name == stty_crt) {
1083 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1084 }
1085 else if (info->name == stty_dec) {
1086 mode->c_cc[VINTR] = 3; /* ^C */
1087 mode->c_cc[VERASE] = 127; /* DEL */
1088 mode->c_cc[VKILL] = 21; /* ^U */
1089 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1090 if (IXANY) mode->c_iflag &= ~IXANY;
1091 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001092}
1093
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001094static void set_control_char_or_die(const struct control_info *info,
1095 const char *arg, struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001096{
1097 unsigned char value;
1098
1099 if (info->name == stty_min || info->name == stty_time)
Denis Vlasenko13858992006-10-08 12:49:22 +00001100 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001101 else if (arg[0] == '\0' || arg[1] == '\0')
1102 value = arg[0];
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001103 else if (streq(arg, "^-") || streq(arg, "undef"))
Eric Andersen98e599c2001-02-14 18:47:33 +00001104 value = _POSIX_VDISABLE;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001105 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001106 value = arg[1] & 0x1f; /* Non-letters get weird results */
Eric Andersen98e599c2001-02-14 18:47:33 +00001107 if (arg[1] == '?')
1108 value = 127;
Eric Andersen98e599c2001-02-14 18:47:33 +00001109 } else
Denis Vlasenko13858992006-10-08 12:49:22 +00001110 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001111 mode->c_cc[info->offset] = value;
1112}
1113
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001114static void display_changed(const struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001115{
1116 int i;
Eric Andersen98e599c2001-02-14 18:47:33 +00001117 tcflag_t *bitsp;
1118 unsigned long mask;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001119 int prev_type = control;
Eric Andersen98e599c2001-02-14 18:47:33 +00001120
1121 display_speed(mode, 1);
1122#ifdef HAVE_C_LINE
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001123 wrapf("line = %d;\n", mode->c_line);
1124#else
1125 wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001126#endif
Eric Andersen98e599c2001-02-14 18:47:33 +00001127
Eric Andersen98e599c2001-02-14 18:47:33 +00001128 for (i = 0; control_info[i].name != stty_min; ++i) {
1129 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1130 continue;
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001131 /* If swtch is the same as susp, don't print both */
Eric Andersen98e599c2001-02-14 18:47:33 +00001132#if VSWTCH == VSUSP
1133 if (control_info[i].name == stty_swtch)
1134 continue;
1135#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001136 /* If eof uses the same slot as min, only print whichever applies */
Eric Andersen98e599c2001-02-14 18:47:33 +00001137#if VEOF == VMIN
1138 if ((mode->c_lflag & ICANON) == 0
1139 && (control_info[i].name == stty_eof
1140 || control_info[i].name == stty_eol)) continue;
1141#endif
Eric Andersen98e599c2001-02-14 18:47:33 +00001142 wrapf("%s = %s;", control_info[i].name,
1143 visible(mode->c_cc[control_info[i].offset]));
1144 }
1145 if ((mode->c_lflag & ICANON) == 0) {
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001146 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
Eric Andersen98e599c2001-02-14 18:47:33 +00001147 (int) mode->c_cc[VTIME]);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001148 }
1149 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001150
Eric Andersen98e599c2001-02-14 18:47:33 +00001151 for (i = 0; i < NUM_mode_info; ++i) {
1152 if (mode_info[i].flags & OMIT)
1153 continue;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001154 if (mode_info[i].type != prev_type) {
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001155 if (current_col) wrapf("\n");
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001156 prev_type = mode_info[i].type;
Eric Andersen98e599c2001-02-14 18:47:33 +00001157 }
1158
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001159 bitsp = mode_type_flag(mode_info[i].type, mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001160 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1161 if ((*bitsp & mask) == mode_info[i].bits) {
1162 if (mode_info[i].flags & SANE_UNSET) {
1163 wrapf("%s", mode_info[i].name);
Eric Andersen98e599c2001-02-14 18:47:33 +00001164 }
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001165 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001166 wrapf("-%s", mode_info[i].name);
Eric Andersen98e599c2001-02-14 18:47:33 +00001167 }
1168 }
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001169 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001170}
1171
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001172static void display_all(const struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001173{
1174 int i;
1175 tcflag_t *bitsp;
1176 unsigned long mask;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001177 int prev_type = control;
Eric Andersen98e599c2001-02-14 18:47:33 +00001178
1179 display_speed(mode, 1);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001180 display_window_size(1);
Eric Andersen98e599c2001-02-14 18:47:33 +00001181#ifdef HAVE_C_LINE
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001182 wrapf("line = %d;\n", mode->c_line);
1183#else
1184 wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001185#endif
Eric Andersen98e599c2001-02-14 18:47:33 +00001186
1187 for (i = 0; control_info[i].name != stty_min; ++i) {
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001188 /* If swtch is the same as susp, don't print both */
Eric Andersen98e599c2001-02-14 18:47:33 +00001189#if VSWTCH == VSUSP
1190 if (control_info[i].name == stty_swtch)
1191 continue;
1192#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001193 /* If eof uses the same slot as min, only print whichever applies */
Eric Andersen98e599c2001-02-14 18:47:33 +00001194#if VEOF == VMIN
1195 if ((mode->c_lflag & ICANON) == 0
1196 && (control_info[i].name == stty_eof
1197 || control_info[i].name == stty_eol)) continue;
1198#endif
1199 wrapf("%s = %s;", control_info[i].name,
1200 visible(mode->c_cc[control_info[i].offset]));
1201 }
1202#if VEOF == VMIN
1203 if ((mode->c_lflag & ICANON) == 0)
1204#endif
1205 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001206 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001207
1208 for (i = 0; i < NUM_mode_info; ++i) {
1209 if (mode_info[i].flags & OMIT)
1210 continue;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001211 if (mode_info[i].type != prev_type) {
1212 wrapf("\n");
1213 prev_type = mode_info[i].type;
Eric Andersen98e599c2001-02-14 18:47:33 +00001214 }
1215
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001216 bitsp = mode_type_flag(mode_info[i].type, mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001217 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1218 if ((*bitsp & mask) == mode_info[i].bits)
1219 wrapf("%s", mode_info[i].name);
1220 else if (mode_info[i].flags & REV)
1221 wrapf("-%s", mode_info[i].name);
1222 }
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001223 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001224}
1225
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001226static void display_speed(const struct termios *mode, int fancy)
Eric Andersen98e599c2001-02-14 18:47:33 +00001227{
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001228 //01234567 8 9
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001229 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001230 unsigned long ispeed, ospeed;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001231
1232 ospeed = ispeed = cfgetispeed(mode);
1233 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001234 ispeed = ospeed; /* in case ispeed was 0 */
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001235 //0123 4 5 6 7 8 9
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001236 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001237 }
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001238 if (fancy) fmt_str += 9;
Rob Landley290fcb42006-06-18 23:59:03 +00001239 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001240}
1241
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001242static void display_recoverable(const struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001243{
1244 int i;
Eric Andersen98e599c2001-02-14 18:47:33 +00001245 printf("%lx:%lx:%lx:%lx",
1246 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1247 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1248 for (i = 0; i < NCCS; ++i)
1249 printf(":%x", (unsigned int) mode->c_cc[i]);
1250 putchar('\n');
1251}
1252
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001253static int recover_mode(const char *arg, struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001254{
1255 int i, n;
1256 unsigned int chr;
1257 unsigned long iflag, oflag, cflag, lflag;
1258
1259 /* Scan into temporaries since it is too much trouble to figure out
Denis Vlasenko79deb662006-09-19 15:12:12 +00001260 the right format for 'tcflag_t' */
Eric Andersen98e599c2001-02-14 18:47:33 +00001261 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1262 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1263 return 0;
1264 mode->c_iflag = iflag;
1265 mode->c_oflag = oflag;
1266 mode->c_cflag = cflag;
1267 mode->c_lflag = lflag;
1268 arg += n;
1269 for (i = 0; i < NCCS; ++i) {
1270 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1271 return 0;
1272 mode->c_cc[i] = chr;
1273 arg += n;
1274 }
1275
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001276 /* Fail if there are too many fields */
Eric Andersen98e599c2001-02-14 18:47:33 +00001277 if (*arg != '\0')
1278 return 0;
1279
1280 return 1;
1281}
1282
Eric Andersen98e599c2001-02-14 18:47:33 +00001283static void sane_mode(struct termios *mode)
1284{
1285 int i;
1286 tcflag_t *bitsp;
1287
1288 for (i = 0; i < NUM_control_info; ++i) {
1289#if VMIN == VEOF
1290 if (control_info[i].name == stty_min)
1291 break;
1292#endif
1293 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1294 }
1295
1296 for (i = 0; i < NUM_mode_info; ++i) {
1297 if (mode_info[i].flags & SANE_SET) {
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001298 bitsp = mode_type_flag(mode_info[i].type, mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001299 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1300 | mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001301 } else if (mode_info[i].flags & SANE_UNSET) {
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001302 bitsp = mode_type_flag(mode_info[i].type, mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001303 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1304 & ~mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001305 }
1306 }
1307}