blob: 15cb05293995e9245f13e68e206550e45d81bc55 [file] [log] [blame]
Eric Andersen98e599c2001-02-14 18:47:33 +00001/* vi: set sw=4 ts=4: */
2/* stty -- change and print terminal line settings
3 Copyright (C) 1990-1999 Free Software Foundation, Inc.
4
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +00005 Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
6*/
Eric Andersen98e599c2001-02-14 18:47:33 +00007/* Usage: stty [-ag] [-F device] [setting...]
8
9 Options:
10 -a Write all current settings to stdout in human-readable form.
11 -g Write all current settings to stdout in stty-readable form.
12 -F Open and use the specified device instead of stdin
13
14 If no args are given, write to stdout the baud rate and settings that
15 have been changed from their defaults. Mode reading and changes
16 are done on the specified device, or stdin if none was specified.
17
18 David MacKenzie <djm@gnu.ai.mit.edu>
19
Eric Andersen7467c8d2001-07-12 20:26:32 +000020 Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
Eric Andersen98e599c2001-02-14 18:47:33 +000021
22 */
23
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +000024#include "busybox.h"
Eric Andersen98e599c2001-02-14 18:47:33 +000025
Eric Andersen98e599c2001-02-14 18:47:33 +000026#ifndef _POSIX_VDISABLE
27# define _POSIX_VDISABLE ((unsigned char) 0)
28#endif
29
30#define Control(c) ((c) & 0x1f)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000031/* Canonical values for control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +000032#ifndef CINTR
Denis Vlasenko9efb0702006-09-19 14:17:10 +000033# define CINTR Control('c')
Eric Andersen98e599c2001-02-14 18:47:33 +000034#endif
35#ifndef CQUIT
36# define CQUIT 28
37#endif
38#ifndef CERASE
39# define CERASE 127
40#endif
41#ifndef CKILL
Denis Vlasenko9efb0702006-09-19 14:17:10 +000042# define CKILL Control('u')
Eric Andersen98e599c2001-02-14 18:47:33 +000043#endif
44#ifndef CEOF
Denis Vlasenko9efb0702006-09-19 14:17:10 +000045# define CEOF Control('d')
Eric Andersen98e599c2001-02-14 18:47:33 +000046#endif
47#ifndef CEOL
48# define CEOL _POSIX_VDISABLE
49#endif
50#ifndef CSTART
Denis Vlasenko9efb0702006-09-19 14:17:10 +000051# define CSTART Control('q')
Eric Andersen98e599c2001-02-14 18:47:33 +000052#endif
53#ifndef CSTOP
Denis Vlasenko9efb0702006-09-19 14:17:10 +000054# define CSTOP Control('s')
Eric Andersen98e599c2001-02-14 18:47:33 +000055#endif
56#ifndef CSUSP
Denis Vlasenko9efb0702006-09-19 14:17:10 +000057# define CSUSP Control('z')
Eric Andersen98e599c2001-02-14 18:47:33 +000058#endif
59#if defined(VEOL2) && !defined(CEOL2)
60# define CEOL2 _POSIX_VDISABLE
61#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +000062/* ISC renamed swtch to susp for termios, but we'll accept either name */
Eric Andersen98e599c2001-02-14 18:47:33 +000063#if defined(VSUSP) && !defined(VSWTCH)
64# define VSWTCH VSUSP
65# define CSWTCH CSUSP
66#endif
67#if defined(VSWTCH) && !defined(CSWTCH)
68# define CSWTCH _POSIX_VDISABLE
69#endif
70
Denis Vlasenko79deb662006-09-19 15:12:12 +000071/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
72 So the default is to disable 'swtch.' */
Eric Andersen98e599c2001-02-14 18:47:33 +000073#if defined (__sparc__) && defined (__svr4__)
74# undef CSWTCH
75# define CSWTCH _POSIX_VDISABLE
76#endif
77
Mark Whitley446dd272001-03-02 20:00:54 +000078#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000079# define VWERASE VWERSE
80#endif
81#if defined(VDSUSP) && !defined (CDSUSP)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000082# define CDSUSP Control('y')
Eric Andersen98e599c2001-02-14 18:47:33 +000083#endif
Mark Whitley446dd272001-03-02 20:00:54 +000084#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
Eric Andersen98e599c2001-02-14 18:47:33 +000085# define VREPRINT VRPRNT
86#endif
87#if defined(VREPRINT) && !defined(CRPRNT)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000088# define CRPRNT Control('r')
Eric Andersen98e599c2001-02-14 18:47:33 +000089#endif
90#if defined(VWERASE) && !defined(CWERASE)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000091# define CWERASE Control('w')
Eric Andersen98e599c2001-02-14 18:47:33 +000092#endif
93#if defined(VLNEXT) && !defined(CLNEXT)
Denis Vlasenko9efb0702006-09-19 14:17:10 +000094# define CLNEXT Control('v')
Eric Andersen98e599c2001-02-14 18:47:33 +000095#endif
96#if defined(VDISCARD) && !defined(VFLUSHO)
97# define VFLUSHO VDISCARD
98#endif
Mark Whitley446dd272001-03-02 20:00:54 +000099#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000100# define VFLUSHO VFLUSH
101#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000102#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000103# define ECHOCTL CTLECH
104#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000105#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000106# define ECHOCTL TCTLECH
107#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000108#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
Eric Andersen98e599c2001-02-14 18:47:33 +0000109# define ECHOKE CRTKIL
110#endif
111#if defined(VFLUSHO) && !defined(CFLUSHO)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000112# define CFLUSHO Control('o')
Eric Andersen98e599c2001-02-14 18:47:33 +0000113#endif
114#if defined(VSTATUS) && !defined(CSTATUS)
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000115# define CSTATUS Control('t')
Eric Andersen98e599c2001-02-14 18:47:33 +0000116#endif
117
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000118/* Which speeds to set */
Eric Andersen98e599c2001-02-14 18:47:33 +0000119enum speed_setting {
120 input_speed, output_speed, both_speeds
121};
122
Denis Vlasenko79deb662006-09-19 15:12:12 +0000123/* Which member(s) of 'struct termios' a mode uses */
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000124enum {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000125 /* Do NOT change the order or values, as mode_type_flag()
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000126 * depends on them */
Eric Andersen98e599c2001-02-14 18:47:33 +0000127 control, input, output, local, combination
128};
129
Mark Whitley446dd272001-03-02 20:00:54 +0000130static const char evenp [] = "evenp";
131static const char raw [] = "raw";
132static const char stty_min [] = "min";
133static const char stty_time [] = "time";
Eric Andersen98e599c2001-02-14 18:47:33 +0000134static const char stty_swtch[] = "swtch";
Mark Whitley446dd272001-03-02 20:00:54 +0000135static const char stty_eol [] = "eol";
136static const char stty_eof [] = "eof";
137static const char parity [] = "parity";
138static const char stty_oddp [] = "oddp";
139static const char stty_nl [] = "nl";
140static const char stty_ek [] = "ek";
141static const char stty_sane [] = "sane";
142static const char cbreak [] = "cbreak";
Eric Andersen98e599c2001-02-14 18:47:33 +0000143static const char stty_pass8[] = "pass8";
Mark Whitley446dd272001-03-02 20:00:54 +0000144static const char litout [] = "litout";
145static const char cooked [] = "cooked";
146static const char decctlq [] = "decctlq";
147static const char stty_tabs [] = "tabs";
Eric Andersen98e599c2001-02-14 18:47:33 +0000148static const char stty_lcase[] = "lcase";
149static const char stty_LCASE[] = "LCASE";
Mark Whitley446dd272001-03-02 20:00:54 +0000150static const char stty_crt [] = "crt";
151static const char stty_dec [] = "dec";
Eric Andersen98e599c2001-02-14 18:47:33 +0000152
Denis Vlasenko79deb662006-09-19 15:12:12 +0000153/* Flags for 'struct mode_info' */
154#define SANE_SET 1 /* Set in 'sane' mode */
155#define SANE_UNSET 2 /* Unset in 'sane' mode */
156#define REV 4 /* Can be turned off by prepending '-' */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000157#define OMIT 8 /* Don't display value */
Eric Andersen98e599c2001-02-14 18:47:33 +0000158
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000159/* Each mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000160struct mode_info {
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000161 const char * const name; /* Name given on command line */
162 const unsigned char type; /* Which structure element to change */
163 const unsigned char flags; /* Setting and display options */
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000164 /* were using short here, but ppc32 was unhappy: */
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000165 const tcflag_t mask; /* Other bits to turn off for this mode */
166 const tcflag_t bits; /* Bits to set for this mode */
Eric Andersen98e599c2001-02-14 18:47:33 +0000167};
168
Denis Vlasenkob2abef32007-01-01 18:18:04 +0000169/* We can optimize it further by using name[8] instead of char *name */
170/* but beware of "if (info->name == evenp)" checks! */
171/* Need to replace them with "if (info == &mode_info[EVENP_INDX])" */
172
Manuel Novoa III cad53642003-03-19 09:13:01 +0000173#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
174
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000175static const struct mode_info mode_info[] = {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000176 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
177 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
178 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
179 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
180 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
181 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
182 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
183 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
184 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
185 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
186 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000187#ifdef CRTSCTS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000188 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000189#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000190 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
191 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
192 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
193 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
194 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
195 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
196 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
197 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
198 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
199 MI_ENTRY("ixon", input, REV, IXON, 0 ),
200 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
201 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000202#ifdef IUCLC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000203 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000204#endif
205#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000206 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000207#endif
208#ifdef IMAXBEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000209 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000210#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000211 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000212#ifdef OLCUC
Manuel Novoa III cad53642003-03-19 09:13:01 +0000213 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000214#endif
215#ifdef OCRNL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000216 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000217#endif
218#ifdef ONLCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000219 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000220#endif
221#ifdef ONOCR
Manuel Novoa III cad53642003-03-19 09:13:01 +0000222 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000223#endif
224#ifdef ONLRET
Manuel Novoa III cad53642003-03-19 09:13:01 +0000225 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000226#endif
227#ifdef OFILL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000228 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000229#endif
230#ifdef OFDEL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000231 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000232#endif
233#ifdef NLDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000234 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
235 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000236#endif
237#ifdef CRDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000238 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
239 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
240 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
241 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000242#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000243
Eric Andersen98e599c2001-02-14 18:47:33 +0000244#ifdef TABDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000245 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
246 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
247 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
248 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000249#else
250# ifdef OXTABS
Manuel Novoa III cad53642003-03-19 09:13:01 +0000251 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000252# endif
253#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000254
Eric Andersen98e599c2001-02-14 18:47:33 +0000255#ifdef BSDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000256 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
257 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000258#endif
259#ifdef VTDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000260 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
261 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000262#endif
263#ifdef FFDLY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000264 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
265 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
Eric Andersen98e599c2001-02-14 18:47:33 +0000266#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000267 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
268 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000269#ifdef IEXTEN
Manuel Novoa III cad53642003-03-19 09:13:01 +0000270 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000271#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000272 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
273 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
274 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
275 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
276 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
277 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000278#ifdef XCASE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000279 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000280#endif
281#ifdef TOSTOP
Manuel Novoa III cad53642003-03-19 09:13:01 +0000282 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000283#endif
284#ifdef ECHOPRT
Manuel Novoa III cad53642003-03-19 09:13:01 +0000285 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
286 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000287#endif
288#ifdef ECHOCTL
Manuel Novoa III cad53642003-03-19 09:13:01 +0000289 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
290 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000291#endif
292#ifdef ECHOKE
Manuel Novoa III cad53642003-03-19 09:13:01 +0000293 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
294 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000295#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000296 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
297 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
298 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
299 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
300 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
301 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
302 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
303 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
304 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
305 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
306 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000307#ifdef IXANY
Manuel Novoa III cad53642003-03-19 09:13:01 +0000308 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000309#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000310#if defined(TABDLY) || defined(OXTABS)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000311 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000312#endif
313#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000314 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
315 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000316#endif
Manuel Novoa III cad53642003-03-19 09:13:01 +0000317 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
318 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
Eric Andersen98e599c2001-02-14 18:47:33 +0000319};
320
Rob Landleybc68cd12006-03-10 19:22:06 +0000321enum {
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000322 NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
Rob Landleybc68cd12006-03-10 19:22:06 +0000323};
Eric Andersen98e599c2001-02-14 18:47:33 +0000324
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000325/* Control character settings */
Eric Andersen98e599c2001-02-14 18:47:33 +0000326struct control_info {
Bernhard Reutner-Fischer21fc7402007-01-17 19:44:24 +0000327 const char * const name; /* Name given on command line */
328 const unsigned char saneval; /* Value to set for 'stty sane' */
329 const unsigned char offset; /* Offset in c_cc */
Eric Andersen98e599c2001-02-14 18:47:33 +0000330};
331
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000332/* Control characters */
Eric Andersen98e599c2001-02-14 18:47:33 +0000333
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000334static const struct control_info control_info[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000335 {"intr", CINTR, VINTR},
336 {"quit", CQUIT, VQUIT},
337 {"erase", CERASE, VERASE},
338 {"kill", CKILL, VKILL},
339 {stty_eof, CEOF, VEOF},
340 {stty_eol, CEOL, VEOL},
Eric Andersen98e599c2001-02-14 18:47:33 +0000341#ifdef VEOL2
Mark Whitley446dd272001-03-02 20:00:54 +0000342 {"eol2", CEOL2, VEOL2},
Eric Andersen98e599c2001-02-14 18:47:33 +0000343#endif
344#ifdef VSWTCH
Mark Whitley446dd272001-03-02 20:00:54 +0000345 {stty_swtch, CSWTCH, VSWTCH},
Eric Andersen98e599c2001-02-14 18:47:33 +0000346#endif
Mark Whitley446dd272001-03-02 20:00:54 +0000347 {"start", CSTART, VSTART},
348 {"stop", CSTOP, VSTOP},
349 {"susp", CSUSP, VSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000350#ifdef VDSUSP
Mark Whitley446dd272001-03-02 20:00:54 +0000351 {"dsusp", CDSUSP, VDSUSP},
Eric Andersen98e599c2001-02-14 18:47:33 +0000352#endif
353#ifdef VREPRINT
Mark Whitley446dd272001-03-02 20:00:54 +0000354 {"rprnt", CRPRNT, VREPRINT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000355#endif
356#ifdef VWERASE
Mark Whitley446dd272001-03-02 20:00:54 +0000357 {"werase", CWERASE, VWERASE},
Eric Andersen98e599c2001-02-14 18:47:33 +0000358#endif
359#ifdef VLNEXT
Mark Whitley446dd272001-03-02 20:00:54 +0000360 {"lnext", CLNEXT, VLNEXT},
Eric Andersen98e599c2001-02-14 18:47:33 +0000361#endif
362#ifdef VFLUSHO
Mark Whitley446dd272001-03-02 20:00:54 +0000363 {"flush", CFLUSHO, VFLUSHO},
Eric Andersen98e599c2001-02-14 18:47:33 +0000364#endif
365#ifdef VSTATUS
Mark Whitley446dd272001-03-02 20:00:54 +0000366 {"status", CSTATUS, VSTATUS},
Eric Andersen98e599c2001-02-14 18:47:33 +0000367#endif
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000368 /* These must be last because of the display routines */
Mark Whitley446dd272001-03-02 20:00:54 +0000369 {stty_min, 1, VMIN},
370 {stty_time, 0, VTIME},
Eric Andersen98e599c2001-02-14 18:47:33 +0000371};
372
Rob Landleybc68cd12006-03-10 19:22:06 +0000373enum {
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000374 NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
Rob Landleybc68cd12006-03-10 19:22:06 +0000375};
Eric Andersen98e599c2001-02-14 18:47:33 +0000376
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000377/* The width of the screen, for output wrapping */
Denis Vlasenko13858992006-10-08 12:49:22 +0000378static unsigned max_col = 80; /* default */
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000379/* Current position, to know when to wrap */
Denis Vlasenko13858992006-10-08 12:49:22 +0000380static unsigned current_col;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000381static const char *device_name = bb_msg_standard_input;
Eric Andersen8876fb22003-06-20 09:01:58 +0000382
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000383/* Return a string that is the printable representation of character CH */
Denis Vlasenko79deb662006-09-19 15:12:12 +0000384/* Adapted from 'cat' by Torbjorn Granlund */
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000385static const char *visible(unsigned int ch)
386{
387 static char buf[10];
388 char *bpout = buf;
389
390 if (ch == _POSIX_VDISABLE)
391 return "<undef>";
392
393 if (ch >= 128) {
394 ch -= 128;
395 *bpout++ = 'M';
396 *bpout++ = '-';
397 }
398
399 if (ch < 32) {
400 *bpout++ = '^';
401 *bpout++ = ch + 64;
402 } else if (ch < 127) {
403 *bpout++ = ch;
404 } else {
405 *bpout++ = '^';
406 *bpout++ = '?';
407 }
408
409 *bpout = '\0';
410 return buf;
411}
412
413static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
414{
415 static const unsigned char tcflag_offsets[] = {
416 offsetof(struct termios, c_cflag), /* control */
417 offsetof(struct termios, c_iflag), /* input */
418 offsetof(struct termios, c_oflag), /* output */
419 offsetof(struct termios, c_lflag), /* local */
420 };
421
422 if (type <= local) {
423 return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
424 }
425 return NULL;
426}
427
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000428static void set_speed_or_die(enum speed_setting type, const char * const arg,
429 struct termios * const mode)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000430{
431 speed_t baud;
432
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +0000433 baud = tty_value_to_baud(xatou(arg));
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000434
435 if (type != output_speed) { /* either input or both */
436 cfsetispeed(mode, baud);
437 }
438 if (type != input_speed) { /* either output or both */
439 cfsetospeed(mode, baud);
440 }
441}
442
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000443static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
Eric Andersen8876fb22003-06-20 09:01:58 +0000444{
445 bb_perror_msg_and_die(fmt, device_name);
446}
447
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000448static void perror_on_device(const char *fmt)
449{
450 bb_perror_msg(fmt, device_name);
451}
452
Eric Andersen98e599c2001-02-14 18:47:33 +0000453/* Print format string MESSAGE and optional args.
454 Wrap to next line first if it won't fit.
Denis Vlasenko9efb0702006-09-19 14:17:10 +0000455 Print a space first unless MESSAGE will start a new line */
Eric Andersen98e599c2001-02-14 18:47:33 +0000456static void wrapf(const char *message, ...)
457{
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000458 char buf[128];
Eric Andersen98e599c2001-02-14 18:47:33 +0000459 va_list args;
Eric Andersen98e599c2001-02-14 18:47:33 +0000460 int buflen;
461
462 va_start(args, message);
Bernhard Reutner-Fischer8eb05492007-01-17 19:46:33 +0000463 buflen = vsnprintf(buf, sizeof(buf), message, args);
Eric Andersen98e599c2001-02-14 18:47:33 +0000464 va_end(args);
Bernhard Reutner-Fischer1a250d92007-01-18 08:41:22 +0000465 /* We seem to be called only with suitable lengths, but check if
466 somebody failed to adhere to this assumption just to be sure. */
467 if (!buflen || buflen >= sizeof(buf)) return;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000468
Eric Andersen98e599c2001-02-14 18:47:33 +0000469 if (current_col > 0) {
Eric Andersen98e599c2001-02-14 18:47:33 +0000470 current_col++;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000471 if (buf[0] != '\n') {
472 if (current_col + buflen >= max_col) {
473 putchar('\n');
474 current_col = 0;
475 } else
476 putchar(' ');
477 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000478 }
479 fputs(buf, stdout);
480 current_col += buflen;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +0000481 if (buf[buflen-1] == '\n')
482 current_col = 0;
Eric Andersen98e599c2001-02-14 18:47:33 +0000483}
484
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000485static void set_window_size(const int rows, const int cols)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000486{
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000487 struct winsize win = { 0, 0, 0, 0};
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000488
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000489 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000490 if (errno != EINVAL) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000491 goto bail;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000492 }
493 memset(&win, 0, sizeof(win));
494 }
495
496 if (rows >= 0)
497 win.ws_row = rows;
498 if (cols >= 0)
499 win.ws_col = cols;
500
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000501 if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000502bail:
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000503 perror_on_device("%s");
504}
505
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000506static void display_window_size(const int fancy)
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000507{
508 const char *fmt_str = "%s\0%s: no size information for this device";
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000509 unsigned width, height;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000510
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000511 if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000512 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
513 perror_on_device(fmt_str);
514 }
515 } else {
516 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +0000517 height, width);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000518 }
519}
520
Eric Andersen98e599c2001-02-14 18:47:33 +0000521static const struct suffix_mult stty_suffixes[] = {
Mark Whitley446dd272001-03-02 20:00:54 +0000522 {"b", 512 },
523 {"k", 1024},
524 {"B", 1024},
525 {NULL, 0 }
Eric Andersen98e599c2001-02-14 18:47:33 +0000526};
527
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000528static const struct mode_info *find_mode(const char *name)
529{
530 int i;
531 for (i = 0; i < NUM_mode_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000532 if (!strcmp(name, mode_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000533 return &mode_info[i];
534 return 0;
535}
536
537static const struct control_info *find_control(const char *name)
538{
539 int i;
540 for (i = 0; i < NUM_control_info; ++i)
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000541 if (!strcmp(name, control_info[i].name))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000542 return &control_info[i];
543 return 0;
544}
545
546enum {
547 param_need_arg = 0x80,
548 param_line = 1 | 0x80,
549 param_rows = 2 | 0x80,
550 param_cols = 3 | 0x80,
551 param_size = 4,
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000552 param_speed = 5,
553 param_ispeed = 6 | 0x80,
554 param_ospeed = 7 | 0x80,
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000555};
556
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000557static int find_param(const char * const name)
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000558{
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000559 const char * const params[] = {
560 "line",
561 "rows",
562 "cols",
563 "columns",
564 "size",
565 "speed",
566 "ispeed",
567 "ospeed",
568 NULL
569 };
570 int i = index_in_str_array(params, name);
Bernhard Reutner-Fischercbd6e652007-02-04 11:13:57 +0000571 if (i < 0)
572 return 0;
573 if (!(i == 4 || i == 5))
574 i |= 0x80;
575
Bernhard Reutner-Fischera6e31ad2007-01-17 19:45:14 +0000576 return i;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000577}
578
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000579static int recover_mode(const char *arg, struct termios *mode)
580{
581 int i, n;
582 unsigned int chr;
583 unsigned long iflag, oflag, cflag, lflag;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000584
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000585 /* Scan into temporaries since it is too much trouble to figure out
586 the right format for 'tcflag_t' */
587 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
588 &iflag, &oflag, &cflag, &lflag, &n) != 4)
589 return 0;
590 mode->c_iflag = iflag;
591 mode->c_oflag = oflag;
592 mode->c_cflag = cflag;
593 mode->c_lflag = lflag;
594 arg += n;
595 for (i = 0; i < NCCS; ++i) {
596 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
597 return 0;
598 mode->c_cc[i] = chr;
599 arg += n;
600 }
601
602 /* Fail if there are too many fields */
603 if (*arg != '\0')
604 return 0;
605
606 return 1;
607}
608
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000609static void display_recoverable(const struct termios *mode,
610 const int ATTRIBUTE_UNUSED dummy)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000611{
612 int i;
613 printf("%lx:%lx:%lx:%lx",
614 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
615 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
616 for (i = 0; i < NCCS; ++i)
617 printf(":%x", (unsigned int) mode->c_cc[i]);
618 putchar('\n');
619}
620
621static void display_speed(const struct termios *mode, int fancy)
622{
623 //01234567 8 9
624 const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
625 unsigned long ispeed, ospeed;
626
627 ospeed = ispeed = cfgetispeed(mode);
628 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
629 ispeed = ospeed; /* in case ispeed was 0 */
630 //0123 4 5 6 7 8 9
631 fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
632 }
633 if (fancy) fmt_str += 9;
634 wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
635}
636
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000637static void do_display(const struct termios *mode, const int all)
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000638{
639 int i;
640 tcflag_t *bitsp;
641 unsigned long mask;
642 int prev_type = control;
643
644 display_speed(mode, 1);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000645 if (all)
646 display_window_size(1);
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000647#ifdef HAVE_C_LINE
648 wrapf("line = %d;\n", mode->c_line);
649#else
650 wrapf("\n");
651#endif
652
653 for (i = 0; control_info[i].name != stty_min; ++i) {
654 /* If swtch is the same as susp, don't print both */
655#if VSWTCH == VSUSP
656 if (control_info[i].name == stty_swtch)
657 continue;
658#endif
659 /* If eof uses the same slot as min, only print whichever applies */
660#if VEOF == VMIN
661 if ((mode->c_lflag & ICANON) == 0
662 && (control_info[i].name == stty_eof
663 || control_info[i].name == stty_eol)) continue;
664#endif
665 wrapf("%s = %s;", control_info[i].name,
666 visible(mode->c_cc[control_info[i].offset]));
667 }
668#if VEOF == VMIN
669 if ((mode->c_lflag & ICANON) == 0)
670#endif
671 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
672 if (current_col) wrapf("\n");
673
674 for (i = 0; i < NUM_mode_info; ++i) {
675 if (mode_info[i].flags & OMIT)
676 continue;
677 if (mode_info[i].type != prev_type) {
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000678 /* wrapf("\n"); */
679 if (current_col) wrapf("\n");
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000680 prev_type = mode_info[i].type;
681 }
682
683 bitsp = mode_type_flag(mode_info[i].type, mode);
684 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000685 if ((*bitsp & mask) == mode_info[i].bits) {
686 if (all || (mode_info[i].flags & SANE_UNSET))
687 wrapf("%s", mode_info[i].name);
688 } else {
689 if ((all && mode_info[i].flags & REV) ||
690 (!all &&
691 (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
692 wrapf("-%s", mode_info[i].name);
693 }
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000694 }
695 if (current_col) wrapf("\n");
696}
697
698static void sane_mode(struct termios *mode)
699{
700 int i;
701 tcflag_t *bitsp;
702
703 for (i = 0; i < NUM_control_info; ++i) {
704#if VMIN == VEOF
705 if (control_info[i].name == stty_min)
706 break;
707#endif
708 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
709 }
710
711 for (i = 0; i < NUM_mode_info; ++i) {
712 if (mode_info[i].flags & SANE_SET) {
713 bitsp = mode_type_flag(mode_info[i].type, mode);
714 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
715 | mode_info[i].bits;
716 } else if (mode_info[i].flags & SANE_UNSET) {
717 bitsp = mode_type_flag(mode_info[i].type, mode);
718 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
719 & ~mode_info[i].bits;
720 }
721 }
722}
723
724/* Save set_mode from #ifdef forest plague */
725#ifndef ONLCR
726#define ONLCR 0
727#endif
728#ifndef OCRNL
729#define OCRNL 0
730#endif
731#ifndef ONLRET
732#define ONLRET 0
733#endif
734#ifndef XCASE
735#define XCASE 0
736#endif
737#ifndef IXANY
738#define IXANY 0
739#endif
740#ifndef TABDLY
741#define TABDLY 0
742#endif
743#ifndef OXTABS
744#define OXTABS 0
745#endif
746#ifndef IUCLC
747#define IUCLC 0
748#endif
749#ifndef OLCUC
750#define OLCUC 0
751#endif
752#ifndef ECHOCTL
753#define ECHOCTL 0
754#endif
755#ifndef ECHOKE
756#define ECHOKE 0
757#endif
758
759static void set_mode(const struct mode_info *info, int reversed,
760 struct termios *mode)
761{
762 tcflag_t *bitsp;
763
764 bitsp = mode_type_flag(info->type, mode);
765
766 if (bitsp) {
767 if (reversed)
768 *bitsp = *bitsp & ~info->mask & ~info->bits;
769 else
770 *bitsp = (*bitsp & ~info->mask) | info->bits;
771 return;
772 }
773
774 /* Combination mode */
775 if (info->name == evenp || info->name == parity) {
776 if (reversed)
777 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
778 else
779 mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
780 } else if (info->name == stty_oddp) {
781 if (reversed)
782 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
783 else
784 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
785 } else if (info->name == stty_nl) {
786 if (reversed) {
787 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
788 mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
789 } else {
790 mode->c_iflag = mode->c_iflag & ~ICRNL;
791 if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
792 }
793 } else if (info->name == stty_ek) {
794 mode->c_cc[VERASE] = CERASE;
795 mode->c_cc[VKILL] = CKILL;
796 } else if (info->name == stty_sane) {
797 sane_mode(mode);
798 }
799 else if (info->name == cbreak) {
800 if (reversed)
801 mode->c_lflag |= ICANON;
802 else
803 mode->c_lflag &= ~ICANON;
804 } else if (info->name == stty_pass8) {
805 if (reversed) {
806 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
807 mode->c_iflag |= ISTRIP;
808 } else {
809 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
810 mode->c_iflag &= ~ISTRIP;
811 }
812 } else if (info->name == litout) {
813 if (reversed) {
814 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
815 mode->c_iflag |= ISTRIP;
816 mode->c_oflag |= OPOST;
817 } else {
818 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
819 mode->c_iflag &= ~ISTRIP;
820 mode->c_oflag &= ~OPOST;
821 }
822 } else if (info->name == raw || info->name == cooked) {
823 if ((info->name[0] == 'r' && reversed)
824 || (info->name[0] == 'c' && !reversed)) {
825 /* Cooked mode */
826 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
827 mode->c_oflag |= OPOST;
828 mode->c_lflag |= ISIG | ICANON;
829#if VMIN == VEOF
830 mode->c_cc[VEOF] = CEOF;
831#endif
832#if VTIME == VEOL
833 mode->c_cc[VEOL] = CEOL;
834#endif
835 } else {
836 /* Raw mode */
837 mode->c_iflag = 0;
838 mode->c_oflag &= ~OPOST;
839 mode->c_lflag &= ~(ISIG | ICANON | XCASE);
840 mode->c_cc[VMIN] = 1;
841 mode->c_cc[VTIME] = 0;
842 }
843 }
844 else if (IXANY && info->name == decctlq) {
845 if (reversed)
846 mode->c_iflag |= IXANY;
847 else
848 mode->c_iflag &= ~IXANY;
849 }
850 else if (TABDLY && info->name == stty_tabs) {
851 if (reversed)
852 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
853 else
854 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
855 }
856 else if (OXTABS && info->name == stty_tabs) {
857 if (reversed)
858 mode->c_oflag |= OXTABS;
859 else
860 mode->c_oflag &= ~OXTABS;
861 }
862 else if (XCASE && IUCLC && OLCUC
863 && (info->name == stty_lcase || info->name == stty_LCASE)) {
864 if (reversed) {
865 mode->c_lflag &= ~XCASE;
866 mode->c_iflag &= ~IUCLC;
867 mode->c_oflag &= ~OLCUC;
868 } else {
869 mode->c_lflag |= XCASE;
870 mode->c_iflag |= IUCLC;
871 mode->c_oflag |= OLCUC;
872 }
873 }
874 else if (info->name == stty_crt) {
875 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
876 }
877 else if (info->name == stty_dec) {
878 mode->c_cc[VINTR] = 3; /* ^C */
879 mode->c_cc[VERASE] = 127; /* DEL */
880 mode->c_cc[VKILL] = 21; /* ^U */
881 mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
882 if (IXANY) mode->c_iflag &= ~IXANY;
883 }
884}
885
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000886static void set_control_char_or_die(const struct control_info *info,
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000887 const char *arg, struct termios *mode)
888{
889 unsigned char value;
890
891 if (info->name == stty_min || info->name == stty_time)
892 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
893 else if (arg[0] == '\0' || arg[1] == '\0')
894 value = arg[0];
Bernhard Reutner-Fischer79cc5592007-01-17 19:46:46 +0000895 else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
Denis Vlasenkof8abc102007-01-12 21:02:04 +0000896 value = _POSIX_VDISABLE;
897 else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
898 value = arg[1] & 0x1f; /* Non-letters get weird results */
899 if (arg[1] == '?')
900 value = 127;
901 } else
902 value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
903 mode->c_cc[info->offset] = value;
904}
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +0000905
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000906#define STTY_require_set_attr (1<<0)
907#define STTY_speed_was_set (1<<1)
908#define STTY_verbose_output (1<<2)
909#define STTY_recoverable_output (1<<3)
910#define STTY_noargs (1<<4)
Denis Vlasenko06af2162007-02-03 17:28:39 +0000911int stty_main(int argc, char **argv);
Rob Landleydfba7412006-03-06 20:47:33 +0000912int stty_main(int argc, char **argv)
Eric Andersen98e599c2001-02-14 18:47:33 +0000913{
914 struct termios mode;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000915 void (*output_func)(const struct termios *, const int);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000916 const char *file_name = NULL;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000917 int display_all = 0;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000918 int stty_state;
919 int k;
920
921 stty_state = STTY_noargs;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000922 output_func = do_display;
Eric Andersen98e599c2001-02-14 18:47:33 +0000923
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000924 /* First pass: only parse/verify command line params */
925 k = 0;
926 while (argv[++k]) {
927 const struct mode_info *mp;
928 const struct control_info *cp;
929 const char *arg = argv[k];
930 const char *argnext = argv[k+1];
931 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +0000932
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000933 if (arg[0] == '-') {
934 int i;
935 mp = find_mode(arg+1);
936 if (mp) {
937 if (!(mp->flags & REV))
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +0000938 goto invalid_argument;
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000939 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000940 continue;
941 }
942 /* It is an option - parse it */
943 i = 0;
944 while (arg[++i]) {
945 switch (arg[i]) {
946 case 'a':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000947 stty_state |= STTY_verbose_output;
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +0000948 output_func = do_display;
949 display_all = 1;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000950 break;
951 case 'g':
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000952 stty_state |= STTY_recoverable_output;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000953 output_func = display_recoverable;
954 break;
955 case 'F':
956 if (file_name)
957 bb_error_msg_and_die("only one device may be specified");
958 file_name = &arg[i+1]; /* "-Fdevice" ? */
959 if (!file_name[0]) { /* nope, "-F device" */
960 int p = k+1; /* argv[p] is argnext */
961 file_name = argnext;
962 if (!file_name)
963 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
964 /* remove -F param from arg[vc] */
965 --argc;
Denis Vlasenko79deb662006-09-19 15:12:12 +0000966 while (argv[p]) { argv[p] = argv[p+1]; ++p; }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000967 }
968 goto end_option;
969 default:
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +0000970 goto invalid_argument;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000971 }
972 }
973end_option:
974 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +0000975 }
976
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000977 mp = find_mode(arg);
978 if (mp) {
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000979 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000980 continue;
981 }
982
983 cp = find_control(arg);
984 if (cp) {
985 if (!argnext)
986 bb_error_msg_and_die(bb_msg_requires_arg, arg);
Denis Vlasenko20b253d2006-09-19 14:24:23 +0000987 /* called for the side effect of xfunc death only */
988 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +0000989 stty_state &= ~STTY_noargs;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +0000990 ++k;
991 continue;
992 }
993
994 param = find_param(arg);
995 if (param & param_need_arg) {
996 if (!argnext)
997 bb_error_msg_and_die(bb_msg_requires_arg, arg);
998 ++k;
999 }
1000
1001 switch (param) {
1002#ifdef HAVE_C_LINE
1003 case param_line:
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001004# ifndef TIOCGWINSZ
Denis Vlasenko13858992006-10-08 12:49:22 +00001005 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Eric Andersen98e599c2001-02-14 18:47:33 +00001006 break;
Denis Vlasenkoe40c04b2006-09-19 14:19:42 +00001007# endif /* else fall-through */
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001008#endif
1009#ifdef TIOCGWINSZ
1010 case param_rows:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001011 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001012 xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001013 break;
1014 case param_size:
1015#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001016 case param_speed:
1017 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001018 case param_ispeed:
1019 /* called for the side effect of xfunc death only */
1020 set_speed_or_die(input_speed, argnext, &mode);
1021 break;
1022 case param_ospeed:
1023 /* called for the side effect of xfunc death only */
1024 set_speed_or_die(output_speed, argnext, &mode);
1025 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001026 default:
1027 if (recover_mode(arg, &mode) == 1) break;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001028 if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001029invalid_argument:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001030 bb_error_msg_and_die("invalid argument '%s'", arg);
1031 }
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001032 stty_state &= ~STTY_noargs;
Eric Andersen98e599c2001-02-14 18:47:33 +00001033 }
1034
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001035 /* Specifying both -a and -g is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001036 if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001037 (STTY_verbose_output | STTY_recoverable_output))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001038 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1039 /* Specifying -a or -g with non-options is an error */
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001040 if (!(stty_state & STTY_noargs) &&
1041 (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001042 bb_error_msg_and_die("modes may not be set when specifying an output style");
Eric Andersen98e599c2001-02-14 18:47:33 +00001043
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001044 /* Now it is safe to start doing things */
Eric Andersen98e599c2001-02-14 18:47:33 +00001045 if (file_name) {
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001046 int fd, fdflags;
Eric Andersen98e599c2001-02-14 18:47:33 +00001047 device_name = file_name;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001048 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
Denis Vlasenko79deb662006-09-19 15:12:12 +00001049 if (fd != STDIN_FILENO) {
1050 dup2(fd, STDIN_FILENO);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001051 close(fd);
1052 }
Denis Vlasenkobd8f43d2006-09-08 17:31:55 +00001053 fdflags = fcntl(STDIN_FILENO, F_GETFL);
Bernhard Reutner-Fischer4fa566d2007-01-17 19:42:30 +00001054 if (fdflags < 0 ||
1055 fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
Denis Vlasenkoa9595882006-09-29 21:30:43 +00001056 perror_on_device_and_die("%s: cannot reset non-blocking mode");
Eric Andersen98e599c2001-02-14 18:47:33 +00001057 }
1058
1059 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001060 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001061 memset(&mode, 0, sizeof(mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001062 if (tcgetattr(STDIN_FILENO, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001063 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001064
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001065 if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
Bernhard Reutner-Fischerd4a745c2007-01-17 19:45:36 +00001066 get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
Bernhard Reutner-Fischer94feb1c2007-01-17 19:46:12 +00001067 output_func(&mode, display_all);
Eric Andersen98e599c2001-02-14 18:47:33 +00001068 return EXIT_SUCCESS;
1069 }
1070
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001071 /* Second pass: perform actions */
Eric Andersenfc059092002-06-06 11:35:29 +00001072 k = 0;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001073 while (argv[++k]) {
1074 const struct mode_info *mp;
1075 const struct control_info *cp;
1076 const char *arg = argv[k];
1077 const char *argnext = argv[k+1];
1078 int param;
Eric Andersen98e599c2001-02-14 18:47:33 +00001079
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001080 if (arg[0] == '-') {
1081 mp = find_mode(arg+1);
1082 if (mp) {
1083 set_mode(mp, 1 /* reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001084 stty_state |= STTY_require_set_attr;
Eric Andersenfc059092002-06-06 11:35:29 +00001085 }
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001086 /* It is an option - already parsed. Skip it */
1087 continue;
Eric Andersen98e599c2001-02-14 18:47:33 +00001088 }
Mark Whitley446dd272001-03-02 20:00:54 +00001089
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001090 mp = find_mode(arg);
1091 if (mp) {
1092 set_mode(mp, 0 /* non-reversed */, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001093 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001094 continue;
1095 }
Mark Whitley446dd272001-03-02 20:00:54 +00001096
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001097 cp = find_control(arg);
1098 if (cp) {
1099 ++k;
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001100 set_control_char_or_die(cp, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001101 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001102 continue;
1103 }
Mark Whitley446dd272001-03-02 20:00:54 +00001104
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001105 param = find_param(arg);
1106 if (param & param_need_arg) {
1107 ++k;
1108 }
1109
1110 switch (param) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001111#ifdef HAVE_C_LINE
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001112 case param_line:
Denis Vlasenko13858992006-10-08 12:49:22 +00001113 mode.c_line = xatoul_sfx(argnext, stty_suffixes);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001114 stty_state |= STTY_require_set_attr;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001115 break;
Eric Andersen98e599c2001-02-14 18:47:33 +00001116#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001117#ifdef TIOCGWINSZ
1118 case param_cols:
Denis Vlasenko13858992006-10-08 12:49:22 +00001119 set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001120 break;
1121 case param_size:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001122 display_window_size(0);
1123 break;
1124 case param_rows:
Denis Vlasenko13858992006-10-08 12:49:22 +00001125 set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001126 break;
1127#endif
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001128 case param_speed:
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001129 display_speed(&mode, 0);
1130 break;
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001131 case param_ispeed:
1132 set_speed_or_die(input_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001133 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001134 break;
1135 case param_ospeed:
1136 set_speed_or_die(output_speed, argnext, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001137 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001138 break;
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001139 default:
1140 if (recover_mode(arg, &mode) == 1)
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001141 stty_state |= STTY_require_set_attr;
Bernhard Reutner-Fischer4950f012007-01-17 19:44:59 +00001142 else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
Denis Vlasenkodebaf2f2006-09-19 14:31:44 +00001143 set_speed_or_die(both_speeds, arg, &mode);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001144 stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
Denis Vlasenko7eab79a2006-09-19 14:16:28 +00001145 } /* else - impossible (caught in the first pass):
1146 bb_error_msg_and_die("invalid argument '%s'", arg); */
Eric Andersen98e599c2001-02-14 18:47:33 +00001147 }
Eric Andersen98e599c2001-02-14 18:47:33 +00001148 }
1149
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001150 if (stty_state & STTY_require_set_attr) {
Eric Andersen98e599c2001-02-14 18:47:33 +00001151 struct termios new_mode;
1152
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001153 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001154 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001155
1156 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1157 it performs *any* of the requested operations. This means it
Denis Vlasenko79deb662006-09-19 15:12:12 +00001158 can report 'success' when it has actually failed to perform
Eric Andersen98e599c2001-02-14 18:47:33 +00001159 some proper subset of the requested operations. To detect
1160 this partial failure, get the current terminal attributes and
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001161 compare them to the requested ones */
Eric Andersen98e599c2001-02-14 18:47:33 +00001162
1163 /* Initialize to all zeroes so there is no risk memcmp will report a
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001164 spurious difference in an uninitialized portion of the structure */
Eric Andersen98e599c2001-02-14 18:47:33 +00001165 memset(&new_mode, 0, sizeof(new_mode));
"Vladimir N. Oleynik"9b9a9202006-01-30 12:23:46 +00001166 if (tcgetattr(STDIN_FILENO, &new_mode))
Denis Vlasenko20b253d2006-09-19 14:24:23 +00001167 perror_on_device_and_die("%s");
Eric Andersen98e599c2001-02-14 18:47:33 +00001168
Eric Andersen98e599c2001-02-14 18:47:33 +00001169 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1170#ifdef CIBAUD
1171 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1172 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1173 sometimes (m1 != m2). The only difference is in the four bits
1174 of the c_cflag field corresponding to the baud rate. To save
1175 Sun users a little confusion, don't report an error if this
1176 happens. But suppress the error only if we haven't tried to
1177 set the baud rate explicitly -- otherwise we'd never give an
Denis Vlasenko9efb0702006-09-19 14:17:10 +00001178 error for a true failure to set the baud rate */
Eric Andersen98e599c2001-02-14 18:47:33 +00001179
1180 new_mode.c_cflag &= (~CIBAUD);
Denis Vlasenko41aaefc2007-01-18 00:53:35 +00001181 if ((stty_state & STTY_speed_was_set)
1182 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
Eric Andersen98e599c2001-02-14 18:47:33 +00001183#endif
Denis Vlasenko89f0b342006-11-18 22:04:09 +00001184 perror_on_device_and_die("%s: cannot perform all requested operations");
Eric Andersen98e599c2001-02-14 18:47:33 +00001185 }
1186 }
1187
1188 return EXIT_SUCCESS;
1189}