summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-02-23 01:25:38 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-02-23 01:25:38 +0000
commit33196372bed41405ff240f13fe63ca5587912ed2 (patch)
tree8e19c7f3323c17dd3f57186b5f2bd61eafb24f9d
parent86620756d2143bd8728c3160db4096169f7c6fc0 (diff)
downloadbusybox-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.h29
-rw-r--r--miscutils/less.c99
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
275char *xmalloc_follow_symlinks(const char *path); 275char *xmalloc_follow_symlinks(const char *path);
276 276
277//enum { 277enum {
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};
280void bb_signals(int sigs, void (*f)(int)); 303void 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 };
90struct globals { 90struct 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
655static void getch_nowait(char* input, int sz) 664static 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. */
697static int less_getch(void) 709static 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
765static void examine_file(void) 775static 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();