blob: cbd2c22e9a3f62bf408095d86c36e2f672921b8f [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
402
Mark Whitley446dd272001-03-02 20:00:54 +0000403static const char * visible(unsigned int ch);
Mark Whitley446dd272001-03-02 20:00:54 +0000404static int recover_mode(char *arg, struct termios *mode);
405static int screen_columns(void);
406static int set_mode(const struct mode_info *info,
407 int reversed, struct termios *mode);
408static speed_t string_to_baud(const char *arg);
409static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
Eric Andersen8876fb22003-06-20 09:01:58 +0000410static void display_all(struct termios *mode, int fd);
411static void display_changed(struct termios *mode, int fd);
412static void display_recoverable(struct termios *mode, int fd);
Mark Whitley446dd272001-03-02 20:00:54 +0000413static void display_speed(struct termios *mode, int fancy);
Eric Andersen8876fb22003-06-20 09:01:58 +0000414static void display_window_size(int fancy, int fd);
Mark Whitley446dd272001-03-02 20:00:54 +0000415static void sane_mode(struct termios *mode);
416static void set_control_char(const struct control_info *info,
417 const char *arg, struct termios *mode);
418static void set_speed(enum speed_setting type,
419 const char *arg, struct termios *mode);
Eric Andersen8876fb22003-06-20 09:01:58 +0000420static void set_window_size(int rows, int cols, int fd);
421
422static const char *device_name;
423
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000424static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000425{
426 bb_perror_msg_and_die(fmt, device_name);
427}
428
Eric Andersen98e599c2001-02-14 18:47:33 +0000429
430/* The width of the screen, for output wrapping. */
431static int max_col;
432
433/* Current position, to know when to wrap. */
434static int current_col;
435
436/* Print format string MESSAGE and optional args.
437 Wrap to next line first if it won't fit.
438 Print a space first unless MESSAGE will start a new line. */
439
440static void wrapf(const char *message, ...)
441{
442 va_list args;
Mark Whitley446dd272001-03-02 20:00:54 +0000443 char buf[1024]; /* Plenty long for our needs. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000444 int buflen;
445
446 va_start(args, message);
447 vsprintf(buf, message, args);
448 va_end(args);
449 buflen = strlen(buf);
450 if (current_col + (current_col > 0) + buflen >= max_col) {
451 putchar('\n');
452 current_col = 0;
453 }
454 if (current_col > 0) {
455 putchar(' ');
456 current_col++;
457 }
458 fputs(buf, stdout);
459 current_col += buflen;
460}
461
462static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000463 {"b", 512 },
464 {"k", 1024},
465 {"B", 1024},
466 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000467};
468
Mark Whitley446dd272001-03-02 20:00:54 +0000469#ifndef TEST
Eric Andersen98e599c2001-02-14 18:47:33 +0000470extern int stty_main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000471#else
472extern int main(int argc, char **argv)
473#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000474{
475 struct termios mode;
Eric Andersen8876fb22003-06-20 09:01:58 +0000476 void (*output_func)(struct termios *, int);
Mark Whitley446dd272001-03-02 20:00:54 +0000477 int optc;
478 int require_set_attr;
479 int speed_was_set;
480 int verbose_output;
481 int recoverable_output;
482 int k;
483 int noargs = 1;
484 char * file_name = NULL;
485 int fd;
Eric Andersen8876fb22003-06-20 09:01:58 +0000486
Eric Andersen98e599c2001-02-14 18:47:33 +0000487
Manuel Novoa III cad53642003-03-19 09:13:01 +0000488 output_func = display_changed;
Eric Andersen98e599c2001-02-14 18:47:33 +0000489 verbose_output = 0;
490 recoverable_output = 0;
491
492 /* Don't print error messages for unrecognized options. */
493 opterr = 0;
494
495 while ((optc = getopt(argc, argv, "agF:")) != -1) {
496 switch (optc) {
497 case 'a':
498 verbose_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000499 output_func = display_all;
Eric Andersen98e599c2001-02-14 18:47:33 +0000500 break;
501
502 case 'g':
503 recoverable_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000504 output_func = display_recoverable;
Eric Andersen98e599c2001-02-14 18:47:33 +0000505 break;
506
507 case 'F':
508 if (file_name)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000509 bb_error_msg_and_die("only one device may be specified");
Eric Andersen98e599c2001-02-14 18:47:33 +0000510 file_name = optarg;
511 break;
512
Mark Whitley446dd272001-03-02 20:00:54 +0000513 default: /* unrecognized option */
Eric Andersen98e599c2001-02-14 18:47:33 +0000514 noargs = 0;
515 break;
516 }
517
518 if (noargs == 0)
519 break;
520 }
521
522 if (optind < argc)
523 noargs = 0;
524
525 /* Specifying both -a and -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000526 if (verbose_output & recoverable_output)
527 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
Eric Andersen98e599c2001-02-14 18:47:33 +0000528
529 /* Specifying any other arguments with -a or -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000530 if (~noargs & (verbose_output | recoverable_output))
531 bb_error_msg_and_die ("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000532
533 /* FIXME: it'd be better not to open the file until we've verified
534 that all arguments are valid. Otherwise, we could end up doing
535 only some of the requested operations and then failing, probably
536 leaving things in an undesirable state. */
537
538 if (file_name) {
539 int fdflags;
540
541 device_name = file_name;
Eric Andersen8876fb22003-06-20 09:01:58 +0000542 fd = bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
Eric Andersen98e599c2001-02-14 18:47:33 +0000543 if ((fdflags = fcntl(fd, F_GETFL)) == -1
544 || fcntl(fd, 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 {
547 fd = 0;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000548 device_name = bb_msg_standard_input;
Eric Andersen98e599c2001-02-14 18:47:33 +0000549 }
550
551 /* Initialize to all zeroes so there is no risk memcmp will report a
552 spurious difference in an uninitialized portion of the structure. */
553 memset(&mode, 0, sizeof(mode));
554 if (tcgetattr(fd, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000555 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000556
Manuel Novoa III cad53642003-03-19 09:13:01 +0000557 if (verbose_output | recoverable_output | noargs) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000558 max_col = screen_columns();
559 current_col = 0;
Eric Andersen8876fb22003-06-20 09:01:58 +0000560 output_func(&mode, fd);
Eric Andersen98e599c2001-02-14 18:47:33 +0000561 return EXIT_SUCCESS;
562 }
563
564 speed_was_set = 0;
565 require_set_attr = 0;
Eric Andersenfc059092002-06-06 11:35:29 +0000566 k = 0;
567 while (++k < argc) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000568 int match_found = 0;
569 int reversed = 0;
570 int i;
571
572 if (argv[k][0] == '-') {
Eric Andersenfc059092002-06-06 11:35:29 +0000573 char *find_dev_opt;
574
Eric Andersen98e599c2001-02-14 18:47:33 +0000575 ++argv[k];
Eric Andersenfc059092002-06-06 11:35:29 +0000576
577 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
578 Find the options that have been parsed. This is really
579 gross, but it's needed because stty SETTINGS look like options to
580 getopt(), so we need to work around things in a really horrible
581 way. If any new options are ever added to stty, the short option
582 MUST NOT be a letter which is the first letter of one of the
583 possible stty settings.
584 */
585 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
586 if(find_dev_opt) {
587 if(find_dev_opt[1]==0) /* -*F /dev/foo */
588 k++; /* skip /dev/foo */
589 continue; /* else -*F/dev/foo - no skip */
590 }
591 if(argv[k][0]=='a' || argv[k][0]=='g')
592 continue;
593 /* Is not options - is reverse params */
Eric Andersen98e599c2001-02-14 18:47:33 +0000594 reversed = 1;
595 }
Mark Whitley446dd272001-03-02 20:00:54 +0000596 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000597 if (STREQ(argv[k], mode_info[i].name)) {
598 match_found = set_mode(&mode_info[i], reversed, &mode);
599 require_set_attr = 1;
600 break;
601 }
Mark Whitley446dd272001-03-02 20:00:54 +0000602
603 if (match_found == 0 && reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000604 bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000605
606 if (match_found == 0)
607 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000608 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000609 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000610 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000611 match_found = 1;
612 ++k;
613 set_control_char(&control_info[i], argv[k], &mode);
614 require_set_attr = 1;
615 break;
616 }
Mark Whitley446dd272001-03-02 20:00:54 +0000617
Eric Andersen98e599c2001-02-14 18:47:33 +0000618 if (match_found == 0) {
619 if (STREQ(argv[k], "ispeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000620 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000621 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000622 ++k;
623 set_speed(input_speed, argv[k], &mode);
624 speed_was_set = 1;
625 require_set_attr = 1;
626 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000627 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000628 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000629 ++k;
630 set_speed(output_speed, argv[k], &mode);
631 speed_was_set = 1;
632 require_set_attr = 1;
633 }
634#ifdef TIOCGWINSZ
635 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000636 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000637 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000638 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000639 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
Eric Andersen8876fb22003-06-20 09:01:58 +0000640 -1, fd);
Eric Andersen98e599c2001-02-14 18:47:33 +0000641 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000642 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000643 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000644 ++k;
645 set_window_size(-1,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000646 (int) bb_xparse_number(argv[k], stty_suffixes),
Eric Andersen8876fb22003-06-20 09:01:58 +0000647 fd);
Eric Andersen98e599c2001-02-14 18:47:33 +0000648 } else if (STREQ(argv[k], "size")) {
649 max_col = screen_columns();
650 current_col = 0;
Eric Andersen8876fb22003-06-20 09:01:58 +0000651 display_window_size(0, fd);
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)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000657 bb_error_msg_and_die("missing argument to `%s'", 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
680 if (tcsetattr(fd, 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));
693 if (tcgetattr(fd, &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
734 bitsp = mode_type_flag(info->type, mode);
735
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
925 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 }
928 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
Eric Andersen8876fb22003-06-20 09:01:58 +0000943set_window_size(int rows, int cols, int fd)
Eric Andersen98e599c2001-02-14 18:47:33 +0000944{
945 struct winsize win;
946
947 if (get_win_size(fd, &win)) {
948 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
Manuel Novoa III cad53642003-03-19 09:13:01 +0000973 if ((ioctl(fd, TIOCSWINSZ, (char *) &win) != 0)
974 || (ioctl(fd, 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
981 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
Eric Andersen8876fb22003-06-20 09:01:58 +0000982 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000983}
984
Eric Andersen8876fb22003-06-20 09:01:58 +0000985static void display_window_size(int fancy, int fd)
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
990 if (get_win_size(fd, &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
Eric Andersen8876fb22003-06-20 09:01:58 +00001043static void display_changed(struct termios *mode, int fd)
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;
1089 if (mode_info[i].type != prev_type) {
1090 if (empty_line == 0) {
1091 putchar('\n');
1092 current_col = 0;
1093 empty_line = 1;
1094 }
1095 prev_type = mode_info[i].type;
1096 }
1097
1098 bitsp = mode_type_flag(mode_info[i].type, mode);
1099 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
Eric Andersen8876fb22003-06-20 09:01:58 +00001118display_all(struct termios *mode, int fd)
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
Eric Andersen8876fb22003-06-20 09:01:58 +00001127 display_window_size(1, fd);
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;
1161 if (mode_info[i].type != prev_type) {
1162 putchar('\n');
1163 current_col = 0;
1164 prev_type = mode_info[i].type;
1165 }
1166
1167 bitsp = mode_type_flag(mode_info[i].type, mode);
1168 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))) {
1187 ispeed = ospeed; /* in case ispeed was 0 */
1188 fmt_str += 43;
1189 }
1190 if (fancy) {
1191 fmt_str += 9;
1192 }
1193 wrapf(fmt_str, bb_baud_to_value(ispeed), bb_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001194 if (!fancy)
1195 current_col = 0;
1196}
1197
Eric Andersen8876fb22003-06-20 09:01:58 +00001198static void display_recoverable(struct termios *mode, int fd)
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{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001242 return bb_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) {
1260 bitsp = mode_type_flag(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) {
1264 bitsp = mode_type_flag(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