diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-23 01:25:38 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-23 01:25:38 +0000 |
commit | 33196372bed41405ff240f13fe63ca5587912ed2 (patch) | |
tree | 8e19c7f3323c17dd3f57186b5f2bd61eafb24f9d | |
parent | 86620756d2143bd8728c3160db4096169f7c6fc0 (diff) | |
download | busybox-w32-33196372bed41405ff240f13fe63ca5587912ed2.tar.gz busybox-w32-33196372bed41405ff240f13fe63ca5587912ed2.tar.bz2 busybox-w32-33196372bed41405ff240f13fe63ca5587912ed2.zip |
less: update line input so that it doesn't interfere with
screen update. Makes "man bash", [enter], [/],
<enter search pattern>, [enter] more usable - manpage
draws as you enter the pattern! Yay!!
less: fix bug where backspace wasn't actually deleting chars
less: "examine file with empty name" doesn't abort anymore.
libbb: add "all fatal signals" mask.
getch_nowait - 207 +207
status_print - 105 +105
examine_file 64 87 +23
move_cursor - 16 +16
m_status_print 185 195 +10
less_main 1656 1663 +7
decode_format_string 790 795 +5
test_main 403 405 +2
process0_stdin 247 249 +2
passwd_main 1070 1072 +2
less_gets 196 178 -18
buffer_print 169 71 -98
less_getch 362 138 -224
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 7/3 up/down: 379/-340) Total: 39 bytes
text data bss dec hex filename
798329 740 7484 806553 c4e99 busybox_old
798368 740 7484 806592 c4ec0 busybox_unstripped
-rw-r--r-- | include/libbb.h | 29 | ||||
-rw-r--r-- | miscutils/less.c | 99 |
2 files changed, 82 insertions, 46 deletions
diff --git a/include/libbb.h b/include/libbb.h index eb8b2f620..873ab8798 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -274,9 +274,32 @@ char *xrealloc_getcwd_or_warn(char *cwd); | |||
274 | 274 | ||
275 | char *xmalloc_follow_symlinks(const char *path); | 275 | char *xmalloc_follow_symlinks(const char *path); |
276 | 276 | ||
277 | //enum { | 277 | enum { |
278 | // BB_SIGS_FATAL = , | 278 | /* bb_signals(BB_SIGS_FATAL, handler) catches all signals which |
279 | //}; | 279 | * otherwise would kill us, except for those resulting from bugs: |
280 | * SIGSEGV, SIGILL, SIGFPE. | ||
281 | * Other fatal signals not included (TODO?): | ||
282 | * SIGBUS Bus error (bad memory access) | ||
283 | * SIGPOLL Pollable event. Synonym of SIGIO | ||
284 | * SIGPROF Profiling timer expired | ||
285 | * SIGSYS Bad argument to routine | ||
286 | * SIGTRAP Trace/breakpoint trap | ||
287 | */ | ||
288 | BB_SIGS_FATAL = 0 | ||
289 | + (1 << SIGHUP) | ||
290 | + (1 << SIGINT) | ||
291 | + (1 << SIGTERM) | ||
292 | + (1 << SIGPIPE) // Write to pipe with no readers | ||
293 | + (1 << SIGQUIT) // Quit from keyboard | ||
294 | + (1 << SIGABRT) // Abort signal from abort(3) | ||
295 | + (1 << SIGALRM) // Timer signal from alarm(2) | ||
296 | + (1 << SIGVTALRM) // Virtual alarm clock | ||
297 | + (1 << SIGXCPU) // CPU time limit exceeded | ||
298 | + (1 << SIGXFSZ) // File size limit exceeded | ||
299 | + (1 << SIGUSR1) // Yes kids, these are also fatal! | ||
300 | + (1 << SIGUSR2) | ||
301 | + 0, | ||
302 | }; | ||
280 | void bb_signals(int sigs, void (*f)(int)); | 303 | void bb_signals(int sigs, void (*f)(int)); |
281 | /* Unlike signal() and bb_signals, sets handler with sigaction() | 304 | /* Unlike signal() and bb_signals, sets handler with sigaction() |
282 | * and in a way that while signal handler is run, no other signals | 305 | * and in a way that while signal handler is run, no other signals |
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(); |