source: branches/2.2.2/mindi-busybox/coreutils/stty.c @ 1247

Last change on this file since 1247 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 36.6 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 the GPL v2 or later, see the file LICENSE in this tarball.
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//#define TEST
25
26#include "busybox.h"
27#include <stddef.h>
28#include <termios.h>
29#include <sys/ioctl.h>
30
31#include <sys/param.h>
32#include <unistd.h>
33
34#ifndef STDIN_FILENO
35# define STDIN_FILENO 0
36#endif
37
38#ifndef STDOUT_FILENO
39# define STDOUT_FILENO 1
40#endif
41
42#include <stdlib.h>
43#include <string.h>
44#include <assert.h>
45#include <ctype.h>
46#include <errno.h>
47#include <limits.h>
48#include <fcntl.h>
49
50#define STREQ(a, b) (strcmp ((a), (b)) == 0)
51
52
53#ifndef _POSIX_VDISABLE
54# define _POSIX_VDISABLE ((unsigned char) 0)
55#endif
56
57#define Control(c) ((c) & 0x1f)
58/* Canonical values for control characters. */
59#ifndef CINTR
60# define CINTR Control ('c')
61#endif
62#ifndef CQUIT
63# define CQUIT 28
64#endif
65#ifndef CERASE
66# define CERASE 127
67#endif
68#ifndef CKILL
69# define CKILL Control ('u')
70#endif
71#ifndef CEOF
72# define CEOF Control ('d')
73#endif
74#ifndef CEOL
75# define CEOL _POSIX_VDISABLE
76#endif
77#ifndef CSTART
78# define CSTART Control ('q')
79#endif
80#ifndef CSTOP
81# define CSTOP Control ('s')
82#endif
83#ifndef CSUSP
84# define CSUSP Control ('z')
85#endif
86#if defined(VEOL2) && !defined(CEOL2)
87# define CEOL2 _POSIX_VDISABLE
88#endif
89/* ISC renamed swtch to susp for termios, but we'll accept either name.  */
90#if defined(VSUSP) && !defined(VSWTCH)
91# define VSWTCH VSUSP
92# define CSWTCH CSUSP
93#endif
94#if defined(VSWTCH) && !defined(CSWTCH)
95# define CSWTCH _POSIX_VDISABLE
96#endif
97
98/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
99   So the default is to disable `swtch.'  */
100#if defined (__sparc__) && defined (__svr4__)
101# undef CSWTCH
102# define CSWTCH _POSIX_VDISABLE
103#endif
104
105#if defined(VWERSE) && !defined (VWERASE)       /* AIX-3.2.5 */
106# define VWERASE VWERSE
107#endif
108#if defined(VDSUSP) && !defined (CDSUSP)
109# define CDSUSP Control ('y')
110#endif
111#if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
112# define VREPRINT VRPRNT
113#endif
114#if defined(VREPRINT) && !defined(CRPRNT)
115# define CRPRNT Control ('r')
116#endif
117#if defined(VWERASE) && !defined(CWERASE)
118# define CWERASE Control ('w')
119#endif
120#if defined(VLNEXT) && !defined(CLNEXT)
121# define CLNEXT Control ('v')
122#endif
123#if defined(VDISCARD) && !defined(VFLUSHO)
124# define VFLUSHO VDISCARD
125#endif
126#if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
127# define VFLUSHO VFLUSH
128#endif
129#if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
130# define ECHOCTL CTLECH
131#endif
132#if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
133# define ECHOCTL TCTLECH
134#endif
135#if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
136# define ECHOKE CRTKIL
137#endif
138#if defined(VFLUSHO) && !defined(CFLUSHO)
139# define CFLUSHO Control ('o')
140#endif
141#if defined(VSTATUS) && !defined(CSTATUS)
142# define CSTATUS Control ('t')
143#endif
144
145/* Which speeds to set.  */
146enum speed_setting {
147    input_speed, output_speed, both_speeds
148};
149
150/* Which member(s) of `struct termios' a mode uses.  */
151enum mode_type {
152    /* Do NOT change the order or values, as mode_type_flag()
153     * depends on them. */
154    control, input, output, local, combination
155};
156
157
158static const char evenp     [] = "evenp";
159static const char raw       [] = "raw";
160static const char stty_min  [] = "min";
161static const char stty_time [] = "time";
162static const char stty_swtch[] = "swtch";
163static const char stty_eol  [] = "eol";
164static const char stty_eof  [] = "eof";
165static const char parity    [] = "parity";
166static const char stty_oddp [] = "oddp";
167static const char stty_nl   [] = "nl";
168static const char stty_ek   [] = "ek";
169static const char stty_sane [] = "sane";
170static const char cbreak    [] = "cbreak";
171static const char stty_pass8[] = "pass8";
172static const char litout    [] = "litout";
173static const char cooked    [] = "cooked";
174static const char decctlq   [] = "decctlq";
175static const char stty_tabs [] = "tabs";
176static const char stty_lcase[] = "lcase";
177static const char stty_LCASE[] = "LCASE";
178static const char stty_crt  [] = "crt";
179static const char stty_dec  [] = "dec";
180
181
182/* Flags for `struct mode_info'. */
183#define SANE_SET 1              /* Set in `sane' mode.                  */
184#define SANE_UNSET 2            /* Unset in `sane' mode.                */
185#define REV 4                   /* Can be turned off by prepending `-'. */
186#define OMIT 8                  /* Don't display value.                 */
187
188/* Each mode.  */
189struct mode_info {
190    const char *name;       /* Name given on command line.           */
191    /* enum mode_type type; */
192    char type;              /* Which structure element to change.    */
193    char flags;             /* Setting and display options.          */
194    unsigned short mask;     /* Other bits to turn off for this mode. */
195    unsigned long bits;     /* Bits to set for this mode.            */
196};
197
198#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
199
200static const struct  mode_info mode_info[] = {
201    MI_ENTRY("parenb",   control,     REV,               PARENB,     0 ),
202    MI_ENTRY("parodd",   control,     REV,               PARODD,     0 ),
203    MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE),
204    MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE),
205    MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE),
206    MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE),
207    MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 ),
208    MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 ),
209    MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 ),
210    MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 ),
211    MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 ),
212#ifdef CRTSCTS
213    MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 ),
214#endif
215    MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 ),
216    MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 ),
217    MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 ),
218    MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 ),
219    MI_ENTRY("inpck",    input,       REV,               INPCK,      0 ),
220    MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 ),
221    MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 ),
222    MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 ),
223    MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 ),
224    MI_ENTRY("ixon",     input,       REV,               IXON,       0 ),
225    MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 ),
226    MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 ),
227#ifdef IUCLC
228    MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 ),
229#endif
230#ifdef IXANY
231    MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 ),
232#endif
233#ifdef IMAXBEL
234    MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 ),
235#endif
236    MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 ),
237#ifdef OLCUC
238    MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 ),
239#endif
240#ifdef OCRNL
241    MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 ),
242#endif
243#ifdef ONLCR
244    MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 ),
245#endif
246#ifdef ONOCR
247    MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 ),
248#endif
249#ifdef ONLRET
250    MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 ),
251#endif
252#ifdef OFILL
253    MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 ),
254#endif
255#ifdef OFDEL
256    MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 ),
257#endif
258#ifdef NLDLY
259    MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY),
260    MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY),
261#endif
262#ifdef CRDLY
263    MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY),
264    MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY),
265    MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY),
266    MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY),
267#endif
268
269#ifdef TABDLY
270    MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY),
271    MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY),
272    MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY),
273    MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY),
274#else
275# ifdef OXTABS
276    MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 ),
277# endif
278#endif
279
280#ifdef BSDLY
281    MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY),
282    MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY),
283#endif
284#ifdef VTDLY
285    MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY),
286    MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY),
287#endif
288#ifdef FFDLY
289    MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY),
290    MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY),
291#endif
292    MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 ),
293    MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 ),
294#ifdef IEXTEN
295    MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 ),
296#endif
297    MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 ),
298    MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 ),
299    MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 ),
300    MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 ),
301    MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 ),
302    MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 ),
303#ifdef XCASE
304    MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 ),
305#endif
306#ifdef TOSTOP
307    MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 ),
308#endif
309#ifdef ECHOPRT
310    MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 ),
311    MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 ),
312#endif
313#ifdef ECHOCTL
314    MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 ),
315    MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 ),
316#endif
317#ifdef ECHOKE
318    MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 ),
319    MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 ),
320#endif
321    MI_ENTRY(evenp,      combination, REV        | OMIT, 0,          0 ),
322    MI_ENTRY(parity,     combination, REV        | OMIT, 0,          0 ),
323    MI_ENTRY(stty_oddp,  combination, REV        | OMIT, 0,          0 ),
324    MI_ENTRY(stty_nl,    combination, REV        | OMIT, 0,          0 ),
325    MI_ENTRY(stty_ek,    combination, OMIT,              0,          0 ),
326    MI_ENTRY(stty_sane,  combination, OMIT,              0,          0 ),
327    MI_ENTRY(cooked,     combination, REV        | OMIT, 0,          0 ),
328    MI_ENTRY(raw,        combination, REV        | OMIT, 0,          0 ),
329    MI_ENTRY(stty_pass8, combination, REV        | OMIT, 0,          0 ),
330    MI_ENTRY(litout,     combination, REV        | OMIT, 0,          0 ),
331    MI_ENTRY(cbreak,     combination, REV        | OMIT, 0,          0 ),
332#ifdef IXANY
333    MI_ENTRY(decctlq,    combination, REV        | OMIT, 0,          0 ),
334#endif
335#if defined (TABDLY) || defined (OXTABS)
336    MI_ENTRY(stty_tabs,  combination, REV        | OMIT, 0,          0 ),
337#endif
338#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
339    MI_ENTRY(stty_lcase, combination, REV        | OMIT, 0,          0 ),
340    MI_ENTRY(stty_LCASE, combination, REV        | OMIT, 0,          0 ),
341#endif
342    MI_ENTRY(stty_crt,   combination, OMIT,              0,          0 ),
343    MI_ENTRY(stty_dec,   combination, OMIT,              0,          0 ),
344};
345
346enum {
347    NUM_mode_info =
348    (sizeof(mode_info) / sizeof(struct mode_info))
349};
350
351/* Control character settings.  */
352struct control_info {
353    const char *name;                       /* Name given on command line.  */
354    unsigned char saneval;          /* Value to set for `stty sane'.  */
355    unsigned char offset;                           /* Offset in c_cc.  */
356};
357
358/* Control characters. */
359
360static const struct  control_info control_info[] = {
361    {"intr",     CINTR,   VINTR},
362    {"quit",     CQUIT,   VQUIT},
363    {"erase",    CERASE,  VERASE},
364    {"kill",     CKILL,   VKILL},
365    {stty_eof,   CEOF,    VEOF},
366    {stty_eol,   CEOL,    VEOL},
367#ifdef VEOL2
368    {"eol2",     CEOL2,   VEOL2},
369#endif
370#ifdef VSWTCH
371    {stty_swtch, CSWTCH,  VSWTCH},
372#endif
373    {"start",    CSTART,  VSTART},
374    {"stop",     CSTOP,   VSTOP},
375    {"susp",     CSUSP,   VSUSP},
376#ifdef VDSUSP
377    {"dsusp",    CDSUSP,  VDSUSP},
378#endif
379#ifdef VREPRINT
380    {"rprnt",    CRPRNT,  VREPRINT},
381#endif
382#ifdef VWERASE
383    {"werase",   CWERASE, VWERASE},
384#endif
385#ifdef VLNEXT
386    {"lnext",    CLNEXT,  VLNEXT},
387#endif
388#ifdef VFLUSHO
389    {"flush",    CFLUSHO, VFLUSHO},
390#endif
391#ifdef VSTATUS
392    {"status",   CSTATUS, VSTATUS},
393#endif
394    /* These must be last because of the display routines. */
395    {stty_min,   1,       VMIN},
396    {stty_time,  0,       VTIME},
397};
398
399enum {
400    NUM_control_info =
401    (sizeof(control_info) / sizeof(struct control_info))
402};
403
404#define EMT(t) ((enum mode_type)(t))
405
406static const char *  visible(unsigned int ch);
407static int           recover_mode(char *arg, struct termios *mode);
408static int           screen_columns(void);
409static int           set_mode(const struct mode_info *info,
410                    int reversed, struct termios *mode);
411static speed_t       string_to_baud(const char *arg);
412static tcflag_t*     mode_type_flag(enum mode_type type, struct termios *mode);
413static void          display_all(struct termios *mode);
414static void          display_changed(struct termios *mode);
415static void          display_recoverable(struct termios *mode);
416static void          display_speed(struct termios *mode, int fancy);
417static void          display_window_size(int fancy);
418static void          sane_mode(struct termios *mode);
419static void          set_control_char(const struct control_info *info,
420                    const char *arg, struct termios *mode);
421static void          set_speed(enum speed_setting type,
422                    const char *arg, struct termios *mode);
423static void          set_window_size(int rows, int cols);
424
425static const char *device_name;
426
427static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
428{
429    bb_perror_msg_and_die(fmt, device_name);
430}
431
432
433/* The width of the screen, for output wrapping. */
434static int max_col;
435
436/* Current position, to know when to wrap. */
437static int current_col;
438
439/* Print format string MESSAGE and optional args.
440   Wrap to next line first if it won't fit.
441   Print a space first unless MESSAGE will start a new line. */
442
443static void wrapf(const char *message, ...)
444{
445    va_list args;
446    char buf[1024];                 /* Plenty long for our needs. */
447    int buflen;
448
449    va_start(args, message);
450    vsprintf(buf, message, args);
451    va_end(args);
452    buflen = strlen(buf);
453    if (current_col + (current_col > 0) + buflen >= max_col) {
454        putchar('\n');
455        current_col = 0;
456    }
457    if (current_col > 0) {
458        putchar(' ');
459        current_col++;
460    }
461    fputs(buf, stdout);
462    current_col += buflen;
463}
464
465static const struct suffix_mult stty_suffixes[] = {
466    {"b",  512 },
467    {"k",  1024},
468    {"B",  1024},
469    {NULL, 0   }
470};
471
472#ifndef TEST
473int stty_main(int argc, char **argv)
474#else
475int main(int argc, char **argv)
476#endif
477{
478    struct termios mode;
479    void (*output_func)(struct termios *);
480    int    optc;
481    int    require_set_attr;
482    int    speed_was_set;
483    int    verbose_output;
484    int    recoverable_output;
485    int    k;
486    int    noargs = 1;
487    char * file_name = NULL;
488
489    output_func = display_changed;
490    verbose_output = 0;
491    recoverable_output = 0;
492
493    /* Don't print error messages for unrecognized options.  */
494    opterr = 0;
495
496    while ((optc = getopt(argc, argv, "agF:")) != -1) {
497        switch (optc) {
498        case 'a':
499            verbose_output = 1;
500            output_func = display_all;
501            break;
502
503        case 'g':
504            recoverable_output = 1;
505            output_func = display_recoverable;
506            break;
507
508        case 'F':
509            if (file_name)
510                bb_error_msg_and_die("only one device may be specified");
511            file_name = optarg;
512            break;
513
514        default:                /* unrecognized option */
515            noargs = 0;
516            break;
517        }
518
519        if (noargs == 0)
520            break;
521    }
522
523    if (optind < argc)
524        noargs = 0;
525
526    /* Specifying both -a and -g gets an error.  */
527    if (verbose_output & recoverable_output)
528        bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
529
530    /* Specifying any other arguments with -a or -g gets an error.  */
531    if (~noargs & (verbose_output | recoverable_output))
532        bb_error_msg_and_die ("modes may not be set when specifying an output style");
533
534    /* FIXME: it'd be better not to open the file until we've verified
535       that all arguments are valid.  Otherwise, we could end up doing
536       only some of the requested operations and then failing, probably
537       leaving things in an undesirable state.  */
538
539    if (file_name) {
540        int fdflags;
541
542        device_name = file_name;
543        fclose(stdin);
544        bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
545        if ((fdflags = fcntl(STDIN_FILENO, F_GETFL)) == -1
546            || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
547            perror_on_device("%s: couldn't reset non-blocking mode");
548    } else {
549        device_name = bb_msg_standard_input;
550    }
551
552    /* Initialize to all zeroes so there is no risk memcmp will report a
553       spurious difference in an uninitialized portion of the structure.  */
554    memset(&mode, 0, sizeof(mode));
555    if (tcgetattr(STDIN_FILENO, &mode))
556        perror_on_device("%s");
557
558    if (verbose_output | recoverable_output | noargs) {
559        max_col = screen_columns();
560        current_col = 0;
561        output_func(&mode);
562        return EXIT_SUCCESS;
563    }
564
565    speed_was_set = 0;
566    require_set_attr = 0;
567    k = 0;
568    while (++k < argc) {
569        int match_found = 0;
570        int reversed = 0;
571        int i;
572
573        if (argv[k][0] == '-') {
574            char *find_dev_opt;
575
576            ++argv[k];
577
578     /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
579    Find the options that have been parsed.  This is really
580    gross, but it's needed because stty SETTINGS look like options to
581    getopt(), so we need to work around things in a really horrible
582    way.  If any new options are ever added to stty, the short option
583    MUST NOT be a letter which is the first letter of one of the
584    possible stty settings.
585     */
586            find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
587            if(find_dev_opt) {
588                if(find_dev_opt[1]==0)  /* -*F   /dev/foo */
589                    k++;            /* skip  /dev/foo */
590                continue;   /* else -*F/dev/foo - no skip */
591            }
592            if(argv[k][0]=='a' || argv[k][0]=='g')
593                continue;
594            /* Is not options - is reverse params */
595            reversed = 1;
596        }
597        for (i = 0; i < NUM_mode_info; ++i)
598            if (STREQ(argv[k], mode_info[i].name)) {
599                match_found = set_mode(&mode_info[i], reversed, &mode);
600                require_set_attr = 1;
601                break;
602            }
603
604        if (match_found == 0 && reversed)
605            bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
606
607        if (match_found == 0)
608            for (i = 0; i < NUM_control_info; ++i)
609                if (STREQ(argv[k], control_info[i].name)) {
610                    if (k == argc - 1)
611                        bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
612                    match_found = 1;
613                    ++k;
614                    set_control_char(&control_info[i], argv[k], &mode);
615                    require_set_attr = 1;
616                    break;
617                }
618
619        if (match_found == 0) {
620            if (STREQ(argv[k], "ispeed")) {
621                if (k == argc - 1)
622                    bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
623                ++k;
624                set_speed(input_speed, argv[k], &mode);
625                speed_was_set = 1;
626                require_set_attr = 1;
627            } else if (STREQ(argv[k], "ospeed")) {
628                if (k == argc - 1)
629                    bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
630                ++k;
631                set_speed(output_speed, argv[k], &mode);
632                speed_was_set = 1;
633                require_set_attr = 1;
634            }
635#ifdef TIOCGWINSZ
636            else if (STREQ(argv[k], "rows")) {
637                if (k == argc - 1)
638                    bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
639                ++k;
640                set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
641                                -1);
642            } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
643                if (k == argc - 1)
644                    bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
645                ++k;
646                set_window_size(-1,
647                        (int) bb_xparse_number(argv[k], stty_suffixes));
648            } else if (STREQ(argv[k], "size")) {
649                max_col = screen_columns();
650                current_col = 0;
651                display_window_size(0);
652            }
653#endif
654#ifdef HAVE_C_LINE
655            else if (STREQ(argv[k], "line")) {
656                if (k == argc - 1)
657                    bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
658                ++k;
659                mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
660                require_set_attr = 1;
661            }
662#endif
663            else if (STREQ(argv[k], "speed")) {
664                max_col = screen_columns();
665                display_speed(&mode, 0);
666            } else if (recover_mode(argv[k], &mode) == 1)
667                require_set_attr = 1;
668            else if (string_to_baud(argv[k]) != (speed_t) - 1) {
669                set_speed(both_speeds, argv[k], &mode);
670                speed_was_set = 1;
671                require_set_attr = 1;
672            } else
673                bb_error_msg_and_die("invalid argument `%s'", argv[k]);
674        }
675    }
676
677    if (require_set_attr) {
678        struct termios new_mode;
679
680        if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
681            perror_on_device("%s");
682
683        /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
684           it performs *any* of the requested operations.  This means it
685           can report `success' when it has actually failed to perform
686           some proper subset of the requested operations.  To detect
687           this partial failure, get the current terminal attributes and
688           compare them to the requested ones.  */
689
690        /* Initialize to all zeroes so there is no risk memcmp will report a
691           spurious difference in an uninitialized portion of the structure.  */
692        memset(&new_mode, 0, sizeof(new_mode));
693        if (tcgetattr(STDIN_FILENO, &new_mode))
694            perror_on_device("%s");
695
696        /* Normally, one shouldn't use memcmp to compare structures that
697           may have `holes' containing uninitialized data, but we have been
698           careful to initialize the storage of these two variables to all
699           zeroes.  One might think it more efficient simply to compare the
700           modified fields, but that would require enumerating those fields --
701           and not all systems have the same fields in this structure.  */
702
703        if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
704#ifdef CIBAUD
705            /* SunOS 4.1.3 (at least) has the problem that after this sequence,
706               tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
707               sometimes (m1 != m2).  The only difference is in the four bits
708               of the c_cflag field corresponding to the baud rate.  To save
709               Sun users a little confusion, don't report an error if this
710               happens.  But suppress the error only if we haven't tried to
711               set the baud rate explicitly -- otherwise we'd never give an
712               error for a true failure to set the baud rate.  */
713
714            new_mode.c_cflag &= (~CIBAUD);
715            if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
716#endif
717                perror_on_device ("%s: unable to perform all requested operations");
718        }
719    }
720
721    return EXIT_SUCCESS;
722}
723
724/* Return 0 if not applied because not reversible; otherwise return 1.  */
725
726static int
727set_mode(const struct mode_info *info, int reversed, struct termios *mode)
728{
729    tcflag_t *bitsp;
730
731    if (reversed && (info->flags & REV) == 0)
732        return 0;
733
734    bitsp = mode_type_flag(EMT(info->type), mode);
735
736    if (bitsp == NULL) {
737        /* Combination mode. */
738        if (info->name == evenp || info->name == parity) {
739            if (reversed)
740                mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
741            else
742                mode->c_cflag =
743                    (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
744        } else if (info->name == stty_oddp) {
745            if (reversed)
746                mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
747            else
748                mode->c_cflag =
749                    (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
750        } else if (info->name == stty_nl) {
751            if (reversed) {
752                mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
753                mode->c_oflag = (mode->c_oflag
754#ifdef ONLCR
755                                 | ONLCR
756#endif
757                    )
758#ifdef OCRNL
759                    & ~OCRNL
760#endif
761#ifdef ONLRET
762                    & ~ONLRET
763#endif
764                    ;
765            } else {
766                mode->c_iflag = mode->c_iflag & ~ICRNL;
767#ifdef ONLCR
768                mode->c_oflag = mode->c_oflag & ~ONLCR;
769#endif
770            }
771        } else if (info->name == stty_ek) {
772            mode->c_cc[VERASE] = CERASE;
773            mode->c_cc[VKILL] = CKILL;
774        } else if (info->name == stty_sane)
775            sane_mode(mode);
776        else if (info->name == cbreak) {
777            if (reversed)
778                mode->c_lflag |= ICANON;
779            else
780                mode->c_lflag &= ~ICANON;
781        } else if (info->name == stty_pass8) {
782            if (reversed) {
783                mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
784                mode->c_iflag |= ISTRIP;
785            } else {
786                mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
787                mode->c_iflag &= ~ISTRIP;
788            }
789        } else if (info->name == litout) {
790            if (reversed) {
791                mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
792                mode->c_iflag |= ISTRIP;
793                mode->c_oflag |= OPOST;
794            } else {
795                mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
796                mode->c_iflag &= ~ISTRIP;
797                mode->c_oflag &= ~OPOST;
798            }
799        } else if (info->name == raw || info->name == cooked) {
800            if ((info->name[0] == 'r' && reversed)
801                || (info->name[0] == 'c' && !reversed)) {
802                /* Cooked mode. */
803                mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
804                mode->c_oflag |= OPOST;
805                mode->c_lflag |= ISIG | ICANON;
806#if VMIN == VEOF
807                mode->c_cc[VEOF] = CEOF;
808#endif
809#if VTIME == VEOL
810                mode->c_cc[VEOL] = CEOL;
811#endif
812            } else {
813                /* Raw mode. */
814                mode->c_iflag = 0;
815                mode->c_oflag &= ~OPOST;
816                mode->c_lflag &= ~(ISIG | ICANON
817#ifdef XCASE
818                                   | XCASE
819#endif
820                    );
821                mode->c_cc[VMIN] = 1;
822                mode->c_cc[VTIME] = 0;
823            }
824        }
825#ifdef IXANY
826        else if (info->name == decctlq) {
827            if (reversed)
828                mode->c_iflag |= IXANY;
829            else
830                mode->c_iflag &= ~IXANY;
831        }
832#endif
833#ifdef TABDLY
834        else if (info->name == stty_tabs) {
835            if (reversed)
836                mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
837            else
838                mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
839        }
840#else
841# ifdef OXTABS
842        else if (info->name == stty_tabs) {
843            if (reversed)
844                mode->c_oflag = mode->c_oflag | OXTABS;
845            else
846                mode->c_oflag = mode->c_oflag & ~OXTABS;
847        }
848# endif
849#endif
850#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
851        else if (info->name == stty_lcase || info->name == stty_LCASE) {
852            if (reversed) {
853                mode->c_lflag &= ~XCASE;
854                mode->c_iflag &= ~IUCLC;
855                mode->c_oflag &= ~OLCUC;
856            } else {
857                mode->c_lflag |= XCASE;
858                mode->c_iflag |= IUCLC;
859                mode->c_oflag |= OLCUC;
860            }
861        }
862#endif
863        else if (info->name == stty_crt)
864            mode->c_lflag |= ECHOE
865#ifdef ECHOCTL
866                | ECHOCTL
867#endif
868#ifdef ECHOKE
869                | ECHOKE
870#endif
871                ;
872        else if (info->name == stty_dec) {
873            mode->c_cc[VINTR] = 3;  /* ^C */
874            mode->c_cc[VERASE] = 127;       /* DEL */
875            mode->c_cc[VKILL] = 21; /* ^U */
876            mode->c_lflag |= ECHOE
877#ifdef ECHOCTL
878                | ECHOCTL
879#endif
880#ifdef ECHOKE
881                | ECHOKE
882#endif
883                ;
884#ifdef IXANY
885            mode->c_iflag &= ~IXANY;
886#endif
887        }
888    } else if (reversed)
889        *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
890    else
891        *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
892
893    return 1;
894}
895
896static void
897set_control_char(const struct control_info *info, const char *arg,
898                 struct termios *mode)
899{
900    unsigned char value;
901
902    if (info->name == stty_min || info->name == stty_time)
903        value = bb_xparse_number(arg, stty_suffixes);
904    else if (arg[0] == '\0' || arg[1] == '\0')
905        value = arg[0];
906    else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
907        value = _POSIX_VDISABLE;
908    else if (arg[0] == '^' && arg[1] != '\0') {     /* Ignore any trailing junk. */
909        if (arg[1] == '?')
910            value = 127;
911        else
912            value = arg[1] & ~0140; /* Non-letters get weird results. */
913    } else
914        value = bb_xparse_number(arg, stty_suffixes);
915    mode->c_cc[info->offset] = value;
916}
917
918static void
919set_speed(enum speed_setting type, const char *arg, struct termios *mode)
920{
921    speed_t baud;
922
923    baud = string_to_baud(arg);
924
925    if (type != output_speed) {     /* either input or both */
926        cfsetispeed(mode, baud);
927    }
928    if (type != input_speed) {      /* either output or both */
929        cfsetospeed(mode, baud);
930    }
931}
932
933#ifdef TIOCGWINSZ
934
935static int get_win_size(int fd, struct winsize *win)
936{
937    int err = ioctl(fd, TIOCGWINSZ, (char *) win);
938
939    return err;
940}
941
942static void
943set_window_size(int rows, int cols)
944{
945    struct winsize win;
946
947    if (get_win_size(STDIN_FILENO, &win)) {
948        if (errno != EINVAL)
949            perror_on_device("%s");
950        memset(&win, 0, sizeof(win));
951    }
952
953    if (rows >= 0)
954        win.ws_row = rows;
955    if (cols >= 0)
956        win.ws_col = cols;
957
958# ifdef TIOCSSIZE
959    /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
960       The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
961       This comment from sys/ttold.h describes Sun's twisted logic - a better
962       test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
963       At any rate, the problem is gone in Solaris 2.x. */
964
965    if (win.ws_row == 0 || win.ws_col == 0) {
966        struct ttysize ttysz;
967
968        ttysz.ts_lines = win.ws_row;
969        ttysz.ts_cols = win.ws_col;
970
971        win.ws_row = win.ws_col = 1;
972
973        if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
974            || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
975            perror_on_device("%s");
976        }
977        return;
978    }
979# endif
980
981    if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
982        perror_on_device("%s");
983}
984
985static void display_window_size(int fancy)
986{
987    const char *fmt_str = "%s" "\0" "%s: no size information for this device";
988    struct winsize win;
989
990    if (get_win_size(STDIN_FILENO, &win)) {
991        if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
992            perror_on_device(fmt_str);
993        }
994    } else {
995        wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
996              win.ws_row, win.ws_col);
997        if (!fancy)
998            current_col = 0;
999    }
1000}
1001#endif
1002
1003static int screen_columns(void)
1004{
1005    int columns;
1006    const char *s;
1007
1008#ifdef TIOCGWINSZ
1009    struct winsize win;
1010
1011    /* With Solaris 2.[123], this ioctl fails and errno is set to
1012       EINVAL for telnet (but not rlogin) sessions.
1013       On ISC 3.0, it fails for the console and the serial port
1014       (but it works for ptys).
1015       It can also fail on any system when stdout isn't a tty.
1016       In case of any failure, just use the default.  */
1017    if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1018        return win.ws_col;
1019#endif
1020
1021    columns = 80;
1022    if ((s = getenv("COLUMNS"))) {
1023        columns = atoi(s);
1024    }
1025    return columns;
1026}
1027
1028static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1029{
1030    static const unsigned char tcflag_offsets[] = {
1031        offsetof(struct termios, c_cflag), /* control */
1032        offsetof(struct termios, c_iflag), /* input */
1033        offsetof(struct termios, c_oflag), /* output */
1034        offsetof(struct termios, c_lflag) /* local */
1035    };
1036
1037    if (((unsigned int) type) <= local) {
1038        return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
1039    }
1040    return NULL;
1041}
1042
1043static void display_changed(struct termios *mode)
1044{
1045    int i;
1046    int empty_line;
1047    tcflag_t *bitsp;
1048    unsigned long mask;
1049    enum mode_type prev_type = control;
1050
1051    display_speed(mode, 1);
1052#ifdef HAVE_C_LINE
1053    wrapf("line = %d;", mode->c_line);
1054#endif
1055    putchar('\n');
1056    current_col = 0;
1057
1058    empty_line = 1;
1059    for (i = 0; control_info[i].name != stty_min; ++i) {
1060        if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1061            continue;
1062        /* If swtch is the same as susp, don't print both.  */
1063#if VSWTCH == VSUSP
1064        if (control_info[i].name == stty_swtch)
1065            continue;
1066#endif
1067        /* If eof uses the same slot as min, only print whichever applies.  */
1068#if VEOF == VMIN
1069        if ((mode->c_lflag & ICANON) == 0
1070            && (control_info[i].name == stty_eof
1071                || control_info[i].name == stty_eol)) continue;
1072#endif
1073
1074        empty_line = 0;
1075        wrapf("%s = %s;", control_info[i].name,
1076              visible(mode->c_cc[control_info[i].offset]));
1077    }
1078    if ((mode->c_lflag & ICANON) == 0) {
1079        wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1080              (int) mode->c_cc[VTIME]);
1081    } else if (empty_line == 0)
1082        putchar('\n');
1083    current_col = 0;
1084
1085    empty_line = 1;
1086    for (i = 0; i < NUM_mode_info; ++i) {
1087        if (mode_info[i].flags & OMIT)
1088            continue;
1089        if (EMT(mode_info[i].type) != prev_type) {
1090            if (empty_line == 0) {
1091                putchar('\n');
1092                current_col = 0;
1093                empty_line = 1;
1094            }
1095            prev_type = EMT(mode_info[i].type);
1096        }
1097
1098        bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1099        mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1100        if ((*bitsp & mask) == mode_info[i].bits) {
1101            if (mode_info[i].flags & SANE_UNSET) {
1102                wrapf("%s", mode_info[i].name);
1103                empty_line = 0;
1104            }
1105        }
1106            else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1107                     (SANE_SET | REV)) {
1108            wrapf("-%s", mode_info[i].name);
1109            empty_line = 0;
1110        }
1111    }
1112    if (empty_line == 0)
1113        putchar('\n');
1114    current_col = 0;
1115}
1116
1117static void
1118display_all(struct termios *mode)
1119{
1120    int i;
1121    tcflag_t *bitsp;
1122    unsigned long mask;
1123    enum mode_type prev_type = control;
1124
1125    display_speed(mode, 1);
1126#ifdef TIOCGWINSZ
1127    display_window_size(1);
1128#endif
1129#ifdef HAVE_C_LINE
1130    wrapf("line = %d;", mode->c_line);
1131#endif
1132    putchar('\n');
1133    current_col = 0;
1134
1135    for (i = 0; control_info[i].name != stty_min; ++i) {
1136        /* If swtch is the same as susp, don't print both.  */
1137#if VSWTCH == VSUSP
1138        if (control_info[i].name == stty_swtch)
1139            continue;
1140#endif
1141        /* If eof uses the same slot as min, only print whichever applies.  */
1142#if VEOF == VMIN
1143        if ((mode->c_lflag & ICANON) == 0
1144            && (control_info[i].name == stty_eof
1145                || control_info[i].name == stty_eol)) continue;
1146#endif
1147        wrapf("%s = %s;", control_info[i].name,
1148              visible(mode->c_cc[control_info[i].offset]));
1149    }
1150#if VEOF == VMIN
1151    if ((mode->c_lflag & ICANON) == 0)
1152#endif
1153        wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1154    if (current_col != 0)
1155        putchar('\n');
1156    current_col = 0;
1157
1158    for (i = 0; i < NUM_mode_info; ++i) {
1159        if (mode_info[i].flags & OMIT)
1160            continue;
1161        if (EMT(mode_info[i].type) != prev_type) {
1162            putchar('\n');
1163            current_col = 0;
1164            prev_type = EMT(mode_info[i].type);
1165        }
1166
1167        bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1168        mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1169        if ((*bitsp & mask) == mode_info[i].bits)
1170            wrapf("%s", mode_info[i].name);
1171        else if (mode_info[i].flags & REV)
1172            wrapf("-%s", mode_info[i].name);
1173    }
1174    putchar('\n');
1175    current_col = 0;
1176}
1177
1178static void display_speed(struct termios *mode, int fancy)
1179{
1180    unsigned long ispeed, ospeed;
1181    const char *fmt_str =
1182        "%lu %lu\n\0"        "ispeed %lu baud; ospeed %lu baud;\0"
1183        "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
1184
1185    ospeed = ispeed = cfgetispeed(mode);
1186    if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1187        ispeed = ospeed;                /* in case ispeed was 0 */
1188        fmt_str += 43;
1189    }
1190    if (fancy) {
1191        fmt_str += 9;
1192    }
1193    wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
1194    if (!fancy)
1195        current_col = 0;
1196}
1197
1198static void display_recoverable(struct termios *mode)
1199{
1200    int i;
1201
1202    printf("%lx:%lx:%lx:%lx",
1203           (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1204           (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1205    for (i = 0; i < NCCS; ++i)
1206        printf(":%x", (unsigned int) mode->c_cc[i]);
1207    putchar('\n');
1208}
1209
1210static int recover_mode(char *arg, struct termios *mode)
1211{
1212    int i, n;
1213    unsigned int chr;
1214    unsigned long iflag, oflag, cflag, lflag;
1215
1216    /* Scan into temporaries since it is too much trouble to figure out
1217       the right format for `tcflag_t'.  */
1218    if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1219               &iflag, &oflag, &cflag, &lflag, &n) != 4)
1220        return 0;
1221    mode->c_iflag = iflag;
1222    mode->c_oflag = oflag;
1223    mode->c_cflag = cflag;
1224    mode->c_lflag = lflag;
1225    arg += n;
1226    for (i = 0; i < NCCS; ++i) {
1227        if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1228            return 0;
1229        mode->c_cc[i] = chr;
1230        arg += n;
1231    }
1232
1233    /* Fail if there are too many fields.  */
1234    if (*arg != '\0')
1235        return 0;
1236
1237    return 1;
1238}
1239
1240static speed_t string_to_baud(const char *arg)
1241{
1242    return tty_value_to_baud(bb_xparse_number(arg, 0));
1243}
1244
1245static void sane_mode(struct termios *mode)
1246{
1247    int i;
1248    tcflag_t *bitsp;
1249
1250    for (i = 0; i < NUM_control_info; ++i) {
1251#if VMIN == VEOF
1252        if (control_info[i].name == stty_min)
1253            break;
1254#endif
1255        mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1256    }
1257
1258    for (i = 0; i < NUM_mode_info; ++i) {
1259        if (mode_info[i].flags & SANE_SET) {
1260            bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1261            *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1262                | mode_info[i].bits;
1263        } else if (mode_info[i].flags & SANE_UNSET) {
1264            bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
1265            *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1266                & ~mode_info[i].bits;
1267        }
1268    }
1269}
1270
1271/* Return a string that is the printable representation of character CH.  */
1272/* Adapted from `cat' by Torbjorn Granlund.  */
1273
1274static const char *visible(unsigned int ch)
1275{
1276    static char buf[10];
1277    char *bpout = buf;
1278
1279    if (ch == _POSIX_VDISABLE) {
1280        return "<undef>";
1281    }
1282
1283    if (ch >= 128) {
1284        ch -= 128;
1285        *bpout++ = 'M';
1286        *bpout++ = '-';
1287    }
1288
1289    if (ch < 32) {
1290        *bpout++ = '^';
1291        *bpout++ = ch + 64;
1292    } else if (ch < 127) {
1293        *bpout++ = ch;
1294    } else {
1295        *bpout++ = '^';
1296        *bpout++ = '?';
1297    }
1298
1299    *bpout = '\0';
1300    return (const char *) buf;
1301}
1302
1303#ifdef TEST
1304
1305const char *bb_applet_name = "stty";
1306
1307#endif
Note: See TracBrowser for help on using the repository browser.