grep: handle pre context lines on demand
[gitweb.git] / grep.c
diff --git a/grep.c b/grep.c
index 4bca759b67e2d386ae87975780b0272209cc2942..9b9d2e39f8ae71e6e9a394f2b0566d7a137082e2 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -531,16 +531,42 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        printf("%.*s\n", rest, bol);
 }
 
+static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
+                            char *bol, unsigned lno)
+{
+       unsigned cur = lno, from = 1;
+
+       if (opt->pre_context < lno)
+               from = lno - opt->pre_context;
+       if (from <= opt->last_shown)
+               from = opt->last_shown + 1;
+
+       /* Rewind. */
+       while (bol > buf && cur > from) {
+               bol--;
+               while (bol > buf && bol[-1] != '\n')
+                       bol--;
+               cur--;
+       }
+
+       /* Back forward. */
+       while (cur < lno) {
+               char *eol = bol;
+
+               while (*eol != '\n')
+                       eol++;
+               show_line(opt, bol, eol, name, cur, '-');
+               bol = eol + 1;
+               cur++;
+       }
+}
+
 static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         char *buf, unsigned long size, int collect_hits)
 {
        char *bol = buf;
        unsigned long left = size;
        unsigned lno = 1;
-       struct pre_context_line {
-               char *bol;
-               char *eol;
-       } *prev = NULL, *pcl;
        unsigned last_hit = 0;
        int binary_match_only = 0;
        unsigned count = 0;
@@ -561,9 +587,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                }
        }
 
-       if (opt->pre_context)
-               prev = xcalloc(opt->pre_context, sizeof(*prev));
-
        while (left) {
                char *eol, ch;
                int hit;
@@ -610,21 +633,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         * the context which is nonsense, but the user
                         * deserves to get that ;-).
                         */
-                       if (opt->pre_context) {
-                               unsigned from;
-                               if (opt->pre_context < lno)
-                                       from = lno - opt->pre_context;
-                               else
-                                       from = 1;
-                               if (from <= opt->last_shown)
-                                       from = opt->last_shown + 1;
-                               while (from < lno) {
-                                       pcl = &prev[lno-from-1];
-                                       show_line(opt, pcl->bol, pcl->eol,
-                                                 name, from, '-');
-                                       from++;
-                               }
-                       }
+                       if (opt->pre_context)
+                               show_pre_context(opt, name, buf, bol, lno);
                        if (!opt->count)
                                show_line(opt, bol, eol, name, lno, ':');
                        last_hit = lno;
@@ -636,12 +646,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         */
                        show_line(opt, bol, eol, name, lno, '-');
                }
-               if (opt->pre_context) {
-                       memmove(prev+1, prev,
-                               (opt->pre_context-1) * sizeof(*prev));
-                       prev->bol = bol;
-                       prev->eol = eol;
-               }
 
        next_line:
                bol = eol + 1;
@@ -651,7 +655,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                lno++;
        }
 
-       free(prev);
        if (collect_hits)
                return 0;