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