diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-17 08:38:45 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-17 08:38:45 +0000 |
commit | 107fe7c081c2e0ab96628b431d9d812cdf9c82b2 (patch) | |
tree | 653d231e2dc06c990044882dffb445c726dad791 /miscutils | |
parent | aefed941c26aade36c5b51c8ef5ea6f740cc0e5d (diff) | |
download | busybox-w32-107fe7c081c2e0ab96628b431d9d812cdf9c82b2.tar.gz busybox-w32-107fe7c081c2e0ab96628b431d9d812cdf9c82b2.tar.bz2 busybox-w32-107fe7c081c2e0ab96628b431d9d812cdf9c82b2.zip |
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
Diffstat (limited to 'miscutils')
-rw-r--r-- | miscutils/less.c | 114 |
1 files changed, 64 insertions, 50 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 { | |||
98 | unsigned max_lineno; /* this one tracks linewrap */ | 98 | unsigned max_lineno; /* this one tracks linewrap */ |
99 | unsigned width; | 99 | unsigned width; |
100 | ssize_t eof_error; /* eof if 0, error if < 0 */ | 100 | ssize_t eof_error; /* eof if 0, error if < 0 */ |
101 | size_t readpos; | 101 | ssize_t readpos; |
102 | size_t readeof; | 102 | ssize_t readeof; /* must be signed */ |
103 | const char **buffer; | 103 | const char **buffer; |
104 | const char **flines; | 104 | const char **flines; |
105 | const char *empty_line_marker; | 105 | const char *empty_line_marker; |
@@ -114,7 +114,8 @@ struct globals { | |||
114 | #if ENABLE_FEATURE_LESS_REGEXP | 114 | #if ENABLE_FEATURE_LESS_REGEXP |
115 | unsigned *match_lines; | 115 | unsigned *match_lines; |
116 | int match_pos; /* signed! */ | 116 | int match_pos; /* signed! */ |
117 | unsigned num_matches; | 117 | int wanted_match; /* signed! */ |
118 | int num_matches; | ||
118 | regex_t pattern; | 119 | regex_t pattern; |
119 | smallint pattern_valid; | 120 | smallint pattern_valid; |
120 | #endif | 121 | #endif |
@@ -146,6 +147,7 @@ struct globals { | |||
146 | #define match_lines (G.match_lines ) | 147 | #define match_lines (G.match_lines ) |
147 | #define match_pos (G.match_pos ) | 148 | #define match_pos (G.match_pos ) |
148 | #define num_matches (G.num_matches ) | 149 | #define num_matches (G.num_matches ) |
150 | #define wanted_match (G.wanted_match ) | ||
149 | #define pattern (G.pattern ) | 151 | #define pattern (G.pattern ) |
150 | #define pattern_valid (G.pattern_valid ) | 152 | #define pattern_valid (G.pattern_valid ) |
151 | #endif | 153 | #endif |
@@ -160,6 +162,7 @@ struct globals { | |||
160 | current_file = 1; \ | 162 | current_file = 1; \ |
161 | eof_error = 1; \ | 163 | eof_error = 1; \ |
162 | terminated = 1; \ | 164 | terminated = 1; \ |
165 | USE_FEATURE_LESS_REGEXP(wanted_match = -1;) \ | ||
163 | } while (0) | 166 | } while (0) |
164 | 167 | ||
165 | /* Reset terminal input to normal */ | 168 | /* Reset terminal input to normal */ |
@@ -235,15 +238,20 @@ static void read_lines(void) | |||
235 | { | 238 | { |
236 | #define readbuf bb_common_bufsiz1 | 239 | #define readbuf bb_common_bufsiz1 |
237 | char *current_line, *p; | 240 | char *current_line, *p; |
238 | USE_FEATURE_LESS_REGEXP(unsigned old_max_fline = max_fline;) | ||
239 | int w = width; | 241 | int w = width; |
240 | char last_terminated = terminated; | 242 | char last_terminated = terminated; |
243 | #if ENABLE_FEATURE_LESS_REGEXP | ||
244 | unsigned old_max_fline = max_fline; | ||
245 | time_t last_time = 0; | ||
246 | unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */ | ||
247 | #endif | ||
241 | 248 | ||
242 | if (option_mask32 & FLAG_N) | 249 | if (option_mask32 & FLAG_N) |
243 | w -= 8; | 250 | w -= 8; |
244 | 251 | ||
245 | current_line = xmalloc(w); | 252 | USE_FEATURE_LESS_REGEXP(again0:) |
246 | p = current_line; | 253 | |
254 | p = current_line = xmalloc(w); | ||
247 | max_fline += last_terminated; | 255 | max_fline += last_terminated; |
248 | if (!last_terminated) { | 256 | if (!last_terminated) { |
249 | const char *cp = flines[max_fline]; | 257 | const char *cp = flines[max_fline]; |
@@ -251,49 +259,26 @@ static void read_lines(void) | |||
251 | cp += 8; | 259 | cp += 8; |
252 | strcpy(current_line, cp); | 260 | strcpy(current_line, cp); |
253 | p += strlen(current_line); | 261 | p += strlen(current_line); |
262 | free((char*)flines[max_fline]); | ||
254 | /* linepos is still valid from previous read_lines() */ | 263 | /* linepos is still valid from previous read_lines() */ |
255 | } else { | 264 | } else { |
256 | linepos = 0; | 265 | linepos = 0; |
257 | } | 266 | } |
258 | 267 | ||
259 | while (1) { | 268 | while (1) { /* read lines until we reach cur_fline or wanted_match */ |
260 | again: | ||
261 | *p = '\0'; | 269 | *p = '\0'; |
262 | terminated = 0; | 270 | terminated = 0; |
263 | while (1) { | 271 | while (1) { /* read chars until we have a line */ |
264 | char c; | 272 | char c; |
265 | /* if no unprocessed chars left, eat more */ | 273 | /* if no unprocessed chars left, eat more */ |
266 | if (readpos >= readeof) { | 274 | if (readpos >= readeof) { |
267 | smallint yielded = 0; | ||
268 | |||
269 | ndelay_on(0); | 275 | ndelay_on(0); |
270 | read_again: | ||
271 | eof_error = safe_read(0, readbuf, sizeof(readbuf)); | 276 | eof_error = safe_read(0, readbuf, sizeof(readbuf)); |
277 | ndelay_off(0); | ||
272 | readpos = 0; | 278 | readpos = 0; |
273 | readeof = eof_error; | 279 | readeof = eof_error; |
274 | if (eof_error < 0) { | 280 | if (eof_error <= 0) |
275 | if (errno == EAGAIN && !yielded) { | ||
276 | /* We can hit EAGAIN while searching for regexp match. | ||
277 | * Yield is not 100% reliable solution in general, | ||
278 | * but for less it should be good enough - | ||
279 | * we give stdin supplier some CPU time to produce | ||
280 | * more input. We do it just once. | ||
281 | * Currently, we do not stop when we found the Nth | ||
282 | * occurrence we were looking for. We read till end | ||
283 | * (or double EAGAIN). TODO? */ | ||
284 | sched_yield(); | ||
285 | yielded = 1; | ||
286 | goto read_again; | ||
287 | } | ||
288 | readeof = 0; | ||
289 | if (errno != EAGAIN) | ||
290 | print_statusline("read error"); | ||
291 | } | ||
292 | ndelay_off(0); | ||
293 | |||
294 | if (eof_error <= 0) { | ||
295 | goto reached_eof; | 281 | goto reached_eof; |
296 | } | ||
297 | } | 282 | } |
298 | c = readbuf[readpos]; | 283 | c = readbuf[readpos]; |
299 | /* backspace? [needed for manpages] */ | 284 | /* backspace? [needed for manpages] */ |
@@ -327,13 +312,13 @@ static void read_lines(void) | |||
327 | if (c == '\0') c = '\n'; | 312 | if (c == '\0') c = '\n'; |
328 | *p++ = c; | 313 | *p++ = c; |
329 | *p = '\0'; | 314 | *p = '\0'; |
330 | } | 315 | } /* end of "read chars until we have a line" loop */ |
331 | /* Corner case: linewrap with only "" wrapping to next line */ | 316 | /* Corner case: linewrap with only "" wrapping to next line */ |
332 | /* Looks ugly on screen, so we do not store this empty line */ | 317 | /* Looks ugly on screen, so we do not store this empty line */ |
333 | if (!last_terminated && !current_line[0]) { | 318 | if (!last_terminated && !current_line[0]) { |
334 | last_terminated = 1; | 319 | last_terminated = 1; |
335 | max_lineno++; | 320 | max_lineno++; |
336 | goto again; | 321 | continue; |
337 | } | 322 | } |
338 | reached_eof: | 323 | reached_eof: |
339 | last_terminated = terminated; | 324 | last_terminated = terminated; |
@@ -353,22 +338,51 @@ static void read_lines(void) | |||
353 | eof_error = 0; /* Pretend we saw EOF */ | 338 | eof_error = 0; /* Pretend we saw EOF */ |
354 | break; | 339 | break; |
355 | } | 340 | } |
356 | if (max_fline > cur_fline + max_displayed_line) | 341 | if (max_fline > cur_fline + max_displayed_line) { |
342 | #if !ENABLE_FEATURE_LESS_REGEXP | ||
357 | break; | 343 | break; |
344 | #else | ||
345 | if (wanted_match >= num_matches) { /* goto_match called us */ | ||
346 | fill_match_lines(old_max_fline); | ||
347 | old_max_fline = max_fline; | ||
348 | } | ||
349 | if (wanted_match < num_matches) | ||
350 | break; | ||
351 | #endif | ||
352 | } | ||
358 | if (eof_error <= 0) { | 353 | if (eof_error <= 0) { |
359 | if (eof_error < 0 && errno == EAGAIN) { | 354 | if (eof_error < 0) { |
360 | /* not yet eof or error, reset flag (or else | 355 | if (errno == EAGAIN) { |
361 | * we will hog CPU - select() will return | 356 | /* not yet eof or error, reset flag (or else |
362 | * immediately */ | 357 | * we will hog CPU - select() will return |
363 | eof_error = 1; | 358 | * immediately */ |
359 | eof_error = 1; | ||
360 | } else { | ||
361 | print_statusline("read error"); | ||
362 | } | ||
364 | } | 363 | } |
364 | #if !ENABLE_FEATURE_LESS_REGEXP | ||
365 | break; | 365 | break; |
366 | #else | ||
367 | if (wanted_match < num_matches) { | ||
368 | break; | ||
369 | } else { /* goto_match called us */ | ||
370 | time_t t = time(NULL); | ||
371 | if (t != last_time) { | ||
372 | last_time = t; | ||
373 | if (--seconds_p1 == 0) | ||
374 | break; | ||
375 | } | ||
376 | sched_yield(); | ||
377 | goto again0; /* go loop again (max 2 seconds) */ | ||
378 | } | ||
379 | #endif | ||
366 | } | 380 | } |
367 | max_fline++; | 381 | max_fline++; |
368 | current_line = xmalloc(w); | 382 | current_line = xmalloc(w); |
369 | p = current_line; | 383 | p = current_line; |
370 | linepos = 0; | 384 | linepos = 0; |
371 | } | 385 | } /* end of "read lines until we reach cur_fline" loop */ |
372 | fill_match_lines(old_max_fline); | 386 | fill_match_lines(old_max_fline); |
373 | #undef readbuf | 387 | #undef readbuf |
374 | } | 388 | } |
@@ -884,24 +898,24 @@ static void normalize_match_pos(int match) | |||
884 | 898 | ||
885 | static void goto_match(int match) | 899 | static void goto_match(int match) |
886 | { | 900 | { |
887 | int sv; | ||
888 | |||
889 | if (!pattern_valid) | 901 | if (!pattern_valid) |
890 | return; | 902 | return; |
891 | if (match < 0) | 903 | if (match < 0) |
892 | match = 0; | 904 | match = 0; |
893 | sv = cur_fline; | ||
894 | /* Try to find next match if eof isn't reached yet */ | 905 | /* Try to find next match if eof isn't reached yet */ |
895 | if (match >= num_matches && eof_error > 0) { | 906 | if (match >= num_matches && eof_error > 0) { |
896 | cur_fline = MAXLINES; /* look as far as needed */ | 907 | wanted_match = match; |
897 | read_lines(); | 908 | read_lines(); |
909 | if (wanted_match >= num_matches) { | ||
910 | /* We still failed to find it. Prevent future | ||
911 | * read_lines() from trying... */ | ||
912 | wanted_match = num_matches - 1; | ||
913 | } | ||
898 | } | 914 | } |
899 | if (num_matches) { | 915 | if (num_matches) { |
900 | cap_cur_fline(cur_fline); | ||
901 | normalize_match_pos(match); | 916 | normalize_match_pos(match); |
902 | buffer_line(match_lines[match_pos]); | 917 | buffer_line(match_lines[match_pos]); |
903 | } else { | 918 | } else { |
904 | cur_fline = sv; | ||
905 | print_statusline("No matches found"); | 919 | print_statusline("No matches found"); |
906 | } | 920 | } |
907 | } | 921 | } |
@@ -1370,7 +1384,7 @@ int less_main(int argc, char **argv) | |||
1370 | get_terminal_width_height(kbd_fd, &width, &max_displayed_line); | 1384 | get_terminal_width_height(kbd_fd, &width, &max_displayed_line); |
1371 | /* 20: two tabstops + 4 */ | 1385 | /* 20: two tabstops + 4 */ |
1372 | if (width < 20 || max_displayed_line < 3) | 1386 | if (width < 20 || max_displayed_line < 3) |
1373 | bb_error_msg_and_die("too narrow here"); | 1387 | return bb_cat(argv); |
1374 | max_displayed_line -= 2; | 1388 | max_displayed_line -= 2; |
1375 | 1389 | ||
1376 | buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); | 1390 | buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); |