blob: 21759631e8e0d692ee285bffdf4e5387b5533a0f [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>
47#include <memory.h>
48#include <fcntl.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000049#include "busybox.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000050
51#define STREQ(a, b) (strcmp ((a), (b)) == 0)
52
53
54#ifndef _POSIX_VDISABLE
55# define _POSIX_VDISABLE ((unsigned char) 0)
56#endif
57
58#define Control(c) ((c) & 0x1f)
59/* Canonical values for control characters. */
60#ifndef CINTR
61# define CINTR Control ('c')
62#endif
63#ifndef CQUIT
64# define CQUIT 28
65#endif
66#ifndef CERASE
67# define CERASE 127
68#endif
69#ifndef CKILL
70# define CKILL Control ('u')
71#endif
72#ifndef CEOF
73# define CEOF Control ('d')
74#endif
75#ifndef CEOL
76# define CEOL _POSIX_VDISABLE
77#endif
78#ifndef CSTART
79# define CSTART Control ('q')
80#endif
81#ifndef CSTOP
82# define CSTOP Control ('s')
83#endif
84#ifndef CSUSP
85# define CSUSP Control ('z')
86#endif
87#if defined(VEOL2) && !defined(CEOL2)
88# define CEOL2 _POSIX_VDISABLE
89#endif
90/* ISC renamed swtch to susp for termios, but we'll accept either name. */
91#if defined(VSUSP) && !defined(VSWTCH)
92# define VSWTCH VSUSP
93# define CSWTCH CSUSP
94#endif
95#if defined(VSWTCH) && !defined(CSWTCH)
96# define CSWTCH _POSIX_VDISABLE
97#endif
98
99/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
100 So the default is to disable `swtch.' */
101#if defined (__sparc__) && defined (__svr4__)
102# undef CSWTCH
103# define CSWTCH _POSIX_VDISABLE
104#endif
105
Mark Whitley446dd272001-03-02 20:00:54 +0000106#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000107# define VWERASE VWERSE
108#endif
109#if defined(VDSUSP) && !defined (CDSUSP)
110# define CDSUSP Control ('y')
111#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000112#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000113# define VREPRINT VRPRNT
114#endif
115#if defined(VREPRINT) && !defined(CRPRNT)
116# define CRPRNT Control ('r')
117#endif
118#if defined(VWERASE) && !defined(CWERASE)
119# define CWERASE Control ('w')
120#endif
121#if defined(VLNEXT) && !defined(CLNEXT)
122# define CLNEXT Control ('v')
123#endif
124#if defined(VDISCARD) && !defined(VFLUSHO)
125# define VFLUSHO VDISCARD
126#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000127#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000128# define VFLUSHO VFLUSH
129#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000130#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000131# define ECHOCTL CTLECH
132#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000133#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000134# define ECHOCTL TCTLECH
135#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000136#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000137# define ECHOKE CRTKIL
138#endif
139#if defined(VFLUSHO) && !defined(CFLUSHO)
140# define CFLUSHO Control ('o')
141#endif
142#if defined(VSTATUS) && !defined(CSTATUS)
143# define CSTATUS Control ('t')
144#endif
145
146/* Which speeds to set. */
147enum speed_setting {
148 input_speed, output_speed, both_speeds
149};
150
Eric Andersen98e599c2001-02-14 18:47:33 +0000151/* Which member(s) of `struct termios' a mode uses. */
152enum mode_type {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000153 /* Do NOT change the order or values, as mode_type_flag()
154 * depends on them. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000155 control, input, output, local, combination
156};
157
158
Mark Whitley446dd272001-03-02 20:00:54 +0000159static const char evenp [] = "evenp";
160static const char raw [] = "raw";
161static const char stty_min [] = "min";
162static const char stty_time [] = "time";
Eric Andersen98e599c2001-02-14 18:47:33 +0000163static const char stty_swtch[] = "swtch";
Mark Whitley446dd272001-03-02 20:00:54 +0000164static const char stty_eol [] = "eol";
165static const char stty_eof [] = "eof";
166static const char parity [] = "parity";
167static const char stty_oddp [] = "oddp";
168static const char stty_nl [] = "nl";
169static const char stty_ek [] = "ek";
170static const char stty_sane [] = "sane";
171static const char cbreak [] = "cbreak";
Eric Andersen98e599c2001-02-14 18:47:33 +0000172static const char stty_pass8[] = "pass8";
Mark Whitley446dd272001-03-02 20:00:54 +0000173static const char litout [] = "litout";
174static const char cooked [] = "cooked";
175static const char decctlq [] = "decctlq";
176static const char stty_tabs [] = "tabs";
Eric Andersen98e599c2001-02-14 18:47:33 +0000177static const char stty_lcase[] = "lcase";
178static const char stty_LCASE[] = "LCASE";
Mark Whitley446dd272001-03-02 20:00:54 +0000179static const char stty_crt [] = "crt";
180static const char stty_dec [] = "dec";
Eric Andersen98e599c2001-02-14 18:47:33 +0000181
182
183/* Flags for `struct mode_info'. */
Mark Whitley446dd272001-03-02 20:00:54 +0000184#define SANE_SET 1 /* Set in `sane' mode. */
185#define SANE_UNSET 2 /* Unset in `sane' mode. */
186#define REV 4 /* Can be turned off by prepending `-'. */
187#define OMIT 8 /* Don't display value. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000188
189/* Each mode. */
190struct mode_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000191 const char *name; /* Name given on command line. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000192 /* enum mode_type type; */
193 char type; /* Which structure element to change. */
Mark Whitley446dd272001-03-02 20:00:54 +0000194 char flags; /* Setting and display options. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000195 unsigned short mask; /* Other bits to turn off for this mode. */
Mark Whitley446dd272001-03-02 20:00:54 +0000196 unsigned long bits; /* Bits to set for this mode. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000197};
198
Manuel Novoa III cad53642003-03-19 09:13:01 +0000199#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
200
Mark Whitley446dd272001-03-02 20:00:54 +0000201static const struct mode_info mode_info[] = {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000202 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
203 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
204 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
205 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
206 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
207 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
208 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
209 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
210 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
211 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
212 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000213#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000214 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000215#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000216 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
217 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
218 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
219 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
220 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
221 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
222 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
223 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
224 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
225 MI_ENTRY("ixon", input, REV, IXON, 0 ),
226 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
227 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000228#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000229 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000230#endif
231#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000232 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000233#endif
234#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000235 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000236#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000237 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000238#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000239 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000240#endif
241#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000242 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000243#endif
244#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000245 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000246#endif
247#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000248 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000249#endif
250#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000251 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000252#endif
253#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000254 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000255#endif
256#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000257 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000258#endif
259#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000260 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
261 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000262#endif
263#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000264 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
265 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
266 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
267 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000268#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000269
Eric Andersen98e599c2001-02-14 18:47:33 +0000270#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000271 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
272 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
273 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
274 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000275#else
276# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000277 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000278# endif
279#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000280
Eric Andersen98e599c2001-02-14 18:47:33 +0000281#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000282 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
283 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000284#endif
285#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000286 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
287 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000288#endif
289#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000290 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
291 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000292#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000293 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
294 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000295#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000296 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000297#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000298 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
299 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
300 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
301 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
302 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
303 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000304#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000305 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000306#endif
307#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000308 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000309#endif
310#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000311 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
312 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000313#endif
314#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000315 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
316 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000317#endif
318#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000319 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
320 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000321#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000322 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
323 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
324 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
325 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
326 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
327 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
328 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
329 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
330 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
331 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
332 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000333#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000334 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000335#endif
336#if defined (TABDLY) || defined (OXTABS)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000337 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000338#endif
339#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000340 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
341 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000342#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000343 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
344 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000345};
346
347static const int NUM_mode_info =
348
349 (sizeof(mode_info) / sizeof(struct mode_info));
350
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
399static const int NUM_control_info =
400 (sizeof(control_info) / sizeof(struct control_info));
401
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000402#define EMT(t) ((enum mode_type)(t))
Eric Andersen98e599c2001-02-14 18:47:33 +0000403
Mark Whitley446dd272001-03-02 20:00:54 +0000404static const char * visible(unsigned int ch);
Mark Whitley446dd272001-03-02 20:00:54 +0000405static int recover_mode(char *arg, struct termios *mode);
406static int screen_columns(void);
407static int set_mode(const struct mode_info *info,
408 int reversed, struct termios *mode);
409static speed_t string_to_baud(const char *arg);
410static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000411static void display_all(struct termios *mode);
412static void display_changed(struct termios *mode);
413static void display_recoverable(struct termios *mode);
Mark Whitley446dd272001-03-02 20:00:54 +0000414static void display_speed(struct termios *mode, int fancy);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000415static void display_window_size(int fancy);
Mark Whitley446dd272001-03-02 20:00:54 +0000416static void sane_mode(struct termios *mode);
417static void set_control_char(const struct control_info *info,
418 const char *arg, struct termios *mode);
419static void set_speed(enum speed_setting type,
420 const char *arg, struct termios *mode);
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000421static void set_window_size(int rows, int cols);
Eric Andersen8876fb22003-06-20 09:01:58 +0000422
423static const char *device_name;
424
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000425static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000426{
427 bb_perror_msg_and_die(fmt, device_name);
428}
429
Eric Andersen98e599c2001-02-14 18:47:33 +0000430
431/* The width of the screen, for output wrapping. */
432static int max_col;
433
434/* Current position, to know when to wrap. */
435static int current_col;
436
437/* Print format string MESSAGE and optional args.
438 Wrap to next line first if it won't fit.
439 Print a space first unless MESSAGE will start a new line. */
440
441static void wrapf(const char *message, ...)
442{
443 va_list args;
Mark Whitley446dd272001-03-02 20:00:54 +0000444 char buf[1024]; /* Plenty long for our needs. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000445 int buflen;
446
447 va_start(args, message);
448 vsprintf(buf, message, args);
449 va_end(args);
450 buflen = strlen(buf);
451 if (current_col + (current_col > 0) + buflen >= max_col) {
452 putchar('\n');
453 current_col = 0;
454 }
455 if (current_col > 0) {
456 putchar(' ');
457 current_col++;
458 }
459 fputs(buf, stdout);
460 current_col += buflen;
461}
462
463static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000464 {"b", 512 },
465 {"k", 1024},
466 {"B", 1024},
467 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000468};
469
Mark Whitley446dd272001-03-02 20:00:54 +0000470#ifndef TEST
Eric Andersen98e599c2001-02-14 18:47:33 +0000471extern int stty_main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000472#else
473extern int main(int argc, char **argv)
474#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000475{
476 struct termios mode;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000477 void (*output_func)(struct termios *);
Mark Whitley446dd272001-03-02 20:00:54 +0000478 int optc;
479 int require_set_attr;
480 int speed_was_set;
481 int verbose_output;
482 int recoverable_output;
483 int k;
484 int noargs = 1;
485 char * file_name = NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +0000486
Manuel Novoa III cad53642003-03-19 09:13:01 +0000487 output_func = display_changed;
Eric Andersen98e599c2001-02-14 18:47:33 +0000488 verbose_output = 0;
489 recoverable_output = 0;
490
491 /* Don't print error messages for unrecognized options. */
492 opterr = 0;
493
494 while ((optc = getopt(argc, argv, "agF:")) != -1) {
495 switch (optc) {
496 case 'a':
497 verbose_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000498 output_func = display_all;
Eric Andersen98e599c2001-02-14 18:47:33 +0000499 break;
500
501 case 'g':
502 recoverable_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000503 output_func = display_recoverable;
Eric Andersen98e599c2001-02-14 18:47:33 +0000504 break;
505
506 case 'F':
507 if (file_name)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000508 bb_error_msg_and_die("only one device may be specified");
Eric Andersen98e599c2001-02-14 18:47:33 +0000509 file_name = optarg;
510 break;
511
Mark Whitley446dd272001-03-02 20:00:54 +0000512 default: /* unrecognized option */
Eric Andersen98e599c2001-02-14 18:47:33 +0000513 noargs = 0;
514 break;
515 }
516
517 if (noargs == 0)
518 break;
519 }
520
521 if (optind < argc)
522 noargs = 0;
523
524 /* Specifying both -a and -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000525 if (verbose_output & recoverable_output)
526 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
Eric Andersen98e599c2001-02-14 18:47:33 +0000527
528 /* Specifying any other arguments with -a or -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000529 if (~noargs & (verbose_output | recoverable_output))
530 bb_error_msg_and_die ("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000531
532 /* FIXME: it'd be better not to open the file until we've verified
533 that all arguments are valid. Otherwise, we could end up doing
534 only some of the requested operations and then failing, probably
535 leaving things in an undesirable state. */
536
537 if (file_name) {
538 int fdflags;
539
540 device_name = file_name;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000541 fclose(stdin);
542 bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
543 if ((fdflags = fcntl(STDIN_FILENO, F_GETFL)) == -1
544 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Eric Andersen8876fb22003-06-20 09:01:58 +0000545 perror_on_device("%s: couldn't reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +0000546 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000547 device_name = bb_msg_standard_input;
Eric Andersen98e599c2001-02-14 18:47:33 +0000548 }
549
550 /* Initialize to all zeroes so there is no risk memcmp will report a
551 spurious difference in an uninitialized portion of the structure. */
552 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000553 if (tcgetattr(STDIN_FILENO, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000554 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000555
Manuel Novoa III cad53642003-03-19 09:13:01 +0000556 if (verbose_output | recoverable_output | noargs) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000557 max_col = screen_columns();
558 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000559 output_func(&mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000560 return EXIT_SUCCESS;
561 }
562
563 speed_was_set = 0;
564 require_set_attr = 0;
Eric Andersenfc059092002-06-06 11:35:29 +0000565 k = 0;
566 while (++k < argc) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000567 int match_found = 0;
568 int reversed = 0;
569 int i;
570
571 if (argv[k][0] == '-') {
Eric Andersenfc059092002-06-06 11:35:29 +0000572 char *find_dev_opt;
573
Eric Andersen98e599c2001-02-14 18:47:33 +0000574 ++argv[k];
Eric Andersenfc059092002-06-06 11:35:29 +0000575
576 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
577 Find the options that have been parsed. This is really
578 gross, but it's needed because stty SETTINGS look like options to
579 getopt(), so we need to work around things in a really horrible
580 way. If any new options are ever added to stty, the short option
581 MUST NOT be a letter which is the first letter of one of the
582 possible stty settings.
583 */
584 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
585 if(find_dev_opt) {
586 if(find_dev_opt[1]==0) /* -*F /dev/foo */
587 k++; /* skip /dev/foo */
588 continue; /* else -*F/dev/foo - no skip */
589 }
590 if(argv[k][0]=='a' || argv[k][0]=='g')
591 continue;
592 /* Is not options - is reverse params */
Eric Andersen98e599c2001-02-14 18:47:33 +0000593 reversed = 1;
594 }
Mark Whitley446dd272001-03-02 20:00:54 +0000595 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000596 if (STREQ(argv[k], mode_info[i].name)) {
597 match_found = set_mode(&mode_info[i], reversed, &mode);
598 require_set_attr = 1;
599 break;
600 }
Mark Whitley446dd272001-03-02 20:00:54 +0000601
602 if (match_found == 0 && reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000603 bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000604
605 if (match_found == 0)
606 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000607 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000608 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000609 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000610 match_found = 1;
611 ++k;
612 set_control_char(&control_info[i], argv[k], &mode);
613 require_set_attr = 1;
614 break;
615 }
Mark Whitley446dd272001-03-02 20:00:54 +0000616
Eric Andersen98e599c2001-02-14 18:47:33 +0000617 if (match_found == 0) {
618 if (STREQ(argv[k], "ispeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000619 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000620 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000621 ++k;
622 set_speed(input_speed, argv[k], &mode);
623 speed_was_set = 1;
624 require_set_attr = 1;
625 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000626 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000627 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000628 ++k;
629 set_speed(output_speed, argv[k], &mode);
630 speed_was_set = 1;
631 require_set_attr = 1;
632 }
633#ifdef TIOCGWINSZ
634 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000635 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000636 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000637 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000638 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000639 -1);
Eric Andersen98e599c2001-02-14 18:47:33 +0000640 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000641 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000642 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000643 ++k;
644 set_window_size(-1,
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000645 (int) bb_xparse_number(argv[k], stty_suffixes));
Eric Andersen98e599c2001-02-14 18:47:33 +0000646 } else if (STREQ(argv[k], "size")) {
647 max_col = screen_columns();
648 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000649 display_window_size(0);
Eric Andersen98e599c2001-02-14 18:47:33 +0000650 }
651#endif
652#ifdef HAVE_C_LINE
653 else if (STREQ(argv[k], "line")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000654 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000655 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000656 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000657 mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000658 require_set_attr = 1;
659 }
660#endif
661 else if (STREQ(argv[k], "speed")) {
662 max_col = screen_columns();
663 display_speed(&mode, 0);
Mark Whitley446dd272001-03-02 20:00:54 +0000664 } else if (recover_mode(argv[k], &mode) == 1)
665 require_set_attr = 1;
666 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000667 set_speed(both_speeds, argv[k], &mode);
668 speed_was_set = 1;
669 require_set_attr = 1;
Mark Whitley446dd272001-03-02 20:00:54 +0000670 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000671 bb_error_msg_and_die("invalid argument `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000672 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000673 }
674
675 if (require_set_attr) {
676 struct termios new_mode;
677
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000678 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000679 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000680
681 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
682 it performs *any* of the requested operations. This means it
683 can report `success' when it has actually failed to perform
684 some proper subset of the requested operations. To detect
685 this partial failure, get the current terminal attributes and
686 compare them to the requested ones. */
687
688 /* Initialize to all zeroes so there is no risk memcmp will report a
689 spurious difference in an uninitialized portion of the structure. */
690 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000691 if (tcgetattr(STDIN_FILENO, &new_mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000692 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000693
694 /* Normally, one shouldn't use memcmp to compare structures that
695 may have `holes' containing uninitialized data, but we have been
696 careful to initialize the storage of these two variables to all
697 zeroes. One might think it more efficient simply to compare the
698 modified fields, but that would require enumerating those fields --
699 and not all systems have the same fields in this structure. */
700
701 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
702#ifdef CIBAUD
703 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
704 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
705 sometimes (m1 != m2). The only difference is in the four bits
706 of the c_cflag field corresponding to the baud rate. To save
707 Sun users a little confusion, don't report an error if this
708 happens. But suppress the error only if we haven't tried to
709 set the baud rate explicitly -- otherwise we'd never give an
710 error for a true failure to set the baud rate. */
711
712 new_mode.c_cflag &= (~CIBAUD);
713 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
714#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000715 perror_on_device ("%s: unable to perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +0000716 }
717 }
718
719 return EXIT_SUCCESS;
720}
721
722/* Return 0 if not applied because not reversible; otherwise return 1. */
723
724static int
725set_mode(const struct mode_info *info, int reversed, struct termios *mode)
726{
727 tcflag_t *bitsp;
728
729 if (reversed && (info->flags & REV) == 0)
730 return 0;
731
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000732 bitsp = mode_type_flag(EMT(info->type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +0000733
734 if (bitsp == NULL) {
735 /* Combination mode. */
736 if (info->name == evenp || info->name == parity) {
737 if (reversed)
738 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
739 else
740 mode->c_cflag =
741 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
742 } else if (info->name == stty_oddp) {
743 if (reversed)
744 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
745 else
746 mode->c_cflag =
747 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
748 } else if (info->name == stty_nl) {
749 if (reversed) {
750 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
751 mode->c_oflag = (mode->c_oflag
752#ifdef ONLCR
753 | ONLCR
754#endif
755 )
756#ifdef OCRNL
757 & ~OCRNL
758#endif
759#ifdef ONLRET
760 & ~ONLRET
761#endif
762 ;
763 } else {
764 mode->c_iflag = mode->c_iflag & ~ICRNL;
765#ifdef ONLCR
766 mode->c_oflag = mode->c_oflag & ~ONLCR;
767#endif
768 }
769 } else if (info->name == stty_ek) {
770 mode->c_cc[VERASE] = CERASE;
771 mode->c_cc[VKILL] = CKILL;
772 } else if (info->name == stty_sane)
773 sane_mode(mode);
774 else if (info->name == cbreak) {
775 if (reversed)
776 mode->c_lflag |= ICANON;
777 else
778 mode->c_lflag &= ~ICANON;
779 } else if (info->name == stty_pass8) {
780 if (reversed) {
781 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
782 mode->c_iflag |= ISTRIP;
783 } else {
784 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
785 mode->c_iflag &= ~ISTRIP;
786 }
787 } else if (info->name == litout) {
788 if (reversed) {
789 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
790 mode->c_iflag |= ISTRIP;
791 mode->c_oflag |= OPOST;
792 } else {
793 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
794 mode->c_iflag &= ~ISTRIP;
795 mode->c_oflag &= ~OPOST;
796 }
797 } else if (info->name == raw || info->name == cooked) {
798 if ((info->name[0] == 'r' && reversed)
799 || (info->name[0] == 'c' && !reversed)) {
800 /* Cooked mode. */
801 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
802 mode->c_oflag |= OPOST;
803 mode->c_lflag |= ISIG | ICANON;
804#if VMIN == VEOF
805 mode->c_cc[VEOF] = CEOF;
806#endif
807#if VTIME == VEOL
808 mode->c_cc[VEOL] = CEOL;
809#endif
810 } else {
811 /* Raw mode. */
812 mode->c_iflag = 0;
813 mode->c_oflag &= ~OPOST;
814 mode->c_lflag &= ~(ISIG | ICANON
815#ifdef XCASE
816 | XCASE
817#endif
818 );
819 mode->c_cc[VMIN] = 1;
820 mode->c_cc[VTIME] = 0;
821 }
822 }
823#ifdef IXANY
824 else if (info->name == decctlq) {
825 if (reversed)
826 mode->c_iflag |= IXANY;
827 else
828 mode->c_iflag &= ~IXANY;
829 }
830#endif
831#ifdef TABDLY
832 else if (info->name == stty_tabs) {
833 if (reversed)
834 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
835 else
836 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
837 }
838#else
839# ifdef OXTABS
840 else if (info->name == stty_tabs) {
841 if (reversed)
842 mode->c_oflag = mode->c_oflag | OXTABS;
843 else
844 mode->c_oflag = mode->c_oflag & ~OXTABS;
845 }
846# endif
847#endif
848#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
849 else if (info->name == stty_lcase || info->name == stty_LCASE) {
850 if (reversed) {
851 mode->c_lflag &= ~XCASE;
852 mode->c_iflag &= ~IUCLC;
853 mode->c_oflag &= ~OLCUC;
854 } else {
855 mode->c_lflag |= XCASE;
856 mode->c_iflag |= IUCLC;
857 mode->c_oflag |= OLCUC;
858 }
859 }
860#endif
861 else if (info->name == stty_crt)
862 mode->c_lflag |= ECHOE
863#ifdef ECHOCTL
864 | ECHOCTL
865#endif
866#ifdef ECHOKE
867 | ECHOKE
868#endif
869 ;
870 else if (info->name == stty_dec) {
Mark Whitley446dd272001-03-02 20:00:54 +0000871 mode->c_cc[VINTR] = 3; /* ^C */
872 mode->c_cc[VERASE] = 127; /* DEL */
873 mode->c_cc[VKILL] = 21; /* ^U */
Eric Andersen98e599c2001-02-14 18:47:33 +0000874 mode->c_lflag |= ECHOE
875#ifdef ECHOCTL
876 | ECHOCTL
877#endif
878#ifdef ECHOKE
879 | ECHOKE
880#endif
881 ;
882#ifdef IXANY
883 mode->c_iflag &= ~IXANY;
884#endif
885 }
886 } else if (reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000887 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000888 else
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
891 return 1;
892}
893
894static void
895set_control_char(const struct control_info *info, const char *arg,
896 struct termios *mode)
897{
898 unsigned char value;
899
900 if (info->name == stty_min || info->name == stty_time)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000901 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000902 else if (arg[0] == '\0' || arg[1] == '\0')
903 value = arg[0];
904 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
905 value = _POSIX_VDISABLE;
Mark Whitley446dd272001-03-02 20:00:54 +0000906 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000907 if (arg[1] == '?')
908 value = 127;
909 else
Mark Whitley446dd272001-03-02 20:00:54 +0000910 value = arg[1] & ~0140; /* Non-letters get weird results. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000911 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000912 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000913 mode->c_cc[info->offset] = value;
914}
915
916static void
917set_speed(enum speed_setting type, const char *arg, struct termios *mode)
918{
919 speed_t baud;
920
921 baud = string_to_baud(arg);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000922
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000923 if (type != output_speed) { /* either input or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000924 cfsetispeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000925 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000926 if (type != input_speed) { /* either output or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000927 cfsetospeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000928 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000929}
930
931#ifdef TIOCGWINSZ
932
933static int get_win_size(int fd, struct winsize *win)
934{
935 int err = ioctl(fd, TIOCGWINSZ, (char *) win);
936
937 return err;
938}
939
940static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000941set_window_size(int rows, int cols)
Eric Andersen98e599c2001-02-14 18:47:33 +0000942{
943 struct winsize win;
944
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000945 if (get_win_size(STDIN_FILENO, &win)) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000946 if (errno != EINVAL)
Eric Andersen8876fb22003-06-20 09:01:58 +0000947 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000948 memset(&win, 0, sizeof(win));
949 }
950
951 if (rows >= 0)
952 win.ws_row = rows;
953 if (cols >= 0)
954 win.ws_col = cols;
955
956# ifdef TIOCSSIZE
957 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
958 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
959 This comment from sys/ttold.h describes Sun's twisted logic - a better
960 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
961 At any rate, the problem is gone in Solaris 2.x. */
962
963 if (win.ws_row == 0 || win.ws_col == 0) {
964 struct ttysize ttysz;
965
966 ttysz.ts_lines = win.ws_row;
967 ttysz.ts_cols = win.ws_col;
968
Manuel Novoa III cad53642003-03-19 09:13:01 +0000969 win.ws_row = win.ws_col = 1;
Eric Andersen98e599c2001-02-14 18:47:33 +0000970
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000971 if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
972 || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000973 perror_on_device("%s");
Glenn L McGrathca1c1af2004-09-15 03:24:32 +0000974 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000975 return;
976 }
977# endif
978
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000979 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Eric Andersen8876fb22003-06-20 09:01:58 +0000980 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000981}
982
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000983static void display_window_size(int fancy)
Eric Andersen98e599c2001-02-14 18:47:33 +0000984{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000985 const char *fmt_str = "%s" "\0" "%s: no size information for this device";
Eric Andersen98e599c2001-02-14 18:47:33 +0000986 struct winsize win;
987
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +0000988 if (get_win_size(STDIN_FILENO, &win)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000989 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000990 perror_on_device(fmt_str);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000991 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000992 } else {
993 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
994 win.ws_row, win.ws_col);
995 if (!fancy)
996 current_col = 0;
997 }
998}
999#endif
1000
1001static int screen_columns(void)
1002{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001003 int columns;
1004 const char *s;
1005
Eric Andersen98e599c2001-02-14 18:47:33 +00001006#ifdef TIOCGWINSZ
1007 struct winsize win;
1008
1009 /* With Solaris 2.[123], this ioctl fails and errno is set to
1010 EINVAL for telnet (but not rlogin) sessions.
1011 On ISC 3.0, it fails for the console and the serial port
1012 (but it works for ptys).
1013 It can also fail on any system when stdout isn't a tty.
1014 In case of any failure, just use the default. */
1015 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1016 return win.ws_col;
1017#endif
1018
Manuel Novoa III cad53642003-03-19 09:13:01 +00001019 columns = 80;
1020 if ((s = getenv("COLUMNS"))) {
1021 columns = atoi(s);
1022 }
1023 return columns;
Eric Andersen98e599c2001-02-14 18:47:33 +00001024}
1025
1026static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1027{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001028 static const unsigned char tcflag_offsets[] = {
1029 offsetof(struct termios, c_cflag), /* control */
1030 offsetof(struct termios, c_iflag), /* input */
1031 offsetof(struct termios, c_oflag), /* output */
1032 offsetof(struct termios, c_lflag) /* local */
1033 };
Eric Andersen98e599c2001-02-14 18:47:33 +00001034
Manuel Novoa III cad53642003-03-19 09:13:01 +00001035 if (((unsigned int) type) <= local) {
1036 return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
Eric Andersen98e599c2001-02-14 18:47:33 +00001037 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001038 return NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +00001039}
1040
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001041static void display_changed(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001042{
1043 int i;
1044 int empty_line;
1045 tcflag_t *bitsp;
1046 unsigned long mask;
1047 enum mode_type prev_type = control;
1048
1049 display_speed(mode, 1);
1050#ifdef HAVE_C_LINE
1051 wrapf("line = %d;", mode->c_line);
1052#endif
1053 putchar('\n');
1054 current_col = 0;
1055
1056 empty_line = 1;
1057 for (i = 0; control_info[i].name != stty_min; ++i) {
1058 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1059 continue;
1060 /* If swtch is the same as susp, don't print both. */
1061#if VSWTCH == VSUSP
1062 if (control_info[i].name == stty_swtch)
1063 continue;
1064#endif
1065 /* If eof uses the same slot as min, only print whichever applies. */
1066#if VEOF == VMIN
1067 if ((mode->c_lflag & ICANON) == 0
1068 && (control_info[i].name == stty_eof
1069 || control_info[i].name == stty_eol)) continue;
1070#endif
1071
1072 empty_line = 0;
1073 wrapf("%s = %s;", control_info[i].name,
1074 visible(mode->c_cc[control_info[i].offset]));
1075 }
1076 if ((mode->c_lflag & ICANON) == 0) {
1077 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1078 (int) mode->c_cc[VTIME]);
1079 } else if (empty_line == 0)
1080 putchar('\n');
1081 current_col = 0;
1082
1083 empty_line = 1;
1084 for (i = 0; i < NUM_mode_info; ++i) {
1085 if (mode_info[i].flags & OMIT)
1086 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001087 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001088 if (empty_line == 0) {
1089 putchar('\n');
1090 current_col = 0;
1091 empty_line = 1;
1092 }
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001093 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001094 }
1095
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001096 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001097 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1098 if ((*bitsp & mask) == mode_info[i].bits) {
1099 if (mode_info[i].flags & SANE_UNSET) {
1100 wrapf("%s", mode_info[i].name);
1101 empty_line = 0;
1102 }
1103 }
1104 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1105 (SANE_SET | REV)) {
1106 wrapf("-%s", mode_info[i].name);
1107 empty_line = 0;
1108 }
1109 }
1110 if (empty_line == 0)
1111 putchar('\n');
1112 current_col = 0;
1113}
1114
1115static void
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001116display_all(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001117{
1118 int i;
1119 tcflag_t *bitsp;
1120 unsigned long mask;
1121 enum mode_type prev_type = control;
1122
1123 display_speed(mode, 1);
1124#ifdef TIOCGWINSZ
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001125 display_window_size(1);
Eric Andersen98e599c2001-02-14 18:47:33 +00001126#endif
1127#ifdef HAVE_C_LINE
1128 wrapf("line = %d;", mode->c_line);
1129#endif
1130 putchar('\n');
1131 current_col = 0;
1132
1133 for (i = 0; control_info[i].name != stty_min; ++i) {
1134 /* If swtch is the same as susp, don't print both. */
1135#if VSWTCH == VSUSP
1136 if (control_info[i].name == stty_swtch)
1137 continue;
1138#endif
1139 /* If eof uses the same slot as min, only print whichever applies. */
1140#if VEOF == VMIN
1141 if ((mode->c_lflag & ICANON) == 0
1142 && (control_info[i].name == stty_eof
1143 || control_info[i].name == stty_eol)) continue;
1144#endif
1145 wrapf("%s = %s;", control_info[i].name,
1146 visible(mode->c_cc[control_info[i].offset]));
1147 }
1148#if VEOF == VMIN
1149 if ((mode->c_lflag & ICANON) == 0)
1150#endif
1151 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1152 if (current_col != 0)
1153 putchar('\n');
1154 current_col = 0;
1155
1156 for (i = 0; i < NUM_mode_info; ++i) {
1157 if (mode_info[i].flags & OMIT)
1158 continue;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001159 if (EMT(mode_info[i].type) != prev_type) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001160 putchar('\n');
1161 current_col = 0;
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001162 prev_type = EMT(mode_info[i].type);
Eric Andersen98e599c2001-02-14 18:47:33 +00001163 }
1164
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001165 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Eric Andersen98e599c2001-02-14 18:47:33 +00001166 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1167 if ((*bitsp & mask) == mode_info[i].bits)
1168 wrapf("%s", mode_info[i].name);
1169 else if (mode_info[i].flags & REV)
1170 wrapf("-%s", mode_info[i].name);
1171 }
1172 putchar('\n');
1173 current_col = 0;
1174}
1175
1176static void display_speed(struct termios *mode, int fancy)
1177{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001178 unsigned long ispeed, ospeed;
1179 const char *fmt_str =
1180 "%lu %lu\n\0" "ispeed %lu baud; ospeed %lu baud;\0"
1181 "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
1182
1183 ospeed = ispeed = cfgetispeed(mode);
1184 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001185 ispeed = ospeed; /* in case ispeed was 0 */
Manuel Novoa III cad53642003-03-19 09:13:01 +00001186 fmt_str += 43;
1187 }
1188 if (fancy) {
1189 fmt_str += 9;
1190 }
1191 wrapf(fmt_str, bb_baud_to_value(ispeed), bb_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001192 if (!fancy)
1193 current_col = 0;
1194}
1195
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001196static void display_recoverable(struct termios *mode)
Eric Andersen98e599c2001-02-14 18:47:33 +00001197{
1198 int i;
1199
1200 printf("%lx:%lx:%lx:%lx",
1201 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1202 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1203 for (i = 0; i < NCCS; ++i)
1204 printf(":%x", (unsigned int) mode->c_cc[i]);
1205 putchar('\n');
1206}
1207
1208static int recover_mode(char *arg, struct termios *mode)
1209{
1210 int i, n;
1211 unsigned int chr;
1212 unsigned long iflag, oflag, cflag, lflag;
1213
1214 /* Scan into temporaries since it is too much trouble to figure out
1215 the right format for `tcflag_t'. */
1216 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1217 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1218 return 0;
1219 mode->c_iflag = iflag;
1220 mode->c_oflag = oflag;
1221 mode->c_cflag = cflag;
1222 mode->c_lflag = lflag;
1223 arg += n;
1224 for (i = 0; i < NCCS; ++i) {
1225 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1226 return 0;
1227 mode->c_cc[i] = chr;
1228 arg += n;
1229 }
1230
1231 /* Fail if there are too many fields. */
1232 if (*arg != '\0')
1233 return 0;
1234
1235 return 1;
1236}
1237
Eric Andersen98e599c2001-02-14 18:47:33 +00001238static speed_t string_to_baud(const char *arg)
1239{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001240 return bb_value_to_baud(bb_xparse_number(arg, 0));
Eric Andersen98e599c2001-02-14 18:47:33 +00001241}
1242
1243static void sane_mode(struct termios *mode)
1244{
1245 int i;
1246 tcflag_t *bitsp;
1247
1248 for (i = 0; i < NUM_control_info; ++i) {
1249#if VMIN == VEOF
1250 if (control_info[i].name == stty_min)
1251 break;
1252#endif
1253 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1254 }
1255
1256 for (i = 0; i < NUM_mode_info; ++i) {
1257 if (mode_info[i].flags & SANE_SET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001258 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001259 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1260 | mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001261 } else if (mode_info[i].flags & SANE_UNSET) {
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001262 bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001263 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1264 & ~mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001265 }
1266 }
1267}
1268
1269/* Return a string that is the printable representation of character CH. */
1270/* Adapted from `cat' by Torbjorn Granlund. */
1271
1272static const char *visible(unsigned int ch)
1273{
1274 static char buf[10];
1275 char *bpout = buf;
1276
Manuel Novoa III cad53642003-03-19 09:13:01 +00001277 if (ch == _POSIX_VDISABLE) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001278 return "<undef>";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001279 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001280
Manuel Novoa III cad53642003-03-19 09:13:01 +00001281 if (ch >= 128) {
1282 ch -= 128;
1283 *bpout++ = 'M';
1284 *bpout++ = '-';
1285 }
1286
1287 if (ch < 32) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001288 *bpout++ = '^';
1289 *bpout++ = ch + 64;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001290 } else if (ch < 127) {
1291 *bpout++ = ch;
1292 } else {
1293 *bpout++ = '^';
1294 *bpout++ = '?';
Eric Andersen98e599c2001-02-14 18:47:33 +00001295 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001296
Eric Andersen98e599c2001-02-14 18:47:33 +00001297 *bpout = '\0';
1298 return (const char *) buf;
1299}
1300
Mark Whitley446dd272001-03-02 20:00:54 +00001301#ifdef TEST
Mark Whitley446dd272001-03-02 20:00:54 +00001302
Manuel Novoa III cad53642003-03-19 09:13:01 +00001303const char *bb_applet_name = "stty";
Mark Whitley446dd272001-03-02 20:00:54 +00001304
Mark Whitley446dd272001-03-02 20:00:54 +00001305#endif