blob: b78368e7bf8106d251678cff5edc5aa68f8337a5 [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
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +000026#include "busybox.h"
Manuel Novoa III cad53642003-03-19 09:13:01 +000027#include <stddef.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000028#include <termios.h>
29#include <sys/ioctl.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000030
31#include <sys/param.h>
32#include <unistd.h>
33
34#ifndef STDIN_FILENO
35# define STDIN_FILENO 0
36#endif
37
38#ifndef STDOUT_FILENO
39# define STDOUT_FILENO 1
40#endif
41
42#include <stdlib.h>
43#include <string.h>
44#include <assert.h>
45#include <ctype.h>
46#include <errno.h>
47#include <limits.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000048#include <fcntl.h>
49
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
Rob Landleybc68cd12006-03-10 19:22:06 +0000346enum {
347 NUM_mode_info =
348 (sizeof(mode_info) / sizeof(struct mode_info))
349};
Eric Andersen98e599c2001-02-14 18:47:33 +0000350
351/* Control character settings. */
352struct control_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000353 const char *name; /* Name given on command line. */
354 unsigned char saneval; /* Value to set for `stty sane'. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000355 unsigned char offset; /* Offset in c_cc. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000356};
357
358/* Control characters. */
359
Mark Whitley446dd272001-03-02 20:00:54 +0000360static const struct control_info control_info[] = {
361 {"intr", CINTR, VINTR},
362 {"quit", CQUIT, VQUIT},
363 {"erase", CERASE, VERASE},
364 {"kill", CKILL, VKILL},
365 {stty_eof, CEOF, VEOF},
366 {stty_eol, CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000367#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000368 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000369#endif
370#ifdef VSWTCH
Mark Whitley446dd272001-03-02 20:00:54 +0000371 {stty_swtch, CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000372#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000373 {"start", CSTART, VSTART},
374 {"stop", CSTOP, VSTOP},
375 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000376#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000377 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000378#endif
379#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000380 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000381#endif
382#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000383 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000384#endif
385#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000386 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000387#endif
388#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000389 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000390#endif
391#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000392 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000393#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000394 /* These must be last because of the display routines. */
Mark Whitley446dd272001-03-02 20:00:54 +0000395 {stty_min, 1, VMIN},
396 {stty_time, 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000397};
398
Rob Landleybc68cd12006-03-10 19:22:06 +0000399enum {
400 NUM_control_info =
401 (sizeof(control_info) / sizeof(struct control_info))
402};
Eric Andersen98e599c2001-02-14 18:47:33 +0000403
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000404#define EMT(t) ((enum mode_type)(t))
Eric Andersen98e599c2001-02-14 18:47:33 +0000405
Mark Whitley446dd272001-03-02 20:00:54 +0000406static const char * visible(unsigned int ch);
Mark Whitley446dd272001-03-02 20:00:54 +0000407static int recover_mode(char *arg, struct termios *mode);
408static int screen_columns(void);
409static int set_mode(const struct mode_info *info,
410 int reversed, struct termios *mode);
411static speed_t string_to_baud(const char *arg);
412static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000413static void display_all(struct termios *mode);
414static void display_changed(struct termios *mode);
415static void display_recoverable(struct termios *mode);
Mark Whitley446dd272001-03-02 20:00:54 +0000416static void display_speed(struct termios *mode, int fancy);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000417static void display_window_size(int fancy);
Mark Whitley446dd272001-03-02 20:00:54 +0000418static void sane_mode(struct termios *mode);
419static void set_control_char(const struct control_info *info,
420 const char *arg, struct termios *mode);
421static void set_speed(enum speed_setting type,
422 const char *arg, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000423static void set_window_size(int rows, int cols);
Eric Andersen8876fb22003-06-20 09:01:58 +0000424
425static const char *device_name;
426
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000427static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000428{
429 bb_perror_msg_and_die(fmt, device_name);
430}
431
Eric Andersen98e599c2001-02-14 18:47:33 +0000432
433/* The width of the screen, for output wrapping. */
434static int max_col;
435
436/* Current position, to know when to wrap. */
437static int current_col;
438
439/* Print format string MESSAGE and optional args.
440 Wrap to next line first if it won't fit.
441 Print a space first unless MESSAGE will start a new line. */
442
443static void wrapf(const char *message, ...)
444{
445 va_list args;
Mark Whitley446dd272001-03-02 20:00:54 +0000446 char buf[1024]; /* Plenty long for our needs. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000447 int buflen;
448
449 va_start(args, message);
450 vsprintf(buf, message, args);
451 va_end(args);
452 buflen = strlen(buf);
453 if (current_col + (current_col > 0) + buflen >= max_col) {
454 putchar('\n');
455 current_col = 0;
456 }
457 if (current_col > 0) {
458 putchar(' ');
459 current_col++;
460 }
461 fputs(buf, stdout);
462 current_col += buflen;
463}
464
465static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000466 {"b", 512 },
467 {"k", 1024},
468 {"B", 1024},
469 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000470};
471
Mark Whitley446dd272001-03-02 20:00:54 +0000472#ifndef TEST
Rob Landleydfba7412006-03-06 20:47:33 +0000473int stty_main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000474#else
Rob Landleydfba7412006-03-06 20:47:33 +0000475int main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000476#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000477{
478 struct termios mode;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000479 void (*output_func)(struct termios *);
Mark Whitley446dd272001-03-02 20:00:54 +0000480 int optc;
481 int require_set_attr;
482 int speed_was_set;
483 int verbose_output;
484 int recoverable_output;
485 int k;
486 int noargs = 1;
487 char * file_name = NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +0000488
Manuel Novoa III cad53642003-03-19 09:13:01 +0000489 output_func = display_changed;
Eric Andersen98e599c2001-02-14 18:47:33 +0000490 verbose_output = 0;
491 recoverable_output = 0;
492
493 /* Don't print error messages for unrecognized options. */
494 opterr = 0;
495
496 while ((optc = getopt(argc, argv, "agF:")) != -1) {
497 switch (optc) {
498 case 'a':
499 verbose_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000500 output_func = display_all;
Eric Andersen98e599c2001-02-14 18:47:33 +0000501 break;
502
503 case 'g':
504 recoverable_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000505 output_func = display_recoverable;
Eric Andersen98e599c2001-02-14 18:47:33 +0000506 break;
507
508 case 'F':
509 if (file_name)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000510 bb_error_msg_and_die("only one device may be specified");
Eric Andersen98e599c2001-02-14 18:47:33 +0000511 file_name = optarg;
512 break;
513
Mark Whitley446dd272001-03-02 20:00:54 +0000514 default: /* unrecognized option */
Eric Andersen98e599c2001-02-14 18:47:33 +0000515 noargs = 0;
516 break;
517 }
518
519 if (noargs == 0)
520 break;
521 }
522
523 if (optind < argc)
524 noargs = 0;
525
526 /* Specifying both -a and -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000527 if (verbose_output & recoverable_output)
528 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
Eric Andersen98e599c2001-02-14 18:47:33 +0000529
530 /* Specifying any other arguments with -a or -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000531 if (~noargs & (verbose_output | recoverable_output))
532 bb_error_msg_and_die ("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000533
534 /* FIXME: it'd be better not to open the file until we've verified
535 that all arguments are valid. Otherwise, we could end up doing
536 only some of the requested operations and then failing, probably
537 leaving things in an undesirable state. */
538
539 if (file_name) {
540 int fdflags;
541
542 device_name = file_name;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000543 fclose(stdin);
544 bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
545 if ((fdflags = fcntl(STDIN_FILENO, F_GETFL)) == -1
546 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Eric Andersen8876fb22003-06-20 09:01:58 +0000547 perror_on_device("%s: couldn't reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +0000548 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000549 device_name = bb_msg_standard_input;
Eric Andersen98e599c2001-02-14 18:47:33 +0000550 }
551
552 /* Initialize to all zeroes so there is no risk memcmp will report a
553 spurious difference in an uninitialized portion of the structure. */
554 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000555 if (tcgetattr(STDIN_FILENO, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000556 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000557
Manuel Novoa III cad53642003-03-19 09:13:01 +0000558 if (verbose_output | recoverable_output | noargs) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000559 max_col = screen_columns();
560 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000561 output_func(&mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000562 return EXIT_SUCCESS;
563 }
564
565 speed_was_set = 0;
566 require_set_attr = 0;
Eric Andersenfc059092002-06-06 11:35:29 +0000567 k = 0;
568 while (++k < argc) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000569 int match_found = 0;
570 int reversed = 0;
571 int i;
572
573 if (argv[k][0] == '-') {
Eric Andersenfc059092002-06-06 11:35:29 +0000574 char *find_dev_opt;
575
Eric Andersen98e599c2001-02-14 18:47:33 +0000576 ++argv[k];
Eric Andersenfc059092002-06-06 11:35:29 +0000577
578 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
579 Find the options that have been parsed. This is really
580 gross, but it's needed because stty SETTINGS look like options to
581 getopt(), so we need to work around things in a really horrible
582 way. If any new options are ever added to stty, the short option
583 MUST NOT be a letter which is the first letter of one of the
584 possible stty settings.
585 */
586 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
587 if(find_dev_opt) {
588 if(find_dev_opt[1]==0) /* -*F /dev/foo */
589 k++; /* skip /dev/foo */
590 continue; /* else -*F/dev/foo - no skip */
591 }
592 if(argv[k][0]=='a' || argv[k][0]=='g')
593 continue;
594 /* Is not options - is reverse params */
Eric Andersen98e599c2001-02-14 18:47:33 +0000595 reversed = 1;
596 }
Mark Whitley446dd272001-03-02 20:00:54 +0000597 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000598 if (STREQ(argv[k], mode_info[i].name)) {
599 match_found = set_mode(&mode_info[i], reversed, &mode);
600 require_set_attr = 1;
601 break;
602 }
Mark Whitley446dd272001-03-02 20:00:54 +0000603
604 if (match_found == 0 && reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000605 bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000606
607 if (match_found == 0)
608 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000609 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000610 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000611 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000612 match_found = 1;
613 ++k;
614 set_control_char(&control_info[i], argv[k], &mode);
615 require_set_attr = 1;
616 break;
617 }
Mark Whitley446dd272001-03-02 20:00:54 +0000618
Eric Andersen98e599c2001-02-14 18:47:33 +0000619 if (match_found == 0) {
620 if (STREQ(argv[k], "ispeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000621 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000622 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000623 ++k;
624 set_speed(input_speed, argv[k], &mode);
625 speed_was_set = 1;
626 require_set_attr = 1;
627 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000628 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000629 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000630 ++k;
631 set_speed(output_speed, argv[k], &mode);
632 speed_was_set = 1;
633 require_set_attr = 1;
634 }
635#ifdef TIOCGWINSZ
636 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000637 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000638 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000639 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000640 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000641 -1);
Eric Andersen98e599c2001-02-14 18:47:33 +0000642 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000643 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000644 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000645 ++k;
646 set_window_size(-1,
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000647 (int) bb_xparse_number(argv[k], stty_suffixes));
Eric Andersen98e599c2001-02-14 18:47:33 +0000648 } else if (STREQ(argv[k], "size")) {
649 max_col = screen_columns();
650 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000651 display_window_size(0);
Eric Andersen98e599c2001-02-14 18:47:33 +0000652 }
653#endif
654#ifdef HAVE_C_LINE
655 else if (STREQ(argv[k], "line")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000656 if (k == argc - 1)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000657 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000658 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000659 mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000660 require_set_attr = 1;
661 }
662#endif
663 else if (STREQ(argv[k], "speed")) {
664 max_col = screen_columns();
665 display_speed(&mode, 0);
Mark Whitley446dd272001-03-02 20:00:54 +0000666 } else if (recover_mode(argv[k], &mode) == 1)
667 require_set_attr = 1;
668 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000669 set_speed(both_speeds, argv[k], &mode);
670 speed_was_set = 1;
671 require_set_attr = 1;
Mark Whitley446dd272001-03-02 20:00:54 +0000672 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000673 bb_error_msg_and_die("invalid argument `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000674 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000675 }
676
677 if (require_set_attr) {
678 struct termios new_mode;
679
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000680 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000681 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000682
683 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
684 it performs *any* of the requested operations. This means it
685 can report `success' when it has actually failed to perform
686 some proper subset of the requested operations. To detect
687 this partial failure, get the current terminal attributes and
688 compare them to the requested ones. */
689
690 /* Initialize to all zeroes so there is no risk memcmp will report a
691 spurious difference in an uninitialized portion of the structure. */
692 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000693 if (tcgetattr(STDIN_FILENO, &new_mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000694 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000695
696 /* Normally, one shouldn't use memcmp to compare structures that
697 may have `holes' containing uninitialized data, but we have been
698 careful to initialize the storage of these two variables to all
699 zeroes. One might think it more efficient simply to compare the
700 modified fields, but that would require enumerating those fields --
701 and not all systems have the same fields in this structure. */
702
703 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
704#ifdef CIBAUD
705 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
706 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
707 sometimes (m1 != m2). The only difference is in the four bits
708 of the c_cflag field corresponding to the baud rate. To save
709 Sun users a little confusion, don't report an error if this
710 happens. But suppress the error only if we haven't tried to
711 set the baud rate explicitly -- otherwise we'd never give an
712 error for a true failure to set the baud rate. */
713
714 new_mode.c_cflag &= (~CIBAUD);
715 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
716#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000717 perror_on_device ("%s: unable to perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +0000718 }
719 }
720
721 return EXIT_SUCCESS;
722}
723
724/* Return 0 if not applied because not reversible; otherwise return 1. */
725
726static int
727set_mode(const struct mode_info *info, int reversed, struct termios *mode)
728{
729 tcflag_t *bitsp;
730
731 if (reversed && (info->flags & REV) == 0)
732 return 0;
733
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000734 bitsp = mode_type_flag(EMT(info->type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000735
736 if (bitsp == NULL) {
737 /* Combination mode. */
738 if (info->name == evenp || info->name == parity) {
739 if (reversed)
740 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
741 else
742 mode->c_cflag =
743 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
744 } else if (info->name == stty_oddp) {
745 if (reversed)
746 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
747 else
748 mode->c_cflag =
749 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
750 } else if (info->name == stty_nl) {
751 if (reversed) {
752 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
753 mode->c_oflag = (mode->c_oflag
754#ifdef ONLCR
755 | ONLCR
756#endif
757 )
758#ifdef OCRNL
759 & ~OCRNL
760#endif
761#ifdef ONLRET
762 & ~ONLRET
763#endif
764 ;
765 } else {
766 mode->c_iflag = mode->c_iflag & ~ICRNL;
767#ifdef ONLCR
768 mode->c_oflag = mode->c_oflag & ~ONLCR;
769#endif
770 }
771 } else if (info->name == stty_ek) {
772 mode->c_cc[VERASE] = CERASE;
773 mode->c_cc[VKILL] = CKILL;
774 } else if (info->name == stty_sane)
775 sane_mode(mode);
776 else if (info->name == cbreak) {
777 if (reversed)
778 mode->c_lflag |= ICANON;
779 else
780 mode->c_lflag &= ~ICANON;
781 } else if (info->name == stty_pass8) {
782 if (reversed) {
783 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
784 mode->c_iflag |= ISTRIP;
785 } else {
786 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
787 mode->c_iflag &= ~ISTRIP;
788 }
789 } else if (info->name == litout) {
790 if (reversed) {
791 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
792 mode->c_iflag |= ISTRIP;
793 mode->c_oflag |= OPOST;
794 } else {
795 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
796 mode->c_iflag &= ~ISTRIP;
797 mode->c_oflag &= ~OPOST;
798 }
799 } else if (info->name == raw || info->name == cooked) {
800 if ((info->name[0] == 'r' && reversed)
801 || (info->name[0] == 'c' && !reversed)) {
802 /* Cooked mode. */
803 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
804 mode->c_oflag |= OPOST;
805 mode->c_lflag |= ISIG | ICANON;
806#if VMIN == VEOF
807 mode->c_cc[VEOF] = CEOF;
808#endif
809#if VTIME == VEOL
810 mode->c_cc[VEOL] = CEOL;
811#endif
812 } else {
813 /* Raw mode. */
814 mode->c_iflag = 0;
815 mode->c_oflag &= ~OPOST;
816 mode->c_lflag &= ~(ISIG | ICANON
817#ifdef XCASE
818 | XCASE
819#endif
820 );
821 mode->c_cc[VMIN] = 1;
822 mode->c_cc[VTIME] = 0;
823 }
824 }
825#ifdef IXANY
826 else if (info->name == decctlq) {
827 if (reversed)
828 mode->c_iflag |= IXANY;
829 else
830 mode->c_iflag &= ~IXANY;
831 }
832#endif
833#ifdef TABDLY
834 else if (info->name == stty_tabs) {
835 if (reversed)
836 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
837 else
838 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
839 }
840#else
841# ifdef OXTABS
842 else if (info->name == stty_tabs) {
843 if (reversed)
844 mode->c_oflag = mode->c_oflag | OXTABS;
845 else
846 mode->c_oflag = mode->c_oflag & ~OXTABS;
847 }
848# endif
849#endif
850#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
851 else if (info->name == stty_lcase || info->name == stty_LCASE) {
852 if (reversed) {
853 mode->c_lflag &= ~XCASE;
854 mode->c_iflag &= ~IUCLC;
855 mode->c_oflag &= ~OLCUC;
856 } else {
857 mode->c_lflag |= XCASE;
858 mode->c_iflag |= IUCLC;
859 mode->c_oflag |= OLCUC;
860 }
861 }
862#endif
863 else if (info->name == stty_crt)
864 mode->c_lflag |= ECHOE
865#ifdef ECHOCTL
866 | ECHOCTL
867#endif
868#ifdef ECHOKE
869 | ECHOKE
870#endif
871 ;
872 else if (info->name == stty_dec) {
Mark Whitley446dd272001-03-02 20:00:54 +0000873 mode->c_cc[VINTR] = 3; /* ^C */
874 mode->c_cc[VERASE] = 127; /* DEL */
875 mode->c_cc[VKILL] = 21; /* ^U */
Eric Andersen98e599c2001-02-14 18:47:33 +0000876 mode->c_lflag |= ECHOE
877#ifdef ECHOCTL
878 | ECHOCTL
879#endif
880#ifdef ECHOKE
881 | ECHOKE
882#endif
883 ;
884#ifdef IXANY
885 mode->c_iflag &= ~IXANY;
886#endif
887 }
888 } else if (reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000889 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000890 else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000891 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000892
893 return 1;
894}
895
896static void
897set_control_char(const struct control_info *info, const char *arg,
898 struct termios *mode)
899{
900 unsigned char value;
901
902 if (info->name == stty_min || info->name == stty_time)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000903 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000904 else if (arg[0] == '\0' || arg[1] == '\0')
905 value = arg[0];
906 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
907 value = _POSIX_VDISABLE;
Mark Whitley446dd272001-03-02 20:00:54 +0000908 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000909 if (arg[1] == '?')
910 value = 127;
911 else
Mark Whitley446dd272001-03-02 20:00:54 +0000912 value = arg[1] & ~0140; /* Non-letters get weird results. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000913 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000914 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000915 mode->c_cc[info->offset] = value;
916}
917
918static void
919set_speed(enum speed_setting type, const char *arg, struct termios *mode)
920{
921 speed_t baud;
922
923 baud = string_to_baud(arg);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000924
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000925 if (type != output_speed) { /* either input or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000926 cfsetispeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000927 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000928 if (type != input_speed) { /* either output or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000929 cfsetospeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000930 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000931}
932
933#ifdef TIOCGWINSZ
934
935static int get_win_size(int fd, struct winsize *win)
936{
937 int err = ioctl(fd, TIOCGWINSZ, (char *) win);
938
939 return err;
940}
941
942static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000943set_window_size(int rows, int cols)
Eric Andersen98e599c2001-02-14 18:47:33 +0000944{
945 struct winsize win;
946
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000947 if (get_win_size(STDIN_FILENO, &win)) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000948 if (errno != EINVAL)
Eric Andersen8876fb22003-06-20 09:01:58 +0000949 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000950 memset(&win, 0, sizeof(win));
951 }
952
953 if (rows >= 0)
954 win.ws_row = rows;
955 if (cols >= 0)
956 win.ws_col = cols;
957
958# ifdef TIOCSSIZE
959 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
960 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
961 This comment from sys/ttold.h describes Sun's twisted logic - a better
962 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
963 At any rate, the problem is gone in Solaris 2.x. */
964
965 if (win.ws_row == 0 || win.ws_col == 0) {
966 struct ttysize ttysz;
967
968 ttysz.ts_lines = win.ws_row;
969 ttysz.ts_cols = win.ws_col;
970
Manuel Novoa III cad53642003-03-19 09:13:01 +0000971 win.ws_row = win.ws_col = 1;
Eric Andersen98e599c2001-02-14 18:47:33 +0000972
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000973 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
974 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000975 perror_on_device("%s");
Glenn L McGrathca1c1af2004-09-15 03:24:32 +0000976 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000977 return;
978 }
979# endif
980
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000981 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Eric Andersen8876fb22003-06-20 09:01:58 +0000982 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000983}
984
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000985static void display_window_size(int fancy)
Eric Andersen98e599c2001-02-14 18:47:33 +0000986{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000987 const char *fmt_str = "%s" "\0" "%s: no size information for this device";
Eric Andersen98e599c2001-02-14 18:47:33 +0000988 struct winsize win;
989
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000990 if (get_win_size(STDIN_FILENO, &win)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000991 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000992 perror_on_device(fmt_str);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000993 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000994 } else {
995 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
996 win.ws_row, win.ws_col);
997 if (!fancy)
998 current_col = 0;
999 }
1000}
1001#endif
1002
1003static int screen_columns(void)
1004{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001005 int columns;
1006 const char *s;
1007
Eric Andersen98e599c2001-02-14 18:47:33 +00001008#ifdef TIOCGWINSZ
1009 struct winsize win;
1010
1011 /* With Solaris 2.[123], this ioctl fails and errno is set to
1012 EINVAL for telnet (but not rlogin) sessions.
1013 On ISC 3.0, it fails for the console and the serial port
1014 (but it works for ptys).
1015 It can also fail on any system when stdout isn't a tty.
1016 In case of any failure, just use the default. */
1017 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1018 return win.ws_col;
1019#endif
1020
Manuel Novoa III cad53642003-03-19 09:13:01 +00001021 columns = 80;
1022 if ((s = getenv("COLUMNS"))) {
1023 columns = atoi(s);
1024 }
1025 return columns;
Eric Andersen98e599c2001-02-14 18:47:33 +00001026}
1027
1028static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1029{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001030 static const unsigned char tcflag_offsets[] = {
1031 offsetof(struct termios, c_cflag), /* control */
1032 offsetof(struct termios, c_iflag), /* input */
1033 offsetof(struct termios, c_oflag), /* output */
1034 offsetof(struct termios, c_lflag) /* local */
1035 };
Eric Andersen98e599c2001-02-14 18:47:33 +00001036
Manuel Novoa III cad53642003-03-19 09:13:01 +00001037 if (((unsigned int) type) <= local) {
1038 return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
Eric Andersen98e599c2001-02-14 18:47:33 +00001039 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001040 return NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +00001041}
1042
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001043static void display_changed(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001044{
1045 int i;
1046 int empty_line;
1047 tcflag_t *bitsp;
1048 unsigned long mask;
1049 enum mode_type prev_type = control;
1050
1051 display_speed(mode, 1);
1052#ifdef HAVE_C_LINE
1053 wrapf("line = %d;", mode->c_line);
1054#endif
1055 putchar('\n');
1056 current_col = 0;
1057
1058 empty_line = 1;
1059 for (i = 0; control_info[i].name != stty_min; ++i) {
1060 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1061 continue;
1062 /* If swtch is the same as susp, don't print both. */
1063#if VSWTCH == VSUSP
1064 if (control_info[i].name == stty_swtch)
1065 continue;
1066#endif
1067 /* If eof uses the same slot as min, only print whichever applies. */
1068#if VEOF == VMIN
1069 if ((mode->c_lflag & ICANON) == 0
1070 && (control_info[i].name == stty_eof
1071 || control_info[i].name == stty_eol)) continue;
1072#endif
1073
1074 empty_line = 0;
1075 wrapf("%s = %s;", control_info[i].name,
1076 visible(mode->c_cc[control_info[i].offset]));
1077 }
1078 if ((mode->c_lflag & ICANON) == 0) {
1079 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1080 (int) mode->c_cc[VTIME]);
1081 } else if (empty_line == 0)
1082 putchar('\n');
1083 current_col = 0;
1084
1085 empty_line = 1;
1086 for (i = 0; i < NUM_mode_info; ++i) {
1087 if (mode_info[i].flags & OMIT)
1088 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001089 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001090 if (empty_line == 0) {
1091 putchar('\n');
1092 current_col = 0;
1093 empty_line = 1;
1094 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001095 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001096 }
1097
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001098 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001099 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1100 if ((*bitsp & mask) == mode_info[i].bits) {
1101 if (mode_info[i].flags & SANE_UNSET) {
1102 wrapf("%s", mode_info[i].name);
1103 empty_line = 0;
1104 }
1105 }
1106 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1107 (SANE_SET | REV)) {
1108 wrapf("-%s", mode_info[i].name);
1109 empty_line = 0;
1110 }
1111 }
1112 if (empty_line == 0)
1113 putchar('\n');
1114 current_col = 0;
1115}
1116
1117static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001118display_all(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001119{
1120 int i;
1121 tcflag_t *bitsp;
1122 unsigned long mask;
1123 enum mode_type prev_type = control;
1124
1125 display_speed(mode, 1);
1126#ifdef TIOCGWINSZ
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001127 display_window_size(1);
Eric Andersen98e599c2001-02-14 18:47:33 +00001128#endif
1129#ifdef HAVE_C_LINE
1130 wrapf("line = %d;", mode->c_line);
1131#endif
1132 putchar('\n');
1133 current_col = 0;
1134
1135 for (i = 0; control_info[i].name != stty_min; ++i) {
1136 /* If swtch is the same as susp, don't print both. */
1137#if VSWTCH == VSUSP
1138 if (control_info[i].name == stty_swtch)
1139 continue;
1140#endif
1141 /* If eof uses the same slot as min, only print whichever applies. */
1142#if VEOF == VMIN
1143 if ((mode->c_lflag & ICANON) == 0
1144 && (control_info[i].name == stty_eof
1145 || control_info[i].name == stty_eol)) continue;
1146#endif
1147 wrapf("%s = %s;", control_info[i].name,
1148 visible(mode->c_cc[control_info[i].offset]));
1149 }
1150#if VEOF == VMIN
1151 if ((mode->c_lflag & ICANON) == 0)
1152#endif
1153 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1154 if (current_col != 0)
1155 putchar('\n');
1156 current_col = 0;
1157
1158 for (i = 0; i < NUM_mode_info; ++i) {
1159 if (mode_info[i].flags & OMIT)
1160 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001161 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001162 putchar('\n');
1163 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001164 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001165 }
1166
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001167 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001168 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1169 if ((*bitsp & mask) == mode_info[i].bits)
1170 wrapf("%s", mode_info[i].name);
1171 else if (mode_info[i].flags & REV)
1172 wrapf("-%s", mode_info[i].name);
1173 }
1174 putchar('\n');
1175 current_col = 0;
1176}
1177
1178static void display_speed(struct termios *mode, int fancy)
1179{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001180 unsigned long ispeed, ospeed;
1181 const char *fmt_str =
1182 "%lu %lu\n\0" "ispeed %lu baud; ospeed %lu baud;\0"
1183 "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
1184
1185 ospeed = ispeed = cfgetispeed(mode);
1186 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001187 ispeed = ospeed; /* in case ispeed was 0 */
Manuel Novoa III cad53642003-03-19 09:13:01 +00001188 fmt_str += 43;
1189 }
1190 if (fancy) {
1191 fmt_str += 9;
1192 }
Rob Landley290fcb42006-06-18 23:59:03 +00001193 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001194 if (!fancy)
1195 current_col = 0;
1196}
1197
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001198static void display_recoverable(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001199{
1200 int i;
1201
1202 printf("%lx:%lx:%lx:%lx",
1203 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1204 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1205 for (i = 0; i < NCCS; ++i)
1206 printf(":%x", (unsigned int) mode->c_cc[i]);
1207 putchar('\n');
1208}
1209
1210static int recover_mode(char *arg, struct termios *mode)
1211{
1212 int i, n;
1213 unsigned int chr;
1214 unsigned long iflag, oflag, cflag, lflag;
1215
1216 /* Scan into temporaries since it is too much trouble to figure out
1217 the right format for `tcflag_t'. */
1218 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1219 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1220 return 0;
1221 mode->c_iflag = iflag;
1222 mode->c_oflag = oflag;
1223 mode->c_cflag = cflag;
1224 mode->c_lflag = lflag;
1225 arg += n;
1226 for (i = 0; i < NCCS; ++i) {
1227 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1228 return 0;
1229 mode->c_cc[i] = chr;
1230 arg += n;
1231 }
1232
1233 /* Fail if there are too many fields. */
1234 if (*arg != '\0')
1235 return 0;
1236
1237 return 1;
1238}
1239
Eric Andersen98e599c2001-02-14 18:47:33 +00001240static speed_t string_to_baud(const char *arg)
1241{
Rob Landley290fcb42006-06-18 23:59:03 +00001242 return tty_value_to_baud(bb_xparse_number(arg, 0));
Eric Andersen98e599c2001-02-14 18:47:33 +00001243}
1244
1245static void sane_mode(struct termios *mode)
1246{
1247 int i;
1248 tcflag_t *bitsp;
1249
1250 for (i = 0; i < NUM_control_info; ++i) {
1251#if VMIN == VEOF
1252 if (control_info[i].name == stty_min)
1253 break;
1254#endif
1255 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1256 }
1257
1258 for (i = 0; i < NUM_mode_info; ++i) {
1259 if (mode_info[i].flags & SANE_SET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001260 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001261 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1262 | mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001263 } else if (mode_info[i].flags & SANE_UNSET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001264 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001265 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1266 & ~mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001267 }
1268 }
1269}
1270
1271/* Return a string that is the printable representation of character CH. */
1272/* Adapted from `cat' by Torbjorn Granlund. */
1273
1274static const char *visible(unsigned int ch)
1275{
1276 static char buf[10];
1277 char *bpout = buf;
1278
Manuel Novoa III cad53642003-03-19 09:13:01 +00001279 if (ch == _POSIX_VDISABLE) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001280 return "<undef>";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001281 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001282
Manuel Novoa III cad53642003-03-19 09:13:01 +00001283 if (ch >= 128) {
1284 ch -= 128;
1285 *bpout++ = 'M';
1286 *bpout++ = '-';
1287 }
1288
1289 if (ch < 32) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001290 *bpout++ = '^';
1291 *bpout++ = ch + 64;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001292 } else if (ch < 127) {
1293 *bpout++ = ch;
1294 } else {
1295 *bpout++ = '^';
1296 *bpout++ = '?';
Eric Andersen98e599c2001-02-14 18:47:33 +00001297 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001298
Eric Andersen98e599c2001-02-14 18:47:33 +00001299 *bpout = '\0';
1300 return (const char *) buf;
1301}
1302
Mark Whitley446dd272001-03-02 20:00:54 +00001303#ifdef TEST
Mark Whitley446dd272001-03-02 20:00:54 +00001304
Manuel Novoa III cad53642003-03-19 09:13:01 +00001305const char *bb_applet_name = "stty";
Mark Whitley446dd272001-03-02 20:00:54 +00001306
Mark Whitley446dd272001-03-02 20:00:54 +00001307#endif