]> git.proxmox.com Git - libgit2.git/blobdiff - src/patch_generate.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / patch_generate.c
index feac4f67a295c4691e4ad6a38933a6929f0d6390..bc598fea870123b5e771c6cd53e8000918cc6cc6 100644 (file)
@@ -4,17 +4,18 @@
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
  */
-#include "common.h"
+
+#include "patch_generate.h"
+
 #include "git2/blob.h"
 #include "diff.h"
 #include "diff_generate.h"
 #include "diff_file.h"
 #include "diff_driver.h"
-#include "patch_generate.h"
 #include "diff_xdiff.h"
 #include "delta.h"
 #include "zstream.h"
-#include "fileops.h"
+#include "futils.h"
 
 static void diff_output_init(
        git_patch_generated_output *, const git_diff_options *, git_diff_file_cb,
@@ -83,7 +84,7 @@ static int patch_generated_normalize_options(
        const git_diff_options *opts)
 {
        if (opts) {
-               GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
+               GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
                memcpy(out, opts, sizeof(git_diff_options));
        } else {
                git_diff_options default_opts = GIT_DIFF_OPTIONS_INIT;
@@ -98,8 +99,8 @@ static int patch_generated_normalize_options(
                git__strdup(opts->new_prefix) :
                git__strdup(DIFF_NEW_PREFIX_DEFAULT);
 
-       GITERR_CHECK_ALLOC(out->old_prefix);
-       GITERR_CHECK_ALLOC(out->new_prefix);
+       GIT_ERROR_CHECK_ALLOC(out->old_prefix);
+       GIT_ERROR_CHECK_ALLOC(out->new_prefix);
 
        return 0;
 }
@@ -134,11 +135,11 @@ static int patch_generated_alloc_from_diff(
 {
        int error;
        git_patch_generated *patch = git__calloc(1, sizeof(git_patch_generated));
-       GITERR_CHECK_ALLOC(patch);
+       GIT_ERROR_CHECK_ALLOC(patch);
 
        if (!(error = patch_generated_init(patch, diff, delta_index))) {
                patch->flags |= GIT_PATCH_GENERATED_ALLOCATED;
-               GIT_REFCOUNT_INC(patch);
+               GIT_REFCOUNT_INC(&patch->base);
        } else {
                git__free(patch);
                patch = NULL;
@@ -206,35 +207,12 @@ static int patch_generated_load(git_patch_generated *patch, git_patch_generated_
                 ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
                  (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0));
 
-       /* always try to load workdir content first because filtering may
-        * need 2x data size and this minimizes peak memory footprint
-        */
-       if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) {
-               if ((error = git_diff_file_content__load(
-                               &patch->ofile, &patch->base.diff_opts)) < 0 ||
-                       should_skip_binary(patch, patch->ofile.file))
-                       goto cleanup;
-       }
-       if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) {
-               if ((error = git_diff_file_content__load(
-                               &patch->nfile, &patch->base.diff_opts)) < 0 ||
-                       should_skip_binary(patch, patch->nfile.file))
-                       goto cleanup;
-       }
-
-       /* once workdir has been tried, load other data as needed */
-       if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) {
-               if ((error = git_diff_file_content__load(
-                               &patch->ofile, &patch->base.diff_opts)) < 0 ||
-                       should_skip_binary(patch, patch->ofile.file))
-                       goto cleanup;
-       }
-       if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) {
-               if ((error = git_diff_file_content__load(
-                               &patch->nfile, &patch->base.diff_opts)) < 0 ||
-                       should_skip_binary(patch, patch->nfile.file))
-                       goto cleanup;
-       }
+       if ((error = git_diff_file_content__load(
+                       &patch->ofile, &patch->base.diff_opts)) < 0 ||
+           (error = git_diff_file_content__load(
+                       &patch->nfile, &patch->base.diff_opts)) < 0 ||
+               should_skip_binary(patch, patch->nfile.file))
+               goto cleanup;
 
        /* if previously missing an oid, and now that we have it the two sides
         * are the same (and not submodules), update MODIFIED -> UNMODIFIED
@@ -268,7 +246,7 @@ static int patch_generated_invoke_file_callback(
        if (!output->file_cb)
                return 0;
 
-       return giterr_set_after_callback_function(
+       return git_error_set_after_callback_function(
                output->file_cb(patch->base.delta, progress, output->payload),
                "git_patch");
 }
@@ -283,8 +261,8 @@ static int create_binary(
        const char *b_data,
        size_t b_datalen)
 {
-       git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT;
-       size_t delta_data_len;
+       git_str deflate = GIT_STR_INIT, delta = GIT_STR_INIT;
+       size_t delta_data_len = 0;
        int error;
 
        /* The git_delta function accepts unsigned long only */
@@ -324,47 +302,53 @@ static int create_binary(
        if (delta.size && delta.size < deflate.size) {
                *out_type = GIT_DIFF_BINARY_DELTA;
                *out_datalen = delta.size;
-               *out_data = git_buf_detach(&delta);
+               *out_data = git_str_detach(&delta);
                *out_inflatedlen = delta_data_len;
        } else {
                *out_type = GIT_DIFF_BINARY_LITERAL;
                *out_datalen = deflate.size;
-               *out_data = git_buf_detach(&deflate);
+               *out_data = git_str_detach(&deflate);
                *out_inflatedlen = b_datalen;
        }
 
 done:
-       git_buf_free(&deflate);
-       git_buf_free(&delta);
+       git_str_dispose(&deflate);
+       git_str_dispose(&delta);
 
        return error;
 }
 
 static int diff_binary(git_patch_generated_output *output, git_patch_generated *patch)
 {
-       git_diff_binary binary = {{0}};
+       git_diff_binary binary = {0};
        const char *old_data = patch->ofile.map.data;
        const char *new_data = patch->nfile.map.data;
        size_t old_len = patch->ofile.map.len,
                new_len = patch->nfile.map.len;
        int error;
 
-       /* Create the old->new delta (as the "new" side of the patch),
-        * and the new->old delta (as the "old" side)
-        */
-       if ((error = create_binary(&binary.old_file.type,
-                       (char **)&binary.old_file.data,
-                       &binary.old_file.datalen,
-                       &binary.old_file.inflatedlen,
-                       new_data, new_len, old_data, old_len)) < 0 ||
-               (error = create_binary(&binary.new_file.type,
-                       (char **)&binary.new_file.data,
-                       &binary.new_file.datalen,
-                       &binary.new_file.inflatedlen,
-                       old_data, old_len, new_data, new_len)) < 0)
-               return error;
+       /* Only load contents if the user actually wants to diff
+        * binary files. */
+       if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) {
+               binary.contains_data = 1;
+
+               /* Create the old->new delta (as the "new" side of the patch),
+                * and the new->old delta (as the "old" side)
+                */
+               if ((error = create_binary(&binary.old_file.type,
+                               (char **)&binary.old_file.data,
+                               &binary.old_file.datalen,
+                               &binary.old_file.inflatedlen,
+                               new_data, new_len, old_data, old_len)) < 0 ||
+                       (error = create_binary(&binary.new_file.type,
+                               (char **)&binary.new_file.data,
+                               &binary.new_file.datalen,
+                               &binary.new_file.inflatedlen,
+                               old_data, old_len, new_data, new_len)) < 0)
+                       return error;
+       }
 
-       error = giterr_set_after_callback_function(
+       error = git_error_set_after_callback_function(
                output->binary_cb(patch->base.delta, &binary, output->payload),
                "git_patch");
 
@@ -411,58 +395,10 @@ static int diff_required(git_diff *diff, const char *action)
 {
        if (diff)
                return 0;
-       giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action);
+       git_error_set(GIT_ERROR_INVALID, "must provide valid diff to %s", action);
        return -1;
 }
 
-int git_diff_foreach(
-       git_diff *diff,
-       git_diff_file_cb file_cb,
-       git_diff_binary_cb binary_cb,
-       git_diff_hunk_cb hunk_cb,
-       git_diff_line_cb data_cb,
-       void *payload)
-{
-       int error = 0;
-       git_xdiff_output xo;
-       size_t idx;
-       git_patch_generated patch;
-
-       if ((error = diff_required(diff, "git_diff_foreach")) < 0)
-               return error;
-
-       memset(&xo, 0, sizeof(xo));
-       memset(&patch, 0, sizeof(patch));
-       diff_output_init(
-               &xo.output, &diff->opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
-       git_xdiff_init(&xo, &diff->opts);
-
-       git_vector_foreach(&diff->deltas, idx, patch.base.delta) {
-
-               /* check flags against patch status */
-               if (git_diff_delta__should_skip(&diff->opts, patch.base.delta))
-                       continue;
-
-               if (binary_cb || hunk_cb || data_cb) {
-                       if ((error = patch_generated_init(&patch, diff, idx)) != 0 ||
-                               (error = patch_generated_load(&patch, &xo.output)) != 0)
-                               return error;
-               }
-
-               if ((error = patch_generated_invoke_file_callback(&patch, &xo.output)) == 0) {
-                       if (binary_cb || hunk_cb || data_cb)
-                                       error = patch_generated_create(&patch, &xo.output);
-               }
-
-               git_patch_free(&patch.base);
-
-               if (error)
-                       break;
-       }
-
-       return error;
-}
-
 typedef struct {
        git_patch_generated patch;
        git_diff_delta delta;
@@ -488,8 +424,17 @@ static int diff_single_generate(patch_generated_with_delta *pd, git_xdiff_output
        patch_generated_init_common(patch);
 
        if (pd->delta.status == GIT_DELTA_UNMODIFIED &&
-               !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED))
+               !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) {
+
+               /* Even empty patches are flagged as binary, and even though
+                * there's no difference, we flag this as "containing data"
+                * (the data is known to be empty, as opposed to wholly unknown).
+                */
+               if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY)
+                       patch->base.binary.contains_data = 1;
+
                return error;
+       }
 
        error = patch_generated_invoke_file_callback(patch, (git_patch_generated_output *)xo);
 
@@ -554,12 +499,12 @@ static int patch_generated_with_delta_alloc(
        size_t new_len = *new_path ? strlen(*new_path) : 0;
        size_t alloc_len;
 
-       GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*pd), old_len);
-       GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, new_len);
-       GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+       GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*pd), old_len);
+       GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, new_len);
+       GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
 
        *out = pd = git__calloc(1, alloc_len);
-       GITERR_CHECK_ALLOC(pd);
+       GIT_ERROR_CHECK_ALLOC(pd);
 
        pd->patch.flags = GIT_PATCH_GENERATED_ALLOCATED;
 
@@ -616,7 +561,7 @@ static int patch_from_sources(
        patch_generated_with_delta *pd;
        git_xdiff_output xo;
 
-       assert(out);
+       GIT_ASSERT_ARG(out);
        *out = NULL;
 
        if ((error = patch_generated_with_delta_alloc(
@@ -695,7 +640,7 @@ int git_patch_from_blob_and_buffer(
        git_patch **out,
        const git_blob *old_blob,
        const char *old_path,
-       const char *buf,
+       const void *buf,
        size_t buflen,
        const char *buf_path,
        const git_diff_options *opts)
@@ -734,7 +679,7 @@ int git_patch_from_buffers(
        const void *old_buf,
        size_t old_len,
        const char *old_path,
-       const char *new_buf,
+       const void *new_buf,
        size_t new_len,
        const char *new_path,
        const git_diff_options *opts)
@@ -746,7 +691,7 @@ int git_patch_from_buffers(
        return patch_from_sources(out, &osrc, &nsrc, opts);
 }
 
-int git_patch_from_diff(
+int git_patch_generated_from_diff(
        git_patch **patch_ptr, git_diff *diff, size_t idx)
 {
        int error = 0;
@@ -761,7 +706,7 @@ int git_patch_from_diff(
 
        delta = git_vector_get(&diff->deltas, idx);
        if (!delta) {
-               giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
+               git_error_set(GIT_ERROR_INVALID, "index out of range for delta in diff");
                return GIT_ENOTFOUND;
        }
 
@@ -805,18 +750,34 @@ git_diff_driver *git_patch_generated_driver(git_patch_generated *patch)
        return patch->ofile.driver;
 }
 
-void git_patch_generated_old_data(
-       char **ptr, size_t *len, git_patch_generated *patch)
+int git_patch_generated_old_data(
+       char **ptr, long *len, git_patch_generated *patch)
 {
+       if (patch->ofile.map.len > LONG_MAX ||
+           patch->ofile.map.len > GIT_XDIFF_MAX_SIZE) {
+               git_error_set(GIT_ERROR_INVALID, "files too large for diff");
+               return -1;
+       }
+
        *ptr = patch->ofile.map.data;
-       *len = patch->ofile.map.len;
+       *len = (long)patch->ofile.map.len;
+
+       return 0;
 }
 
-void git_patch_generated_new_data(
-       char **ptr, size_t *len, git_patch_generated *patch)
+int git_patch_generated_new_data(
+       char **ptr, long *len, git_patch_generated *patch)
 {
+       if (patch->ofile.map.len > LONG_MAX ||
+           patch->ofile.map.len > GIT_XDIFF_MAX_SIZE) {
+               git_error_set(GIT_ERROR_INVALID, "files too large for diff");
+               return -1;
+       }
+
        *ptr = patch->nfile.map.data;
-       *len = patch->nfile.map.len;
+       *len = (long)patch->nfile.map.len;
+
+       return 0;
 }
 
 static int patch_generated_file_cb(
@@ -841,7 +802,7 @@ static int patch_generated_binary_cb(
 
        if (binary->old_file.data) {
                patch->binary.old_file.data = git__malloc(binary->old_file.datalen);
-               GITERR_CHECK_ALLOC(patch->binary.old_file.data);
+               GIT_ERROR_CHECK_ALLOC(patch->binary.old_file.data);
 
                memcpy((char *)patch->binary.old_file.data,
                        binary->old_file.data, binary->old_file.datalen);
@@ -849,7 +810,7 @@ static int patch_generated_binary_cb(
 
        if (binary->new_file.data) {
                patch->binary.new_file.data = git__malloc(binary->new_file.datalen);
-               GITERR_CHECK_ALLOC(patch->binary.new_file.data);
+               GIT_ERROR_CHECK_ALLOC(patch->binary.new_file.data);
 
                memcpy((char *)patch->binary.new_file.data,
                        binary->new_file.data, binary->new_file.datalen);
@@ -869,7 +830,7 @@ static int git_patch_hunk_cb(
        GIT_UNUSED(delta);
 
        hunk = git_array_alloc(patch->base.hunks);
-       GITERR_CHECK_ALLOC(hunk);
+       GIT_ERROR_CHECK_ALLOC(hunk);
 
        memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
 
@@ -889,16 +850,16 @@ static int patch_generated_line_cb(
 {
        git_patch_generated  *patch = payload;
        git_patch_hunk *hunk;
-       git_diff_line   *line;
+       git_diff_line *line;
 
        GIT_UNUSED(delta);
        GIT_UNUSED(hunk_);
 
        hunk = git_array_last(patch->base.hunks);
-       assert(hunk); /* programmer error if no hunk is available */
+       GIT_ASSERT(hunk); /* programmer error if no hunk is available */
 
        line = git_array_alloc(patch->base.lines);
-       GITERR_CHECK_ALLOC(line);
+       GIT_ERROR_CHECK_ALLOC(line);
 
        memcpy(line, line_, sizeof(*line));