diff: use tempfile module
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 06bdfb8ae5acab2c6090ca4154a26fca6da8b74c..528d25ca9c3ec8df316bf2cd331151d7a71d5dd3 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2,6 +2,7 @@
  * Copyright (C) 2005 Junio C Hamano
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
@@ -248,8 +249,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                        return 0;
                if (!value)
                        return config_error_nonbool(var);
-               color_parse(value, var, diff_colors[slot]);
-               return 0;
+               return color_parse(value, diff_colors[slot]);
        }
 
        /* like GNU diff's --suppress-blank-empty option  */
@@ -309,11 +309,26 @@ static const char *external_diff(void)
        return external_diff_cmd;
 }
 
+/*
+ * Keep track of files used for diffing. Sometimes such an entry
+ * refers to a temporary file, sometimes to an existing file, and
+ * sometimes to "/dev/null".
+ */
 static struct diff_tempfile {
-       const char *name; /* filename external diff should read from */
+       /*
+        * filename external diff should read from, or NULL if this
+        * entry is currently not in use:
+        */
+       const char *name;
+
        char hex[41];
        char mode[10];
-       char tmp_path[PATH_MAX];
+
+       /*
+        * If this diff_tempfile instance refers to a temporary file,
+        * this tempfile object is used to manage its lifetime.
+        */
+       struct tempfile tempfile;
 } diff_temp[2];
 
 typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
@@ -376,7 +391,7 @@ static unsigned long diff_filespec_size(struct diff_filespec *one)
 {
        if (!DIFF_FILE_VALID(one))
                return 0;
-       diff_populate_filespec(one, 1);
+       diff_populate_filespec(one, CHECK_SIZE_ONLY);
        return one->size;
 }
 
@@ -525,9 +540,9 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
        ep += 2; /* skip over @@ */
 
        /* The hunk header in fraginfo color */
-       strbuf_add(&msgbuf, frag, strlen(frag));
+       strbuf_addstr(&msgbuf, frag);
        strbuf_add(&msgbuf, line, ep - line);
-       strbuf_add(&msgbuf, reset, strlen(reset));
+       strbuf_addstr(&msgbuf, reset);
 
        /*
         * trailing "\r\n"
@@ -541,15 +556,15 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
                if (*ep != ' ' && *ep != '\t')
                        break;
        if (ep != cp) {
-               strbuf_add(&msgbuf, plain, strlen(plain));
+               strbuf_addstr(&msgbuf, plain);
                strbuf_add(&msgbuf, cp, ep - cp);
-               strbuf_add(&msgbuf, reset, strlen(reset));
+               strbuf_addstr(&msgbuf, reset);
        }
 
        if (ep < line + len) {
-               strbuf_add(&msgbuf, func, strlen(func));
+               strbuf_addstr(&msgbuf, func);
                strbuf_add(&msgbuf, ep, line + len - ep);
-               strbuf_add(&msgbuf, reset, strlen(reset));
+               strbuf_addstr(&msgbuf, reset);
        }
 
        strbuf_add(&msgbuf, line + len, org_len - len);
@@ -565,25 +580,16 @@ static struct diff_tempfile *claim_diff_tempfile(void) {
        die("BUG: diff is failing to clean up its tempfiles");
 }
 
-static int remove_tempfile_installed;
-
 static void remove_tempfile(void)
 {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
-               if (diff_temp[i].name == diff_temp[i].tmp_path)
-                       unlink_or_warn(diff_temp[i].name);
+               if (is_tempfile_active(&diff_temp[i].tempfile))
+                       delete_tempfile(&diff_temp[i].tempfile);
                diff_temp[i].name = NULL;
        }
 }
 
-static void remove_tempfile_on_signal(int signo)
-{
-       remove_tempfile();
-       sigchain_pop(signo);
-       raise(signo);
-}
-
 static void print_line_count(FILE *file, int count)
 {
        switch (count) {
@@ -1910,11 +1916,11 @@ static void show_dirstat(struct diff_options *options)
                        diff_free_filespec_data(p->one);
                        diff_free_filespec_data(p->two);
                } else if (DIFF_FILE_VALID(p->one)) {
-                       diff_populate_filespec(p->one, 1);
+                       diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
                        copied = added = 0;
                        diff_free_filespec_data(p->one);
                } else if (DIFF_FILE_VALID(p->two)) {
-                       diff_populate_filespec(p->two, 1);
+                       diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
                        copied = 0;
                        added = p->two->size;
                        diff_free_filespec_data(p->two);
@@ -2094,7 +2100,6 @@ static unsigned char *deflate_it(char *data,
        unsigned char *deflated;
        git_zstream stream;
 
-       memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        bound = git_deflate_bound(&stream, size);
        deflated = xmalloc(bound);
@@ -2188,8 +2193,8 @@ int diff_filespec_is_binary(struct diff_filespec *one)
                        one->is_binary = one->driver->binary;
                else {
                        if (!one->data && DIFF_FILE_VALID(one))
-                               diff_populate_filespec(one, 0);
-                       if (one->data)
+                               diff_populate_filespec(one, CHECK_BINARY);
+                       if (one->is_binary == -1 && one->data)
                                one->is_binary = buffer_is_binary(one->data,
                                                one->size);
                        if (one->is_binary == -1)
@@ -2324,6 +2329,19 @@ static void builtin_diff(const char *name_a,
        } else if (!DIFF_OPT_TST(o, TEXT) &&
            ( (!textconv_one && diff_filespec_is_binary(one)) ||
              (!textconv_two && diff_filespec_is_binary(two)) )) {
+               if (!one->data && !two->data &&
+                   S_ISREG(one->mode) && S_ISREG(two->mode) &&
+                   !DIFF_OPT_TST(o, BINARY)) {
+                       if (!hashcmp(one->sha1, two->sha1)) {
+                               if (must_show_header)
+                                       fprintf(o->file, "%s", header.buf);
+                               goto free_ab_and_return;
+                       }
+                       fprintf(o->file, "%s", header.buf);
+                       fprintf(o->file, "%sBinary files %s and %s differ\n",
+                               line_prefix, lbl[0], lbl[1]);
+                       goto free_ab_and_return;
+               }
                if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                        die("unable to read files to diff");
                /* Quite common confusing case */
@@ -2668,8 +2686,9 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
  * grab the data for the blob (or file) for our own in-core comparison.
  * diff_filespec has data and size fields for this purpose.
  */
-int diff_populate_filespec(struct diff_filespec *s, int size_only)
+int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
 {
+       int size_only = flags & CHECK_SIZE_ONLY;
        int err = 0;
        /*
         * demote FAIL to WARN to allow inspecting the situation
@@ -2724,6 +2743,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                }
                if (size_only)
                        return 0;
+               if ((flags & CHECK_BINARY) &&
+                   s->size > big_file_threshold && s->is_binary == -1) {
+                       s->is_binary = 1;
+                       return 0;
+               }
                fd = open(s->path, O_RDONLY);
                if (fd < 0)
                        goto err_empty;
@@ -2745,16 +2769,21 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
        }
        else {
                enum object_type type;
-               if (size_only) {
+               if (size_only || (flags & CHECK_BINARY)) {
                        type = sha1_object_info(s->sha1, &s->size);
                        if (type < 0)
                                die("unable to read %s", sha1_to_hex(s->sha1));
-               } else {
-                       s->data = read_sha1_file(s->sha1, &type, &s->size);
-                       if (!s->data)
-                               die("unable to read %s", sha1_to_hex(s->sha1));
-                       s->should_free = 1;
+                       if (size_only)
+                               return 0;
+                       if (s->size > big_file_threshold && s->is_binary == -1) {
+                               s->is_binary = 1;
+                               return 0;
+                       }
                }
+               s->data = read_sha1_file(s->sha1, &type, &s->size);
+               if (!s->data)
+                       die("unable to read %s", sha1_to_hex(s->sha1));
+               s->should_free = 1;
        }
        return 0;
 }
@@ -2795,8 +2824,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        strbuf_addstr(&template, "XXXXXX_");
        strbuf_addstr(&template, base);
 
-       fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf,
-                       strlen(base) + 1);
+       fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
        if (fd < 0)
                die_errno("unable to create temp-file");
        if (convert_to_working_tree(path,
@@ -2806,8 +2834,8 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        }
        if (write_in_full(fd, blob, size) != size)
                die_errno("unable to write temp-file");
-       close(fd);
-       temp->name = temp->tmp_path;
+       close_tempfile(&temp->tempfile);
+       temp->name = get_tempfile_path(&temp->tempfile);
        strcpy(temp->hex, sha1_to_hex(sha1));
        temp->hex[40] = 0;
        sprintf(temp->mode, "%06o", mode);
@@ -2832,12 +2860,6 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                return temp;
        }
 
-       if (!remove_tempfile_installed) {
-               atexit(remove_tempfile);
-               sigchain_push_common(remove_tempfile_on_signal);
-               remove_tempfile_installed = 1;
-       }
-
        if (!S_ISGITLINK(one->mode) &&
            (!one->sha1_valid ||
             reuse_worktree_file(name, one->sha1, 1))) {
@@ -4518,7 +4540,7 @@ void diff_flush(struct diff_options *options)
                        show_stats(&diffstat, options);
                if (output_format & DIFF_FORMAT_SHORTSTAT)
                        show_shortstats(&diffstat, options);
-               if (output_format & DIFF_FORMAT_DIRSTAT)
+               if (output_format & DIFF_FORMAT_DIRSTAT && dirstat_by_line)
                        show_dirstat_by_line(&diffstat, options);
                free_diffstat_info(&diffstat);
                separator++;
@@ -4688,8 +4710,8 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
            !DIFF_FILE_VALID(p->two) ||
            (p->one->sha1_valid && p->two->sha1_valid) ||
            (p->one->mode != p->two->mode) ||
-           diff_populate_filespec(p->one, 1) ||
-           diff_populate_filespec(p->two, 1) ||
+           diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
+           diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
            (p->one->size != p->two->size) ||
            !diff_filespec_is_identical(p->one, p->two)) /* (2) */
                p->skip_stat_unmatch_result = 1;
@@ -4931,7 +4953,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
        struct diff_tempfile *temp;
        const char *argv[3];
        const char **arg = argv;
-       struct child_process child;
+       struct child_process child = CHILD_PROCESS_INIT;
        struct strbuf buf = STRBUF_INIT;
        int err = 0;
 
@@ -4940,7 +4962,6 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
        *arg++ = temp->name;
        *arg = NULL;
 
-       memset(&child, 0, sizeof(child));
        child.use_shell = 1;
        child.argv = argv;
        child.out = -1;