Merge branch 'lt/diffgen' into next
[git.git] / diff.c
diff --git a/diff.c b/diff.c
index c73a40b..4440465 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -8,6 +8,7 @@
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "xdiff/xdiff.h"
 
 static const char *diff_opts = "-pu";
 
@@ -178,6 +179,70 @@ static void emit_rewrite_diff(const char *name_a,
                copy_file('+', temp[1].name);
 }
 
+static int fill_mmfile(mmfile_t *mf, const char *file)
+{
+       int fd = open(file, O_RDONLY);
+       struct stat st;
+       char *buf;
+       unsigned long size;
+
+       mf->ptr = NULL;
+       mf->size = 0;
+       if (fd < 0)
+               return 0;
+       fstat(fd, &st);
+       size = st.st_size;
+       buf = xmalloc(size);
+       mf->ptr = buf;
+       mf->size = size;
+       while (size) {
+               int retval = read(fd, buf, size);
+               if (retval < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       break;
+               }
+               if (!retval)
+                       break;
+               buf += retval;
+               size -= retval;
+       }
+       mf->size -= size;
+       close(fd);
+       return 0;
+}
+
+struct emit_callback {
+       const char **label_path;
+};
+
+static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+{
+       int i;
+       struct emit_callback *ecbdata = priv;
+
+       if (ecbdata->label_path[0]) {
+               printf("--- %s\n", ecbdata->label_path[0]);
+               printf("+++ %s\n", ecbdata->label_path[1]);
+               ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
+       }
+       for (i = 0; i < nbuf; i++)
+               if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
+                       return -1;
+       return 0;
+}
+
+#define FIRST_FEW_BYTES 8000
+static int mmfile_is_binary(mmfile_t *mf)
+{
+       long sz = mf->size;
+       if (FIRST_FEW_BYTES < sz)
+               sz = FIRST_FEW_BYTES;
+       if (memchr(mf->ptr, 0, sz))
+               return 1;
+       return 0;
+}
+
 static const char *builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_tempfile *temp,
@@ -186,6 +251,7 @@ static const char *builtin_diff(const char *name_a,
                         const char **args)
 {
        int i, next_at, cmd_size;
+       mmfile_t mf1, mf2;
        const char *const diff_cmd = "diff -L%s -L%s";
        const char *const diff_arg  = "-- %s %s||:"; /* "||:" is to return 0 */
        const char *input_name_sq[2];
@@ -255,12 +321,44 @@ static const char *builtin_diff(const char *name_a,
                }
        }
 
-       /* This is disgusting */
-       *args++ = "sh";
-       *args++ = "-c";
-       *args++ = cmd;
-       *args = NULL;
-       return "/bin/sh";
+       /* Un-quote the paths */
+       if (label_path[0][0] != '/')
+               label_path[0] = quote_two("a/", name_a);
+       if (label_path[1][0] != '/')
+               label_path[1] = quote_two("b/", name_b);
+
+       if (fill_mmfile(&mf1, temp[0].name) < 0 ||
+           fill_mmfile(&mf2, temp[1].name) < 0)
+               die("unable to read files to diff");
+
+       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+               printf("Binary files %s and %s differ\n",
+                      label_path[0], label_path[1]);
+       else {
+               /* Crazy xdl interfaces.. */
+               const char *diffopts = getenv("GIT_DIFF_OPTS");
+               xpparam_t xpp;
+               xdemitconf_t xecfg;
+               xdemitcb_t ecb;
+               struct emit_callback ecbdata;
+
+               ecbdata.label_path = label_path;
+               xpp.flags = XDF_NEED_MINIMAL;
+               xecfg.ctxlen = 3;
+               if (!diffopts)
+                       ;
+               else if (!strncmp(diffopts, "--unified=", 10))
+                       xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
+               else if (!strncmp(diffopts, "-u", 2))
+                       xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
+               ecb.outf = fn_out;
+               ecb.priv = &ecbdata;
+               xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+       }
+
+       free(mf1.ptr);
+       free(mf2.ptr);
+       return NULL;
 }
 
 struct diff_filespec *alloc_filespec(const char *path)