less: fix bugs discovered with "git log -p | less -m" on kernel tree
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 7 Apr 2014 21:32:29 +0000 (23:32 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 13 Apr 2014 14:02:59 +0000 (16:02 +0200)
function                                             old     new   delta
read_lines                                           685     733     +48

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
miscutils/less.c

index 574f222e0f88899dbcc3c9d71e1139bbeaca6678..36d0a0bd953d1e5d1afc489925637e01a8969d96 100644 (file)
@@ -404,6 +404,9 @@ static void fill_match_lines(unsigned pos);
  * last_line_pos - screen line position of next char to be read
  *      (takes into account tabs and backspaces)
  * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
+ *
+ * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs,
+ * "/search on very long input" and "reaching max line count" corner cases.
  */
 static void read_lines(void)
 {
@@ -414,9 +417,13 @@ static void read_lines(void)
 #if ENABLE_FEATURE_LESS_REGEXP
        unsigned old_max_fline = max_fline;
        time_t last_time = 0;
-       unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */
+       int had_progress = 2;
 #endif
 
+       /* (careful: max_fline can be -1) */
+       if (max_fline + 1 > MAXLINES)
+               return;
+
        if (option_mask32 & FLAG_N)
                w -= 8;
 
@@ -441,6 +448,7 @@ static void read_lines(void)
                        char c;
                        /* if no unprocessed chars left, eat more */
                        if (readpos >= readeof) {
+                               errno = 0;
                                ndelay_on(0);
                                eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf));
                                ndelay_off(0);
@@ -448,6 +456,7 @@ static void read_lines(void)
                                readeof = eof_error;
                                if (eof_error <= 0)
                                        goto reached_eof;
+                               had_progress = 1;
                        }
                        c = readbuf[readpos];
                        /* backspace? [needed for manpages] */
@@ -519,31 +528,23 @@ static void read_lines(void)
 #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(bb_msg_read_error);
-                               }
-                       }
 #if !ENABLE_FEATURE_LESS_REGEXP
                        break;
 #else
                        if (wanted_match < num_matches) {
                                break;
-                       } else { /* goto_match called us */
+                       } /* else: goto_match() called us */
+                       if (errno == EAGAIN) {
                                time_t t = time(NULL);
                                if (t != last_time) {
                                        last_time = t;
-                                       if (--seconds_p1 == 0)
+                                       if (--had_progress < 0)
                                                break;
                                }
                                sched_yield();
-                               goto again0; /* go loop again (max 2 seconds) */
+                               goto again0;
                        }
+                       break;
 #endif
                }
                max_fline++;
@@ -551,6 +552,15 @@ static void read_lines(void)
                p = current_line;
                last_line_pos = 0;
        } /* end of "read lines until we reach cur_fline" loop */
+
+       if (eof_error < 0) {
+               if (errno == EAGAIN) {
+                       eof_error = 1;
+               } else {
+                       print_statusline(bb_msg_read_error);
+               }
+       }
+
        fill_match_lines(old_max_fline);
 #if ENABLE_FEATURE_LESS_REGEXP
        /* prevent us from being stuck in search for a match */