blob: a67a17c0fbf94a5a7ed3a6a880242752c3631c70 [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
33#include <termios.h>
34#include <sys/ioctl.h>
35#include <getopt.h>
36
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
158/* What to output and how. */
159enum output_type {
Mark Whitley446dd272001-03-02 20:00:54 +0000160 changed, all, recoverable /* Default, -a, -g. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000161};
162
163/* Which member(s) of `struct termios' a mode uses. */
164enum mode_type {
165 control, input, output, local, combination
166};
167
168
Mark Whitley446dd272001-03-02 20:00:54 +0000169static const char evenp [] = "evenp";
170static const char raw [] = "raw";
171static const char stty_min [] = "min";
172static const char stty_time [] = "time";
Eric Andersen98e599c2001-02-14 18:47:33 +0000173static const char stty_swtch[] = "swtch";
Mark Whitley446dd272001-03-02 20:00:54 +0000174static const char stty_eol [] = "eol";
175static const char stty_eof [] = "eof";
176static const char parity [] = "parity";
177static const char stty_oddp [] = "oddp";
178static const char stty_nl [] = "nl";
179static const char stty_ek [] = "ek";
180static const char stty_sane [] = "sane";
181static const char cbreak [] = "cbreak";
Eric Andersen98e599c2001-02-14 18:47:33 +0000182static const char stty_pass8[] = "pass8";
Mark Whitley446dd272001-03-02 20:00:54 +0000183static const char litout [] = "litout";
184static const char cooked [] = "cooked";
185static const char decctlq [] = "decctlq";
186static const char stty_tabs [] = "tabs";
Eric Andersen98e599c2001-02-14 18:47:33 +0000187static const char stty_lcase[] = "lcase";
188static const char stty_LCASE[] = "LCASE";
Mark Whitley446dd272001-03-02 20:00:54 +0000189static const char stty_crt [] = "crt";
190static const char stty_dec [] = "dec";
Eric Andersen98e599c2001-02-14 18:47:33 +0000191
192
193/* Flags for `struct mode_info'. */
Mark Whitley446dd272001-03-02 20:00:54 +0000194#define SANE_SET 1 /* Set in `sane' mode. */
195#define SANE_UNSET 2 /* Unset in `sane' mode. */
196#define REV 4 /* Can be turned off by prepending `-'. */
197#define OMIT 8 /* Don't display value. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000198
199/* Each mode. */
200struct mode_info {
Mark Whitley446dd272001-03-02 20:00:54 +0000201 const char *name; /* Name given on command line. */
202 enum mode_type type; /* Which structure element to change. */
203 char flags; /* Setting and display options. */
204 unsigned long bits; /* Bits to set for this mode. */
205 unsigned long mask; /* Other bits to turn off for this mode. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000206};
207
Mark Whitley446dd272001-03-02 20:00:54 +0000208static const struct mode_info mode_info[] = {
209 {"parenb", control, REV, PARENB, 0 },
210 {"parodd", control, REV, PARODD, 0 },
211 {"cs5", control, 0, CS5, CSIZE},
212 {"cs6", control, 0, CS6, CSIZE},
213 {"cs7", control, 0, CS7, CSIZE},
214 {"cs8", control, 0, CS8, CSIZE},
215 {"hupcl", control, REV, HUPCL, 0 },
216 {"hup", control, REV | OMIT, HUPCL, 0 },
217 {"cstopb", control, REV, CSTOPB, 0 },
218 {"cread", control, SANE_SET | REV, CREAD, 0 },
219 {"clocal", control, REV, CLOCAL, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000220#ifdef CRTSCTS
Mark Whitley446dd272001-03-02 20:00:54 +0000221 {"crtscts", control, REV, CRTSCTS, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000222#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000223 {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 },
224 {"brkint", input, SANE_SET | REV, BRKINT, 0 },
225 {"ignpar", input, REV, IGNPAR, 0 },
226 {"parmrk", input, REV, PARMRK, 0 },
227 {"inpck", input, REV, INPCK, 0 },
228 {"istrip", input, REV, ISTRIP, 0 },
229 {"inlcr", input, SANE_UNSET | REV, INLCR, 0 },
230 {"igncr", input, SANE_UNSET | REV, IGNCR, 0 },
231 {"icrnl", input, SANE_SET | REV, ICRNL, 0 },
232 {"ixon", input, REV, IXON, 0 },
233 {"ixoff", input, SANE_UNSET | REV, IXOFF, 0 },
234 {"tandem", input, REV | OMIT, IXOFF, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000235#ifdef IUCLC
Mark Whitley446dd272001-03-02 20:00:54 +0000236 {"iuclc", input, SANE_UNSET | REV, IUCLC, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000237#endif
238#ifdef IXANY
Mark Whitley446dd272001-03-02 20:00:54 +0000239 {"ixany", input, SANE_UNSET | REV, IXANY, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000240#endif
241#ifdef IMAXBEL
Mark Whitley446dd272001-03-02 20:00:54 +0000242 {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000243#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000244 {"opost", output, SANE_SET | REV, OPOST, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000245#ifdef OLCUC
Mark Whitley446dd272001-03-02 20:00:54 +0000246 {"olcuc", output, SANE_UNSET | REV, OLCUC, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000247#endif
248#ifdef OCRNL
Mark Whitley446dd272001-03-02 20:00:54 +0000249 {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000250#endif
251#ifdef ONLCR
Mark Whitley446dd272001-03-02 20:00:54 +0000252 {"onlcr", output, SANE_SET | REV, ONLCR, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000253#endif
254#ifdef ONOCR
Mark Whitley446dd272001-03-02 20:00:54 +0000255 {"onocr", output, SANE_UNSET | REV, ONOCR, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000256#endif
257#ifdef ONLRET
Mark Whitley446dd272001-03-02 20:00:54 +0000258 {"onlret", output, SANE_UNSET | REV, ONLRET, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000259#endif
260#ifdef OFILL
Mark Whitley446dd272001-03-02 20:00:54 +0000261 {"ofill", output, SANE_UNSET | REV, OFILL, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000262#endif
263#ifdef OFDEL
Mark Whitley446dd272001-03-02 20:00:54 +0000264 {"ofdel", output, SANE_UNSET | REV, OFDEL, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000265#endif
266#ifdef NLDLY
Mark Whitley446dd272001-03-02 20:00:54 +0000267 {"nl1", output, SANE_UNSET, NL1, NLDLY},
268 {"nl0", output, SANE_SET, NL0, NLDLY},
Eric Andersen98e599c2001-02-14 18:47:33 +0000269#endif
270#ifdef CRDLY
Mark Whitley446dd272001-03-02 20:00:54 +0000271 {"cr3", output, SANE_UNSET, CR3, CRDLY},
272 {"cr2", output, SANE_UNSET, CR2, CRDLY},
273 {"cr1", output, SANE_UNSET, CR1, CRDLY},
274 {"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
Mark Whitley446dd272001-03-02 20:00:54 +0000278 {"tab3", output, SANE_UNSET, TAB3, TABDLY},
279 {"tab2", output, SANE_UNSET, TAB2, TABDLY},
280 {"tab1", output, SANE_UNSET, TAB1, TABDLY},
281 {"tab0", output, SANE_SET, TAB0, TABDLY},
Eric Andersen98e599c2001-02-14 18:47:33 +0000282#else
283# ifdef OXTABS
Mark Whitley446dd272001-03-02 20:00:54 +0000284 {"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
Mark Whitley446dd272001-03-02 20:00:54 +0000289 {"bs1", output, SANE_UNSET, BS1, BSDLY},
290 {"bs0", output, SANE_SET, BS0, BSDLY},
Eric Andersen98e599c2001-02-14 18:47:33 +0000291#endif
292#ifdef VTDLY
Mark Whitley446dd272001-03-02 20:00:54 +0000293 {"vt1", output, SANE_UNSET, VT1, VTDLY},
294 {"vt0", output, SANE_SET, VT0, VTDLY},
Eric Andersen98e599c2001-02-14 18:47:33 +0000295#endif
296#ifdef FFDLY
Mark Whitley446dd272001-03-02 20:00:54 +0000297 {"ff1", output, SANE_UNSET, FF1, FFDLY},
298 {"ff0", output, SANE_SET, FF0, FFDLY},
Eric Andersen98e599c2001-02-14 18:47:33 +0000299#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000300 {"isig", local, SANE_SET | REV, ISIG, 0 },
301 {"icanon", local, SANE_SET | REV, ICANON, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000302#ifdef IEXTEN
Mark Whitley446dd272001-03-02 20:00:54 +0000303 {"iexten", local, SANE_SET | REV, IEXTEN, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000304#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000305 {"echo", local, SANE_SET | REV, ECHO, 0 },
306 {"echoe", local, SANE_SET | REV, ECHOE, 0 },
307 {"crterase", local, REV | OMIT, ECHOE, 0 },
308 {"echok", local, SANE_SET | REV, ECHOK, 0 },
309 {"echonl", local, SANE_UNSET | REV, ECHONL, 0 },
310 {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000311#ifdef XCASE
Mark Whitley446dd272001-03-02 20:00:54 +0000312 {"xcase", local, SANE_UNSET | REV, XCASE, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000313#endif
314#ifdef TOSTOP
Mark Whitley446dd272001-03-02 20:00:54 +0000315 {"tostop", local, SANE_UNSET | REV, TOSTOP, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000316#endif
317#ifdef ECHOPRT
Mark Whitley446dd272001-03-02 20:00:54 +0000318 {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 },
319 {"prterase", local, REV | OMIT, ECHOPRT, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000320#endif
321#ifdef ECHOCTL
Mark Whitley446dd272001-03-02 20:00:54 +0000322 {"echoctl", local, SANE_SET | REV, ECHOCTL, 0 },
323 {"ctlecho", local, REV | OMIT, ECHOCTL, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000324#endif
325#ifdef ECHOKE
Mark Whitley446dd272001-03-02 20:00:54 +0000326 {"echoke", local, SANE_SET | REV, ECHOKE, 0 },
327 {"crtkill", local, REV | OMIT, ECHOKE, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000328#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000329 {evenp, combination, REV | OMIT, 0, 0 },
330 {parity, combination, REV | OMIT, 0, 0 },
331 {stty_oddp, combination, REV | OMIT, 0, 0 },
332 {stty_nl, combination, REV | OMIT, 0, 0 },
333 {stty_ek, combination, OMIT, 0, 0 },
334 {stty_sane, combination, OMIT, 0, 0 },
335 {cooked, combination, REV | OMIT, 0, 0 },
336 {raw, combination, REV | OMIT, 0, 0 },
337 {stty_pass8, combination, REV | OMIT, 0, 0 },
338 {litout, combination, REV | OMIT, 0, 0 },
339 {cbreak, combination, REV | OMIT, 0, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000340#ifdef IXANY
Mark Whitley446dd272001-03-02 20:00:54 +0000341 {decctlq, combination, REV | OMIT, 0, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000342#endif
343#if defined (TABDLY) || defined (OXTABS)
Mark Whitley446dd272001-03-02 20:00:54 +0000344 {stty_tabs, combination, REV | OMIT, 0, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000345#endif
346#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Mark Whitley446dd272001-03-02 20:00:54 +0000347 {stty_lcase, combination, REV | OMIT, 0, 0 },
348 {stty_LCASE, combination, REV | OMIT, 0, 0 },
Eric Andersen98e599c2001-02-14 18:47:33 +0000349#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000350 {stty_crt, combination, OMIT, 0, 0 },
351 {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'. */
362 int 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);
Eric Andersen98e599c2001-02-14 18:47:33 +0000411static unsigned long baud_to_value(speed_t speed);
Mark Whitley446dd272001-03-02 20:00:54 +0000412static int recover_mode(char *arg, struct termios *mode);
413static int screen_columns(void);
414static int set_mode(const struct mode_info *info,
415 int reversed, struct termios *mode);
416static speed_t string_to_baud(const char *arg);
417static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
418static void display_all(struct termios *mode, int fd,
419 const char *device_name);
420static void display_changed(struct termios *mode);
421static void display_recoverable(struct termios *mode);
422static void display_settings(enum output_type output_type,
423 struct termios *mode, int fd,
424 const char *device_name);
425static void display_speed(struct termios *mode, int fancy);
426static void display_window_size(int fancy, int fd,
427 const char *device_name);
428static void sane_mode(struct termios *mode);
429static void set_control_char(const struct control_info *info,
430 const char *arg, struct termios *mode);
431static void set_speed(enum speed_setting type,
432 const char *arg, struct termios *mode);
433static void set_window_size(int rows, int cols, int fd,
434 const char *device_name);
Eric Andersen98e599c2001-02-14 18:47:33 +0000435
436/* The width of the screen, for output wrapping. */
437static int max_col;
438
439/* Current position, to know when to wrap. */
440static int current_col;
441
442/* Print format string MESSAGE and optional args.
443 Wrap to next line first if it won't fit.
444 Print a space first unless MESSAGE will start a new line. */
445
446static void wrapf(const char *message, ...)
447{
448 va_list args;
Mark Whitley446dd272001-03-02 20:00:54 +0000449 char buf[1024]; /* Plenty long for our needs. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000450 int buflen;
451
452 va_start(args, message);
453 vsprintf(buf, message, args);
454 va_end(args);
455 buflen = strlen(buf);
456 if (current_col + (current_col > 0) + buflen >= max_col) {
457 putchar('\n');
458 current_col = 0;
459 }
460 if (current_col > 0) {
461 putchar(' ');
462 current_col++;
463 }
464 fputs(buf, stdout);
465 current_col += buflen;
466}
467
468static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000469 {"b", 512 },
470 {"k", 1024},
471 {"B", 1024},
472 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000473};
474
Mark Whitley446dd272001-03-02 20:00:54 +0000475#ifndef TEST
Eric Andersen98e599c2001-02-14 18:47:33 +0000476extern int stty_main(int argc, char **argv)
Mark Whitley446dd272001-03-02 20:00:54 +0000477#else
478extern int main(int argc, char **argv)
479#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000480{
481 struct termios mode;
Mark Whitley446dd272001-03-02 20:00:54 +0000482 enum output_type output_type;
483 int optc;
484 int require_set_attr;
485 int speed_was_set;
486 int verbose_output;
487 int recoverable_output;
488 int k;
489 int noargs = 1;
490 char * file_name = NULL;
491 int fd;
Eric Andersen98e599c2001-02-14 18:47:33 +0000492 const char *device_name;
493
494 output_type = changed;
495 verbose_output = 0;
496 recoverable_output = 0;
497
498 /* Don't print error messages for unrecognized options. */
499 opterr = 0;
500
501 while ((optc = getopt(argc, argv, "agF:")) != -1) {
502 switch (optc) {
503 case 'a':
504 verbose_output = 1;
505 output_type = all;
506 break;
507
508 case 'g':
509 recoverable_output = 1;
510 output_type = recoverable;
511 break;
512
513 case 'F':
514 if (file_name)
515 error_msg_and_die("only one device may be specified");
516 file_name = optarg;
517 break;
518
Mark Whitley446dd272001-03-02 20:00:54 +0000519 default: /* unrecognized option */
Eric Andersen98e599c2001-02-14 18:47:33 +0000520 noargs = 0;
521 break;
522 }
523
524 if (noargs == 0)
525 break;
526 }
527
528 if (optind < argc)
529 noargs = 0;
530
531 /* Specifying both -a and -g gets an error. */
532 if (verbose_output && recoverable_output)
533 error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
534
535 /* Specifying any other arguments with -a or -g gets an error. */
536 if (!noargs && (verbose_output || recoverable_output))
537 error_msg_and_die ("modes may not be set when specifying an output style");
538
539 /* FIXME: it'd be better not to open the file until we've verified
540 that all arguments are valid. Otherwise, we could end up doing
541 only some of the requested operations and then failing, probably
542 leaving things in an undesirable state. */
543
544 if (file_name) {
545 int fdflags;
546
547 device_name = file_name;
548 fd = open(device_name, O_RDONLY | O_NONBLOCK);
549 if (fd < 0)
550 perror_msg_and_die("%s", device_name);
551 if ((fdflags = fcntl(fd, F_GETFL)) == -1
552 || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
553 perror_msg_and_die("%s: couldn't reset non-blocking mode",
554 device_name);
555 } else {
556 fd = 0;
557 device_name = "standard input";
558 }
559
560 /* Initialize to all zeroes so there is no risk memcmp will report a
561 spurious difference in an uninitialized portion of the structure. */
562 memset(&mode, 0, sizeof(mode));
563 if (tcgetattr(fd, &mode))
564 perror_msg_and_die("%s", device_name);
565
566 if (verbose_output || recoverable_output || noargs) {
567 max_col = screen_columns();
568 current_col = 0;
569 display_settings(output_type, &mode, fd, device_name);
570 return EXIT_SUCCESS;
571 }
572
573 speed_was_set = 0;
574 require_set_attr = 0;
Eric Andersenfc059092002-06-06 11:35:29 +0000575 k = 0;
576 while (++k < argc) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000577 int match_found = 0;
578 int reversed = 0;
579 int i;
580
581 if (argv[k][0] == '-') {
Eric Andersenfc059092002-06-06 11:35:29 +0000582 char *find_dev_opt;
583
Eric Andersen98e599c2001-02-14 18:47:33 +0000584 ++argv[k];
Eric Andersenfc059092002-06-06 11:35:29 +0000585
586 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
587 Find the options that have been parsed. This is really
588 gross, but it's needed because stty SETTINGS look like options to
589 getopt(), so we need to work around things in a really horrible
590 way. If any new options are ever added to stty, the short option
591 MUST NOT be a letter which is the first letter of one of the
592 possible stty settings.
593 */
594 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
595 if(find_dev_opt) {
596 if(find_dev_opt[1]==0) /* -*F /dev/foo */
597 k++; /* skip /dev/foo */
598 continue; /* else -*F/dev/foo - no skip */
599 }
600 if(argv[k][0]=='a' || argv[k][0]=='g')
601 continue;
602 /* Is not options - is reverse params */
Eric Andersen98e599c2001-02-14 18:47:33 +0000603 reversed = 1;
604 }
Mark Whitley446dd272001-03-02 20:00:54 +0000605 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000606 if (STREQ(argv[k], mode_info[i].name)) {
607 match_found = set_mode(&mode_info[i], reversed, &mode);
608 require_set_attr = 1;
609 break;
610 }
Mark Whitley446dd272001-03-02 20:00:54 +0000611
612 if (match_found == 0 && reversed)
Eric Andersen98e599c2001-02-14 18:47:33 +0000613 error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000614
615 if (match_found == 0)
616 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000617 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000618 if (k == argc - 1)
619 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000620 match_found = 1;
621 ++k;
622 set_control_char(&control_info[i], argv[k], &mode);
623 require_set_attr = 1;
624 break;
625 }
Mark Whitley446dd272001-03-02 20:00:54 +0000626
Eric Andersen98e599c2001-02-14 18:47:33 +0000627 if (match_found == 0) {
628 if (STREQ(argv[k], "ispeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000629 if (k == argc - 1)
630 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000631 ++k;
632 set_speed(input_speed, argv[k], &mode);
633 speed_was_set = 1;
634 require_set_attr = 1;
635 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000636 if (k == argc - 1)
637 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000638 ++k;
639 set_speed(output_speed, argv[k], &mode);
640 speed_was_set = 1;
641 require_set_attr = 1;
642 }
643#ifdef TIOCGWINSZ
644 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000645 if (k == argc - 1)
646 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000647 ++k;
648 set_window_size((int) parse_number(argv[k], stty_suffixes),
649 -1, fd, device_name);
650 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000651 if (k == argc - 1)
652 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000653 ++k;
654 set_window_size(-1,
Mark Whitley446dd272001-03-02 20:00:54 +0000655 (int) parse_number(argv[k], stty_suffixes),
656 fd, device_name);
Eric Andersen98e599c2001-02-14 18:47:33 +0000657 } else if (STREQ(argv[k], "size")) {
658 max_col = screen_columns();
659 current_col = 0;
660 display_window_size(0, fd, device_name);
661 }
662#endif
663#ifdef HAVE_C_LINE
664 else if (STREQ(argv[k], "line")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000665 if (k == argc - 1)
Eric Andersen98e599c2001-02-14 18:47:33 +0000666 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000667 ++k;
668 mode.c_line = parse_number(argv[k], stty_suffixes);
669 require_set_attr = 1;
670 }
671#endif
672 else if (STREQ(argv[k], "speed")) {
673 max_col = screen_columns();
674 display_speed(&mode, 0);
Mark Whitley446dd272001-03-02 20:00:54 +0000675 } else if (recover_mode(argv[k], &mode) == 1)
676 require_set_attr = 1;
677 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000678 set_speed(both_speeds, argv[k], &mode);
679 speed_was_set = 1;
680 require_set_attr = 1;
Mark Whitley446dd272001-03-02 20:00:54 +0000681 } else
682 error_msg_and_die("invalid argument `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000683 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000684 }
685
686 if (require_set_attr) {
687 struct termios new_mode;
688
689 if (tcsetattr(fd, TCSADRAIN, &mode))
690 perror_msg_and_die("%s", device_name);
691
692 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
693 it performs *any* of the requested operations. This means it
694 can report `success' when it has actually failed to perform
695 some proper subset of the requested operations. To detect
696 this partial failure, get the current terminal attributes and
697 compare them to the requested ones. */
698
699 /* Initialize to all zeroes so there is no risk memcmp will report a
700 spurious difference in an uninitialized portion of the structure. */
701 memset(&new_mode, 0, sizeof(new_mode));
702 if (tcgetattr(fd, &new_mode))
703 perror_msg_and_die("%s", device_name);
704
705 /* Normally, one shouldn't use memcmp to compare structures that
706 may have `holes' containing uninitialized data, but we have been
707 careful to initialize the storage of these two variables to all
708 zeroes. One might think it more efficient simply to compare the
709 modified fields, but that would require enumerating those fields --
710 and not all systems have the same fields in this structure. */
711
712 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
713#ifdef CIBAUD
714 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
715 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
716 sometimes (m1 != m2). The only difference is in the four bits
717 of the c_cflag field corresponding to the baud rate. To save
718 Sun users a little confusion, don't report an error if this
719 happens. But suppress the error only if we haven't tried to
720 set the baud rate explicitly -- otherwise we'd never give an
721 error for a true failure to set the baud rate. */
722
723 new_mode.c_cflag &= (~CIBAUD);
724 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
725#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000726 error_msg_and_die ("%s: unable to perform all requested operations",
727 device_name);
Eric Andersen98e599c2001-02-14 18:47:33 +0000728 }
729 }
730
731 return EXIT_SUCCESS;
732}
733
734/* Return 0 if not applied because not reversible; otherwise return 1. */
735
736static int
737set_mode(const struct mode_info *info, int reversed, struct termios *mode)
738{
739 tcflag_t *bitsp;
740
741 if (reversed && (info->flags & REV) == 0)
742 return 0;
743
744 bitsp = mode_type_flag(info->type, mode);
745
746 if (bitsp == NULL) {
747 /* Combination mode. */
748 if (info->name == evenp || info->name == parity) {
749 if (reversed)
750 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
751 else
752 mode->c_cflag =
753 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
754 } else if (info->name == stty_oddp) {
755 if (reversed)
756 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
757 else
758 mode->c_cflag =
759 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
760 } else if (info->name == stty_nl) {
761 if (reversed) {
762 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
763 mode->c_oflag = (mode->c_oflag
764#ifdef ONLCR
765 | ONLCR
766#endif
767 )
768#ifdef OCRNL
769 & ~OCRNL
770#endif
771#ifdef ONLRET
772 & ~ONLRET
773#endif
774 ;
775 } else {
776 mode->c_iflag = mode->c_iflag & ~ICRNL;
777#ifdef ONLCR
778 mode->c_oflag = mode->c_oflag & ~ONLCR;
779#endif
780 }
781 } else if (info->name == stty_ek) {
782 mode->c_cc[VERASE] = CERASE;
783 mode->c_cc[VKILL] = CKILL;
784 } else if (info->name == stty_sane)
785 sane_mode(mode);
786 else if (info->name == cbreak) {
787 if (reversed)
788 mode->c_lflag |= ICANON;
789 else
790 mode->c_lflag &= ~ICANON;
791 } else if (info->name == stty_pass8) {
792 if (reversed) {
793 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
794 mode->c_iflag |= ISTRIP;
795 } else {
796 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
797 mode->c_iflag &= ~ISTRIP;
798 }
799 } else if (info->name == litout) {
800 if (reversed) {
801 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
802 mode->c_iflag |= ISTRIP;
803 mode->c_oflag |= OPOST;
804 } else {
805 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
806 mode->c_iflag &= ~ISTRIP;
807 mode->c_oflag &= ~OPOST;
808 }
809 } else if (info->name == raw || info->name == cooked) {
810 if ((info->name[0] == 'r' && reversed)
811 || (info->name[0] == 'c' && !reversed)) {
812 /* Cooked mode. */
813 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
814 mode->c_oflag |= OPOST;
815 mode->c_lflag |= ISIG | ICANON;
816#if VMIN == VEOF
817 mode->c_cc[VEOF] = CEOF;
818#endif
819#if VTIME == VEOL
820 mode->c_cc[VEOL] = CEOL;
821#endif
822 } else {
823 /* Raw mode. */
824 mode->c_iflag = 0;
825 mode->c_oflag &= ~OPOST;
826 mode->c_lflag &= ~(ISIG | ICANON
827#ifdef XCASE
828 | XCASE
829#endif
830 );
831 mode->c_cc[VMIN] = 1;
832 mode->c_cc[VTIME] = 0;
833 }
834 }
835#ifdef IXANY
836 else if (info->name == decctlq) {
837 if (reversed)
838 mode->c_iflag |= IXANY;
839 else
840 mode->c_iflag &= ~IXANY;
841 }
842#endif
843#ifdef TABDLY
844 else if (info->name == stty_tabs) {
845 if (reversed)
846 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
847 else
848 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
849 }
850#else
851# ifdef OXTABS
852 else if (info->name == stty_tabs) {
853 if (reversed)
854 mode->c_oflag = mode->c_oflag | OXTABS;
855 else
856 mode->c_oflag = mode->c_oflag & ~OXTABS;
857 }
858# endif
859#endif
860#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
861 else if (info->name == stty_lcase || info->name == stty_LCASE) {
862 if (reversed) {
863 mode->c_lflag &= ~XCASE;
864 mode->c_iflag &= ~IUCLC;
865 mode->c_oflag &= ~OLCUC;
866 } else {
867 mode->c_lflag |= XCASE;
868 mode->c_iflag |= IUCLC;
869 mode->c_oflag |= OLCUC;
870 }
871 }
872#endif
873 else if (info->name == stty_crt)
874 mode->c_lflag |= ECHOE
875#ifdef ECHOCTL
876 | ECHOCTL
877#endif
878#ifdef ECHOKE
879 | ECHOKE
880#endif
881 ;
882 else if (info->name == stty_dec) {
Mark Whitley446dd272001-03-02 20:00:54 +0000883 mode->c_cc[VINTR] = 3; /* ^C */
884 mode->c_cc[VERASE] = 127; /* DEL */
885 mode->c_cc[VKILL] = 21; /* ^U */
Eric Andersen98e599c2001-02-14 18:47:33 +0000886 mode->c_lflag |= ECHOE
887#ifdef ECHOCTL
888 | ECHOCTL
889#endif
890#ifdef ECHOKE
891 | ECHOKE
892#endif
893 ;
894#ifdef IXANY
895 mode->c_iflag &= ~IXANY;
896#endif
897 }
898 } else if (reversed)
899 *bitsp = *bitsp & ~info->mask & ~info->bits;
900 else
901 *bitsp = (*bitsp & ~info->mask) | info->bits;
902
903 return 1;
904}
905
906static void
907set_control_char(const struct control_info *info, const char *arg,
908 struct termios *mode)
909{
910 unsigned char value;
911
912 if (info->name == stty_min || info->name == stty_time)
913 value = parse_number(arg, stty_suffixes);
914 else if (arg[0] == '\0' || arg[1] == '\0')
915 value = arg[0];
916 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
917 value = _POSIX_VDISABLE;
Mark Whitley446dd272001-03-02 20:00:54 +0000918 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000919 if (arg[1] == '?')
920 value = 127;
921 else
Mark Whitley446dd272001-03-02 20:00:54 +0000922 value = arg[1] & ~0140; /* Non-letters get weird results. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000923 } else
924 value = parse_number(arg, stty_suffixes);
925 mode->c_cc[info->offset] = value;
926}
927
928static void
929set_speed(enum speed_setting type, const char *arg, struct termios *mode)
930{
931 speed_t baud;
932
933 baud = string_to_baud(arg);
934 if (type == input_speed || type == both_speeds)
935 cfsetispeed(mode, baud);
936 if (type == output_speed || type == both_speeds)
937 cfsetospeed(mode, baud);
938}
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
950set_window_size(int rows, int cols, int fd, const char *device_name)
951{
952 struct winsize win;
953
954 if (get_win_size(fd, &win)) {
955 if (errno != EINVAL)
956 perror_msg_and_die("%s", device_name);
957 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
978 win.ws_row = 1;
979 win.ws_col = 1;
980
981 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
982 perror_msg_and_die("%s", device_name);
983
984 if (ioctl(fd, TIOCSSIZE, (char *) &ttysz))
985 perror_msg_and_die("%s", device_name);
986 return;
987 }
988# endif
989
990 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
991 perror_msg_and_die("%s", device_name);
992}
993
994static void display_window_size(int fancy, int fd, const char *device_name)
995{
996 struct winsize win;
997
998 if (get_win_size(fd, &win)) {
999 if (errno != EINVAL)
1000 perror_msg_and_die("%s", device_name);
1001 if (!fancy)
1002 perror_msg_and_die("%s: no size information for this device",
1003 device_name);
1004 } else {
1005 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
1006 win.ws_row, win.ws_col);
1007 if (!fancy)
1008 current_col = 0;
1009 }
1010}
1011#endif
1012
1013static int screen_columns(void)
1014{
1015#ifdef TIOCGWINSZ
1016 struct winsize win;
1017
1018 /* With Solaris 2.[123], this ioctl fails and errno is set to
1019 EINVAL for telnet (but not rlogin) sessions.
1020 On ISC 3.0, it fails for the console and the serial port
1021 (but it works for ptys).
1022 It can also fail on any system when stdout isn't a tty.
1023 In case of any failure, just use the default. */
1024 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1025 return win.ws_col;
1026#endif
1027
1028 if (getenv("COLUMNS"))
1029 return atoi(getenv("COLUMNS"));
1030 return 80;
1031}
1032
1033static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1034{
1035 switch (type) {
1036 case control:
1037 return &mode->c_cflag;
1038
1039 case input:
1040 return &mode->c_iflag;
1041
1042 case output:
1043 return &mode->c_oflag;
1044
1045 case local:
1046 return &mode->c_lflag;
1047
Mark Whitley446dd272001-03-02 20:00:54 +00001048 default: /* combination: */
Eric Andersen98e599c2001-02-14 18:47:33 +00001049 return NULL;
1050 }
1051}
1052
1053static void
1054display_settings(enum output_type output_type, struct termios *mode,
1055 int fd, const char *device_name)
1056{
1057 switch (output_type) {
1058 case changed:
1059 display_changed(mode);
1060 break;
1061
1062 case all:
1063 display_all(mode, fd, device_name);
1064 break;
1065
1066 case recoverable:
1067 display_recoverable(mode);
1068 break;
1069 }
1070}
1071
1072static void display_changed(struct termios *mode)
1073{
1074 int i;
1075 int empty_line;
1076 tcflag_t *bitsp;
1077 unsigned long mask;
1078 enum mode_type prev_type = control;
1079
1080 display_speed(mode, 1);
1081#ifdef HAVE_C_LINE
1082 wrapf("line = %d;", mode->c_line);
1083#endif
1084 putchar('\n');
1085 current_col = 0;
1086
1087 empty_line = 1;
1088 for (i = 0; control_info[i].name != stty_min; ++i) {
1089 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1090 continue;
1091 /* If swtch is the same as susp, don't print both. */
1092#if VSWTCH == VSUSP
1093 if (control_info[i].name == stty_swtch)
1094 continue;
1095#endif
1096 /* If eof uses the same slot as min, only print whichever applies. */
1097#if VEOF == VMIN
1098 if ((mode->c_lflag & ICANON) == 0
1099 && (control_info[i].name == stty_eof
1100 || control_info[i].name == stty_eol)) continue;
1101#endif
1102
1103 empty_line = 0;
1104 wrapf("%s = %s;", control_info[i].name,
1105 visible(mode->c_cc[control_info[i].offset]));
1106 }
1107 if ((mode->c_lflag & ICANON) == 0) {
1108 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1109 (int) mode->c_cc[VTIME]);
1110 } else if (empty_line == 0)
1111 putchar('\n');
1112 current_col = 0;
1113
1114 empty_line = 1;
1115 for (i = 0; i < NUM_mode_info; ++i) {
1116 if (mode_info[i].flags & OMIT)
1117 continue;
1118 if (mode_info[i].type != prev_type) {
1119 if (empty_line == 0) {
1120 putchar('\n');
1121 current_col = 0;
1122 empty_line = 1;
1123 }
1124 prev_type = mode_info[i].type;
1125 }
1126
1127 bitsp = mode_type_flag(mode_info[i].type, mode);
1128 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1129 if ((*bitsp & mask) == mode_info[i].bits) {
1130 if (mode_info[i].flags & SANE_UNSET) {
1131 wrapf("%s", mode_info[i].name);
1132 empty_line = 0;
1133 }
1134 }
1135 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1136 (SANE_SET | REV)) {
1137 wrapf("-%s", mode_info[i].name);
1138 empty_line = 0;
1139 }
1140 }
1141 if (empty_line == 0)
1142 putchar('\n');
1143 current_col = 0;
1144}
1145
1146static void
1147display_all(struct termios *mode, int fd, const char *device_name)
1148{
1149 int i;
1150 tcflag_t *bitsp;
1151 unsigned long mask;
1152 enum mode_type prev_type = control;
1153
1154 display_speed(mode, 1);
1155#ifdef TIOCGWINSZ
1156 display_window_size(1, fd, device_name);
1157#endif
1158#ifdef HAVE_C_LINE
1159 wrapf("line = %d;", mode->c_line);
1160#endif
1161 putchar('\n');
1162 current_col = 0;
1163
1164 for (i = 0; control_info[i].name != stty_min; ++i) {
1165 /* If swtch is the same as susp, don't print both. */
1166#if VSWTCH == VSUSP
1167 if (control_info[i].name == stty_swtch)
1168 continue;
1169#endif
1170 /* If eof uses the same slot as min, only print whichever applies. */
1171#if VEOF == VMIN
1172 if ((mode->c_lflag & ICANON) == 0
1173 && (control_info[i].name == stty_eof
1174 || control_info[i].name == stty_eol)) continue;
1175#endif
1176 wrapf("%s = %s;", control_info[i].name,
1177 visible(mode->c_cc[control_info[i].offset]));
1178 }
1179#if VEOF == VMIN
1180 if ((mode->c_lflag & ICANON) == 0)
1181#endif
1182 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1183 if (current_col != 0)
1184 putchar('\n');
1185 current_col = 0;
1186
1187 for (i = 0; i < NUM_mode_info; ++i) {
1188 if (mode_info[i].flags & OMIT)
1189 continue;
1190 if (mode_info[i].type != prev_type) {
1191 putchar('\n');
1192 current_col = 0;
1193 prev_type = mode_info[i].type;
1194 }
1195
1196 bitsp = mode_type_flag(mode_info[i].type, mode);
1197 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1198 if ((*bitsp & mask) == mode_info[i].bits)
1199 wrapf("%s", mode_info[i].name);
1200 else if (mode_info[i].flags & REV)
1201 wrapf("-%s", mode_info[i].name);
1202 }
1203 putchar('\n');
1204 current_col = 0;
1205}
1206
1207static void display_speed(struct termios *mode, int fancy)
1208{
1209 if (cfgetispeed(mode) == 0 || cfgetispeed(mode) == cfgetospeed(mode))
1210 wrapf(fancy ? "speed %lu baud;" : "%lu\n",
1211 baud_to_value(cfgetospeed(mode)));
1212 else
1213 wrapf(fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
1214 baud_to_value(cfgetispeed(mode)),
1215 baud_to_value(cfgetospeed(mode)));
1216 if (!fancy)
1217 current_col = 0;
1218}
1219
1220static void display_recoverable(struct termios *mode)
1221{
1222 int i;
1223
1224 printf("%lx:%lx:%lx:%lx",
1225 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1226 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1227 for (i = 0; i < NCCS; ++i)
1228 printf(":%x", (unsigned int) mode->c_cc[i]);
1229 putchar('\n');
1230}
1231
1232static int recover_mode(char *arg, struct termios *mode)
1233{
1234 int i, n;
1235 unsigned int chr;
1236 unsigned long iflag, oflag, cflag, lflag;
1237
1238 /* Scan into temporaries since it is too much trouble to figure out
1239 the right format for `tcflag_t'. */
1240 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1241 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1242 return 0;
1243 mode->c_iflag = iflag;
1244 mode->c_oflag = oflag;
1245 mode->c_cflag = cflag;
1246 mode->c_lflag = lflag;
1247 arg += n;
1248 for (i = 0; i < NCCS; ++i) {
1249 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1250 return 0;
1251 mode->c_cc[i] = chr;
1252 arg += n;
1253 }
1254
1255 /* Fail if there are too many fields. */
1256 if (*arg != '\0')
1257 return 0;
1258
1259 return 1;
1260}
1261
1262struct speed_map {
Mark Whitley446dd272001-03-02 20:00:54 +00001263 speed_t speed; /* Internal form. */
1264 unsigned long value; /* Numeric value. */
Eric Andersen98e599c2001-02-14 18:47:33 +00001265};
1266
1267static const struct speed_map speeds[] = {
1268 {B0, 0},
1269 {B50, 50},
1270 {B75, 75},
1271 {B110, 110},
1272 {B134, 134},
1273 {B150, 150},
1274 {B200, 200},
1275 {B300, 300},
1276 {B600, 600},
1277 {B1200, 1200},
1278 {B1800, 1800},
1279 {B2400, 2400},
1280 {B4800, 4800},
1281 {B9600, 9600},
1282 {B19200, 19200},
1283 {B38400, 38400},
1284#ifdef B57600
1285 {B57600, 57600},
1286#endif
1287#ifdef B115200
1288 {B115200, 115200},
1289#endif
1290#ifdef B230400
1291 {B230400, 230400},
1292#endif
1293#ifdef B460800
1294 {B460800, 460800},
1295#endif
1296};
1297
1298static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map));
1299
1300static speed_t string_to_baud(const char *arg)
1301{
1302 int i;
Eric Andersen98e599c2001-02-14 18:47:33 +00001303
1304 for (i = 0; i < NUM_SPEEDS; ++i)
Eric Andersen963791a2001-02-18 20:13:18 +00001305 if (parse_number(arg, 0) == speeds[i].value)
Eric Andersen98e599c2001-02-14 18:47:33 +00001306 return speeds[i].speed;
1307 return (speed_t) - 1;
1308}
1309
1310static unsigned long baud_to_value(speed_t speed)
1311{
1312 int i;
1313
1314 for (i = 0; i < NUM_SPEEDS; ++i)
1315 if (speed == speeds[i].speed)
1316 return speeds[i].value;
1317 return 0;
1318}
1319
1320static void sane_mode(struct termios *mode)
1321{
1322 int i;
1323 tcflag_t *bitsp;
1324
1325 for (i = 0; i < NUM_control_info; ++i) {
1326#if VMIN == VEOF
1327 if (control_info[i].name == stty_min)
1328 break;
1329#endif
1330 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1331 }
1332
1333 for (i = 0; i < NUM_mode_info; ++i) {
1334 if (mode_info[i].flags & SANE_SET) {
1335 bitsp = mode_type_flag(mode_info[i].type, mode);
1336 *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
1337 } else if (mode_info[i].flags & SANE_UNSET) {
1338 bitsp = mode_type_flag(mode_info[i].type, mode);
1339 *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
1340 }
1341 }
1342}
1343
1344/* Return a string that is the printable representation of character CH. */
1345/* Adapted from `cat' by Torbjorn Granlund. */
1346
1347static const char *visible(unsigned int ch)
1348{
1349 static char buf[10];
1350 char *bpout = buf;
1351
1352 if (ch == _POSIX_VDISABLE)
1353 return "<undef>";
1354
1355 if (ch >= 32) {
1356 if (ch < 127)
1357 *bpout++ = ch;
1358 else if (ch == 127) {
1359 *bpout++ = '^';
1360 *bpout++ = '?';
1361 } else {
1362 *bpout++ = 'M', *bpout++ = '-';
1363 if (ch >= 128 + 32) {
1364 if (ch < 128 + 127)
1365 *bpout++ = ch - 128;
1366 else {
1367 *bpout++ = '^';
1368 *bpout++ = '?';
1369 }
1370 } else {
1371 *bpout++ = '^';
1372 *bpout++ = ch - 128 + 64;
1373 }
1374 }
1375 } else {
1376 *bpout++ = '^';
1377 *bpout++ = ch + 64;
1378 }
1379 *bpout = '\0';
1380 return (const char *) buf;
1381}
1382
Mark Whitley446dd272001-03-02 20:00:54 +00001383#ifdef TEST
Mark Whitley446dd272001-03-02 20:00:54 +00001384
1385const char *applet_name = "stty";
1386
Mark Whitley446dd272001-03-02 20:00:54 +00001387#endif
1388
Eric Andersen98e599c2001-02-14 18:47:33 +00001389/*
1390Local Variables:
1391c-file-style: "linux"
1392c-basic-offset: 4
1393tab-width: 4
1394End:
1395*/