blob: 22784a2601200db76cee6545b61380b67a8b030e [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 Vlasenko9efb0702006-09-19 14:17:10 +0000161 const char *name; /* Name given on command line */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000162 char type; /* Which structure element to change */
163 char flags; /* Setting and display options */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000164 unsigned short mask; /* Other bits to turn off for this mode */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000165 unsigned long bits; /* Bits to set for this mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000166};
167
Manuel Novoa III cad53642003-03-19 09:13:01 +0000168#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
169
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000170static const struct mode_info mode_info[] = {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000171 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
172 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
173 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
174 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
175 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
176 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
177 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
178 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
179 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
180 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
181 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000182#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000183 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000184#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000185 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
186 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
187 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
188 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
189 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
190 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
191 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
192 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
193 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
194 MI_ENTRY("ixon", input, REV, IXON, 0 ),
195 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
196 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000197#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000198 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000199#endif
200#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000201 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000202#endif
203#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000204 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000205#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000206 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000207#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000208 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000209#endif
210#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000211 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000212#endif
213#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000214 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000215#endif
216#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000217 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000218#endif
219#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000220 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000221#endif
222#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000223 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000224#endif
225#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000226 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000227#endif
228#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000229 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
230 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000231#endif
232#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000233 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
234 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
235 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
236 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000237#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000238
Eric Andersen98e599c2001-02-14 18:47:33 +0000239#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000240 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
241 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
242 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
243 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000244#else
245# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000246 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000247# endif
248#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000249
Eric Andersen98e599c2001-02-14 18:47:33 +0000250#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000251 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
252 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000253#endif
254#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000255 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
256 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000257#endif
258#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000259 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
260 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000261#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000262 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
263 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000264#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000265 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000266#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000267 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
268 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
269 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
270 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
271 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
272 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000273#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000274 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000275#endif
276#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000277 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000278#endif
279#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000280 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
281 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000282#endif
283#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000284 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
285 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000286#endif
287#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000288 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
289 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000290#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000291 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
292 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
293 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
294 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
295 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
296 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
297 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
298 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
299 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
300 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
301 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000302#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000303 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000304#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000305#if defined(TABDLY) || defined(OXTABS)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000306 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000307#endif
308#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000309 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
310 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000311#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000312 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
313 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000314};
315
Rob Landleybc68cd12006-03-10 19:22:06 +0000316enum {
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000317 NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
Rob Landleybc68cd12006-03-10 19:22:06 +0000318};
Eric Andersen98e599c2001-02-14 18:47:33 +0000319
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000320/* Control character settings */
Eric Andersen98e599c2001-02-14 18:47:33 +0000321struct control_info {
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000322 const char *name; /* Name given on command line */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000323 unsigned char saneval; /* Value to set for 'stty sane' */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000324 unsigned char offset; /* Offset in c_cc */
Eric Andersen98e599c2001-02-14 18:47:33 +0000325};
326
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000327/* Control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +0000328
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000329static const struct control_info control_info[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000330 {"intr", CINTR, VINTR},
331 {"quit", CQUIT, VQUIT},
332 {"erase", CERASE, VERASE},
333 {"kill", CKILL, VKILL},
334 {stty_eof, CEOF, VEOF},
335 {stty_eol, CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000336#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000337 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000338#endif
339#ifdef VSWTCH
Mark Whitley446dd272001-03-02 20:00:54 +0000340 {stty_swtch, CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000341#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000342 {"start", CSTART, VSTART},
343 {"stop", CSTOP, VSTOP},
344 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000345#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000346 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000347#endif
348#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000349 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000350#endif
351#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000352 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000353#endif
354#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000355 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000356#endif
357#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000358 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000359#endif
360#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000361 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000362#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000363 /* These must be last because of the display routines */
Mark Whitley446dd272001-03-02 20:00:54 +0000364 {stty_min, 1, VMIN},
365 {stty_time, 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000366};
367
Rob Landleybc68cd12006-03-10 19:22:06 +0000368enum {
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000369 NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
Rob Landleybc68cd12006-03-10 19:22:06 +0000370};
Eric Andersen98e599c2001-02-14 18:47:33 +0000371
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000372/* The width of the screen, for output wrapping */
Denis Vlasenko13858992006-10-08 12:49:22 +0000373static unsigned max_col = 80; /* default */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000374/* Current position, to know when to wrap */
Denis Vlasenko13858992006-10-08 12:49:22 +0000375static unsigned current_col;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000376static const char *device_name = bb_msg_standard_input;
Eric Andersen8876fb22003-06-20 09:01:58 +0000377
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000378/* Return a string that is the printable representation of character CH */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000379/* Adapted from 'cat' by Torbjorn Granlund */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000380static const char *visible(unsigned int ch)
381{
382 static char buf[10];
383 char *bpout = buf;
384
385 if (ch == _POSIX_VDISABLE)
386 return "<undef>";
387
388 if (ch >= 128) {
389 ch -= 128;
390 *bpout++ = 'M';
391 *bpout++ = '-';
392 }
393
394 if (ch < 32) {
395 *bpout++ = '^';
396 *bpout++ = ch + 64;
397 } else if (ch < 127) {
398 *bpout++ = ch;
399 } else {
400 *bpout++ = '^';
401 *bpout++ = '?';
402 }
403
404 *bpout = '\0';
405 return buf;
406}
407
408static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
409{
410 static const unsigned char tcflag_offsets[] = {
411 offsetof(struct termios, c_cflag), /* control */
412 offsetof(struct termios, c_iflag), /* input */
413 offsetof(struct termios, c_oflag), /* output */
414 offsetof(struct termios, c_lflag), /* local */
415 };
416
417 if (type <= local) {
418 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
419 }
420 return NULL;
421}
422
423static speed_t string_to_baud_or_die(const char *arg)
424{
Denis Vlasenko13858992006-10-08 12:49:22 +0000425 return tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000426}
427
428static void set_speed_or_die(enum speed_setting type, const char *arg,
429 struct termios *mode)
430{
431 speed_t baud;
432
433 baud = string_to_baud_or_die(arg);
434
435 if (type != output_speed) { /* either input or both */
436 cfsetispeed(mode, baud);
437 }
438 if (type != input_speed) { /* either output or both */
439 cfsetospeed(mode, baud);
440 }
441}
442
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000443static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000444{
445 bb_perror_msg_and_die(fmt, device_name);
446}
447
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000448static void perror_on_device(const char *fmt)
449{
450 bb_perror_msg(fmt, device_name);
451}
452
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000453/* No, inline won't be as efficient (gcc 3.4.3) */
454#define streq(a,b) (!strcmp((a),(b)))
Eric Andersen98e599c2001-02-14 18:47:33 +0000455
456/* Print format string MESSAGE and optional args.
457 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000458 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000459static void wrapf(const char *message, ...)
460{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000461 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000462 va_list args;
Eric Andersen98e599c2001-02-14 18:47:33 +0000463 int buflen;
464
465 va_start(args, message);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000466 vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000467 va_end(args);
468 buflen = strlen(buf);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000469 if (!buflen) return;
470
Eric Andersen98e599c2001-02-14 18:47:33 +0000471 if (current_col > 0) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000472 current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000473 if (buf[0] != '\n') {
474 if (current_col + buflen >= max_col) {
475 putchar('\n');
476 current_col = 0;
477 } else
478 putchar(' ');
479 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000480 }
481 fputs(buf, stdout);
482 current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000483 if (buf[buflen-1] == '\n')
484 current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000485}
486
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000487#ifdef TIOCGWINSZ
488
Denis Vlasenko79deb662006-09-19 15:12:12 +0000489static int get_win_size(int fd, struct winsize *win)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000490{
Denis Vlasenko79deb662006-09-19 15:12:12 +0000491 return ioctl(fd, TIOCGWINSZ, (char *) win);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000492}
493
494static void set_window_size(int rows, int cols)
495{
496 struct winsize win;
497
Denis Vlasenko79deb662006-09-19 15:12:12 +0000498 if (get_win_size(STDIN_FILENO, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000499 if (errno != EINVAL) {
500 perror_on_device("%s");
501 return;
502 }
503 memset(&win, 0, sizeof(win));
504 }
505
506 if (rows >= 0)
507 win.ws_row = rows;
508 if (cols >= 0)
509 win.ws_col = cols;
510
511# ifdef TIOCSSIZE
512 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
513 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
514 This comment from sys/ttold.h describes Sun's twisted logic - a better
515 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
516 At any rate, the problem is gone in Solaris 2.x */
517
518 if (win.ws_row == 0 || win.ws_col == 0) {
519 struct ttysize ttysz;
520
521 ttysz.ts_lines = win.ws_row;
522 ttysz.ts_cols = win.ws_col;
523
524 win.ws_row = win.ws_col = 1;
525
526 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
527 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
528 perror_on_device("%s");
529 }
530 return;
531 }
532# endif
533
534 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
535 perror_on_device("%s");
536}
537
538static void display_window_size(int fancy)
539{
540 const char *fmt_str = "%s\0%s: no size information for this device";
541 struct winsize win;
542
Denis Vlasenko79deb662006-09-19 15:12:12 +0000543 if (get_win_size(STDIN_FILENO, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000544 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
545 perror_on_device(fmt_str);
546 }
547 } else {
548 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
549 win.ws_row, win.ws_col);
550 }
551}
552
553#else /* !TIOCGWINSZ */
554
555static inline void display_window_size(int fancy) {}
556
557#endif /* !TIOCGWINSZ */
558
Denis Vlasenko13858992006-10-08 12:49:22 +0000559static int screen_columns_or_die(void)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000560{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000561 const char *s;
562
563#ifdef TIOCGWINSZ
564 struct winsize win;
565
566 /* With Solaris 2.[123], this ioctl fails and errno is set to
567 EINVAL for telnet (but not rlogin) sessions.
568 On ISC 3.0, it fails for the console and the serial port
569 (but it works for ptys).
570 It can also fail on any system when stdout isn't a tty.
571 In case of any failure, just use the default */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000572 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000573 return win.ws_col;
574#endif
575
Denis Vlasenko13858992006-10-08 12:49:22 +0000576 s = getenv("COLUMNS");
577 if (s)
578 return xatoi_u(s);
579 return 80;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000580}
581
Eric Andersen98e599c2001-02-14 18:47:33 +0000582static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000583 {"b", 512 },
584 {"k", 1024},
585 {"B", 1024},
586 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000587};
588
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000589static const struct mode_info *find_mode(const char *name)
590{
591 int i;
592 for (i = 0; i < NUM_mode_info; ++i)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000593 if (streq(name, mode_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000594 return &mode_info[i];
595 return 0;
596}
597
598static const struct control_info *find_control(const char *name)
599{
600 int i;
601 for (i = 0; i < NUM_control_info; ++i)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000602 if (streq(name, control_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000603 return &control_info[i];
604 return 0;
605}
606
607enum {
608 param_need_arg = 0x80,
609 param_line = 1 | 0x80,
610 param_rows = 2 | 0x80,
611 param_cols = 3 | 0x80,
612 param_size = 4,
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000613 param_speed = 5,
614 param_ispeed = 6 | 0x80,
615 param_ospeed = 7 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000616};
617
618static int find_param(const char *name)
619{
620#ifdef HAVE_C_LINE
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000621 if (streq(name, "line")) return param_line;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000622#endif
623#ifdef TIOCGWINSZ
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000624 if (streq(name, "rows")) return param_rows;
625 if (streq(name, "cols")) return param_cols;
626 if (streq(name, "columns")) return param_cols;
627 if (streq(name, "size")) return param_size;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000628#endif
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000629 if (streq(name, "speed")) return param_speed;
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000630 if (streq(name, "ispeed")) return param_ispeed;
631 if (streq(name, "ospeed")) return param_ospeed;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000632 return 0;
633}
634
635
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000636static int recover_mode(const char *arg, struct termios *mode);
637static void set_mode(const struct mode_info *info,
638 int reversed, struct termios *mode);
639static void display_all(const struct termios *mode);
640static void display_changed(const struct termios *mode);
641static void display_recoverable(const struct termios *mode);
642static void display_speed(const struct termios *mode, int fancy);
643static void sane_mode(struct termios *mode);
644static void set_control_char_or_die(const struct control_info *info,
645 const char *arg, struct termios *mode);
646
Rob Landleydfba7412006-03-06 20:47:33 +0000647int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +0000648{
649 struct termios mode;
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000650 void (*output_func)(const struct termios *);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000651 const char *file_name = NULL;
652 int require_set_attr;
653 int speed_was_set;
654 int verbose_output;
655 int recoverable_output;
656 int noargs;
657 int k;
Eric Andersen98e599c2001-02-14 18:47:33 +0000658
Manuel Novoa III cad53642003-03-19 09:13:01 +0000659 output_func = display_changed;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000660 noargs = 1;
661 speed_was_set = 0;
662 require_set_attr = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000663 verbose_output = 0;
664 recoverable_output = 0;
665
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000666 /* First pass: only parse/verify command line params */
667 k = 0;
668 while (argv[++k]) {
669 const struct mode_info *mp;
670 const struct control_info *cp;
671 const char *arg = argv[k];
672 const char *argnext = argv[k+1];
673 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000674
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000675 if (arg[0] == '-') {
676 int i;
677 mp = find_mode(arg+1);
678 if (mp) {
679 if (!(mp->flags & REV))
680 bb_error_msg_and_die("invalid argument '%s'", arg);
681 noargs = 0;
682 continue;
683 }
684 /* It is an option - parse it */
685 i = 0;
686 while (arg[++i]) {
687 switch (arg[i]) {
688 case 'a':
689 verbose_output = 1;
690 output_func = display_all;
691 break;
692 case 'g':
693 recoverable_output = 1;
694 output_func = display_recoverable;
695 break;
696 case 'F':
697 if (file_name)
698 bb_error_msg_and_die("only one device may be specified");
699 file_name = &arg[i+1]; /* "-Fdevice" ? */
700 if (!file_name[0]) { /* nope, "-F device" */
701 int p = k+1; /* argv[p] is argnext */
702 file_name = argnext;
703 if (!file_name)
704 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
705 /* remove -F param from arg[vc] */
706 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000707 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000708 }
709 goto end_option;
710 default:
711 bb_error_msg_and_die("invalid argument '%s'", arg);
712 }
713 }
714end_option:
715 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +0000716 }
717
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000718 mp = find_mode(arg);
719 if (mp) {
720 noargs = 0;
721 continue;
722 }
723
724 cp = find_control(arg);
725 if (cp) {
726 if (!argnext)
727 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000728 /* called for the side effect of xfunc death only */
729 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000730 noargs = 0;
731 ++k;
732 continue;
733 }
734
735 param = find_param(arg);
736 if (param & param_need_arg) {
737 if (!argnext)
738 bb_error_msg_and_die(bb_msg_requires_arg, arg);
739 ++k;
740 }
741
742 switch (param) {
743#ifdef HAVE_C_LINE
744 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000745# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +0000746 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000747 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000748# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000749#endif
750#ifdef TIOCGWINSZ
751 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000752 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +0000753 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000754 break;
755 case param_size:
756#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000757 case param_speed:
758 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000759 case param_ispeed:
760 /* called for the side effect of xfunc death only */
761 set_speed_or_die(input_speed, argnext, &mode);
762 break;
763 case param_ospeed:
764 /* called for the side effect of xfunc death only */
765 set_speed_or_die(output_speed, argnext, &mode);
766 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000767 default:
768 if (recover_mode(arg, &mode) == 1) break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000769 if (string_to_baud_or_die(arg) != (speed_t) -1) break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000770 bb_error_msg_and_die("invalid argument '%s'", arg);
771 }
772 noargs = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000773 }
774
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000775 /* Specifying both -a and -g is an error */
776 if (verbose_output && recoverable_output)
777 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
778 /* Specifying -a or -g with non-options is an error */
779 if (!noargs && (verbose_output || recoverable_output))
780 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000781
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000782 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +0000783 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000784 int fd, fdflags;
Eric Andersen98e599c2001-02-14 18:47:33 +0000785 device_name = file_name;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000786 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +0000787 if (fd != STDIN_FILENO) {
788 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000789 close(fd);
790 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +0000791 fdflags = fcntl(STDIN_FILENO, F_GETFL);
792 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +0000793 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +0000794 }
795
796 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000797 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +0000798 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000799 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000800 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000801
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000802 if (verbose_output || recoverable_output || noargs) {
Denis Vlasenko13858992006-10-08 12:49:22 +0000803 max_col = screen_columns_or_die();
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000804 output_func(&mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000805 return EXIT_SUCCESS;
806 }
807
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000808 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +0000809 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000810 while (argv[++k]) {
811 const struct mode_info *mp;
812 const struct control_info *cp;
813 const char *arg = argv[k];
814 const char *argnext = argv[k+1];
815 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000816
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000817 if (arg[0] == '-') {
818 mp = find_mode(arg+1);
819 if (mp) {
820 set_mode(mp, 1 /* reversed */, &mode);
Eric Andersenfc059092002-06-06 11:35:29 +0000821 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000822 /* It is an option - already parsed. Skip it */
823 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +0000824 }
Mark Whitley446dd272001-03-02 20:00:54 +0000825
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000826 mp = find_mode(arg);
827 if (mp) {
828 set_mode(mp, 0 /* non-reversed */, &mode);
829 continue;
830 }
Mark Whitley446dd272001-03-02 20:00:54 +0000831
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000832 cp = find_control(arg);
833 if (cp) {
834 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000835 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000836 continue;
837 }
Mark Whitley446dd272001-03-02 20:00:54 +0000838
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000839 param = find_param(arg);
840 if (param & param_need_arg) {
841 ++k;
842 }
843
844 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000845#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000846 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +0000847 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000848 require_set_attr = 1;
849 break;
Eric Andersen98e599c2001-02-14 18:47:33 +0000850#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000851#ifdef TIOCGWINSZ
852 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +0000853 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000854 break;
855 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000856 display_window_size(0);
857 break;
858 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +0000859 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000860 break;
861#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000862 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000863 display_speed(&mode, 0);
864 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000865 case param_ispeed:
866 set_speed_or_die(input_speed, argnext, &mode);
867 speed_was_set = 1;
868 require_set_attr = 1;
869 break;
870 case param_ospeed:
871 set_speed_or_die(output_speed, argnext, &mode);
872 speed_was_set = 1;
873 require_set_attr = 1;
874 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000875 default:
876 if (recover_mode(arg, &mode) == 1)
Mark Whitley446dd272001-03-02 20:00:54 +0000877 require_set_attr = 1;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000878 else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
879 set_speed_or_die(both_speeds, arg, &mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000880 speed_was_set = 1;
881 require_set_attr = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000882 } /* else - impossible (caught in the first pass):
883 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +0000884 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000885 }
886
887 if (require_set_attr) {
888 struct termios new_mode;
889
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000890 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000891 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000892
893 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
894 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +0000895 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +0000896 some proper subset of the requested operations. To detect
897 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000898 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +0000899
900 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000901 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +0000902 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000903 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000904 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000905
Eric Andersen98e599c2001-02-14 18:47:33 +0000906 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
907#ifdef CIBAUD
908 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
909 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
910 sometimes (m1 != m2). The only difference is in the four bits
911 of the c_cflag field corresponding to the baud rate. To save
912 Sun users a little confusion, don't report an error if this
913 happens. But suppress the error only if we haven't tried to
914 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000915 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +0000916
917 new_mode.c_cflag &= (~CIBAUD);
918 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
919#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +0000920 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +0000921 }
922 }
923
924 return EXIT_SUCCESS;
925}
926
Denis Vlasenko8971cda2006-09-19 14:20:22 +0000927/* Save set_mode from #ifdef forest plague */
928#ifndef ONLCR
929#define ONLCR 0
930#endif
931#ifndef OCRNL
932#define OCRNL 0
933#endif
934#ifndef ONLRET
935#define ONLRET 0
936#endif
937#ifndef XCASE
938#define XCASE 0
939#endif
940#ifndef IXANY
941#define IXANY 0
942#endif
943#ifndef TABDLY
944#define TABDLY 0
945#endif
946#ifndef OXTABS
947#define OXTABS 0
948#endif
949#ifndef IUCLC
950#define IUCLC 0
951#endif
952#ifndef OLCUC
953#define OLCUC 0
954#endif
955#ifndef ECHOCTL
956#define ECHOCTL 0
957#endif
958#ifndef ECHOKE
959#define ECHOKE 0
960#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000961
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000962static void set_mode(const struct mode_info *info, int reversed,
963 struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +0000964{
965 tcflag_t *bitsp;
966
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000967 bitsp = mode_type_flag(info->type, mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000968
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000969 if (bitsp) {
970 if (reversed)
971 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
972 else
973 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
974 return;
975 }
976
977 /* Combination mode */
978 if (info->name == evenp || info->name == parity) {
979 if (reversed)
980 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
981 else
982 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
983 } else if (info->name == stty_oddp) {
984 if (reversed)
985 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
986 else
987 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
988 } else if (info->name == stty_nl) {
989 if (reversed) {
990 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
991 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
992 } else {
993 mode->c_iflag = mode->c_iflag & ~ICRNL;
994 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
995 }
996 } else if (info->name == stty_ek) {
997 mode->c_cc[VERASE] = CERASE;
998 mode->c_cc[VKILL] = CKILL;
999 } else if (info->name == stty_sane) {
1000 sane_mode(mode);
1001 }
1002 else if (info->name == cbreak) {
1003 if (reversed)
1004 mode->c_lflag |= ICANON;
1005 else
1006 mode->c_lflag &= ~ICANON;
1007 } else if (info->name == stty_pass8) {
1008 if (reversed) {
1009 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1010 mode->c_iflag |= ISTRIP;
1011 } else {
1012 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1013 mode->c_iflag &= ~ISTRIP;
1014 }
1015 } else if (info->name == litout) {
1016 if (reversed) {
1017 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1018 mode->c_iflag |= ISTRIP;
1019 mode->c_oflag |= OPOST;
1020 } else {
1021 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1022 mode->c_iflag &= ~ISTRIP;
1023 mode->c_oflag &= ~OPOST;
1024 }
1025 } else if (info->name == raw || info->name == cooked) {
1026 if ((info->name[0] == 'r' && reversed)
1027 || (info->name[0] == 'c' && !reversed)) {
1028 /* Cooked mode */
1029 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1030 mode->c_oflag |= OPOST;
1031 mode->c_lflag |= ISIG | ICANON;
Eric Andersen98e599c2001-02-14 18:47:33 +00001032#if VMIN == VEOF
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001033 mode->c_cc[VEOF] = CEOF;
Eric Andersen98e599c2001-02-14 18:47:33 +00001034#endif
1035#if VTIME == VEOL
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001036 mode->c_cc[VEOL] = CEOL;
Eric Andersen98e599c2001-02-14 18:47:33 +00001037#endif
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001038 } else {
1039 /* Raw mode */
1040 mode->c_iflag = 0;
1041 mode->c_oflag &= ~OPOST;
1042 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1043 mode->c_cc[VMIN] = 1;
1044 mode->c_cc[VTIME] = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +00001045 }
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001046 }
1047 else if (IXANY && info->name == decctlq) {
1048 if (reversed)
1049 mode->c_iflag |= IXANY;
1050 else
1051 mode->c_iflag &= ~IXANY;
1052 }
1053 else if (TABDLY && info->name == stty_tabs) {
1054 if (reversed)
1055 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1056 else
1057 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1058 }
1059 else if (OXTABS && info->name == stty_tabs) {
1060 if (reversed)
1061 mode->c_oflag |= OXTABS;
1062 else
1063 mode->c_oflag &= ~OXTABS;
1064 }
1065 else if (XCASE && IUCLC && OLCUC
1066 && (info->name == stty_lcase || info->name == stty_LCASE)) {
1067 if (reversed) {
1068 mode->c_lflag &= ~XCASE;
1069 mode->c_iflag &= ~IUCLC;
1070 mode->c_oflag &= ~OLCUC;
1071 } else {
1072 mode->c_lflag |= XCASE;
1073 mode->c_iflag |= IUCLC;
1074 mode->c_oflag |= OLCUC;
Eric Andersen98e599c2001-02-14 18:47:33 +00001075 }
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001076 }
1077 else if (info->name == stty_crt) {
1078 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1079 }
1080 else if (info->name == stty_dec) {
1081 mode->c_cc[VINTR] = 3; /* ^C */
1082 mode->c_cc[VERASE] = 127; /* DEL */
1083 mode->c_cc[VKILL] = 21; /* ^U */
1084 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1085 if (IXANY) mode->c_iflag &= ~IXANY;
1086 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001087}
1088
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001089static void set_control_char_or_die(const struct control_info *info,
1090 const char *arg, struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001091{
1092 unsigned char value;
1093
1094 if (info->name == stty_min || info->name == stty_time)
Denis Vlasenko13858992006-10-08 12:49:22 +00001095 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001096 else if (arg[0] == '\0' || arg[1] == '\0')
1097 value = arg[0];
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001098 else if (streq(arg, "^-") || streq(arg, "undef"))
Eric Andersen98e599c2001-02-14 18:47:33 +00001099 value = _POSIX_VDISABLE;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001100 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001101 value = arg[1] & 0x1f; /* Non-letters get weird results */
Eric Andersen98e599c2001-02-14 18:47:33 +00001102 if (arg[1] == '?')
1103 value = 127;
Eric Andersen98e599c2001-02-14 18:47:33 +00001104 } else
Denis Vlasenko13858992006-10-08 12:49:22 +00001105 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001106 mode->c_cc[info->offset] = value;
1107}
1108
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001109static void display_changed(const struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001110{
1111 int i;
Eric Andersen98e599c2001-02-14 18:47:33 +00001112 tcflag_t *bitsp;
1113 unsigned long mask;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001114 int prev_type = control;
Eric Andersen98e599c2001-02-14 18:47:33 +00001115
1116 display_speed(mode, 1);
1117#ifdef HAVE_C_LINE
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001118 wrapf("line = %d;\n", mode->c_line);
1119#else
1120 wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001121#endif
Eric Andersen98e599c2001-02-14 18:47:33 +00001122
Eric Andersen98e599c2001-02-14 18:47:33 +00001123 for (i = 0; control_info[i].name != stty_min; ++i) {
1124 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1125 continue;
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001126 /* If swtch is the same as susp, don't print both */
Eric Andersen98e599c2001-02-14 18:47:33 +00001127#if VSWTCH == VSUSP
1128 if (control_info[i].name == stty_swtch)
1129 continue;
1130#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001131 /* If eof uses the same slot as min, only print whichever applies */
Eric Andersen98e599c2001-02-14 18:47:33 +00001132#if VEOF == VMIN
1133 if ((mode->c_lflag & ICANON) == 0
1134 && (control_info[i].name == stty_eof
1135 || control_info[i].name == stty_eol)) continue;
1136#endif
Eric Andersen98e599c2001-02-14 18:47:33 +00001137 wrapf("%s = %s;", control_info[i].name,
1138 visible(mode->c_cc[control_info[i].offset]));
1139 }
1140 if ((mode->c_lflag & ICANON) == 0) {
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001141 wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN],
Eric Andersen98e599c2001-02-14 18:47:33 +00001142 (int) mode->c_cc[VTIME]);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001143 }
1144 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001145
Eric Andersen98e599c2001-02-14 18:47:33 +00001146 for (i = 0; i < NUM_mode_info; ++i) {
1147 if (mode_info[i].flags & OMIT)
1148 continue;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001149 if (mode_info[i].type != prev_type) {
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001150 if (current_col) wrapf("\n");
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001151 prev_type = mode_info[i].type;
Eric Andersen98e599c2001-02-14 18:47:33 +00001152 }
1153
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001154 bitsp = mode_type_flag(mode_info[i].type, mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001155 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1156 if ((*bitsp & mask) == mode_info[i].bits) {
1157 if (mode_info[i].flags & SANE_UNSET) {
1158 wrapf("%s", mode_info[i].name);
Eric Andersen98e599c2001-02-14 18:47:33 +00001159 }
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001160 } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001161 wrapf("-%s", mode_info[i].name);
Eric Andersen98e599c2001-02-14 18:47:33 +00001162 }
1163 }
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001164 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001165}
1166
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001167static void display_all(const struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001168{
1169 int i;
1170 tcflag_t *bitsp;
1171 unsigned long mask;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001172 int prev_type = control;
Eric Andersen98e599c2001-02-14 18:47:33 +00001173
1174 display_speed(mode, 1);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001175 display_window_size(1);
Eric Andersen98e599c2001-02-14 18:47:33 +00001176#ifdef HAVE_C_LINE
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001177 wrapf("line = %d;\n", mode->c_line);
1178#else
1179 wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001180#endif
Eric Andersen98e599c2001-02-14 18:47:33 +00001181
1182 for (i = 0; control_info[i].name != stty_min; ++i) {
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001183 /* If swtch is the same as susp, don't print both */
Eric Andersen98e599c2001-02-14 18:47:33 +00001184#if VSWTCH == VSUSP
1185 if (control_info[i].name == stty_swtch)
1186 continue;
1187#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001188 /* If eof uses the same slot as min, only print whichever applies */
Eric Andersen98e599c2001-02-14 18:47:33 +00001189#if VEOF == VMIN
1190 if ((mode->c_lflag & ICANON) == 0
1191 && (control_info[i].name == stty_eof
1192 || control_info[i].name == stty_eol)) continue;
1193#endif
1194 wrapf("%s = %s;", control_info[i].name,
1195 visible(mode->c_cc[control_info[i].offset]));
1196 }
1197#if VEOF == VMIN
1198 if ((mode->c_lflag & ICANON) == 0)
1199#endif
1200 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001201 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001202
1203 for (i = 0; i < NUM_mode_info; ++i) {
1204 if (mode_info[i].flags & OMIT)
1205 continue;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001206 if (mode_info[i].type != prev_type) {
1207 wrapf("\n");
1208 prev_type = mode_info[i].type;
Eric Andersen98e599c2001-02-14 18:47:33 +00001209 }
1210
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001211 bitsp = mode_type_flag(mode_info[i].type, mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001212 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1213 if ((*bitsp & mask) == mode_info[i].bits)
1214 wrapf("%s", mode_info[i].name);
1215 else if (mode_info[i].flags & REV)
1216 wrapf("-%s", mode_info[i].name);
1217 }
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001218 if (current_col) wrapf("\n");
Eric Andersen98e599c2001-02-14 18:47:33 +00001219}
1220
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001221static void display_speed(const struct termios *mode, int fancy)
Eric Andersen98e599c2001-02-14 18:47:33 +00001222{
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001223 //01234567 8 9
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001224 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001225 unsigned long ispeed, ospeed;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001226
1227 ospeed = ispeed = cfgetispeed(mode);
1228 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001229 ispeed = ospeed; /* in case ispeed was 0 */
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001230 //0123 4 5 6 7 8 9
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001231 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001232 }
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001233 if (fancy) fmt_str += 9;
Rob Landley290fcb42006-06-18 23:59:03 +00001234 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001235}
1236
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001237static void display_recoverable(const struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001238{
1239 int i;
Eric Andersen98e599c2001-02-14 18:47:33 +00001240 printf("%lx:%lx:%lx:%lx",
1241 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1242 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1243 for (i = 0; i < NCCS; ++i)
1244 printf(":%x", (unsigned int) mode->c_cc[i]);
1245 putchar('\n');
1246}
1247
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001248static int recover_mode(const char *arg, struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001249{
1250 int i, n;
1251 unsigned int chr;
1252 unsigned long iflag, oflag, cflag, lflag;
1253
1254 /* Scan into temporaries since it is too much trouble to figure out
Denis Vlasenko79deb662006-09-19 15:12:12 +00001255 the right format for 'tcflag_t' */
Eric Andersen98e599c2001-02-14 18:47:33 +00001256 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1257 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1258 return 0;
1259 mode->c_iflag = iflag;
1260 mode->c_oflag = oflag;
1261 mode->c_cflag = cflag;
1262 mode->c_lflag = lflag;
1263 arg += n;
1264 for (i = 0; i < NCCS; ++i) {
1265 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1266 return 0;
1267 mode->c_cc[i] = chr;
1268 arg += n;
1269 }
1270
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001271 /* Fail if there are too many fields */
Eric Andersen98e599c2001-02-14 18:47:33 +00001272 if (*arg != '\0')
1273 return 0;
1274
1275 return 1;
1276}
1277
Eric Andersen98e599c2001-02-14 18:47:33 +00001278static void sane_mode(struct termios *mode)
1279{
1280 int i;
1281 tcflag_t *bitsp;
1282
1283 for (i = 0; i < NUM_control_info; ++i) {
1284#if VMIN == VEOF
1285 if (control_info[i].name == stty_min)
1286 break;
1287#endif
1288 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1289 }
1290
1291 for (i = 0; i < NUM_mode_info; ++i) {
1292 if (mode_info[i].flags & SANE_SET) {
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001293 bitsp = mode_type_flag(mode_info[i].type, mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001294 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1295 | mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001296 } else if (mode_info[i].flags & SANE_UNSET) {
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001297 bitsp = mode_type_flag(mode_info[i].type, mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001298 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1299 & ~mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001300 }
1301 }
1302}