refactor userdiff textconv code
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index d50355e842b025fe98808a3800d9b0863f5c1a91..6f01595ece4e9934bc9167bdbb61fad362bb7663 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -38,6 +38,9 @@ static char diff_colors[][COLOR_MAXLEN] = {
        "\033[41m",     /* WHITESPACE (red background) */
 };
 
+static void diff_filespec_load_driver(struct diff_filespec *one);
+static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+
 static int parse_diff_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -290,6 +293,7 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
        }
        else if (diff_populate_filespec(one, 0))
                return -1;
+
        mf->ptr = one->data;
        mf->size = one->size;
        return 0;
@@ -1268,46 +1272,37 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
        emit_binary_diff_body(file, two, one);
 }
 
-static void diff_filespec_check_attr(struct diff_filespec *one)
+static void diff_filespec_load_driver(struct diff_filespec *one)
 {
-       struct userdiff_driver *drv;
-       int check_from_data = 0;
-
-       if (one->checked_attr)
-               return;
-
-       drv = userdiff_find_by_path(one->path);
-       one->is_binary = 0;
-
-       /* binaryness */
-       if (drv == USERDIFF_ATTR_TRUE)
-               ;
-       else if (drv == USERDIFF_ATTR_FALSE)
-               one->is_binary = 1;
-       else
-               check_from_data = 1;
-
-       if (check_from_data) {
-               if (!one->data && DIFF_FILE_VALID(one))
-                       diff_populate_filespec(one, 0);
-
-               if (one->data)
-                       one->is_binary = buffer_is_binary(one->data, one->size);
-       }
+       if (!one->driver)
+               one->driver = userdiff_find_by_path(one->path);
+       if (!one->driver)
+               one->driver = userdiff_find_by_name("default");
 }
 
 int diff_filespec_is_binary(struct diff_filespec *one)
 {
-       diff_filespec_check_attr(one);
+       if (one->is_binary == -1) {
+               diff_filespec_load_driver(one);
+               if (one->driver->binary != -1)
+                       one->is_binary = one->driver->binary;
+               else {
+                       if (!one->data && DIFF_FILE_VALID(one))
+                               diff_populate_filespec(one, 0);
+                       if (one->data)
+                               one->is_binary = buffer_is_binary(one->data,
+                                               one->size);
+                       if (one->is_binary == -1)
+                               one->is_binary = 0;
+               }
+       }
        return one->is_binary;
 }
 
 static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
 {
-       struct userdiff_driver *drv = userdiff_find_by_path(one->path);
-       if (!drv)
-               drv = userdiff_find_by_name("default");
-       return drv && drv->funcname.pattern ? &drv->funcname : NULL;
+       diff_filespec_load_driver(one);
+       return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
@@ -1318,6 +1313,14 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
                options->b_prefix = b;
 }
 
+static const char *get_textconv(struct diff_filespec *one)
+{
+       if (!DIFF_FILE_VALID(one))
+               return NULL;
+       diff_filespec_load_driver(one);
+       return one->driver->textconv;
+}
+
 static void builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_filespec *one,
@@ -1332,6 +1335,7 @@ static void builtin_diff(const char *name_a,
        const char *set = diff_get_color_opt(o, DIFF_METAINFO);
        const char *reset = diff_get_color_opt(o, DIFF_RESET);
        const char *a_prefix, *b_prefix;
+       const char *textconv_one, *textconv_two;
 
        diff_set_mnemonic_prefix(o, "a/", "b/");
        if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
@@ -1385,8 +1389,12 @@ static void builtin_diff(const char *name_a,
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
 
+       textconv_one = get_textconv(one);
+       textconv_two = get_textconv(two);
+
        if (!DIFF_OPT_TST(o, TEXT) &&
-           (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) {
+           ( (diff_filespec_is_binary(one) && !textconv_one) ||
+             (diff_filespec_is_binary(two) && !textconv_two) )) {
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1407,6 +1415,21 @@ static void builtin_diff(const char *name_a,
                struct emit_callback ecbdata;
                const struct userdiff_funcname *pe;
 
+               if (textconv_one) {
+                       size_t size;
+                       mf1.ptr = run_textconv(textconv_one, one, &size);
+                       if (!mf1.ptr)
+                               die("unable to read files to diff");
+                       mf1.size = size;
+               }
+               if (textconv_two) {
+                       size_t size;
+                       mf2.ptr = run_textconv(textconv_two, two, &size);
+                       if (!mf2.ptr)
+                               die("unable to read files to diff");
+                       mf2.size = size;
+               }
+
                pe = diff_funcname_pattern(one);
                if (!pe)
                        pe = diff_funcname_pattern(two);
@@ -1438,6 +1461,10 @@ static void builtin_diff(const char *name_a,
                              &xpp, &xecfg, &ecb);
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        free_diff_words_data(&ecbdata);
+               if (textconv_one)
+                       free(mf1.ptr);
+               if (textconv_two)
+                       free(mf2.ptr);
        }
 
  free_ab_and_return:
@@ -1559,6 +1586,7 @@ struct diff_filespec *alloc_filespec(const char *path)
        spec->path = (char *)(spec + 1);
        memcpy(spec->path, path, namelen+1);
        spec->count = 1;
+       spec->is_binary = -1;
        return spec;
 }
 
@@ -3381,3 +3409,34 @@ void diff_unmerge(struct diff_options *options,
        fill_filespec(one, sha1, mode);
        diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
 }
+
+static char *run_textconv(const char *pgm, struct diff_filespec *spec,
+               size_t *outsize)
+{
+       struct diff_tempfile temp;
+       const char *argv[3];
+       const char **arg = argv;
+       struct child_process child;
+       struct strbuf buf = STRBUF_INIT;
+
+       prepare_temp_file(spec->path, &temp, spec);
+       *arg++ = pgm;
+       *arg++ = temp.name;
+       *arg = NULL;
+
+       memset(&child, 0, sizeof(child));
+       child.argv = argv;
+       child.out = -1;
+       if (start_command(&child) != 0 ||
+           strbuf_read(&buf, child.out, 0) < 0 ||
+           finish_command(&child) != 0) {
+               if (temp.name == temp.tmp_path)
+                       unlink(temp.name);
+               error("error running textconv command '%s'", pgm);
+               return NULL;
+       }
+       if (temp.name == temp.tmp_path)
+               unlink(temp.name);
+
+       return strbuf_detach(&buf, outsize);
+}