blob: a3526136f9de756b3ff7ec84e289b6c3b8f8cb45 [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
Mark Whitley446dd272001-03-02 20:00:54 +000024//#define TEST
Eric Andersen98e599c2001-02-14 18:47:33 +000025
Manuel Novoa III cad53642003-03-19 09:13:01 +000026#include <stddef.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000027#include <termios.h>
28#include <sys/ioctl.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000029
30#include <sys/param.h>
31#include <unistd.h>
32
33#ifndef STDIN_FILENO
34# define STDIN_FILENO 0
35#endif
36
37#ifndef STDOUT_FILENO
38# define STDOUT_FILENO 1
39#endif
40
41#include <stdlib.h>
42#include <string.h>
43#include <assert.h>
44#include <ctype.h>
45#include <errno.h>
46#include <limits.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000047#include <fcntl.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000048#include "busybox.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000049
50#define STREQ(a, b) (strcmp ((a), (b)) == 0)
51
52
53#ifndef _POSIX_VDISABLE
54# define _POSIX_VDISABLE ((unsigned char) 0)
55#endif
56
57#define Control(c) ((c) & 0x1f)
58/* Canonical values for control characters. */
59#ifndef CINTR
60# define CINTR Control ('c')
61#endif
62#ifndef CQUIT
63# define CQUIT 28
64#endif
65#ifndef CERASE
66# define CERASE 127
67#endif
68#ifndef CKILL
69# define CKILL Control ('u')
70#endif
71#ifndef CEOF
72# define CEOF Control ('d')
73#endif
74#ifndef CEOL
75# define CEOL _POSIX_VDISABLE
76#endif
77#ifndef CSTART
78# define CSTART Control ('q')
79#endif
80#ifndef CSTOP
81# define CSTOP Control ('s')
82#endif
83#ifndef CSUSP
84# define CSUSP Control ('z')
85#endif
86#if defined(VEOL2) && !defined(CEOL2)
87# define CEOL2 _POSIX_VDISABLE
88#endif
89/* ISC renamed swtch to susp for termios, but we'll accept either name. */
90#if defined(VSUSP) && !defined(VSWTCH)
91# define VSWTCH VSUSP
92# define CSWTCH CSUSP
93#endif
94#if defined(VSWTCH) && !defined(CSWTCH)
95# define CSWTCH _POSIX_VDISABLE
96#endif
97
98/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
99 So the default is to disable `swtch.' */
100#if defined (__sparc__) && defined (__svr4__)
101# undef CSWTCH
102# define CSWTCH _POSIX_VDISABLE
103#endif
104
Mark Whitley446dd272001-03-02 20:00:54 +0000105#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000106# define VWERASE VWERSE
107#endif
108#if defined(VDSUSP) && !defined (CDSUSP)
109# define CDSUSP Control ('y')
110#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000111#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000112# define VREPRINT VRPRNT
113#endif
114#if defined(VREPRINT) && !defined(CRPRNT)
115# define CRPRNT Control ('r')
116#endif
117#if defined(VWERASE) && !defined(CWERASE)
118# define CWERASE Control ('w')
119#endif
120#if defined(VLNEXT) && !defined(CLNEXT)
121# define CLNEXT Control ('v')
122#endif
123#if defined(VDISCARD) && !defined(VFLUSHO)
124# define VFLUSHO VDISCARD
125#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000126#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000127# define VFLUSHO VFLUSH
128#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000129#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000130# define ECHOCTL CTLECH
131#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000132#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000133# define ECHOCTL TCTLECH
134#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000135#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000136# define ECHOKE CRTKIL
137#endif
138#if defined(VFLUSHO) && !defined(CFLUSHO)
139# define CFLUSHO Control ('o')
140#endif
141#if defined(VSTATUS) && !defined(CSTATUS)
142# define CSTATUS Control ('t')
143#endif
144
145/* Which speeds to set. */
146enum speed_setting {
147 input_speed, output_speed, both_speeds
148};
149
Eric Andersen98e599c2001-02-14 18:47:33 +0000150/* Which member(s) of `struct termios' a mode uses. */
151enum mode_type {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000152 /* Do NOT change the order or values, as mode_type_flag()
153 * depends on them. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000154 control, input, output, local, combination
155};
156
157
Mark Whitley446dd272001-03-02 20:00:54 +0000158static const char evenp [] = "evenp";
159static const char raw [] = "raw";
160static const char stty_min [] = "min";
161static const char stty_time [] = "time";
Eric Andersen98e599c2001-02-14 18:47:33 +0000162static const char stty_swtch[] = "swtch";
Mark Whitley446dd272001-03-02 20:00:54 +0000163static const char stty_eol [] = "eol";
164static const char stty_eof [] = "eof";
165static const char parity [] = "parity";
166static const char stty_oddp [] = "oddp";
167static const char stty_nl [] = "nl";
168static const char stty_ek [] = "ek";
169static const char stty_sane [] = "sane";
170static const char cbreak [] = "cbreak";
Eric Andersen98e599c2001-02-14 18:47:33 +0000171static const char stty_pass8[] = "pass8";
Mark Whitley446dd272001-03-02 20:00:54 +0000172static const char litout [] = "litout";
173static const char cooked [] = "cooked";
174static const char decctlq [] = "decctlq";
175static const char stty_tabs [] = "tabs";
Eric Andersen98e599c2001-02-14 18:47:33 +0000176static const char stty_lcase[] = "lcase";
177static const char stty_LCASE[] = "LCASE";
Mark Whitley446dd272001-03-02 20:00:54 +0000178static const char stty_crt [] = "crt";
179static const char stty_dec [] = "dec";
Eric Andersen98e599c2001-02-14 18:47:33 +0000180
181
182/* Flags for `struct mode_info'. */
Mark Whitley446dd272001-03-02 20:00:54 +0000183#define SANE_SET 1 /* Set in `sane' mode. */
184#define SANE_UNSET 2 /* Unset in `sane' mode. */
185#define REV 4 /* Can be turned off by prepending `-'. */
186#define OMIT 8 /* Don't display value. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000187
188/* Each mode. */
189struct mode_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000190 const char *name; /* Name given on command line. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000191 /* enum mode_type type; */
192 char type; /* Which structure element to change. */
Mark Whitley446dd272001-03-02 20:00:54 +0000193 char flags; /* Setting and display options. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000194 unsigned short mask; /* Other bits to turn off for this mode. */
Mark Whitley446dd272001-03-02 20:00:54 +0000195 unsigned long bits; /* Bits to set for this mode. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000196};
197
Manuel Novoa III cad53642003-03-19 09:13:01 +0000198#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
199
Mark Whitley446dd272001-03-02 20:00:54 +0000200static const struct mode_info mode_info[] = {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000201 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
202 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
203 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
204 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
205 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
206 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
207 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
208 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
209 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
210 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
211 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000212#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000213 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000214#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000215 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
216 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
217 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
218 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
219 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
220 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
221 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
222 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
223 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
224 MI_ENTRY("ixon", input, REV, IXON, 0 ),
225 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
226 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000227#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000228 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000229#endif
230#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000231 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000232#endif
233#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000234 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000235#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000236 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000237#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000238 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000239#endif
240#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000241 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000242#endif
243#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000244 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000245#endif
246#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000247 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000248#endif
249#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000250 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000251#endif
252#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000253 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000254#endif
255#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000256 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000257#endif
258#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000259 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
260 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000261#endif
262#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000263 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
264 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
265 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
266 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000267#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000268
Eric Andersen98e599c2001-02-14 18:47:33 +0000269#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000270 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
271 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
272 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
273 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000274#else
275# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000276 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000277# endif
278#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000279
Eric Andersen98e599c2001-02-14 18:47:33 +0000280#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000281 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
282 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000283#endif
284#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000285 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
286 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000287#endif
288#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000289 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
290 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000291#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000292 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
293 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000294#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000295 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000296#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000297 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
298 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
299 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
300 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
301 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
302 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000303#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000304 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000305#endif
306#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000307 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000308#endif
309#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000310 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
311 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000312#endif
313#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000314 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
315 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000316#endif
317#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000318 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
319 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000320#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000321 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
322 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
323 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
324 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
325 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
326 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
327 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
328 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
329 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
330 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
331 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000332#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000333 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000334#endif
335#if defined (TABDLY) || defined (OXTABS)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000336 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000337#endif
338#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000339 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
340 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000341#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000342 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
343 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000344};
345
346static const int NUM_mode_info =
347
348 (sizeof(mode_info) / sizeof(struct mode_info));
349
350/* Control character settings. */
351struct control_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000352 const char *name; /* Name given on command line. */
353 unsigned char saneval; /* Value to set for `stty sane'. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000354 unsigned char offset; /* Offset in c_cc. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000355};
356
357/* Control characters. */
358
Mark Whitley446dd272001-03-02 20:00:54 +0000359static const struct control_info control_info[] = {
360 {"intr", CINTR, VINTR},
361 {"quit", CQUIT, VQUIT},
362 {"erase", CERASE, VERASE},
363 {"kill", CKILL, VKILL},
364 {stty_eof, CEOF, VEOF},
365 {stty_eol, CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000366#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000367 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000368#endif
369#ifdef VSWTCH
Mark Whitley446dd272001-03-02 20:00:54 +0000370 {stty_swtch, CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000371#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000372 {"start", CSTART, VSTART},
373 {"stop", CSTOP, VSTOP},
374 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000375#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000376 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000377#endif
378#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000379 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000380#endif
381#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000382 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000383#endif
384#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000385 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000386#endif
387#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000388 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000389#endif
390#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000391 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000392#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000393 /* These must be last because of the display routines. */
Mark Whitley446dd272001-03-02 20:00:54 +0000394 {stty_min, 1, VMIN},
395 {stty_time, 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000396};
397
398static const int NUM_control_info =
399 (sizeof(control_info) / sizeof(struct control_info));
400
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000401#define EMT(t) ((enum mode_type)(t))
Eric Andersen98e599c2001-02-14 18:47:33 +0000402
Mark Whitley446dd272001-03-02 20:00:54 +0000403static const char * visible(unsigned int ch);
Mark Whitley446dd272001-03-02 20:00:54 +0000404static int recover_mode(char *arg, struct termios *mode);
405static int screen_columns(void);
406static int set_mode(const struct mode_info *info,
407 int reversed, struct termios *mode);
408static speed_t string_to_baud(const char *arg);
409static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000410static void display_all(struct termios *mode);
411static void display_changed(struct termios *mode);
412static void display_recoverable(struct termios *mode);
Mark Whitley446dd272001-03-02 20:00:54 +0000413static void display_speed(struct termios *mode, int fancy);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000414static void display_window_size(int fancy);
Mark Whitley446dd272001-03-02 20:00:54 +0000415static void sane_mode(struct termios *mode);
416static void set_control_char(const struct control_info *info,
417 const char *arg, struct termios *mode);
418static void set_speed(enum speed_setting type,
419 const char *arg, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000420static void set_window_size(int rows, int cols);
Eric Andersen8876fb22003-06-20 09:01:58 +0000421
422static const char *device_name;
423
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000424static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000425{
426 bb_perror_msg_and_die(fmt, device_name);
427}
428
Eric Andersen98e599c2001-02-14 18:47:33 +0000429
430/* The width of the screen, for output wrapping. */
431static int max_col;
432
433/* Current position, to know when to wrap. */
434static int current_col;
435
436/* Print format string MESSAGE and optional args.
437 Wrap to next line first if it won't fit.
438 Print a space first unless MESSAGE will start a new line. */
439
440static void wrapf(const char *message, ...)
441{
442 va_list args;
Mark Whitley446dd272001-03-02 20:00:54 +0000443 char buf[1024]; /* Plenty long for our needs. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000444 int buflen;
445
446 va_start(args, message);
447 vsprintf(buf, message, args);
448 va_end(args);
449 buflen = strlen(buf);
450 if (current_col + (current_col > 0) + buflen >= max_col) {
451 putchar('\n');
452 current_col = 0;
453 }
454 if (current_col > 0) {
455 putchar(' ');
456 current_col++;
457 }
458 fputs(buf, stdout);
459 current_col += buflen;
460}
461
462static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000463 {"b", 512 },
464 {"k", 1024},
465 {"B", 1024},
466 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000467};
468
Mark Whitley446dd272001-03-02 20:00:54 +0000469#ifndef TEST
Rob Landleydfba7412006-03-06 20:47:33 +0000470int stty_main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000471#else
Rob Landleydfba7412006-03-06 20:47:33 +0000472int main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000473#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000474{
475 struct termios mode;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000476 void (*output_func)(struct termios *);
Mark Whitley446dd272001-03-02 20:00:54 +0000477 int optc;
478 int require_set_attr;
479 int speed_was_set;
480 int verbose_output;
481 int recoverable_output;
482 int k;
483 int noargs = 1;
484 char * file_name = NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +0000485
Manuel Novoa III cad53642003-03-19 09:13:01 +0000486 output_func = display_changed;
Eric Andersen98e599c2001-02-14 18:47:33 +0000487 verbose_output = 0;
488 recoverable_output = 0;
489
490 /* Don't print error messages for unrecognized options. */
491 opterr = 0;
492
493 while ((optc = getopt(argc, argv, "agF:")) != -1) {
494 switch (optc) {
495 case 'a':
496 verbose_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000497 output_func = display_all;
Eric Andersen98e599c2001-02-14 18:47:33 +0000498 break;
499
500 case 'g':
501 recoverable_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000502 output_func = display_recoverable;
Eric Andersen98e599c2001-02-14 18:47:33 +0000503 break;
504
505 case 'F':
506 if (file_name)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000507 bb_error_msg_and_die("only one device may be specified");
Eric Andersen98e599c2001-02-14 18:47:33 +0000508 file_name = optarg;
509 break;
510
Mark Whitley446dd272001-03-02 20:00:54 +0000511 default: /* unrecognized option */
Eric Andersen98e599c2001-02-14 18:47:33 +0000512 noargs = 0;
513 break;
514 }
515
516 if (noargs == 0)
517 break;
518 }
519
520 if (optind < argc)
521 noargs = 0;
522
523 /* Specifying both -a and -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000524 if (verbose_output & recoverable_output)
525 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
Eric Andersen98e599c2001-02-14 18:47:33 +0000526
527 /* Specifying any other arguments with -a or -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000528 if (~noargs & (verbose_output | recoverable_output))
529 bb_error_msg_and_die ("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000530
531 /* FIXME: it'd be better not to open the file until we've verified
532 that all arguments are valid. Otherwise, we could end up doing
533 only some of the requested operations and then failing, probably
534 leaving things in an undesirable state. */
535
536 if (file_name) {
537 int fdflags;
538
539 device_name = file_name;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000540 fclose(stdin);
541 bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
542 if ((fdflags = fcntl(STDIN_FILENO, F_GETFL)) == -1
543 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Eric Andersen8876fb22003-06-20 09:01:58 +0000544 perror_on_device("%s: couldn't reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +0000545 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000546 device_name = bb_msg_standard_input;
Eric Andersen98e599c2001-02-14 18:47:33 +0000547 }
548
549 /* Initialize to all zeroes so there is no risk memcmp will report a
550 spurious difference in an uninitialized portion of the structure. */
551 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000552 if (tcgetattr(STDIN_FILENO, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000553 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000554
Manuel Novoa III cad53642003-03-19 09:13:01 +0000555 if (verbose_output | recoverable_output | noargs) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000556 max_col = screen_columns();
557 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000558 output_func(&mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000559 return EXIT_SUCCESS;
560 }
561
562 speed_was_set = 0;
563 require_set_attr = 0;
Eric Andersenfc059092002-06-06 11:35:29 +0000564 k = 0;
565 while (++k < argc) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000566 int match_found = 0;
567 int reversed = 0;
568 int i;
569
570 if (argv[k][0] == '-') {
Eric Andersenfc059092002-06-06 11:35:29 +0000571 char *find_dev_opt;
572
Eric Andersen98e599c2001-02-14 18:47:33 +0000573 ++argv[k];
Eric Andersenfc059092002-06-06 11:35:29 +0000574
575 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
576 Find the options that have been parsed. This is really
577 gross, but it's needed because stty SETTINGS look like options to
578 getopt(), so we need to work around things in a really horrible
579 way. If any new options are ever added to stty, the short option
580 MUST NOT be a letter which is the first letter of one of the
581 possible stty settings.
582 */
583 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
584 if(find_dev_opt) {
585 if(find_dev_opt[1]==0) /* -*F /dev/foo */
586 k++; /* skip /dev/foo */
587 continue; /* else -*F/dev/foo - no skip */
588 }
589 if(argv[k][0]=='a' || argv[k][0]=='g')
590 continue;
591 /* Is not options - is reverse params */
Eric Andersen98e599c2001-02-14 18:47:33 +0000592 reversed = 1;
593 }
Mark Whitley446dd272001-03-02 20:00:54 +0000594 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000595 if (STREQ(argv[k], mode_info[i].name)) {
596 match_found = set_mode(&mode_info[i], reversed, &mode);
597 require_set_attr = 1;
598 break;
599 }
Mark Whitley446dd272001-03-02 20:00:54 +0000600
601 if (match_found == 0 && reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000602 bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000603
604 if (match_found == 0)
605 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000606 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000607 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000608 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000609 match_found = 1;
610 ++k;
611 set_control_char(&control_info[i], argv[k], &mode);
612 require_set_attr = 1;
613 break;
614 }
Mark Whitley446dd272001-03-02 20:00:54 +0000615
Eric Andersen98e599c2001-02-14 18:47:33 +0000616 if (match_found == 0) {
617 if (STREQ(argv[k], "ispeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000618 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000619 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000620 ++k;
621 set_speed(input_speed, argv[k], &mode);
622 speed_was_set = 1;
623 require_set_attr = 1;
624 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000625 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000626 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000627 ++k;
628 set_speed(output_speed, argv[k], &mode);
629 speed_was_set = 1;
630 require_set_attr = 1;
631 }
632#ifdef TIOCGWINSZ
633 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000634 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000635 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000636 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000637 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000638 -1);
Eric Andersen98e599c2001-02-14 18:47:33 +0000639 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000640 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000641 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000642 ++k;
643 set_window_size(-1,
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000644 (int) bb_xparse_number(argv[k], stty_suffixes));
Eric Andersen98e599c2001-02-14 18:47:33 +0000645 } else if (STREQ(argv[k], "size")) {
646 max_col = screen_columns();
647 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000648 display_window_size(0);
Eric Andersen98e599c2001-02-14 18:47:33 +0000649 }
650#endif
651#ifdef HAVE_C_LINE
652 else if (STREQ(argv[k], "line")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000653 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000654 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000655 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000656 mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000657 require_set_attr = 1;
658 }
659#endif
660 else if (STREQ(argv[k], "speed")) {
661 max_col = screen_columns();
662 display_speed(&mode, 0);
Mark Whitley446dd272001-03-02 20:00:54 +0000663 } else if (recover_mode(argv[k], &mode) == 1)
664 require_set_attr = 1;
665 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000666 set_speed(both_speeds, argv[k], &mode);
667 speed_was_set = 1;
668 require_set_attr = 1;
Mark Whitley446dd272001-03-02 20:00:54 +0000669 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000670 bb_error_msg_and_die("invalid argument `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000671 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000672 }
673
674 if (require_set_attr) {
675 struct termios new_mode;
676
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000677 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000678 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000679
680 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
681 it performs *any* of the requested operations. This means it
682 can report `success' when it has actually failed to perform
683 some proper subset of the requested operations. To detect
684 this partial failure, get the current terminal attributes and
685 compare them to the requested ones. */
686
687 /* Initialize to all zeroes so there is no risk memcmp will report a
688 spurious difference in an uninitialized portion of the structure. */
689 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000690 if (tcgetattr(STDIN_FILENO, &new_mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000691 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000692
693 /* Normally, one shouldn't use memcmp to compare structures that
694 may have `holes' containing uninitialized data, but we have been
695 careful to initialize the storage of these two variables to all
696 zeroes. One might think it more efficient simply to compare the
697 modified fields, but that would require enumerating those fields --
698 and not all systems have the same fields in this structure. */
699
700 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
701#ifdef CIBAUD
702 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
703 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
704 sometimes (m1 != m2). The only difference is in the four bits
705 of the c_cflag field corresponding to the baud rate. To save
706 Sun users a little confusion, don't report an error if this
707 happens. But suppress the error only if we haven't tried to
708 set the baud rate explicitly -- otherwise we'd never give an
709 error for a true failure to set the baud rate. */
710
711 new_mode.c_cflag &= (~CIBAUD);
712 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
713#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000714 perror_on_device ("%s: unable to perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +0000715 }
716 }
717
718 return EXIT_SUCCESS;
719}
720
721/* Return 0 if not applied because not reversible; otherwise return 1. */
722
723static int
724set_mode(const struct mode_info *info, int reversed, struct termios *mode)
725{
726 tcflag_t *bitsp;
727
728 if (reversed && (info->flags & REV) == 0)
729 return 0;
730
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000731 bitsp = mode_type_flag(EMT(info->type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000732
733 if (bitsp == NULL) {
734 /* Combination mode. */
735 if (info->name == evenp || info->name == parity) {
736 if (reversed)
737 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
738 else
739 mode->c_cflag =
740 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
741 } else if (info->name == stty_oddp) {
742 if (reversed)
743 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
744 else
745 mode->c_cflag =
746 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
747 } else if (info->name == stty_nl) {
748 if (reversed) {
749 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
750 mode->c_oflag = (mode->c_oflag
751#ifdef ONLCR
752 | ONLCR
753#endif
754 )
755#ifdef OCRNL
756 & ~OCRNL
757#endif
758#ifdef ONLRET
759 & ~ONLRET
760#endif
761 ;
762 } else {
763 mode->c_iflag = mode->c_iflag & ~ICRNL;
764#ifdef ONLCR
765 mode->c_oflag = mode->c_oflag & ~ONLCR;
766#endif
767 }
768 } else if (info->name == stty_ek) {
769 mode->c_cc[VERASE] = CERASE;
770 mode->c_cc[VKILL] = CKILL;
771 } else if (info->name == stty_sane)
772 sane_mode(mode);
773 else if (info->name == cbreak) {
774 if (reversed)
775 mode->c_lflag |= ICANON;
776 else
777 mode->c_lflag &= ~ICANON;
778 } else if (info->name == stty_pass8) {
779 if (reversed) {
780 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
781 mode->c_iflag |= ISTRIP;
782 } else {
783 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
784 mode->c_iflag &= ~ISTRIP;
785 }
786 } else if (info->name == litout) {
787 if (reversed) {
788 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
789 mode->c_iflag |= ISTRIP;
790 mode->c_oflag |= OPOST;
791 } else {
792 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
793 mode->c_iflag &= ~ISTRIP;
794 mode->c_oflag &= ~OPOST;
795 }
796 } else if (info->name == raw || info->name == cooked) {
797 if ((info->name[0] == 'r' && reversed)
798 || (info->name[0] == 'c' && !reversed)) {
799 /* Cooked mode. */
800 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
801 mode->c_oflag |= OPOST;
802 mode->c_lflag |= ISIG | ICANON;
803#if VMIN == VEOF
804 mode->c_cc[VEOF] = CEOF;
805#endif
806#if VTIME == VEOL
807 mode->c_cc[VEOL] = CEOL;
808#endif
809 } else {
810 /* Raw mode. */
811 mode->c_iflag = 0;
812 mode->c_oflag &= ~OPOST;
813 mode->c_lflag &= ~(ISIG | ICANON
814#ifdef XCASE
815 | XCASE
816#endif
817 );
818 mode->c_cc[VMIN] = 1;
819 mode->c_cc[VTIME] = 0;
820 }
821 }
822#ifdef IXANY
823 else if (info->name == decctlq) {
824 if (reversed)
825 mode->c_iflag |= IXANY;
826 else
827 mode->c_iflag &= ~IXANY;
828 }
829#endif
830#ifdef TABDLY
831 else if (info->name == stty_tabs) {
832 if (reversed)
833 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
834 else
835 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
836 }
837#else
838# ifdef OXTABS
839 else if (info->name == stty_tabs) {
840 if (reversed)
841 mode->c_oflag = mode->c_oflag | OXTABS;
842 else
843 mode->c_oflag = mode->c_oflag & ~OXTABS;
844 }
845# endif
846#endif
847#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
848 else if (info->name == stty_lcase || info->name == stty_LCASE) {
849 if (reversed) {
850 mode->c_lflag &= ~XCASE;
851 mode->c_iflag &= ~IUCLC;
852 mode->c_oflag &= ~OLCUC;
853 } else {
854 mode->c_lflag |= XCASE;
855 mode->c_iflag |= IUCLC;
856 mode->c_oflag |= OLCUC;
857 }
858 }
859#endif
860 else if (info->name == stty_crt)
861 mode->c_lflag |= ECHOE
862#ifdef ECHOCTL
863 | ECHOCTL
864#endif
865#ifdef ECHOKE
866 | ECHOKE
867#endif
868 ;
869 else if (info->name == stty_dec) {
Mark Whitley446dd272001-03-02 20:00:54 +0000870 mode->c_cc[VINTR] = 3; /* ^C */
871 mode->c_cc[VERASE] = 127; /* DEL */
872 mode->c_cc[VKILL] = 21; /* ^U */
Eric Andersen98e599c2001-02-14 18:47:33 +0000873 mode->c_lflag |= ECHOE
874#ifdef ECHOCTL
875 | ECHOCTL
876#endif
877#ifdef ECHOKE
878 | ECHOKE
879#endif
880 ;
881#ifdef IXANY
882 mode->c_iflag &= ~IXANY;
883#endif
884 }
885 } else if (reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000886 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000887 else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000888 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000889
890 return 1;
891}
892
893static void
894set_control_char(const struct control_info *info, const char *arg,
895 struct termios *mode)
896{
897 unsigned char value;
898
899 if (info->name == stty_min || info->name == stty_time)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000900 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000901 else if (arg[0] == '\0' || arg[1] == '\0')
902 value = arg[0];
903 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
904 value = _POSIX_VDISABLE;
Mark Whitley446dd272001-03-02 20:00:54 +0000905 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000906 if (arg[1] == '?')
907 value = 127;
908 else
Mark Whitley446dd272001-03-02 20:00:54 +0000909 value = arg[1] & ~0140; /* Non-letters get weird results. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000910 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000911 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000912 mode->c_cc[info->offset] = value;
913}
914
915static void
916set_speed(enum speed_setting type, const char *arg, struct termios *mode)
917{
918 speed_t baud;
919
920 baud = string_to_baud(arg);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000921
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000922 if (type != output_speed) { /* either input or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000923 cfsetispeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000924 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000925 if (type != input_speed) { /* either output or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000926 cfsetospeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000927 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000928}
929
930#ifdef TIOCGWINSZ
931
932static int get_win_size(int fd, struct winsize *win)
933{
934 int err = ioctl(fd, TIOCGWINSZ, (char *) win);
935
936 return err;
937}
938
939static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000940set_window_size(int rows, int cols)
Eric Andersen98e599c2001-02-14 18:47:33 +0000941{
942 struct winsize win;
943
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000944 if (get_win_size(STDIN_FILENO, &win)) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000945 if (errno != EINVAL)
Eric Andersen8876fb22003-06-20 09:01:58 +0000946 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000947 memset(&win, 0, sizeof(win));
948 }
949
950 if (rows >= 0)
951 win.ws_row = rows;
952 if (cols >= 0)
953 win.ws_col = cols;
954
955# ifdef TIOCSSIZE
956 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
957 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
958 This comment from sys/ttold.h describes Sun's twisted logic - a better
959 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
960 At any rate, the problem is gone in Solaris 2.x. */
961
962 if (win.ws_row == 0 || win.ws_col == 0) {
963 struct ttysize ttysz;
964
965 ttysz.ts_lines = win.ws_row;
966 ttysz.ts_cols = win.ws_col;
967
Manuel Novoa III cad53642003-03-19 09:13:01 +0000968 win.ws_row = win.ws_col = 1;
Eric Andersen98e599c2001-02-14 18:47:33 +0000969
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000970 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
971 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000972 perror_on_device("%s");
Glenn L McGrathca1c1af2004-09-15 03:24:32 +0000973 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000974 return;
975 }
976# endif
977
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000978 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Eric Andersen8876fb22003-06-20 09:01:58 +0000979 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000980}
981
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000982static void display_window_size(int fancy)
Eric Andersen98e599c2001-02-14 18:47:33 +0000983{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000984 const char *fmt_str = "%s" "\0" "%s: no size information for this device";
Eric Andersen98e599c2001-02-14 18:47:33 +0000985 struct winsize win;
986
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000987 if (get_win_size(STDIN_FILENO, &win)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000988 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000989 perror_on_device(fmt_str);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000990 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000991 } else {
992 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
993 win.ws_row, win.ws_col);
994 if (!fancy)
995 current_col = 0;
996 }
997}
998#endif
999
1000static int screen_columns(void)
1001{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001002 int columns;
1003 const char *s;
1004
Eric Andersen98e599c2001-02-14 18:47:33 +00001005#ifdef TIOCGWINSZ
1006 struct winsize win;
1007
1008 /* With Solaris 2.[123], this ioctl fails and errno is set to
1009 EINVAL for telnet (but not rlogin) sessions.
1010 On ISC 3.0, it fails for the console and the serial port
1011 (but it works for ptys).
1012 It can also fail on any system when stdout isn't a tty.
1013 In case of any failure, just use the default. */
1014 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1015 return win.ws_col;
1016#endif
1017
Manuel Novoa III cad53642003-03-19 09:13:01 +00001018 columns = 80;
1019 if ((s = getenv("COLUMNS"))) {
1020 columns = atoi(s);
1021 }
1022 return columns;
Eric Andersen98e599c2001-02-14 18:47:33 +00001023}
1024
1025static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1026{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001027 static const unsigned char tcflag_offsets[] = {
1028 offsetof(struct termios, c_cflag), /* control */
1029 offsetof(struct termios, c_iflag), /* input */
1030 offsetof(struct termios, c_oflag), /* output */
1031 offsetof(struct termios, c_lflag) /* local */
1032 };
Eric Andersen98e599c2001-02-14 18:47:33 +00001033
Manuel Novoa III cad53642003-03-19 09:13:01 +00001034 if (((unsigned int) type) <= local) {
1035 return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
Eric Andersen98e599c2001-02-14 18:47:33 +00001036 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001037 return NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +00001038}
1039
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001040static void display_changed(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001041{
1042 int i;
1043 int empty_line;
1044 tcflag_t *bitsp;
1045 unsigned long mask;
1046 enum mode_type prev_type = control;
1047
1048 display_speed(mode, 1);
1049#ifdef HAVE_C_LINE
1050 wrapf("line = %d;", mode->c_line);
1051#endif
1052 putchar('\n');
1053 current_col = 0;
1054
1055 empty_line = 1;
1056 for (i = 0; control_info[i].name != stty_min; ++i) {
1057 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1058 continue;
1059 /* If swtch is the same as susp, don't print both. */
1060#if VSWTCH == VSUSP
1061 if (control_info[i].name == stty_swtch)
1062 continue;
1063#endif
1064 /* If eof uses the same slot as min, only print whichever applies. */
1065#if VEOF == VMIN
1066 if ((mode->c_lflag & ICANON) == 0
1067 && (control_info[i].name == stty_eof
1068 || control_info[i].name == stty_eol)) continue;
1069#endif
1070
1071 empty_line = 0;
1072 wrapf("%s = %s;", control_info[i].name,
1073 visible(mode->c_cc[control_info[i].offset]));
1074 }
1075 if ((mode->c_lflag & ICANON) == 0) {
1076 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1077 (int) mode->c_cc[VTIME]);
1078 } else if (empty_line == 0)
1079 putchar('\n');
1080 current_col = 0;
1081
1082 empty_line = 1;
1083 for (i = 0; i < NUM_mode_info; ++i) {
1084 if (mode_info[i].flags & OMIT)
1085 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001086 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001087 if (empty_line == 0) {
1088 putchar('\n');
1089 current_col = 0;
1090 empty_line = 1;
1091 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001092 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001093 }
1094
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001095 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001096 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1097 if ((*bitsp & mask) == mode_info[i].bits) {
1098 if (mode_info[i].flags & SANE_UNSET) {
1099 wrapf("%s", mode_info[i].name);
1100 empty_line = 0;
1101 }
1102 }
1103 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1104 (SANE_SET | REV)) {
1105 wrapf("-%s", mode_info[i].name);
1106 empty_line = 0;
1107 }
1108 }
1109 if (empty_line == 0)
1110 putchar('\n');
1111 current_col = 0;
1112}
1113
1114static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001115display_all(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001116{
1117 int i;
1118 tcflag_t *bitsp;
1119 unsigned long mask;
1120 enum mode_type prev_type = control;
1121
1122 display_speed(mode, 1);
1123#ifdef TIOCGWINSZ
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001124 display_window_size(1);
Eric Andersen98e599c2001-02-14 18:47:33 +00001125#endif
1126#ifdef HAVE_C_LINE
1127 wrapf("line = %d;", mode->c_line);
1128#endif
1129 putchar('\n');
1130 current_col = 0;
1131
1132 for (i = 0; control_info[i].name != stty_min; ++i) {
1133 /* If swtch is the same as susp, don't print both. */
1134#if VSWTCH == VSUSP
1135 if (control_info[i].name == stty_swtch)
1136 continue;
1137#endif
1138 /* If eof uses the same slot as min, only print whichever applies. */
1139#if VEOF == VMIN
1140 if ((mode->c_lflag & ICANON) == 0
1141 && (control_info[i].name == stty_eof
1142 || control_info[i].name == stty_eol)) continue;
1143#endif
1144 wrapf("%s = %s;", control_info[i].name,
1145 visible(mode->c_cc[control_info[i].offset]));
1146 }
1147#if VEOF == VMIN
1148 if ((mode->c_lflag & ICANON) == 0)
1149#endif
1150 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1151 if (current_col != 0)
1152 putchar('\n');
1153 current_col = 0;
1154
1155 for (i = 0; i < NUM_mode_info; ++i) {
1156 if (mode_info[i].flags & OMIT)
1157 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001158 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001159 putchar('\n');
1160 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001161 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001162 }
1163
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001164 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001165 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1166 if ((*bitsp & mask) == mode_info[i].bits)
1167 wrapf("%s", mode_info[i].name);
1168 else if (mode_info[i].flags & REV)
1169 wrapf("-%s", mode_info[i].name);
1170 }
1171 putchar('\n');
1172 current_col = 0;
1173}
1174
1175static void display_speed(struct termios *mode, int fancy)
1176{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001177 unsigned long ispeed, ospeed;
1178 const char *fmt_str =
1179 "%lu %lu\n\0" "ispeed %lu baud; ospeed %lu baud;\0"
1180 "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
1181
1182 ospeed = ispeed = cfgetispeed(mode);
1183 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001184 ispeed = ospeed; /* in case ispeed was 0 */
Manuel Novoa III cad53642003-03-19 09:13:01 +00001185 fmt_str += 43;
1186 }
1187 if (fancy) {
1188 fmt_str += 9;
1189 }
1190 wrapf(fmt_str, bb_baud_to_value(ispeed), bb_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001191 if (!fancy)
1192 current_col = 0;
1193}
1194
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001195static void display_recoverable(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001196{
1197 int i;
1198
1199 printf("%lx:%lx:%lx:%lx",
1200 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1201 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1202 for (i = 0; i < NCCS; ++i)
1203 printf(":%x", (unsigned int) mode->c_cc[i]);
1204 putchar('\n');
1205}
1206
1207static int recover_mode(char *arg, struct termios *mode)
1208{
1209 int i, n;
1210 unsigned int chr;
1211 unsigned long iflag, oflag, cflag, lflag;
1212
1213 /* Scan into temporaries since it is too much trouble to figure out
1214 the right format for `tcflag_t'. */
1215 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1216 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1217 return 0;
1218 mode->c_iflag = iflag;
1219 mode->c_oflag = oflag;
1220 mode->c_cflag = cflag;
1221 mode->c_lflag = lflag;
1222 arg += n;
1223 for (i = 0; i < NCCS; ++i) {
1224 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1225 return 0;
1226 mode->c_cc[i] = chr;
1227 arg += n;
1228 }
1229
1230 /* Fail if there are too many fields. */
1231 if (*arg != '\0')
1232 return 0;
1233
1234 return 1;
1235}
1236
Eric Andersen98e599c2001-02-14 18:47:33 +00001237static speed_t string_to_baud(const char *arg)
1238{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001239 return bb_value_to_baud(bb_xparse_number(arg, 0));
Eric Andersen98e599c2001-02-14 18:47:33 +00001240}
1241
1242static void sane_mode(struct termios *mode)
1243{
1244 int i;
1245 tcflag_t *bitsp;
1246
1247 for (i = 0; i < NUM_control_info; ++i) {
1248#if VMIN == VEOF
1249 if (control_info[i].name == stty_min)
1250 break;
1251#endif
1252 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1253 }
1254
1255 for (i = 0; i < NUM_mode_info; ++i) {
1256 if (mode_info[i].flags & SANE_SET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001257 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001258 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1259 | mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001260 } else if (mode_info[i].flags & SANE_UNSET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001261 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001262 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1263 & ~mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001264 }
1265 }
1266}
1267
1268/* Return a string that is the printable representation of character CH. */
1269/* Adapted from `cat' by Torbjorn Granlund. */
1270
1271static const char *visible(unsigned int ch)
1272{
1273 static char buf[10];
1274 char *bpout = buf;
1275
Manuel Novoa III cad53642003-03-19 09:13:01 +00001276 if (ch == _POSIX_VDISABLE) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001277 return "<undef>";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001278 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001279
Manuel Novoa III cad53642003-03-19 09:13:01 +00001280 if (ch >= 128) {
1281 ch -= 128;
1282 *bpout++ = 'M';
1283 *bpout++ = '-';
1284 }
1285
1286 if (ch < 32) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001287 *bpout++ = '^';
1288 *bpout++ = ch + 64;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001289 } else if (ch < 127) {
1290 *bpout++ = ch;
1291 } else {
1292 *bpout++ = '^';
1293 *bpout++ = '?';
Eric Andersen98e599c2001-02-14 18:47:33 +00001294 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001295
Eric Andersen98e599c2001-02-14 18:47:33 +00001296 *bpout = '\0';
1297 return (const char *) buf;
1298}
1299
Mark Whitley446dd272001-03-02 20:00:54 +00001300#ifdef TEST
Mark Whitley446dd272001-03-02 20:00:54 +00001301
Manuel Novoa III cad53642003-03-19 09:13:01 +00001302const char *bb_applet_name = "stty";
Mark Whitley446dd272001-03-02 20:00:54 +00001303
Mark Whitley446dd272001-03-02 20:00:54 +00001304#endif