aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2006-09-19 14:16:28 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2006-09-19 14:16:28 +0000
commit7eab79acc58a14078a8cf14fb9be7aa5103a5d41 (patch)
tree6ac5a597a975f2b5d7cdcbc83648a34277f9c0d6
parent85f9e32f7f00b4579abb60637495dcd3a8a8bce0 (diff)
downloadbusybox-w32-7eab79acc58a14078a8cf14fb9be7aa5103a5d41.tar.gz
busybox-w32-7eab79acc58a14078a8cf14fb9be7aa5103a5d41.tar.bz2
busybox-w32-7eab79acc58a14078a8cf14fb9be7aa5103a5d41.zip
stty: fix a longstanding FIXME (was able to die half-way setting term params)
-rw-r--r--coreutils/stty.c412
1 files changed, 241 insertions, 171 deletions
diff --git a/coreutils/stty.c b/coreutils/stty.c
index d709bfc1e..a63c1d1fa 100644
--- a/coreutils/stty.c
+++ b/coreutils/stty.c
@@ -173,7 +173,7 @@ struct mode_info {
173 173
174#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B } 174#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
175 175
176static const struct mode_info mode_info[] = { 176static const struct mode_info mode_info[] = {
177 MI_ENTRY("parenb", control, REV, PARENB, 0 ), 177 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
178 MI_ENTRY("parodd", control, REV, PARODD, 0 ), 178 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
179 MI_ENTRY("cs5", control, 0, CS5, CSIZE), 179 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
@@ -333,7 +333,7 @@ struct control_info {
333 333
334/* Control characters. */ 334/* Control characters. */
335 335
336static const struct control_info control_info[] = { 336static const struct control_info control_info[] = {
337 {"intr", CINTR, VINTR}, 337 {"intr", CINTR, VINTR},
338 {"quit", CQUIT, VQUIT}, 338 {"quit", CQUIT, VQUIT},
339 {"erase", CERASE, VERASE}, 339 {"erase", CERASE, VERASE},
@@ -380,9 +380,9 @@ enum {
380#define EMT(t) ((enum mode_type)(t)) 380#define EMT(t) ((enum mode_type)(t))
381 381
382static const char * visible(unsigned int ch); 382static const char * visible(unsigned int ch);
383static int recover_mode(char *arg, struct termios *mode); 383static int recover_mode(const char *arg, struct termios *mode);
384static int screen_columns(void); 384static int screen_columns(void);
385static int set_mode(const struct mode_info *info, 385static void set_mode(const struct mode_info *info,
386 int reversed, struct termios *mode); 386 int reversed, struct termios *mode);
387static speed_t string_to_baud(const char *arg); 387static speed_t string_to_baud(const char *arg);
388static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode); 388static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
@@ -398,7 +398,7 @@ static void set_speed(enum speed_setting type,
398 const char *arg, struct termios *mode); 398 const char *arg, struct termios *mode);
399static void set_window_size(int rows, int cols); 399static void set_window_size(int rows, int cols);
400 400
401static const char *device_name; 401static const char *device_name = bb_msg_standard_input;
402 402
403static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt) 403static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
404{ 404{
@@ -445,80 +445,192 @@ static const struct suffix_mult stty_suffixes[] = {
445 {NULL, 0 } 445 {NULL, 0 }
446}; 446};
447 447
448static const struct mode_info *find_mode(const char *name)
449{
450 int i;
451 for (i = 0; i < NUM_mode_info; ++i)
452 if (STREQ(name, mode_info[i].name))
453 return &mode_info[i];
454 return 0;
455}
456
457static const struct control_info *find_control(const char *name)
458{
459 int i;
460 for (i = 0; i < NUM_control_info; ++i)
461 if (STREQ(name, control_info[i].name))
462 return &control_info[i];
463 return 0;
464}
465
466enum {
467 param_need_arg = 0x80,
468 param_line = 1 | 0x80,
469 param_rows = 2 | 0x80,
470 param_cols = 3 | 0x80,
471 param_size = 4,
472 param_ispeed = 5 | 0x80,
473 param_ospeed = 6 | 0x80,
474 param_speed = 7,
475};
476
477static int find_param(const char *name)
478{
479#ifdef HAVE_C_LINE
480 if (STREQ(name, "line")) return param_line;
481#endif
482#ifdef TIOCGWINSZ
483 if (STREQ(name, "rows")) return param_rows;
484 if (STREQ(name, "cols")) return param_cols;
485 if (STREQ(name, "columns")) return param_cols;
486 if (STREQ(name, "size")) return param_size;
487#endif
488 if (STREQ(name, "ispeed")) return param_ispeed;
489 if (STREQ(name, "ospeed")) return param_ospeed;
490 if (STREQ(name, "speed")) return param_speed;
491 return 0;
492}
493
494
448int stty_main(int argc, char **argv) 495int stty_main(int argc, char **argv)
449{ 496{
450 struct termios mode; 497 struct termios mode;
451 void (*output_func)(struct termios *); 498 void (*output_func)(struct termios *);
452 int optc; 499 const char *file_name = NULL;
453 int require_set_attr; 500 int require_set_attr;
454 int speed_was_set; 501 int speed_was_set;
455 int verbose_output; 502 int verbose_output;
456 int recoverable_output; 503 int recoverable_output;
457 int k; 504 int noargs;
458 int noargs = 1; 505 int k;
459 char * file_name = NULL;
460 506
461 output_func = display_changed; 507 output_func = display_changed;
508 noargs = 1;
509 speed_was_set = 0;
510 require_set_attr = 0;
462 verbose_output = 0; 511 verbose_output = 0;
463 recoverable_output = 0; 512 recoverable_output = 0;
464 513
465 /* Don't print error messages for unrecognized options. */ 514 /* First pass: only parse/verify command line params */
466 opterr = 0; 515 k = 0;
467 516 while (argv[++k]) {
468 while ((optc = getopt(argc, argv, "agF:")) != -1) { 517 const struct mode_info *mp;
469 switch (optc) { 518 const struct control_info *cp;
470 case 'a': 519 const char *arg = argv[k];
471 verbose_output = 1; 520 const char *argnext = argv[k+1];
472 output_func = display_all; 521 int param;
473 break; 522
474 523 if (arg[0] == '-') {
475 case 'g': 524 int i;
476 recoverable_output = 1; 525 mp = find_mode(arg+1);
477 output_func = display_recoverable; 526 if (mp) {
478 break; 527 if (!(mp->flags & REV))
528 bb_error_msg_and_die("invalid argument '%s'", arg);
529 noargs = 0;
530 continue;
531 }
532 /* It is an option - parse it */
533 i = 0;
534 while (arg[++i]) {
535 switch (arg[i]) {
536 case 'a':
537 verbose_output = 1;
538 output_func = display_all;
539 break;
540 case 'g':
541 recoverable_output = 1;
542 output_func = display_recoverable;
543 break;
544 case 'F':
545 if (file_name)
546 bb_error_msg_and_die("only one device may be specified");
547 file_name = &arg[i+1]; /* "-Fdevice" ? */
548 if (!file_name[0]) { /* nope, "-F device" */
549 int p = k+1; /* argv[p] is argnext */
550 file_name = argnext;
551 if (!file_name)
552 bb_error_msg_and_die(bb_msg_requires_arg, "-F");
553 /* remove -F param from arg[vc] */
554 --argc;
555 while (argv[p+1]) { argv[p] = argv[p+1]; ++p; }
556 }
557 goto end_option;
558 default:
559 bb_error_msg_and_die("invalid argument '%s'", arg);
560 }
561 }
562end_option:
563 continue;
564 }
479 565
480 case 'F': 566 mp = find_mode(arg);
481 if (file_name) 567 if (mp) {
482 bb_error_msg_and_die("only one device may be specified"); 568 noargs = 0;
483 file_name = optarg; 569 continue;
484 break; 570 }
485 571
486 default: /* unrecognized option */ 572 cp = find_control(arg);
573 if (cp) {
574 if (!argnext)
575 bb_error_msg_and_die(bb_msg_requires_arg, arg);
487 noargs = 0; 576 noargs = 0;
488 break; 577 ++k;
578 continue;
489 } 579 }
490 580
491 if (noargs == 0) 581 param = find_param(arg);
492 break; 582 if (param & param_need_arg) {
493 } 583 if (!argnext)
584 bb_error_msg_and_die(bb_msg_requires_arg, arg);
585 ++k;
586 }
494 587
495 if (optind < argc) 588 switch (param) {
589#ifdef HAVE_C_LINE
590 case param_line:
591 bb_xparse_number(argnext, stty_suffixes);
592 break;
593#endif
594#ifdef TIOCGWINSZ
595 case param_rows:
596 bb_xparse_number(argnext, stty_suffixes);
597 break;
598 case param_cols:
599 bb_xparse_number(argnext, stty_suffixes);
600 break;
601 case param_size:
602#endif
603 case param_ispeed:
604 case param_ospeed:
605 case param_speed:
606 break;
607 default:
608 if (recover_mode(arg, &mode) == 1) break;
609 if (string_to_baud(arg) != (speed_t) -1) break;
610 bb_error_msg_and_die("invalid argument '%s'", arg);
611 }
496 noargs = 0; 612 noargs = 0;
613 }
497 614
498 /* Specifying both -a and -g gets an error. */ 615 /* Specifying both -a and -g is an error */
499 if (verbose_output & recoverable_output) 616 if (verbose_output && recoverable_output)
500 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive"); 617 bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
501 618 /* Specifying -a or -g with non-options is an error */
502 /* Specifying any other arguments with -a or -g gets an error. */ 619 if (!noargs && (verbose_output || recoverable_output))
503 if (~noargs & (verbose_output | recoverable_output)) 620 bb_error_msg_and_die("modes may not be set when specifying an output style");
504 bb_error_msg_and_die ("modes may not be set when specifying an output style");
505
506 /* FIXME: it'd be better not to open the file until we've verified
507 that all arguments are valid. Otherwise, we could end up doing
508 only some of the requested operations and then failing, probably
509 leaving things in an undesirable state. */
510 621
622 /* Now it is safe to start doing things */
511 if (file_name) { 623 if (file_name) {
512 int fdflags; 624 int fd, fdflags;
513
514 device_name = file_name; 625 device_name = file_name;
515 fclose(stdin); 626 fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
516 xopen(device_name, O_RDONLY | O_NONBLOCK); 627 if (fd != 0) {
628 dup2(fd, 0);
629 close(fd);
630 }
517 fdflags = fcntl(STDIN_FILENO, F_GETFL); 631 fdflags = fcntl(STDIN_FILENO, F_GETFL);
518 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0) 632 if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
519 perror_on_device("%s: couldn't reset non-blocking mode"); 633 perror_on_device("%s: couldn't reset non-blocking mode");
520 } else {
521 device_name = bb_msg_standard_input;
522 } 634 }
523 635
524 /* Initialize to all zeroes so there is no risk memcmp will report a 636 /* Initialize to all zeroes so there is no risk memcmp will report a
@@ -527,122 +639,92 @@ int stty_main(int argc, char **argv)
527 if (tcgetattr(STDIN_FILENO, &mode)) 639 if (tcgetattr(STDIN_FILENO, &mode))
528 perror_on_device("%s"); 640 perror_on_device("%s");
529 641
530 if (verbose_output | recoverable_output | noargs) { 642 if (verbose_output || recoverable_output || noargs) {
531 max_col = screen_columns(); 643 max_col = screen_columns();
532 current_col = 0; 644 current_col = 0;
533 output_func(&mode); 645 output_func(&mode);
534 return EXIT_SUCCESS; 646 return EXIT_SUCCESS;
535 } 647 }
536 648
537 speed_was_set = 0; 649 /* Second pass: perform actions */
538 require_set_attr = 0;
539 k = 0; 650 k = 0;
540 while (++k < argc) { 651 while (argv[++k]) {
541 int match_found = 0; 652 const struct mode_info *mp;
542 int reversed = 0; 653 const struct control_info *cp;
543 int i; 654 const char *arg = argv[k];
544 655 const char *argnext = argv[k+1];
545 if (argv[k][0] == '-') { 656 int param;
546 char *find_dev_opt; 657
547 658 if (arg[0] == '-') {
548 ++argv[k]; 659 mp = find_mode(arg+1);
549 660 if (mp) {
550 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc. 661 set_mode(mp, 1 /* reversed */, &mode);
551 Find the options that have been parsed. This is really
552 gross, but it's needed because stty SETTINGS look like options to
553 getopt(), so we need to work around things in a really horrible
554 way. If any new options are ever added to stty, the short option
555 MUST NOT be a letter which is the first letter of one of the
556 possible stty settings.
557 */
558 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
559 if(find_dev_opt) {
560 if(find_dev_opt[1]==0) /* -*F /dev/foo */
561 k++; /* skip /dev/foo */
562 continue; /* else -*F/dev/foo - no skip */
563 } 662 }
564 if(argv[k][0]=='a' || argv[k][0]=='g') 663 /* It is an option - already parsed. Skip it */
565 continue; 664 continue;
566 /* Is not options - is reverse params */
567 reversed = 1;
568 } 665 }
569 for (i = 0; i < NUM_mode_info; ++i)
570 if (STREQ(argv[k], mode_info[i].name)) {
571 match_found = set_mode(&mode_info[i], reversed, &mode);
572 require_set_attr = 1;
573 break;
574 }
575 666
576 if (match_found == 0 && reversed) 667 mp = find_mode(arg);
577 bb_error_msg_and_die("invalid argument `%s'", --argv[k]); 668 if (mp) {
578 669 set_mode(mp, 0 /* non-reversed */, &mode);
579 if (match_found == 0) 670 continue;
580 for (i = 0; i < NUM_control_info; ++i) 671 }
581 if (STREQ(argv[k], control_info[i].name)) {
582 if (k == argc - 1)
583 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
584 match_found = 1;
585 ++k;
586 set_control_char(&control_info[i], argv[k], &mode);
587 require_set_attr = 1;
588 break;
589 }
590 672
591 if (match_found == 0) { 673 cp = find_control(arg);
592 if (STREQ(argv[k], "ispeed")) { 674 if (cp) {
593 if (k == argc - 1) 675 ++k;
594 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]); 676 set_control_char(cp, argnext, &mode);
595 ++k; 677 continue;
596 set_speed(input_speed, argv[k], &mode); 678 }
597 speed_was_set = 1; 679
598 require_set_attr = 1; 680 param = find_param(arg);
599 } else if (STREQ(argv[k], "ospeed")) { 681 if (param & param_need_arg) {
600 if (k == argc - 1) 682 ++k;
601 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]); 683 }
602 ++k; 684
603 set_speed(output_speed, argv[k], &mode); 685 switch (param) {
604 speed_was_set = 1;
605 require_set_attr = 1;
606 }
607#ifdef TIOCGWINSZ
608 else if (STREQ(argv[k], "rows")) {
609 if (k == argc - 1)
610 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
611 ++k;
612 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
613 -1);
614 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
615 if (k == argc - 1)
616 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]);
617 ++k;
618 set_window_size(-1,
619 (int) bb_xparse_number(argv[k], stty_suffixes));
620 } else if (STREQ(argv[k], "size")) {
621 max_col = screen_columns();
622 current_col = 0;
623 display_window_size(0);
624 }
625#endif
626#ifdef HAVE_C_LINE 686#ifdef HAVE_C_LINE
627 else if (STREQ(argv[k], "line")) { 687 case param_line:
628 if (k == argc - 1) 688 mode.c_line = bb_xparse_number(argnext, stty_suffixes);
629 bb_error_msg_and_die(bb_msg_requires_arg, argv[k]); 689 require_set_attr = 1;
630 ++k; 690 break;
631 mode.c_line = bb_xparse_number(argv[k], stty_suffixes); 691#endif
632 require_set_attr = 1; 692#ifdef TIOCGWINSZ
633 } 693 case param_cols:
694 set_window_size(-1, (int) bb_xparse_number(argnext, stty_suffixes));
695 break;
696 case param_size:
697 max_col = screen_columns();
698 current_col = 0;
699 display_window_size(0);
700 break;
701 case param_rows:
702 set_window_size((int) bb_xparse_number(argnext, stty_suffixes), -1);
703 break;
634#endif 704#endif
635 else if (STREQ(argv[k], "speed")) { 705 case param_ispeed:
636 max_col = screen_columns(); 706 set_speed(input_speed, argnext, &mode);
637 display_speed(&mode, 0); 707 speed_was_set = 1;
638 } else if (recover_mode(argv[k], &mode) == 1) 708 require_set_attr = 1;
709 break;
710 case param_ospeed:
711 set_speed(output_speed, argnext, &mode);
712 speed_was_set = 1;
713 require_set_attr = 1;
714 break;
715 case param_speed:
716 max_col = screen_columns();
717 display_speed(&mode, 0);
718 break;
719 default:
720 if (recover_mode(arg, &mode) == 1)
639 require_set_attr = 1; 721 require_set_attr = 1;
640 else if (string_to_baud(argv[k]) != (speed_t) - 1) { 722 else /* true: if (string_to_baud(arg) != (speed_t) -1) */ {
641 set_speed(both_speeds, argv[k], &mode); 723 set_speed(both_speeds, arg, &mode);
642 speed_was_set = 1; 724 speed_was_set = 1;
643 require_set_attr = 1; 725 require_set_attr = 1;
644 } else 726 } /* else - impossible (caught in the first pass):
645 bb_error_msg_and_die("invalid argument `%s'", argv[k]); 727 bb_error_msg_and_die("invalid argument '%s'", arg); */
646 } 728 }
647 } 729 }
648 730
@@ -665,13 +747,6 @@ int stty_main(int argc, char **argv)
665 if (tcgetattr(STDIN_FILENO, &new_mode)) 747 if (tcgetattr(STDIN_FILENO, &new_mode))
666 perror_on_device("%s"); 748 perror_on_device("%s");
667 749
668 /* Normally, one shouldn't use memcmp to compare structures that
669 may have `holes' containing uninitialized data, but we have been
670 careful to initialize the storage of these two variables to all
671 zeroes. One might think it more efficient simply to compare the
672 modified fields, but that would require enumerating those fields --
673 and not all systems have the same fields in this structure. */
674
675 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { 750 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
676#ifdef CIBAUD 751#ifdef CIBAUD
677 /* SunOS 4.1.3 (at least) has the problem that after this sequence, 752 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
@@ -695,14 +770,11 @@ int stty_main(int argc, char **argv)
695 770
696/* Return 0 if not applied because not reversible; otherwise return 1. */ 771/* Return 0 if not applied because not reversible; otherwise return 1. */
697 772
698static int 773static void
699set_mode(const struct mode_info *info, int reversed, struct termios *mode) 774set_mode(const struct mode_info *info, int reversed, struct termios *mode)
700{ 775{
701 tcflag_t *bitsp; 776 tcflag_t *bitsp;
702 777
703 if (reversed && (info->flags & REV) == 0)
704 return 0;
705
706 bitsp = mode_type_flag(EMT(info->type), mode); 778 bitsp = mode_type_flag(EMT(info->type), mode);
707 779
708 if (bitsp == NULL) { 780 if (bitsp == NULL) {
@@ -861,8 +933,6 @@ set_mode(const struct mode_info *info, int reversed, struct termios *mode)
861 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits; 933 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
862 else 934 else
863 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits; 935 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
864
865 return 1;
866} 936}
867 937
868static void 938static void
@@ -1179,7 +1249,7 @@ static void display_recoverable(struct termios *mode)
1179 putchar('\n'); 1249 putchar('\n');
1180} 1250}
1181 1251
1182static int recover_mode(char *arg, struct termios *mode) 1252static int recover_mode(const char *arg, struct termios *mode)
1183{ 1253{
1184 int i, n; 1254 int i, n;
1185 unsigned int chr; 1255 unsigned int chr;
@@ -1269,5 +1339,5 @@ static const char *visible(unsigned int ch)
1269 } 1339 }
1270 1340
1271 *bpout = '\0'; 1341 *bpout = '\0';
1272 return (const char *) buf; 1342 return buf;
1273} 1343}