diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-25 01:23:02 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-12-25 01:23:02 +0000 |
commit | 5a4f0994b0ddd8ba8d96675382c42040ef516753 (patch) | |
tree | 23fb8e56edac5f408e95bad153af86ee5bd71331 | |
parent | 7a50a64986758e0890e76411d5997c4a07d9c698 (diff) | |
download | busybox-w32-5a4f0994b0ddd8ba8d96675382c42040ef516753.tar.gz busybox-w32-5a4f0994b0ddd8ba8d96675382c42040ef516753.tar.bz2 busybox-w32-5a4f0994b0ddd8ba8d96675382c42040ef516753.zip |
less: fix regexp search '/' on large files
-rw-r--r-- | miscutils/less.c | 118 |
1 files changed, 71 insertions, 47 deletions
diff --git a/miscutils/less.c b/miscutils/less.c index 290635d32..60c54e610 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
@@ -11,14 +11,14 @@ | |||
11 | * TODO: | 11 | * TODO: |
12 | * - Add more regular expression support - search modifiers, certain matches, etc. | 12 | * - Add more regular expression support - search modifiers, certain matches, etc. |
13 | * - Add more complex bracket searching - currently, nested brackets are | 13 | * - Add more complex bracket searching - currently, nested brackets are |
14 | * not considered. | 14 | * not considered. |
15 | * - Add support for "F" as an input. This causes less to act in | 15 | * - Add support for "F" as an input. This causes less to act in |
16 | * a similar way to tail -f. | 16 | * a similar way to tail -f. |
17 | * - Allow horizontal scrolling. | 17 | * - Allow horizontal scrolling. |
18 | * | 18 | * |
19 | * Notes: | 19 | * Notes: |
20 | * - the inp file pointer is used so that keyboard input works after | 20 | * - the inp file pointer is used so that keyboard input works after |
21 | * redirected input has been read from stdin | 21 | * redirected input has been read from stdin |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "busybox.h" | 24 | #include "busybox.h" |
@@ -130,7 +130,6 @@ static void less_exit(int code) | |||
130 | * and restore it when we exit. Less does this with the | 130 | * and restore it when we exit. Less does this with the |
131 | * "ti" and "te" termcap commands; can this be done with | 131 | * "ti" and "te" termcap commands; can this be done with |
132 | * only termios.h? */ | 132 | * only termios.h? */ |
133 | |||
134 | putchar('\n'); | 133 | putchar('\n'); |
135 | fflush_stdout_and_exit(code); | 134 | fflush_stdout_and_exit(code); |
136 | } | 135 | } |
@@ -158,12 +157,18 @@ static void print_statusline(const char *str) | |||
158 | printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); | 157 | printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); |
159 | } | 158 | } |
160 | 159 | ||
160 | #if ENABLE_FEATURE_LESS_REGEXP | ||
161 | static void fill_match_lines(unsigned pos); | ||
162 | #else | ||
163 | #define fill_match_lines(pos) ((void)0) | ||
164 | #endif | ||
165 | |||
166 | |||
161 | static void read_lines(void) | 167 | static void read_lines(void) |
162 | { | 168 | { |
163 | /* TODO: regexp match array should be updated too */ | ||
164 | |||
165 | #define readbuf bb_common_bufsiz1 | 169 | #define readbuf bb_common_bufsiz1 |
166 | char *current_line, *p; | 170 | char *current_line, *p; |
171 | unsigned old_max_fline = max_fline; | ||
167 | int w = width; | 172 | int w = width; |
168 | char last_terminated = terminated; | 173 | char last_terminated = terminated; |
169 | 174 | ||
@@ -179,6 +184,8 @@ static void read_lines(void) | |||
179 | cp += 8; | 184 | cp += 8; |
180 | strcpy(current_line, cp); | 185 | strcpy(current_line, cp); |
181 | p += strlen(current_line); | 186 | p += strlen(current_line); |
187 | } else { | ||
188 | linepos = 0; | ||
182 | } | 189 | } |
183 | 190 | ||
184 | while (1) { | 191 | while (1) { |
@@ -188,9 +195,9 @@ static void read_lines(void) | |||
188 | while (1) { | 195 | while (1) { |
189 | char c; | 196 | char c; |
190 | if (readpos >= readeof) { | 197 | if (readpos >= readeof) { |
191 | ndelay_on(0); | 198 | ndelay_on(0); |
192 | eof_error = safe_read(0, readbuf, sizeof(readbuf)); | 199 | eof_error = safe_read(0, readbuf, sizeof(readbuf)); |
193 | ndelay_off(0); | 200 | ndelay_off(0); |
194 | readpos = 0; | 201 | readpos = 0; |
195 | readeof = eof_error; | 202 | readeof = eof_error; |
196 | if (eof_error < 0) { | 203 | if (eof_error < 0) { |
@@ -254,16 +261,16 @@ ndelay_off(0); | |||
254 | p = current_line; | 261 | p = current_line; |
255 | linepos = 0; | 262 | linepos = 0; |
256 | } | 263 | } |
264 | fill_match_lines(old_max_fline); | ||
257 | #undef readbuf | 265 | #undef readbuf |
258 | } | 266 | } |
259 | 267 | ||
260 | #if ENABLE_FEATURE_LESS_FLAGS | 268 | #if ENABLE_FEATURE_LESS_FLAGS |
261 | |||
262 | /* Interestingly, writing calc_percent as a function saves around 32 bytes | 269 | /* Interestingly, writing calc_percent as a function saves around 32 bytes |
263 | * on my build. */ | 270 | * on my build. */ |
264 | static int calc_percent(void) | 271 | static int calc_percent(void) |
265 | { | 272 | { |
266 | unsigned p = 100 * (cur_fline + max_displayed_line) / (max_fline + 1); | 273 | unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1); |
267 | return p <= 100 ? p : 100; | 274 | return p <= 100 ? p : 100; |
268 | } | 275 | } |
269 | 276 | ||
@@ -288,7 +295,6 @@ static void m_status_print(void) | |||
288 | percentage = calc_percent(); | 295 | percentage = calc_percent(); |
289 | printf("%i%%"NORMAL, percentage); | 296 | printf("%i%%"NORMAL, percentage); |
290 | } | 297 | } |
291 | |||
292 | #endif | 298 | #endif |
293 | 299 | ||
294 | /* Print the status line */ | 300 | /* Print the status line */ |
@@ -479,9 +485,12 @@ static void buffer_up(int nlines) | |||
479 | 485 | ||
480 | static void buffer_line(int linenum) | 486 | static void buffer_line(int linenum) |
481 | { | 487 | { |
488 | if (linenum < 0) | ||
489 | linenum = 0; | ||
490 | cur_fline = linenum; | ||
491 | read_lines(); | ||
482 | if (linenum + max_displayed_line > max_fline) | 492 | if (linenum + max_displayed_line > max_fline) |
483 | linenum = max_fline - max_displayed_line + TILDES; | 493 | linenum = max_fline - max_displayed_line + TILDES; |
484 | if (linenum < 0) linenum = 0; | ||
485 | cur_fline = linenum; | 494 | cur_fline = linenum; |
486 | buffer_fill_and_print(); | 495 | buffer_fill_and_print(); |
487 | } | 496 | } |
@@ -523,15 +532,24 @@ static void reinitialize(void) | |||
523 | buffer_fill_and_print(); | 532 | buffer_fill_and_print(); |
524 | } | 533 | } |
525 | 534 | ||
526 | static char* getch_nowait(void) | 535 | static void getch_nowait(char* input, int sz) |
527 | { | 536 | { |
528 | static char input[16]; | 537 | ssize_t rd; |
529 | ssize_t sz; | ||
530 | fd_set readfds; | 538 | fd_set readfds; |
531 | again: | 539 | again: |
532 | fflush(stdout); | 540 | fflush(stdout); |
541 | |||
542 | /* NB: select returns whenever read will not block. Therefore: | ||
543 | * (a) with O_NONBLOCK'ed fds select will return immediately | ||
544 | * (b) if eof is reached, select will also return | ||
545 | * because read will immediately return 0 bytes. | ||
546 | * Even if select says that input is available, read CAN block | ||
547 | * (switch fd into O_NONBLOCK'ed mode to avoid it) | ||
548 | */ | ||
533 | FD_ZERO(&readfds); | 549 | FD_ZERO(&readfds); |
534 | if (max_fline <= cur_fline + max_displayed_line && eof_error > 0) { | 550 | if (max_fline <= cur_fline + max_displayed_line |
551 | && eof_error > 0 /* did NOT reach eof yet */ | ||
552 | ) { | ||
535 | /* We are interested in stdin */ | 553 | /* We are interested in stdin */ |
536 | FD_SET(0, &readfds); | 554 | FD_SET(0, &readfds); |
537 | } | 555 | } |
@@ -541,17 +559,16 @@ static char* getch_nowait(void) | |||
541 | 559 | ||
542 | input[0] = '\0'; | 560 | input[0] = '\0'; |
543 | ndelay_on(kbd_fd); | 561 | ndelay_on(kbd_fd); |
544 | sz = read(kbd_fd, input, sizeof(input)); | 562 | rd = read(kbd_fd, input, sz); |
545 | ndelay_off(kbd_fd); | 563 | ndelay_off(kbd_fd); |
546 | if (sz < 0) { | 564 | if (rd < 0) { |
547 | /* No keyboard input, but we have input on stdin! */ | 565 | /* No keyboard input, but we have input on stdin! */ |
548 | if (errno != EAGAIN) /* Huh?? */ | 566 | if (errno != EAGAIN) /* Huh?? */ |
549 | return input; | 567 | return; |
550 | read_lines(); | 568 | read_lines(); |
551 | buffer_fill_and_print(); | 569 | buffer_fill_and_print(); |
552 | goto again; | 570 | goto again; |
553 | } | 571 | } |
554 | return input; | ||
555 | } | 572 | } |
556 | 573 | ||
557 | /* Grab a character from input without requiring the return key. If the | 574 | /* Grab a character from input without requiring the return key. If the |
@@ -559,10 +576,10 @@ static char* getch_nowait(void) | |||
559 | * special return codes. Note that this function works best with raw input. */ | 576 | * special return codes. Note that this function works best with raw input. */ |
560 | static int less_getch(void) | 577 | static int less_getch(void) |
561 | { | 578 | { |
562 | char *input; | 579 | char input[16]; |
563 | unsigned i; | 580 | unsigned i; |
564 | again: | 581 | again: |
565 | input = getch_nowait(); | 582 | getch_nowait(input, sizeof(input)); |
566 | /* Detect escape sequences (i.e. arrow keys) and handle | 583 | /* Detect escape sequences (i.e. arrow keys) and handle |
567 | * them accordingly */ | 584 | * them accordingly */ |
568 | 585 | ||
@@ -718,6 +735,24 @@ static void goto_match(int match) | |||
718 | buffer_line(match_lines[normalize_match_pos(match)]); | 735 | buffer_line(match_lines[normalize_match_pos(match)]); |
719 | } | 736 | } |
720 | 737 | ||
738 | static void fill_match_lines(unsigned pos) | ||
739 | { | ||
740 | if (!pattern_valid) | ||
741 | return; | ||
742 | /* Run the regex on each line of the current file */ | ||
743 | while (pos <= max_fline) { | ||
744 | /* If this line matches */ | ||
745 | if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0 | ||
746 | /* and we didn't match it last time */ | ||
747 | && !(num_matches && match_lines[num_matches-1] == pos) | ||
748 | ) { | ||
749 | match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int)); | ||
750 | match_lines[num_matches++] = pos; | ||
751 | } | ||
752 | pos++; | ||
753 | } | ||
754 | } | ||
755 | |||
721 | static void regex_process(void) | 756 | static void regex_process(void) |
722 | { | 757 | { |
723 | char *uncomp_regex, *err; | 758 | char *uncomp_regex, *err; |
@@ -751,24 +786,21 @@ static void regex_process(void) | |||
751 | return; | 786 | return; |
752 | } | 787 | } |
753 | pattern_valid = 1; | 788 | pattern_valid = 1; |
789 | match_pos = 0; | ||
754 | 790 | ||
755 | /* Run the regex on each line of the current file */ | 791 | fill_match_lines(0); |
756 | for (match_pos = 0; match_pos <= max_fline; match_pos++) { | ||
757 | if (regexec(&pattern, flines[match_pos], 0, NULL, 0) == 0) { | ||
758 | match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int)); | ||
759 | match_lines[num_matches++] = match_pos; | ||
760 | } | ||
761 | } | ||
762 | 792 | ||
763 | if (num_matches == 0 || max_fline <= max_displayed_line) { | 793 | if (num_matches == 0 || max_fline <= max_displayed_line) { |
764 | buffer_print(); | 794 | buffer_print(); |
765 | return; | 795 | return; |
766 | } | 796 | } |
767 | for (match_pos = 0; match_pos < num_matches; match_pos++) { | 797 | while (match_pos < num_matches) { |
768 | if (match_lines[match_pos] > cur_fline) | 798 | if (match_lines[match_pos] > cur_fline) |
769 | break; | 799 | break; |
800 | match_pos++; | ||
770 | } | 801 | } |
771 | if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) match_pos--; | 802 | if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) |
803 | match_pos--; | ||
772 | buffer_line(match_lines[normalize_match_pos(match_pos)]); | 804 | buffer_line(match_lines[normalize_match_pos(match_pos)]); |
773 | } | 805 | } |
774 | #endif | 806 | #endif |
@@ -809,11 +841,9 @@ static void number_process(int first_digit) | |||
809 | switch (keypress) { | 841 | switch (keypress) { |
810 | case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': | 842 | case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': |
811 | buffer_down(num); | 843 | buffer_down(num); |
812 | buffer_print(); | ||
813 | break; | 844 | break; |
814 | case KEY_UP: case 'b': case 'w': case 'y': case 'u': | 845 | case KEY_UP: case 'b': case 'w': case 'y': case 'u': |
815 | buffer_up(num); | 846 | buffer_up(num); |
816 | buffer_print(); | ||
817 | break; | 847 | break; |
818 | case 'g': case '<': case 'G': case '>': | 848 | case 'g': case '<': case 'G': case '>': |
819 | cur_fline = num + max_displayed_line; | 849 | cur_fline = num + max_displayed_line; |
@@ -898,12 +928,13 @@ static void show_flag_status(void) | |||
898 | } | 928 | } |
899 | 929 | ||
900 | clear_line(); | 930 | clear_line(); |
901 | printf(HIGHLIGHT"%s%u"NORMAL, "The status of the flag is: ", flag_val != 0); | 931 | printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0); |
902 | } | 932 | } |
903 | #endif | 933 | #endif |
904 | 934 | ||
905 | static void save_input_to_file(void) | 935 | static void save_input_to_file(void) |
906 | { | 936 | { |
937 | const char *msg = ""; | ||
907 | char *current_line; | 938 | char *current_line; |
908 | int i; | 939 | int i; |
909 | FILE *fp; | 940 | FILE *fp; |
@@ -912,19 +943,18 @@ static void save_input_to_file(void) | |||
912 | current_line = less_gets(sizeof("Log file: ")-1); | 943 | current_line = less_gets(sizeof("Log file: ")-1); |
913 | if (strlen(current_line) > 0) { | 944 | if (strlen(current_line) > 0) { |
914 | fp = fopen(current_line, "w"); | 945 | fp = fopen(current_line, "w"); |
915 | free(current_line); | ||
916 | if (!fp) { | 946 | if (!fp) { |
917 | print_statusline("Error opening log file"); | 947 | msg = "Error opening log file"; |
918 | return; | 948 | goto ret; |
919 | } | 949 | } |
920 | for (i = 0; i < max_fline; i++) | 950 | for (i = 0; i <= max_fline; i++) |
921 | fprintf(fp, "%s\n", flines[i]); | 951 | fprintf(fp, "%s\n", flines[i]); |
922 | fclose(fp); | 952 | fclose(fp); |
923 | buffer_print(); | 953 | msg = "Done"; |
924 | return; | ||
925 | } | 954 | } |
955 | ret: | ||
956 | print_statusline(msg); | ||
926 | free(current_line); | 957 | free(current_line); |
927 | print_statusline("No log file"); | ||
928 | } | 958 | } |
929 | 959 | ||
930 | #if ENABLE_FEATURE_LESS_MARKS | 960 | #if ENABLE_FEATURE_LESS_MARKS |
@@ -1033,27 +1063,21 @@ static void keypress_process(int keypress) | |||
1033 | switch (keypress) { | 1063 | switch (keypress) { |
1034 | case KEY_DOWN: case 'e': case 'j': case 0x0d: | 1064 | case KEY_DOWN: case 'e': case 'j': case 0x0d: |
1035 | buffer_down(1); | 1065 | buffer_down(1); |
1036 | buffer_print(); | ||
1037 | break; | 1066 | break; |
1038 | case KEY_UP: case 'y': case 'k': | 1067 | case KEY_UP: case 'y': case 'k': |
1039 | buffer_up(1); | 1068 | buffer_up(1); |
1040 | buffer_print(); | ||
1041 | break; | 1069 | break; |
1042 | case PAGE_DOWN: case ' ': case 'z': | 1070 | case PAGE_DOWN: case ' ': case 'z': |
1043 | buffer_down(max_displayed_line + 1); | 1071 | buffer_down(max_displayed_line + 1); |
1044 | buffer_print(); | ||
1045 | break; | 1072 | break; |
1046 | case PAGE_UP: case 'w': case 'b': | 1073 | case PAGE_UP: case 'w': case 'b': |
1047 | buffer_up(max_displayed_line + 1); | 1074 | buffer_up(max_displayed_line + 1); |
1048 | buffer_print(); | ||
1049 | break; | 1075 | break; |
1050 | case 'd': | 1076 | case 'd': |
1051 | buffer_down((max_displayed_line + 1) / 2); | 1077 | buffer_down((max_displayed_line + 1) / 2); |
1052 | buffer_print(); | ||
1053 | break; | 1078 | break; |
1054 | case 'u': | 1079 | case 'u': |
1055 | buffer_up((max_displayed_line + 1) / 2); | 1080 | buffer_up((max_displayed_line + 1) / 2); |
1056 | buffer_print(); | ||
1057 | break; | 1081 | break; |
1058 | case KEY_HOME: case 'g': case 'p': case '<': case '%': | 1082 | case KEY_HOME: case 'g': case 'p': case '<': case '%': |
1059 | buffer_line(0); | 1083 | buffer_line(0); |