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
This commit is contained in:
Denis Vlasenko 2008-03-17 08:38:45 +00:00
parent aefed941c2
commit 107fe7c081

View File

@ -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 *));