blob: 2e00a496dac5e883a6ef2c9480b555eba51d059f [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;
575 k = optind;
576 while (k < argc) {
577 int match_found = 0;
578 int reversed = 0;
579 int i;
580
581 if (argv[k][0] == '-') {
582 ++argv[k];
583 reversed = 1;
584 }
Mark Whitley446dd272001-03-02 20:00:54 +0000585 for (i = 0; i < NUM_mode_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000586 if (STREQ(argv[k], mode_info[i].name)) {
587 match_found = set_mode(&mode_info[i], reversed, &mode);
588 require_set_attr = 1;
589 break;
590 }
Mark Whitley446dd272001-03-02 20:00:54 +0000591
592 if (match_found == 0 && reversed)
Eric Andersen98e599c2001-02-14 18:47:33 +0000593 error_msg_and_die("invalid argument `%s'", --argv[k]);
Mark Whitley446dd272001-03-02 20:00:54 +0000594
595 if (match_found == 0)
596 for (i = 0; i < NUM_control_info; ++i)
Eric Andersen98e599c2001-02-14 18:47:33 +0000597 if (STREQ(argv[k], control_info[i].name)) {
Mark Whitley446dd272001-03-02 20:00:54 +0000598 if (k == argc - 1)
599 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000600 match_found = 1;
601 ++k;
602 set_control_char(&control_info[i], argv[k], &mode);
603 require_set_attr = 1;
604 break;
605 }
Mark Whitley446dd272001-03-02 20:00:54 +0000606
Eric Andersen98e599c2001-02-14 18:47:33 +0000607 if (match_found == 0) {
608 if (STREQ(argv[k], "ispeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000609 if (k == argc - 1)
610 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000611 ++k;
612 set_speed(input_speed, argv[k], &mode);
613 speed_was_set = 1;
614 require_set_attr = 1;
615 } else if (STREQ(argv[k], "ospeed")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000616 if (k == argc - 1)
617 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000618 ++k;
619 set_speed(output_speed, argv[k], &mode);
620 speed_was_set = 1;
621 require_set_attr = 1;
622 }
623#ifdef TIOCGWINSZ
624 else if (STREQ(argv[k], "rows")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000625 if (k == argc - 1)
626 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000627 ++k;
628 set_window_size((int) parse_number(argv[k], stty_suffixes),
629 -1, fd, device_name);
630 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000631 if (k == argc - 1)
632 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000633 ++k;
634 set_window_size(-1,
Mark Whitley446dd272001-03-02 20:00:54 +0000635 (int) parse_number(argv[k], stty_suffixes),
636 fd, device_name);
Eric Andersen98e599c2001-02-14 18:47:33 +0000637 } else if (STREQ(argv[k], "size")) {
638 max_col = screen_columns();
639 current_col = 0;
640 display_window_size(0, fd, device_name);
641 }
642#endif
643#ifdef HAVE_C_LINE
644 else if (STREQ(argv[k], "line")) {
Mark Whitley446dd272001-03-02 20:00:54 +0000645 if (k == argc - 1)
Eric Andersen98e599c2001-02-14 18:47:33 +0000646 error_msg_and_die("missing argument to `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000647 ++k;
648 mode.c_line = parse_number(argv[k], stty_suffixes);
649 require_set_attr = 1;
650 }
651#endif
652 else if (STREQ(argv[k], "speed")) {
653 max_col = screen_columns();
654 display_speed(&mode, 0);
Mark Whitley446dd272001-03-02 20:00:54 +0000655 } else if (recover_mode(argv[k], &mode) == 1)
656 require_set_attr = 1;
657 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000658 set_speed(both_speeds, argv[k], &mode);
659 speed_was_set = 1;
660 require_set_attr = 1;
Mark Whitley446dd272001-03-02 20:00:54 +0000661 } else
662 error_msg_and_die("invalid argument `%s'", argv[k]);
Eric Andersen98e599c2001-02-14 18:47:33 +0000663 }
664 k++;
665 }
666
667 if (require_set_attr) {
668 struct termios new_mode;
669
670 if (tcsetattr(fd, TCSADRAIN, &mode))
671 perror_msg_and_die("%s", device_name);
672
673 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
674 it performs *any* of the requested operations. This means it
675 can report `success' when it has actually failed to perform
676 some proper subset of the requested operations. To detect
677 this partial failure, get the current terminal attributes and
678 compare them to the requested ones. */
679
680 /* Initialize to all zeroes so there is no risk memcmp will report a
681 spurious difference in an uninitialized portion of the structure. */
682 memset(&new_mode, 0, sizeof(new_mode));
683 if (tcgetattr(fd, &new_mode))
684 perror_msg_and_die("%s", device_name);
685
686 /* Normally, one shouldn't use memcmp to compare structures that
687 may have `holes' containing uninitialized data, but we have been
688 careful to initialize the storage of these two variables to all
689 zeroes. One might think it more efficient simply to compare the
690 modified fields, but that would require enumerating those fields --
691 and not all systems have the same fields in this structure. */
692
693 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
694#ifdef CIBAUD
695 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
696 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
697 sometimes (m1 != m2). The only difference is in the four bits
698 of the c_cflag field corresponding to the baud rate. To save
699 Sun users a little confusion, don't report an error if this
700 happens. But suppress the error only if we haven't tried to
701 set the baud rate explicitly -- otherwise we'd never give an
702 error for a true failure to set the baud rate. */
703
704 new_mode.c_cflag &= (~CIBAUD);
705 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
706#endif
Eric Andersen98e599c2001-02-14 18:47:33 +0000707 error_msg_and_die ("%s: unable to perform all requested operations",
708 device_name);
Eric Andersen98e599c2001-02-14 18:47:33 +0000709 }
710 }
711
712 return EXIT_SUCCESS;
713}
714
715/* Return 0 if not applied because not reversible; otherwise return 1. */
716
717static int
718set_mode(const struct mode_info *info, int reversed, struct termios *mode)
719{
720 tcflag_t *bitsp;
721
722 if (reversed && (info->flags & REV) == 0)
723 return 0;
724
725 bitsp = mode_type_flag(info->type, mode);
726
727 if (bitsp == NULL) {
728 /* Combination mode. */
729 if (info->name == evenp || info->name == parity) {
730 if (reversed)
731 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
732 else
733 mode->c_cflag =
734 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
735 } else if (info->name == stty_oddp) {
736 if (reversed)
737 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
738 else
739 mode->c_cflag =
740 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
741 } else if (info->name == stty_nl) {
742 if (reversed) {
743 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
744 mode->c_oflag = (mode->c_oflag
745#ifdef ONLCR
746 | ONLCR
747#endif
748 )
749#ifdef OCRNL
750 & ~OCRNL
751#endif
752#ifdef ONLRET
753 & ~ONLRET
754#endif
755 ;
756 } else {
757 mode->c_iflag = mode->c_iflag & ~ICRNL;
758#ifdef ONLCR
759 mode->c_oflag = mode->c_oflag & ~ONLCR;
760#endif
761 }
762 } else if (info->name == stty_ek) {
763 mode->c_cc[VERASE] = CERASE;
764 mode->c_cc[VKILL] = CKILL;
765 } else if (info->name == stty_sane)
766 sane_mode(mode);
767 else if (info->name == cbreak) {
768 if (reversed)
769 mode->c_lflag |= ICANON;
770 else
771 mode->c_lflag &= ~ICANON;
772 } else if (info->name == stty_pass8) {
773 if (reversed) {
774 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
775 mode->c_iflag |= ISTRIP;
776 } else {
777 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
778 mode->c_iflag &= ~ISTRIP;
779 }
780 } else if (info->name == litout) {
781 if (reversed) {
782 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
783 mode->c_iflag |= ISTRIP;
784 mode->c_oflag |= OPOST;
785 } else {
786 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
787 mode->c_iflag &= ~ISTRIP;
788 mode->c_oflag &= ~OPOST;
789 }
790 } else if (info->name == raw || info->name == cooked) {
791 if ((info->name[0] == 'r' && reversed)
792 || (info->name[0] == 'c' && !reversed)) {
793 /* Cooked mode. */
794 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
795 mode->c_oflag |= OPOST;
796 mode->c_lflag |= ISIG | ICANON;
797#if VMIN == VEOF
798 mode->c_cc[VEOF] = CEOF;
799#endif
800#if VTIME == VEOL
801 mode->c_cc[VEOL] = CEOL;
802#endif
803 } else {
804 /* Raw mode. */
805 mode->c_iflag = 0;
806 mode->c_oflag &= ~OPOST;
807 mode->c_lflag &= ~(ISIG | ICANON
808#ifdef XCASE
809 | XCASE
810#endif
811 );
812 mode->c_cc[VMIN] = 1;
813 mode->c_cc[VTIME] = 0;
814 }
815 }
816#ifdef IXANY
817 else if (info->name == decctlq) {
818 if (reversed)
819 mode->c_iflag |= IXANY;
820 else
821 mode->c_iflag &= ~IXANY;
822 }
823#endif
824#ifdef TABDLY
825 else if (info->name == stty_tabs) {
826 if (reversed)
827 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
828 else
829 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
830 }
831#else
832# ifdef OXTABS
833 else if (info->name == stty_tabs) {
834 if (reversed)
835 mode->c_oflag = mode->c_oflag | OXTABS;
836 else
837 mode->c_oflag = mode->c_oflag & ~OXTABS;
838 }
839# endif
840#endif
841#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
842 else if (info->name == stty_lcase || info->name == stty_LCASE) {
843 if (reversed) {
844 mode->c_lflag &= ~XCASE;
845 mode->c_iflag &= ~IUCLC;
846 mode->c_oflag &= ~OLCUC;
847 } else {
848 mode->c_lflag |= XCASE;
849 mode->c_iflag |= IUCLC;
850 mode->c_oflag |= OLCUC;
851 }
852 }
853#endif
854 else if (info->name == stty_crt)
855 mode->c_lflag |= ECHOE
856#ifdef ECHOCTL
857 | ECHOCTL
858#endif
859#ifdef ECHOKE
860 | ECHOKE
861#endif
862 ;
863 else if (info->name == stty_dec) {
Mark Whitley446dd272001-03-02 20:00:54 +0000864 mode->c_cc[VINTR] = 3; /* ^C */
865 mode->c_cc[VERASE] = 127; /* DEL */
866 mode->c_cc[VKILL] = 21; /* ^U */
Eric Andersen98e599c2001-02-14 18:47:33 +0000867 mode->c_lflag |= ECHOE
868#ifdef ECHOCTL
869 | ECHOCTL
870#endif
871#ifdef ECHOKE
872 | ECHOKE
873#endif
874 ;
875#ifdef IXANY
876 mode->c_iflag &= ~IXANY;
877#endif
878 }
879 } else if (reversed)
880 *bitsp = *bitsp & ~info->mask & ~info->bits;
881 else
882 *bitsp = (*bitsp & ~info->mask) | info->bits;
883
884 return 1;
885}
886
887static void
888set_control_char(const struct control_info *info, const char *arg,
889 struct termios *mode)
890{
891 unsigned char value;
892
893 if (info->name == stty_min || info->name == stty_time)
894 value = parse_number(arg, stty_suffixes);
895 else if (arg[0] == '\0' || arg[1] == '\0')
896 value = arg[0];
897 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
898 value = _POSIX_VDISABLE;
Mark Whitley446dd272001-03-02 20:00:54 +0000899 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000900 if (arg[1] == '?')
901 value = 127;
902 else
Mark Whitley446dd272001-03-02 20:00:54 +0000903 value = arg[1] & ~0140; /* Non-letters get weird results. */
Eric Andersen98e599c2001-02-14 18:47:33 +0000904 } else
905 value = parse_number(arg, stty_suffixes);
906 mode->c_cc[info->offset] = value;
907}
908
909static void
910set_speed(enum speed_setting type, const char *arg, struct termios *mode)
911{
912 speed_t baud;
913
914 baud = string_to_baud(arg);
915 if (type == input_speed || type == both_speeds)
916 cfsetispeed(mode, baud);
917 if (type == output_speed || type == both_speeds)
918 cfsetospeed(mode, baud);
919}
920
921#ifdef TIOCGWINSZ
922
923static int get_win_size(int fd, struct winsize *win)
924{
925 int err = ioctl(fd, TIOCGWINSZ, (char *) win);
926
927 return err;
928}
929
930static void
931set_window_size(int rows, int cols, int fd, const char *device_name)
932{
933 struct winsize win;
934
935 if (get_win_size(fd, &win)) {
936 if (errno != EINVAL)
937 perror_msg_and_die("%s", device_name);
938 memset(&win, 0, sizeof(win));
939 }
940
941 if (rows >= 0)
942 win.ws_row = rows;
943 if (cols >= 0)
944 win.ws_col = cols;
945
946# ifdef TIOCSSIZE
947 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
948 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
949 This comment from sys/ttold.h describes Sun's twisted logic - a better
950 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
951 At any rate, the problem is gone in Solaris 2.x. */
952
953 if (win.ws_row == 0 || win.ws_col == 0) {
954 struct ttysize ttysz;
955
956 ttysz.ts_lines = win.ws_row;
957 ttysz.ts_cols = win.ws_col;
958
959 win.ws_row = 1;
960 win.ws_col = 1;
961
962 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
963 perror_msg_and_die("%s", device_name);
964
965 if (ioctl(fd, TIOCSSIZE, (char *) &ttysz))
966 perror_msg_and_die("%s", device_name);
967 return;
968 }
969# endif
970
971 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
972 perror_msg_and_die("%s", device_name);
973}
974
975static void display_window_size(int fancy, int fd, const char *device_name)
976{
977 struct winsize win;
978
979 if (get_win_size(fd, &win)) {
980 if (errno != EINVAL)
981 perror_msg_and_die("%s", device_name);
982 if (!fancy)
983 perror_msg_and_die("%s: no size information for this device",
984 device_name);
985 } else {
986 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
987 win.ws_row, win.ws_col);
988 if (!fancy)
989 current_col = 0;
990 }
991}
992#endif
993
994static int screen_columns(void)
995{
996#ifdef TIOCGWINSZ
997 struct winsize win;
998
999 /* With Solaris 2.[123], this ioctl fails and errno is set to
1000 EINVAL for telnet (but not rlogin) sessions.
1001 On ISC 3.0, it fails for the console and the serial port
1002 (but it works for ptys).
1003 It can also fail on any system when stdout isn't a tty.
1004 In case of any failure, just use the default. */
1005 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1006 return win.ws_col;
1007#endif
1008
1009 if (getenv("COLUMNS"))
1010 return atoi(getenv("COLUMNS"));
1011 return 80;
1012}
1013
1014static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1015{
1016 switch (type) {
1017 case control:
1018 return &mode->c_cflag;
1019
1020 case input:
1021 return &mode->c_iflag;
1022
1023 case output:
1024 return &mode->c_oflag;
1025
1026 case local:
1027 return &mode->c_lflag;
1028
Mark Whitley446dd272001-03-02 20:00:54 +00001029 default: /* combination: */
Eric Andersen98e599c2001-02-14 18:47:33 +00001030 return NULL;
1031 }
1032}
1033
1034static void
1035display_settings(enum output_type output_type, struct termios *mode,
1036 int fd, const char *device_name)
1037{
1038 switch (output_type) {
1039 case changed:
1040 display_changed(mode);
1041 break;
1042
1043 case all:
1044 display_all(mode, fd, device_name);
1045 break;
1046
1047 case recoverable:
1048 display_recoverable(mode);
1049 break;
1050 }
1051}
1052
1053static void display_changed(struct termios *mode)
1054{
1055 int i;
1056 int empty_line;
1057 tcflag_t *bitsp;
1058 unsigned long mask;
1059 enum mode_type prev_type = control;
1060
1061 display_speed(mode, 1);
1062#ifdef HAVE_C_LINE
1063 wrapf("line = %d;", mode->c_line);
1064#endif
1065 putchar('\n');
1066 current_col = 0;
1067
1068 empty_line = 1;
1069 for (i = 0; control_info[i].name != stty_min; ++i) {
1070 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1071 continue;
1072 /* If swtch is the same as susp, don't print both. */
1073#if VSWTCH == VSUSP
1074 if (control_info[i].name == stty_swtch)
1075 continue;
1076#endif
1077 /* If eof uses the same slot as min, only print whichever applies. */
1078#if VEOF == VMIN
1079 if ((mode->c_lflag & ICANON) == 0
1080 && (control_info[i].name == stty_eof
1081 || control_info[i].name == stty_eol)) continue;
1082#endif
1083
1084 empty_line = 0;
1085 wrapf("%s = %s;", control_info[i].name,
1086 visible(mode->c_cc[control_info[i].offset]));
1087 }
1088 if ((mode->c_lflag & ICANON) == 0) {
1089 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1090 (int) mode->c_cc[VTIME]);
1091 } else if (empty_line == 0)
1092 putchar('\n');
1093 current_col = 0;
1094
1095 empty_line = 1;
1096 for (i = 0; i < NUM_mode_info; ++i) {
1097 if (mode_info[i].flags & OMIT)
1098 continue;
1099 if (mode_info[i].type != prev_type) {
1100 if (empty_line == 0) {
1101 putchar('\n');
1102 current_col = 0;
1103 empty_line = 1;
1104 }
1105 prev_type = mode_info[i].type;
1106 }
1107
1108 bitsp = mode_type_flag(mode_info[i].type, mode);
1109 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1110 if ((*bitsp & mask) == mode_info[i].bits) {
1111 if (mode_info[i].flags & SANE_UNSET) {
1112 wrapf("%s", mode_info[i].name);
1113 empty_line = 0;
1114 }
1115 }
1116 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1117 (SANE_SET | REV)) {
1118 wrapf("-%s", mode_info[i].name);
1119 empty_line = 0;
1120 }
1121 }
1122 if (empty_line == 0)
1123 putchar('\n');
1124 current_col = 0;
1125}
1126
1127static void
1128display_all(struct termios *mode, int fd, const char *device_name)
1129{
1130 int i;
1131 tcflag_t *bitsp;
1132 unsigned long mask;
1133 enum mode_type prev_type = control;
1134
1135 display_speed(mode, 1);
1136#ifdef TIOCGWINSZ
1137 display_window_size(1, fd, device_name);
1138#endif
1139#ifdef HAVE_C_LINE
1140 wrapf("line = %d;", mode->c_line);
1141#endif
1142 putchar('\n');
1143 current_col = 0;
1144
1145 for (i = 0; control_info[i].name != stty_min; ++i) {
1146 /* If swtch is the same as susp, don't print both. */
1147#if VSWTCH == VSUSP
1148 if (control_info[i].name == stty_swtch)
1149 continue;
1150#endif
1151 /* If eof uses the same slot as min, only print whichever applies. */
1152#if VEOF == VMIN
1153 if ((mode->c_lflag & ICANON) == 0
1154 && (control_info[i].name == stty_eof
1155 || control_info[i].name == stty_eol)) continue;
1156#endif
1157 wrapf("%s = %s;", control_info[i].name,
1158 visible(mode->c_cc[control_info[i].offset]));
1159 }
1160#if VEOF == VMIN
1161 if ((mode->c_lflag & ICANON) == 0)
1162#endif
1163 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1164 if (current_col != 0)
1165 putchar('\n');
1166 current_col = 0;
1167
1168 for (i = 0; i < NUM_mode_info; ++i) {
1169 if (mode_info[i].flags & OMIT)
1170 continue;
1171 if (mode_info[i].type != prev_type) {
1172 putchar('\n');
1173 current_col = 0;
1174 prev_type = mode_info[i].type;
1175 }
1176
1177 bitsp = mode_type_flag(mode_info[i].type, mode);
1178 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1179 if ((*bitsp & mask) == mode_info[i].bits)
1180 wrapf("%s", mode_info[i].name);
1181 else if (mode_info[i].flags & REV)
1182 wrapf("-%s", mode_info[i].name);
1183 }
1184 putchar('\n');
1185 current_col = 0;
1186}
1187
1188static void display_speed(struct termios *mode, int fancy)
1189{
1190 if (cfgetispeed(mode) == 0 || cfgetispeed(mode) == cfgetospeed(mode))
1191 wrapf(fancy ? "speed %lu baud;" : "%lu\n",
1192 baud_to_value(cfgetospeed(mode)));
1193 else
1194 wrapf(fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
1195 baud_to_value(cfgetispeed(mode)),
1196 baud_to_value(cfgetospeed(mode)));
1197 if (!fancy)
1198 current_col = 0;
1199}
1200
1201static void display_recoverable(struct termios *mode)
1202{
1203 int i;
1204
1205 printf("%lx:%lx:%lx:%lx",
1206 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1207 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1208 for (i = 0; i < NCCS; ++i)
1209 printf(":%x", (unsigned int) mode->c_cc[i]);
1210 putchar('\n');
1211}
1212
1213static int recover_mode(char *arg, struct termios *mode)
1214{
1215 int i, n;
1216 unsigned int chr;
1217 unsigned long iflag, oflag, cflag, lflag;
1218
1219 /* Scan into temporaries since it is too much trouble to figure out
1220 the right format for `tcflag_t'. */
1221 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1222 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1223 return 0;
1224 mode->c_iflag = iflag;
1225 mode->c_oflag = oflag;
1226 mode->c_cflag = cflag;
1227 mode->c_lflag = lflag;
1228 arg += n;
1229 for (i = 0; i < NCCS; ++i) {
1230 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1231 return 0;
1232 mode->c_cc[i] = chr;
1233 arg += n;
1234 }
1235
1236 /* Fail if there are too many fields. */
1237 if (*arg != '\0')
1238 return 0;
1239
1240 return 1;
1241}
1242
1243struct speed_map {
Mark Whitley446dd272001-03-02 20:00:54 +00001244 speed_t speed; /* Internal form. */
1245 unsigned long value; /* Numeric value. */
Eric Andersen98e599c2001-02-14 18:47:33 +00001246};
1247
1248static const struct speed_map speeds[] = {
1249 {B0, 0},
1250 {B50, 50},
1251 {B75, 75},
1252 {B110, 110},
1253 {B134, 134},
1254 {B150, 150},
1255 {B200, 200},
1256 {B300, 300},
1257 {B600, 600},
1258 {B1200, 1200},
1259 {B1800, 1800},
1260 {B2400, 2400},
1261 {B4800, 4800},
1262 {B9600, 9600},
1263 {B19200, 19200},
1264 {B38400, 38400},
1265#ifdef B57600
1266 {B57600, 57600},
1267#endif
1268#ifdef B115200
1269 {B115200, 115200},
1270#endif
1271#ifdef B230400
1272 {B230400, 230400},
1273#endif
1274#ifdef B460800
1275 {B460800, 460800},
1276#endif
1277};
1278
1279static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map));
1280
1281static speed_t string_to_baud(const char *arg)
1282{
1283 int i;
Eric Andersen98e599c2001-02-14 18:47:33 +00001284
1285 for (i = 0; i < NUM_SPEEDS; ++i)
Eric Andersen963791a2001-02-18 20:13:18 +00001286 if (parse_number(arg, 0) == speeds[i].value)
Eric Andersen98e599c2001-02-14 18:47:33 +00001287 return speeds[i].speed;
1288 return (speed_t) - 1;
1289}
1290
1291static unsigned long baud_to_value(speed_t speed)
1292{
1293 int i;
1294
1295 for (i = 0; i < NUM_SPEEDS; ++i)
1296 if (speed == speeds[i].speed)
1297 return speeds[i].value;
1298 return 0;
1299}
1300
1301static void sane_mode(struct termios *mode)
1302{
1303 int i;
1304 tcflag_t *bitsp;
1305
1306 for (i = 0; i < NUM_control_info; ++i) {
1307#if VMIN == VEOF
1308 if (control_info[i].name == stty_min)
1309 break;
1310#endif
1311 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1312 }
1313
1314 for (i = 0; i < NUM_mode_info; ++i) {
1315 if (mode_info[i].flags & SANE_SET) {
1316 bitsp = mode_type_flag(mode_info[i].type, mode);
1317 *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
1318 } else if (mode_info[i].flags & SANE_UNSET) {
1319 bitsp = mode_type_flag(mode_info[i].type, mode);
1320 *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
1321 }
1322 }
1323}
1324
1325/* Return a string that is the printable representation of character CH. */
1326/* Adapted from `cat' by Torbjorn Granlund. */
1327
1328static const char *visible(unsigned int ch)
1329{
1330 static char buf[10];
1331 char *bpout = buf;
1332
1333 if (ch == _POSIX_VDISABLE)
1334 return "<undef>";
1335
1336 if (ch >= 32) {
1337 if (ch < 127)
1338 *bpout++ = ch;
1339 else if (ch == 127) {
1340 *bpout++ = '^';
1341 *bpout++ = '?';
1342 } else {
1343 *bpout++ = 'M', *bpout++ = '-';
1344 if (ch >= 128 + 32) {
1345 if (ch < 128 + 127)
1346 *bpout++ = ch - 128;
1347 else {
1348 *bpout++ = '^';
1349 *bpout++ = '?';
1350 }
1351 } else {
1352 *bpout++ = '^';
1353 *bpout++ = ch - 128 + 64;
1354 }
1355 }
1356 } else {
1357 *bpout++ = '^';
1358 *bpout++ = ch + 64;
1359 }
1360 *bpout = '\0';
1361 return (const char *) buf;
1362}
1363
Mark Whitley446dd272001-03-02 20:00:54 +00001364#ifdef TEST
Mark Whitley446dd272001-03-02 20:00:54 +00001365
1366const char *applet_name = "stty";
1367
Mark Whitley446dd272001-03-02 20:00:54 +00001368#endif
1369
Eric Andersen98e599c2001-02-14 18:47:33 +00001370/*
1371Local Variables:
1372c-file-style: "linux"
1373c-basic-offset: 4
1374tab-width: 4
1375End:
1376*/