source: branches/3.2/mindi-busybox/coreutils/stty.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
File size: 42.8 KB
Line 
1/* 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   Licensed under GPLv2 or later, see file LICENSE in this source tree.
6*/
7/* 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
20   Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
21
22   */
23
24//usage:#define stty_trivial_usage
25//usage:       "[-a|g] [-F DEVICE] [SETTING]..."
26//usage:#define stty_full_usage "\n\n"
27//usage:       "Without arguments, prints baud rate, line discipline,\n"
28//usage:       "and deviations from stty sane\n"
29//usage:     "\n    -F DEVICE   Open device instead of stdin"
30//usage:     "\n    -a      Print all current settings in human-readable form"
31//usage:     "\n    -g      Print in stty-readable form"
32//usage:     "\n    [SETTING]   See manpage"
33
34#include "libbb.h"
35
36#ifndef _POSIX_VDISABLE
37# define _POSIX_VDISABLE ((unsigned char) 0)
38#endif
39
40#define Control(c) ((c) & 0x1f)
41/* Canonical values for control characters */
42#ifndef CINTR
43# define CINTR Control('c')
44#endif
45#ifndef CQUIT
46# define CQUIT 28
47#endif
48#ifndef CERASE
49# define CERASE 127
50#endif
51#ifndef CKILL
52# define CKILL Control('u')
53#endif
54#ifndef CEOF
55# define CEOF Control('d')
56#endif
57#ifndef CEOL
58# define CEOL _POSIX_VDISABLE
59#endif
60#ifndef CSTART
61# define CSTART Control('q')
62#endif
63#ifndef CSTOP
64# define CSTOP Control('s')
65#endif
66#ifndef CSUSP
67# define CSUSP Control('z')
68#endif
69#if defined(VEOL2) && !defined(CEOL2)
70# define CEOL2 _POSIX_VDISABLE
71#endif
72/* glibc-2.12.1 uses only VSWTC name */
73#if defined(VSWTC) && !defined(VSWTCH)
74# define VSWTCH VSWTC
75#endif
76/* ISC renamed swtch to susp for termios, but we'll accept either name */
77#if defined(VSUSP) && !defined(VSWTCH)
78# define VSWTCH VSUSP
79# define CSWTCH CSUSP
80#endif
81#if defined(VSWTCH) && !defined(CSWTCH)
82# define CSWTCH _POSIX_VDISABLE
83#endif
84
85/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
86   So the default is to disable 'swtch.'  */
87#if defined(__sparc__) && defined(__svr4__)
88# undef CSWTCH
89# define CSWTCH _POSIX_VDISABLE
90#endif
91
92#if defined(VWERSE) && !defined(VWERASE)       /* AIX-3.2.5 */
93# define VWERASE VWERSE
94#endif
95#if defined(VDSUSP) && !defined(CDSUSP)
96# define CDSUSP Control('y')
97#endif
98#if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
99# define VREPRINT VRPRNT
100#endif
101#if defined(VREPRINT) && !defined(CRPRNT)
102# define CRPRNT Control('r')
103#endif
104#if defined(VWERASE) && !defined(CWERASE)
105# define CWERASE Control('w')
106#endif
107#if defined(VLNEXT) && !defined(CLNEXT)
108# define CLNEXT Control('v')
109#endif
110#if defined(VDISCARD) && !defined(VFLUSHO)
111# define VFLUSHO VDISCARD
112#endif
113#if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
114# define VFLUSHO VFLUSH
115#endif
116#if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
117# define ECHOCTL CTLECH
118#endif
119#if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
120# define ECHOCTL TCTLECH
121#endif
122#if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
123# define ECHOKE CRTKIL
124#endif
125#if defined(VFLUSHO) && !defined(CFLUSHO)
126# define CFLUSHO Control('o')
127#endif
128#if defined(VSTATUS) && !defined(CSTATUS)
129# define CSTATUS Control('t')
130#endif
131
132/* Save us from #ifdef forest plague */
133#ifndef BSDLY
134# define BSDLY 0
135#endif
136#ifndef CIBAUD
137# define CIBAUD 0
138#endif
139#ifndef CRDLY
140# define CRDLY 0
141#endif
142#ifndef CRTSCTS
143# define CRTSCTS 0
144#endif
145#ifndef ECHOCTL
146# define ECHOCTL 0
147#endif
148#ifndef ECHOKE
149# define ECHOKE 0
150#endif
151#ifndef ECHOPRT
152# define ECHOPRT 0
153#endif
154#ifndef FFDLY
155# define FFDLY 0
156#endif
157#ifndef IEXTEN
158# define IEXTEN 0
159#endif
160#ifndef IMAXBEL
161# define IMAXBEL 0
162#endif
163#ifndef IUCLC
164# define IUCLC 0
165#endif
166#ifndef IXANY
167# define IXANY 0
168#endif
169#ifndef NLDLY
170# define NLDLY 0
171#endif
172#ifndef OCRNL
173# define OCRNL 0
174#endif
175#ifndef OFDEL
176# define OFDEL 0
177#endif
178#ifndef OFILL
179# define OFILL 0
180#endif
181#ifndef OLCUC
182# define OLCUC 0
183#endif
184#ifndef ONLCR
185# define ONLCR 0
186#endif
187#ifndef ONLRET
188# define ONLRET 0
189#endif
190#ifndef ONOCR
191# define ONOCR 0
192#endif
193#ifndef OXTABS
194# define OXTABS 0
195#endif
196#ifndef TABDLY
197# define TABDLY 0
198#endif
199#ifndef TAB1
200# define TAB1 0
201#endif
202#ifndef TAB2
203# define TAB2 0
204#endif
205#ifndef TOSTOP
206# define TOSTOP 0
207#endif
208#ifndef VDSUSP
209# define VDSUSP 0
210#endif
211#ifndef VEOL2
212# define VEOL2 0
213#endif
214#ifndef VFLUSHO
215# define VFLUSHO 0
216#endif
217#ifndef VLNEXT
218# define VLNEXT 0
219#endif
220#ifndef VREPRINT
221# define VREPRINT 0
222#endif
223#ifndef VSTATUS
224# define VSTATUS 0
225#endif
226#ifndef VSWTCH
227# define VSWTCH 0
228#endif
229#ifndef VTDLY
230# define VTDLY 0
231#endif
232#ifndef VWERASE
233# define VWERASE 0
234#endif
235#ifndef XCASE
236# define XCASE 0
237#endif
238#ifndef IUTF8
239# define IUTF8 0
240#endif
241
242/* Which speeds to set */
243enum speed_setting {
244    input_speed, output_speed, both_speeds
245};
246
247/* Which member(s) of 'struct termios' a mode uses */
248enum {
249    /* Do NOT change the order or values, as mode_type_flag()
250     * depends on them */
251    control, input, output, local, combination
252};
253
254/* Flags for 'struct mode_info' */
255#define SANE_SET 1              /* Set in 'sane' mode                  */
256#define SANE_UNSET 2            /* Unset in 'sane' mode                */
257#define REV 4                   /* Can be turned off by prepending '-' */
258#define OMIT 8                  /* Don't display value                 */
259
260
261/* Each mode.
262 * This structure should be kept as small as humanly possible.
263 */
264struct mode_info {
265    const uint8_t type;           /* Which structure element to change    */
266    const uint8_t flags;          /* Setting and display options          */
267    /* only these values are ever used, so... */
268#if   (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
269    const uint8_t mask;
270#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
271    const uint16_t mask;
272#else
273    const tcflag_t mask;          /* Other bits to turn off for this mode */
274#endif
275    /* was using short here, but ppc32 was unhappy */
276    const tcflag_t bits;          /* Bits to set for this mode            */
277};
278
279enum {
280    /* Must match mode_name[] and mode_info[] order! */
281    IDX_evenp = 0,
282    IDX_parity,
283    IDX_oddp,
284    IDX_nl,
285    IDX_ek,
286    IDX_sane,
287    IDX_cooked,
288    IDX_raw,
289    IDX_pass8,
290    IDX_litout,
291    IDX_cbreak,
292    IDX_crt,
293    IDX_dec,
294#if IXANY
295    IDX_decctlq,
296#endif
297#if TABDLY || OXTABS
298    IDX_tabs,
299#endif
300#if XCASE && IUCLC && OLCUC
301    IDX_lcase,
302    IDX_LCASE,
303#endif
304};
305
306#define MI_ENTRY(N,T,F,B,M) N "\0"
307
308/* Mode names given on command line */
309static const char mode_name[] =
310    MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
311    MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
312    MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
313    MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
314    MI_ENTRY("ek",       combination, OMIT,              0,          0 )
315    MI_ENTRY("sane",     combination, OMIT,              0,          0 )
316    MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
317    MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
318    MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
319    MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
320    MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
321    MI_ENTRY("crt",      combination, OMIT,              0,          0 )
322    MI_ENTRY("dec",      combination, OMIT,              0,          0 )
323#if IXANY
324    MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
325#endif
326#if TABDLY || OXTABS
327    MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
328#endif
329#if XCASE && IUCLC && OLCUC
330    MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
331    MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
332#endif
333    MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
334    MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
335    MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
336    MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
337    MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
338    MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
339    MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
340    MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
341    MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
342    MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
343    MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
344#if CRTSCTS
345    MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
346#endif
347    MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
348    MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
349    MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
350    MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
351    MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
352    MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
353    MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
354    MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
355    MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
356    MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
357    MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
358    MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
359#if IUCLC
360    MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
361#endif
362#if IXANY
363    MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
364#endif
365#if IMAXBEL
366    MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
367#endif
368#if IUTF8
369    MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
370#endif
371    MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
372#if OLCUC
373    MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
374#endif
375#if OCRNL
376    MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
377#endif
378#if ONLCR
379    MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
380#endif
381#if ONOCR
382    MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
383#endif
384#if ONLRET
385    MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
386#endif
387#if OFILL
388    MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
389#endif
390#if OFDEL
391    MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
392#endif
393#if NLDLY
394    MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
395    MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
396#endif
397#if CRDLY
398    MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
399    MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
400    MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
401    MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
402#endif
403
404#if TABDLY
405    MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
406# if TAB2
407    MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
408# endif
409# if TAB1
410    MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
411# endif
412    MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
413#else
414# if OXTABS
415    MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
416# endif
417#endif
418
419#if BSDLY
420    MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
421    MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
422#endif
423#if VTDLY
424    MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
425    MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
426#endif
427#if FFDLY
428    MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
429    MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
430#endif
431    MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
432    MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
433#if IEXTEN
434    MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
435#endif
436    MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
437    MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
438    MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
439    MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
440    MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
441    MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
442#if XCASE
443    MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
444#endif
445#if TOSTOP
446    MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
447#endif
448#if ECHOPRT
449    MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
450    MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
451#endif
452#if ECHOCTL
453    MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
454    MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
455#endif
456#if ECHOKE
457    MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
458    MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
459#endif
460    ;
461
462#undef MI_ENTRY
463#define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
464
465static const struct mode_info mode_info[] = {
466    /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
467    MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
468    MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
469    MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
470    MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
471    MI_ENTRY("ek",       combination, OMIT,              0,          0 )
472    MI_ENTRY("sane",     combination, OMIT,              0,          0 )
473    MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
474    MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
475    MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
476    MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
477    MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
478    MI_ENTRY("crt",      combination, OMIT,              0,          0 )
479    MI_ENTRY("dec",      combination, OMIT,              0,          0 )
480#if IXANY
481    MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
482#endif
483#if TABDLY || OXTABS
484    MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
485#endif
486#if XCASE && IUCLC && OLCUC
487    MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
488    MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
489#endif
490    MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
491    MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
492    MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
493    MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
494    MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
495    MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
496    MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
497    MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
498    MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
499    MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
500    MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
501#if CRTSCTS
502    MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
503#endif
504    MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
505    MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
506    MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
507    MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
508    MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
509    MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
510    MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
511    MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
512    MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
513    MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
514    MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
515    MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
516#if IUCLC
517    MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
518#endif
519#if IXANY
520    MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
521#endif
522#if IMAXBEL
523    MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
524#endif
525#if IUTF8
526    MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
527#endif
528    MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
529#if OLCUC
530    MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
531#endif
532#if OCRNL
533    MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
534#endif
535#if ONLCR
536    MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
537#endif
538#if ONOCR
539    MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
540#endif
541#if ONLRET
542    MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
543#endif
544#if OFILL
545    MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
546#endif
547#if OFDEL
548    MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
549#endif
550#if NLDLY
551    MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
552    MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
553#endif
554#if CRDLY
555    MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
556    MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
557    MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
558    MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
559#endif
560
561#if TABDLY
562    MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
563# if TAB2
564    MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
565# endif
566# if TAB1
567    MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
568# endif
569    MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
570#else
571# if OXTABS
572    MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
573# endif
574#endif
575
576#if BSDLY
577    MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
578    MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
579#endif
580#if VTDLY
581    MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
582    MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
583#endif
584#if FFDLY
585    MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
586    MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
587#endif
588    MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
589    MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
590#if IEXTEN
591    MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
592#endif
593    MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
594    MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
595    MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
596    MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
597    MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
598    MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
599#if XCASE
600    MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
601#endif
602#if TOSTOP
603    MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
604#endif
605#if ECHOPRT
606    MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
607    MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
608#endif
609#if ECHOCTL
610    MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
611    MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
612#endif
613#if ECHOKE
614    MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
615    MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
616#endif
617};
618
619enum {
620    NUM_mode_info = ARRAY_SIZE(mode_info)
621};
622
623
624/* Control characters */
625struct control_info {
626    const uint8_t saneval;  /* Value to set for 'stty sane' */
627    const uint8_t offset;   /* Offset in c_cc */
628};
629
630enum {
631    /* Must match control_name[] and control_info[] order! */
632    CIDX_intr = 0,
633    CIDX_quit,
634    CIDX_erase,
635    CIDX_kill,
636    CIDX_eof,
637    CIDX_eol,
638#if VEOL2
639    CIDX_eol2,
640#endif
641#if VSWTCH
642    CIDX_swtch,
643#endif
644    CIDX_start,
645    CIDX_stop,
646    CIDX_susp,
647#if VDSUSP
648    CIDX_dsusp,
649#endif
650#if VREPRINT
651    CIDX_rprnt,
652#endif
653#if VWERASE
654    CIDX_werase,
655#endif
656#if VLNEXT
657    CIDX_lnext,
658#endif
659#if VFLUSHO
660    CIDX_flush,
661#endif
662#if VSTATUS
663    CIDX_status,
664#endif
665    CIDX_min,
666    CIDX_time,
667};
668
669#define CI_ENTRY(n,s,o) n "\0"
670
671/* Name given on command line */
672static const char control_name[] =
673    CI_ENTRY("intr",     CINTR,   VINTR   )
674    CI_ENTRY("quit",     CQUIT,   VQUIT   )
675    CI_ENTRY("erase",    CERASE,  VERASE  )
676    CI_ENTRY("kill",     CKILL,   VKILL   )
677    CI_ENTRY("eof",      CEOF,    VEOF    )
678    CI_ENTRY("eol",      CEOL,    VEOL    )
679#if VEOL2
680    CI_ENTRY("eol2",     CEOL2,   VEOL2   )
681#endif
682#if VSWTCH
683    CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
684#endif
685    CI_ENTRY("start",    CSTART,  VSTART  )
686    CI_ENTRY("stop",     CSTOP,   VSTOP   )
687    CI_ENTRY("susp",     CSUSP,   VSUSP   )
688#if VDSUSP
689    CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
690#endif
691#if VREPRINT
692    CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
693#endif
694#if VWERASE
695    CI_ENTRY("werase",   CWERASE, VWERASE )
696#endif
697#if VLNEXT
698    CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
699#endif
700#if VFLUSHO
701    CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
702#endif
703#if VSTATUS
704    CI_ENTRY("status",   CSTATUS, VSTATUS )
705#endif
706    /* These must be last because of the display routines */
707    CI_ENTRY("min",      1,       VMIN    )
708    CI_ENTRY("time",     0,       VTIME   )
709    ;
710
711#undef CI_ENTRY
712#define CI_ENTRY(n,s,o) { s, o },
713
714static const struct control_info control_info[] = {
715    /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
716    CI_ENTRY("intr",     CINTR,   VINTR   )
717    CI_ENTRY("quit",     CQUIT,   VQUIT   )
718    CI_ENTRY("erase",    CERASE,  VERASE  )
719    CI_ENTRY("kill",     CKILL,   VKILL   )
720    CI_ENTRY("eof",      CEOF,    VEOF    )
721    CI_ENTRY("eol",      CEOL,    VEOL    )
722#if VEOL2
723    CI_ENTRY("eol2",     CEOL2,   VEOL2   )
724#endif
725#if VSWTCH
726    CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
727#endif
728    CI_ENTRY("start",    CSTART,  VSTART  )
729    CI_ENTRY("stop",     CSTOP,   VSTOP   )
730    CI_ENTRY("susp",     CSUSP,   VSUSP   )
731#if VDSUSP
732    CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
733#endif
734#if VREPRINT
735    CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
736#endif
737#if VWERASE
738    CI_ENTRY("werase",   CWERASE, VWERASE )
739#endif
740#if VLNEXT
741    CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
742#endif
743#if VFLUSHO
744    CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
745#endif
746#if VSTATUS
747    CI_ENTRY("status",   CSTATUS, VSTATUS )
748#endif
749    /* These must be last because of the display routines */
750    CI_ENTRY("min",      1,       VMIN    )
751    CI_ENTRY("time",     0,       VTIME   )
752};
753
754enum {
755    NUM_control_info = ARRAY_SIZE(control_info)
756};
757
758
759struct globals {
760    const char *device_name;
761    /* The width of the screen, for output wrapping */
762    unsigned max_col;
763    /* Current position, to know when to wrap */
764    unsigned current_col;
765    char buf[10];
766} FIX_ALIASING;
767#define G (*(struct globals*)&bb_common_bufsiz1)
768#define INIT_G() do { \
769    G.device_name = bb_msg_standard_input; \
770    G.max_col = 80; \
771} while (0)
772
773
774/* Return a string that is the printable representation of character CH */
775/* Adapted from 'cat' by Torbjorn Granlund */
776static const char *visible(unsigned ch)
777{
778    char *bpout = G.buf;
779
780    if (ch == _POSIX_VDISABLE)
781        return "<undef>";
782
783    if (ch >= 128) {
784        ch -= 128;
785        *bpout++ = 'M';
786        *bpout++ = '-';
787    }
788
789    if (ch < 32) {
790        *bpout++ = '^';
791        *bpout++ = ch + 64;
792    } else if (ch < 127) {
793        *bpout++ = ch;
794    } else {
795        *bpout++ = '^';
796        *bpout++ = '?';
797    }
798
799    *bpout = '\0';
800    return G.buf;
801}
802
803static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
804{
805    static const uint8_t tcflag_offsets[] ALIGN1 = {
806        offsetof(struct termios, c_cflag), /* control */
807        offsetof(struct termios, c_iflag), /* input */
808        offsetof(struct termios, c_oflag), /* output */
809        offsetof(struct termios, c_lflag)  /* local */
810    };
811
812    if (type <= local) {
813        return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
814    }
815    return NULL;
816}
817
818static void set_speed_or_die(enum speed_setting type, const char *arg,
819                    struct termios *mode)
820{
821    speed_t baud;
822
823    baud = tty_value_to_baud(xatou(arg));
824
825    if (type != output_speed) {     /* either input or both */
826        cfsetispeed(mode, baud);
827    }
828    if (type != input_speed) {      /* either output or both */
829        cfsetospeed(mode, baud);
830    }
831}
832
833static NORETURN void perror_on_device_and_die(const char *fmt)
834{
835    bb_perror_msg_and_die(fmt, G.device_name);
836}
837
838static void perror_on_device(const char *fmt)
839{
840    bb_perror_msg(fmt, G.device_name);
841}
842
843/* Print format string MESSAGE and optional args.
844   Wrap to next line first if it won't fit.
845   Print a space first unless MESSAGE will start a new line */
846static void wrapf(const char *message, ...)
847{
848    char buf[128];
849    va_list args;
850    unsigned buflen;
851
852    va_start(args, message);
853    buflen = vsnprintf(buf, sizeof(buf), message, args);
854    va_end(args);
855    /* We seem to be called only with suitable lengths, but check if
856       somebody failed to adhere to this assumption just to be sure.  */
857    if (!buflen || buflen >= sizeof(buf)) return;
858
859    if (G.current_col > 0) {
860        G.current_col++;
861        if (buf[0] != '\n') {
862            if (G.current_col + buflen >= G.max_col) {
863                bb_putchar('\n');
864                G.current_col = 0;
865            } else
866                bb_putchar(' ');
867        }
868    }
869    fputs(buf, stdout);
870    G.current_col += buflen;
871    if (buf[buflen-1] == '\n')
872        G.current_col = 0;
873}
874
875static void newline(void)
876{
877    if (G.current_col != 0)
878        wrapf("\n");
879}
880
881#ifdef TIOCGWINSZ
882static void set_window_size(int rows, int cols)
883{
884    struct winsize win = { 0, 0, 0, 0 };
885
886    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
887        if (errno != EINVAL) {
888            goto bail;
889        }
890        memset(&win, 0, sizeof(win));
891    }
892
893    if (rows >= 0)
894        win.ws_row = rows;
895    if (cols >= 0)
896        win.ws_col = cols;
897
898    if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
899bail:
900        perror_on_device("%s");
901}
902#endif
903
904static void display_window_size(int fancy)
905{
906    const char *fmt_str = "%s\0%s: no size information for this device";
907    unsigned width, height;
908
909    if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
910        if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
911            perror_on_device(fmt_str);
912        }
913    } else {
914        wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
915                height, width);
916    }
917}
918
919static const struct suffix_mult stty_suffixes[] = {
920    { "b",  512 },
921    { "k", 1024 },
922    { "B", 1024 },
923    { "", 0 }
924};
925
926static const struct mode_info *find_mode(const char *name)
927{
928    int i = index_in_strings(mode_name, name);
929    return i >= 0 ? &mode_info[i] : NULL;
930}
931
932static const struct control_info *find_control(const char *name)
933{
934    int i = index_in_strings(control_name, name);
935    return i >= 0 ? &control_info[i] : NULL;
936}
937
938enum {
939    param_need_arg = 0x80,
940    param_line    = 1 | 0x80,
941    param_rows    = 2 | 0x80,
942    param_cols    = 3 | 0x80,
943    param_columns = 4 | 0x80,
944    param_size    = 5,
945    param_speed   = 6,
946    param_ispeed  = 7 | 0x80,
947    param_ospeed  = 8 | 0x80,
948};
949
950static int find_param(const char *name)
951{
952    static const char params[] ALIGN1 =
953        "line\0"    /* 1 */
954        "rows\0"    /* 2 */
955        "cols\0"    /* 3 */
956        "columns\0" /* 4 */
957        "size\0"    /* 5 */
958        "speed\0"   /* 6 */
959        "ispeed\0"
960        "ospeed\0";
961    int i = index_in_strings(params, name) + 1;
962    if (i == 0)
963        return 0;
964    if (i != 5 && i != 6)
965        i |= 0x80;
966    return i;
967}
968
969static int recover_mode(const char *arg, struct termios *mode)
970{
971    int i, n;
972    unsigned chr;
973    unsigned long iflag, oflag, cflag, lflag;
974
975    /* Scan into temporaries since it is too much trouble to figure out
976       the right format for 'tcflag_t' */
977    if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
978               &iflag, &oflag, &cflag, &lflag, &n) != 4)
979        return 0;
980    mode->c_iflag = iflag;
981    mode->c_oflag = oflag;
982    mode->c_cflag = cflag;
983    mode->c_lflag = lflag;
984    arg += n;
985    for (i = 0; i < NCCS; ++i) {
986        if (sscanf(arg, ":%x%n", &chr, &n) != 1)
987            return 0;
988        mode->c_cc[i] = chr;
989        arg += n;
990    }
991
992    /* Fail if there are too many fields */
993    if (*arg != '\0')
994        return 0;
995
996    return 1;
997}
998
999static void display_recoverable(const struct termios *mode,
1000                int UNUSED_PARAM dummy)
1001{
1002    int i;
1003    printf("%lx:%lx:%lx:%lx",
1004           (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1005           (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1006    for (i = 0; i < NCCS; ++i)
1007        printf(":%x", (unsigned int) mode->c_cc[i]);
1008    bb_putchar('\n');
1009}
1010
1011static void display_speed(const struct termios *mode, int fancy)
1012{
1013    //____________________ 01234567 8 9
1014    const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
1015    unsigned long ispeed, ospeed;
1016
1017    ispeed = cfgetispeed(mode);
1018    ospeed = cfgetospeed(mode);
1019    if (ispeed == 0 || ispeed == ospeed) {
1020        ispeed = ospeed;                /* in case ispeed was 0 */
1021        //________ 0123 4 5 6 7 8 9
1022        fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
1023    }
1024    if (fancy) fmt_str += 9;
1025    wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1026}
1027
1028static void do_display(const struct termios *mode, int all)
1029{
1030    int i;
1031    tcflag_t *bitsp;
1032    unsigned long mask;
1033    int prev_type = control;
1034
1035    display_speed(mode, 1);
1036    if (all)
1037        display_window_size(1);
1038#ifdef __linux__
1039    wrapf("line = %u;\n", mode->c_line);
1040#else
1041    newline();
1042#endif
1043
1044    for (i = 0; i != CIDX_min; ++i) {
1045        /* If swtch is the same as susp, don't print both */
1046#if VSWTCH == VSUSP
1047        if (i == CIDX_swtch)
1048            continue;
1049#endif
1050        /* If eof uses the same slot as min, only print whichever applies */
1051#if VEOF == VMIN
1052        if (!(mode->c_lflag & ICANON)
1053         && (i == CIDX_eof || i == CIDX_eol)
1054        ) {
1055            continue;
1056        }
1057#endif
1058        wrapf("%s = %s;", nth_string(control_name, i),
1059                visible(mode->c_cc[control_info[i].offset]));
1060    }
1061#if VEOF == VMIN
1062    if ((mode->c_lflag & ICANON) == 0)
1063#endif
1064        wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1065    newline();
1066
1067    for (i = 0; i < NUM_mode_info; ++i) {
1068        if (mode_info[i].flags & OMIT)
1069            continue;
1070        if (mode_info[i].type != prev_type) {
1071            newline();
1072            prev_type = mode_info[i].type;
1073        }
1074
1075        bitsp = mode_type_flag(mode_info[i].type, mode);
1076        mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1077        if ((*bitsp & mask) == mode_info[i].bits) {
1078            if (all || (mode_info[i].flags & SANE_UNSET))
1079                wrapf("-%s"+1, nth_string(mode_name, i));
1080        } else {
1081            if ((all && mode_info[i].flags & REV)
1082             || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
1083            ) {
1084                wrapf("-%s", nth_string(mode_name, i));
1085            }
1086        }
1087    }
1088    newline();
1089}
1090
1091static void sane_mode(struct termios *mode)
1092{
1093    int i;
1094    tcflag_t *bitsp;
1095
1096    for (i = 0; i < NUM_control_info; ++i) {
1097#if VMIN == VEOF
1098        if (i == CIDX_min)
1099            break;
1100#endif
1101        mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1102    }
1103
1104    for (i = 0; i < NUM_mode_info; ++i) {
1105        if (mode_info[i].flags & SANE_SET) {
1106            bitsp = mode_type_flag(mode_info[i].type, mode);
1107            *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1108                | mode_info[i].bits;
1109        } else if (mode_info[i].flags & SANE_UNSET) {
1110            bitsp = mode_type_flag(mode_info[i].type, mode);
1111            *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1112                & ~mode_info[i].bits;
1113        }
1114    }
1115}
1116
1117static void set_mode(const struct mode_info *info, int reversed,
1118                    struct termios *mode)
1119{
1120    tcflag_t *bitsp;
1121
1122    bitsp = mode_type_flag(info->type, mode);
1123
1124    if (bitsp) {
1125        if (reversed)
1126            *bitsp = *bitsp & ~info->mask & ~info->bits;
1127        else
1128            *bitsp = (*bitsp & ~info->mask) | info->bits;
1129        return;
1130    }
1131
1132    /* Combination mode */
1133    if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1134        if (reversed)
1135            mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1136        else
1137            mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1138    } else if (info == &mode_info[IDX_oddp]) {
1139        if (reversed)
1140            mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1141        else
1142            mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1143    } else if (info == &mode_info[IDX_nl]) {
1144        if (reversed) {
1145            mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1146            mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1147        } else {
1148            mode->c_iflag = mode->c_iflag & ~ICRNL;
1149            if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1150        }
1151    } else if (info == &mode_info[IDX_ek]) {
1152        mode->c_cc[VERASE] = CERASE;
1153        mode->c_cc[VKILL] = CKILL;
1154    } else if (info == &mode_info[IDX_sane]) {
1155        sane_mode(mode);
1156    } else if (info == &mode_info[IDX_cbreak]) {
1157        if (reversed)
1158            mode->c_lflag |= ICANON;
1159        else
1160            mode->c_lflag &= ~ICANON;
1161    } else if (info == &mode_info[IDX_pass8]) {
1162        if (reversed) {
1163            mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1164            mode->c_iflag |= ISTRIP;
1165        } else {
1166            mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1167            mode->c_iflag &= ~ISTRIP;
1168        }
1169    } else if (info == &mode_info[IDX_litout]) {
1170        if (reversed) {
1171            mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1172            mode->c_iflag |= ISTRIP;
1173            mode->c_oflag |= OPOST;
1174        } else {
1175            mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1176            mode->c_iflag &= ~ISTRIP;
1177            mode->c_oflag &= ~OPOST;
1178        }
1179    } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1180        if ((info == &mode_info[IDX_raw] && reversed)
1181         || (info == &mode_info[IDX_cooked] && !reversed)
1182        ) {
1183            /* Cooked mode */
1184            mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1185            mode->c_oflag |= OPOST;
1186            mode->c_lflag |= ISIG | ICANON;
1187#if VMIN == VEOF
1188            mode->c_cc[VEOF] = CEOF;
1189#endif
1190#if VTIME == VEOL
1191            mode->c_cc[VEOL] = CEOL;
1192#endif
1193        } else {
1194            /* Raw mode */
1195            mode->c_iflag = 0;
1196            mode->c_oflag &= ~OPOST;
1197            mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1198            mode->c_cc[VMIN] = 1;
1199            mode->c_cc[VTIME] = 0;
1200        }
1201    }
1202#if IXANY
1203    else if (info == &mode_info[IDX_decctlq]) {
1204        if (reversed)
1205            mode->c_iflag |= IXANY;
1206        else
1207            mode->c_iflag &= ~IXANY;
1208    }
1209#endif
1210#if TABDLY
1211    else if (info == &mode_info[IDX_tabs]) {
1212        if (reversed)
1213            mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1214        else
1215            mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1216    }
1217#endif
1218#if OXTABS
1219    else if (info == &mode_info[IDX_tabs]) {
1220        if (reversed)
1221            mode->c_oflag |= OXTABS;
1222        else
1223            mode->c_oflag &= ~OXTABS;
1224    }
1225#endif
1226#if XCASE && IUCLC && OLCUC
1227    else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
1228        if (reversed) {
1229            mode->c_lflag &= ~XCASE;
1230            mode->c_iflag &= ~IUCLC;
1231            mode->c_oflag &= ~OLCUC;
1232        } else {
1233            mode->c_lflag |= XCASE;
1234            mode->c_iflag |= IUCLC;
1235            mode->c_oflag |= OLCUC;
1236        }
1237    }
1238#endif
1239    else if (info == &mode_info[IDX_crt]) {
1240        mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1241    } else if (info == &mode_info[IDX_dec]) {
1242        mode->c_cc[VINTR] = 3; /* ^C */
1243        mode->c_cc[VERASE] = 127; /* DEL */
1244        mode->c_cc[VKILL] = 21; /* ^U */
1245        mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1246        if (IXANY) mode->c_iflag &= ~IXANY;
1247    }
1248}
1249
1250static void set_control_char_or_die(const struct control_info *info,
1251            const char *arg, struct termios *mode)
1252{
1253    unsigned char value;
1254
1255    if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1256        value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1257    else if (arg[0] == '\0' || arg[1] == '\0')
1258        value = arg[0];
1259    else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1260        value = _POSIX_VDISABLE;
1261    else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1262        value = arg[1] & 0x1f; /* Non-letters get weird results */
1263        if (arg[1] == '?')
1264            value = 127;
1265    } else
1266        value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1267    mode->c_cc[info->offset] = value;
1268}
1269
1270#define STTY_require_set_attr   (1 << 0)
1271#define STTY_speed_was_set      (1 << 1)
1272#define STTY_verbose_output     (1 << 2)
1273#define STTY_recoverable_output (1 << 3)
1274#define STTY_noargs             (1 << 4)
1275
1276int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1277int stty_main(int argc UNUSED_PARAM, char **argv)
1278{
1279    struct termios mode;
1280    void (*output_func)(const struct termios *, int);
1281    const char *file_name = NULL;
1282    int display_all = 0;
1283    int stty_state;
1284    int k;
1285
1286    INIT_G();
1287
1288    stty_state = STTY_noargs;
1289    output_func = do_display;
1290
1291    /* First pass: only parse/verify command line params */
1292    k = 0;
1293    while (argv[++k]) {
1294        const struct mode_info *mp;
1295        const struct control_info *cp;
1296        const char *arg = argv[k];
1297        const char *argnext = argv[k+1];
1298        int param;
1299
1300        if (arg[0] == '-') {
1301            int i;
1302            mp = find_mode(arg+1);
1303            if (mp) {
1304                if (!(mp->flags & REV))
1305                    goto invalid_argument;
1306                stty_state &= ~STTY_noargs;
1307                continue;
1308            }
1309            /* It is an option - parse it */
1310            i = 0;
1311            while (arg[++i]) {
1312                switch (arg[i]) {
1313                case 'a':
1314                    stty_state |= STTY_verbose_output;
1315                    output_func = do_display;
1316                    display_all = 1;
1317                    break;
1318                case 'g':
1319                    stty_state |= STTY_recoverable_output;
1320                    output_func = display_recoverable;
1321                    break;
1322                case 'F':
1323                    if (file_name)
1324                        bb_error_msg_and_die("only one device may be specified");
1325                    file_name = &arg[i+1]; /* "-Fdevice" ? */
1326                    if (!file_name[0]) { /* nope, "-F device" */
1327                        int p = k+1; /* argv[p] is argnext */
1328                        file_name = argnext;
1329                        if (!file_name)
1330                            bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1331                        /* remove -F param from arg[vc] */
1332                        while (argv[p]) {
1333                            argv[p] = argv[p+1];
1334                            ++p;
1335                        }
1336                    }
1337                    goto end_option;
1338                default:
1339                    goto invalid_argument;
1340                }
1341            }
1342 end_option:
1343            continue;
1344        }
1345
1346        mp = find_mode(arg);
1347        if (mp) {
1348            stty_state &= ~STTY_noargs;
1349            continue;
1350        }
1351
1352        cp = find_control(arg);
1353        if (cp) {
1354            if (!argnext)
1355                bb_error_msg_and_die(bb_msg_requires_arg, arg);
1356            /* called for the side effect of xfunc death only */
1357            set_control_char_or_die(cp, argnext, &mode);
1358            stty_state &= ~STTY_noargs;
1359            ++k;
1360            continue;
1361        }
1362
1363        param = find_param(arg);
1364        if (param & param_need_arg) {
1365            if (!argnext)
1366                bb_error_msg_and_die(bb_msg_requires_arg, arg);
1367            ++k;
1368        }
1369
1370        switch (param) {
1371#ifdef __linux__
1372        case param_line:
1373# ifndef TIOCGWINSZ
1374            xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1375            break;
1376# endif /* else fall-through */
1377#endif
1378#ifdef TIOCGWINSZ
1379        case param_rows:
1380        case param_cols:
1381        case param_columns:
1382            xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1383            break;
1384        case param_size:
1385#endif
1386        case param_speed:
1387            break;
1388        case param_ispeed:
1389            /* called for the side effect of xfunc death only */
1390            set_speed_or_die(input_speed, argnext, &mode);
1391            break;
1392        case param_ospeed:
1393            /* called for the side effect of xfunc death only */
1394            set_speed_or_die(output_speed, argnext, &mode);
1395            break;
1396        default:
1397            if (recover_mode(arg, &mode) == 1) break;
1398            if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1399 invalid_argument:
1400            bb_error_msg_and_die("invalid argument '%s'", arg);
1401        }
1402        stty_state &= ~STTY_noargs;
1403    }
1404
1405    /* Specifying both -a and -g is an error */
1406    if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1407        (STTY_verbose_output | STTY_recoverable_output)
1408    ) {
1409        bb_error_msg_and_die("-a and -g are mutually exclusive");
1410    }
1411    /* Specifying -a or -g with non-options is an error */
1412    if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
1413     && !(stty_state & STTY_noargs)
1414    ) {
1415        bb_error_msg_and_die("modes may not be set when -a or -g is used");
1416    }
1417
1418    /* Now it is safe to start doing things */
1419    if (file_name) {
1420        G.device_name = file_name;
1421        xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1422        ndelay_off(STDIN_FILENO);
1423    }
1424
1425    /* Initialize to all zeroes so there is no risk memcmp will report a
1426       spurious difference in an uninitialized portion of the structure */
1427    memset(&mode, 0, sizeof(mode));
1428    if (tcgetattr(STDIN_FILENO, &mode))
1429        perror_on_device_and_die("%s");
1430
1431    if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1432        get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1433        output_func(&mode, display_all);
1434        return EXIT_SUCCESS;
1435    }
1436
1437    /* Second pass: perform actions */
1438    k = 0;
1439    while (argv[++k]) {
1440        const struct mode_info *mp;
1441        const struct control_info *cp;
1442        const char *arg = argv[k];
1443        const char *argnext = argv[k+1];
1444        int param;
1445
1446        if (arg[0] == '-') {
1447            mp = find_mode(arg+1);
1448            if (mp) {
1449                set_mode(mp, 1 /* reversed */, &mode);
1450                stty_state |= STTY_require_set_attr;
1451            }
1452            /* It is an option - already parsed. Skip it */
1453            continue;
1454        }
1455
1456        mp = find_mode(arg);
1457        if (mp) {
1458            set_mode(mp, 0 /* non-reversed */, &mode);
1459            stty_state |= STTY_require_set_attr;
1460            continue;
1461        }
1462
1463        cp = find_control(arg);
1464        if (cp) {
1465            ++k;
1466            set_control_char_or_die(cp, argnext, &mode);
1467            stty_state |= STTY_require_set_attr;
1468            continue;
1469        }
1470
1471        param = find_param(arg);
1472        if (param & param_need_arg) {
1473            ++k;
1474        }
1475
1476        switch (param) {
1477#ifdef __linux__
1478        case param_line:
1479            mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1480            stty_state |= STTY_require_set_attr;
1481            break;
1482#endif
1483#ifdef TIOCGWINSZ
1484        case param_cols:
1485        case param_columns:
1486            set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1487            break;
1488        case param_size:
1489            display_window_size(0);
1490            break;
1491        case param_rows:
1492            set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1493            break;
1494#endif
1495        case param_speed:
1496            display_speed(&mode, 0);
1497            break;
1498        case param_ispeed:
1499            set_speed_or_die(input_speed, argnext, &mode);
1500            stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1501            break;
1502        case param_ospeed:
1503            set_speed_or_die(output_speed, argnext, &mode);
1504            stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1505            break;
1506        default:
1507            if (recover_mode(arg, &mode) == 1)
1508                stty_state |= STTY_require_set_attr;
1509            else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1510                set_speed_or_die(both_speeds, arg, &mode);
1511                stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1512            } /* else - impossible (caught in the first pass):
1513                bb_error_msg_and_die("invalid argument '%s'", arg); */
1514        }
1515    }
1516
1517    if (stty_state & STTY_require_set_attr) {
1518        struct termios new_mode;
1519
1520        if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1521            perror_on_device_and_die("%s");
1522
1523        /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1524           it performs *any* of the requested operations.  This means it
1525           can report 'success' when it has actually failed to perform
1526           some proper subset of the requested operations.  To detect
1527           this partial failure, get the current terminal attributes and
1528           compare them to the requested ones */
1529
1530        /* Initialize to all zeroes so there is no risk memcmp will report a
1531           spurious difference in an uninitialized portion of the structure */
1532        memset(&new_mode, 0, sizeof(new_mode));
1533        if (tcgetattr(STDIN_FILENO, &new_mode))
1534            perror_on_device_and_die("%s");
1535
1536        if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1537#if CIBAUD
1538            /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1539               tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1540               sometimes (m1 != m2).  The only difference is in the four bits
1541               of the c_cflag field corresponding to the baud rate.  To save
1542               Sun users a little confusion, don't report an error if this
1543               happens.  But suppress the error only if we haven't tried to
1544               set the baud rate explicitly -- otherwise we'd never give an
1545               error for a true failure to set the baud rate */
1546
1547            new_mode.c_cflag &= (~CIBAUD);
1548            if ((stty_state & STTY_speed_was_set)
1549             || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1550#endif
1551                perror_on_device_and_die("%s: cannot perform all requested operations");
1552        }
1553    }
1554
1555    return EXIT_SUCCESS;
1556}
Note: See TracBrowser for help on using the repository browser.