blob: 863f28d92eb1b73de71c233400f8cafa2da25446 [file] [log] [blame]
Eric Andersen98e599c2001-02-14 18:47:33 +00001/* vi: set sw=4 ts=4: */
2/* stty -- change and print terminal line settings
3 Copyright (C) 1990-1999 Free Software Foundation, Inc.
4
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +00005 Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
6*/
Eric Andersen98e599c2001-02-14 18:47:33 +00007/* Usage: stty [-ag] [-F device] [setting...]
8
9 Options:
10 -a Write all current settings to stdout in human-readable form.
11 -g Write all current settings to stdout in stty-readable form.
12 -F Open and use the specified device instead of stdin
13
14 If no args are given, write to stdout the baud rate and settings that
15 have been changed from their defaults. Mode reading and changes
16 are done on the specified device, or stdin if none was specified.
17
18 David MacKenzie <djm@gnu.ai.mit.edu>
19
Eric Andersen7467c8d2001-07-12 20:26:32 +000020 Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
Eric Andersen98e599c2001-02-14 18:47:33 +000021
22 */
23
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000024#include "libbb.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000025
Eric Andersen98e599c2001-02-14 18:47:33 +000026#ifndef _POSIX_VDISABLE
27# define _POSIX_VDISABLE ((unsigned char) 0)
28#endif
29
30#define Control(c) ((c) & 0x1f)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000031/* Canonical values for control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +000032#ifndef CINTR
Denis Vlasenko9efb0702006-09-19 14:17:10 +000033# define CINTR Control('c')
Eric Andersen98e599c2001-02-14 18:47:33 +000034#endif
35#ifndef CQUIT
36# define CQUIT 28
37#endif
38#ifndef CERASE
39# define CERASE 127
40#endif
41#ifndef CKILL
Denis Vlasenko9efb0702006-09-19 14:17:10 +000042# define CKILL Control('u')
Eric Andersen98e599c2001-02-14 18:47:33 +000043#endif
44#ifndef CEOF
Denis Vlasenko9efb0702006-09-19 14:17:10 +000045# define CEOF Control('d')
Eric Andersen98e599c2001-02-14 18:47:33 +000046#endif
47#ifndef CEOL
48# define CEOL _POSIX_VDISABLE
49#endif
50#ifndef CSTART
Denis Vlasenko9efb0702006-09-19 14:17:10 +000051# define CSTART Control('q')
Eric Andersen98e599c2001-02-14 18:47:33 +000052#endif
53#ifndef CSTOP
Denis Vlasenko9efb0702006-09-19 14:17:10 +000054# define CSTOP Control('s')
Eric Andersen98e599c2001-02-14 18:47:33 +000055#endif
56#ifndef CSUSP
Denis Vlasenko9efb0702006-09-19 14:17:10 +000057# define CSUSP Control('z')
Eric Andersen98e599c2001-02-14 18:47:33 +000058#endif
59#if defined(VEOL2) && !defined(CEOL2)
60# define CEOL2 _POSIX_VDISABLE
61#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +000062/* ISC renamed swtch to susp for termios, but we'll accept either name */
Eric Andersen98e599c2001-02-14 18:47:33 +000063#if defined(VSUSP) && !defined(VSWTCH)
64# define VSWTCH VSUSP
65# define CSWTCH CSUSP
66#endif
67#if defined(VSWTCH) && !defined(CSWTCH)
68# define CSWTCH _POSIX_VDISABLE
69#endif
70
Denis Vlasenko79deb662006-09-19 15:12:12 +000071/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
72 So the default is to disable 'swtch.' */
Denis Vlasenkoff131b92007-04-10 15:42:06 +000073#if defined(__sparc__) && defined(__svr4__)
Eric Andersen98e599c2001-02-14 18:47:33 +000074# undef CSWTCH
75# define CSWTCH _POSIX_VDISABLE
76#endif
77
Denis Vlasenkoff131b92007-04-10 15:42:06 +000078#if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000079# define VWERASE VWERSE
80#endif
Denis Vlasenkoff131b92007-04-10 15:42:06 +000081#if defined(VDSUSP) && !defined(CDSUSP)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000082# define CDSUSP Control('y')
Eric Andersen98e599c2001-02-14 18:47:33 +000083#endif
Mark Whitley446dd272001-03-02 20:00:54 +000084#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000085# define VREPRINT VRPRNT
86#endif
87#if defined(VREPRINT) && !defined(CRPRNT)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000088# define CRPRNT Control('r')
Eric Andersen98e599c2001-02-14 18:47:33 +000089#endif
90#if defined(VWERASE) && !defined(CWERASE)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000091# define CWERASE Control('w')
Eric Andersen98e599c2001-02-14 18:47:33 +000092#endif
93#if defined(VLNEXT) && !defined(CLNEXT)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000094# define CLNEXT Control('v')
Eric Andersen98e599c2001-02-14 18:47:33 +000095#endif
96#if defined(VDISCARD) && !defined(VFLUSHO)
97# define VFLUSHO VDISCARD
98#endif
Mark Whitley446dd272001-03-02 20:00:54 +000099#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000100# define VFLUSHO VFLUSH
101#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000102#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000103# define ECHOCTL CTLECH
104#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000105#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000106# define ECHOCTL TCTLECH
107#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000108#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000109# define ECHOKE CRTKIL
110#endif
111#if defined(VFLUSHO) && !defined(CFLUSHO)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000112# define CFLUSHO Control('o')
Eric Andersen98e599c2001-02-14 18:47:33 +0000113#endif
114#if defined(VSTATUS) && !defined(CSTATUS)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000115# define CSTATUS Control('t')
Eric Andersen98e599c2001-02-14 18:47:33 +0000116#endif
117
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000118/* Which speeds to set */
Eric Andersen98e599c2001-02-14 18:47:33 +0000119enum speed_setting {
120 input_speed, output_speed, both_speeds
121};
122
Denis Vlasenko79deb662006-09-19 15:12:12 +0000123/* Which member(s) of 'struct termios' a mode uses */
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000124enum {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000125 /* Do NOT change the order or values, as mode_type_flag()
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000126 * depends on them */
Eric Andersen98e599c2001-02-14 18:47:33 +0000127 control, input, output, local, combination
128};
129
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000130static const char evenp [] ALIGN1 = "evenp";
131static const char raw [] ALIGN1 = "raw";
132static const char stty_min [] ALIGN1 = "min";
133static const char stty_time [] ALIGN1 = "time";
134static const char stty_swtch[] ALIGN1 = "swtch";
135static const char stty_eol [] ALIGN1 = "eol";
136static const char stty_eof [] ALIGN1 = "eof";
137static const char parity [] ALIGN1 = "parity";
138static const char stty_oddp [] ALIGN1 = "oddp";
139static const char stty_nl [] ALIGN1 = "nl";
140static const char stty_ek [] ALIGN1 = "ek";
141static const char stty_sane [] ALIGN1 = "sane";
142static const char cbreak [] ALIGN1 = "cbreak";
143static const char stty_pass8[] ALIGN1 = "pass8";
144static const char litout [] ALIGN1 = "litout";
145static const char cooked [] ALIGN1 = "cooked";
146static const char decctlq [] ALIGN1 = "decctlq";
147static const char stty_tabs [] ALIGN1 = "tabs";
148static const char stty_lcase[] ALIGN1 = "lcase";
149static const char stty_LCASE[] ALIGN1 = "LCASE";
150static const char stty_crt [] ALIGN1 = "crt";
151static const char stty_dec [] ALIGN1 = "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 Vlasenko6ca409e2007-08-12 20:58:27 +0000161 const char *const name; /* Name given on command line */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000162 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 Vlasenko80b8b392007-06-25 10:55:35 +0000322 NUM_mode_info = ARRAY_SIZE(mode_info)
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 Vlasenko6ca409e2007-08-12 20:58:27 +0000327 const char *const name; /* Name given on command line */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000328 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 Vlasenko80b8b392007-06-25 10:55:35 +0000374 NUM_control_info = ARRAY_SIZE(control_info)
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 */
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000378unsigned max_col = 80; /* default */
379
380struct globals {
381 /* Current position, to know when to wrap */
382 unsigned current_col;
383 char buf[10];
384};
385#define G (*(struct globals*)&bb_common_bufsiz1)
386
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000387static const char *device_name = bb_msg_standard_input;
Eric Andersen8876fb22003-06-20 09:01:58 +0000388
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000389/* Return a string that is the printable representation of character CH */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000390/* Adapted from 'cat' by Torbjorn Granlund */
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000391static const char *visible(unsigned ch)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000392{
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000393 char *bpout = G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000394
395 if (ch == _POSIX_VDISABLE)
396 return "<undef>";
397
398 if (ch >= 128) {
399 ch -= 128;
400 *bpout++ = 'M';
401 *bpout++ = '-';
402 }
403
404 if (ch < 32) {
405 *bpout++ = '^';
406 *bpout++ = ch + 64;
407 } else if (ch < 127) {
408 *bpout++ = ch;
409 } else {
410 *bpout++ = '^';
411 *bpout++ = '?';
412 }
413
414 *bpout = '\0';
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000415 return G.buf;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000416}
417
418static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
419{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000420 static const unsigned char tcflag_offsets[] ALIGN1 = {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000421 offsetof(struct termios, c_cflag), /* control */
422 offsetof(struct termios, c_iflag), /* input */
423 offsetof(struct termios, c_oflag), /* output */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000424 offsetof(struct termios, c_lflag) /* local */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000425 };
426
427 if (type <= local) {
428 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
429 }
430 return NULL;
431}
432
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000433static void set_speed_or_die(enum speed_setting type, const char *const arg,
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000434 struct termios * const mode)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000435{
436 speed_t baud;
437
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000438 baud = tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000439
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
Eric Andersen98e599c2001-02-14 18:47:33 +0000458/* Print format string MESSAGE and optional args.
459 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000460 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000461static void wrapf(const char *message, ...)
462{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000463 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000464 va_list args;
Eric Andersen98e599c2001-02-14 18:47:33 +0000465 int buflen;
466
467 va_start(args, message);
Bernhard Reutner-Fischer8eb05492007-01-17 19:46:33 +0000468 buflen = vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000469 va_end(args);
Bernhard Reutner-Fischer1a250d92007-01-18 08:41:22 +0000470 /* We seem to be called only with suitable lengths, but check if
471 somebody failed to adhere to this assumption just to be sure. */
472 if (!buflen || buflen >= sizeof(buf)) return;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000473
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000474 if (G.current_col > 0) {
475 G.current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000476 if (buf[0] != '\n') {
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000477 if (G.current_col + buflen >= max_col) {
Denis Vlasenko79deb662006-09-19 15:12:12 +0000478 putchar('\n');
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000479 G.current_col = 0;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000480 } else
481 putchar(' ');
482 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000483 }
484 fputs(buf, stdout);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000485 G.current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000486 if (buf[buflen-1] == '\n')
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000487 G.current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000488}
489
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000490static void set_window_size(const int rows, const int cols)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000491{
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000492 struct winsize win = { 0, 0, 0, 0};
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000493
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000494 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000495 if (errno != EINVAL) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000496 goto bail;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000497 }
498 memset(&win, 0, sizeof(win));
499 }
500
501 if (rows >= 0)
502 win.ws_row = rows;
503 if (cols >= 0)
504 win.ws_col = cols;
505
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000506 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000507bail:
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000508 perror_on_device("%s");
509}
510
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000511static void display_window_size(const int fancy)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000512{
513 const char *fmt_str = "%s\0%s: no size information for this device";
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000514 unsigned width, height;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000515
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000516 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000517 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
518 perror_on_device(fmt_str);
519 }
520 } else {
521 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000522 height, width);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000523 }
524}
525
Eric Andersen98e599c2001-02-14 18:47:33 +0000526static const struct suffix_mult stty_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000527 { "b", 512 },
528 { "k", 1024 },
529 { "B", 1024 },
530 { }
Eric Andersen98e599c2001-02-14 18:47:33 +0000531};
532
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000533static const struct mode_info *find_mode(const char *name)
534{
535 int i;
536 for (i = 0; i < NUM_mode_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000537 if (!strcmp(name, mode_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000538 return &mode_info[i];
539 return 0;
540}
541
542static const struct control_info *find_control(const char *name)
543{
544 int i;
545 for (i = 0; i < NUM_control_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000546 if (!strcmp(name, control_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000547 return &control_info[i];
548 return 0;
549}
550
551enum {
552 param_need_arg = 0x80,
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000553 param_line = 1 | 0x80,
554 param_rows = 2 | 0x80,
555 param_cols = 3 | 0x80,
556 param_columns = 4 | 0x80,
557 param_size = 5,
558 param_speed = 6,
559 param_ispeed = 7 | 0x80,
560 param_ospeed = 8 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000561};
562
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000563static int find_param(const char *const name)
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000564{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000565 static const char params[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000566 "line\0" /* 1 */
567 "rows\0" /* 2 */
568 "cols\0" /* 3 */
569 "columns\0" /* 4 */
570 "size\0" /* 5 */
571 "speed\0" /* 6 */
572 "ispeed\0"
573 "ospeed\0";
574 int i = index_in_strings(params, name) + 1;
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000575 if (i == 0)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000576 return 0;
Denis Vlasenko9ace6132007-04-19 19:55:54 +0000577 if (i != 5 && i != 6)
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000578 i |= 0x80;
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000579 return i;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000580}
581
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000582static int recover_mode(const char *arg, struct termios *mode)
583{
584 int i, n;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000585 unsigned chr;
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000586 unsigned long iflag, oflag, cflag, lflag;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000587
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000588 /* Scan into temporaries since it is too much trouble to figure out
589 the right format for 'tcflag_t' */
590 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
591 &iflag, &oflag, &cflag, &lflag, &n) != 4)
592 return 0;
593 mode->c_iflag = iflag;
594 mode->c_oflag = oflag;
595 mode->c_cflag = cflag;
596 mode->c_lflag = lflag;
597 arg += n;
598 for (i = 0; i < NCCS; ++i) {
599 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
600 return 0;
601 mode->c_cc[i] = chr;
602 arg += n;
603 }
604
605 /* Fail if there are too many fields */
606 if (*arg != '\0')
607 return 0;
608
609 return 1;
610}
611
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000612static void display_recoverable(const struct termios *mode,
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000613 int ATTRIBUTE_UNUSED dummy)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000614{
615 int i;
616 printf("%lx:%lx:%lx:%lx",
617 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
618 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
619 for (i = 0; i < NCCS; ++i)
620 printf(":%x", (unsigned int) mode->c_cc[i]);
621 putchar('\n');
622}
623
624static void display_speed(const struct termios *mode, int fancy)
625{
626 //01234567 8 9
627 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
628 unsigned long ispeed, ospeed;
629
630 ospeed = ispeed = cfgetispeed(mode);
631 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
632 ispeed = ospeed; /* in case ispeed was 0 */
Denis Vlasenko4b924f32007-05-30 00:29:55 +0000633 //0123 4 5 6 7 8 9
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000634 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
635 }
636 if (fancy) fmt_str += 9;
637 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
638}
639
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000640static void do_display(const struct termios *mode, const int all)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000641{
642 int i;
643 tcflag_t *bitsp;
644 unsigned long mask;
645 int prev_type = control;
646
647 display_speed(mode, 1);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000648 if (all)
649 display_window_size(1);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000650#ifdef HAVE_C_LINE
651 wrapf("line = %d;\n", mode->c_line);
652#else
653 wrapf("\n");
654#endif
655
656 for (i = 0; control_info[i].name != stty_min; ++i) {
657 /* If swtch is the same as susp, don't print both */
658#if VSWTCH == VSUSP
659 if (control_info[i].name == stty_swtch)
660 continue;
661#endif
662 /* If eof uses the same slot as min, only print whichever applies */
663#if VEOF == VMIN
664 if ((mode->c_lflag & ICANON) == 0
665 && (control_info[i].name == stty_eof
666 || control_info[i].name == stty_eol)) continue;
667#endif
668 wrapf("%s = %s;", control_info[i].name,
669 visible(mode->c_cc[control_info[i].offset]));
670 }
671#if VEOF == VMIN
672 if ((mode->c_lflag & ICANON) == 0)
673#endif
674 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000675 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000676
677 for (i = 0; i < NUM_mode_info; ++i) {
678 if (mode_info[i].flags & OMIT)
679 continue;
680 if (mode_info[i].type != prev_type) {
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000681 /* wrapf("\n"); */
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000682 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000683 prev_type = mode_info[i].type;
684 }
685
686 bitsp = mode_type_flag(mode_info[i].type, mode);
687 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000688 if ((*bitsp & mask) == mode_info[i].bits) {
689 if (all || (mode_info[i].flags & SANE_UNSET))
690 wrapf("%s", mode_info[i].name);
691 } else {
692 if ((all && mode_info[i].flags & REV) ||
693 (!all &&
694 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
695 wrapf("-%s", mode_info[i].name);
696 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000697 }
Bernhard Reutner-Fischer3a602442007-04-04 13:59:49 +0000698 if (G.current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000699}
700
701static void sane_mode(struct termios *mode)
702{
703 int i;
704 tcflag_t *bitsp;
705
706 for (i = 0; i < NUM_control_info; ++i) {
707#if VMIN == VEOF
708 if (control_info[i].name == stty_min)
709 break;
710#endif
711 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
712 }
713
714 for (i = 0; i < NUM_mode_info; ++i) {
715 if (mode_info[i].flags & SANE_SET) {
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 } else if (mode_info[i].flags & SANE_UNSET) {
720 bitsp = mode_type_flag(mode_info[i].type, mode);
721 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
722 & ~mode_info[i].bits;
723 }
724 }
725}
726
727/* Save set_mode from #ifdef forest plague */
728#ifndef ONLCR
729#define ONLCR 0
730#endif
731#ifndef OCRNL
732#define OCRNL 0
733#endif
734#ifndef ONLRET
735#define ONLRET 0
736#endif
737#ifndef XCASE
738#define XCASE 0
739#endif
740#ifndef IXANY
741#define IXANY 0
742#endif
743#ifndef TABDLY
744#define TABDLY 0
745#endif
746#ifndef OXTABS
747#define OXTABS 0
748#endif
749#ifndef IUCLC
750#define IUCLC 0
751#endif
752#ifndef OLCUC
753#define OLCUC 0
754#endif
755#ifndef ECHOCTL
756#define ECHOCTL 0
757#endif
758#ifndef ECHOKE
759#define ECHOKE 0
760#endif
761
762static void set_mode(const struct mode_info *info, int reversed,
763 struct termios *mode)
764{
765 tcflag_t *bitsp;
766
767 bitsp = mode_type_flag(info->type, mode);
768
769 if (bitsp) {
770 if (reversed)
771 *bitsp = *bitsp & ~info->mask & ~info->bits;
772 else
773 *bitsp = (*bitsp & ~info->mask) | info->bits;
774 return;
775 }
776
777 /* Combination mode */
778 if (info->name == evenp || info->name == parity) {
779 if (reversed)
780 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
781 else
782 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
783 } else if (info->name == stty_oddp) {
784 if (reversed)
785 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
786 else
787 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
788 } else if (info->name == stty_nl) {
789 if (reversed) {
790 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
791 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
792 } else {
793 mode->c_iflag = mode->c_iflag & ~ICRNL;
794 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
795 }
796 } else if (info->name == stty_ek) {
797 mode->c_cc[VERASE] = CERASE;
798 mode->c_cc[VKILL] = CKILL;
799 } else if (info->name == stty_sane) {
800 sane_mode(mode);
801 }
802 else if (info->name == cbreak) {
803 if (reversed)
804 mode->c_lflag |= ICANON;
805 else
806 mode->c_lflag &= ~ICANON;
807 } else if (info->name == stty_pass8) {
808 if (reversed) {
809 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
810 mode->c_iflag |= ISTRIP;
811 } else {
812 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
813 mode->c_iflag &= ~ISTRIP;
814 }
815 } else if (info->name == litout) {
816 if (reversed) {
817 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
818 mode->c_iflag |= ISTRIP;
819 mode->c_oflag |= OPOST;
820 } else {
821 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
822 mode->c_iflag &= ~ISTRIP;
823 mode->c_oflag &= ~OPOST;
824 }
825 } else if (info->name == raw || info->name == cooked) {
826 if ((info->name[0] == 'r' && reversed)
827 || (info->name[0] == 'c' && !reversed)) {
828 /* Cooked mode */
829 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
830 mode->c_oflag |= OPOST;
831 mode->c_lflag |= ISIG | ICANON;
832#if VMIN == VEOF
833 mode->c_cc[VEOF] = CEOF;
834#endif
835#if VTIME == VEOL
836 mode->c_cc[VEOL] = CEOL;
837#endif
838 } else {
839 /* Raw mode */
840 mode->c_iflag = 0;
841 mode->c_oflag &= ~OPOST;
842 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
843 mode->c_cc[VMIN] = 1;
844 mode->c_cc[VTIME] = 0;
845 }
846 }
847 else if (IXANY && info->name == decctlq) {
848 if (reversed)
849 mode->c_iflag |= IXANY;
850 else
851 mode->c_iflag &= ~IXANY;
852 }
853 else if (TABDLY && info->name == stty_tabs) {
854 if (reversed)
855 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
856 else
857 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
858 }
859 else if (OXTABS && info->name == stty_tabs) {
860 if (reversed)
861 mode->c_oflag |= OXTABS;
862 else
863 mode->c_oflag &= ~OXTABS;
864 }
865 else if (XCASE && IUCLC && OLCUC
866 && (info->name == stty_lcase || info->name == stty_LCASE)) {
867 if (reversed) {
868 mode->c_lflag &= ~XCASE;
869 mode->c_iflag &= ~IUCLC;
870 mode->c_oflag &= ~OLCUC;
871 } else {
872 mode->c_lflag |= XCASE;
873 mode->c_iflag |= IUCLC;
874 mode->c_oflag |= OLCUC;
875 }
876 }
877 else if (info->name == stty_crt) {
878 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
879 }
880 else if (info->name == stty_dec) {
881 mode->c_cc[VINTR] = 3; /* ^C */
882 mode->c_cc[VERASE] = 127; /* DEL */
883 mode->c_cc[VKILL] = 21; /* ^U */
884 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
885 if (IXANY) mode->c_iflag &= ~IXANY;
886 }
887}
888
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000889static void set_control_char_or_die(const struct control_info *info,
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000890 const char *arg, struct termios *mode)
891{
892 unsigned char value;
893
894 if (info->name == stty_min || info->name == stty_time)
895 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
896 else if (arg[0] == '\0' || arg[1] == '\0')
897 value = arg[0];
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000898 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000899 value = _POSIX_VDISABLE;
900 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
901 value = arg[1] & 0x1f; /* Non-letters get weird results */
902 if (arg[1] == '?')
903 value = 127;
904 } else
905 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
906 mode->c_cc[info->offset] = value;
907}
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000908
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000909#define STTY_require_set_attr (1<<0)
910#define STTY_speed_was_set (1<<1)
911#define STTY_verbose_output (1<<2)
912#define STTY_recoverable_output (1<<3)
913#define STTY_noargs (1<<4)
Denis Vlasenko06af2162007-02-03 17:28:39 +0000914int stty_main(int argc, char **argv);
Rob Landleydfba7412006-03-06 20:47:33 +0000915int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +0000916{
917 struct termios mode;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000918 void (*output_func)(const struct termios *, const int);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000919 const char *file_name = NULL;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000920 int display_all = 0;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000921 int stty_state;
922 int k;
923
924 stty_state = STTY_noargs;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000925 output_func = do_display;
Eric Andersen98e599c2001-02-14 18:47:33 +0000926
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000927 /* First pass: only parse/verify command line params */
928 k = 0;
929 while (argv[++k]) {
930 const struct mode_info *mp;
931 const struct control_info *cp;
932 const char *arg = argv[k];
933 const char *argnext = argv[k+1];
934 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000935
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000936 if (arg[0] == '-') {
937 int i;
938 mp = find_mode(arg+1);
939 if (mp) {
940 if (!(mp->flags & REV))
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +0000941 goto invalid_argument;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000942 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000943 continue;
944 }
945 /* It is an option - parse it */
946 i = 0;
947 while (arg[++i]) {
948 switch (arg[i]) {
949 case 'a':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000950 stty_state |= STTY_verbose_output;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000951 output_func = do_display;
952 display_all = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000953 break;
954 case 'g':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000955 stty_state |= STTY_recoverable_output;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000956 output_func = display_recoverable;
957 break;
958 case 'F':
959 if (file_name)
960 bb_error_msg_and_die("only one device may be specified");
961 file_name = &arg[i+1]; /* "-Fdevice" ? */
962 if (!file_name[0]) { /* nope, "-F device" */
963 int p = k+1; /* argv[p] is argnext */
964 file_name = argnext;
965 if (!file_name)
966 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
967 /* remove -F param from arg[vc] */
968 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000969 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000970 }
971 goto end_option;
972 default:
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +0000973 goto invalid_argument;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000974 }
975 }
Denis Vlasenko240a1cf2007-04-08 16:07:02 +0000976 end_option:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000977 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +0000978 }
979
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000980 mp = find_mode(arg);
981 if (mp) {
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000982 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000983 continue;
984 }
985
986 cp = find_control(arg);
987 if (cp) {
988 if (!argnext)
989 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000990 /* called for the side effect of xfunc death only */
991 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000992 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000993 ++k;
994 continue;
995 }
996
997 param = find_param(arg);
998 if (param & param_need_arg) {
999 if (!argnext)
1000 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1001 ++k;
1002 }
1003
1004 switch (param) {
1005#ifdef HAVE_C_LINE
1006 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001007# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +00001008 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001009 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001010# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001011#endif
1012#ifdef TIOCGWINSZ
1013 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001014 case param_cols:
Denis Vlasenko9ace6132007-04-19 19:55:54 +00001015 case param_columns:
Denis Vlasenko13858992006-10-08 12:49:22 +00001016 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001017 break;
1018 case param_size:
1019#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001020 case param_speed:
1021 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001022 case param_ispeed:
1023 /* called for the side effect of xfunc death only */
1024 set_speed_or_die(input_speed, argnext, &mode);
1025 break;
1026 case param_ospeed:
1027 /* called for the side effect of xfunc death only */
1028 set_speed_or_die(output_speed, argnext, &mode);
1029 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001030 default:
1031 if (recover_mode(arg, &mode) == 1) break;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001032 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00001033 invalid_argument:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001034 bb_error_msg_and_die("invalid argument '%s'", arg);
1035 }
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001036 stty_state &= ~STTY_noargs;
Eric Andersen98e599c2001-02-14 18:47:33 +00001037 }
1038
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001039 /* Specifying both -a and -g is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001040 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001041 (STTY_verbose_output | STTY_recoverable_output))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001042 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1043 /* Specifying -a or -g with non-options is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001044 if (!(stty_state & STTY_noargs) &&
1045 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001046 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +00001047
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001048 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +00001049 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001050 int fd, fdflags;
Eric Andersen98e599c2001-02-14 18:47:33 +00001051 device_name = file_name;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001052 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +00001053 if (fd != STDIN_FILENO) {
1054 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001055 close(fd);
1056 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +00001057 fdflags = fcntl(STDIN_FILENO, F_GETFL);
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001058 if (fdflags < 0 ||
1059 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +00001060 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +00001061 }
1062
1063 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001064 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001065 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001066 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001067 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001068
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001069 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +00001070 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001071 output_func(&mode, display_all);
Eric Andersen98e599c2001-02-14 18:47:33 +00001072 return EXIT_SUCCESS;
1073 }
1074
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001075 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +00001076 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001077 while (argv[++k]) {
1078 const struct mode_info *mp;
1079 const struct control_info *cp;
1080 const char *arg = argv[k];
1081 const char *argnext = argv[k+1];
1082 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001083
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001084 if (arg[0] == '-') {
1085 mp = find_mode(arg+1);
1086 if (mp) {
1087 set_mode(mp, 1 /* reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001088 stty_state |= STTY_require_set_attr;
Eric Andersenfc059092002-06-06 11:35:29 +00001089 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001090 /* It is an option - already parsed. Skip it */
1091 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001092 }
Mark Whitley446dd272001-03-02 20:00:54 +00001093
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001094 mp = find_mode(arg);
1095 if (mp) {
1096 set_mode(mp, 0 /* non-reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001097 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001098 continue;
1099 }
Mark Whitley446dd272001-03-02 20:00:54 +00001100
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001101 cp = find_control(arg);
1102 if (cp) {
1103 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001104 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001105 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001106 continue;
1107 }
Mark Whitley446dd272001-03-02 20:00:54 +00001108
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001109 param = find_param(arg);
1110 if (param & param_need_arg) {
1111 ++k;
1112 }
1113
1114 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001115#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001116 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +00001117 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001118 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001119 break;
Eric Andersen98e599c2001-02-14 18:47:33 +00001120#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001121#ifdef TIOCGWINSZ
1122 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001123 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001124 break;
1125 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001126 display_window_size(0);
1127 break;
1128 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +00001129 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001130 break;
1131#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001132 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001133 display_speed(&mode, 0);
1134 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001135 case param_ispeed:
1136 set_speed_or_die(input_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001137 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001138 break;
1139 case param_ospeed:
1140 set_speed_or_die(output_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001141 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001142 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001143 default:
1144 if (recover_mode(arg, &mode) == 1)
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001145 stty_state |= STTY_require_set_attr;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001146 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001147 set_speed_or_die(both_speeds, arg, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001148 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001149 } /* else - impossible (caught in the first pass):
1150 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +00001151 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001152 }
1153
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001154 if (stty_state & STTY_require_set_attr) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001155 struct termios new_mode;
1156
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001157 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001158 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001159
1160 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1161 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +00001162 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +00001163 some proper subset of the requested operations. To detect
1164 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001165 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +00001166
1167 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001168 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001169 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001170 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001171 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001172
Eric Andersen98e599c2001-02-14 18:47:33 +00001173 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1174#ifdef CIBAUD
1175 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1176 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1177 sometimes (m1 != m2). The only difference is in the four bits
1178 of the c_cflag field corresponding to the baud rate. To save
1179 Sun users a little confusion, don't report an error if this
1180 happens. But suppress the error only if we haven't tried to
1181 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001182 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +00001183
1184 new_mode.c_cflag &= (~CIBAUD);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001185 if ((stty_state & STTY_speed_was_set)
1186 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
Eric Andersen98e599c2001-02-14 18:47:33 +00001187#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +00001188 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +00001189 }
1190 }
1191
1192 return EXIT_SUCCESS;
1193}