diffcore-pickaxe.con commit mergesort: rename it to llist_mergesort() (7365c95)
   1/*
   2 * Copyright (C) 2005 Junio C Hamano
   3 * Copyright (C) 2010 Google Inc.
   4 */
   5#include "cache.h"
   6#include "diff.h"
   7#include "diffcore.h"
   8#include "xdiff-interface.h"
   9#include "kwset.h"
  10
  11typedef int (*pickaxe_fn)(struct diff_filepair *p, struct diff_options *o, regex_t *regexp, kwset_t kws);
  12
  13static void pickaxe(struct diff_queue_struct *q, struct diff_options *o,
  14                    regex_t *regexp, kwset_t kws, pickaxe_fn fn)
  15{
  16        int i;
  17        struct diff_queue_struct outq;
  18
  19        DIFF_QUEUE_CLEAR(&outq);
  20
  21        if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
  22                /* Showing the whole changeset if needle exists */
  23                for (i = 0; i < q->nr; i++) {
  24                        struct diff_filepair *p = q->queue[i];
  25                        if (fn(p, o, regexp, kws))
  26                                return; /* do not munge the queue */
  27                }
  28
  29                /*
  30                 * Otherwise we will clear the whole queue by copying
  31                 * the empty outq at the end of this function, but
  32                 * first clear the current entries in the queue.
  33                 */
  34                for (i = 0; i < q->nr; i++)
  35                        diff_free_filepair(q->queue[i]);
  36        } else {
  37                /* Showing only the filepairs that has the needle */
  38                for (i = 0; i < q->nr; i++) {
  39                        struct diff_filepair *p = q->queue[i];
  40                        if (fn(p, o, regexp, kws))
  41                                diff_q(&outq, p);
  42                        else
  43                                diff_free_filepair(p);
  44                }
  45        }
  46
  47        free(q->queue);
  48        *q = outq;
  49}
  50
  51struct diffgrep_cb {
  52        regex_t *regexp;
  53        int hit;
  54};
  55
  56static void diffgrep_consume(void *priv, char *line, unsigned long len)
  57{
  58        struct diffgrep_cb *data = priv;
  59        regmatch_t regmatch;
  60        int hold;
  61
  62        if (line[0] != '+' && line[0] != '-')
  63                return;
  64        if (data->hit)
  65                /*
  66                 * NEEDSWORK: we should have a way to terminate the
  67                 * caller early.
  68                 */
  69                return;
  70        /* Yuck -- line ought to be "const char *"! */
  71        hold = line[len];
  72        line[len] = '\0';
  73        data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
  74        line[len] = hold;
  75}
  76
  77static void fill_one(struct diff_filespec *one,
  78                     mmfile_t *mf, struct userdiff_driver **textconv)
  79{
  80        if (DIFF_FILE_VALID(one)) {
  81                *textconv = get_textconv(one);
  82                mf->size = fill_textconv(*textconv, one, &mf->ptr);
  83        } else {
  84                memset(mf, 0, sizeof(*mf));
  85        }
  86}
  87
  88static int diff_grep(struct diff_filepair *p, struct diff_options *o,
  89                     regex_t *regexp, kwset_t kws)
  90{
  91        regmatch_t regmatch;
  92        struct userdiff_driver *textconv_one = NULL;
  93        struct userdiff_driver *textconv_two = NULL;
  94        mmfile_t mf1, mf2;
  95        int hit;
  96
  97        if (diff_unmodified_pair(p))
  98                return 0;
  99
 100        fill_one(p->one, &mf1, &textconv_one);
 101        fill_one(p->two, &mf2, &textconv_two);
 102
 103        if (!mf1.ptr) {
 104                if (!mf2.ptr)
 105                        return 0; /* ignore unmerged */
 106                /* created "two" -- does it have what we are looking for? */
 107                hit = !regexec(regexp, p->two->data, 1, &regmatch, 0);
 108        } else if (!mf2.ptr) {
 109                /* removed "one" -- did it have what we are looking for? */
 110                hit = !regexec(regexp, p->one->data, 1, &regmatch, 0);
 111        } else {
 112                /*
 113                 * We have both sides; need to run textual diff and see if
 114                 * the pattern appears on added/deleted lines.
 115                 */
 116                struct diffgrep_cb ecbdata;
 117                xpparam_t xpp;
 118                xdemitconf_t xecfg;
 119
 120                memset(&xpp, 0, sizeof(xpp));
 121                memset(&xecfg, 0, sizeof(xecfg));
 122                ecbdata.regexp = regexp;
 123                ecbdata.hit = 0;
 124                xecfg.ctxlen = o->context;
 125                xecfg.interhunkctxlen = o->interhunkcontext;
 126                xdi_diff_outf(&mf1, &mf2, diffgrep_consume, &ecbdata,
 127                              &xpp, &xecfg);
 128                hit = ecbdata.hit;
 129        }
 130        if (textconv_one)
 131                free(mf1.ptr);
 132        if (textconv_two)
 133                free(mf2.ptr);
 134        return hit;
 135}
 136
 137static void diffcore_pickaxe_grep(struct diff_options *o)
 138{
 139        int err;
 140        regex_t regex;
 141
 142        err = regcomp(&regex, o->pickaxe, REG_EXTENDED | REG_NEWLINE);
 143        if (err) {
 144                char errbuf[1024];
 145                regerror(err, &regex, errbuf, 1024);
 146                regfree(&regex);
 147                die("invalid log-grep regex: %s", errbuf);
 148        }
 149
 150        pickaxe(&diff_queued_diff, o, &regex, NULL, diff_grep);
 151
 152        regfree(&regex);
 153        return;
 154}
 155
 156static unsigned int contains(struct diff_filespec *one, struct diff_options *o,
 157                             regex_t *regexp, kwset_t kws)
 158{
 159        unsigned int cnt;
 160        unsigned long sz;
 161        const char *data;
 162        if (!o->pickaxe[0])
 163                return 0;
 164        if (diff_populate_filespec(one, 0))
 165                return 0;
 166
 167        sz = one->size;
 168        data = one->data;
 169        cnt = 0;
 170
 171        if (regexp) {
 172                regmatch_t regmatch;
 173                int flags = 0;
 174
 175                assert(data[sz] == '\0');
 176                while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
 177                        flags |= REG_NOTBOL;
 178                        data += regmatch.rm_eo;
 179                        if (*data && regmatch.rm_so == regmatch.rm_eo)
 180                                data++;
 181                        cnt++;
 182                }
 183
 184        } else { /* Classic exact string match */
 185                while (sz) {
 186                        struct kwsmatch kwsm;
 187                        size_t offset = kwsexec(kws, data, sz, &kwsm);
 188                        const char *found;
 189                        if (offset == -1)
 190                                break;
 191                        else
 192                                found = data + offset;
 193                        sz -= found - data + kwsm.size[0];
 194                        data = found + kwsm.size[0];
 195                        cnt++;
 196                }
 197        }
 198        diff_free_filespec_data(one);
 199        return cnt;
 200}
 201
 202static int has_changes(struct diff_filepair *p, struct diff_options *o,
 203                       regex_t *regexp, kwset_t kws)
 204{
 205        if (!DIFF_FILE_VALID(p->one)) {
 206                if (!DIFF_FILE_VALID(p->two))
 207                        return 0; /* ignore unmerged */
 208                /* created */
 209                return contains(p->two, o, regexp, kws) != 0;
 210        }
 211        if (!DIFF_FILE_VALID(p->two))
 212                return contains(p->one, o, regexp, kws) != 0;
 213        if (!diff_unmodified_pair(p)) {
 214                return contains(p->one, o, regexp, kws) !=
 215                       contains(p->two, o, regexp, kws);
 216        }
 217        return 0;
 218}
 219
 220static void diffcore_pickaxe_count(struct diff_options *o)
 221{
 222        const char *needle = o->pickaxe;
 223        int opts = o->pickaxe_opts;
 224        unsigned long len = strlen(needle);
 225        regex_t regex, *regexp = NULL;
 226        kwset_t kws = NULL;
 227
 228        if (opts & DIFF_PICKAXE_REGEX) {
 229                int err;
 230                err = regcomp(&regex, needle, REG_EXTENDED | REG_NEWLINE);
 231                if (err) {
 232                        /* The POSIX.2 people are surely sick */
 233                        char errbuf[1024];
 234                        regerror(err, &regex, errbuf, 1024);
 235                        regfree(&regex);
 236                        die("invalid pickaxe regex: %s", errbuf);
 237                }
 238                regexp = &regex;
 239        } else {
 240                kws = kwsalloc(NULL);
 241                kwsincr(kws, needle, len);
 242                kwsprep(kws);
 243        }
 244
 245        pickaxe(&diff_queued_diff, o, regexp, kws, has_changes);
 246
 247        if (opts & DIFF_PICKAXE_REGEX)
 248                regfree(&regex);
 249        else
 250                kwsfree(kws);
 251        return;
 252}
 253
 254void diffcore_pickaxe(struct diff_options *o)
 255{
 256        /* Might want to warn when both S and G are on; I don't care... */
 257        if (o->pickaxe_opts & DIFF_PICKAXE_KIND_G)
 258                diffcore_pickaxe_grep(o);
 259        else
 260                diffcore_pickaxe_count(o);
 261}