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
diff --git a/miscutils/less.c b/miscutils/less.c
index 8d4f7ec..3a9dea0 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -98,8 +98,8 @@
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 @@
#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 @@
#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 @@
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 @@
{
#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 @@
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 @@
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 @@
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 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 @@
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 *));