source: MondoRescue/branches/stable/mindi-busybox/coreutils/stty.c@ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 18 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.