From 107fe7c081c2e0ab96628b431d9d812cdf9c82b2 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Mon, 17 Mar 2008 08:38:45 +0000 Subject: [PATCH] less: improve search when data is not supplied fast enough by stdin - now will try reading for 1-2 seconds before declaring that there is no match. This fixes a very common annoyance with long manpages. function old new delta read_lines 653 719 +66 buffer_down 28 83 +55 goto_match 140 141 +1 cap_cur_fline 72 - -72 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 3/0 up/down: 122/-72) Total: 50 bytes text data bss dec hex filename 798734 661 7428 806823 c4fa7 busybox_old 798768 661 7428 806857 c4fc9 busybox_unstripped --- miscutils/less.c | 116 ++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 51 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index 8d4f7ecd4..3a9dea039 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -98,8 +98,8 @@ struct globals { unsigned max_lineno; /* this one tracks linewrap */ unsigned width; ssize_t eof_error; /* eof if 0, error if < 0 */ - size_t readpos; - size_t readeof; + ssize_t readpos; + ssize_t readeof; /* must be signed */ const char **buffer; const char **flines; const char *empty_line_marker; @@ -114,7 +114,8 @@ struct globals { #if ENABLE_FEATURE_LESS_REGEXP unsigned *match_lines; int match_pos; /* signed! */ - unsigned num_matches; + int wanted_match; /* signed! */ + int num_matches; regex_t pattern; smallint pattern_valid; #endif @@ -146,6 +147,7 @@ struct globals { #define match_lines (G.match_lines ) #define match_pos (G.match_pos ) #define num_matches (G.num_matches ) +#define wanted_match (G.wanted_match ) #define pattern (G.pattern ) #define pattern_valid (G.pattern_valid ) #endif @@ -160,6 +162,7 @@ struct globals { current_file = 1; \ eof_error = 1; \ terminated = 1; \ + USE_FEATURE_LESS_REGEXP(wanted_match = -1;) \ } while (0) /* Reset terminal input to normal */ @@ -235,15 +238,20 @@ static void read_lines(void) { #define readbuf bb_common_bufsiz1 char *current_line, *p; - USE_FEATURE_LESS_REGEXP(unsigned old_max_fline = max_fline;) int w = width; char last_terminated = terminated; +#if ENABLE_FEATURE_LESS_REGEXP + unsigned old_max_fline = max_fline; + time_t last_time = 0; + unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */ +#endif if (option_mask32 & FLAG_N) w -= 8; - current_line = xmalloc(w); - p = current_line; + USE_FEATURE_LESS_REGEXP(again0:) + + p = current_line = xmalloc(w); max_fline += last_terminated; if (!last_terminated) { const char *cp = flines[max_fline]; @@ -251,49 +259,26 @@ static void read_lines(void) cp += 8; strcpy(current_line, cp); p += strlen(current_line); + free((char*)flines[max_fline]); /* linepos is still valid from previous read_lines() */ } else { linepos = 0; } - while (1) { - again: + while (1) { /* read lines until we reach cur_fline or wanted_match */ *p = '\0'; terminated = 0; - while (1) { + while (1) { /* read chars until we have a line */ char c; /* if no unprocessed chars left, eat more */ if (readpos >= readeof) { - smallint yielded = 0; - ndelay_on(0); - read_again: eof_error = safe_read(0, readbuf, sizeof(readbuf)); + ndelay_off(0); readpos = 0; readeof = eof_error; - if (eof_error < 0) { - if (errno == EAGAIN && !yielded) { - /* We can hit EAGAIN while searching for regexp match. - * Yield is not 100% reliable solution in general, - * but for less it should be good enough - - * we give stdin supplier some CPU time to produce - * more input. We do it just once. - * Currently, we do not stop when we found the Nth - * occurrence we were looking for. We read till end - * (or double EAGAIN). TODO? */ - sched_yield(); - yielded = 1; - goto read_again; - } - readeof = 0; - if (errno != EAGAIN) - print_statusline("read error"); - } - ndelay_off(0); - - if (eof_error <= 0) { + if (eof_error <= 0) goto reached_eof; - } } c = readbuf[readpos]; /* backspace? [needed for manpages] */ @@ -327,13 +312,13 @@ static void read_lines(void) if (c == '\0') c = '\n'; *p++ = c; *p = '\0'; - } + } /* end of "read chars until we have a line" loop */ /* Corner case: linewrap with only "" wrapping to next line */ /* Looks ugly on screen, so we do not store this empty line */ if (!last_terminated && !current_line[0]) { last_terminated = 1; max_lineno++; - goto again; + continue; } reached_eof: last_terminated = terminated; @@ -353,22 +338,51 @@ static void read_lines(void) eof_error = 0; /* Pretend we saw EOF */ break; } - if (max_fline > cur_fline + max_displayed_line) + if (max_fline > cur_fline + max_displayed_line) { +#if !ENABLE_FEATURE_LESS_REGEXP break; - if (eof_error <= 0) { - if (eof_error < 0 && errno == EAGAIN) { - /* not yet eof or error, reset flag (or else - * we will hog CPU - select() will return - * immediately */ - eof_error = 1; +#else + if (wanted_match >= num_matches) { /* goto_match called us */ + fill_match_lines(old_max_fline); + old_max_fline = max_fline; } + if (wanted_match < num_matches) + break; +#endif + } + if (eof_error <= 0) { + if (eof_error < 0) { + if (errno == EAGAIN) { + /* not yet eof or error, reset flag (or else + * we will hog CPU - select() will return + * immediately */ + eof_error = 1; + } else { + print_statusline("read error"); + } + } +#if !ENABLE_FEATURE_LESS_REGEXP break; +#else + if (wanted_match < num_matches) { + break; + } else { /* goto_match called us */ + time_t t = time(NULL); + if (t != last_time) { + last_time = t; + if (--seconds_p1 == 0) + break; + } + sched_yield(); + goto again0; /* go loop again (max 2 seconds) */ + } +#endif } max_fline++; current_line = xmalloc(w); p = current_line; linepos = 0; - } + } /* end of "read lines until we reach cur_fline" loop */ fill_match_lines(old_max_fline); #undef readbuf } @@ -884,24 +898,24 @@ static void normalize_match_pos(int match) static void goto_match(int match) { - int sv; - if (!pattern_valid) return; if (match < 0) match = 0; - sv = cur_fline; /* Try to find next match if eof isn't reached yet */ if (match >= num_matches && eof_error > 0) { - cur_fline = MAXLINES; /* look as far as needed */ + wanted_match = match; read_lines(); + if (wanted_match >= num_matches) { + /* We still failed to find it. Prevent future + * read_lines() from trying... */ + wanted_match = num_matches - 1; + } } if (num_matches) { - cap_cur_fline(cur_fline); normalize_match_pos(match); buffer_line(match_lines[match_pos]); } else { - cur_fline = sv; print_statusline("No matches found"); } } @@ -1370,7 +1384,7 @@ int less_main(int argc, char **argv) get_terminal_width_height(kbd_fd, &width, &max_displayed_line); /* 20: two tabstops + 4 */ if (width < 20 || max_displayed_line < 3) - bb_error_msg_and_die("too narrow here"); + return bb_cat(argv); max_displayed_line -= 2; buffer = xmalloc((max_displayed_line+1) * sizeof(char *));