From 5a4f0994b0ddd8ba8d96675382c42040ef516753 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Mon, 25 Dec 2006 01:23:02 +0000 Subject: less: fix regexp search '/' on large files --- miscutils/less.c | 118 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 47 deletions(-) (limited to 'miscutils/less.c') 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 @@ * TODO: * - Add more regular expression support - search modifiers, certain matches, etc. * - Add more complex bracket searching - currently, nested brackets are - * not considered. + * not considered. * - Add support for "F" as an input. This causes less to act in - * a similar way to tail -f. + * a similar way to tail -f. * - Allow horizontal scrolling. * * Notes: * - the inp file pointer is used so that keyboard input works after - * redirected input has been read from stdin + * redirected input has been read from stdin */ #include "busybox.h" @@ -130,7 +130,6 @@ static void less_exit(int code) * and restore it when we exit. Less does this with the * "ti" and "te" termcap commands; can this be done with * only termios.h? */ - putchar('\n'); fflush_stdout_and_exit(code); } @@ -158,12 +157,18 @@ static void print_statusline(const char *str) printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); } +#if ENABLE_FEATURE_LESS_REGEXP +static void fill_match_lines(unsigned pos); +#else +#define fill_match_lines(pos) ((void)0) +#endif + + static void read_lines(void) { - /* TODO: regexp match array should be updated too */ - #define readbuf bb_common_bufsiz1 char *current_line, *p; + unsigned old_max_fline = max_fline; int w = width; char last_terminated = terminated; @@ -179,6 +184,8 @@ static void read_lines(void) cp += 8; strcpy(current_line, cp); p += strlen(current_line); + } else { + linepos = 0; } while (1) { @@ -188,9 +195,9 @@ static void read_lines(void) while (1) { char c; if (readpos >= readeof) { -ndelay_on(0); + ndelay_on(0); eof_error = safe_read(0, readbuf, sizeof(readbuf)); -ndelay_off(0); + ndelay_off(0); readpos = 0; readeof = eof_error; if (eof_error < 0) { @@ -254,16 +261,16 @@ ndelay_off(0); p = current_line; linepos = 0; } + fill_match_lines(old_max_fline); #undef readbuf } #if ENABLE_FEATURE_LESS_FLAGS - /* Interestingly, writing calc_percent as a function saves around 32 bytes * on my build. */ static int calc_percent(void) { - unsigned p = 100 * (cur_fline + max_displayed_line) / (max_fline + 1); + unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1); return p <= 100 ? p : 100; } @@ -288,7 +295,6 @@ static void m_status_print(void) percentage = calc_percent(); printf("%i%%"NORMAL, percentage); } - #endif /* Print the status line */ @@ -479,9 +485,12 @@ static void buffer_up(int nlines) static void buffer_line(int linenum) { + if (linenum < 0) + linenum = 0; + cur_fline = linenum; + read_lines(); if (linenum + max_displayed_line > max_fline) linenum = max_fline - max_displayed_line + TILDES; - if (linenum < 0) linenum = 0; cur_fline = linenum; buffer_fill_and_print(); } @@ -523,15 +532,24 @@ static void reinitialize(void) buffer_fill_and_print(); } -static char* getch_nowait(void) +static void getch_nowait(char* input, int sz) { - static char input[16]; - ssize_t sz; + ssize_t rd; fd_set readfds; again: fflush(stdout); + + /* NB: select returns whenever read will not block. Therefore: + * (a) with O_NONBLOCK'ed fds select will return immediately + * (b) if eof is reached, select will also return + * because read will immediately return 0 bytes. + * Even if select says that input is available, read CAN block + * (switch fd into O_NONBLOCK'ed mode to avoid it) + */ FD_ZERO(&readfds); - if (max_fline <= cur_fline + max_displayed_line && eof_error > 0) { + if (max_fline <= cur_fline + max_displayed_line + && eof_error > 0 /* did NOT reach eof yet */ + ) { /* We are interested in stdin */ FD_SET(0, &readfds); } @@ -541,17 +559,16 @@ static char* getch_nowait(void) input[0] = '\0'; ndelay_on(kbd_fd); - sz = read(kbd_fd, input, sizeof(input)); + rd = read(kbd_fd, input, sz); ndelay_off(kbd_fd); - if (sz < 0) { + if (rd < 0) { /* No keyboard input, but we have input on stdin! */ if (errno != EAGAIN) /* Huh?? */ - return input; + return; read_lines(); buffer_fill_and_print(); goto again; } - return input; } /* Grab a character from input without requiring the return key. If the @@ -559,10 +576,10 @@ static char* getch_nowait(void) * special return codes. Note that this function works best with raw input. */ static int less_getch(void) { - char *input; + char input[16]; unsigned i; again: - input = getch_nowait(); + getch_nowait(input, sizeof(input)); /* Detect escape sequences (i.e. arrow keys) and handle * them accordingly */ @@ -718,6 +735,24 @@ static void goto_match(int match) buffer_line(match_lines[normalize_match_pos(match)]); } +static void fill_match_lines(unsigned pos) +{ + if (!pattern_valid) + return; + /* Run the regex on each line of the current file */ + while (pos <= max_fline) { + /* If this line matches */ + if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0 + /* and we didn't match it last time */ + && !(num_matches && match_lines[num_matches-1] == pos) + ) { + match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int)); + match_lines[num_matches++] = pos; + } + pos++; + } +} + static void regex_process(void) { char *uncomp_regex, *err; @@ -751,24 +786,21 @@ static void regex_process(void) return; } pattern_valid = 1; + match_pos = 0; - /* Run the regex on each line of the current file */ - for (match_pos = 0; match_pos <= max_fline; match_pos++) { - if (regexec(&pattern, flines[match_pos], 0, NULL, 0) == 0) { - match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int)); - match_lines[num_matches++] = match_pos; - } - } + fill_match_lines(0); if (num_matches == 0 || max_fline <= max_displayed_line) { buffer_print(); return; } - for (match_pos = 0; match_pos < num_matches; match_pos++) { + while (match_pos < num_matches) { if (match_lines[match_pos] > cur_fline) break; + match_pos++; } - if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) match_pos--; + if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) + match_pos--; buffer_line(match_lines[normalize_match_pos(match_pos)]); } #endif @@ -809,11 +841,9 @@ static void number_process(int first_digit) switch (keypress) { case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': buffer_down(num); - buffer_print(); break; case KEY_UP: case 'b': case 'w': case 'y': case 'u': buffer_up(num); - buffer_print(); break; case 'g': case '<': case 'G': case '>': cur_fline = num + max_displayed_line; @@ -898,12 +928,13 @@ static void show_flag_status(void) } clear_line(); - printf(HIGHLIGHT"%s%u"NORMAL, "The status of the flag is: ", flag_val != 0); + printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0); } #endif static void save_input_to_file(void) { + const char *msg = ""; char *current_line; int i; FILE *fp; @@ -912,19 +943,18 @@ static void save_input_to_file(void) current_line = less_gets(sizeof("Log file: ")-1); if (strlen(current_line) > 0) { fp = fopen(current_line, "w"); - free(current_line); if (!fp) { - print_statusline("Error opening log file"); - return; + msg = "Error opening log file"; + goto ret; } - for (i = 0; i < max_fline; i++) + for (i = 0; i <= max_fline; i++) fprintf(fp, "%s\n", flines[i]); fclose(fp); - buffer_print(); - return; + msg = "Done"; } + ret: + print_statusline(msg); free(current_line); - print_statusline("No log file"); } #if ENABLE_FEATURE_LESS_MARKS @@ -1033,27 +1063,21 @@ static void keypress_process(int keypress) switch (keypress) { case KEY_DOWN: case 'e': case 'j': case 0x0d: buffer_down(1); - buffer_print(); break; case KEY_UP: case 'y': case 'k': buffer_up(1); - buffer_print(); break; case PAGE_DOWN: case ' ': case 'z': buffer_down(max_displayed_line + 1); - buffer_print(); break; case PAGE_UP: case 'w': case 'b': buffer_up(max_displayed_line + 1); - buffer_print(); break; case 'd': buffer_down((max_displayed_line + 1) / 2); - buffer_print(); break; case 'u': buffer_up((max_displayed_line + 1) / 2); - buffer_print(); break; case KEY_HOME: case 'g': case 'p': case '<': case '%': buffer_line(0); -- cgit v1.2.3