Fourth batch
[gitweb.git] / diffcore-pickaxe.c
index 63722f86dca4c00389d6f305fd8aa1af8ef8ed6c..a9c6d60df22862e47ccb7fc1c7de9ed7cc4b1236 100644 (file)
@@ -7,52 +7,13 @@
 #include "diffcore.h"
 #include "xdiff-interface.h"
 #include "kwset.h"
+#include "commit.h"
+#include "quote.h"
 
 typedef int (*pickaxe_fn)(mmfile_t *one, mmfile_t *two,
                          struct diff_options *o,
                          regex_t *regexp, kwset_t kws);
 
-static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
-                        regex_t *regexp, kwset_t kws, pickaxe_fn fn);
-
-static void pickaxe(struct diff_queue_struct *q, struct diff_options *o,
-                   regex_t *regexp, kwset_t kws, pickaxe_fn fn)
-{
-       int i;
-       struct diff_queue_struct outq;
-
-       DIFF_QUEUE_CLEAR(&outq);
-
-       if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
-               /* Showing the whole changeset if needle exists */
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       if (pickaxe_match(p, o, regexp, kws, fn))
-                               return; /* do not munge the queue */
-               }
-
-               /*
-                * Otherwise we will clear the whole queue by copying
-                * the empty outq at the end of this function, but
-                * first clear the current entries in the queue.
-                */
-               for (i = 0; i < q->nr; i++)
-                       diff_free_filepair(q->queue[i]);
-       } else {
-               /* Showing only the filepairs that has the needle */
-               for (i = 0; i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       if (pickaxe_match(p, o, regexp, kws, fn))
-                               diff_q(&outq, p);
-                       else
-                               diff_free_filepair(p);
-               }
-       }
-
-       free(q->queue);
-       *q = outq;
-}
-
 struct diffgrep_cb {
        regex_t *regexp;
        int hit;
@@ -62,7 +23,6 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
 {
        struct diffgrep_cb *data = priv;
        regmatch_t regmatch;
-       int hold;
 
        if (line[0] != '+' && line[0] != '-')
                return;
@@ -72,11 +32,8 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
                 * caller early.
                 */
                return;
-       /* Yuck -- line ought to be "const char *"! */
-       hold = line[len];
-       line[len] = '\0';
-       data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
-       line[len] = hold;
+       data->hit = !regexec_buf(data->regexp, line + 1, len - 1, 1,
+                                &regmatch, 0);
 }
 
 static int diff_grep(mmfile_t *one, mmfile_t *two,
@@ -89,9 +46,11 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
        xdemitconf_t xecfg;
 
        if (!one)
-               return !regexec(regexp, two->ptr, 1, &regmatch, 0);
+               return !regexec_buf(regexp, two->ptr, two->size,
+                                   1, &regmatch, 0);
        if (!two)
-               return !regexec(regexp, one->ptr, 1, &regmatch, 0);
+               return !regexec_buf(regexp, one->ptr, one->size,
+                                   1, &regmatch, 0);
 
        /*
         * We have both sides; need to run textual diff and see if
@@ -103,36 +62,13 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
        ecbdata.hit = 0;
        xecfg.ctxlen = o->context;
        xecfg.interhunkctxlen = o->interhunkcontext;
-       xdi_diff_outf(one, two, diffgrep_consume, &ecbdata,
-                     &xpp, &xecfg);
+       if (xdi_diff_outf(one, two, discard_hunk_line, diffgrep_consume,
+                         &ecbdata, &xpp, &xecfg))
+               return 0;
        return ecbdata.hit;
 }
 
-static void diffcore_pickaxe_grep(struct diff_options *o)
-{
-       int err;
-       regex_t regex;
-       int cflags = REG_EXTENDED | REG_NEWLINE;
-
-       if (DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE))
-               cflags |= REG_ICASE;
-
-       err = regcomp(&regex, o->pickaxe, cflags);
-       if (err) {
-               char errbuf[1024];
-               regerror(err, &regex, errbuf, 1024);
-               regfree(&regex);
-               die("invalid log-grep regex: %s", errbuf);
-       }
-
-       pickaxe(&diff_queued_diff, o, &regex, NULL, diff_grep);
-
-       regfree(&regex);
-       return;
-}
-
-static unsigned int contains(mmfile_t *mf, struct diff_options *o,
-                            regex_t *regexp, kwset_t kws)
+static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
 {
        unsigned int cnt;
        unsigned long sz;
@@ -146,12 +82,15 @@ static unsigned int contains(mmfile_t *mf, struct diff_options *o,
                regmatch_t regmatch;
                int flags = 0;
 
-               assert(data[sz] == '\0');
-               while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
+               while (sz && *data &&
+                      !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
                        flags |= REG_NOTBOL;
                        data += regmatch.rm_eo;
-                       if (*data && regmatch.rm_so == regmatch.rm_eo)
+                       sz -= regmatch.rm_eo;
+                       if (sz && *data && regmatch.rm_so == regmatch.rm_eo) {
                                data++;
+                               sz--;
+                       }
                        cnt++;
                }
 
@@ -159,13 +98,10 @@ static unsigned int contains(mmfile_t *mf, struct diff_options *o,
                while (sz) {
                        struct kwsmatch kwsm;
                        size_t offset = kwsexec(kws, data, sz, &kwsm);
-                       const char *found;
                        if (offset == -1)
                                break;
-                       else
-                               found = data + offset;
-                       sz -= found - data + kwsm.size[0];
-                       data = found + kwsm.size[0];
+                       sz -= offset + kwsm.size[0];
+                       data += offset + kwsm.size[0];
                        cnt++;
                }
        }
@@ -176,11 +112,9 @@ static int has_changes(mmfile_t *one, mmfile_t *two,
                       struct diff_options *o,
                       regex_t *regexp, kwset_t kws)
 {
-       if (!one)
-               return contains(two, o, regexp, kws) != 0;
-       if (!two)
-               return contains(one, o, regexp, kws) != 0;
-       return contains(one, o, regexp, kws) != contains(two, o, regexp, kws);
+       unsigned int one_contains = one ? contains(one, regexp, kws) : 0;
+       unsigned int two_contains = two ? contains(two, regexp, kws) : 0;
+       return one_contains != two_contains;
 }
 
 static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
@@ -191,16 +125,23 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
        mmfile_t mf1, mf2;
        int ret;
 
-       if (!o->pickaxe[0])
-               return 0;
-
        /* ignore unmerged */
        if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))
                return 0;
 
-       if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
-               textconv_one = get_textconv(p->one);
-               textconv_two = get_textconv(p->two);
+       if (o->objfind) {
+               return  (DIFF_FILE_VALID(p->one) &&
+                        oidset_contains(o->objfind, &p->one->oid)) ||
+                       (DIFF_FILE_VALID(p->two) &&
+                        oidset_contains(o->objfind, &p->two->oid));
+       }
+
+       if (!o->pickaxe[0])
+               return 0;
+
+       if (o->flags.allow_textconv) {
+               textconv_one = get_textconv(o->repo, p->one);
+               textconv_two = get_textconv(o->repo, p->two);
        }
 
        /*
@@ -213,8 +154,14 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
        if (textconv_one == textconv_two && diff_unmodified_pair(p))
                return 0;
 
-       mf1.size = fill_textconv(textconv_one, p->one, &mf1.ptr);
-       mf2.size = fill_textconv(textconv_two, p->two, &mf2.ptr);
+       if ((o->pickaxe_opts & DIFF_PICKAXE_KIND_G) &&
+           !o->flags.text &&
+           ((!textconv_one && diff_filespec_is_binary(o->repo, p->one)) ||
+            (!textconv_two && diff_filespec_is_binary(o->repo, p->two))))
+               return 0;
+
+       mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr);
+       mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr);
 
        ret = fn(DIFF_FILE_VALID(p->one) ? &mf1 : NULL,
                 DIFF_FILE_VALID(p->two) ? &mf2 : NULL,
@@ -230,46 +177,92 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
        return ret;
 }
 
-static void diffcore_pickaxe_count(struct diff_options *o)
+static void pickaxe(struct diff_queue_struct *q, struct diff_options *o,
+                   regex_t *regexp, kwset_t kws, pickaxe_fn fn)
+{
+       int i;
+       struct diff_queue_struct outq;
+
+       DIFF_QUEUE_CLEAR(&outq);
+
+       if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
+               /* Showing the whole changeset if needle exists */
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (pickaxe_match(p, o, regexp, kws, fn))
+                               return; /* do not munge the queue */
+               }
+
+               /*
+                * Otherwise we will clear the whole queue by copying
+                * the empty outq at the end of this function, but
+                * first clear the current entries in the queue.
+                */
+               for (i = 0; i < q->nr; i++)
+                       diff_free_filepair(q->queue[i]);
+       } else {
+               /* Showing only the filepairs that has the needle */
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (pickaxe_match(p, o, regexp, kws, fn))
+                               diff_q(&outq, p);
+                       else
+                               diff_free_filepair(p);
+               }
+       }
+
+       free(q->queue);
+       *q = outq;
+}
+
+static void regcomp_or_die(regex_t *regex, const char *needle, int cflags)
+{
+       int err = regcomp(regex, needle, cflags);
+       if (err) {
+               /* The POSIX.2 people are surely sick */
+               char errbuf[1024];
+               regerror(err, regex, errbuf, 1024);
+               die("invalid regex: %s", errbuf);
+       }
+}
+
+void diffcore_pickaxe(struct diff_options *o)
 {
        const char *needle = o->pickaxe;
        int opts = o->pickaxe_opts;
-       unsigned long len = strlen(needle);
        regex_t regex, *regexp = NULL;
        kwset_t kws = NULL;
 
-       if (opts & DIFF_PICKAXE_REGEX) {
-               int err;
-               err = regcomp(&regex, needle, REG_EXTENDED | REG_NEWLINE);
-               if (err) {
-                       /* The POSIX.2 people are surely sick */
-                       char errbuf[1024];
-                       regerror(err, &regex, errbuf, 1024);
-                       regfree(&regex);
-                       die("invalid pickaxe regex: %s", errbuf);
-               }
+       if (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {
+               int cflags = REG_EXTENDED | REG_NEWLINE;
+               if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE)
+                       cflags |= REG_ICASE;
+               regcomp_or_die(&regex, needle, cflags);
                regexp = &regex;
-       } else {
-               kws = kwsalloc(DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE)
-                              ? tolower_trans_tbl : NULL);
-               kwsincr(kws, needle, len);
-               kwsprep(kws);
+       } else if (opts & DIFF_PICKAXE_KIND_S) {
+               if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&
+                   has_non_ascii(needle)) {
+                       struct strbuf sb = STRBUF_INIT;
+                       int cflags = REG_NEWLINE | REG_ICASE;
+
+                       basic_regex_quote_buf(&sb, needle);
+                       regcomp_or_die(&regex, sb.buf, cflags);
+                       strbuf_release(&sb);
+                       regexp = &regex;
+               } else {
+                       kws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE
+                                      ? tolower_trans_tbl : NULL);
+                       kwsincr(kws, needle, strlen(needle));
+                       kwsprep(kws);
+               }
        }
 
-       pickaxe(&diff_queued_diff, o, regexp, kws, has_changes);
+       pickaxe(&diff_queued_diff, o, regexp, kws,
+               (opts & DIFF_PICKAXE_KIND_G) ? diff_grep : has_changes);
 
-       if (opts & DIFF_PICKAXE_REGEX)
-               regfree(&regex);
-       else
+       if (regexp)
+               regfree(regexp);
+       if (kws)
                kwsfree(kws);
        return;
 }
-
-void diffcore_pickaxe(struct diff_options *o)
-{
-       /* Might want to warn when both S and G are on; I don't care... */
-       if (o->pickaxe_opts & DIFF_PICKAXE_KIND_G)
-               diffcore_pickaxe_grep(o);
-       else
-               diffcore_pickaxe_count(o);
-}