blob: bd3a36911ec84d409f56a98fb99619e74bbb40d8 [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
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 You should have received a copy of the GNU General Public License
11 along with this program; if not, write to the Free Software Foundation,
12 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
13
14/* Usage: stty [-ag] [-F device] [setting...]
15
16 Options:
17 -a Write all current settings to stdout in human-readable form.
18 -g Write all current settings to stdout in stty-readable form.
19 -F Open and use the specified device instead of stdin
20
21 If no args are given, write to stdout the baud rate and settings that
22 have been changed from their defaults. Mode reading and changes
23 are done on the specified device, or stdin if none was specified.
24
25 David MacKenzie <djm@gnu.ai.mit.edu>
26
Eric Andersen7467c8d2001-07-12 20:26:32 +000027 Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
Eric Andersen98e599c2001-02-14 18:47:33 +000028
29 */
30
Mark Whitley446dd272001-03-02 20:00:54 +000031//#define TEST
Eric Andersen98e599c2001-02-14 18:47:33 +000032
Manuel Novoa III cad53642003-03-19 09:13:01 +000033#include <stddef.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000034#include <termios.h>
35#include <sys/ioctl.h>
Eric Andersen98e599c2001-02-14 18:47:33 +000036
37#include <sys/param.h>
38#include <unistd.h>
39
40#ifndef STDIN_FILENO
41# define STDIN_FILENO 0
42#endif
43
44#ifndef STDOUT_FILENO
45# define STDOUT_FILENO 1
46#endif
47
48#include <stdlib.h>
49#include <string.h>
50#include <assert.h>
51#include <ctype.h>
52#include <errno.h>
53#include <limits.h>
54#include <memory.h>
55#include <fcntl.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000056#include "busybox.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000057
58#define STREQ(a, b) (strcmp ((a), (b)) == 0)
59
60
61#ifndef _POSIX_VDISABLE
62# define _POSIX_VDISABLE ((unsigned char) 0)
63#endif
64
65#define Control(c) ((c) & 0x1f)
66/* Canonical values for control characters. */
67#ifndef CINTR
68# define CINTR Control ('c')
69#endif
70#ifndef CQUIT
71# define CQUIT 28
72#endif
73#ifndef CERASE
74# define CERASE 127
75#endif
76#ifndef CKILL
77# define CKILL Control ('u')
78#endif
79#ifndef CEOF
80# define CEOF Control ('d')
81#endif
82#ifndef CEOL
83# define CEOL _POSIX_VDISABLE
84#endif
85#ifndef CSTART
86# define CSTART Control ('q')
87#endif
88#ifndef CSTOP
89# define CSTOP Control ('s')
90#endif
91#ifndef CSUSP
92# define CSUSP Control ('z')
93#endif
94#if defined(VEOL2) && !defined(CEOL2)
95# define CEOL2 _POSIX_VDISABLE
96#endif
97/* ISC renamed swtch to susp for termios, but we'll accept either name. */
98#if defined(VSUSP) && !defined(VSWTCH)
99# define VSWTCH VSUSP
100# define CSWTCH CSUSP
101#endif
102#if defined(VSWTCH) && !defined(CSWTCH)
103# define CSWTCH _POSIX_VDISABLE
104#endif
105
106/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
107 So the default is to disable `swtch.' */
108#if defined (__sparc__) && defined (__svr4__)
109# undef CSWTCH
110# define CSWTCH _POSIX_VDISABLE
111#endif
112
Mark Whitley446dd272001-03-02 20:00:54 +0000113#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000114# define VWERASE VWERSE
115#endif
116#if defined(VDSUSP) && !defined (CDSUSP)
117# define CDSUSP Control ('y')
118#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000119#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000120# define VREPRINT VRPRNT
121#endif
122#if defined(VREPRINT) && !defined(CRPRNT)
123# define CRPRNT Control ('r')
124#endif
125#if defined(VWERASE) && !defined(CWERASE)
126# define CWERASE Control ('w')
127#endif
128#if defined(VLNEXT) && !defined(CLNEXT)
129# define CLNEXT Control ('v')
130#endif
131#if defined(VDISCARD) && !defined(VFLUSHO)
132# define VFLUSHO VDISCARD
133#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000134#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000135# define VFLUSHO VFLUSH
136#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000137#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000138# define ECHOCTL CTLECH
139#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000140#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000141# define ECHOCTL TCTLECH
142#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000143#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000144# define ECHOKE CRTKIL
145#endif
146#if defined(VFLUSHO) && !defined(CFLUSHO)
147# define CFLUSHO Control ('o')
148#endif
149#if defined(VSTATUS) && !defined(CSTATUS)
150# define CSTATUS Control ('t')
151#endif
152
153/* Which speeds to set. */
154enum speed_setting {
155 input_speed, output_speed, both_speeds
156};
157
Eric Andersen98e599c2001-02-14 18:47:33 +0000158/* Which member(s) of `struct termios' a mode uses. */
159enum mode_type {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000160 /* Do NOT change the order or values, as mode_type_flag()
161 * depends on them. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000162 control, input, output, local, combination
163};
164
165
Mark Whitley446dd272001-03-02 20:00:54 +0000166static const char evenp [] = "evenp";
167static const char raw [] = "raw";
168static const char stty_min [] = "min";
169static const char stty_time [] = "time";
Eric Andersen98e599c2001-02-14 18:47:33 +0000170static const char stty_swtch[] = "swtch";
Mark Whitley446dd272001-03-02 20:00:54 +0000171static const char stty_eol [] = "eol";
172static const char stty_eof [] = "eof";
173static const char parity [] = "parity";
174static const char stty_oddp [] = "oddp";
175static const char stty_nl [] = "nl";
176static const char stty_ek [] = "ek";
177static const char stty_sane [] = "sane";
178static const char cbreak [] = "cbreak";
Eric Andersen98e599c2001-02-14 18:47:33 +0000179static const char stty_pass8[] = "pass8";
Mark Whitley446dd272001-03-02 20:00:54 +0000180static const char litout [] = "litout";
181static const char cooked [] = "cooked";
182static const char decctlq [] = "decctlq";
183static const char stty_tabs [] = "tabs";
Eric Andersen98e599c2001-02-14 18:47:33 +0000184static const char stty_lcase[] = "lcase";
185static const char stty_LCASE[] = "LCASE";
Mark Whitley446dd272001-03-02 20:00:54 +0000186static const char stty_crt [] = "crt";
187static const char stty_dec [] = "dec";
Eric Andersen98e599c2001-02-14 18:47:33 +0000188
189
190/* Flags for `struct mode_info'. */
Mark Whitley446dd272001-03-02 20:00:54 +0000191#define SANE_SET 1 /* Set in `sane' mode. */
192#define SANE_UNSET 2 /* Unset in `sane' mode. */
193#define REV 4 /* Can be turned off by prepending `-'. */
194#define OMIT 8 /* Don't display value. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000195
196/* Each mode. */
197struct mode_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000198 const char *name; /* Name given on command line. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000199 /* enum mode_type type; */
200 char type; /* Which structure element to change. */
Mark Whitley446dd272001-03-02 20:00:54 +0000201 char flags; /* Setting and display options. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000202 unsigned short mask; /* Other bits to turn off for this mode. */
Mark Whitley446dd272001-03-02 20:00:54 +0000203 unsigned long bits; /* Bits to set for this mode. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000204};
205
Manuel Novoa III cad53642003-03-19 09:13:01 +0000206#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
207
Mark Whitley446dd272001-03-02 20:00:54 +0000208static const struct mode_info mode_info[] = {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000209 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
210 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
211 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
212 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
213 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
214 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
215 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
216 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
217 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
218 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
219 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000220#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000221 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000222#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000223 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
224 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
225 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
226 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
227 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
228 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
229 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
230 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
231 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
232 MI_ENTRY("ixon", input, REV, IXON, 0 ),
233 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
234 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000235#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000236 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000237#endif
238#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000239 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000240#endif
241#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000242 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000243#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000244 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000245#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000246 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000247#endif
248#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000249 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000250#endif
251#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000252 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000253#endif
254#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000255 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000256#endif
257#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000258 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000259#endif
260#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000261 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000262#endif
263#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000264 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000265#endif
266#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000267 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
268 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000269#endif
270#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000271 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
272 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
273 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
274 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000275#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000276
Eric Andersen98e599c2001-02-14 18:47:33 +0000277#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000278 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
279 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
280 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
281 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000282#else
283# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000284 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000285# endif
286#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000287
Eric Andersen98e599c2001-02-14 18:47:33 +0000288#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000289 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
290 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000291#endif
292#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000293 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
294 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000295#endif
296#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000297 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
298 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000299#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000300 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
301 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000302#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000303 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000304#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000305 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
306 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
307 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
308 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
309 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
310 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000311#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000312 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000313#endif
314#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000315 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000316#endif
317#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000318 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
319 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000320#endif
321#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000322 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
323 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000324#endif
325#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000326 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
327 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000328#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000329 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
330 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
331 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
332 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
333 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
334 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
335 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
336 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
337 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
338 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
339 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000340#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000341 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000342#endif
343#if defined (TABDLY) || defined (OXTABS)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000344 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000345#endif
346#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000347 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
348 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000349#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000350 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
351 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000352};
353
354static const int NUM_mode_info =
355
356 (sizeof(mode_info) / sizeof(struct mode_info));
357
358/* Control character settings. */
359struct control_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000360 const char *name; /* Name given on command line. */
361 unsigned char saneval; /* Value to set for `stty sane'. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000362 unsigned char offset; /* Offset in c_cc. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000363};
364
365/* Control characters. */
366
Mark Whitley446dd272001-03-02 20:00:54 +0000367static const struct control_info control_info[] = {
368 {"intr", CINTR, VINTR},
369 {"quit", CQUIT, VQUIT},
370 {"erase", CERASE, VERASE},
371 {"kill", CKILL, VKILL},
372 {stty_eof, CEOF, VEOF},
373 {stty_eol, CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000374#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000375 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000376#endif
377#ifdef VSWTCH
Mark Whitley446dd272001-03-02 20:00:54 +0000378 {stty_swtch, CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000379#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000380 {"start", CSTART, VSTART},
381 {"stop", CSTOP, VSTOP},
382 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000383#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000384 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000385#endif
386#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000387 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000388#endif
389#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000390 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000391#endif
392#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000393 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000394#endif
395#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000396 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000397#endif
398#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000399 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000400#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000401 /* These must be last because of the display routines. */
Mark Whitley446dd272001-03-02 20:00:54 +0000402 {stty_min, 1, VMIN},
403 {stty_time, 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000404};
405
406static const int NUM_control_info =
407 (sizeof(control_info) / sizeof(struct control_info));
408
409
Mark Whitley446dd272001-03-02 20:00:54 +0000410static const char * visible(unsigned int ch);
Mark Whitley446dd272001-03-02 20:00:54 +0000411static int recover_mode(char *arg, struct termios *mode);
412static int screen_columns(void);
413static int set_mode(const struct mode_info *info,
414 int reversed, struct termios *mode);
415static speed_t string_to_baud(const char *arg);
416static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
Eric Andersen8876fb22003-06-20 09:01:58 +0000417static void display_all(struct termios *mode, int fd);
418static void display_changed(struct termios *mode, int fd);
419static void display_recoverable(struct termios *mode, int fd);
Mark Whitley446dd272001-03-02 20:00:54 +0000420static void display_speed(struct termios *mode, int fancy);
Eric Andersen8876fb22003-06-20 09:01:58 +0000421static void display_window_size(int fancy, int fd);
Mark Whitley446dd272001-03-02 20:00:54 +0000422static void sane_mode(struct termios *mode);
423static void set_control_char(const struct control_info *info,
424 const char *arg, struct termios *mode);
425static void set_speed(enum speed_setting type,
426 const char *arg, struct termios *mode);
Eric Andersen8876fb22003-06-20 09:01:58 +0000427static void set_window_size(int rows, int cols, int fd);
428
429static const char *device_name;
430
431static __attribute__ ((noreturn)) void perror_on_device(const char *fmt)
432{
433 bb_perror_msg_and_die(fmt, device_name);
434}
435
Eric Andersen98e599c2001-02-14 18:47:33 +0000436
437/* The width of the screen, for output wrapping. */
438static int max_col;
439
440/* Current position, to know when to wrap. */
441static int current_col;
442
443/* Print format string MESSAGE and optional args.
444 Wrap to next line first if it won't fit.
445 Print a space first unless MESSAGE will start a new line. */
446
447static void wrapf(const char *message, ...)
448{
449 va_list args;
Mark Whitley446dd272001-03-02 20:00:54 +0000450 char buf[1024]; /* Plenty long for our needs. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000451 int buflen;
452
453 va_start(args, message);
454 vsprintf(buf, message, args);
455 va_end(args);
456 buflen = strlen(buf);
457 if (current_col + (current_col > 0) + buflen >= max_col) {
458 putchar('\n');
459 current_col = 0;
460 }
461 if (current_col > 0) {
462 putchar(' ');
463 current_col++;
464 }
465 fputs(buf, stdout);
466 current_col += buflen;
467}
468
469static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000470 {"b", 512 },
471 {"k", 1024},
472 {"B", 1024},
473 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000474};
475
Mark Whitley446dd272001-03-02 20:00:54 +0000476#ifndef TEST
Eric Andersen98e599c2001-02-14 18:47:33 +0000477extern int stty_main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000478#else
479extern int main(int argc, char **argv)
480#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000481{
482 struct termios mode;
Eric Andersen8876fb22003-06-20 09:01:58 +0000483 void (*output_func)(struct termios *, int);
Mark Whitley446dd272001-03-02 20:00:54 +0000484 int optc;
485 int require_set_attr;
486 int speed_was_set;
487 int verbose_output;
488 int recoverable_output;
489 int k;
490 int noargs = 1;
491 char * file_name = NULL;
492 int fd;
Eric Andersen8876fb22003-06-20 09:01:58 +0000493
Eric Andersen98e599c2001-02-14 18:47:33 +0000494
Manuel Novoa III cad53642003-03-19 09:13:01 +0000495 output_func = display_changed;
Eric Andersen98e599c2001-02-14 18:47:33 +0000496 verbose_output = 0;
497 recoverable_output = 0;
498
499 /* Don't print error messages for unrecognized options. */
500 opterr = 0;
501
502 while ((optc = getopt(argc, argv, "agF:")) != -1) {
503 switch (optc) {
504 case 'a':
505 verbose_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000506 output_func = display_all;
Eric Andersen98e599c2001-02-14 18:47:33 +0000507 break;
508
509 case 'g':
510 recoverable_output = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000511 output_func = display_recoverable;
Eric Andersen98e599c2001-02-14 18:47:33 +0000512 break;
513
514 case 'F':
515 if (file_name)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000516 bb_error_msg_and_die("only one device may be specified");
Eric Andersen98e599c2001-02-14 18:47:33 +0000517 file_name = optarg;
518 break;
519
Mark Whitley446dd272001-03-02 20:00:54 +0000520 default: /* unrecognized option */
Eric Andersen98e599c2001-02-14 18:47:33 +0000521 noargs = 0;
522 break;
523 }
524
525 if (noargs == 0)
526 break;
527 }
528
529 if (optind < argc)
530 noargs = 0;
531
532 /* Specifying both -a and -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000533 if (verbose_output & recoverable_output)
534 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
Eric Andersen98e599c2001-02-14 18:47:33 +0000535
536 /* Specifying any other arguments with -a or -g gets an error. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000537 if (~noargs & (verbose_output | recoverable_output))
538 bb_error_msg_and_die ("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +0000539
540 /* FIXME: it'd be better not to open the file until we've verified
541 that all arguments are valid. Otherwise, we could end up doing
542 only some of the requested operations and then failing, probably
543 leaving things in an undesirable state. */
544
545 if (file_name) {
546 int fdflags;
547
548 device_name = file_name;
Eric Andersen8876fb22003-06-20 09:01:58 +0000549 fd = bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
Eric Andersen98e599c2001-02-14 18:47:33 +0000550 if ((fdflags = fcntl(fd, F_GETFL)) == -1
551 || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Eric Andersen8876fb22003-06-20 09:01:58 +0000552 perror_on_device("%s: couldn't reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +0000553 } else {
554 fd = 0;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000555 device_name = bb_msg_standard_input;
Eric Andersen98e599c2001-02-14 18:47:33 +0000556 }
557
558 /* Initialize to all zeroes so there is no risk memcmp will report a
559 spurious difference in an uninitialized portion of the structure. */
560 memset(&mode, 0, sizeof(mode));
561 if (tcgetattr(fd, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000562 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000563
Manuel Novoa III cad53642003-03-19 09:13:01 +0000564 if (verbose_output | recoverable_output | noargs) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000565 max_col = screen_columns();
566 current_col = 0;
Eric Andersen8876fb22003-06-20 09:01:58 +0000567 output_func(&mode, fd);
Eric Andersen98e599c2001-02-14 18:47:33 +0000568 return EXIT_SUCCESS;
569 }
570
571 speed_was_set = 0;
572 require_set_attr = 0;
Eric Andersenfc059092002-06-06 11:35:29 +0000573 k = 0;
574 while (++k < argc) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000575 int match_found = 0;
576 int reversed = 0;
577 int i;
578
579 if (argv[k][0] == '-') {
Eric Andersenfc059092002-06-06 11:35:29 +0000580 char *find_dev_opt;
581
Eric Andersen98e599c2001-02-14 18:47:33 +0000582 ++argv[k];
Eric Andersenfc059092002-06-06 11:35:29 +0000583
584 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
585 Find the options that have been parsed. This is really
586 gross, but it's needed because stty SETTINGS look like options to
587 getopt(), so we need to work around things in a really horrible
588 way. If any new options are ever added to stty, the short option
589 MUST NOT be a letter which is the first letter of one of the
590 possible stty settings.
591 */
592 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
593 if(find_dev_opt) {
594 if(find_dev_opt[1]==0) /* -*F /dev/foo */
595 k++; /* skip /dev/foo */
596 continue; /* else -*F/dev/foo - no skip */
597 }
598 if(argv[k][0]=='a' || argv[k][0]=='g')
599 continue;
600 /* Is not options - is reverse params */
Eric Andersen98e599c2001-02-14 18:47:33 +0000601 reversed = 1;
602 }
Mark Whitley446dd272001-03-02 20:00:54 +0000603 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000604 if (STREQ(argv[k], mode_info[i].name)) {
605 match_found = set_mode(&mode_info[i], reversed, &mode);
606 require_set_attr = 1;
607 break;
608 }
Mark Whitley446dd272001-03-02 20:00:54 +0000609
610 if (match_found == 0 && reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000611 bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000612
613 if (match_found == 0)
614 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000615 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000616 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000617 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000618 match_found = 1;
619 ++k;
620 set_control_char(&control_info[i], argv[k], &mode);
621 require_set_attr = 1;
622 break;
623 }
Mark Whitley446dd272001-03-02 20:00:54 +0000624
Eric Andersen98e599c2001-02-14 18:47:33 +0000625 if (match_found == 0) {
626 if (STREQ(argv[k], "ispeed")) {
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(input_speed, argv[k], &mode);
631 speed_was_set = 1;
632 require_set_attr = 1;
633 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000634 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000635 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000636 ++k;
637 set_speed(output_speed, argv[k], &mode);
638 speed_was_set = 1;
639 require_set_attr = 1;
640 }
641#ifdef TIOCGWINSZ
642 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000643 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000644 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000645 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000646 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
Eric Andersen8876fb22003-06-20 09:01:58 +0000647 -1, fd);
Eric Andersen98e599c2001-02-14 18:47:33 +0000648 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000649 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000650 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000651 ++k;
652 set_window_size(-1,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000653 (int) bb_xparse_number(argv[k], stty_suffixes),
Eric Andersen8876fb22003-06-20 09:01:58 +0000654 fd);
Eric Andersen98e599c2001-02-14 18:47:33 +0000655 } else if (STREQ(argv[k], "size")) {
656 max_col = screen_columns();
657 current_col = 0;
Eric Andersen8876fb22003-06-20 09:01:58 +0000658 display_window_size(0, fd);
Eric Andersen98e599c2001-02-14 18:47:33 +0000659 }
660#endif
661#ifdef HAVE_C_LINE
662 else if (STREQ(argv[k], "line")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000663 if (k == argc - 1)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000664 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000665 ++k;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000666 mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000667 require_set_attr = 1;
668 }
669#endif
670 else if (STREQ(argv[k], "speed")) {
671 max_col = screen_columns();
672 display_speed(&mode, 0);
Mark Whitley446dd272001-03-02 20:00:54 +0000673 } else if (recover_mode(argv[k], &mode) == 1)
674 require_set_attr = 1;
675 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000676 set_speed(both_speeds, argv[k], &mode);
677 speed_was_set = 1;
678 require_set_attr = 1;
Mark Whitley446dd272001-03-02 20:00:54 +0000679 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000680 bb_error_msg_and_die("invalid argument `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000681 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000682 }
683
684 if (require_set_attr) {
685 struct termios new_mode;
686
687 if (tcsetattr(fd, TCSADRAIN, &mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000688 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000689
690 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
691 it performs *any* of the requested operations. This means it
692 can report `success' when it has actually failed to perform
693 some proper subset of the requested operations. To detect
694 this partial failure, get the current terminal attributes and
695 compare them to the requested ones. */
696
697 /* Initialize to all zeroes so there is no risk memcmp will report a
698 spurious difference in an uninitialized portion of the structure. */
699 memset(&new_mode, 0, sizeof(new_mode));
700 if (tcgetattr(fd, &new_mode))
Eric Andersen8876fb22003-06-20 09:01:58 +0000701 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000702
703 /* Normally, one shouldn't use memcmp to compare structures that
704 may have `holes' containing uninitialized data, but we have been
705 careful to initialize the storage of these two variables to all
706 zeroes. One might think it more efficient simply to compare the
707 modified fields, but that would require enumerating those fields --
708 and not all systems have the same fields in this structure. */
709
710 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
711#ifdef CIBAUD
712 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
713 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
714 sometimes (m1 != m2). The only difference is in the four bits
715 of the c_cflag field corresponding to the baud rate. To save
716 Sun users a little confusion, don't report an error if this
717 happens. But suppress the error only if we haven't tried to
718 set the baud rate explicitly -- otherwise we'd never give an
719 error for a true failure to set the baud rate. */
720
721 new_mode.c_cflag &= (~CIBAUD);
722 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
723#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000724 perror_on_device ("%s: unable to perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +0000725 }
726 }
727
728 return EXIT_SUCCESS;
729}
730
731/* Return 0 if not applied because not reversible; otherwise return 1. */
732
733static int
734set_mode(const struct mode_info *info, int reversed, struct termios *mode)
735{
736 tcflag_t *bitsp;
737
738 if (reversed && (info->flags & REV) == 0)
739 return 0;
740
741 bitsp = mode_type_flag(info->type, mode);
742
743 if (bitsp == NULL) {
744 /* Combination mode. */
745 if (info->name == evenp || info->name == parity) {
746 if (reversed)
747 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
748 else
749 mode->c_cflag =
750 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
751 } else if (info->name == stty_oddp) {
752 if (reversed)
753 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
754 else
755 mode->c_cflag =
756 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
757 } else if (info->name == stty_nl) {
758 if (reversed) {
759 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
760 mode->c_oflag = (mode->c_oflag
761#ifdef ONLCR
762 | ONLCR
763#endif
764 )
765#ifdef OCRNL
766 & ~OCRNL
767#endif
768#ifdef ONLRET
769 & ~ONLRET
770#endif
771 ;
772 } else {
773 mode->c_iflag = mode->c_iflag & ~ICRNL;
774#ifdef ONLCR
775 mode->c_oflag = mode->c_oflag & ~ONLCR;
776#endif
777 }
778 } else if (info->name == stty_ek) {
779 mode->c_cc[VERASE] = CERASE;
780 mode->c_cc[VKILL] = CKILL;
781 } else if (info->name == stty_sane)
782 sane_mode(mode);
783 else if (info->name == cbreak) {
784 if (reversed)
785 mode->c_lflag |= ICANON;
786 else
787 mode->c_lflag &= ~ICANON;
788 } else if (info->name == stty_pass8) {
789 if (reversed) {
790 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
791 mode->c_iflag |= ISTRIP;
792 } else {
793 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
794 mode->c_iflag &= ~ISTRIP;
795 }
796 } else if (info->name == litout) {
797 if (reversed) {
798 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
799 mode->c_iflag |= ISTRIP;
800 mode->c_oflag |= OPOST;
801 } else {
802 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
803 mode->c_iflag &= ~ISTRIP;
804 mode->c_oflag &= ~OPOST;
805 }
806 } else if (info->name == raw || info->name == cooked) {
807 if ((info->name[0] == 'r' && reversed)
808 || (info->name[0] == 'c' && !reversed)) {
809 /* Cooked mode. */
810 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
811 mode->c_oflag |= OPOST;
812 mode->c_lflag |= ISIG | ICANON;
813#if VMIN == VEOF
814 mode->c_cc[VEOF] = CEOF;
815#endif
816#if VTIME == VEOL
817 mode->c_cc[VEOL] = CEOL;
818#endif
819 } else {
820 /* Raw mode. */
821 mode->c_iflag = 0;
822 mode->c_oflag &= ~OPOST;
823 mode->c_lflag &= ~(ISIG | ICANON
824#ifdef XCASE
825 | XCASE
826#endif
827 );
828 mode->c_cc[VMIN] = 1;
829 mode->c_cc[VTIME] = 0;
830 }
831 }
832#ifdef IXANY
833 else if (info->name == decctlq) {
834 if (reversed)
835 mode->c_iflag |= IXANY;
836 else
837 mode->c_iflag &= ~IXANY;
838 }
839#endif
840#ifdef TABDLY
841 else if (info->name == stty_tabs) {
842 if (reversed)
843 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
844 else
845 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
846 }
847#else
848# ifdef OXTABS
849 else if (info->name == stty_tabs) {
850 if (reversed)
851 mode->c_oflag = mode->c_oflag | OXTABS;
852 else
853 mode->c_oflag = mode->c_oflag & ~OXTABS;
854 }
855# endif
856#endif
857#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
858 else if (info->name == stty_lcase || info->name == stty_LCASE) {
859 if (reversed) {
860 mode->c_lflag &= ~XCASE;
861 mode->c_iflag &= ~IUCLC;
862 mode->c_oflag &= ~OLCUC;
863 } else {
864 mode->c_lflag |= XCASE;
865 mode->c_iflag |= IUCLC;
866 mode->c_oflag |= OLCUC;
867 }
868 }
869#endif
870 else if (info->name == stty_crt)
871 mode->c_lflag |= ECHOE
872#ifdef ECHOCTL
873 | ECHOCTL
874#endif
875#ifdef ECHOKE
876 | ECHOKE
877#endif
878 ;
879 else if (info->name == stty_dec) {
Mark Whitley446dd272001-03-02 20:00:54 +0000880 mode->c_cc[VINTR] = 3; /* ^C */
881 mode->c_cc[VERASE] = 127; /* DEL */
882 mode->c_cc[VKILL] = 21; /* ^U */
Eric Andersen98e599c2001-02-14 18:47:33 +0000883 mode->c_lflag |= ECHOE
884#ifdef ECHOCTL
885 | ECHOCTL
886#endif
887#ifdef ECHOKE
888 | ECHOKE
889#endif
890 ;
891#ifdef IXANY
892 mode->c_iflag &= ~IXANY;
893#endif
894 }
895 } else if (reversed)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000896 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000897 else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000898 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
Eric Andersen98e599c2001-02-14 18:47:33 +0000899
900 return 1;
901}
902
903static void
904set_control_char(const struct control_info *info, const char *arg,
905 struct termios *mode)
906{
907 unsigned char value;
908
909 if (info->name == stty_min || info->name == stty_time)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000910 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000911 else if (arg[0] == '\0' || arg[1] == '\0')
912 value = arg[0];
913 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
914 value = _POSIX_VDISABLE;
Mark Whitley446dd272001-03-02 20:00:54 +0000915 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000916 if (arg[1] == '?')
917 value = 127;
918 else
Mark Whitley446dd272001-03-02 20:00:54 +0000919 value = arg[1] & ~0140; /* Non-letters get weird results. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000920 } else
Manuel Novoa III cad53642003-03-19 09:13:01 +0000921 value = bb_xparse_number(arg, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +0000922 mode->c_cc[info->offset] = value;
923}
924
925static void
926set_speed(enum speed_setting type, const char *arg, struct termios *mode)
927{
928 speed_t baud;
929
930 baud = string_to_baud(arg);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000931
932 if (type != output_speed) { /* either input or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000933 cfsetispeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000934 }
935 if (type != input_speed) { /* either output or both */
Eric Andersen98e599c2001-02-14 18:47:33 +0000936 cfsetospeed(mode, baud);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000937 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000938}
939
940#ifdef TIOCGWINSZ
941
942static int get_win_size(int fd, struct winsize *win)
943{
944 int err = ioctl(fd, TIOCGWINSZ, (char *) win);
945
946 return err;
947}
948
949static void
Eric Andersen8876fb22003-06-20 09:01:58 +0000950set_window_size(int rows, int cols, int fd)
Eric Andersen98e599c2001-02-14 18:47:33 +0000951{
952 struct winsize win;
953
954 if (get_win_size(fd, &win)) {
955 if (errno != EINVAL)
Eric Andersen8876fb22003-06-20 09:01:58 +0000956 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000957 memset(&win, 0, sizeof(win));
958 }
959
960 if (rows >= 0)
961 win.ws_row = rows;
962 if (cols >= 0)
963 win.ws_col = cols;
964
965# ifdef TIOCSSIZE
966 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
967 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
968 This comment from sys/ttold.h describes Sun's twisted logic - a better
969 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
970 At any rate, the problem is gone in Solaris 2.x. */
971
972 if (win.ws_row == 0 || win.ws_col == 0) {
973 struct ttysize ttysz;
974
975 ttysz.ts_lines = win.ws_row;
976 ttysz.ts_cols = win.ws_col;
977
Manuel Novoa III cad53642003-03-19 09:13:01 +0000978 win.ws_row = win.ws_col = 1;
Eric Andersen98e599c2001-02-14 18:47:33 +0000979
Manuel Novoa III cad53642003-03-19 09:13:01 +0000980 if ((ioctl(fd, TIOCSWINSZ, (char *) &win) != 0)
981 || (ioctl(fd, TIOCSSIZE, (char *) &ttysz) != 0)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000982 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000983 return;
984 }
985# endif
986
987 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
Eric Andersen8876fb22003-06-20 09:01:58 +0000988 perror_on_device("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +0000989}
990
Eric Andersen8876fb22003-06-20 09:01:58 +0000991static void display_window_size(int fancy, int fd)
Eric Andersen98e599c2001-02-14 18:47:33 +0000992{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000993 const char *fmt_str = "%s" "\0" "%s: no size information for this device";
Eric Andersen98e599c2001-02-14 18:47:33 +0000994 struct winsize win;
995
996 if (get_win_size(fd, &win)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000997 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
Eric Andersen8876fb22003-06-20 09:01:58 +0000998 perror_on_device(fmt_str);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000999 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001000 } else {
1001 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
1002 win.ws_row, win.ws_col);
1003 if (!fancy)
1004 current_col = 0;
1005 }
1006}
1007#endif
1008
1009static int screen_columns(void)
1010{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001011 int columns;
1012 const char *s;
1013
Eric Andersen98e599c2001-02-14 18:47:33 +00001014#ifdef TIOCGWINSZ
1015 struct winsize win;
1016
1017 /* With Solaris 2.[123], this ioctl fails and errno is set to
1018 EINVAL for telnet (but not rlogin) sessions.
1019 On ISC 3.0, it fails for the console and the serial port
1020 (but it works for ptys).
1021 It can also fail on any system when stdout isn't a tty.
1022 In case of any failure, just use the default. */
1023 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1024 return win.ws_col;
1025#endif
1026
Manuel Novoa III cad53642003-03-19 09:13:01 +00001027 columns = 80;
1028 if ((s = getenv("COLUMNS"))) {
1029 columns = atoi(s);
1030 }
1031 return columns;
Eric Andersen98e599c2001-02-14 18:47:33 +00001032}
1033
1034static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1035{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001036 static const unsigned char tcflag_offsets[] = {
1037 offsetof(struct termios, c_cflag), /* control */
1038 offsetof(struct termios, c_iflag), /* input */
1039 offsetof(struct termios, c_oflag), /* output */
1040 offsetof(struct termios, c_lflag) /* local */
1041 };
Eric Andersen98e599c2001-02-14 18:47:33 +00001042
Manuel Novoa III cad53642003-03-19 09:13:01 +00001043 if (((unsigned int) type) <= local) {
1044 return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
Eric Andersen98e599c2001-02-14 18:47:33 +00001045 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001046 return NULL;
Eric Andersen98e599c2001-02-14 18:47:33 +00001047}
1048
Eric Andersen8876fb22003-06-20 09:01:58 +00001049static void display_changed(struct termios *mode, int fd)
Eric Andersen98e599c2001-02-14 18:47:33 +00001050{
1051 int i;
1052 int empty_line;
1053 tcflag_t *bitsp;
1054 unsigned long mask;
1055 enum mode_type prev_type = control;
1056
1057 display_speed(mode, 1);
1058#ifdef HAVE_C_LINE
1059 wrapf("line = %d;", mode->c_line);
1060#endif
1061 putchar('\n');
1062 current_col = 0;
1063
1064 empty_line = 1;
1065 for (i = 0; control_info[i].name != stty_min; ++i) {
1066 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1067 continue;
1068 /* If swtch is the same as susp, don't print both. */
1069#if VSWTCH == VSUSP
1070 if (control_info[i].name == stty_swtch)
1071 continue;
1072#endif
1073 /* If eof uses the same slot as min, only print whichever applies. */
1074#if VEOF == VMIN
1075 if ((mode->c_lflag & ICANON) == 0
1076 && (control_info[i].name == stty_eof
1077 || control_info[i].name == stty_eol)) continue;
1078#endif
1079
1080 empty_line = 0;
1081 wrapf("%s = %s;", control_info[i].name,
1082 visible(mode->c_cc[control_info[i].offset]));
1083 }
1084 if ((mode->c_lflag & ICANON) == 0) {
1085 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1086 (int) mode->c_cc[VTIME]);
1087 } else if (empty_line == 0)
1088 putchar('\n');
1089 current_col = 0;
1090
1091 empty_line = 1;
1092 for (i = 0; i < NUM_mode_info; ++i) {
1093 if (mode_info[i].flags & OMIT)
1094 continue;
1095 if (mode_info[i].type != prev_type) {
1096 if (empty_line == 0) {
1097 putchar('\n');
1098 current_col = 0;
1099 empty_line = 1;
1100 }
1101 prev_type = mode_info[i].type;
1102 }
1103
1104 bitsp = mode_type_flag(mode_info[i].type, mode);
1105 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1106 if ((*bitsp & mask) == mode_info[i].bits) {
1107 if (mode_info[i].flags & SANE_UNSET) {
1108 wrapf("%s", mode_info[i].name);
1109 empty_line = 0;
1110 }
1111 }
1112 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1113 (SANE_SET | REV)) {
1114 wrapf("-%s", mode_info[i].name);
1115 empty_line = 0;
1116 }
1117 }
1118 if (empty_line == 0)
1119 putchar('\n');
1120 current_col = 0;
1121}
1122
1123static void
Eric Andersen8876fb22003-06-20 09:01:58 +00001124display_all(struct termios *mode, int fd)
Eric Andersen98e599c2001-02-14 18:47:33 +00001125{
1126 int i;
1127 tcflag_t *bitsp;
1128 unsigned long mask;
1129 enum mode_type prev_type = control;
1130
1131 display_speed(mode, 1);
1132#ifdef TIOCGWINSZ
Eric Andersen8876fb22003-06-20 09:01:58 +00001133 display_window_size(1, fd);
Eric Andersen98e599c2001-02-14 18:47:33 +00001134#endif
1135#ifdef HAVE_C_LINE
1136 wrapf("line = %d;", mode->c_line);
1137#endif
1138 putchar('\n');
1139 current_col = 0;
1140
1141 for (i = 0; control_info[i].name != stty_min; ++i) {
1142 /* If swtch is the same as susp, don't print both. */
1143#if VSWTCH == VSUSP
1144 if (control_info[i].name == stty_swtch)
1145 continue;
1146#endif
1147 /* If eof uses the same slot as min, only print whichever applies. */
1148#if VEOF == VMIN
1149 if ((mode->c_lflag & ICANON) == 0
1150 && (control_info[i].name == stty_eof
1151 || control_info[i].name == stty_eol)) continue;
1152#endif
1153 wrapf("%s = %s;", control_info[i].name,
1154 visible(mode->c_cc[control_info[i].offset]));
1155 }
1156#if VEOF == VMIN
1157 if ((mode->c_lflag & ICANON) == 0)
1158#endif
1159 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1160 if (current_col != 0)
1161 putchar('\n');
1162 current_col = 0;
1163
1164 for (i = 0; i < NUM_mode_info; ++i) {
1165 if (mode_info[i].flags & OMIT)
1166 continue;
1167 if (mode_info[i].type != prev_type) {
1168 putchar('\n');
1169 current_col = 0;
1170 prev_type = mode_info[i].type;
1171 }
1172
1173 bitsp = mode_type_flag(mode_info[i].type, mode);
1174 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1175 if ((*bitsp & mask) == mode_info[i].bits)
1176 wrapf("%s", mode_info[i].name);
1177 else if (mode_info[i].flags & REV)
1178 wrapf("-%s", mode_info[i].name);
1179 }
1180 putchar('\n');
1181 current_col = 0;
1182}
1183
1184static void display_speed(struct termios *mode, int fancy)
1185{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001186 unsigned long ispeed, ospeed;
1187 const char *fmt_str =
1188 "%lu %lu\n\0" "ispeed %lu baud; ospeed %lu baud;\0"
1189 "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
1190
1191 ospeed = ispeed = cfgetispeed(mode);
1192 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1193 ispeed = ospeed; /* in case ispeed was 0 */
1194 fmt_str += 43;
1195 }
1196 if (fancy) {
1197 fmt_str += 9;
1198 }
1199 wrapf(fmt_str, bb_baud_to_value(ispeed), bb_baud_to_value(ospeed));
Eric Andersen98e599c2001-02-14 18:47:33 +00001200 if (!fancy)
1201 current_col = 0;
1202}
1203
Eric Andersen8876fb22003-06-20 09:01:58 +00001204static void display_recoverable(struct termios *mode, int fd)
Eric Andersen98e599c2001-02-14 18:47:33 +00001205{
1206 int i;
1207
1208 printf("%lx:%lx:%lx:%lx",
1209 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1210 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1211 for (i = 0; i < NCCS; ++i)
1212 printf(":%x", (unsigned int) mode->c_cc[i]);
1213 putchar('\n');
1214}
1215
1216static int recover_mode(char *arg, struct termios *mode)
1217{
1218 int i, n;
1219 unsigned int chr;
1220 unsigned long iflag, oflag, cflag, lflag;
1221
1222 /* Scan into temporaries since it is too much trouble to figure out
1223 the right format for `tcflag_t'. */
1224 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1225 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1226 return 0;
1227 mode->c_iflag = iflag;
1228 mode->c_oflag = oflag;
1229 mode->c_cflag = cflag;
1230 mode->c_lflag = lflag;
1231 arg += n;
1232 for (i = 0; i < NCCS; ++i) {
1233 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1234 return 0;
1235 mode->c_cc[i] = chr;
1236 arg += n;
1237 }
1238
1239 /* Fail if there are too many fields. */
1240 if (*arg != '\0')
1241 return 0;
1242
1243 return 1;
1244}
1245
Eric Andersen98e599c2001-02-14 18:47:33 +00001246static speed_t string_to_baud(const char *arg)
1247{
Manuel Novoa III cad53642003-03-19 09:13:01 +00001248 return bb_value_to_baud(bb_xparse_number(arg, 0));
Eric Andersen98e599c2001-02-14 18:47:33 +00001249}
1250
1251static void sane_mode(struct termios *mode)
1252{
1253 int i;
1254 tcflag_t *bitsp;
1255
1256 for (i = 0; i < NUM_control_info; ++i) {
1257#if VMIN == VEOF
1258 if (control_info[i].name == stty_min)
1259 break;
1260#endif
1261 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1262 }
1263
1264 for (i = 0; i < NUM_mode_info; ++i) {
1265 if (mode_info[i].flags & SANE_SET) {
1266 bitsp = mode_type_flag(mode_info[i].type, mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001267 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1268 | mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001269 } else if (mode_info[i].flags & SANE_UNSET) {
1270 bitsp = mode_type_flag(mode_info[i].type, mode);
Manuel Novoa III cad53642003-03-19 09:13:01 +00001271 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1272 & ~mode_info[i].bits;
Eric Andersen98e599c2001-02-14 18:47:33 +00001273 }
1274 }
1275}
1276
1277/* Return a string that is the printable representation of character CH. */
1278/* Adapted from `cat' by Torbjorn Granlund. */
1279
1280static const char *visible(unsigned int ch)
1281{
1282 static char buf[10];
1283 char *bpout = buf;
1284
Manuel Novoa III cad53642003-03-19 09:13:01 +00001285 if (ch == _POSIX_VDISABLE) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001286 return "<undef>";
Manuel Novoa III cad53642003-03-19 09:13:01 +00001287 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001288
Manuel Novoa III cad53642003-03-19 09:13:01 +00001289 if (ch >= 128) {
1290 ch -= 128;
1291 *bpout++ = 'M';
1292 *bpout++ = '-';
1293 }
1294
1295 if (ch < 32) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001296 *bpout++ = '^';
1297 *bpout++ = ch + 64;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001298 } else if (ch < 127) {
1299 *bpout++ = ch;
1300 } else {
1301 *bpout++ = '^';
1302 *bpout++ = '?';
Eric Andersen98e599c2001-02-14 18:47:33 +00001303 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001304
Eric Andersen98e599c2001-02-14 18:47:33 +00001305 *bpout = '\0';
1306 return (const char *) buf;
1307}
1308
Mark Whitley446dd272001-03-02 20:00:54 +00001309#ifdef TEST
Mark Whitley446dd272001-03-02 20:00:54 +00001310
Manuel Novoa III cad53642003-03-19 09:13:01 +00001311const char *bb_applet_name = "stty";
Mark Whitley446dd272001-03-02 20:00:54 +00001312
Mark Whitley446dd272001-03-02 20:00:54 +00001313#endif