Code

Merge branch 'ag/blame-encoding'
[git.git] / diff.c
diff --git a/diff.c b/diff.c
index d50355e842b025fe98808a3800d9b0863f5c1a91..e368fef14fdccf63fc3525349841ae93aea93046 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,8 +293,19 @@ 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;
+
+       diff_filespec_load_driver(one);
+       if (one->driver->textconv) {
+               size_t size;
+               mf->ptr = run_textconv(one->driver->textconv, one, &size);
+               if (!mf->ptr)
+                       return -1;
+               mf->size = size;
+       }
+       else {
+               mf->ptr = one->data;
+               mf->size = one->size;
+       }
        return 0;
 }
 
@@ -1268,46 +1282,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)
+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)
@@ -1559,6 +1564,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 +3387,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);
+}