diff options
Diffstat (limited to 'miscutils/less.c')
| -rw-r--r-- | miscutils/less.c | 99 |
1 files changed, 56 insertions, 43 deletions
diff --git a/miscutils/less.c b/miscutils/less.c index 85c5ec536..4229dfe15 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
| @@ -90,6 +90,7 @@ enum { pattern_valid = 0 }; | |||
| 90 | struct globals { | 90 | struct globals { |
| 91 | int cur_fline; /* signed */ | 91 | int cur_fline; /* signed */ |
| 92 | int kbd_fd; /* fd to get input from */ | 92 | int kbd_fd; /* fd to get input from */ |
| 93 | int less_gets_pos; | ||
| 93 | /* last position in last line, taking into account tabs */ | 94 | /* last position in last line, taking into account tabs */ |
| 94 | size_t linepos; | 95 | size_t linepos; |
| 95 | unsigned max_displayed_line; | 96 | unsigned max_displayed_line; |
| @@ -123,6 +124,7 @@ struct globals { | |||
| 123 | #define G (*ptr_to_globals) | 124 | #define G (*ptr_to_globals) |
| 124 | #define cur_fline (G.cur_fline ) | 125 | #define cur_fline (G.cur_fline ) |
| 125 | #define kbd_fd (G.kbd_fd ) | 126 | #define kbd_fd (G.kbd_fd ) |
| 127 | #define less_gets_pos (G.less_gets_pos ) | ||
| 126 | #define linepos (G.linepos ) | 128 | #define linepos (G.linepos ) |
| 127 | #define max_displayed_line (G.max_displayed_line) | 129 | #define max_displayed_line (G.max_displayed_line) |
| 128 | #define max_fline (G.max_fline ) | 130 | #define max_fline (G.max_fline ) |
| @@ -152,6 +154,7 @@ struct globals { | |||
| 152 | #define term_less (G.term_less ) | 154 | #define term_less (G.term_less ) |
| 153 | #define INIT_G() do { \ | 155 | #define INIT_G() do { \ |
| 154 | PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ | 156 | PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ |
| 157 | less_gets_pos = -1; \ | ||
| 155 | empty_line_marker = "~"; \ | 158 | empty_line_marker = "~"; \ |
| 156 | num_files = 1; \ | 159 | num_files = 1; \ |
| 157 | current_file = 1; \ | 160 | current_file = 1; \ |
| @@ -385,6 +388,9 @@ static void m_status_print(void) | |||
| 385 | { | 388 | { |
| 386 | int percentage; | 389 | int percentage; |
| 387 | 390 | ||
| 391 | if (less_gets_pos >= 0) /* don't touch statusline while input is done! */ | ||
| 392 | return; | ||
| 393 | |||
| 388 | clear_line(); | 394 | clear_line(); |
| 389 | printf(HIGHLIGHT"%s", filename); | 395 | printf(HIGHLIGHT"%s", filename); |
| 390 | if (num_files > 1) | 396 | if (num_files > 1) |
| @@ -408,6 +414,9 @@ static void status_print(void) | |||
| 408 | { | 414 | { |
| 409 | const char *p; | 415 | const char *p; |
| 410 | 416 | ||
| 417 | if (less_gets_pos >= 0) /* don't touch statusline while input is done! */ | ||
| 418 | return; | ||
| 419 | |||
| 411 | /* Change the status if flags have been set */ | 420 | /* Change the status if flags have been set */ |
| 412 | #if ENABLE_FEATURE_LESS_FLAGS | 421 | #if ENABLE_FEATURE_LESS_FLAGS |
| 413 | if (option_mask32 & (FLAG_M|FLAG_m)) { | 422 | if (option_mask32 & (FLAG_M|FLAG_m)) { |
| @@ -652,43 +661,46 @@ static void reinitialize(void) | |||
| 652 | buffer_fill_and_print(); | 661 | buffer_fill_and_print(); |
| 653 | } | 662 | } |
| 654 | 663 | ||
| 655 | static void getch_nowait(char* input, int sz) | 664 | static ssize_t getch_nowait(char* input, int sz) |
| 656 | { | 665 | { |
| 657 | ssize_t rd; | 666 | ssize_t rd; |
| 658 | fd_set readfds; | 667 | struct pollfd pfd[2]; |
| 659 | again: | ||
| 660 | fflush(stdout); | ||
| 661 | 668 | ||
| 662 | /* NB: select returns whenever read will not block. Therefore: | 669 | pfd[0].fd = STDIN_FILENO; |
| 663 | * (a) with O_NONBLOCK'ed fds select will return immediately | 670 | pfd[0].events = POLLIN; |
| 664 | * (b) if eof is reached, select will also return | 671 | pfd[1].fd = kbd_fd; |
| 665 | * because read will immediately return 0 bytes. | 672 | pfd[1].events = POLLIN; |
| 666 | * Even if select says that input is available, read CAN block | 673 | again: |
| 674 | tcsetattr(kbd_fd, TCSANOW, &term_less); | ||
| 675 | /* NB: select/poll returns whenever read will not block. Therefore: | ||
| 676 | * if eof is reached, select/poll will return immediately | ||
| 677 | * because read will immediately return 0 bytes. | ||
| 678 | * Even if select/poll says that input is available, read CAN block | ||
| 667 | * (switch fd into O_NONBLOCK'ed mode to avoid it) | 679 | * (switch fd into O_NONBLOCK'ed mode to avoid it) |
| 668 | */ | 680 | */ |
| 669 | FD_ZERO(&readfds); | 681 | rd = 1; |
| 670 | if (max_fline <= cur_fline + max_displayed_line | 682 | if (max_fline <= cur_fline + max_displayed_line |
| 671 | && eof_error > 0 /* did NOT reach eof yet */ | 683 | && eof_error > 0 /* did NOT reach eof yet */ |
| 672 | ) { | 684 | ) { |
| 673 | /* We are interested in stdin */ | 685 | /* We are interested in stdin */ |
| 674 | FD_SET(0, &readfds); | 686 | rd = 0; |
| 675 | } | 687 | } |
| 676 | FD_SET(kbd_fd, &readfds); | 688 | /* position cursor if line input is done */ |
| 677 | tcsetattr(kbd_fd, TCSANOW, &term_less); | 689 | if (less_gets_pos >= 0) |
| 678 | select(kbd_fd + 1, &readfds, NULL, NULL, NULL); | 690 | move_cursor(max_displayed_line + 2, less_gets_pos + 1); |
| 691 | fflush(stdout); | ||
| 692 | safe_poll(pfd + rd, 2 - rd, -1); | ||
| 679 | 693 | ||
| 680 | input[0] = '\0'; | 694 | input[0] = '\0'; |
| 681 | ndelay_on(kbd_fd); | 695 | rd = safe_read(kbd_fd, input, sz); /* NB: kbd_fd is in O_NONBLOCK mode */ |
| 682 | rd = read(kbd_fd, input, sz); | 696 | if (rd < 0 && errno == EAGAIN) { |
| 683 | ndelay_off(kbd_fd); | 697 | /* No keyboard input -> we have input on stdin! */ |
| 684 | if (rd < 0) { | ||
| 685 | /* No keyboard input, but we have input on stdin! */ | ||
| 686 | if (errno != EAGAIN) /* Huh?? */ | ||
| 687 | return; | ||
| 688 | read_lines(); | 698 | read_lines(); |
| 689 | buffer_fill_and_print(); | 699 | buffer_fill_and_print(); |
| 690 | goto again; | 700 | goto again; |
| 691 | } | 701 | } |
| 702 | set_tty_cooked(); | ||
| 703 | return rd; | ||
| 692 | } | 704 | } |
| 693 | 705 | ||
| 694 | /* Grab a character from input without requiring the return key. If the | 706 | /* Grab a character from input without requiring the return key. If the |
| @@ -696,7 +708,7 @@ static void getch_nowait(char* input, int sz) | |||
| 696 | * special return codes. Note that this function works best with raw input. */ | 708 | * special return codes. Note that this function works best with raw input. */ |
| 697 | static int less_getch(void) | 709 | static int less_getch(void) |
| 698 | { | 710 | { |
| 699 | char input[16]; | 711 | unsigned char input[16]; |
| 700 | unsigned i; | 712 | unsigned i; |
| 701 | again: | 713 | again: |
| 702 | memset(input, 0, sizeof(input)); | 714 | memset(input, 0, sizeof(input)); |
| @@ -705,7 +717,6 @@ static int less_getch(void) | |||
| 705 | /* Detect escape sequences (i.e. arrow keys) and handle | 717 | /* Detect escape sequences (i.e. arrow keys) and handle |
| 706 | * them accordingly */ | 718 | * them accordingly */ |
| 707 | if (input[0] == '\033' && input[1] == '[') { | 719 | if (input[0] == '\033' && input[1] == '[') { |
| 708 | set_tty_cooked(); | ||
| 709 | i = input[2] - REAL_KEY_UP; | 720 | i = input[2] - REAL_KEY_UP; |
| 710 | if (i < 4) | 721 | if (i < 4) |
| 711 | return 20 + i; | 722 | return 20 + i; |
| @@ -724,8 +735,8 @@ static int less_getch(void) | |||
| 724 | } | 735 | } |
| 725 | /* Reject almost all control chars */ | 736 | /* Reject almost all control chars */ |
| 726 | i = input[0]; | 737 | i = input[0]; |
| 727 | if (i < ' ' && i != 0x0d && i != 8) goto again; | 738 | if (i < ' ' && i != 0x0d && i != 8) |
| 728 | set_tty_cooked(); | 739 | goto again; |
| 729 | return i; | 740 | return i; |
| 730 | } | 741 | } |
| 731 | 742 | ||
| @@ -734,22 +745,21 @@ static char* less_gets(int sz) | |||
| 734 | char c; | 745 | char c; |
| 735 | int i = 0; | 746 | int i = 0; |
| 736 | char *result = xzalloc(1); | 747 | char *result = xzalloc(1); |
| 737 | while (1) { | ||
| 738 | fflush(stdout); | ||
| 739 | |||
| 740 | /* I be damned if I know why is it needed *repeatedly*, | ||
| 741 | * but it is needed. Is it because of stdio? */ | ||
| 742 | tcsetattr(kbd_fd, TCSANOW, &term_less); | ||
| 743 | 748 | ||
| 749 | while (1) { | ||
| 744 | c = '\0'; | 750 | c = '\0'; |
| 745 | read(kbd_fd, &c, 1); | 751 | less_gets_pos = sz + i; |
| 746 | if (c == 0x0d) | 752 | getch_nowait(&c, 1); |
| 753 | if (c == 0x0d) { | ||
| 754 | less_gets_pos = -1; | ||
| 747 | return result; | 755 | return result; |
| 756 | } | ||
| 748 | if (c == 0x7f) | 757 | if (c == 0x7f) |
| 749 | c = 8; | 758 | c = 8; |
| 750 | if (c == 8 && i) { | 759 | if (c == 8 && i) { |
| 751 | printf("\x8 \x8"); | 760 | printf("\x8 \x8"); |
| 752 | i--; | 761 | i--; |
| 762 | result[i] = '\0'; | ||
| 753 | } | 763 | } |
| 754 | if (c < ' ') | 764 | if (c < ' ') |
| 755 | continue; | 765 | continue; |
| @@ -764,9 +774,17 @@ static char* less_gets(int sz) | |||
| 764 | 774 | ||
| 765 | static void examine_file(void) | 775 | static void examine_file(void) |
| 766 | { | 776 | { |
| 777 | char *new_fname; | ||
| 778 | |||
| 767 | print_statusline("Examine: "); | 779 | print_statusline("Examine: "); |
| 780 | new_fname = less_gets(sizeof("Examine: ")-1); | ||
| 781 | if (!new_fname[0]) { | ||
| 782 | free(new_fname); | ||
| 783 | status_print(); | ||
| 784 | return; | ||
| 785 | } | ||
| 768 | free(filename); | 786 | free(filename); |
| 769 | filename = less_gets(sizeof("Examine: ")-1); | 787 | filename = new_fname; |
| 770 | /* files start by = argv. why we assume that argv is infinitely long?? | 788 | /* files start by = argv. why we assume that argv is infinitely long?? |
| 771 | files[num_files] = filename; | 789 | files[num_files] = filename; |
| 772 | current_file = num_files + 1; | 790 | current_file = num_files + 1; |
| @@ -1087,7 +1105,7 @@ static void save_input_to_file(void) | |||
| 1087 | 1105 | ||
| 1088 | print_statusline("Log file: "); | 1106 | print_statusline("Log file: "); |
| 1089 | current_line = less_gets(sizeof("Log file: ")-1); | 1107 | current_line = less_gets(sizeof("Log file: ")-1); |
| 1090 | if (strlen(current_line) > 0) { | 1108 | if (current_line[0]) { |
| 1091 | fp = fopen(current_line, "w"); | 1109 | fp = fopen(current_line, "w"); |
| 1092 | if (!fp) { | 1110 | if (!fp) { |
| 1093 | msg = "Error opening log file"; | 1111 | msg = "Error opening log file"; |
| @@ -1334,6 +1352,7 @@ int less_main(int argc, char **argv) | |||
| 1334 | kbd_fd = open(CURRENT_TTY, O_RDONLY); | 1352 | kbd_fd = open(CURRENT_TTY, O_RDONLY); |
| 1335 | if (kbd_fd < 0) | 1353 | if (kbd_fd < 0) |
| 1336 | return bb_cat(argv); | 1354 | return bb_cat(argv); |
| 1355 | ndelay_on(kbd_fd); | ||
| 1337 | 1356 | ||
| 1338 | if (!num_files) { | 1357 | if (!num_files) { |
| 1339 | if (isatty(STDIN_FILENO)) { | 1358 | if (isatty(STDIN_FILENO)) { |
| @@ -1354,11 +1373,9 @@ int less_main(int argc, char **argv) | |||
| 1354 | if (option_mask32 & FLAG_TILDE) | 1373 | if (option_mask32 & FLAG_TILDE) |
| 1355 | empty_line_marker = ""; | 1374 | empty_line_marker = ""; |
| 1356 | 1375 | ||
| 1376 | bb_signals(BB_SIGS_FATAL, sig_catcher); | ||
| 1377 | |||
| 1357 | tcgetattr(kbd_fd, &term_orig); | 1378 | tcgetattr(kbd_fd, &term_orig); |
| 1358 | bb_signals(0 | ||
| 1359 | + (1 << SIGTERM) | ||
| 1360 | + (1 << SIGINT) | ||
| 1361 | , sig_catcher); | ||
| 1362 | term_less = term_orig; | 1379 | term_less = term_orig; |
| 1363 | term_less.c_lflag &= ~(ICANON | ECHO); | 1380 | term_less.c_lflag &= ~(ICANON | ECHO); |
| 1364 | term_less.c_iflag &= ~(IXON | ICRNL); | 1381 | term_less.c_iflag &= ~(IXON | ICRNL); |
| @@ -1366,10 +1383,6 @@ int less_main(int argc, char **argv) | |||
| 1366 | term_less.c_cc[VMIN] = 1; | 1383 | term_less.c_cc[VMIN] = 1; |
| 1367 | term_less.c_cc[VTIME] = 0; | 1384 | term_less.c_cc[VTIME] = 0; |
| 1368 | 1385 | ||
| 1369 | /* Want to do it just once, but it doesn't work, */ | ||
| 1370 | /* so we are redoing it (see code above). Mystery... */ | ||
| 1371 | /*tcsetattr(kbd_fd, TCSANOW, &term_less);*/ | ||
| 1372 | |||
| 1373 | reinitialize(); | 1386 | reinitialize(); |
| 1374 | while (1) { | 1387 | while (1) { |
| 1375 | keypress = less_getch(); | 1388 | keypress = less_getch(); |
