From 4d5acae0ca9c3a72c7ba7d82d684e7c0e57f8e92 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 11 Aug 2016 10:52:29 +0200 Subject: apply: make some names more specific To prepare for some structs and constants being moved from builtin/apply.c to apply.h, we should give them some more specific names to avoid possible name collisions in the global namespace. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 1a488f9..ab8f0bd 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -21,7 +21,7 @@ #include "ll-merge.h" #include "rerere.h" -enum ws_error_action { +enum apply_ws_error_action { nowarn_ws_error, warn_on_ws_error, die_on_ws_error, @@ -29,7 +29,7 @@ enum ws_error_action { }; -enum ws_ignore { +enum apply_ws_ignore { ignore_ws_none, ignore_ws_change }; @@ -45,8 +45,8 @@ enum ws_ignore { * See also "struct string_list symlink_changes" in "struct * apply_state". */ -#define SYMLINK_GOES_AWAY 01 -#define SYMLINK_IN_RESULT 02 +#define APPLY_SYMLINK_GOES_AWAY 01 +#define APPLY_SYMLINK_IN_RESULT 02 struct apply_state { const char *prefix; @@ -110,8 +110,8 @@ struct apply_state { struct string_list fn_table; /* These control whitespace errors */ - enum ws_error_action ws_error_action; - enum ws_ignore ws_ignore_action; + enum apply_ws_error_action ws_error_action; + enum apply_ws_ignore ws_ignore_action; const char *whitespace_option; int whitespace_error; int squelch_whitespace_errors; @@ -3750,11 +3750,11 @@ static void prepare_symlink_changes(struct apply_state *state, struct patch *pat if ((patch->old_name && S_ISLNK(patch->old_mode)) && (patch->is_rename || patch->is_delete)) /* the symlink at patch->old_name is removed */ - register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY); + register_symlink_changes(state, patch->old_name, APPLY_SYMLINK_GOES_AWAY); if (patch->new_name && S_ISLNK(patch->new_mode)) /* the symlink at patch->new_name is created or remains */ - register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT); + register_symlink_changes(state, patch->new_name, APPLY_SYMLINK_IN_RESULT); } } @@ -3769,9 +3769,9 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na break; name->buf[name->len] = '\0'; change = check_symlink_changes(state, name->buf); - if (change & SYMLINK_IN_RESULT) + if (change & APPLY_SYMLINK_IN_RESULT) return 1; - if (change & SYMLINK_GOES_AWAY) + if (change & APPLY_SYMLINK_GOES_AWAY) /* * This cannot be "return 0", because we may * see a new one created at a higher level. -- cgit v0.10.2-6-g49f6 From 71501a71d0431ade410afa618adf55806e1f5f11 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:02:59 +0200 Subject: apply: move 'struct apply_state' to apply.h To libify `git apply` functionality we must make 'struct apply_state' usable outside "builtin/apply.c". Let's do that by creating a new "apply.h" and moving 'struct apply_state' there. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.h b/apply.h new file mode 100644 index 0000000..7493a40 --- /dev/null +++ b/apply.h @@ -0,0 +1,100 @@ +#ifndef APPLY_H +#define APPLY_H + +enum apply_ws_error_action { + nowarn_ws_error, + warn_on_ws_error, + die_on_ws_error, + correct_ws_error +}; + +enum apply_ws_ignore { + ignore_ws_none, + ignore_ws_change +}; + +/* + * We need to keep track of how symlinks in the preimage are + * manipulated by the patches. A patch to add a/b/c where a/b + * is a symlink should not be allowed to affect the directory + * the symlink points at, but if the same patch removes a/b, + * it is perfectly fine, as the patch removes a/b to make room + * to create a directory a/b so that a/b/c can be created. + * + * See also "struct string_list symlink_changes" in "struct + * apply_state". + */ +#define APPLY_SYMLINK_GOES_AWAY 01 +#define APPLY_SYMLINK_IN_RESULT 02 + +struct apply_state { + const char *prefix; + int prefix_length; + + /* These are lock_file related */ + struct lock_file *lock_file; + int newfd; + + /* These control what gets looked at and modified */ + int apply; /* this is not a dry-run */ + int cached; /* apply to the index only */ + int check; /* preimage must match working tree, don't actually apply */ + int check_index; /* preimage must match the indexed version */ + int update_index; /* check_index && apply */ + + /* These control cosmetic aspect of the output */ + int diffstat; /* just show a diffstat, and don't actually apply */ + int numstat; /* just show a numeric diffstat, and don't actually apply */ + int summary; /* just report creation, deletion, etc, and don't actually apply */ + + /* These boolean parameters control how the apply is done */ + int allow_overlap; + int apply_in_reverse; + int apply_with_reject; + int apply_verbosely; + int no_add; + int threeway; + int unidiff_zero; + int unsafe_paths; + + /* Other non boolean parameters */ + const char *fake_ancestor; + const char *patch_input_file; + int line_termination; + struct strbuf root; + int p_value; + int p_value_known; + unsigned int p_context; + + /* Exclude and include path parameters */ + struct string_list limit_by_name; + int has_include; + + /* Various "current state" */ + int linenr; /* current line number */ + struct string_list symlink_changes; /* we have to track symlinks */ + + /* + * For "diff-stat" like behaviour, we keep track of the biggest change + * we've seen, and the longest filename. That allows us to do simple + * scaling. + */ + int max_change; + int max_len; + + /* + * Records filenames that have been touched, in order to handle + * the case where more than one patches touch the same file. + */ + struct string_list fn_table; + + /* These control whitespace errors */ + enum apply_ws_error_action ws_error_action; + enum apply_ws_ignore ws_ignore_action; + const char *whitespace_option; + int whitespace_error; + int squelch_whitespace_errors; + int applied_after_fixing_ws; +}; + +#endif diff --git a/builtin/apply.c b/builtin/apply.c index ab8f0bd..ed923ca 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -20,103 +20,7 @@ #include "xdiff-interface.h" #include "ll-merge.h" #include "rerere.h" - -enum apply_ws_error_action { - nowarn_ws_error, - warn_on_ws_error, - die_on_ws_error, - correct_ws_error -}; - - -enum apply_ws_ignore { - ignore_ws_none, - ignore_ws_change -}; - -/* - * We need to keep track of how symlinks in the preimage are - * manipulated by the patches. A patch to add a/b/c where a/b - * is a symlink should not be allowed to affect the directory - * the symlink points at, but if the same patch removes a/b, - * it is perfectly fine, as the patch removes a/b to make room - * to create a directory a/b so that a/b/c can be created. - * - * See also "struct string_list symlink_changes" in "struct - * apply_state". - */ -#define APPLY_SYMLINK_GOES_AWAY 01 -#define APPLY_SYMLINK_IN_RESULT 02 - -struct apply_state { - const char *prefix; - int prefix_length; - - /* These are lock_file related */ - struct lock_file *lock_file; - int newfd; - - /* These control what gets looked at and modified */ - int apply; /* this is not a dry-run */ - int cached; /* apply to the index only */ - int check; /* preimage must match working tree, don't actually apply */ - int check_index; /* preimage must match the indexed version */ - int update_index; /* check_index && apply */ - - /* These control cosmetic aspect of the output */ - int diffstat; /* just show a diffstat, and don't actually apply */ - int numstat; /* just show a numeric diffstat, and don't actually apply */ - int summary; /* just report creation, deletion, etc, and don't actually apply */ - - /* These boolean parameters control how the apply is done */ - int allow_overlap; - int apply_in_reverse; - int apply_with_reject; - int apply_verbosely; - int no_add; - int threeway; - int unidiff_zero; - int unsafe_paths; - - /* Other non boolean parameters */ - const char *fake_ancestor; - const char *patch_input_file; - int line_termination; - struct strbuf root; - int p_value; - int p_value_known; - unsigned int p_context; - - /* Exclude and include path parameters */ - struct string_list limit_by_name; - int has_include; - - /* Various "current state" */ - int linenr; /* current line number */ - struct string_list symlink_changes; /* we have to track symlinks */ - - /* - * For "diff-stat" like behaviour, we keep track of the biggest change - * we've seen, and the longest filename. That allows us to do simple - * scaling. - */ - int max_change; - int max_len; - - /* - * Records filenames that have been touched, in order to handle - * the case where more than one patches touch the same file. - */ - struct string_list fn_table; - - /* These control whitespace errors */ - enum apply_ws_error_action ws_error_action; - enum apply_ws_ignore ws_ignore_action; - const char *whitespace_option; - int whitespace_error; - int squelch_whitespace_errors; - int applied_after_fixing_ws; -}; +#include "apply.h" static const char * const apply_usage[] = { N_("git apply [] [...]"), -- cgit v0.10.2-6-g49f6 From f07a9f7643c8b261b5d03d9c288c44916277f05e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:00 +0200 Subject: builtin/apply: make apply_patch() return -1 or -128 instead of die()ing To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. As a first step in this direction, let's make apply_patch() return -1 or -128 in case of errors instead of dying. For now its only caller apply_all_patches() will exit(128) when apply_patch() return -128 and it will exit(1) when it returns -1. We exit() with code 128 because that was what die() was doing and we want to keep the distinction between exiting with code 1 and exiting with code 128. Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index ed923ca..435030a 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4404,6 +4404,15 @@ static struct lock_file lock_file; #define INACCURATE_EOF (1<<0) #define RECOUNT (1<<1) +/* + * Try to apply a patch. + * + * Returns: + * -128 if a bad error happened (like patch unreadable) + * -1 if patch did not apply and user cannot deal with it + * 0 if the patch applied + * 1 if the patch did not apply but user might fix it + */ static int apply_patch(struct apply_state *state, int fd, const char *filename, @@ -4413,6 +4422,7 @@ static int apply_patch(struct apply_state *state, struct strbuf buf = STRBUF_INIT; /* owns the patch text */ struct patch *list = NULL, **listp = &list; int skipped_patch = 0; + int res = 0; state->patch_input_file = filename; read_patch_file(&buf, fd); @@ -4445,8 +4455,11 @@ static int apply_patch(struct apply_state *state, offset += nr; } - if (!list && !skipped_patch) - die(_("unrecognized input")); + if (!list && !skipped_patch) { + error(_("unrecognized input")); + res = -128; + goto end; + } if (state->whitespace_error && (state->ws_error_action == die_on_ws_error)) state->apply = 0; @@ -4455,21 +4468,23 @@ static int apply_patch(struct apply_state *state, if (state->update_index && state->newfd < 0) state->newfd = hold_locked_index(state->lock_file, 1); - if (state->check_index) { - if (read_cache() < 0) - die(_("unable to read index file")); + if (state->check_index && read_cache() < 0) { + error(_("unable to read index file")); + res = -128; + goto end; } if ((state->check || state->apply) && check_patch_list(state, list) < 0 && - !state->apply_with_reject) - exit(1); + !state->apply_with_reject) { + res = -1; + goto end; + } if (state->apply && write_out_results(state, list)) { - if (state->apply_with_reject) - exit(1); /* with --3way, we still need to write the index out */ - return 1; + res = state->apply_with_reject ? -1 : 1; + goto end; } if (state->fake_ancestor) @@ -4484,10 +4499,11 @@ static int apply_patch(struct apply_state *state, if (state->summary) summary_patch_list(list); +end: free_patch_list(list); strbuf_release(&buf); string_list_clear(&state->fn_table, 0); - return 0; + return res; } static void git_apply_config(void) @@ -4628,6 +4644,7 @@ static int apply_all_patches(struct apply_state *state, int options) { int i; + int res; int errs = 0; int read_stdin = 1; @@ -4636,7 +4653,10 @@ static int apply_all_patches(struct apply_state *state, int fd; if (!strcmp(arg, "-")) { - errs |= apply_patch(state, 0, "", options); + res = apply_patch(state, 0, "", options); + if (res < 0) + goto end; + errs |= res; read_stdin = 0; continue; } else if (0 < state->prefix_length) @@ -4649,12 +4669,19 @@ static int apply_all_patches(struct apply_state *state, die_errno(_("can't open patch '%s'"), arg); read_stdin = 0; set_default_whitespace_mode(state); - errs |= apply_patch(state, fd, arg, options); + res = apply_patch(state, fd, arg, options); + if (res < 0) + goto end; + errs |= res; close(fd); } set_default_whitespace_mode(state); - if (read_stdin) - errs |= apply_patch(state, 0, "", options); + if (read_stdin) { + res = apply_patch(state, 0, "", options); + if (res < 0) + goto end; + errs |= res; + } if (state->whitespace_error) { if (state->squelch_whitespace_errors && @@ -4690,6 +4717,9 @@ static int apply_all_patches(struct apply_state *state, } return !!errs; + +end: + exit(res == -1 ? 1 : 128); } int cmd_apply(int argc, const char **argv, const char *prefix) -- cgit v0.10.2-6-g49f6 From 3bee345d7b6c7d95e9585b224320689979a58f9e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:01 +0200 Subject: builtin/apply: read_patch_file() return -1 instead of die()ing To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. Let's do that by returning -1 instead of die()ing in read_patch_file(). Helped-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 435030a..dd7afee 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -335,10 +335,10 @@ static void say_patch_name(FILE *output, const char *fmt, struct patch *patch) #define SLOP (16) -static void read_patch_file(struct strbuf *sb, int fd) +static int read_patch_file(struct strbuf *sb, int fd) { if (strbuf_read(sb, fd, 0) < 0) - die_errno("git apply: failed to read"); + return error_errno("git apply: failed to read"); /* * Make sure that we have some slop in the buffer @@ -347,6 +347,7 @@ static void read_patch_file(struct strbuf *sb, int fd) */ strbuf_grow(sb, SLOP); memset(sb->buf + sb->len, 0, SLOP); + return 0; } static unsigned long linelen(const char *buffer, unsigned long size) @@ -4425,7 +4426,8 @@ static int apply_patch(struct apply_state *state, int res = 0; state->patch_input_file = filename; - read_patch_file(&buf, fd); + if (read_patch_file(&buf, fd) < 0) + return -128; offset = 0; while (offset < buf.len) { struct patch *patch; -- cgit v0.10.2-6-g49f6 From 5950851e44fc6f19e9fc9261bac4a61e59fc5121 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:02 +0200 Subject: builtin/apply: make find_header() return -128 instead of die()ing To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in builtin/apply.c, let's make find_header() return -128 instead of calling die(). We could make it return -1, unfortunately find_header() already returns -1 when no header is found. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index dd7afee..434ba0c 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1419,6 +1419,14 @@ static int parse_fragment_header(const char *line, int len, struct fragment *fra return offset; } +/* + * Find file diff header + * + * Returns: + * -1 if no header was found + * -128 in case of error + * the size of the header in bytes (called "offset") otherwise + */ static int find_header(struct apply_state *state, const char *line, unsigned long size, @@ -1452,8 +1460,9 @@ static int find_header(struct apply_state *state, struct fragment dummy; if (parse_fragment_header(line, len, &dummy) < 0) continue; - die(_("patch fragment without header at line %d: %.*s"), - state->linenr, (int)len-1, line); + error(_("patch fragment without header at line %d: %.*s"), + state->linenr, (int)len-1, line); + return -128; } if (size < len + 6) @@ -1468,19 +1477,23 @@ static int find_header(struct apply_state *state, if (git_hdr_len <= len) continue; if (!patch->old_name && !patch->new_name) { - if (!patch->def_name) - die(Q_("git diff header lacks filename information when removing " - "%d leading pathname component (line %d)", - "git diff header lacks filename information when removing " - "%d leading pathname components (line %d)", - state->p_value), - state->p_value, state->linenr); + if (!patch->def_name) { + error(Q_("git diff header lacks filename information when removing " + "%d leading pathname component (line %d)", + "git diff header lacks filename information when removing " + "%d leading pathname components (line %d)", + state->p_value), + state->p_value, state->linenr); + return -128; + } patch->old_name = xstrdup(patch->def_name); patch->new_name = xstrdup(patch->def_name); } - if (!patch->is_delete && !patch->new_name) - die("git diff header lacks filename information " - "(line %d)", state->linenr); + if (!patch->is_delete && !patch->new_name) { + error("git diff header lacks filename information " + "(line %d)", state->linenr); + return -128; + } patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; @@ -1996,6 +2009,9 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si int hdrsize, patchsize; int offset = find_header(state, buffer, size, &hdrsize, patch); + if (offset == -128) + exit(128); + if (offset < 0) return offset; diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh index 85716dd..9bd7dd2 100755 --- a/t/t4254-am-corrupt.sh +++ b/t/t4254-am-corrupt.sh @@ -29,7 +29,7 @@ test_expect_success 'try to apply corrupted patch' ' ' test_expect_success 'compare diagnostic; ensure file is still here' ' - echo "fatal: git diff header lacks filename information (line 4)" >expected && + echo "error: git diff header lacks filename information (line 4)" >expected && test_path_is_file f && test_cmp expected actual ' -- cgit v0.10.2-6-g49f6 From b654b34c1cf877709febb602991f46e7ba0d947d Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:03 +0200 Subject: builtin/apply: make parse_chunk() return a negative integer on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing or exit()ing. To do that in a compatible manner with the rest of the error handling in builtin/apply.c, parse_chunk() should return a negative integer instead of calling die() or exit(). As parse_chunk() is called only by apply_patch() which already returns either -1 or -128 when an error happened, let's make it also return -1 or -128. This makes it compatible with what find_header() and parse_binary() already return. Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 434ba0c..c07d142 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1996,22 +1996,22 @@ static int use_patch(struct apply_state *state, struct patch *p) return !state->has_include; } - /* * Read the patch text in "buffer" that extends for "size" bytes; stop * reading after seeing a single patch (i.e. changes to a single file). * Create fragments (i.e. patch hunks) and hang them to the given patch. - * Return the number of bytes consumed, so that the caller can call us - * again for the next patch. + * + * Returns: + * -1 if no header was found or parse_binary() failed, + * -128 on another error, + * the number of bytes consumed otherwise, + * so that the caller can call us again for the next patch. */ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch) { int hdrsize, patchsize; int offset = find_header(state, buffer, size, &hdrsize, patch); - if (offset == -128) - exit(128); - if (offset < 0) return offset; @@ -2071,8 +2071,10 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si * empty to us here. */ if ((state->apply || state->check) && - (!patch->is_binary && !metadata_changes(patch))) - die(_("patch with only garbage at line %d"), state->linenr); + (!patch->is_binary && !metadata_changes(patch))) { + error(_("patch with only garbage at line %d"), state->linenr); + return -128; + } } return offset + hdrsize + patchsize; @@ -4455,6 +4457,10 @@ static int apply_patch(struct apply_state *state, nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); if (nr < 0) { free_patch(patch); + if (nr == -128) { + res = -128; + goto end; + } break; } if (state->apply_in_reverse) -- cgit v0.10.2-6-g49f6 From dae197f753c8b3ccdc9c97cfc04f0dbd99a5cc3c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:04 +0200 Subject: builtin/apply: make parse_single_patch() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in builtin/apply.c, parse_single_patch() should return a negative integer instead of calling die(). Let's do that by using error() and let's adjust the related test cases accordingly. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index c07d142..10aaba7 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1671,6 +1671,10 @@ static int parse_fragment(struct apply_state *state, * * The (fragment->patch, fragment->size) pair points into the memory given * by the caller, not a copy, when we return. + * + * Returns: + * -1 in case of error, + * the number of bytes in the patch otherwise. */ static int parse_single_patch(struct apply_state *state, const char *line, @@ -1688,8 +1692,10 @@ static int parse_single_patch(struct apply_state *state, fragment = xcalloc(1, sizeof(*fragment)); fragment->linenr = state->linenr; len = parse_fragment(state, line, size, patch, fragment); - if (len <= 0) - die(_("corrupt patch at line %d"), state->linenr); + if (len <= 0) { + free(fragment); + return error(_("corrupt patch at line %d"), state->linenr); + } fragment->patch = line; fragment->size = len; oldlines += fragment->oldlines; @@ -1725,9 +1731,9 @@ static int parse_single_patch(struct apply_state *state, patch->is_delete = 0; if (0 < patch->is_new && oldlines) - die(_("new file %s depends on old contents"), patch->new_name); + return error(_("new file %s depends on old contents"), patch->new_name); if (0 < patch->is_delete && newlines) - die(_("deleted file %s still has contents"), patch->old_name); + return error(_("deleted file %s still has contents"), patch->old_name); if (!patch->is_delete && !newlines && context) fprintf_ln(stderr, _("** warning: " @@ -2029,6 +2035,9 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si size - offset - hdrsize, patch); + if (patchsize < 0) + return -128; + if (!patchsize) { static const char git_binary[] = "GIT binary patch\n"; int hd = hdrsize + offset; diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index 643d729..0a8af76 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -68,7 +68,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' ' sed -e "s/-CIT/xCIT/" broken && test_must_fail git apply --stat --summary broken 2>detected && detected=$(cat detected) && - detected=$(expr "$detected" : "fatal.*at line \\([0-9]*\\)\$") && + detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") && detected=$(sed -ne "${detected}p" broken) && test "$detected" = xCIT ' @@ -77,7 +77,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' ' git diff --binary | sed -e "s/-CIT/xCIT/" >broken && test_must_fail git apply --stat --summary broken 2>detected && detected=$(cat detected) && - detected=$(expr "$detected" : "fatal.*at line \\([0-9]*\\)\$") && + detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") && detected=$(sed -ne "${detected}p" broken) && test "$detected" = xCIT ' -- cgit v0.10.2-6-g49f6 From aaf6c447aabb16ed71345d5baf8b12ced26c5c95 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:05 +0200 Subject: builtin/apply: make parse_whitespace_option() return -1 instead of die()ing To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in builtin/apply.c, parse_whitespace_option() should return -1 instead of calling die(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 10aaba7..06a76f2 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -27,34 +27,34 @@ static const char * const apply_usage[] = { NULL }; -static void parse_whitespace_option(struct apply_state *state, const char *option) +static int parse_whitespace_option(struct apply_state *state, const char *option) { if (!option) { state->ws_error_action = warn_on_ws_error; - return; + return 0; } if (!strcmp(option, "warn")) { state->ws_error_action = warn_on_ws_error; - return; + return 0; } if (!strcmp(option, "nowarn")) { state->ws_error_action = nowarn_ws_error; - return; + return 0; } if (!strcmp(option, "error")) { state->ws_error_action = die_on_ws_error; - return; + return 0; } if (!strcmp(option, "error-all")) { state->ws_error_action = die_on_ws_error; state->squelch_whitespace_errors = 0; - return; + return 0; } if (!strcmp(option, "strip") || !strcmp(option, "fix")) { state->ws_error_action = correct_ws_error; - return; + return 0; } - die(_("unrecognized whitespace option '%s'"), option); + return error(_("unrecognized whitespace option '%s'"), option); } static void parse_ignorewhitespace_option(struct apply_state *state, @@ -4589,7 +4589,8 @@ static int option_parse_whitespace(const struct option *opt, { struct apply_state *state = opt->value; state->whitespace_option = arg; - parse_whitespace_option(state, arg); + if (parse_whitespace_option(state, arg)) + exit(1); return 0; } @@ -4626,8 +4627,8 @@ static void init_apply_state(struct apply_state *state, strbuf_init(&state->root, 0); git_apply_config(); - if (apply_default_whitespace) - parse_whitespace_option(state, apply_default_whitespace); + if (apply_default_whitespace && parse_whitespace_option(state, apply_default_whitespace)) + exit(1); if (apply_default_ignorewhitespace) parse_ignorewhitespace_option(state, apply_default_ignorewhitespace); } -- cgit v0.10.2-6-g49f6 From f95fdc256b4732e6363823daa707ea6058e6db8e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:06 +0200 Subject: builtin/apply: make parse_ignorewhitespace_option() return -1 instead of die()ing To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", parse_ignorewhitespace_option() should return -1 instead of calling die(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 06a76f2..ecb1f7a 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -57,20 +57,20 @@ static int parse_whitespace_option(struct apply_state *state, const char *option return error(_("unrecognized whitespace option '%s'"), option); } -static void parse_ignorewhitespace_option(struct apply_state *state, - const char *option) +static int parse_ignorewhitespace_option(struct apply_state *state, + const char *option) { if (!option || !strcmp(option, "no") || !strcmp(option, "false") || !strcmp(option, "never") || !strcmp(option, "none")) { state->ws_ignore_action = ignore_ws_none; - return; + return 0; } if (!strcmp(option, "change")) { state->ws_ignore_action = ignore_ws_change; - return; + return 0; } - die(_("unrecognized whitespace ignore option '%s'"), option); + return error(_("unrecognized whitespace ignore option '%s'"), option); } static void set_default_whitespace_mode(struct apply_state *state) @@ -4629,8 +4629,8 @@ static void init_apply_state(struct apply_state *state, git_apply_config(); if (apply_default_whitespace && parse_whitespace_option(state, apply_default_whitespace)) exit(1); - if (apply_default_ignorewhitespace) - parse_ignorewhitespace_option(state, apply_default_ignorewhitespace); + if (apply_default_ignorewhitespace && parse_ignorewhitespace_option(state, apply_default_ignorewhitespace)) + exit(1); } static void clear_apply_state(struct apply_state *state) -- cgit v0.10.2-6-g49f6 From bb493a5c147a4b60f0f412a71bf9236ede4a560c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:07 +0200 Subject: builtin/apply: move init_apply_state() to apply.c To libify `git apply` functionality we must make init_apply_state() usable outside "builtin/apply.c". Let's do that by moving it into a new "apply.c". Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 6a13386..3230fd0 100644 --- a/Makefile +++ b/Makefile @@ -683,6 +683,7 @@ LIB_OBJS += abspath.o LIB_OBJS += advice.o LIB_OBJS += alias.o LIB_OBJS += alloc.o +LIB_OBJS += apply.o LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o diff --git a/apply.c b/apply.c new file mode 100644 index 0000000..c858ca4 --- /dev/null +++ b/apply.c @@ -0,0 +1,94 @@ +#include "cache.h" +#include "lockfile.h" +#include "apply.h" + +static void git_apply_config(void) +{ + git_config_get_string_const("apply.whitespace", &apply_default_whitespace); + git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace); + git_config(git_default_config, NULL); +} + +int parse_whitespace_option(struct apply_state *state, const char *option) +{ + if (!option) { + state->ws_error_action = warn_on_ws_error; + return 0; + } + if (!strcmp(option, "warn")) { + state->ws_error_action = warn_on_ws_error; + return 0; + } + if (!strcmp(option, "nowarn")) { + state->ws_error_action = nowarn_ws_error; + return 0; + } + if (!strcmp(option, "error")) { + state->ws_error_action = die_on_ws_error; + return 0; + } + if (!strcmp(option, "error-all")) { + state->ws_error_action = die_on_ws_error; + state->squelch_whitespace_errors = 0; + return 0; + } + if (!strcmp(option, "strip") || !strcmp(option, "fix")) { + state->ws_error_action = correct_ws_error; + return 0; + } + return error(_("unrecognized whitespace option '%s'"), option); +} + +int parse_ignorewhitespace_option(struct apply_state *state, + const char *option) +{ + if (!option || !strcmp(option, "no") || + !strcmp(option, "false") || !strcmp(option, "never") || + !strcmp(option, "none")) { + state->ws_ignore_action = ignore_ws_none; + return 0; + } + if (!strcmp(option, "change")) { + state->ws_ignore_action = ignore_ws_change; + return 0; + } + return error(_("unrecognized whitespace ignore option '%s'"), option); +} + +void init_apply_state(struct apply_state *state, + const char *prefix, + struct lock_file *lock_file) +{ + memset(state, 0, sizeof(*state)); + state->prefix = prefix; + state->prefix_length = state->prefix ? strlen(state->prefix) : 0; + state->lock_file = lock_file; + state->newfd = -1; + state->apply = 1; + state->line_termination = '\n'; + state->p_value = 1; + state->p_context = UINT_MAX; + state->squelch_whitespace_errors = 5; + state->ws_error_action = warn_on_ws_error; + state->ws_ignore_action = ignore_ws_none; + state->linenr = 1; + string_list_init(&state->fn_table, 0); + string_list_init(&state->limit_by_name, 0); + string_list_init(&state->symlink_changes, 0); + strbuf_init(&state->root, 0); + + git_apply_config(); + if (apply_default_whitespace && parse_whitespace_option(state, apply_default_whitespace)) + exit(1); + if (apply_default_ignorewhitespace && parse_ignorewhitespace_option(state, apply_default_ignorewhitespace)) + exit(1); +} + +void clear_apply_state(struct apply_state *state) +{ + string_list_clear(&state->limit_by_name, 0); + string_list_clear(&state->symlink_changes, 0); + strbuf_release(&state->root); + + /* &state->fn_table is cleared at the end of apply_patch() */ +} diff --git a/apply.h b/apply.h index 7493a40..08c0a25 100644 --- a/apply.h +++ b/apply.h @@ -97,4 +97,14 @@ struct apply_state { int applied_after_fixing_ws; }; +extern int parse_whitespace_option(struct apply_state *state, + const char *option); +extern int parse_ignorewhitespace_option(struct apply_state *state, + const char *option); + +extern void init_apply_state(struct apply_state *state, + const char *prefix, + struct lock_file *lock_file); +extern void clear_apply_state(struct apply_state *state); + #endif diff --git a/builtin/apply.c b/builtin/apply.c index ecb1f7a..bb6ff77 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -27,52 +27,6 @@ static const char * const apply_usage[] = { NULL }; -static int parse_whitespace_option(struct apply_state *state, const char *option) -{ - if (!option) { - state->ws_error_action = warn_on_ws_error; - return 0; - } - if (!strcmp(option, "warn")) { - state->ws_error_action = warn_on_ws_error; - return 0; - } - if (!strcmp(option, "nowarn")) { - state->ws_error_action = nowarn_ws_error; - return 0; - } - if (!strcmp(option, "error")) { - state->ws_error_action = die_on_ws_error; - return 0; - } - if (!strcmp(option, "error-all")) { - state->ws_error_action = die_on_ws_error; - state->squelch_whitespace_errors = 0; - return 0; - } - if (!strcmp(option, "strip") || !strcmp(option, "fix")) { - state->ws_error_action = correct_ws_error; - return 0; - } - return error(_("unrecognized whitespace option '%s'"), option); -} - -static int parse_ignorewhitespace_option(struct apply_state *state, - const char *option) -{ - if (!option || !strcmp(option, "no") || - !strcmp(option, "false") || !strcmp(option, "never") || - !strcmp(option, "none")) { - state->ws_ignore_action = ignore_ws_none; - return 0; - } - if (!strcmp(option, "change")) { - state->ws_ignore_action = ignore_ws_change; - return 0; - } - return error(_("unrecognized whitespace ignore option '%s'"), option); -} - static void set_default_whitespace_mode(struct apply_state *state) { if (!state->whitespace_option && !apply_default_whitespace) @@ -4539,13 +4493,6 @@ end: return res; } -static void git_apply_config(void) -{ - git_config_get_string_const("apply.whitespace", &apply_default_whitespace); - git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace); - git_config(git_default_config, NULL); -} - static int option_parse_exclude(const struct option *opt, const char *arg, int unset) { @@ -4604,44 +4551,6 @@ static int option_parse_directory(const struct option *opt, return 0; } -static void init_apply_state(struct apply_state *state, - const char *prefix, - struct lock_file *lock_file) -{ - memset(state, 0, sizeof(*state)); - state->prefix = prefix; - state->prefix_length = state->prefix ? strlen(state->prefix) : 0; - state->lock_file = lock_file; - state->newfd = -1; - state->apply = 1; - state->line_termination = '\n'; - state->p_value = 1; - state->p_context = UINT_MAX; - state->squelch_whitespace_errors = 5; - state->ws_error_action = warn_on_ws_error; - state->ws_ignore_action = ignore_ws_none; - state->linenr = 1; - string_list_init(&state->fn_table, 0); - string_list_init(&state->limit_by_name, 0); - string_list_init(&state->symlink_changes, 0); - strbuf_init(&state->root, 0); - - git_apply_config(); - if (apply_default_whitespace && parse_whitespace_option(state, apply_default_whitespace)) - exit(1); - if (apply_default_ignorewhitespace && parse_ignorewhitespace_option(state, apply_default_ignorewhitespace)) - exit(1); -} - -static void clear_apply_state(struct apply_state *state) -{ - string_list_clear(&state->limit_by_name, 0); - string_list_clear(&state->symlink_changes, 0); - strbuf_release(&state->root); - - /* &state->fn_table is cleared at the end of apply_patch() */ -} - static void check_apply_state(struct apply_state *state, int force_apply) { int is_not_gitdir = !startup_info->have_repository; -- cgit v0.10.2-6-g49f6 From 2f5a6d1218de4dfa326ff289b784d3e293b8141f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:08 +0200 Subject: apply: make init_apply_state() return -1 instead of exit()ing To libify `git apply` functionality we have to signal errors to the caller instead of exit()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", init_apply_state() should return -1 instead of calling exit(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index c858ca4..6e0e992 100644 --- a/apply.c +++ b/apply.c @@ -55,9 +55,9 @@ int parse_ignorewhitespace_option(struct apply_state *state, return error(_("unrecognized whitespace ignore option '%s'"), option); } -void init_apply_state(struct apply_state *state, - const char *prefix, - struct lock_file *lock_file) +int init_apply_state(struct apply_state *state, + const char *prefix, + struct lock_file *lock_file) { memset(state, 0, sizeof(*state)); state->prefix = prefix; @@ -79,9 +79,10 @@ void init_apply_state(struct apply_state *state, git_apply_config(); if (apply_default_whitespace && parse_whitespace_option(state, apply_default_whitespace)) - exit(1); + return -1; if (apply_default_ignorewhitespace && parse_ignorewhitespace_option(state, apply_default_ignorewhitespace)) - exit(1); + return -1; + return 0; } void clear_apply_state(struct apply_state *state) diff --git a/apply.h b/apply.h index 08c0a25..e18a18a 100644 --- a/apply.h +++ b/apply.h @@ -102,9 +102,9 @@ extern int parse_whitespace_option(struct apply_state *state, extern int parse_ignorewhitespace_option(struct apply_state *state, const char *option); -extern void init_apply_state(struct apply_state *state, - const char *prefix, - struct lock_file *lock_file); +extern int init_apply_state(struct apply_state *state, + const char *prefix, + struct lock_file *lock_file); extern void clear_apply_state(struct apply_state *state); #endif diff --git a/builtin/apply.c b/builtin/apply.c index bb6ff77..61fd316 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4741,7 +4741,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix) OPT_END() }; - init_apply_state(&state, prefix, &lock_file); + if (init_apply_state(&state, prefix, &lock_file)) + exit(128); argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); -- cgit v0.10.2-6-g49f6 From f36538d88b12595d527c60ae1b882c89bb5d1b2a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:09 +0200 Subject: builtin/apply: make check_apply_state() return -1 instead of die()ing To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", check_apply_state() should return -1 instead of calling die(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 61fd316..bb89e07 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4551,17 +4551,17 @@ static int option_parse_directory(const struct option *opt, return 0; } -static void check_apply_state(struct apply_state *state, int force_apply) +static int check_apply_state(struct apply_state *state, int force_apply) { int is_not_gitdir = !startup_info->have_repository; if (state->apply_with_reject && state->threeway) - die("--reject and --3way cannot be used together."); + return error("--reject and --3way cannot be used together."); if (state->cached && state->threeway) - die("--cached and --3way cannot be used together."); + return error("--cached and --3way cannot be used together."); if (state->threeway) { if (is_not_gitdir) - die(_("--3way outside a repository")); + return error(_("--3way outside a repository")); state->check_index = 1; } if (state->apply_with_reject) @@ -4569,16 +4569,18 @@ static void check_apply_state(struct apply_state *state, int force_apply) if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor)) state->apply = 0; if (state->check_index && is_not_gitdir) - die(_("--index outside a repository")); + return error(_("--index outside a repository")); if (state->cached) { if (is_not_gitdir) - die(_("--cached outside a repository")); + return error(_("--cached outside a repository")); state->check_index = 1; } if (state->check_index) state->unsafe_paths = 0; if (!state->lock_file) - die("BUG: state->lock_file should not be NULL"); + return error("BUG: state->lock_file should not be NULL"); + + return 0; } static int apply_all_patches(struct apply_state *state, @@ -4747,7 +4749,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); - check_apply_state(&state, force_apply); + if (check_apply_state(&state, force_apply)) + exit(128); ret = apply_all_patches(&state, argc, argv, options); -- cgit v0.10.2-6-g49f6 From b6446d54ec70817ddd96b5c9668dd74a996719bf Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:10 +0200 Subject: builtin/apply: move check_apply_state() to apply.c To libify `git apply` functionality we must make check_apply_state() usable outside "builtin/apply.c". Let's do that by moving it into "apply.c". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index 6e0e992..2eac3e3 100644 --- a/apply.c +++ b/apply.c @@ -93,3 +93,35 @@ void clear_apply_state(struct apply_state *state) /* &state->fn_table is cleared at the end of apply_patch() */ } + +int check_apply_state(struct apply_state *state, int force_apply) +{ + int is_not_gitdir = !startup_info->have_repository; + + if (state->apply_with_reject && state->threeway) + return error("--reject and --3way cannot be used together."); + if (state->cached && state->threeway) + return error("--cached and --3way cannot be used together."); + if (state->threeway) { + if (is_not_gitdir) + return error(_("--3way outside a repository")); + state->check_index = 1; + } + if (state->apply_with_reject) + state->apply = state->apply_verbosely = 1; + if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor)) + state->apply = 0; + if (state->check_index && is_not_gitdir) + return error(_("--index outside a repository")); + if (state->cached) { + if (is_not_gitdir) + return error(_("--cached outside a repository")); + state->check_index = 1; + } + if (state->check_index) + state->unsafe_paths = 0; + if (!state->lock_file) + return error("BUG: state->lock_file should not be NULL"); + + return 0; +} diff --git a/apply.h b/apply.h index e18a18a..53f09b5 100644 --- a/apply.h +++ b/apply.h @@ -106,5 +106,6 @@ extern int init_apply_state(struct apply_state *state, const char *prefix, struct lock_file *lock_file); extern void clear_apply_state(struct apply_state *state); +extern int check_apply_state(struct apply_state *state, int force_apply); #endif diff --git a/builtin/apply.c b/builtin/apply.c index bb89e07..075ada4 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4551,38 +4551,6 @@ static int option_parse_directory(const struct option *opt, return 0; } -static int check_apply_state(struct apply_state *state, int force_apply) -{ - int is_not_gitdir = !startup_info->have_repository; - - if (state->apply_with_reject && state->threeway) - return error("--reject and --3way cannot be used together."); - if (state->cached && state->threeway) - return error("--cached and --3way cannot be used together."); - if (state->threeway) { - if (is_not_gitdir) - return error(_("--3way outside a repository")); - state->check_index = 1; - } - if (state->apply_with_reject) - state->apply = state->apply_verbosely = 1; - if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor)) - state->apply = 0; - if (state->check_index && is_not_gitdir) - return error(_("--index outside a repository")); - if (state->cached) { - if (is_not_gitdir) - return error(_("--cached outside a repository")); - state->check_index = 1; - } - if (state->check_index) - state->unsafe_paths = 0; - if (!state->lock_file) - return error("BUG: state->lock_file should not be NULL"); - - return 0; -} - static int apply_all_patches(struct apply_state *state, int argc, const char **argv, -- cgit v0.10.2-6-g49f6 From fef7ba5353095e87b3bcd712fa15eb71e1f53b30 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:11 +0200 Subject: builtin/apply: make apply_all_patches() return 128 or 1 on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To finish libifying the apply functionality, apply_all_patches() should not die() or exit() in case of error, but return either 128 or 1, so that it gives the same exit code as when die() or exit(1) is called. This way scripts relying on the exit code don't need to be changed. While doing that we must take care that file descriptors are properly closed and, if needed, reset to a sensible value. Also, according to the lockfile API, when finished with a lockfile, one should either commit it or roll it back. This is even more important now that the same lockfile can be passed to init_apply_state() many times to be reused by series of calls to the apply lib functions. Helped-by: Nguyễn Thái Ngọc Duy Helped-by: Johannes Schindelin Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 075ada4..5530ba1 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4578,15 +4578,18 @@ static int apply_all_patches(struct apply_state *state, arg); fd = open(arg, O_RDONLY); - if (fd < 0) - die_errno(_("can't open patch '%s'"), arg); + if (fd < 0) { + error(_("can't open patch '%s': %s"), arg, strerror(errno)); + res = -128; + goto end; + } read_stdin = 0; set_default_whitespace_mode(state); res = apply_patch(state, fd, arg, options); + close(fd); if (res < 0) goto end; errs |= res; - close(fd); } set_default_whitespace_mode(state); if (read_stdin) { @@ -4606,11 +4609,14 @@ static int apply_all_patches(struct apply_state *state, squelched), squelched); } - if (state->ws_error_action == die_on_ws_error) - die(Q_("%d line adds whitespace errors.", - "%d lines add whitespace errors.", - state->whitespace_error), - state->whitespace_error); + if (state->ws_error_action == die_on_ws_error) { + error(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + state->whitespace_error), + state->whitespace_error); + res = -128; + goto end; + } if (state->applied_after_fixing_ws && state->apply) warning("%d line%s applied after" " fixing whitespace errors.", @@ -4624,15 +4630,24 @@ static int apply_all_patches(struct apply_state *state, } if (state->update_index) { - if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK)) - die(_("Unable to write new index file")); + res = write_locked_index(&the_index, state->lock_file, COMMIT_LOCK); + if (res) { + error(_("Unable to write new index file")); + res = -128; + goto end; + } state->newfd = -1; } return !!errs; end: - exit(res == -1 ? 1 : 128); + if (state->newfd >= 0) { + rollback_lock_file(state->lock_file); + state->newfd = -1; + } + + return (res == -1 ? 1 : 128); } int cmd_apply(int argc, const char **argv, const char *prefix) -- cgit v0.10.2-6-g49f6 From 9724e6ff48506323ab897e2d9f8d27febd4d9bb0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:12 +0200 Subject: builtin/apply: make parse_traditional_patch() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", parse_traditional_patch() should return -1 instead of calling die(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 5530ba1..f99498b 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -755,10 +755,10 @@ static int has_epoch_timestamp(const char *nameline) * files, we can happily check the index for a match, but for creating a * new file we should try to match whatever "patch" does. I have no idea. */ -static void parse_traditional_patch(struct apply_state *state, - const char *first, - const char *second, - struct patch *patch) +static int parse_traditional_patch(struct apply_state *state, + const char *first, + const char *second, + struct patch *patch) { char *name; @@ -803,7 +803,9 @@ static void parse_traditional_patch(struct apply_state *state, } } if (!name) - die(_("unable to find filename in patch at line %d"), state->linenr); + return error(_("unable to find filename in patch at line %d"), state->linenr); + + return 0; } static int gitdiff_hdrend(struct apply_state *state, @@ -1467,7 +1469,8 @@ static int find_header(struct apply_state *state, continue; /* Ok, we'll consider it a patch */ - parse_traditional_patch(state, line, line+len, patch); + if (parse_traditional_patch(state, line, line+len, patch)) + return -128; *hdrsize = len + nextlen; state->linenr += 2; return offset; -- cgit v0.10.2-6-g49f6 From 70af7662d47ac9f450c248720a379a8db817163b Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:13 +0200 Subject: builtin/apply: make gitdiff_*() return 1 at end of header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gitdiff_*() functions that are called as p->fn() in parse_git_header() should return 1 instead of -1 in case of end of header or unrecognized input, as these are not real errors. It just instructs the parser to break out. This makes it possible for gitdiff_*() functions to return -1 in case of a real error. This will be done in a following patch. Helped-by: Nguyễn Thái Ngọc Duy Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index f99498b..eb918e5 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -812,7 +812,7 @@ static int gitdiff_hdrend(struct apply_state *state, const char *line, struct patch *patch) { - return -1; + return 1; } /* @@ -1016,7 +1016,7 @@ static int gitdiff_unrecognized(struct apply_state *state, const char *line, struct patch *patch) { - return -1; + return 1; } /* @@ -1248,9 +1248,13 @@ static int parse_git_header(struct apply_state *state, for (i = 0; i < ARRAY_SIZE(optable); i++) { const struct opentry *p = optable + i; int oplen = strlen(p->str); + int res; if (len < oplen || memcmp(p->str, line, oplen)) continue; - if (p->fn(state, line + oplen, patch) < 0) + res = p->fn(state, line + oplen, patch); + if (res < 0) + return -1; + if (res > 0) return offset; break; } @@ -1430,6 +1434,8 @@ static int find_header(struct apply_state *state, */ if (!memcmp("diff --git ", line, 11)) { int git_hdr_len = parse_git_header(state, line, len, size, patch); + if (git_hdr_len < 0) + return -128; if (git_hdr_len <= len) continue; if (!patch->old_name && !patch->new_name) { -- cgit v0.10.2-6-g49f6 From dbf1b5fb6a86acafd8294e98b464e2aa370fdde0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:14 +0200 Subject: builtin/apply: make gitdiff_*() return -1 on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", gitdiff_*() functions should return -1 instead of calling die(). A previous patch made it possible for gitdiff_*() functions to return -1 in case of error. Let's take advantage of that to make gitdiff_verify_name() return -1 on error, and to have gitdiff_oldname() and gitdiff_newname() directly return what gitdiff_verify_name() returns. Helped-by: Nguyễn Thái Ngọc Duy Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index eb918e5..6b16173 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -827,54 +827,56 @@ static int gitdiff_hdrend(struct apply_state *state, #define DIFF_OLD_NAME 0 #define DIFF_NEW_NAME 1 -static void gitdiff_verify_name(struct apply_state *state, - const char *line, - int isnull, - char **name, - int side) +static int gitdiff_verify_name(struct apply_state *state, + const char *line, + int isnull, + char **name, + int side) { if (!*name && !isnull) { *name = find_name(state, line, NULL, state->p_value, TERM_TAB); - return; + return 0; } if (*name) { int len = strlen(*name); char *another; if (isnull) - die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), - *name, state->linenr); + return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), + *name, state->linenr); another = find_name(state, line, NULL, state->p_value, TERM_TAB); - if (!another || memcmp(another, *name, len + 1)) - die((side == DIFF_NEW_NAME) ? + if (!another || memcmp(another, *name, len + 1)) { + free(another); + return error((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr); + } free(another); } else { /* expect "/dev/null" */ if (memcmp("/dev/null", line, 9) || line[9] != '\n') - die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); + return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); } + + return 0; } static int gitdiff_oldname(struct apply_state *state, const char *line, struct patch *patch) { - gitdiff_verify_name(state, line, - patch->is_new, &patch->old_name, - DIFF_OLD_NAME); - return 0; + return gitdiff_verify_name(state, line, + patch->is_new, &patch->old_name, + DIFF_OLD_NAME); } static int gitdiff_newname(struct apply_state *state, const char *line, struct patch *patch) { - gitdiff_verify_name(state, line, - patch->is_delete, &patch->new_name, - DIFF_NEW_NAME); - return 0; + return gitdiff_verify_name(state, line, + patch->is_delete, &patch->new_name, + DIFF_NEW_NAME); } static int gitdiff_oldmode(struct apply_state *state, -- cgit v0.10.2-6-g49f6 From 119ab159e65b229feb3851334441ca24aab131ba Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:15 +0200 Subject: builtin/apply: change die_on_unsafe_path() to check_unsafe_path() To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", die_on_unsafe_path() should return a negative integer instead of calling die(), so while doing that let's change its name to check_unsafe_path(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 6b16173..166e94d 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3704,7 +3704,7 @@ static int path_is_beyond_symlink(struct apply_state *state, const char *name_) return ret; } -static void die_on_unsafe_path(struct patch *patch) +static int check_unsafe_path(struct patch *patch) { const char *old_name = NULL; const char *new_name = NULL; @@ -3716,9 +3716,10 @@ static void die_on_unsafe_path(struct patch *patch) new_name = patch->new_name; if (old_name && !verify_path(old_name)) - die(_("invalid path '%s'"), old_name); + return error(_("invalid path '%s'"), old_name); if (new_name && !verify_path(new_name)) - die(_("invalid path '%s'"), new_name); + return error(_("invalid path '%s'"), new_name); + return 0; } /* @@ -3808,8 +3809,8 @@ static int check_patch(struct apply_state *state, struct patch *patch) } } - if (!state->unsafe_paths) - die_on_unsafe_path(patch); + if (!state->unsafe_paths && check_unsafe_path(patch)) + return -128; /* * An attempt to read from or delete a path that is beyond a @@ -3837,10 +3838,14 @@ static int check_patch_list(struct apply_state *state, struct patch *patch) prepare_symlink_changes(state, patch); prepare_fn_table(state, patch); while (patch) { + int res; if (state->apply_verbosely) say_patch_name(stderr, _("Checking patch %s..."), patch); - err |= check_patch(state, patch); + res = check_patch(state, patch); + if (res == -128) + return -128; + err |= res; patch = patch->next; } return err; @@ -4472,11 +4477,16 @@ static int apply_patch(struct apply_state *state, goto end; } - if ((state->check || state->apply) && - check_patch_list(state, list) < 0 && - !state->apply_with_reject) { - res = -1; - goto end; + if (state->check || state->apply) { + int r = check_patch_list(state, list); + if (r == -128) { + res = -128; + goto end; + } + if (r < 0 && !state->apply_with_reject) { + res = -1; + goto end; + } } if (state->apply && write_out_results(state, list)) { -- cgit v0.10.2-6-g49f6 From fe41b8022560e24c1617cc8b3bd11b72bd1ff4bd Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:16 +0200 Subject: builtin/apply: make build_fake_ancestor() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", build_fake_ancestor() should return -1 instead of calling die(). Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 166e94d..575981b 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -3900,11 +3900,12 @@ static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20 } /* Build an index that contains the just the files needed for a 3way merge */ -static void build_fake_ancestor(struct patch *list, const char *filename) +static int build_fake_ancestor(struct patch *list, const char *filename) { struct patch *patch; struct index_state result = { NULL }; static struct lock_file lock; + int res; /* Once we start supporting the reverse patch, it may be * worth showing the new sha1 prefix, but until then... @@ -3922,31 +3923,38 @@ static void build_fake_ancestor(struct patch *list, const char *filename) if (!preimage_sha1_in_gitlink_patch(patch, sha1)) ; /* ok, the textual part looks sane */ else - die("sha1 information is lacking or useless for submodule %s", - name); + return error("sha1 information is lacking or " + "useless for submodule %s", name); } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) { ; /* ok */ } else if (!patch->lines_added && !patch->lines_deleted) { /* mode-only change: update the current */ if (get_current_sha1(patch->old_name, sha1)) - die("mode change for %s, which is not " - "in current HEAD", name); + return error("mode change for %s, which is not " + "in current HEAD", name); } else - die("sha1 information is lacking or useless " - "(%s).", name); + return error("sha1 information is lacking or useless " + "(%s).", name); ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0); if (!ce) - die(_("make_cache_entry failed for path '%s'"), name); - if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) - die ("Could not add %s to temporary index", name); + return error(_("make_cache_entry failed for path '%s'"), + name); + if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) { + free(ce); + return error("Could not add %s to temporary index", + name); + } } hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR); - if (write_locked_index(&result, &lock, COMMIT_LOCK)) - die ("Could not write temporary index to %s", filename); - + res = write_locked_index(&result, &lock, COMMIT_LOCK); discard_index(&result); + + if (res) + return error("Could not write temporary index to %s", filename); + + return 0; } static void stat_patch_list(struct apply_state *state, struct patch *patch) @@ -4495,8 +4503,11 @@ static int apply_patch(struct apply_state *state, goto end; } - if (state->fake_ancestor) - build_fake_ancestor(list, state->fake_ancestor); + if (state->fake_ancestor && + build_fake_ancestor(list, state->fake_ancestor)) { + res = -128; + goto end; + } if (state->diffstat) stat_patch_list(state, list); -- cgit v0.10.2-6-g49f6 From 6e8df314692105e7d3e69f548440e4b817bf3211 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:17 +0200 Subject: builtin/apply: make remove_file() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", remove_file() should return -1 instead of calling die(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 575981b..27fb6e2 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4085,17 +4085,18 @@ static void patch_stats(struct apply_state *state, struct patch *patch) } } -static void remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) +static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) { if (state->update_index) { if (remove_file_from_cache(patch->old_name) < 0) - die(_("unable to remove %s from index"), patch->old_name); + return error(_("unable to remove %s from index"), patch->old_name); } if (!state->cached) { if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { remove_path(patch->old_name); } } + return 0; } static void add_index_file(struct apply_state *state, @@ -4274,8 +4275,10 @@ static void write_out_one_result(struct apply_state *state, int phase) { if (patch->is_delete > 0) { - if (phase == 0) - remove_file(state, patch, 1); + if (phase == 0) { + if (remove_file(state, patch, 1)) + exit(128); + } return; } if (patch->is_new > 0 || patch->is_copy) { @@ -4287,8 +4290,10 @@ static void write_out_one_result(struct apply_state *state, * Rename or modification boils down to the same * thing: remove the old, write the new */ - if (phase == 0) - remove_file(state, patch, patch->is_rename); + if (phase == 0) { + if (remove_file(state, patch, patch->is_rename)) + exit(128); + } if (phase == 1) create_file(state, patch); } -- cgit v0.10.2-6-g49f6 From a902edceebd0a25a307163f050326bda8f494204 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:18 +0200 Subject: builtin/apply: make add_conflicted_stages_file() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", add_conflicted_stages_file() should return -1 instead of calling die(). Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 27fb6e2..ad0b875 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4224,7 +4224,7 @@ static void create_one_file(struct apply_state *state, die_errno(_("unable to write file '%s' mode %o"), path, mode); } -static void add_conflicted_stages_file(struct apply_state *state, +static int add_conflicted_stages_file(struct apply_state *state, struct patch *patch) { int stage, namelen; @@ -4232,7 +4232,7 @@ static void add_conflicted_stages_file(struct apply_state *state, struct cache_entry *ce; if (!state->update_index) - return; + return 0; namelen = strlen(patch->new_name); ce_size = cache_entry_size(namelen); mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644); @@ -4247,9 +4247,14 @@ static void add_conflicted_stages_file(struct apply_state *state, ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = namelen; hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash); - if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) - die(_("unable to add cache entry for %s"), patch->new_name); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { + free(ce); + return error(_("unable to add cache entry for %s"), + patch->new_name); + } } + + return 0; } static void create_file(struct apply_state *state, struct patch *patch) @@ -4263,9 +4268,10 @@ static void create_file(struct apply_state *state, struct patch *patch) mode = S_IFREG | 0644; create_one_file(state, path, mode, buf, size); - if (patch->conflicted_threeway) - add_conflicted_stages_file(state, patch); - else + if (patch->conflicted_threeway) { + if (add_conflicted_stages_file(state, patch)) + exit(128); + } else add_index_file(state, path, mode, buf, size); } -- cgit v0.10.2-6-g49f6 From 69e1609f812d5accc1b54f737faba354f5bd85db Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:19 +0200 Subject: builtin/apply: make add_index_file() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", add_index_file() should return -1 instead of calling die(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index ad0b875..a646900 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4099,11 +4099,11 @@ static int remove_file(struct apply_state *state, struct patch *patch, int rmdir return 0; } -static void add_index_file(struct apply_state *state, - const char *path, - unsigned mode, - void *buf, - unsigned long size) +static int add_index_file(struct apply_state *state, + const char *path, + unsigned mode, + void *buf, + unsigned long size) { struct stat st; struct cache_entry *ce; @@ -4111,7 +4111,7 @@ static void add_index_file(struct apply_state *state, unsigned ce_size = cache_entry_size(namelen); if (!state->update_index) - return; + return 0; ce = xcalloc(1, ce_size); memcpy(ce->name, path, namelen); @@ -4122,20 +4122,32 @@ static void add_index_file(struct apply_state *state, const char *s; if (!skip_prefix(buf, "Subproject commit ", &s) || - get_sha1_hex(s, ce->sha1)) - die(_("corrupt patch for submodule %s"), path); + get_sha1_hex(s, ce->sha1)) { + free(ce); + return error(_("corrupt patch for submodule %s"), path); + } } else { if (!state->cached) { - if (lstat(path, &st) < 0) - die_errno(_("unable to stat newly created file '%s'"), - path); + if (lstat(path, &st) < 0) { + free(ce); + return error(_("unable to stat newly " + "created file '%s': %s"), + path, strerror(errno)); + } fill_stat_cache_info(ce, &st); } - if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) - die(_("unable to create backing store for newly created file %s"), path); + if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) { + free(ce); + return error(_("unable to create backing store " + "for newly created file %s"), path); + } } - if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) - die(_("unable to add cache entry for %s"), path); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { + free(ce); + return error(_("unable to add cache entry for %s"), path); + } + + return 0; } static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) @@ -4271,8 +4283,10 @@ static void create_file(struct apply_state *state, struct patch *patch) if (patch->conflicted_threeway) { if (add_conflicted_stages_file(state, patch)) exit(128); - } else - add_index_file(state, path, mode, buf, size); + } else { + if (add_index_file(state, path, mode, buf, size)) + exit(128); + } } /* phase zero is to remove, phase one is to create */ -- cgit v0.10.2-6-g49f6 From 8f5b5445d7c015dc5f2bbf65d23779d355c0c36e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:20 +0200 Subject: builtin/apply: make create_file() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of exit()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", create_file() should just return what add_conflicted_stages_file() and add_index_file() are returning instead of calling exit(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index a646900..fdfeab0 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4269,7 +4269,7 @@ static int add_conflicted_stages_file(struct apply_state *state, return 0; } -static void create_file(struct apply_state *state, struct patch *patch) +static int create_file(struct apply_state *state, struct patch *patch) { char *path = patch->new_name; unsigned mode = patch->new_mode; @@ -4280,13 +4280,10 @@ static void create_file(struct apply_state *state, struct patch *patch) mode = S_IFREG | 0644; create_one_file(state, path, mode, buf, size); - if (patch->conflicted_threeway) { - if (add_conflicted_stages_file(state, patch)) - exit(128); - } else { - if (add_index_file(state, path, mode, buf, size)) - exit(128); - } + if (patch->conflicted_threeway) + return add_conflicted_stages_file(state, patch); + else + return add_index_file(state, path, mode, buf, size); } /* phase zero is to remove, phase one is to create */ @@ -4302,8 +4299,10 @@ static void write_out_one_result(struct apply_state *state, return; } if (patch->is_new > 0 || patch->is_copy) { - if (phase == 1) - create_file(state, patch); + if (phase == 1) { + if (create_file(state, patch)) + exit(128); + } return; } /* @@ -4314,8 +4313,10 @@ static void write_out_one_result(struct apply_state *state, if (remove_file(state, patch, patch->is_rename)) exit(128); } - if (phase == 1) - create_file(state, patch); + if (phase == 1) { + if (create_file(state, patch)) + exit(128); + } } static int write_out_one_reject(struct apply_state *state, struct patch *patch) -- cgit v0.10.2-6-g49f6 From 434389deb193016464702b25af2b79bb5c6fd098 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:21 +0200 Subject: builtin/apply: make write_out_one_result() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of exit()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", write_out_one_result() should just return what remove_file() and create_file() are returning instead of calling exit(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index fdfeab0..003acec 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4287,36 +4287,29 @@ static int create_file(struct apply_state *state, struct patch *patch) } /* phase zero is to remove, phase one is to create */ -static void write_out_one_result(struct apply_state *state, - struct patch *patch, - int phase) +static int write_out_one_result(struct apply_state *state, + struct patch *patch, + int phase) { if (patch->is_delete > 0) { - if (phase == 0) { - if (remove_file(state, patch, 1)) - exit(128); - } - return; + if (phase == 0) + return remove_file(state, patch, 1); + return 0; } if (patch->is_new > 0 || patch->is_copy) { - if (phase == 1) { - if (create_file(state, patch)) - exit(128); - } - return; + if (phase == 1) + return create_file(state, patch); + return 0; } /* * Rename or modification boils down to the same * thing: remove the old, write the new */ - if (phase == 0) { - if (remove_file(state, patch, patch->is_rename)) - exit(128); - } - if (phase == 1) { - if (create_file(state, patch)) - exit(128); - } + if (phase == 0) + return remove_file(state, patch, patch->is_rename); + if (phase == 1) + return create_file(state, patch); + return 0; } static int write_out_one_reject(struct apply_state *state, struct patch *patch) @@ -4403,7 +4396,8 @@ static int write_out_results(struct apply_state *state, struct patch *list) if (l->rejected) errs = 1; else { - write_out_one_result(state, l, phase); + if (write_out_one_result(state, l, phase)) + exit(128); if (phase == 1) { if (write_out_one_reject(state, l)) errs = 1; -- cgit v0.10.2-6-g49f6 From ccceb7bb13a37b1834bc1c455e40abc710997dd3 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 8 Aug 2016 23:03:22 +0200 Subject: builtin/apply: make write_out_results() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of exit()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", write_out_results() should return -1 instead of calling exit(). Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 003acec..c787ead 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4383,6 +4383,12 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch) return -1; } +/* + * Returns: + * -1 if an error happened + * 0 if the patch applied cleanly + * 1 if the patch did not apply cleanly + */ static int write_out_results(struct apply_state *state, struct patch *list) { int phase; @@ -4396,8 +4402,10 @@ static int write_out_results(struct apply_state *state, struct patch *list) if (l->rejected) errs = 1; else { - if (write_out_one_result(state, l, phase)) - exit(128); + if (write_out_one_result(state, l, phase)) { + string_list_clear(&cpath, 0); + return -1; + } if (phase == 1) { if (write_out_one_reject(state, l)) errs = 1; @@ -4517,10 +4525,17 @@ static int apply_patch(struct apply_state *state, } } - if (state->apply && write_out_results(state, list)) { - /* with --3way, we still need to write the index out */ - res = state->apply_with_reject ? -1 : 1; - goto end; + if (state->apply) { + int write_res = write_out_results(state, list); + if (write_res < 0) { + res = -128; + goto end; + } + if (write_res > 0) { + /* with --3way, we still need to write the index out */ + res = state->apply_with_reject ? -1 : 1; + goto end; + } } if (state->fake_ancestor && -- cgit v0.10.2-6-g49f6 From 739d8a16b5f1fefc42177c4619605c8cddb3a094 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:18 +0200 Subject: builtin/apply: make try_create_file() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of die()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", try_create_file() should return -1 in case of error. Unfortunately try_create_file() currently returns -1 to signal a recoverable error. To fix that, let's make it return 1 in case of a recoverable error and -1 in case of an unrecoverable error. Helped-by: Eric Sunshine Helped-by: Jeff King Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index c787ead..3145e03 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4150,38 +4150,48 @@ static int add_index_file(struct apply_state *state, return 0; } +/* + * Returns: + * -1 if an unrecoverable error happened + * 0 if everything went well + * 1 if a recoverable error happened + */ static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) { - int fd; + int fd, res; struct strbuf nbuf = STRBUF_INIT; if (S_ISGITLINK(mode)) { struct stat st; if (!lstat(path, &st) && S_ISDIR(st.st_mode)) return 0; - return mkdir(path, 0777); + return !!mkdir(path, 0777); } if (has_symlinks && S_ISLNK(mode)) /* Although buf:size is counted string, it also is NUL * terminated. */ - return symlink(buf, path); + return !!symlink(buf, path); fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666); if (fd < 0) - return -1; + return 1; if (convert_to_working_tree(path, buf, size, &nbuf)) { size = nbuf.len; buf = nbuf.buf; } - write_or_die(fd, buf, size); + + res = write_in_full(fd, buf, size) < 0; + if (res) + error_errno(_("failed to write to '%s'"), path); strbuf_release(&nbuf); - if (close(fd) < 0) - die_errno(_("closing file '%s'"), path); - return 0; + if (close(fd) < 0 && !res) + return error_errno(_("closing file '%s'"), path); + + return res ? -1 : 0; } /* @@ -4195,15 +4205,24 @@ static void create_one_file(struct apply_state *state, const char *buf, unsigned long size) { + int res; + if (state->cached) return; - if (!try_create_file(path, mode, buf, size)) + + res = try_create_file(path, mode, buf, size); + if (res < 0) + exit(128); + if (!res) return; if (errno == ENOENT) { if (safe_create_leading_directories(path)) return; - if (!try_create_file(path, mode, buf, size)) + res = try_create_file(path, mode, buf, size); + if (res < 0) + exit(128); + if (!res) return; } @@ -4222,7 +4241,10 @@ static void create_one_file(struct apply_state *state, for (;;) { char newpath[PATH_MAX]; mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr); - if (!try_create_file(newpath, mode, buf, size)) { + res = try_create_file(newpath, mode, buf, size); + if (res < 0) + exit(128); + if (!res) { if (!rename(newpath, path)) return; unlink_or_warn(newpath); -- cgit v0.10.2-6-g49f6 From 603752a88df398cbe6cad449b9fbd49aa28dfa20 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:19 +0200 Subject: builtin/apply: make create_one_file() return -1 on error To libify `git apply` functionality we have to signal errors to the caller instead of exit()ing. To do that in a compatible manner with the rest of the error handling in "builtin/apply.c", create_one_file() should return -1 instead of calling exit(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 3145e03..5cd7fd8 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4198,32 +4198,36 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, * We optimistically assume that the directories exist, * which is true 99% of the time anyway. If they don't, * we create them and try again. + * + * Returns: + * -1 on error + * 0 otherwise */ -static void create_one_file(struct apply_state *state, - char *path, - unsigned mode, - const char *buf, - unsigned long size) +static int create_one_file(struct apply_state *state, + char *path, + unsigned mode, + const char *buf, + unsigned long size) { int res; if (state->cached) - return; + return 0; res = try_create_file(path, mode, buf, size); if (res < 0) - exit(128); + return -1; if (!res) - return; + return 0; if (errno == ENOENT) { if (safe_create_leading_directories(path)) - return; + return 0; res = try_create_file(path, mode, buf, size); if (res < 0) - exit(128); + return -1; if (!res) - return; + return 0; } if (errno == EEXIST || errno == EACCES) { @@ -4243,10 +4247,10 @@ static void create_one_file(struct apply_state *state, mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr); res = try_create_file(newpath, mode, buf, size); if (res < 0) - exit(128); + return -1; if (!res) { if (!rename(newpath, path)) - return; + return 0; unlink_or_warn(newpath); break; } @@ -4255,7 +4259,8 @@ static void create_one_file(struct apply_state *state, ++nr; } } - die_errno(_("unable to write file '%s' mode %o"), path, mode); + return error_errno(_("unable to write file '%s' mode %o"), + path, mode); } static int add_conflicted_stages_file(struct apply_state *state, @@ -4300,7 +4305,8 @@ static int create_file(struct apply_state *state, struct patch *patch) if (!mode) mode = S_IFREG | 0644; - create_one_file(state, path, mode, buf, size); + if (create_one_file(state, path, mode, buf, size)) + return -1; if (patch->conflicted_threeway) return add_conflicted_stages_file(state, patch); -- cgit v0.10.2-6-g49f6 From da8e30dcd96f75eac9a343f973198242b44a1ca9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:20 +0200 Subject: builtin/apply: rename option parsing functions As these functions are going to be part of the libified apply API, let's give them a name that is more specific to the apply API. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/apply.c b/builtin/apply.c index 5cd7fd8..1f56303 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4588,16 +4588,16 @@ end: return res; } -static int option_parse_exclude(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_exclude(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; add_name_limit(state, arg, 1); return 0; } -static int option_parse_include(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_include(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; add_name_limit(state, arg, 0); @@ -4605,9 +4605,9 @@ static int option_parse_include(const struct option *opt, return 0; } -static int option_parse_p(const struct option *opt, - const char *arg, - int unset) +static int apply_option_parse_p(const struct option *opt, + const char *arg, + int unset) { struct apply_state *state = opt->value; state->p_value = atoi(arg); @@ -4615,8 +4615,8 @@ static int option_parse_p(const struct option *opt, return 0; } -static int option_parse_space_change(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_space_change(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; if (unset) @@ -4626,8 +4626,8 @@ static int option_parse_space_change(const struct option *opt, return 0; } -static int option_parse_whitespace(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_whitespace(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; state->whitespace_option = arg; @@ -4636,8 +4636,8 @@ static int option_parse_whitespace(const struct option *opt, return 0; } -static int option_parse_directory(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_directory(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; strbuf_reset(&state->root); @@ -4755,13 +4755,13 @@ int cmd_apply(int argc, const char **argv, const char *prefix) struct option builtin_apply_options[] = { { OPTION_CALLBACK, 0, "exclude", &state, N_("path"), N_("don't apply changes matching the given path"), - 0, option_parse_exclude }, + 0, apply_option_parse_exclude }, { OPTION_CALLBACK, 0, "include", &state, N_("path"), N_("apply changes matching the given path"), - 0, option_parse_include }, + 0, apply_option_parse_include }, { OPTION_CALLBACK, 'p', NULL, &state, N_("num"), N_("remove leading slashes from traditional diff paths"), - 0, option_parse_p }, + 0, apply_option_parse_p }, OPT_BOOL(0, "no-add", &state.no_add, N_("ignore additions made by the patch")), OPT_BOOL(0, "stat", &state.diffstat, @@ -4793,13 +4793,13 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("ensure at least lines of context match")), { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"), N_("detect new or modified lines that have whitespace errors"), - 0, option_parse_whitespace }, + 0, apply_option_parse_whitespace }, { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL, N_("ignore changes in whitespace when finding context"), - PARSE_OPT_NOARG, option_parse_space_change }, + PARSE_OPT_NOARG, apply_option_parse_space_change }, { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL, N_("ignore changes in whitespace when finding context"), - PARSE_OPT_NOARG, option_parse_space_change }, + PARSE_OPT_NOARG, apply_option_parse_space_change }, OPT_BOOL('R', "reverse", &state.apply_in_reverse, N_("apply the patch in reverse")), OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero, @@ -4817,7 +4817,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) RECOUNT), { OPTION_CALLBACK, 0, "directory", &state, N_("root"), N_("prepend to all filenames"), - 0, option_parse_directory }, + 0, apply_option_parse_directory }, OPT_END() }; -- cgit v0.10.2-6-g49f6 From 803bf4e012687d92f4c136febe0881852738d57d Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:21 +0200 Subject: apply: rename and move opt constants to apply.h The constants for the "inaccurate-eof" and the "recount" options will be used in both "apply.c" and "builtin/apply.c", so they need to go into "apply.h", and therefore they need a name that is more specific to the API they belong to. Helped-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.h b/apply.h index 53f09b5..48abd8e 100644 --- a/apply.h +++ b/apply.h @@ -108,4 +108,11 @@ extern int init_apply_state(struct apply_state *state, extern void clear_apply_state(struct apply_state *state); extern int check_apply_state(struct apply_state *state, int force_apply); +/* + * Some aspects of the apply behavior are controlled by the following + * bits in the "options" parameter passed to apply_all_patches(). + */ +#define APPLY_OPT_INACCURATE_EOF (1<<0) /* accept inaccurate eof */ +#define APPLY_OPT_RECOUNT (1<<1) /* accept inaccurate line count */ + #endif diff --git a/builtin/apply.c b/builtin/apply.c index 1f56303..da31af2 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4463,9 +4463,6 @@ static int write_out_results(struct apply_state *state, struct patch *list) static struct lock_file lock_file; -#define INACCURATE_EOF (1<<0) -#define RECOUNT (1<<1) - /* * Try to apply a patch. * @@ -4495,8 +4492,8 @@ static int apply_patch(struct apply_state *state, int nr; patch = xcalloc(1, sizeof(*patch)); - patch->inaccurate_eof = !!(options & INACCURATE_EOF); - patch->recount = !!(options & RECOUNT); + patch->inaccurate_eof = !!(options & APPLY_OPT_INACCURATE_EOF); + patch->recount = !!(options & APPLY_OPT_RECOUNT); nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); if (nr < 0) { free_patch(patch); @@ -4811,10 +4808,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix) OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")), OPT_BIT(0, "inaccurate-eof", &options, N_("tolerate incorrectly detected missing new-line at the end of file"), - INACCURATE_EOF), + APPLY_OPT_INACCURATE_EOF), OPT_BIT(0, "recount", &options, N_("do not trust the line counts in the hunk headers"), - RECOUNT), + APPLY_OPT_RECOUNT), { OPTION_CALLBACK, 0, "directory", &state, N_("root"), N_("prepend to all filenames"), 0, apply_option_parse_directory }, -- cgit v0.10.2-6-g49f6 From 13b5af22f39f5e7d952a4c98ffb7ea25053800c1 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 22 Apr 2016 20:55:46 +0200 Subject: apply: move libified code from builtin/apply.c to apply.{c,h} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As most of the apply code in builtin/apply.c has been libified by a number of previous commits, it can now be moved to apply.{c,h}, so that more code can use it. Helped-by: Nguyễn Thái Ngọc Duy Helped-by: Ramsay Jones Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index 2eac3e3..5e3780d 100644 --- a/apply.c +++ b/apply.c @@ -1,5 +1,23 @@ +/* + * apply.c + * + * Copyright (C) Linus Torvalds, 2005 + * + * This applies patches on top of some (arbitrary) version of the SCM. + * + */ + #include "cache.h" +#include "blob.h" +#include "delta.h" +#include "diff.h" +#include "dir.h" +#include "xdiff-interface.h" +#include "ll-merge.h" #include "lockfile.h" +#include "parse-options.h" +#include "quote.h" +#include "rerere.h" #include "apply.h" static void git_apply_config(void) @@ -125,3 +143,4716 @@ int check_apply_state(struct apply_state *state, int force_apply) return 0; } + +static void set_default_whitespace_mode(struct apply_state *state) +{ + if (!state->whitespace_option && !apply_default_whitespace) + state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); +} + +/* + * This represents one "hunk" from a patch, starting with + * "@@ -oldpos,oldlines +newpos,newlines @@" marker. The + * patch text is pointed at by patch, and its byte length + * is stored in size. leading and trailing are the number + * of context lines. + */ +struct fragment { + unsigned long leading, trailing; + unsigned long oldpos, oldlines; + unsigned long newpos, newlines; + /* + * 'patch' is usually borrowed from buf in apply_patch(), + * but some codepaths store an allocated buffer. + */ + const char *patch; + unsigned free_patch:1, + rejected:1; + int size; + int linenr; + struct fragment *next; +}; + +/* + * When dealing with a binary patch, we reuse "leading" field + * to store the type of the binary hunk, either deflated "delta" + * or deflated "literal". + */ +#define binary_patch_method leading +#define BINARY_DELTA_DEFLATED 1 +#define BINARY_LITERAL_DEFLATED 2 + +/* + * This represents a "patch" to a file, both metainfo changes + * such as creation/deletion, filemode and content changes represented + * as a series of fragments. + */ +struct patch { + char *new_name, *old_name, *def_name; + unsigned int old_mode, new_mode; + int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */ + int rejected; + unsigned ws_rule; + int lines_added, lines_deleted; + int score; + unsigned int is_toplevel_relative:1; + unsigned int inaccurate_eof:1; + unsigned int is_binary:1; + unsigned int is_copy:1; + unsigned int is_rename:1; + unsigned int recount:1; + unsigned int conflicted_threeway:1; + unsigned int direct_to_threeway:1; + struct fragment *fragments; + char *result; + size_t resultsize; + char old_sha1_prefix[41]; + char new_sha1_prefix[41]; + struct patch *next; + + /* three-way fallback result */ + struct object_id threeway_stage[3]; +}; + +static void free_fragment_list(struct fragment *list) +{ + while (list) { + struct fragment *next = list->next; + if (list->free_patch) + free((char *)list->patch); + free(list); + list = next; + } +} + +static void free_patch(struct patch *patch) +{ + free_fragment_list(patch->fragments); + free(patch->def_name); + free(patch->old_name); + free(patch->new_name); + free(patch->result); + free(patch); +} + +static void free_patch_list(struct patch *list) +{ + while (list) { + struct patch *next = list->next; + free_patch(list); + list = next; + } +} + +/* + * A line in a file, len-bytes long (includes the terminating LF, + * except for an incomplete line at the end if the file ends with + * one), and its contents hashes to 'hash'. + */ +struct line { + size_t len; + unsigned hash : 24; + unsigned flag : 8; +#define LINE_COMMON 1 +#define LINE_PATCHED 2 +}; + +/* + * This represents a "file", which is an array of "lines". + */ +struct image { + char *buf; + size_t len; + size_t nr; + size_t alloc; + struct line *line_allocated; + struct line *line; +}; + +static uint32_t hash_line(const char *cp, size_t len) +{ + size_t i; + uint32_t h; + for (i = 0, h = 0; i < len; i++) { + if (!isspace(cp[i])) { + h = h * 3 + (cp[i] & 0xff); + } + } + return h; +} + +/* + * Compare lines s1 of length n1 and s2 of length n2, ignoring + * whitespace difference. Returns 1 if they match, 0 otherwise + */ +static int fuzzy_matchlines(const char *s1, size_t n1, + const char *s2, size_t n2) +{ + const char *last1 = s1 + n1 - 1; + const char *last2 = s2 + n2 - 1; + int result = 0; + + /* ignore line endings */ + while ((*last1 == '\r') || (*last1 == '\n')) + last1--; + while ((*last2 == '\r') || (*last2 == '\n')) + last2--; + + /* skip leading whitespaces, if both begin with whitespace */ + if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) { + while (isspace(*s1) && (s1 <= last1)) + s1++; + while (isspace(*s2) && (s2 <= last2)) + s2++; + } + /* early return if both lines are empty */ + if ((s1 > last1) && (s2 > last2)) + return 1; + while (!result) { + result = *s1++ - *s2++; + /* + * Skip whitespace inside. We check for whitespace on + * both buffers because we don't want "a b" to match + * "ab" + */ + if (isspace(*s1) && isspace(*s2)) { + while (isspace(*s1) && s1 <= last1) + s1++; + while (isspace(*s2) && s2 <= last2) + s2++; + } + /* + * If we reached the end on one side only, + * lines don't match + */ + if ( + ((s2 > last2) && (s1 <= last1)) || + ((s1 > last1) && (s2 <= last2))) + return 0; + if ((s1 > last1) && (s2 > last2)) + break; + } + + return !result; +} + +static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag) +{ + ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc); + img->line_allocated[img->nr].len = len; + img->line_allocated[img->nr].hash = hash_line(bol, len); + img->line_allocated[img->nr].flag = flag; + img->nr++; +} + +/* + * "buf" has the file contents to be patched (read from various sources). + * attach it to "image" and add line-based index to it. + * "image" now owns the "buf". + */ +static void prepare_image(struct image *image, char *buf, size_t len, + int prepare_linetable) +{ + const char *cp, *ep; + + memset(image, 0, sizeof(*image)); + image->buf = buf; + image->len = len; + + if (!prepare_linetable) + return; + + ep = image->buf + image->len; + cp = image->buf; + while (cp < ep) { + const char *next; + for (next = cp; next < ep && *next != '\n'; next++) + ; + if (next < ep) + next++; + add_line_info(image, cp, next - cp, 0); + cp = next; + } + image->line = image->line_allocated; +} + +static void clear_image(struct image *image) +{ + free(image->buf); + free(image->line_allocated); + memset(image, 0, sizeof(*image)); +} + +/* fmt must contain _one_ %s and no other substitution */ +static void say_patch_name(FILE *output, const char *fmt, struct patch *patch) +{ + struct strbuf sb = STRBUF_INIT; + + if (patch->old_name && patch->new_name && + strcmp(patch->old_name, patch->new_name)) { + quote_c_style(patch->old_name, &sb, NULL, 0); + strbuf_addstr(&sb, " => "); + quote_c_style(patch->new_name, &sb, NULL, 0); + } else { + const char *n = patch->new_name; + if (!n) + n = patch->old_name; + quote_c_style(n, &sb, NULL, 0); + } + fprintf(output, fmt, sb.buf); + fputc('\n', output); + strbuf_release(&sb); +} + +#define SLOP (16) + +static int read_patch_file(struct strbuf *sb, int fd) +{ + if (strbuf_read(sb, fd, 0) < 0) + return error_errno("git apply: failed to read"); + + /* + * Make sure that we have some slop in the buffer + * so that we can do speculative "memcmp" etc, and + * see to it that it is NUL-filled. + */ + strbuf_grow(sb, SLOP); + memset(sb->buf + sb->len, 0, SLOP); + return 0; +} + +static unsigned long linelen(const char *buffer, unsigned long size) +{ + unsigned long len = 0; + while (size--) { + len++; + if (*buffer++ == '\n') + break; + } + return len; +} + +static int is_dev_null(const char *str) +{ + return skip_prefix(str, "/dev/null", &str) && isspace(*str); +} + +#define TERM_SPACE 1 +#define TERM_TAB 2 + +static int name_terminate(int c, int terminate) +{ + if (c == ' ' && !(terminate & TERM_SPACE)) + return 0; + if (c == '\t' && !(terminate & TERM_TAB)) + return 0; + + return 1; +} + +/* remove double slashes to make --index work with such filenames */ +static char *squash_slash(char *name) +{ + int i = 0, j = 0; + + if (!name) + return NULL; + + while (name[i]) { + if ((name[j++] = name[i++]) == '/') + while (name[i] == '/') + i++; + } + name[j] = '\0'; + return name; +} + +static char *find_name_gnu(struct apply_state *state, + const char *line, + const char *def, + int p_value) +{ + struct strbuf name = STRBUF_INIT; + char *cp; + + /* + * Proposed "new-style" GNU patch/diff format; see + * http://marc.info/?l=git&m=112927316408690&w=2 + */ + if (unquote_c_style(&name, line, NULL)) { + strbuf_release(&name); + return NULL; + } + + for (cp = name.buf; p_value; p_value--) { + cp = strchr(cp, '/'); + if (!cp) { + strbuf_release(&name); + return NULL; + } + cp++; + } + + strbuf_remove(&name, 0, cp - name.buf); + if (state->root.len) + strbuf_insert(&name, 0, state->root.buf, state->root.len); + return squash_slash(strbuf_detach(&name, NULL)); +} + +static size_t sane_tz_len(const char *line, size_t len) +{ + const char *tz, *p; + + if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ') + return 0; + tz = line + len - strlen(" +0500"); + + if (tz[1] != '+' && tz[1] != '-') + return 0; + + for (p = tz + 2; p != line + len; p++) + if (!isdigit(*p)) + return 0; + + return line + len - tz; +} + +static size_t tz_with_colon_len(const char *line, size_t len) +{ + const char *tz, *p; + + if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':') + return 0; + tz = line + len - strlen(" +08:00"); + + if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-')) + return 0; + p = tz + 2; + if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || + !isdigit(*p++) || !isdigit(*p++)) + return 0; + + return line + len - tz; +} + +static size_t date_len(const char *line, size_t len) +{ + const char *date, *p; + + if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-') + return 0; + p = date = line + len - strlen("72-02-05"); + + if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' || + !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' || + !isdigit(*p++) || !isdigit(*p++)) /* Not a date. */ + return 0; + + if (date - line >= strlen("19") && + isdigit(date[-1]) && isdigit(date[-2])) /* 4-digit year */ + date -= strlen("19"); + + return line + len - date; +} + +static size_t short_time_len(const char *line, size_t len) +{ + const char *time, *p; + + if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':') + return 0; + p = time = line + len - strlen(" 07:01:32"); + + /* Permit 1-digit hours? */ + if (*p++ != ' ' || + !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || + !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || + !isdigit(*p++) || !isdigit(*p++)) /* Not a time. */ + return 0; + + return line + len - time; +} + +static size_t fractional_time_len(const char *line, size_t len) +{ + const char *p; + size_t n; + + /* Expected format: 19:41:17.620000023 */ + if (!len || !isdigit(line[len - 1])) + return 0; + p = line + len - 1; + + /* Fractional seconds. */ + while (p > line && isdigit(*p)) + p--; + if (*p != '.') + return 0; + + /* Hours, minutes, and whole seconds. */ + n = short_time_len(line, p - line); + if (!n) + return 0; + + return line + len - p + n; +} + +static size_t trailing_spaces_len(const char *line, size_t len) +{ + const char *p; + + /* Expected format: ' ' x (1 or more) */ + if (!len || line[len - 1] != ' ') + return 0; + + p = line + len; + while (p != line) { + p--; + if (*p != ' ') + return line + len - (p + 1); + } + + /* All spaces! */ + return len; +} + +static size_t diff_timestamp_len(const char *line, size_t len) +{ + const char *end = line + len; + size_t n; + + /* + * Posix: 2010-07-05 19:41:17 + * GNU: 2010-07-05 19:41:17.620000023 -0500 + */ + + if (!isdigit(end[-1])) + return 0; + + n = sane_tz_len(line, end - line); + if (!n) + n = tz_with_colon_len(line, end - line); + end -= n; + + n = short_time_len(line, end - line); + if (!n) + n = fractional_time_len(line, end - line); + end -= n; + + n = date_len(line, end - line); + if (!n) /* No date. Too bad. */ + return 0; + end -= n; + + if (end == line) /* No space before date. */ + return 0; + if (end[-1] == '\t') { /* Success! */ + end--; + return line + len - end; + } + if (end[-1] != ' ') /* No space before date. */ + return 0; + + /* Whitespace damage. */ + end -= trailing_spaces_len(line, end - line); + return line + len - end; +} + +static char *find_name_common(struct apply_state *state, + const char *line, + const char *def, + int p_value, + const char *end, + int terminate) +{ + int len; + const char *start = NULL; + + if (p_value == 0) + start = line; + while (line != end) { + char c = *line; + + if (!end && isspace(c)) { + if (c == '\n') + break; + if (name_terminate(c, terminate)) + break; + } + line++; + if (c == '/' && !--p_value) + start = line; + } + if (!start) + return squash_slash(xstrdup_or_null(def)); + len = line - start; + if (!len) + return squash_slash(xstrdup_or_null(def)); + + /* + * Generally we prefer the shorter name, especially + * if the other one is just a variation of that with + * something else tacked on to the end (ie "file.orig" + * or "file~"). + */ + if (def) { + int deflen = strlen(def); + if (deflen < len && !strncmp(start, def, deflen)) + return squash_slash(xstrdup(def)); + } + + if (state->root.len) { + char *ret = xstrfmt("%s%.*s", state->root.buf, len, start); + return squash_slash(ret); + } + + return squash_slash(xmemdupz(start, len)); +} + +static char *find_name(struct apply_state *state, + const char *line, + char *def, + int p_value, + int terminate) +{ + if (*line == '"') { + char *name = find_name_gnu(state, line, def, p_value); + if (name) + return name; + } + + return find_name_common(state, line, def, p_value, NULL, terminate); +} + +static char *find_name_traditional(struct apply_state *state, + const char *line, + char *def, + int p_value) +{ + size_t len; + size_t date_len; + + if (*line == '"') { + char *name = find_name_gnu(state, line, def, p_value); + if (name) + return name; + } + + len = strchrnul(line, '\n') - line; + date_len = diff_timestamp_len(line, len); + if (!date_len) + return find_name_common(state, line, def, p_value, NULL, TERM_TAB); + len -= date_len; + + return find_name_common(state, line, def, p_value, line + len, 0); +} + +static int count_slashes(const char *cp) +{ + int cnt = 0; + char ch; + + while ((ch = *cp++)) + if (ch == '/') + cnt++; + return cnt; +} + +/* + * Given the string after "--- " or "+++ ", guess the appropriate + * p_value for the given patch. + */ +static int guess_p_value(struct apply_state *state, const char *nameline) +{ + char *name, *cp; + int val = -1; + + if (is_dev_null(nameline)) + return -1; + name = find_name_traditional(state, nameline, NULL, 0); + if (!name) + return -1; + cp = strchr(name, '/'); + if (!cp) + val = 0; + else if (state->prefix) { + /* + * Does it begin with "a/$our-prefix" and such? Then this is + * very likely to apply to our directory. + */ + if (!strncmp(name, state->prefix, state->prefix_length)) + val = count_slashes(state->prefix); + else { + cp++; + if (!strncmp(cp, state->prefix, state->prefix_length)) + val = count_slashes(state->prefix) + 1; + } + } + free(name); + return val; +} + +/* + * Does the ---/+++ line have the POSIX timestamp after the last HT? + * GNU diff puts epoch there to signal a creation/deletion event. Is + * this such a timestamp? + */ +static int has_epoch_timestamp(const char *nameline) +{ + /* + * We are only interested in epoch timestamp; any non-zero + * fraction cannot be one, hence "(\.0+)?" in the regexp below. + * For the same reason, the date must be either 1969-12-31 or + * 1970-01-01, and the seconds part must be "00". + */ + const char stamp_regexp[] = + "^(1969-12-31|1970-01-01)" + " " + "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?" + " " + "([-+][0-2][0-9]:?[0-5][0-9])\n"; + const char *timestamp = NULL, *cp, *colon; + static regex_t *stamp; + regmatch_t m[10]; + int zoneoffset; + int hourminute; + int status; + + for (cp = nameline; *cp != '\n'; cp++) { + if (*cp == '\t') + timestamp = cp + 1; + } + if (!timestamp) + return 0; + if (!stamp) { + stamp = xmalloc(sizeof(*stamp)); + if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) { + warning(_("Cannot prepare timestamp regexp %s"), + stamp_regexp); + return 0; + } + } + + status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0); + if (status) { + if (status != REG_NOMATCH) + warning(_("regexec returned %d for input: %s"), + status, timestamp); + return 0; + } + + zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10); + if (*colon == ':') + zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10); + else + zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100); + if (timestamp[m[3].rm_so] == '-') + zoneoffset = -zoneoffset; + + /* + * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31 + * (west of GMT) or 1970-01-01 (east of GMT) + */ + if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) || + (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10))) + return 0; + + hourminute = (strtol(timestamp + 11, NULL, 10) * 60 + + strtol(timestamp + 14, NULL, 10) - + zoneoffset); + + return ((zoneoffset < 0 && hourminute == 1440) || + (0 <= zoneoffset && !hourminute)); +} + +/* + * Get the name etc info from the ---/+++ lines of a traditional patch header + * + * FIXME! The end-of-filename heuristics are kind of screwy. For existing + * files, we can happily check the index for a match, but for creating a + * new file we should try to match whatever "patch" does. I have no idea. + */ +static int parse_traditional_patch(struct apply_state *state, + const char *first, + const char *second, + struct patch *patch) +{ + char *name; + + first += 4; /* skip "--- " */ + second += 4; /* skip "+++ " */ + if (!state->p_value_known) { + int p, q; + p = guess_p_value(state, first); + q = guess_p_value(state, second); + if (p < 0) p = q; + if (0 <= p && p == q) { + state->p_value = p; + state->p_value_known = 1; + } + } + if (is_dev_null(first)) { + patch->is_new = 1; + patch->is_delete = 0; + name = find_name_traditional(state, second, NULL, state->p_value); + patch->new_name = name; + } else if (is_dev_null(second)) { + patch->is_new = 0; + patch->is_delete = 1; + name = find_name_traditional(state, first, NULL, state->p_value); + patch->old_name = name; + } else { + char *first_name; + first_name = find_name_traditional(state, first, NULL, state->p_value); + name = find_name_traditional(state, second, first_name, state->p_value); + free(first_name); + if (has_epoch_timestamp(first)) { + patch->is_new = 1; + patch->is_delete = 0; + patch->new_name = name; + } else if (has_epoch_timestamp(second)) { + patch->is_new = 0; + patch->is_delete = 1; + patch->old_name = name; + } else { + patch->old_name = name; + patch->new_name = xstrdup_or_null(name); + } + } + if (!name) + return error(_("unable to find filename in patch at line %d"), state->linenr); + + return 0; +} + +static int gitdiff_hdrend(struct apply_state *state, + const char *line, + struct patch *patch) +{ + return 1; +} + +/* + * We're anal about diff header consistency, to make + * sure that we don't end up having strange ambiguous + * patches floating around. + * + * As a result, gitdiff_{old|new}name() will check + * their names against any previous information, just + * to make sure.. + */ +#define DIFF_OLD_NAME 0 +#define DIFF_NEW_NAME 1 + +static int gitdiff_verify_name(struct apply_state *state, + const char *line, + int isnull, + char **name, + int side) +{ + if (!*name && !isnull) { + *name = find_name(state, line, NULL, state->p_value, TERM_TAB); + return 0; + } + + if (*name) { + int len = strlen(*name); + char *another; + if (isnull) + return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), + *name, state->linenr); + another = find_name(state, line, NULL, state->p_value, TERM_TAB); + if (!another || memcmp(another, *name, len + 1)) { + free(another); + return error((side == DIFF_NEW_NAME) ? + _("git apply: bad git-diff - inconsistent new filename on line %d") : + _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr); + } + free(another); + } else { + /* expect "/dev/null" */ + if (memcmp("/dev/null", line, 9) || line[9] != '\n') + return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); + } + + return 0; +} + +static int gitdiff_oldname(struct apply_state *state, + const char *line, + struct patch *patch) +{ + return gitdiff_verify_name(state, line, + patch->is_new, &patch->old_name, + DIFF_OLD_NAME); +} + +static int gitdiff_newname(struct apply_state *state, + const char *line, + struct patch *patch) +{ + return gitdiff_verify_name(state, line, + patch->is_delete, &patch->new_name, + DIFF_NEW_NAME); +} + +static int gitdiff_oldmode(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->old_mode = strtoul(line, NULL, 8); + return 0; +} + +static int gitdiff_newmode(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->new_mode = strtoul(line, NULL, 8); + return 0; +} + +static int gitdiff_delete(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->is_delete = 1; + free(patch->old_name); + patch->old_name = xstrdup_or_null(patch->def_name); + return gitdiff_oldmode(state, line, patch); +} + +static int gitdiff_newfile(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->is_new = 1; + free(patch->new_name); + patch->new_name = xstrdup_or_null(patch->def_name); + return gitdiff_newmode(state, line, patch); +} + +static int gitdiff_copysrc(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->is_copy = 1; + free(patch->old_name); + patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + return 0; +} + +static int gitdiff_copydst(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->is_copy = 1; + free(patch->new_name); + patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + return 0; +} + +static int gitdiff_renamesrc(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->is_rename = 1; + free(patch->old_name); + patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + return 0; +} + +static int gitdiff_renamedst(struct apply_state *state, + const char *line, + struct patch *patch) +{ + patch->is_rename = 1; + free(patch->new_name); + patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + return 0; +} + +static int gitdiff_similarity(struct apply_state *state, + const char *line, + struct patch *patch) +{ + unsigned long val = strtoul(line, NULL, 10); + if (val <= 100) + patch->score = val; + return 0; +} + +static int gitdiff_dissimilarity(struct apply_state *state, + const char *line, + struct patch *patch) +{ + unsigned long val = strtoul(line, NULL, 10); + if (val <= 100) + patch->score = val; + return 0; +} + +static int gitdiff_index(struct apply_state *state, + const char *line, + struct patch *patch) +{ + /* + * index line is N hexadecimal, "..", N hexadecimal, + * and optional space with octal mode. + */ + const char *ptr, *eol; + int len; + + ptr = strchr(line, '.'); + if (!ptr || ptr[1] != '.' || 40 < ptr - line) + return 0; + len = ptr - line; + memcpy(patch->old_sha1_prefix, line, len); + patch->old_sha1_prefix[len] = 0; + + line = ptr + 2; + ptr = strchr(line, ' '); + eol = strchrnul(line, '\n'); + + if (!ptr || eol < ptr) + ptr = eol; + len = ptr - line; + + if (40 < len) + return 0; + memcpy(patch->new_sha1_prefix, line, len); + patch->new_sha1_prefix[len] = 0; + if (*ptr == ' ') + patch->old_mode = strtoul(ptr+1, NULL, 8); + return 0; +} + +/* + * This is normal for a diff that doesn't change anything: we'll fall through + * into the next diff. Tell the parser to break out. + */ +static int gitdiff_unrecognized(struct apply_state *state, + const char *line, + struct patch *patch) +{ + return 1; +} + +/* + * Skip p_value leading components from "line"; as we do not accept + * absolute paths, return NULL in that case. + */ +static const char *skip_tree_prefix(struct apply_state *state, + const char *line, + int llen) +{ + int nslash; + int i; + + if (!state->p_value) + return (llen && line[0] == '/') ? NULL : line; + + nslash = state->p_value; + for (i = 0; i < llen; i++) { + int ch = line[i]; + if (ch == '/' && --nslash <= 0) + return (i == 0) ? NULL : &line[i + 1]; + } + return NULL; +} + +/* + * This is to extract the same name that appears on "diff --git" + * line. We do not find and return anything if it is a rename + * patch, and it is OK because we will find the name elsewhere. + * We need to reliably find name only when it is mode-change only, + * creation or deletion of an empty file. In any of these cases, + * both sides are the same name under a/ and b/ respectively. + */ +static char *git_header_name(struct apply_state *state, + const char *line, + int llen) +{ + const char *name; + const char *second = NULL; + size_t len, line_len; + + line += strlen("diff --git "); + llen -= strlen("diff --git "); + + if (*line == '"') { + const char *cp; + struct strbuf first = STRBUF_INIT; + struct strbuf sp = STRBUF_INIT; + + if (unquote_c_style(&first, line, &second)) + goto free_and_fail1; + + /* strip the a/b prefix including trailing slash */ + cp = skip_tree_prefix(state, first.buf, first.len); + if (!cp) + goto free_and_fail1; + strbuf_remove(&first, 0, cp - first.buf); + + /* + * second points at one past closing dq of name. + * find the second name. + */ + while ((second < line + llen) && isspace(*second)) + second++; + + if (line + llen <= second) + goto free_and_fail1; + if (*second == '"') { + if (unquote_c_style(&sp, second, NULL)) + goto free_and_fail1; + cp = skip_tree_prefix(state, sp.buf, sp.len); + if (!cp) + goto free_and_fail1; + /* They must match, otherwise ignore */ + if (strcmp(cp, first.buf)) + goto free_and_fail1; + strbuf_release(&sp); + return strbuf_detach(&first, NULL); + } + + /* unquoted second */ + cp = skip_tree_prefix(state, second, line + llen - second); + if (!cp) + goto free_and_fail1; + if (line + llen - cp != first.len || + memcmp(first.buf, cp, first.len)) + goto free_and_fail1; + return strbuf_detach(&first, NULL); + + free_and_fail1: + strbuf_release(&first); + strbuf_release(&sp); + return NULL; + } + + /* unquoted first name */ + name = skip_tree_prefix(state, line, llen); + if (!name) + return NULL; + + /* + * since the first name is unquoted, a dq if exists must be + * the beginning of the second name. + */ + for (second = name; second < line + llen; second++) { + if (*second == '"') { + struct strbuf sp = STRBUF_INIT; + const char *np; + + if (unquote_c_style(&sp, second, NULL)) + goto free_and_fail2; + + np = skip_tree_prefix(state, sp.buf, sp.len); + if (!np) + goto free_and_fail2; + + len = sp.buf + sp.len - np; + if (len < second - name && + !strncmp(np, name, len) && + isspace(name[len])) { + /* Good */ + strbuf_remove(&sp, 0, np - sp.buf); + return strbuf_detach(&sp, NULL); + } + + free_and_fail2: + strbuf_release(&sp); + return NULL; + } + } + + /* + * Accept a name only if it shows up twice, exactly the same + * form. + */ + second = strchr(name, '\n'); + if (!second) + return NULL; + line_len = second - name; + for (len = 0 ; ; len++) { + switch (name[len]) { + default: + continue; + case '\n': + return NULL; + case '\t': case ' ': + /* + * Is this the separator between the preimage + * and the postimage pathname? Again, we are + * only interested in the case where there is + * no rename, as this is only to set def_name + * and a rename patch has the names elsewhere + * in an unambiguous form. + */ + if (!name[len + 1]) + return NULL; /* no postimage name */ + second = skip_tree_prefix(state, name + len + 1, + line_len - (len + 1)); + if (!second) + return NULL; + /* + * Does len bytes starting at "name" and "second" + * (that are separated by one HT or SP we just + * found) exactly match? + */ + if (second[len] == '\n' && !strncmp(name, second, len)) + return xmemdupz(name, len); + } + } +} + +/* Verify that we recognize the lines following a git header */ +static int parse_git_header(struct apply_state *state, + const char *line, + int len, + unsigned int size, + struct patch *patch) +{ + unsigned long offset; + + /* A git diff has explicit new/delete information, so we don't guess */ + patch->is_new = 0; + patch->is_delete = 0; + + /* + * Some things may not have the old name in the + * rest of the headers anywhere (pure mode changes, + * or removing or adding empty files), so we get + * the default name from the header. + */ + patch->def_name = git_header_name(state, line, len); + if (patch->def_name && state->root.len) { + char *s = xstrfmt("%s%s", state->root.buf, patch->def_name); + free(patch->def_name); + patch->def_name = s; + } + + line += len; + size -= len; + state->linenr++; + for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) { + static const struct opentry { + const char *str; + int (*fn)(struct apply_state *, const char *, struct patch *); + } optable[] = { + { "@@ -", gitdiff_hdrend }, + { "--- ", gitdiff_oldname }, + { "+++ ", gitdiff_newname }, + { "old mode ", gitdiff_oldmode }, + { "new mode ", gitdiff_newmode }, + { "deleted file mode ", gitdiff_delete }, + { "new file mode ", gitdiff_newfile }, + { "copy from ", gitdiff_copysrc }, + { "copy to ", gitdiff_copydst }, + { "rename old ", gitdiff_renamesrc }, + { "rename new ", gitdiff_renamedst }, + { "rename from ", gitdiff_renamesrc }, + { "rename to ", gitdiff_renamedst }, + { "similarity index ", gitdiff_similarity }, + { "dissimilarity index ", gitdiff_dissimilarity }, + { "index ", gitdiff_index }, + { "", gitdiff_unrecognized }, + }; + int i; + + len = linelen(line, size); + if (!len || line[len-1] != '\n') + break; + for (i = 0; i < ARRAY_SIZE(optable); i++) { + const struct opentry *p = optable + i; + int oplen = strlen(p->str); + int res; + if (len < oplen || memcmp(p->str, line, oplen)) + continue; + res = p->fn(state, line + oplen, patch); + if (res < 0) + return -1; + if (res > 0) + return offset; + break; + } + } + + return offset; +} + +static int parse_num(const char *line, unsigned long *p) +{ + char *ptr; + + if (!isdigit(*line)) + return 0; + *p = strtoul(line, &ptr, 10); + return ptr - line; +} + +static int parse_range(const char *line, int len, int offset, const char *expect, + unsigned long *p1, unsigned long *p2) +{ + int digits, ex; + + if (offset < 0 || offset >= len) + return -1; + line += offset; + len -= offset; + + digits = parse_num(line, p1); + if (!digits) + return -1; + + offset += digits; + line += digits; + len -= digits; + + *p2 = 1; + if (*line == ',') { + digits = parse_num(line+1, p2); + if (!digits) + return -1; + + offset += digits+1; + line += digits+1; + len -= digits+1; + } + + ex = strlen(expect); + if (ex > len) + return -1; + if (memcmp(line, expect, ex)) + return -1; + + return offset + ex; +} + +static void recount_diff(const char *line, int size, struct fragment *fragment) +{ + int oldlines = 0, newlines = 0, ret = 0; + + if (size < 1) { + warning("recount: ignore empty hunk"); + return; + } + + for (;;) { + int len = linelen(line, size); + size -= len; + line += len; + + if (size < 1) + break; + + switch (*line) { + case ' ': case '\n': + newlines++; + /* fall through */ + case '-': + oldlines++; + continue; + case '+': + newlines++; + continue; + case '\\': + continue; + case '@': + ret = size < 3 || !starts_with(line, "@@ "); + break; + case 'd': + ret = size < 5 || !starts_with(line, "diff "); + break; + default: + ret = -1; + break; + } + if (ret) { + warning(_("recount: unexpected line: %.*s"), + (int)linelen(line, size), line); + return; + } + break; + } + fragment->oldlines = oldlines; + fragment->newlines = newlines; +} + +/* + * Parse a unified diff fragment header of the + * form "@@ -a,b +c,d @@" + */ +static int parse_fragment_header(const char *line, int len, struct fragment *fragment) +{ + int offset; + + if (!len || line[len-1] != '\n') + return -1; + + /* Figure out the number of lines in a fragment */ + offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines); + offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines); + + return offset; +} + +/* + * Find file diff header + * + * Returns: + * -1 if no header was found + * -128 in case of error + * the size of the header in bytes (called "offset") otherwise + */ +static int find_header(struct apply_state *state, + const char *line, + unsigned long size, + int *hdrsize, + struct patch *patch) +{ + unsigned long offset, len; + + patch->is_toplevel_relative = 0; + patch->is_rename = patch->is_copy = 0; + patch->is_new = patch->is_delete = -1; + patch->old_mode = patch->new_mode = 0; + patch->old_name = patch->new_name = NULL; + for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) { + unsigned long nextlen; + + len = linelen(line, size); + if (!len) + break; + + /* Testing this early allows us to take a few shortcuts.. */ + if (len < 6) + continue; + + /* + * Make sure we don't find any unconnected patch fragments. + * That's a sign that we didn't find a header, and that a + * patch has become corrupted/broken up. + */ + if (!memcmp("@@ -", line, 4)) { + struct fragment dummy; + if (parse_fragment_header(line, len, &dummy) < 0) + continue; + error(_("patch fragment without header at line %d: %.*s"), + state->linenr, (int)len-1, line); + return -128; + } + + if (size < len + 6) + break; + + /* + * Git patch? It might not have a real patch, just a rename + * or mode change, so we handle that specially + */ + if (!memcmp("diff --git ", line, 11)) { + int git_hdr_len = parse_git_header(state, line, len, size, patch); + if (git_hdr_len < 0) + return -128; + if (git_hdr_len <= len) + continue; + if (!patch->old_name && !patch->new_name) { + if (!patch->def_name) { + error(Q_("git diff header lacks filename information when removing " + "%d leading pathname component (line %d)", + "git diff header lacks filename information when removing " + "%d leading pathname components (line %d)", + state->p_value), + state->p_value, state->linenr); + return -128; + } + patch->old_name = xstrdup(patch->def_name); + patch->new_name = xstrdup(patch->def_name); + } + if (!patch->is_delete && !patch->new_name) { + error("git diff header lacks filename information " + "(line %d)", state->linenr); + return -128; + } + patch->is_toplevel_relative = 1; + *hdrsize = git_hdr_len; + return offset; + } + + /* --- followed by +++ ? */ + if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4)) + continue; + + /* + * We only accept unified patches, so we want it to + * at least have "@@ -a,b +c,d @@\n", which is 14 chars + * minimum ("@@ -0,0 +1 @@\n" is the shortest). + */ + nextlen = linelen(line + len, size - len); + if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4)) + continue; + + /* Ok, we'll consider it a patch */ + if (parse_traditional_patch(state, line, line+len, patch)) + return -128; + *hdrsize = len + nextlen; + state->linenr += 2; + return offset; + } + return -1; +} + +static void record_ws_error(struct apply_state *state, + unsigned result, + const char *line, + int len, + int linenr) +{ + char *err; + + if (!result) + return; + + state->whitespace_error++; + if (state->squelch_whitespace_errors && + state->squelch_whitespace_errors < state->whitespace_error) + return; + + err = whitespace_error_string(result); + fprintf(stderr, "%s:%d: %s.\n%.*s\n", + state->patch_input_file, linenr, err, len, line); + free(err); +} + +static void check_whitespace(struct apply_state *state, + const char *line, + int len, + unsigned ws_rule) +{ + unsigned result = ws_check(line + 1, len - 1, ws_rule); + + record_ws_error(state, result, line + 1, len - 2, state->linenr); +} + +/* + * Parse a unified diff. Note that this really needs to parse each + * fragment separately, since the only way to know the difference + * between a "---" that is part of a patch, and a "---" that starts + * the next patch is to look at the line counts.. + */ +static int parse_fragment(struct apply_state *state, + const char *line, + unsigned long size, + struct patch *patch, + struct fragment *fragment) +{ + int added, deleted; + int len = linelen(line, size), offset; + unsigned long oldlines, newlines; + unsigned long leading, trailing; + + offset = parse_fragment_header(line, len, fragment); + if (offset < 0) + return -1; + if (offset > 0 && patch->recount) + recount_diff(line + offset, size - offset, fragment); + oldlines = fragment->oldlines; + newlines = fragment->newlines; + leading = 0; + trailing = 0; + + /* Parse the thing.. */ + line += len; + size -= len; + state->linenr++; + added = deleted = 0; + for (offset = len; + 0 < size; + offset += len, size -= len, line += len, state->linenr++) { + if (!oldlines && !newlines) + break; + len = linelen(line, size); + if (!len || line[len-1] != '\n') + return -1; + switch (*line) { + default: + return -1; + case '\n': /* newer GNU diff, an empty context line */ + case ' ': + oldlines--; + newlines--; + if (!deleted && !added) + leading++; + trailing++; + if (!state->apply_in_reverse && + state->ws_error_action == correct_ws_error) + check_whitespace(state, line, len, patch->ws_rule); + break; + case '-': + if (state->apply_in_reverse && + state->ws_error_action != nowarn_ws_error) + check_whitespace(state, line, len, patch->ws_rule); + deleted++; + oldlines--; + trailing = 0; + break; + case '+': + if (!state->apply_in_reverse && + state->ws_error_action != nowarn_ws_error) + check_whitespace(state, line, len, patch->ws_rule); + added++; + newlines--; + trailing = 0; + break; + + /* + * We allow "\ No newline at end of file". Depending + * on locale settings when the patch was produced we + * don't know what this line looks like. The only + * thing we do know is that it begins with "\ ". + * Checking for 12 is just for sanity check -- any + * l10n of "\ No newline..." is at least that long. + */ + case '\\': + if (len < 12 || memcmp(line, "\\ ", 2)) + return -1; + break; + } + } + if (oldlines || newlines) + return -1; + if (!deleted && !added) + return -1; + + fragment->leading = leading; + fragment->trailing = trailing; + + /* + * If a fragment ends with an incomplete line, we failed to include + * it in the above loop because we hit oldlines == newlines == 0 + * before seeing it. + */ + if (12 < size && !memcmp(line, "\\ ", 2)) + offset += linelen(line, size); + + patch->lines_added += added; + patch->lines_deleted += deleted; + + if (0 < patch->is_new && oldlines) + return error(_("new file depends on old contents")); + if (0 < patch->is_delete && newlines) + return error(_("deleted file still has contents")); + return offset; +} + +/* + * We have seen "diff --git a/... b/..." header (or a traditional patch + * header). Read hunks that belong to this patch into fragments and hang + * them to the given patch structure. + * + * The (fragment->patch, fragment->size) pair points into the memory given + * by the caller, not a copy, when we return. + * + * Returns: + * -1 in case of error, + * the number of bytes in the patch otherwise. + */ +static int parse_single_patch(struct apply_state *state, + const char *line, + unsigned long size, + struct patch *patch) +{ + unsigned long offset = 0; + unsigned long oldlines = 0, newlines = 0, context = 0; + struct fragment **fragp = &patch->fragments; + + while (size > 4 && !memcmp(line, "@@ -", 4)) { + struct fragment *fragment; + int len; + + fragment = xcalloc(1, sizeof(*fragment)); + fragment->linenr = state->linenr; + len = parse_fragment(state, line, size, patch, fragment); + if (len <= 0) { + free(fragment); + return error(_("corrupt patch at line %d"), state->linenr); + } + fragment->patch = line; + fragment->size = len; + oldlines += fragment->oldlines; + newlines += fragment->newlines; + context += fragment->leading + fragment->trailing; + + *fragp = fragment; + fragp = &fragment->next; + + offset += len; + line += len; + size -= len; + } + + /* + * If something was removed (i.e. we have old-lines) it cannot + * be creation, and if something was added it cannot be + * deletion. However, the reverse is not true; --unified=0 + * patches that only add are not necessarily creation even + * though they do not have any old lines, and ones that only + * delete are not necessarily deletion. + * + * Unfortunately, a real creation/deletion patch do _not_ have + * any context line by definition, so we cannot safely tell it + * apart with --unified=0 insanity. At least if the patch has + * more than one hunk it is not creation or deletion. + */ + if (patch->is_new < 0 && + (oldlines || (patch->fragments && patch->fragments->next))) + patch->is_new = 0; + if (patch->is_delete < 0 && + (newlines || (patch->fragments && patch->fragments->next))) + patch->is_delete = 0; + + if (0 < patch->is_new && oldlines) + return error(_("new file %s depends on old contents"), patch->new_name); + if (0 < patch->is_delete && newlines) + return error(_("deleted file %s still has contents"), patch->old_name); + if (!patch->is_delete && !newlines && context) + fprintf_ln(stderr, + _("** warning: " + "file %s becomes empty but is not deleted"), + patch->new_name); + + return offset; +} + +static inline int metadata_changes(struct patch *patch) +{ + return patch->is_rename > 0 || + patch->is_copy > 0 || + patch->is_new > 0 || + patch->is_delete || + (patch->old_mode && patch->new_mode && + patch->old_mode != patch->new_mode); +} + +static char *inflate_it(const void *data, unsigned long size, + unsigned long inflated_size) +{ + git_zstream stream; + void *out; + int st; + + memset(&stream, 0, sizeof(stream)); + + stream.next_in = (unsigned char *)data; + stream.avail_in = size; + stream.next_out = out = xmalloc(inflated_size); + stream.avail_out = inflated_size; + git_inflate_init(&stream); + st = git_inflate(&stream, Z_FINISH); + git_inflate_end(&stream); + if ((st != Z_STREAM_END) || stream.total_out != inflated_size) { + free(out); + return NULL; + } + return out; +} + +/* + * Read a binary hunk and return a new fragment; fragment->patch + * points at an allocated memory that the caller must free, so + * it is marked as "->free_patch = 1". + */ +static struct fragment *parse_binary_hunk(struct apply_state *state, + char **buf_p, + unsigned long *sz_p, + int *status_p, + int *used_p) +{ + /* + * Expect a line that begins with binary patch method ("literal" + * or "delta"), followed by the length of data before deflating. + * a sequence of 'length-byte' followed by base-85 encoded data + * should follow, terminated by a newline. + * + * Each 5-byte sequence of base-85 encodes up to 4 bytes, + * and we would limit the patch line to 66 characters, + * so one line can fit up to 13 groups that would decode + * to 52 bytes max. The length byte 'A'-'Z' corresponds + * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes. + */ + int llen, used; + unsigned long size = *sz_p; + char *buffer = *buf_p; + int patch_method; + unsigned long origlen; + char *data = NULL; + int hunk_size = 0; + struct fragment *frag; + + llen = linelen(buffer, size); + used = llen; + + *status_p = 0; + + if (starts_with(buffer, "delta ")) { + patch_method = BINARY_DELTA_DEFLATED; + origlen = strtoul(buffer + 6, NULL, 10); + } + else if (starts_with(buffer, "literal ")) { + patch_method = BINARY_LITERAL_DEFLATED; + origlen = strtoul(buffer + 8, NULL, 10); + } + else + return NULL; + + state->linenr++; + buffer += llen; + while (1) { + int byte_length, max_byte_length, newsize; + llen = linelen(buffer, size); + used += llen; + state->linenr++; + if (llen == 1) { + /* consume the blank line */ + buffer++; + size--; + break; + } + /* + * Minimum line is "A00000\n" which is 7-byte long, + * and the line length must be multiple of 5 plus 2. + */ + if ((llen < 7) || (llen-2) % 5) + goto corrupt; + max_byte_length = (llen - 2) / 5 * 4; + byte_length = *buffer; + if ('A' <= byte_length && byte_length <= 'Z') + byte_length = byte_length - 'A' + 1; + else if ('a' <= byte_length && byte_length <= 'z') + byte_length = byte_length - 'a' + 27; + else + goto corrupt; + /* if the input length was not multiple of 4, we would + * have filler at the end but the filler should never + * exceed 3 bytes + */ + if (max_byte_length < byte_length || + byte_length <= max_byte_length - 4) + goto corrupt; + newsize = hunk_size + byte_length; + data = xrealloc(data, newsize); + if (decode_85(data + hunk_size, buffer + 1, byte_length)) + goto corrupt; + hunk_size = newsize; + buffer += llen; + size -= llen; + } + + frag = xcalloc(1, sizeof(*frag)); + frag->patch = inflate_it(data, hunk_size, origlen); + frag->free_patch = 1; + if (!frag->patch) + goto corrupt; + free(data); + frag->size = origlen; + *buf_p = buffer; + *sz_p = size; + *used_p = used; + frag->binary_patch_method = patch_method; + return frag; + + corrupt: + free(data); + *status_p = -1; + error(_("corrupt binary patch at line %d: %.*s"), + state->linenr-1, llen-1, buffer); + return NULL; +} + +/* + * Returns: + * -1 in case of error, + * the length of the parsed binary patch otherwise + */ +static int parse_binary(struct apply_state *state, + char *buffer, + unsigned long size, + struct patch *patch) +{ + /* + * We have read "GIT binary patch\n"; what follows is a line + * that says the patch method (currently, either "literal" or + * "delta") and the length of data before deflating; a + * sequence of 'length-byte' followed by base-85 encoded data + * follows. + * + * When a binary patch is reversible, there is another binary + * hunk in the same format, starting with patch method (either + * "literal" or "delta") with the length of data, and a sequence + * of length-byte + base-85 encoded data, terminated with another + * empty line. This data, when applied to the postimage, produces + * the preimage. + */ + struct fragment *forward; + struct fragment *reverse; + int status; + int used, used_1; + + forward = parse_binary_hunk(state, &buffer, &size, &status, &used); + if (!forward && !status) + /* there has to be one hunk (forward hunk) */ + return error(_("unrecognized binary patch at line %d"), state->linenr-1); + if (status) + /* otherwise we already gave an error message */ + return status; + + reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1); + if (reverse) + used += used_1; + else if (status) { + /* + * Not having reverse hunk is not an error, but having + * a corrupt reverse hunk is. + */ + free((void*) forward->patch); + free(forward); + return status; + } + forward->next = reverse; + patch->fragments = forward; + patch->is_binary = 1; + return used; +} + +static void prefix_one(struct apply_state *state, char **name) +{ + char *old_name = *name; + if (!old_name) + return; + *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name)); + free(old_name); +} + +static void prefix_patch(struct apply_state *state, struct patch *p) +{ + if (!state->prefix || p->is_toplevel_relative) + return; + prefix_one(state, &p->new_name); + prefix_one(state, &p->old_name); +} + +/* + * include/exclude + */ + +static void add_name_limit(struct apply_state *state, + const char *name, + int exclude) +{ + struct string_list_item *it; + + it = string_list_append(&state->limit_by_name, name); + it->util = exclude ? NULL : (void *) 1; +} + +static int use_patch(struct apply_state *state, struct patch *p) +{ + const char *pathname = p->new_name ? p->new_name : p->old_name; + int i; + + /* Paths outside are not touched regardless of "--include" */ + if (0 < state->prefix_length) { + int pathlen = strlen(pathname); + if (pathlen <= state->prefix_length || + memcmp(state->prefix, pathname, state->prefix_length)) + return 0; + } + + /* See if it matches any of exclude/include rule */ + for (i = 0; i < state->limit_by_name.nr; i++) { + struct string_list_item *it = &state->limit_by_name.items[i]; + if (!wildmatch(it->string, pathname, 0, NULL)) + return (it->util != NULL); + } + + /* + * If we had any include, a path that does not match any rule is + * not used. Otherwise, we saw bunch of exclude rules (or none) + * and such a path is used. + */ + return !state->has_include; +} + +/* + * Read the patch text in "buffer" that extends for "size" bytes; stop + * reading after seeing a single patch (i.e. changes to a single file). + * Create fragments (i.e. patch hunks) and hang them to the given patch. + * + * Returns: + * -1 if no header was found or parse_binary() failed, + * -128 on another error, + * the number of bytes consumed otherwise, + * so that the caller can call us again for the next patch. + */ +static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch) +{ + int hdrsize, patchsize; + int offset = find_header(state, buffer, size, &hdrsize, patch); + + if (offset < 0) + return offset; + + prefix_patch(state, patch); + + if (!use_patch(state, patch)) + patch->ws_rule = 0; + else + patch->ws_rule = whitespace_rule(patch->new_name + ? patch->new_name + : patch->old_name); + + patchsize = parse_single_patch(state, + buffer + offset + hdrsize, + size - offset - hdrsize, + patch); + + if (patchsize < 0) + return -128; + + if (!patchsize) { + static const char git_binary[] = "GIT binary patch\n"; + int hd = hdrsize + offset; + unsigned long llen = linelen(buffer + hd, size - hd); + + if (llen == sizeof(git_binary) - 1 && + !memcmp(git_binary, buffer + hd, llen)) { + int used; + state->linenr++; + used = parse_binary(state, buffer + hd + llen, + size - hd - llen, patch); + if (used < 0) + return -1; + if (used) + patchsize = used + llen; + else + patchsize = 0; + } + else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) { + static const char *binhdr[] = { + "Binary files ", + "Files ", + NULL, + }; + int i; + for (i = 0; binhdr[i]; i++) { + int len = strlen(binhdr[i]); + if (len < size - hd && + !memcmp(binhdr[i], buffer + hd, len)) { + state->linenr++; + patch->is_binary = 1; + patchsize = llen; + break; + } + } + } + + /* Empty patch cannot be applied if it is a text patch + * without metadata change. A binary patch appears + * empty to us here. + */ + if ((state->apply || state->check) && + (!patch->is_binary && !metadata_changes(patch))) { + error(_("patch with only garbage at line %d"), state->linenr); + return -128; + } + } + + return offset + hdrsize + patchsize; +} + +#define swap(a,b) myswap((a),(b),sizeof(a)) + +#define myswap(a, b, size) do { \ + unsigned char mytmp[size]; \ + memcpy(mytmp, &a, size); \ + memcpy(&a, &b, size); \ + memcpy(&b, mytmp, size); \ +} while (0) + +static void reverse_patches(struct patch *p) +{ + for (; p; p = p->next) { + struct fragment *frag = p->fragments; + + swap(p->new_name, p->old_name); + swap(p->new_mode, p->old_mode); + swap(p->is_new, p->is_delete); + swap(p->lines_added, p->lines_deleted); + swap(p->old_sha1_prefix, p->new_sha1_prefix); + + for (; frag; frag = frag->next) { + swap(frag->newpos, frag->oldpos); + swap(frag->newlines, frag->oldlines); + } + } +} + +static const char pluses[] = +"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; +static const char minuses[]= +"----------------------------------------------------------------------"; + +static void show_stats(struct apply_state *state, struct patch *patch) +{ + struct strbuf qname = STRBUF_INIT; + char *cp = patch->new_name ? patch->new_name : patch->old_name; + int max, add, del; + + quote_c_style(cp, &qname, NULL, 0); + + /* + * "scale" the filename + */ + max = state->max_len; + if (max > 50) + max = 50; + + if (qname.len > max) { + cp = strchr(qname.buf + qname.len + 3 - max, '/'); + if (!cp) + cp = qname.buf + qname.len + 3 - max; + strbuf_splice(&qname, 0, cp - qname.buf, "...", 3); + } + + if (patch->is_binary) { + printf(" %-*s | Bin\n", max, qname.buf); + strbuf_release(&qname); + return; + } + + printf(" %-*s |", max, qname.buf); + strbuf_release(&qname); + + /* + * scale the add/delete + */ + max = max + state->max_change > 70 ? 70 - max : state->max_change; + add = patch->lines_added; + del = patch->lines_deleted; + + if (state->max_change > 0) { + int total = ((add + del) * max + state->max_change / 2) / state->max_change; + add = (add * max + state->max_change / 2) / state->max_change; + del = total - add; + } + printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted, + add, pluses, del, minuses); +} + +static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) +{ + switch (st->st_mode & S_IFMT) { + case S_IFLNK: + if (strbuf_readlink(buf, path, st->st_size) < 0) + return error(_("unable to read symlink %s"), path); + return 0; + case S_IFREG: + if (strbuf_read_file(buf, path, st->st_size) != st->st_size) + return error(_("unable to open or read %s"), path); + convert_to_git(path, buf->buf, buf->len, buf, 0); + return 0; + default: + return -1; + } +} + +/* + * Update the preimage, and the common lines in postimage, + * from buffer buf of length len. If postlen is 0 the postimage + * is updated in place, otherwise it's updated on a new buffer + * of length postlen + */ + +static void update_pre_post_images(struct image *preimage, + struct image *postimage, + char *buf, + size_t len, size_t postlen) +{ + int i, ctx, reduced; + char *new, *old, *fixed; + struct image fixed_preimage; + + /* + * Update the preimage with whitespace fixes. Note that we + * are not losing preimage->buf -- apply_one_fragment() will + * free "oldlines". + */ + prepare_image(&fixed_preimage, buf, len, 1); + assert(postlen + ? fixed_preimage.nr == preimage->nr + : fixed_preimage.nr <= preimage->nr); + for (i = 0; i < fixed_preimage.nr; i++) + fixed_preimage.line[i].flag = preimage->line[i].flag; + free(preimage->line_allocated); + *preimage = fixed_preimage; + + /* + * Adjust the common context lines in postimage. This can be + * done in-place when we are shrinking it with whitespace + * fixing, but needs a new buffer when ignoring whitespace or + * expanding leading tabs to spaces. + * + * We trust the caller to tell us if the update can be done + * in place (postlen==0) or not. + */ + old = postimage->buf; + if (postlen) + new = postimage->buf = xmalloc(postlen); + else + new = old; + fixed = preimage->buf; + + for (i = reduced = ctx = 0; i < postimage->nr; i++) { + size_t l_len = postimage->line[i].len; + if (!(postimage->line[i].flag & LINE_COMMON)) { + /* an added line -- no counterparts in preimage */ + memmove(new, old, l_len); + old += l_len; + new += l_len; + continue; + } + + /* a common context -- skip it in the original postimage */ + old += l_len; + + /* and find the corresponding one in the fixed preimage */ + while (ctx < preimage->nr && + !(preimage->line[ctx].flag & LINE_COMMON)) { + fixed += preimage->line[ctx].len; + ctx++; + } + + /* + * preimage is expected to run out, if the caller + * fixed addition of trailing blank lines. + */ + if (preimage->nr <= ctx) { + reduced++; + continue; + } + + /* and copy it in, while fixing the line length */ + l_len = preimage->line[ctx].len; + memcpy(new, fixed, l_len); + new += l_len; + fixed += l_len; + postimage->line[i].len = l_len; + ctx++; + } + + if (postlen + ? postlen < new - postimage->buf + : postimage->len < new - postimage->buf) + die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d", + (int)postlen, (int) postimage->len, (int)(new - postimage->buf)); + + /* Fix the length of the whole thing */ + postimage->len = new - postimage->buf; + postimage->nr -= reduced; +} + +static int line_by_line_fuzzy_match(struct image *img, + struct image *preimage, + struct image *postimage, + unsigned long try, + int try_lno, + int preimage_limit) +{ + int i; + size_t imgoff = 0; + size_t preoff = 0; + size_t postlen = postimage->len; + size_t extra_chars; + char *buf; + char *preimage_eof; + char *preimage_end; + struct strbuf fixed; + char *fixed_buf; + size_t fixed_len; + + for (i = 0; i < preimage_limit; i++) { + size_t prelen = preimage->line[i].len; + size_t imglen = img->line[try_lno+i].len; + + if (!fuzzy_matchlines(img->buf + try + imgoff, imglen, + preimage->buf + preoff, prelen)) + return 0; + if (preimage->line[i].flag & LINE_COMMON) + postlen += imglen - prelen; + imgoff += imglen; + preoff += prelen; + } + + /* + * Ok, the preimage matches with whitespace fuzz. + * + * imgoff now holds the true length of the target that + * matches the preimage before the end of the file. + * + * Count the number of characters in the preimage that fall + * beyond the end of the file and make sure that all of them + * are whitespace characters. (This can only happen if + * we are removing blank lines at the end of the file.) + */ + buf = preimage_eof = preimage->buf + preoff; + for ( ; i < preimage->nr; i++) + preoff += preimage->line[i].len; + preimage_end = preimage->buf + preoff; + for ( ; buf < preimage_end; buf++) + if (!isspace(*buf)) + return 0; + + /* + * Update the preimage and the common postimage context + * lines to use the same whitespace as the target. + * If whitespace is missing in the target (i.e. + * if the preimage extends beyond the end of the file), + * use the whitespace from the preimage. + */ + extra_chars = preimage_end - preimage_eof; + strbuf_init(&fixed, imgoff + extra_chars); + strbuf_add(&fixed, img->buf + try, imgoff); + strbuf_add(&fixed, preimage_eof, extra_chars); + fixed_buf = strbuf_detach(&fixed, &fixed_len); + update_pre_post_images(preimage, postimage, + fixed_buf, fixed_len, postlen); + return 1; +} + +static int match_fragment(struct apply_state *state, + struct image *img, + struct image *preimage, + struct image *postimage, + unsigned long try, + int try_lno, + unsigned ws_rule, + int match_beginning, int match_end) +{ + int i; + char *fixed_buf, *buf, *orig, *target; + struct strbuf fixed; + size_t fixed_len, postlen; + int preimage_limit; + + if (preimage->nr + try_lno <= img->nr) { + /* + * The hunk falls within the boundaries of img. + */ + preimage_limit = preimage->nr; + if (match_end && (preimage->nr + try_lno != img->nr)) + return 0; + } else if (state->ws_error_action == correct_ws_error && + (ws_rule & WS_BLANK_AT_EOF)) { + /* + * This hunk extends beyond the end of img, and we are + * removing blank lines at the end of the file. This + * many lines from the beginning of the preimage must + * match with img, and the remainder of the preimage + * must be blank. + */ + preimage_limit = img->nr - try_lno; + } else { + /* + * The hunk extends beyond the end of the img and + * we are not removing blanks at the end, so we + * should reject the hunk at this position. + */ + return 0; + } + + if (match_beginning && try_lno) + return 0; + + /* Quick hash check */ + for (i = 0; i < preimage_limit; i++) + if ((img->line[try_lno + i].flag & LINE_PATCHED) || + (preimage->line[i].hash != img->line[try_lno + i].hash)) + return 0; + + if (preimage_limit == preimage->nr) { + /* + * Do we have an exact match? If we were told to match + * at the end, size must be exactly at try+fragsize, + * otherwise try+fragsize must be still within the preimage, + * and either case, the old piece should match the preimage + * exactly. + */ + if ((match_end + ? (try + preimage->len == img->len) + : (try + preimage->len <= img->len)) && + !memcmp(img->buf + try, preimage->buf, preimage->len)) + return 1; + } else { + /* + * The preimage extends beyond the end of img, so + * there cannot be an exact match. + * + * There must be one non-blank context line that match + * a line before the end of img. + */ + char *buf_end; + + buf = preimage->buf; + buf_end = buf; + for (i = 0; i < preimage_limit; i++) + buf_end += preimage->line[i].len; + + for ( ; buf < buf_end; buf++) + if (!isspace(*buf)) + break; + if (buf == buf_end) + return 0; + } + + /* + * No exact match. If we are ignoring whitespace, run a line-by-line + * fuzzy matching. We collect all the line length information because + * we need it to adjust whitespace if we match. + */ + if (state->ws_ignore_action == ignore_ws_change) + return line_by_line_fuzzy_match(img, preimage, postimage, + try, try_lno, preimage_limit); + + if (state->ws_error_action != correct_ws_error) + return 0; + + /* + * The hunk does not apply byte-by-byte, but the hash says + * it might with whitespace fuzz. We weren't asked to + * ignore whitespace, we were asked to correct whitespace + * errors, so let's try matching after whitespace correction. + * + * While checking the preimage against the target, whitespace + * errors in both fixed, we count how large the corresponding + * postimage needs to be. The postimage prepared by + * apply_one_fragment() has whitespace errors fixed on added + * lines already, but the common lines were propagated as-is, + * which may become longer when their whitespace errors are + * fixed. + */ + + /* First count added lines in postimage */ + postlen = 0; + for (i = 0; i < postimage->nr; i++) { + if (!(postimage->line[i].flag & LINE_COMMON)) + postlen += postimage->line[i].len; + } + + /* + * The preimage may extend beyond the end of the file, + * but in this loop we will only handle the part of the + * preimage that falls within the file. + */ + strbuf_init(&fixed, preimage->len + 1); + orig = preimage->buf; + target = img->buf + try; + for (i = 0; i < preimage_limit; i++) { + size_t oldlen = preimage->line[i].len; + size_t tgtlen = img->line[try_lno + i].len; + size_t fixstart = fixed.len; + struct strbuf tgtfix; + int match; + + /* Try fixing the line in the preimage */ + ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL); + + /* Try fixing the line in the target */ + strbuf_init(&tgtfix, tgtlen); + ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL); + + /* + * If they match, either the preimage was based on + * a version before our tree fixed whitespace breakage, + * or we are lacking a whitespace-fix patch the tree + * the preimage was based on already had (i.e. target + * has whitespace breakage, the preimage doesn't). + * In either case, we are fixing the whitespace breakages + * so we might as well take the fix together with their + * real change. + */ + match = (tgtfix.len == fixed.len - fixstart && + !memcmp(tgtfix.buf, fixed.buf + fixstart, + fixed.len - fixstart)); + + /* Add the length if this is common with the postimage */ + if (preimage->line[i].flag & LINE_COMMON) + postlen += tgtfix.len; + + strbuf_release(&tgtfix); + if (!match) + goto unmatch_exit; + + orig += oldlen; + target += tgtlen; + } + + + /* + * Now handle the lines in the preimage that falls beyond the + * end of the file (if any). They will only match if they are + * empty or only contain whitespace (if WS_BLANK_AT_EOL is + * false). + */ + for ( ; i < preimage->nr; i++) { + size_t fixstart = fixed.len; /* start of the fixed preimage */ + size_t oldlen = preimage->line[i].len; + int j; + + /* Try fixing the line in the preimage */ + ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL); + + for (j = fixstart; j < fixed.len; j++) + if (!isspace(fixed.buf[j])) + goto unmatch_exit; + + orig += oldlen; + } + + /* + * Yes, the preimage is based on an older version that still + * has whitespace breakages unfixed, and fixing them makes the + * hunk match. Update the context lines in the postimage. + */ + fixed_buf = strbuf_detach(&fixed, &fixed_len); + if (postlen < postimage->len) + postlen = 0; + update_pre_post_images(preimage, postimage, + fixed_buf, fixed_len, postlen); + return 1; + + unmatch_exit: + strbuf_release(&fixed); + return 0; +} + +static int find_pos(struct apply_state *state, + struct image *img, + struct image *preimage, + struct image *postimage, + int line, + unsigned ws_rule, + int match_beginning, int match_end) +{ + int i; + unsigned long backwards, forwards, try; + int backwards_lno, forwards_lno, try_lno; + + /* + * If match_beginning or match_end is specified, there is no + * point starting from a wrong line that will never match and + * wander around and wait for a match at the specified end. + */ + if (match_beginning) + line = 0; + else if (match_end) + line = img->nr - preimage->nr; + + /* + * Because the comparison is unsigned, the following test + * will also take care of a negative line number that can + * result when match_end and preimage is larger than the target. + */ + if ((size_t) line > img->nr) + line = img->nr; + + try = 0; + for (i = 0; i < line; i++) + try += img->line[i].len; + + /* + * There's probably some smart way to do this, but I'll leave + * that to the smart and beautiful people. I'm simple and stupid. + */ + backwards = try; + backwards_lno = line; + forwards = try; + forwards_lno = line; + try_lno = line; + + for (i = 0; ; i++) { + if (match_fragment(state, img, preimage, postimage, + try, try_lno, ws_rule, + match_beginning, match_end)) + return try_lno; + + again: + if (backwards_lno == 0 && forwards_lno == img->nr) + break; + + if (i & 1) { + if (backwards_lno == 0) { + i++; + goto again; + } + backwards_lno--; + backwards -= img->line[backwards_lno].len; + try = backwards; + try_lno = backwards_lno; + } else { + if (forwards_lno == img->nr) { + i++; + goto again; + } + forwards += img->line[forwards_lno].len; + forwards_lno++; + try = forwards; + try_lno = forwards_lno; + } + + } + return -1; +} + +static void remove_first_line(struct image *img) +{ + img->buf += img->line[0].len; + img->len -= img->line[0].len; + img->line++; + img->nr--; +} + +static void remove_last_line(struct image *img) +{ + img->len -= img->line[--img->nr].len; +} + +/* + * The change from "preimage" and "postimage" has been found to + * apply at applied_pos (counts in line numbers) in "img". + * Update "img" to remove "preimage" and replace it with "postimage". + */ +static void update_image(struct apply_state *state, + struct image *img, + int applied_pos, + struct image *preimage, + struct image *postimage) +{ + /* + * remove the copy of preimage at offset in img + * and replace it with postimage + */ + int i, nr; + size_t remove_count, insert_count, applied_at = 0; + char *result; + int preimage_limit; + + /* + * If we are removing blank lines at the end of img, + * the preimage may extend beyond the end. + * If that is the case, we must be careful only to + * remove the part of the preimage that falls within + * the boundaries of img. Initialize preimage_limit + * to the number of lines in the preimage that falls + * within the boundaries. + */ + preimage_limit = preimage->nr; + if (preimage_limit > img->nr - applied_pos) + preimage_limit = img->nr - applied_pos; + + for (i = 0; i < applied_pos; i++) + applied_at += img->line[i].len; + + remove_count = 0; + for (i = 0; i < preimage_limit; i++) + remove_count += img->line[applied_pos + i].len; + insert_count = postimage->len; + + /* Adjust the contents */ + result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1)); + memcpy(result, img->buf, applied_at); + memcpy(result + applied_at, postimage->buf, postimage->len); + memcpy(result + applied_at + postimage->len, + img->buf + (applied_at + remove_count), + img->len - (applied_at + remove_count)); + free(img->buf); + img->buf = result; + img->len += insert_count - remove_count; + result[img->len] = '\0'; + + /* Adjust the line table */ + nr = img->nr + postimage->nr - preimage_limit; + if (preimage_limit < postimage->nr) { + /* + * NOTE: this knows that we never call remove_first_line() + * on anything other than pre/post image. + */ + REALLOC_ARRAY(img->line, nr); + img->line_allocated = img->line; + } + if (preimage_limit != postimage->nr) + memmove(img->line + applied_pos + postimage->nr, + img->line + applied_pos + preimage_limit, + (img->nr - (applied_pos + preimage_limit)) * + sizeof(*img->line)); + memcpy(img->line + applied_pos, + postimage->line, + postimage->nr * sizeof(*img->line)); + if (!state->allow_overlap) + for (i = 0; i < postimage->nr; i++) + img->line[applied_pos + i].flag |= LINE_PATCHED; + img->nr = nr; +} + +/* + * Use the patch-hunk text in "frag" to prepare two images (preimage and + * postimage) for the hunk. Find lines that match "preimage" in "img" and + * replace the part of "img" with "postimage" text. + */ +static int apply_one_fragment(struct apply_state *state, + struct image *img, struct fragment *frag, + int inaccurate_eof, unsigned ws_rule, + int nth_fragment) +{ + int match_beginning, match_end; + const char *patch = frag->patch; + int size = frag->size; + char *old, *oldlines; + struct strbuf newlines; + int new_blank_lines_at_end = 0; + int found_new_blank_lines_at_end = 0; + int hunk_linenr = frag->linenr; + unsigned long leading, trailing; + int pos, applied_pos; + struct image preimage; + struct image postimage; + + memset(&preimage, 0, sizeof(preimage)); + memset(&postimage, 0, sizeof(postimage)); + oldlines = xmalloc(size); + strbuf_init(&newlines, size); + + old = oldlines; + while (size > 0) { + char first; + int len = linelen(patch, size); + int plen; + int added_blank_line = 0; + int is_blank_context = 0; + size_t start; + + if (!len) + break; + + /* + * "plen" is how much of the line we should use for + * the actual patch data. Normally we just remove the + * first character on the line, but if the line is + * followed by "\ No newline", then we also remove the + * last one (which is the newline, of course). + */ + plen = len - 1; + if (len < size && patch[len] == '\\') + plen--; + first = *patch; + if (state->apply_in_reverse) { + if (first == '-') + first = '+'; + else if (first == '+') + first = '-'; + } + + switch (first) { + case '\n': + /* Newer GNU diff, empty context line */ + if (plen < 0) + /* ... followed by '\No newline'; nothing */ + break; + *old++ = '\n'; + strbuf_addch(&newlines, '\n'); + add_line_info(&preimage, "\n", 1, LINE_COMMON); + add_line_info(&postimage, "\n", 1, LINE_COMMON); + is_blank_context = 1; + break; + case ' ': + if (plen && (ws_rule & WS_BLANK_AT_EOF) && + ws_blank_line(patch + 1, plen, ws_rule)) + is_blank_context = 1; + case '-': + memcpy(old, patch + 1, plen); + add_line_info(&preimage, old, plen, + (first == ' ' ? LINE_COMMON : 0)); + old += plen; + if (first == '-') + break; + /* Fall-through for ' ' */ + case '+': + /* --no-add does not add new lines */ + if (first == '+' && state->no_add) + break; + + start = newlines.len; + if (first != '+' || + !state->whitespace_error || + state->ws_error_action != correct_ws_error) { + strbuf_add(&newlines, patch + 1, plen); + } + else { + ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws); + } + add_line_info(&postimage, newlines.buf + start, newlines.len - start, + (first == '+' ? 0 : LINE_COMMON)); + if (first == '+' && + (ws_rule & WS_BLANK_AT_EOF) && + ws_blank_line(patch + 1, plen, ws_rule)) + added_blank_line = 1; + break; + case '@': case '\\': + /* Ignore it, we already handled it */ + break; + default: + if (state->apply_verbosely) + error(_("invalid start of line: '%c'"), first); + applied_pos = -1; + goto out; + } + if (added_blank_line) { + if (!new_blank_lines_at_end) + found_new_blank_lines_at_end = hunk_linenr; + new_blank_lines_at_end++; + } + else if (is_blank_context) + ; + else + new_blank_lines_at_end = 0; + patch += len; + size -= len; + hunk_linenr++; + } + if (inaccurate_eof && + old > oldlines && old[-1] == '\n' && + newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') { + old--; + strbuf_setlen(&newlines, newlines.len - 1); + } + + leading = frag->leading; + trailing = frag->trailing; + + /* + * A hunk to change lines at the beginning would begin with + * @@ -1,L +N,M @@ + * but we need to be careful. -U0 that inserts before the second + * line also has this pattern. + * + * And a hunk to add to an empty file would begin with + * @@ -0,0 +N,M @@ + * + * In other words, a hunk that is (frag->oldpos <= 1) with or + * without leading context must match at the beginning. + */ + match_beginning = (!frag->oldpos || + (frag->oldpos == 1 && !state->unidiff_zero)); + + /* + * A hunk without trailing lines must match at the end. + * However, we simply cannot tell if a hunk must match end + * from the lack of trailing lines if the patch was generated + * with unidiff without any context. + */ + match_end = !state->unidiff_zero && !trailing; + + pos = frag->newpos ? (frag->newpos - 1) : 0; + preimage.buf = oldlines; + preimage.len = old - oldlines; + postimage.buf = newlines.buf; + postimage.len = newlines.len; + preimage.line = preimage.line_allocated; + postimage.line = postimage.line_allocated; + + for (;;) { + + applied_pos = find_pos(state, img, &preimage, &postimage, pos, + ws_rule, match_beginning, match_end); + + if (applied_pos >= 0) + break; + + /* Am I at my context limits? */ + if ((leading <= state->p_context) && (trailing <= state->p_context)) + break; + if (match_beginning || match_end) { + match_beginning = match_end = 0; + continue; + } + + /* + * Reduce the number of context lines; reduce both + * leading and trailing if they are equal otherwise + * just reduce the larger context. + */ + if (leading >= trailing) { + remove_first_line(&preimage); + remove_first_line(&postimage); + pos--; + leading--; + } + if (trailing > leading) { + remove_last_line(&preimage); + remove_last_line(&postimage); + trailing--; + } + } + + if (applied_pos >= 0) { + if (new_blank_lines_at_end && + preimage.nr + applied_pos >= img->nr && + (ws_rule & WS_BLANK_AT_EOF) && + state->ws_error_action != nowarn_ws_error) { + record_ws_error(state, WS_BLANK_AT_EOF, "+", 1, + found_new_blank_lines_at_end); + if (state->ws_error_action == correct_ws_error) { + while (new_blank_lines_at_end--) + remove_last_line(&postimage); + } + /* + * We would want to prevent write_out_results() + * from taking place in apply_patch() that follows + * the callchain led us here, which is: + * apply_patch->check_patch_list->check_patch-> + * apply_data->apply_fragments->apply_one_fragment + */ + if (state->ws_error_action == die_on_ws_error) + state->apply = 0; + } + + if (state->apply_verbosely && applied_pos != pos) { + int offset = applied_pos - pos; + if (state->apply_in_reverse) + offset = 0 - offset; + fprintf_ln(stderr, + Q_("Hunk #%d succeeded at %d (offset %d line).", + "Hunk #%d succeeded at %d (offset %d lines).", + offset), + nth_fragment, applied_pos + 1, offset); + } + + /* + * Warn if it was necessary to reduce the number + * of context lines. + */ + if ((leading != frag->leading) || + (trailing != frag->trailing)) + fprintf_ln(stderr, _("Context reduced to (%ld/%ld)" + " to apply fragment at %d"), + leading, trailing, applied_pos+1); + update_image(state, img, applied_pos, &preimage, &postimage); + } else { + if (state->apply_verbosely) + error(_("while searching for:\n%.*s"), + (int)(old - oldlines), oldlines); + } + +out: + free(oldlines); + strbuf_release(&newlines); + free(preimage.line_allocated); + free(postimage.line_allocated); + + return (applied_pos < 0); +} + +static int apply_binary_fragment(struct apply_state *state, + struct image *img, + struct patch *patch) +{ + struct fragment *fragment = patch->fragments; + unsigned long len; + void *dst; + + if (!fragment) + return error(_("missing binary patch data for '%s'"), + patch->new_name ? + patch->new_name : + patch->old_name); + + /* Binary patch is irreversible without the optional second hunk */ + if (state->apply_in_reverse) { + if (!fragment->next) + return error("cannot reverse-apply a binary patch " + "without the reverse hunk to '%s'", + patch->new_name + ? patch->new_name : patch->old_name); + fragment = fragment->next; + } + switch (fragment->binary_patch_method) { + case BINARY_DELTA_DEFLATED: + dst = patch_delta(img->buf, img->len, fragment->patch, + fragment->size, &len); + if (!dst) + return -1; + clear_image(img); + img->buf = dst; + img->len = len; + return 0; + case BINARY_LITERAL_DEFLATED: + clear_image(img); + img->len = fragment->size; + img->buf = xmemdupz(fragment->patch, img->len); + return 0; + } + return -1; +} + +/* + * Replace "img" with the result of applying the binary patch. + * The binary patch data itself in patch->fragment is still kept + * but the preimage prepared by the caller in "img" is freed here + * or in the helper function apply_binary_fragment() this calls. + */ +static int apply_binary(struct apply_state *state, + struct image *img, + struct patch *patch) +{ + const char *name = patch->old_name ? patch->old_name : patch->new_name; + unsigned char sha1[20]; + + /* + * For safety, we require patch index line to contain + * full 40-byte textual SHA1 for old and new, at least for now. + */ + if (strlen(patch->old_sha1_prefix) != 40 || + strlen(patch->new_sha1_prefix) != 40 || + get_sha1_hex(patch->old_sha1_prefix, sha1) || + get_sha1_hex(patch->new_sha1_prefix, sha1)) + return error("cannot apply binary patch to '%s' " + "without full index line", name); + + if (patch->old_name) { + /* + * See if the old one matches what the patch + * applies to. + */ + hash_sha1_file(img->buf, img->len, blob_type, sha1); + if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix)) + return error("the patch applies to '%s' (%s), " + "which does not match the " + "current contents.", + name, sha1_to_hex(sha1)); + } + else { + /* Otherwise, the old one must be empty. */ + if (img->len) + return error("the patch applies to an empty " + "'%s' but it is not empty", name); + } + + get_sha1_hex(patch->new_sha1_prefix, sha1); + if (is_null_sha1(sha1)) { + clear_image(img); + return 0; /* deletion patch */ + } + + if (has_sha1_file(sha1)) { + /* We already have the postimage */ + enum object_type type; + unsigned long size; + char *result; + + result = read_sha1_file(sha1, &type, &size); + if (!result) + return error("the necessary postimage %s for " + "'%s' cannot be read", + patch->new_sha1_prefix, name); + clear_image(img); + img->buf = result; + img->len = size; + } else { + /* + * We have verified buf matches the preimage; + * apply the patch data to it, which is stored + * in the patch->fragments->{patch,size}. + */ + if (apply_binary_fragment(state, img, patch)) + return error(_("binary patch does not apply to '%s'"), + name); + + /* verify that the result matches */ + hash_sha1_file(img->buf, img->len, blob_type, sha1); + if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix)) + return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"), + name, patch->new_sha1_prefix, sha1_to_hex(sha1)); + } + + return 0; +} + +static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch) +{ + struct fragment *frag = patch->fragments; + const char *name = patch->old_name ? patch->old_name : patch->new_name; + unsigned ws_rule = patch->ws_rule; + unsigned inaccurate_eof = patch->inaccurate_eof; + int nth = 0; + + if (patch->is_binary) + return apply_binary(state, img, patch); + + while (frag) { + nth++; + if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) { + error(_("patch failed: %s:%ld"), name, frag->oldpos); + if (!state->apply_with_reject) + return -1; + frag->rejected = 1; + } + frag = frag->next; + } + return 0; +} + +static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode) +{ + if (S_ISGITLINK(mode)) { + strbuf_grow(buf, 100); + strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1)); + } else { + enum object_type type; + unsigned long sz; + char *result; + + result = read_sha1_file(sha1, &type, &sz); + if (!result) + return -1; + /* XXX read_sha1_file NUL-terminates */ + strbuf_attach(buf, result, sz, sz + 1); + } + return 0; +} + +static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf) +{ + if (!ce) + return 0; + return read_blob_object(buf, ce->sha1, ce->ce_mode); +} + +static struct patch *in_fn_table(struct apply_state *state, const char *name) +{ + struct string_list_item *item; + + if (name == NULL) + return NULL; + + item = string_list_lookup(&state->fn_table, name); + if (item != NULL) + return (struct patch *)item->util; + + return NULL; +} + +/* + * item->util in the filename table records the status of the path. + * Usually it points at a patch (whose result records the contents + * of it after applying it), but it could be PATH_WAS_DELETED for a + * path that a previously applied patch has already removed, or + * PATH_TO_BE_DELETED for a path that a later patch would remove. + * + * The latter is needed to deal with a case where two paths A and B + * are swapped by first renaming A to B and then renaming B to A; + * moving A to B should not be prevented due to presence of B as we + * will remove it in a later patch. + */ +#define PATH_TO_BE_DELETED ((struct patch *) -2) +#define PATH_WAS_DELETED ((struct patch *) -1) + +static int to_be_deleted(struct patch *patch) +{ + return patch == PATH_TO_BE_DELETED; +} + +static int was_deleted(struct patch *patch) +{ + return patch == PATH_WAS_DELETED; +} + +static void add_to_fn_table(struct apply_state *state, struct patch *patch) +{ + struct string_list_item *item; + + /* + * Always add new_name unless patch is a deletion + * This should cover the cases for normal diffs, + * file creations and copies + */ + if (patch->new_name != NULL) { + item = string_list_insert(&state->fn_table, patch->new_name); + item->util = patch; + } + + /* + * store a failure on rename/deletion cases because + * later chunks shouldn't patch old names + */ + if ((patch->new_name == NULL) || (patch->is_rename)) { + item = string_list_insert(&state->fn_table, patch->old_name); + item->util = PATH_WAS_DELETED; + } +} + +static void prepare_fn_table(struct apply_state *state, struct patch *patch) +{ + /* + * store information about incoming file deletion + */ + while (patch) { + if ((patch->new_name == NULL) || (patch->is_rename)) { + struct string_list_item *item; + item = string_list_insert(&state->fn_table, patch->old_name); + item->util = PATH_TO_BE_DELETED; + } + patch = patch->next; + } +} + +static int checkout_target(struct index_state *istate, + struct cache_entry *ce, struct stat *st) +{ + struct checkout costate; + + memset(&costate, 0, sizeof(costate)); + costate.base_dir = ""; + costate.refresh_cache = 1; + costate.istate = istate; + if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st)) + return error(_("cannot checkout %s"), ce->name); + return 0; +} + +static struct patch *previous_patch(struct apply_state *state, + struct patch *patch, + int *gone) +{ + struct patch *previous; + + *gone = 0; + if (patch->is_copy || patch->is_rename) + return NULL; /* "git" patches do not depend on the order */ + + previous = in_fn_table(state, patch->old_name); + if (!previous) + return NULL; + + if (to_be_deleted(previous)) + return NULL; /* the deletion hasn't happened yet */ + + if (was_deleted(previous)) + *gone = 1; + + return previous; +} + +static int verify_index_match(const struct cache_entry *ce, struct stat *st) +{ + if (S_ISGITLINK(ce->ce_mode)) { + if (!S_ISDIR(st->st_mode)) + return -1; + return 0; + } + return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); +} + +#define SUBMODULE_PATCH_WITHOUT_INDEX 1 + +static int load_patch_target(struct apply_state *state, + struct strbuf *buf, + const struct cache_entry *ce, + struct stat *st, + const char *name, + unsigned expected_mode) +{ + if (state->cached || state->check_index) { + if (read_file_or_gitlink(ce, buf)) + return error(_("failed to read %s"), name); + } else if (name) { + if (S_ISGITLINK(expected_mode)) { + if (ce) + return read_file_or_gitlink(ce, buf); + else + return SUBMODULE_PATCH_WITHOUT_INDEX; + } else if (has_symlink_leading_path(name, strlen(name))) { + return error(_("reading from '%s' beyond a symbolic link"), name); + } else { + if (read_old_data(st, name, buf)) + return error(_("failed to read %s"), name); + } + } + return 0; +} + +/* + * We are about to apply "patch"; populate the "image" with the + * current version we have, from the working tree or from the index, + * depending on the situation e.g. --cached/--index. If we are + * applying a non-git patch that incrementally updates the tree, + * we read from the result of a previous diff. + */ +static int load_preimage(struct apply_state *state, + struct image *image, + struct patch *patch, struct stat *st, + const struct cache_entry *ce) +{ + struct strbuf buf = STRBUF_INIT; + size_t len; + char *img; + struct patch *previous; + int status; + + previous = previous_patch(state, patch, &status); + if (status) + return error(_("path %s has been renamed/deleted"), + patch->old_name); + if (previous) { + /* We have a patched copy in memory; use that. */ + strbuf_add(&buf, previous->result, previous->resultsize); + } else { + status = load_patch_target(state, &buf, ce, st, + patch->old_name, patch->old_mode); + if (status < 0) + return status; + else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) { + /* + * There is no way to apply subproject + * patch without looking at the index. + * NEEDSWORK: shouldn't this be flagged + * as an error??? + */ + free_fragment_list(patch->fragments); + patch->fragments = NULL; + } else if (status) { + return error(_("failed to read %s"), patch->old_name); + } + } + + img = strbuf_detach(&buf, &len); + prepare_image(image, img, len, !patch->is_binary); + return 0; +} + +static int three_way_merge(struct image *image, + char *path, + const unsigned char *base, + const unsigned char *ours, + const unsigned char *theirs) +{ + mmfile_t base_file, our_file, their_file; + mmbuffer_t result = { NULL }; + int status; + + read_mmblob(&base_file, base); + read_mmblob(&our_file, ours); + read_mmblob(&their_file, theirs); + status = ll_merge(&result, path, + &base_file, "base", + &our_file, "ours", + &their_file, "theirs", NULL); + free(base_file.ptr); + free(our_file.ptr); + free(their_file.ptr); + if (status < 0 || !result.ptr) { + free(result.ptr); + return -1; + } + clear_image(image); + image->buf = result.ptr; + image->len = result.size; + + return status; +} + +/* + * When directly falling back to add/add three-way merge, we read from + * the current contents of the new_name. In no cases other than that + * this function will be called. + */ +static int load_current(struct apply_state *state, + struct image *image, + struct patch *patch) +{ + struct strbuf buf = STRBUF_INIT; + int status, pos; + size_t len; + char *img; + struct stat st; + struct cache_entry *ce; + char *name = patch->new_name; + unsigned mode = patch->new_mode; + + if (!patch->is_new) + die("BUG: patch to %s is not a creation", patch->old_name); + + pos = cache_name_pos(name, strlen(name)); + if (pos < 0) + return error(_("%s: does not exist in index"), name); + ce = active_cache[pos]; + if (lstat(name, &st)) { + if (errno != ENOENT) + return error(_("%s: %s"), name, strerror(errno)); + if (checkout_target(&the_index, ce, &st)) + return -1; + } + if (verify_index_match(ce, &st)) + return error(_("%s: does not match index"), name); + + status = load_patch_target(state, &buf, ce, &st, name, mode); + if (status < 0) + return status; + else if (status) + return -1; + img = strbuf_detach(&buf, &len); + prepare_image(image, img, len, !patch->is_binary); + return 0; +} + +static int try_threeway(struct apply_state *state, + struct image *image, + struct patch *patch, + struct stat *st, + const struct cache_entry *ce) +{ + unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; + struct strbuf buf = STRBUF_INIT; + size_t len; + int status; + char *img; + struct image tmp_image; + + /* No point falling back to 3-way merge in these cases */ + if (patch->is_delete || + S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode)) + return -1; + + /* Preimage the patch was prepared for */ + if (patch->is_new) + write_sha1_file("", 0, blob_type, pre_sha1); + else if (get_sha1(patch->old_sha1_prefix, pre_sha1) || + read_blob_object(&buf, pre_sha1, patch->old_mode)) + return error("repository lacks the necessary blob to fall back on 3-way merge."); + + fprintf(stderr, "Falling back to three-way merge...\n"); + + img = strbuf_detach(&buf, &len); + prepare_image(&tmp_image, img, len, 1); + /* Apply the patch to get the post image */ + if (apply_fragments(state, &tmp_image, patch) < 0) { + clear_image(&tmp_image); + return -1; + } + /* post_sha1[] is theirs */ + write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1); + clear_image(&tmp_image); + + /* our_sha1[] is ours */ + if (patch->is_new) { + if (load_current(state, &tmp_image, patch)) + return error("cannot read the current contents of '%s'", + patch->new_name); + } else { + if (load_preimage(state, &tmp_image, patch, st, ce)) + return error("cannot read the current contents of '%s'", + patch->old_name); + } + write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1); + clear_image(&tmp_image); + + /* in-core three-way merge between post and our using pre as base */ + status = three_way_merge(image, patch->new_name, + pre_sha1, our_sha1, post_sha1); + if (status < 0) { + fprintf(stderr, "Failed to fall back on three-way merge...\n"); + return status; + } + + if (status) { + patch->conflicted_threeway = 1; + if (patch->is_new) + oidclr(&patch->threeway_stage[0]); + else + hashcpy(patch->threeway_stage[0].hash, pre_sha1); + hashcpy(patch->threeway_stage[1].hash, our_sha1); + hashcpy(patch->threeway_stage[2].hash, post_sha1); + fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name); + } else { + fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name); + } + return 0; +} + +static int apply_data(struct apply_state *state, struct patch *patch, + struct stat *st, const struct cache_entry *ce) +{ + struct image image; + + if (load_preimage(state, &image, patch, st, ce) < 0) + return -1; + + if (patch->direct_to_threeway || + apply_fragments(state, &image, patch) < 0) { + /* Note: with --reject, apply_fragments() returns 0 */ + if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0) + return -1; + } + patch->result = image.buf; + patch->resultsize = image.len; + add_to_fn_table(state, patch); + free(image.line_allocated); + + if (0 < patch->is_delete && patch->resultsize) + return error(_("removal patch leaves file contents")); + + return 0; +} + +/* + * If "patch" that we are looking at modifies or deletes what we have, + * we would want it not to lose any local modification we have, either + * in the working tree or in the index. + * + * This also decides if a non-git patch is a creation patch or a + * modification to an existing empty file. We do not check the state + * of the current tree for a creation patch in this function; the caller + * check_patch() separately makes sure (and errors out otherwise) that + * the path the patch creates does not exist in the current tree. + */ +static int check_preimage(struct apply_state *state, + struct patch *patch, + struct cache_entry **ce, + struct stat *st) +{ + const char *old_name = patch->old_name; + struct patch *previous = NULL; + int stat_ret = 0, status; + unsigned st_mode = 0; + + if (!old_name) + return 0; + + assert(patch->is_new <= 0); + previous = previous_patch(state, patch, &status); + + if (status) + return error(_("path %s has been renamed/deleted"), old_name); + if (previous) { + st_mode = previous->new_mode; + } else if (!state->cached) { + stat_ret = lstat(old_name, st); + if (stat_ret && errno != ENOENT) + return error(_("%s: %s"), old_name, strerror(errno)); + } + + if (state->check_index && !previous) { + int pos = cache_name_pos(old_name, strlen(old_name)); + if (pos < 0) { + if (patch->is_new < 0) + goto is_new; + return error(_("%s: does not exist in index"), old_name); + } + *ce = active_cache[pos]; + if (stat_ret < 0) { + if (checkout_target(&the_index, *ce, st)) + return -1; + } + if (!state->cached && verify_index_match(*ce, st)) + return error(_("%s: does not match index"), old_name); + if (state->cached) + st_mode = (*ce)->ce_mode; + } else if (stat_ret < 0) { + if (patch->is_new < 0) + goto is_new; + return error(_("%s: %s"), old_name, strerror(errno)); + } + + if (!state->cached && !previous) + st_mode = ce_mode_from_stat(*ce, st->st_mode); + + if (patch->is_new < 0) + patch->is_new = 0; + if (!patch->old_mode) + patch->old_mode = st_mode; + if ((st_mode ^ patch->old_mode) & S_IFMT) + return error(_("%s: wrong type"), old_name); + if (st_mode != patch->old_mode) + warning(_("%s has type %o, expected %o"), + old_name, st_mode, patch->old_mode); + if (!patch->new_mode && !patch->is_delete) + patch->new_mode = st_mode; + return 0; + + is_new: + patch->is_new = 1; + patch->is_delete = 0; + free(patch->old_name); + patch->old_name = NULL; + return 0; +} + + +#define EXISTS_IN_INDEX 1 +#define EXISTS_IN_WORKTREE 2 + +static int check_to_create(struct apply_state *state, + const char *new_name, + int ok_if_exists) +{ + struct stat nst; + + if (state->check_index && + cache_name_pos(new_name, strlen(new_name)) >= 0 && + !ok_if_exists) + return EXISTS_IN_INDEX; + if (state->cached) + return 0; + + if (!lstat(new_name, &nst)) { + if (S_ISDIR(nst.st_mode) || ok_if_exists) + return 0; + /* + * A leading component of new_name might be a symlink + * that is going to be removed with this patch, but + * still pointing at somewhere that has the path. + * In such a case, path "new_name" does not exist as + * far as git is concerned. + */ + if (has_symlink_leading_path(new_name, strlen(new_name))) + return 0; + + return EXISTS_IN_WORKTREE; + } else if ((errno != ENOENT) && (errno != ENOTDIR)) { + return error("%s: %s", new_name, strerror(errno)); + } + return 0; +} + +static uintptr_t register_symlink_changes(struct apply_state *state, + const char *path, + uintptr_t what) +{ + struct string_list_item *ent; + + ent = string_list_lookup(&state->symlink_changes, path); + if (!ent) { + ent = string_list_insert(&state->symlink_changes, path); + ent->util = (void *)0; + } + ent->util = (void *)(what | ((uintptr_t)ent->util)); + return (uintptr_t)ent->util; +} + +static uintptr_t check_symlink_changes(struct apply_state *state, const char *path) +{ + struct string_list_item *ent; + + ent = string_list_lookup(&state->symlink_changes, path); + if (!ent) + return 0; + return (uintptr_t)ent->util; +} + +static void prepare_symlink_changes(struct apply_state *state, struct patch *patch) +{ + for ( ; patch; patch = patch->next) { + if ((patch->old_name && S_ISLNK(patch->old_mode)) && + (patch->is_rename || patch->is_delete)) + /* the symlink at patch->old_name is removed */ + register_symlink_changes(state, patch->old_name, APPLY_SYMLINK_GOES_AWAY); + + if (patch->new_name && S_ISLNK(patch->new_mode)) + /* the symlink at patch->new_name is created or remains */ + register_symlink_changes(state, patch->new_name, APPLY_SYMLINK_IN_RESULT); + } +} + +static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name) +{ + do { + unsigned int change; + + while (--name->len && name->buf[name->len] != '/') + ; /* scan backwards */ + if (!name->len) + break; + name->buf[name->len] = '\0'; + change = check_symlink_changes(state, name->buf); + if (change & APPLY_SYMLINK_IN_RESULT) + return 1; + if (change & APPLY_SYMLINK_GOES_AWAY) + /* + * This cannot be "return 0", because we may + * see a new one created at a higher level. + */ + continue; + + /* otherwise, check the preimage */ + if (state->check_index) { + struct cache_entry *ce; + + ce = cache_file_exists(name->buf, name->len, ignore_case); + if (ce && S_ISLNK(ce->ce_mode)) + return 1; + } else { + struct stat st; + if (!lstat(name->buf, &st) && S_ISLNK(st.st_mode)) + return 1; + } + } while (1); + return 0; +} + +static int path_is_beyond_symlink(struct apply_state *state, const char *name_) +{ + int ret; + struct strbuf name = STRBUF_INIT; + + assert(*name_ != '\0'); + strbuf_addstr(&name, name_); + ret = path_is_beyond_symlink_1(state, &name); + strbuf_release(&name); + + return ret; +} + +static int check_unsafe_path(struct patch *patch) +{ + const char *old_name = NULL; + const char *new_name = NULL; + if (patch->is_delete) + old_name = patch->old_name; + else if (!patch->is_new && !patch->is_copy) + old_name = patch->old_name; + if (!patch->is_delete) + new_name = patch->new_name; + + if (old_name && !verify_path(old_name)) + return error(_("invalid path '%s'"), old_name); + if (new_name && !verify_path(new_name)) + return error(_("invalid path '%s'"), new_name); + return 0; +} + +/* + * Check and apply the patch in-core; leave the result in patch->result + * for the caller to write it out to the final destination. + */ +static int check_patch(struct apply_state *state, struct patch *patch) +{ + struct stat st; + const char *old_name = patch->old_name; + const char *new_name = patch->new_name; + const char *name = old_name ? old_name : new_name; + struct cache_entry *ce = NULL; + struct patch *tpatch; + int ok_if_exists; + int status; + + patch->rejected = 1; /* we will drop this after we succeed */ + + status = check_preimage(state, patch, &ce, &st); + if (status) + return status; + old_name = patch->old_name; + + /* + * A type-change diff is always split into a patch to delete + * old, immediately followed by a patch to create new (see + * diff.c::run_diff()); in such a case it is Ok that the entry + * to be deleted by the previous patch is still in the working + * tree and in the index. + * + * A patch to swap-rename between A and B would first rename A + * to B and then rename B to A. While applying the first one, + * the presence of B should not stop A from getting renamed to + * B; ask to_be_deleted() about the later rename. Removal of + * B and rename from A to B is handled the same way by asking + * was_deleted(). + */ + if ((tpatch = in_fn_table(state, new_name)) && + (was_deleted(tpatch) || to_be_deleted(tpatch))) + ok_if_exists = 1; + else + ok_if_exists = 0; + + if (new_name && + ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) { + int err = check_to_create(state, new_name, ok_if_exists); + + if (err && state->threeway) { + patch->direct_to_threeway = 1; + } else switch (err) { + case 0: + break; /* happy */ + case EXISTS_IN_INDEX: + return error(_("%s: already exists in index"), new_name); + break; + case EXISTS_IN_WORKTREE: + return error(_("%s: already exists in working directory"), + new_name); + default: + return err; + } + + if (!patch->new_mode) { + if (0 < patch->is_new) + patch->new_mode = S_IFREG | 0644; + else + patch->new_mode = patch->old_mode; + } + } + + if (new_name && old_name) { + int same = !strcmp(old_name, new_name); + if (!patch->new_mode) + patch->new_mode = patch->old_mode; + if ((patch->old_mode ^ patch->new_mode) & S_IFMT) { + if (same) + return error(_("new mode (%o) of %s does not " + "match old mode (%o)"), + patch->new_mode, new_name, + patch->old_mode); + else + return error(_("new mode (%o) of %s does not " + "match old mode (%o) of %s"), + patch->new_mode, new_name, + patch->old_mode, old_name); + } + } + + if (!state->unsafe_paths && check_unsafe_path(patch)) + return -128; + + /* + * An attempt to read from or delete a path that is beyond a + * symbolic link will be prevented by load_patch_target() that + * is called at the beginning of apply_data() so we do not + * have to worry about a patch marked with "is_delete" bit + * here. We however need to make sure that the patch result + * is not deposited to a path that is beyond a symbolic link + * here. + */ + if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name)) + return error(_("affected file '%s' is beyond a symbolic link"), + patch->new_name); + + if (apply_data(state, patch, &st, ce) < 0) + return error(_("%s: patch does not apply"), name); + patch->rejected = 0; + return 0; +} + +static int check_patch_list(struct apply_state *state, struct patch *patch) +{ + int err = 0; + + prepare_symlink_changes(state, patch); + prepare_fn_table(state, patch); + while (patch) { + int res; + if (state->apply_verbosely) + say_patch_name(stderr, + _("Checking patch %s..."), patch); + res = check_patch(state, patch); + if (res == -128) + return -128; + err |= res; + patch = patch->next; + } + return err; +} + +/* This function tries to read the sha1 from the current index */ +static int get_current_sha1(const char *path, unsigned char *sha1) +{ + int pos; + + if (read_cache() < 0) + return -1; + pos = cache_name_pos(path, strlen(path)); + if (pos < 0) + return -1; + hashcpy(sha1, active_cache[pos]->sha1); + return 0; +} + +static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20]) +{ + /* + * A usable gitlink patch has only one fragment (hunk) that looks like: + * @@ -1 +1 @@ + * -Subproject commit + * +Subproject commit + * or + * @@ -1 +0,0 @@ + * -Subproject commit + * for a removal patch. + */ + struct fragment *hunk = p->fragments; + static const char heading[] = "-Subproject commit "; + char *preimage; + + if (/* does the patch have only one hunk? */ + hunk && !hunk->next && + /* is its preimage one line? */ + hunk->oldpos == 1 && hunk->oldlines == 1 && + /* does preimage begin with the heading? */ + (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL && + starts_with(++preimage, heading) && + /* does it record full SHA-1? */ + !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) && + preimage[sizeof(heading) + 40 - 1] == '\n' && + /* does the abbreviated name on the index line agree with it? */ + starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix)) + return 0; /* it all looks fine */ + + /* we may have full object name on the index line */ + return get_sha1_hex(p->old_sha1_prefix, sha1); +} + +/* Build an index that contains the just the files needed for a 3way merge */ +static int build_fake_ancestor(struct patch *list, const char *filename) +{ + struct patch *patch; + struct index_state result = { NULL }; + static struct lock_file lock; + int res; + + /* Once we start supporting the reverse patch, it may be + * worth showing the new sha1 prefix, but until then... + */ + for (patch = list; patch; patch = patch->next) { + unsigned char sha1[20]; + struct cache_entry *ce; + const char *name; + + name = patch->old_name ? patch->old_name : patch->new_name; + if (0 < patch->is_new) + continue; + + if (S_ISGITLINK(patch->old_mode)) { + if (!preimage_sha1_in_gitlink_patch(patch, sha1)) + ; /* ok, the textual part looks sane */ + else + return error("sha1 information is lacking or " + "useless for submodule %s", name); + } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) { + ; /* ok */ + } else if (!patch->lines_added && !patch->lines_deleted) { + /* mode-only change: update the current */ + if (get_current_sha1(patch->old_name, sha1)) + return error("mode change for %s, which is not " + "in current HEAD", name); + } else + return error("sha1 information is lacking or useless " + "(%s).", name); + + ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0); + if (!ce) + return error(_("make_cache_entry failed for path '%s'"), + name); + if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) { + free(ce); + return error("Could not add %s to temporary index", + name); + } + } + + hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR); + res = write_locked_index(&result, &lock, COMMIT_LOCK); + discard_index(&result); + + if (res) + return error("Could not write temporary index to %s", filename); + + return 0; +} + +static void stat_patch_list(struct apply_state *state, struct patch *patch) +{ + int files, adds, dels; + + for (files = adds = dels = 0 ; patch ; patch = patch->next) { + files++; + adds += patch->lines_added; + dels += patch->lines_deleted; + show_stats(state, patch); + } + + print_stat_summary(stdout, files, adds, dels); +} + +static void numstat_patch_list(struct apply_state *state, + struct patch *patch) +{ + for ( ; patch; patch = patch->next) { + const char *name; + name = patch->new_name ? patch->new_name : patch->old_name; + if (patch->is_binary) + printf("-\t-\t"); + else + printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); + write_name_quoted(name, stdout, state->line_termination); + } +} + +static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name) +{ + if (mode) + printf(" %s mode %06o %s\n", newdelete, mode, name); + else + printf(" %s %s\n", newdelete, name); +} + +static void show_mode_change(struct patch *p, int show_name) +{ + if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) { + if (show_name) + printf(" mode change %06o => %06o %s\n", + p->old_mode, p->new_mode, p->new_name); + else + printf(" mode change %06o => %06o\n", + p->old_mode, p->new_mode); + } +} + +static void show_rename_copy(struct patch *p) +{ + const char *renamecopy = p->is_rename ? "rename" : "copy"; + const char *old, *new; + + /* Find common prefix */ + old = p->old_name; + new = p->new_name; + while (1) { + const char *slash_old, *slash_new; + slash_old = strchr(old, '/'); + slash_new = strchr(new, '/'); + if (!slash_old || + !slash_new || + slash_old - old != slash_new - new || + memcmp(old, new, slash_new - new)) + break; + old = slash_old + 1; + new = slash_new + 1; + } + /* p->old_name thru old is the common prefix, and old and new + * through the end of names are renames + */ + if (old != p->old_name) + printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy, + (int)(old - p->old_name), p->old_name, + old, new, p->score); + else + printf(" %s %s => %s (%d%%)\n", renamecopy, + p->old_name, p->new_name, p->score); + show_mode_change(p, 0); +} + +static void summary_patch_list(struct patch *patch) +{ + struct patch *p; + + for (p = patch; p; p = p->next) { + if (p->is_new) + show_file_mode_name("create", p->new_mode, p->new_name); + else if (p->is_delete) + show_file_mode_name("delete", p->old_mode, p->old_name); + else { + if (p->is_rename || p->is_copy) + show_rename_copy(p); + else { + if (p->score) { + printf(" rewrite %s (%d%%)\n", + p->new_name, p->score); + show_mode_change(p, 0); + } + else + show_mode_change(p, 1); + } + } + } +} + +static void patch_stats(struct apply_state *state, struct patch *patch) +{ + int lines = patch->lines_added + patch->lines_deleted; + + if (lines > state->max_change) + state->max_change = lines; + if (patch->old_name) { + int len = quote_c_style(patch->old_name, NULL, NULL, 0); + if (!len) + len = strlen(patch->old_name); + if (len > state->max_len) + state->max_len = len; + } + if (patch->new_name) { + int len = quote_c_style(patch->new_name, NULL, NULL, 0); + if (!len) + len = strlen(patch->new_name); + if (len > state->max_len) + state->max_len = len; + } +} + +static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) +{ + if (state->update_index) { + if (remove_file_from_cache(patch->old_name) < 0) + return error(_("unable to remove %s from index"), patch->old_name); + } + if (!state->cached) { + if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { + remove_path(patch->old_name); + } + } + return 0; +} + +static int add_index_file(struct apply_state *state, + const char *path, + unsigned mode, + void *buf, + unsigned long size) +{ + struct stat st; + struct cache_entry *ce; + int namelen = strlen(path); + unsigned ce_size = cache_entry_size(namelen); + + if (!state->update_index) + return 0; + + ce = xcalloc(1, ce_size); + memcpy(ce->name, path, namelen); + ce->ce_mode = create_ce_mode(mode); + ce->ce_flags = create_ce_flags(0); + ce->ce_namelen = namelen; + if (S_ISGITLINK(mode)) { + const char *s; + + if (!skip_prefix(buf, "Subproject commit ", &s) || + get_sha1_hex(s, ce->sha1)) { + free(ce); + return error(_("corrupt patch for submodule %s"), path); + } + } else { + if (!state->cached) { + if (lstat(path, &st) < 0) { + free(ce); + return error(_("unable to stat newly " + "created file '%s': %s"), + path, strerror(errno)); + } + fill_stat_cache_info(ce, &st); + } + if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) { + free(ce); + return error(_("unable to create backing store " + "for newly created file %s"), path); + } + } + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { + free(ce); + return error(_("unable to add cache entry for %s"), path); + } + + return 0; +} + +/* + * Returns: + * -1 if an unrecoverable error happened + * 0 if everything went well + * 1 if a recoverable error happened + */ +static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) +{ + int fd, res; + struct strbuf nbuf = STRBUF_INIT; + + if (S_ISGITLINK(mode)) { + struct stat st; + if (!lstat(path, &st) && S_ISDIR(st.st_mode)) + return 0; + return !!mkdir(path, 0777); + } + + if (has_symlinks && S_ISLNK(mode)) + /* Although buf:size is counted string, it also is NUL + * terminated. + */ + return !!symlink(buf, path); + + fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666); + if (fd < 0) + return 1; + + if (convert_to_working_tree(path, buf, size, &nbuf)) { + size = nbuf.len; + buf = nbuf.buf; + } + + res = write_in_full(fd, buf, size) < 0; + if (res) + error_errno(_("failed to write to '%s'"), path); + strbuf_release(&nbuf); + + if (close(fd) < 0 && !res) + return error_errno(_("closing file '%s'"), path); + + return res ? -1 : 0; +} + +/* + * We optimistically assume that the directories exist, + * which is true 99% of the time anyway. If they don't, + * we create them and try again. + * + * Returns: + * -1 on error + * 0 otherwise + */ +static int create_one_file(struct apply_state *state, + char *path, + unsigned mode, + const char *buf, + unsigned long size) +{ + int res; + + if (state->cached) + return 0; + + res = try_create_file(path, mode, buf, size); + if (res < 0) + return -1; + if (!res) + return 0; + + if (errno == ENOENT) { + if (safe_create_leading_directories(path)) + return 0; + res = try_create_file(path, mode, buf, size); + if (res < 0) + return -1; + if (!res) + return 0; + } + + if (errno == EEXIST || errno == EACCES) { + /* We may be trying to create a file where a directory + * used to be. + */ + struct stat st; + if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path))) + errno = EEXIST; + } + + if (errno == EEXIST) { + unsigned int nr = getpid(); + + for (;;) { + char newpath[PATH_MAX]; + mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr); + res = try_create_file(newpath, mode, buf, size); + if (res < 0) + return -1; + if (!res) { + if (!rename(newpath, path)) + return 0; + unlink_or_warn(newpath); + break; + } + if (errno != EEXIST) + break; + ++nr; + } + } + return error_errno(_("unable to write file '%s' mode %o"), + path, mode); +} + +static int add_conflicted_stages_file(struct apply_state *state, + struct patch *patch) +{ + int stage, namelen; + unsigned ce_size, mode; + struct cache_entry *ce; + + if (!state->update_index) + return 0; + namelen = strlen(patch->new_name); + ce_size = cache_entry_size(namelen); + mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644); + + remove_file_from_cache(patch->new_name); + for (stage = 1; stage < 4; stage++) { + if (is_null_oid(&patch->threeway_stage[stage - 1])) + continue; + ce = xcalloc(1, ce_size); + memcpy(ce->name, patch->new_name, namelen); + ce->ce_mode = create_ce_mode(mode); + ce->ce_flags = create_ce_flags(stage); + ce->ce_namelen = namelen; + hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { + free(ce); + return error(_("unable to add cache entry for %s"), + patch->new_name); + } + } + + return 0; +} + +static int create_file(struct apply_state *state, struct patch *patch) +{ + char *path = patch->new_name; + unsigned mode = patch->new_mode; + unsigned long size = patch->resultsize; + char *buf = patch->result; + + if (!mode) + mode = S_IFREG | 0644; + if (create_one_file(state, path, mode, buf, size)) + return -1; + + if (patch->conflicted_threeway) + return add_conflicted_stages_file(state, patch); + else + return add_index_file(state, path, mode, buf, size); +} + +/* phase zero is to remove, phase one is to create */ +static int write_out_one_result(struct apply_state *state, + struct patch *patch, + int phase) +{ + if (patch->is_delete > 0) { + if (phase == 0) + return remove_file(state, patch, 1); + return 0; + } + if (patch->is_new > 0 || patch->is_copy) { + if (phase == 1) + return create_file(state, patch); + return 0; + } + /* + * Rename or modification boils down to the same + * thing: remove the old, write the new + */ + if (phase == 0) + return remove_file(state, patch, patch->is_rename); + if (phase == 1) + return create_file(state, patch); + return 0; +} + +static int write_out_one_reject(struct apply_state *state, struct patch *patch) +{ + FILE *rej; + char namebuf[PATH_MAX]; + struct fragment *frag; + int cnt = 0; + struct strbuf sb = STRBUF_INIT; + + for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) { + if (!frag->rejected) + continue; + cnt++; + } + + if (!cnt) { + if (state->apply_verbosely) + say_patch_name(stderr, + _("Applied patch %s cleanly."), patch); + return 0; + } + + /* This should not happen, because a removal patch that leaves + * contents are marked "rejected" at the patch level. + */ + if (!patch->new_name) + die(_("internal error")); + + /* Say this even without --verbose */ + strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...", + "Applying patch %%s with %d rejects...", + cnt), + cnt); + say_patch_name(stderr, sb.buf, patch); + strbuf_release(&sb); + + cnt = strlen(patch->new_name); + if (ARRAY_SIZE(namebuf) <= cnt + 5) { + cnt = ARRAY_SIZE(namebuf) - 5; + warning(_("truncating .rej filename to %.*s.rej"), + cnt - 1, patch->new_name); + } + memcpy(namebuf, patch->new_name, cnt); + memcpy(namebuf + cnt, ".rej", 5); + + rej = fopen(namebuf, "w"); + if (!rej) + return error(_("cannot open %s: %s"), namebuf, strerror(errno)); + + /* Normal git tools never deal with .rej, so do not pretend + * this is a git patch by saying --git or giving extended + * headers. While at it, maybe please "kompare" that wants + * the trailing TAB and some garbage at the end of line ;-). + */ + fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n", + patch->new_name, patch->new_name); + for (cnt = 1, frag = patch->fragments; + frag; + cnt++, frag = frag->next) { + if (!frag->rejected) { + fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt); + continue; + } + fprintf_ln(stderr, _("Rejected hunk #%d."), cnt); + fprintf(rej, "%.*s", frag->size, frag->patch); + if (frag->patch[frag->size-1] != '\n') + fputc('\n', rej); + } + fclose(rej); + return -1; +} + +/* + * Returns: + * -1 if an error happened + * 0 if the patch applied cleanly + * 1 if the patch did not apply cleanly + */ +static int write_out_results(struct apply_state *state, struct patch *list) +{ + int phase; + int errs = 0; + struct patch *l; + struct string_list cpath = STRING_LIST_INIT_DUP; + + for (phase = 0; phase < 2; phase++) { + l = list; + while (l) { + if (l->rejected) + errs = 1; + else { + if (write_out_one_result(state, l, phase)) { + string_list_clear(&cpath, 0); + return -1; + } + if (phase == 1) { + if (write_out_one_reject(state, l)) + errs = 1; + if (l->conflicted_threeway) { + string_list_append(&cpath, l->new_name); + errs = 1; + } + } + } + l = l->next; + } + } + + if (cpath.nr) { + struct string_list_item *item; + + string_list_sort(&cpath); + for_each_string_list_item(item, &cpath) + fprintf(stderr, "U %s\n", item->string); + string_list_clear(&cpath, 0); + + rerere(0); + } + + return errs; +} + +/* + * Try to apply a patch. + * + * Returns: + * -128 if a bad error happened (like patch unreadable) + * -1 if patch did not apply and user cannot deal with it + * 0 if the patch applied + * 1 if the patch did not apply but user might fix it + */ +static int apply_patch(struct apply_state *state, + int fd, + const char *filename, + int options) +{ + size_t offset; + struct strbuf buf = STRBUF_INIT; /* owns the patch text */ + struct patch *list = NULL, **listp = &list; + int skipped_patch = 0; + int res = 0; + + state->patch_input_file = filename; + if (read_patch_file(&buf, fd) < 0) + return -128; + offset = 0; + while (offset < buf.len) { + struct patch *patch; + int nr; + + patch = xcalloc(1, sizeof(*patch)); + patch->inaccurate_eof = !!(options & APPLY_OPT_INACCURATE_EOF); + patch->recount = !!(options & APPLY_OPT_RECOUNT); + nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); + if (nr < 0) { + free_patch(patch); + if (nr == -128) { + res = -128; + goto end; + } + break; + } + if (state->apply_in_reverse) + reverse_patches(patch); + if (use_patch(state, patch)) { + patch_stats(state, patch); + *listp = patch; + listp = &patch->next; + } + else { + if (state->apply_verbosely) + say_patch_name(stderr, _("Skipped patch '%s'."), patch); + free_patch(patch); + skipped_patch++; + } + offset += nr; + } + + if (!list && !skipped_patch) { + error(_("unrecognized input")); + res = -128; + goto end; + } + + if (state->whitespace_error && (state->ws_error_action == die_on_ws_error)) + state->apply = 0; + + state->update_index = state->check_index && state->apply; + if (state->update_index && state->newfd < 0) + state->newfd = hold_locked_index(state->lock_file, 1); + + if (state->check_index && read_cache() < 0) { + error(_("unable to read index file")); + res = -128; + goto end; + } + + if (state->check || state->apply) { + int r = check_patch_list(state, list); + if (r == -128) { + res = -128; + goto end; + } + if (r < 0 && !state->apply_with_reject) { + res = -1; + goto end; + } + } + + if (state->apply) { + int write_res = write_out_results(state, list); + if (write_res < 0) { + res = -128; + goto end; + } + if (write_res > 0) { + /* with --3way, we still need to write the index out */ + res = state->apply_with_reject ? -1 : 1; + goto end; + } + } + + if (state->fake_ancestor && + build_fake_ancestor(list, state->fake_ancestor)) { + res = -128; + goto end; + } + + if (state->diffstat) + stat_patch_list(state, list); + + if (state->numstat) + numstat_patch_list(state, list); + + if (state->summary) + summary_patch_list(list); + +end: + free_patch_list(list); + strbuf_release(&buf); + string_list_clear(&state->fn_table, 0); + return res; +} + +int apply_option_parse_exclude(const struct option *opt, + const char *arg, int unset) +{ + struct apply_state *state = opt->value; + add_name_limit(state, arg, 1); + return 0; +} + +int apply_option_parse_include(const struct option *opt, + const char *arg, int unset) +{ + struct apply_state *state = opt->value; + add_name_limit(state, arg, 0); + state->has_include = 1; + return 0; +} + +int apply_option_parse_p(const struct option *opt, + const char *arg, + int unset) +{ + struct apply_state *state = opt->value; + state->p_value = atoi(arg); + state->p_value_known = 1; + return 0; +} + +int apply_option_parse_space_change(const struct option *opt, + const char *arg, int unset) +{ + struct apply_state *state = opt->value; + if (unset) + state->ws_ignore_action = ignore_ws_none; + else + state->ws_ignore_action = ignore_ws_change; + return 0; +} + +int apply_option_parse_whitespace(const struct option *opt, + const char *arg, int unset) +{ + struct apply_state *state = opt->value; + state->whitespace_option = arg; + if (parse_whitespace_option(state, arg)) + exit(1); + return 0; +} + +int apply_option_parse_directory(const struct option *opt, + const char *arg, int unset) +{ + struct apply_state *state = opt->value; + strbuf_reset(&state->root); + strbuf_addstr(&state->root, arg); + strbuf_complete(&state->root, '/'); + return 0; +} + +int apply_all_patches(struct apply_state *state, + int argc, + const char **argv, + int options) +{ + int i; + int res; + int errs = 0; + int read_stdin = 1; + + for (i = 0; i < argc; i++) { + const char *arg = argv[i]; + int fd; + + if (!strcmp(arg, "-")) { + res = apply_patch(state, 0, "", options); + if (res < 0) + goto end; + errs |= res; + read_stdin = 0; + continue; + } else if (0 < state->prefix_length) + arg = prefix_filename(state->prefix, + state->prefix_length, + arg); + + fd = open(arg, O_RDONLY); + if (fd < 0) { + error(_("can't open patch '%s': %s"), arg, strerror(errno)); + res = -128; + goto end; + } + read_stdin = 0; + set_default_whitespace_mode(state); + res = apply_patch(state, fd, arg, options); + close(fd); + if (res < 0) + goto end; + errs |= res; + } + set_default_whitespace_mode(state); + if (read_stdin) { + res = apply_patch(state, 0, "", options); + if (res < 0) + goto end; + errs |= res; + } + + if (state->whitespace_error) { + if (state->squelch_whitespace_errors && + state->squelch_whitespace_errors < state->whitespace_error) { + int squelched = + state->whitespace_error - state->squelch_whitespace_errors; + warning(Q_("squelched %d whitespace error", + "squelched %d whitespace errors", + squelched), + squelched); + } + if (state->ws_error_action == die_on_ws_error) { + error(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + state->whitespace_error), + state->whitespace_error); + res = -128; + goto end; + } + if (state->applied_after_fixing_ws && state->apply) + warning("%d line%s applied after" + " fixing whitespace errors.", + state->applied_after_fixing_ws, + state->applied_after_fixing_ws == 1 ? "" : "s"); + else if (state->whitespace_error) + warning(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + state->whitespace_error), + state->whitespace_error); + } + + if (state->update_index) { + res = write_locked_index(&the_index, state->lock_file, COMMIT_LOCK); + if (res) { + error(_("Unable to write new index file")); + res = -128; + goto end; + } + state->newfd = -1; + } + + return !!errs; + +end: + if (state->newfd >= 0) { + rollback_lock_file(state->lock_file); + state->newfd = -1; + } + + return (res == -1 ? 1 : 128); +} diff --git a/apply.h b/apply.h index 48abd8e..28cbe6c 100644 --- a/apply.h +++ b/apply.h @@ -102,6 +102,20 @@ extern int parse_whitespace_option(struct apply_state *state, extern int parse_ignorewhitespace_option(struct apply_state *state, const char *option); +extern int apply_option_parse_exclude(const struct option *opt, + const char *arg, int unset); +extern int apply_option_parse_include(const struct option *opt, + const char *arg, int unset); +extern int apply_option_parse_p(const struct option *opt, + const char *arg, + int unset); +extern int apply_option_parse_whitespace(const struct option *opt, + const char *arg, int unset); +extern int apply_option_parse_directory(const struct option *opt, + const char *arg, int unset); +extern int apply_option_parse_space_change(const struct option *opt, + const char *arg, int unset); + extern int init_apply_state(struct apply_state *state, const char *prefix, struct lock_file *lock_file); @@ -115,4 +129,9 @@ extern int check_apply_state(struct apply_state *state, int force_apply); #define APPLY_OPT_INACCURATE_EOF (1<<0) /* accept inaccurate eof */ #define APPLY_OPT_RECOUNT (1<<1) /* accept inaccurate line count */ +extern int apply_all_patches(struct apply_state *state, + int argc, + const char **argv, + int options); + #endif diff --git a/builtin/apply.c b/builtin/apply.c index da31af2..9c66474 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1,25 +1,7 @@ -/* - * apply.c - * - * Copyright (C) Linus Torvalds, 2005 - * - * This applies patches on top of some (arbitrary) version of the SCM. - * - */ #include "cache.h" -#include "lockfile.h" -#include "cache-tree.h" -#include "quote.h" -#include "blob.h" -#include "delta.h" #include "builtin.h" -#include "string-list.h" -#include "dir.h" -#include "diff.h" #include "parse-options.h" -#include "xdiff-interface.h" -#include "ll-merge.h" -#include "rerere.h" +#include "lockfile.h" #include "apply.h" static const char * const apply_usage[] = { @@ -27,4721 +9,8 @@ static const char * const apply_usage[] = { NULL }; -static void set_default_whitespace_mode(struct apply_state *state) -{ - if (!state->whitespace_option && !apply_default_whitespace) - state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); -} - -/* - * This represents one "hunk" from a patch, starting with - * "@@ -oldpos,oldlines +newpos,newlines @@" marker. The - * patch text is pointed at by patch, and its byte length - * is stored in size. leading and trailing are the number - * of context lines. - */ -struct fragment { - unsigned long leading, trailing; - unsigned long oldpos, oldlines; - unsigned long newpos, newlines; - /* - * 'patch' is usually borrowed from buf in apply_patch(), - * but some codepaths store an allocated buffer. - */ - const char *patch; - unsigned free_patch:1, - rejected:1; - int size; - int linenr; - struct fragment *next; -}; - -/* - * When dealing with a binary patch, we reuse "leading" field - * to store the type of the binary hunk, either deflated "delta" - * or deflated "literal". - */ -#define binary_patch_method leading -#define BINARY_DELTA_DEFLATED 1 -#define BINARY_LITERAL_DEFLATED 2 - -/* - * This represents a "patch" to a file, both metainfo changes - * such as creation/deletion, filemode and content changes represented - * as a series of fragments. - */ -struct patch { - char *new_name, *old_name, *def_name; - unsigned int old_mode, new_mode; - int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */ - int rejected; - unsigned ws_rule; - int lines_added, lines_deleted; - int score; - unsigned int is_toplevel_relative:1; - unsigned int inaccurate_eof:1; - unsigned int is_binary:1; - unsigned int is_copy:1; - unsigned int is_rename:1; - unsigned int recount:1; - unsigned int conflicted_threeway:1; - unsigned int direct_to_threeway:1; - struct fragment *fragments; - char *result; - size_t resultsize; - char old_sha1_prefix[41]; - char new_sha1_prefix[41]; - struct patch *next; - - /* three-way fallback result */ - struct object_id threeway_stage[3]; -}; - -static void free_fragment_list(struct fragment *list) -{ - while (list) { - struct fragment *next = list->next; - if (list->free_patch) - free((char *)list->patch); - free(list); - list = next; - } -} - -static void free_patch(struct patch *patch) -{ - free_fragment_list(patch->fragments); - free(patch->def_name); - free(patch->old_name); - free(patch->new_name); - free(patch->result); - free(patch); -} - -static void free_patch_list(struct patch *list) -{ - while (list) { - struct patch *next = list->next; - free_patch(list); - list = next; - } -} - -/* - * A line in a file, len-bytes long (includes the terminating LF, - * except for an incomplete line at the end if the file ends with - * one), and its contents hashes to 'hash'. - */ -struct line { - size_t len; - unsigned hash : 24; - unsigned flag : 8; -#define LINE_COMMON 1 -#define LINE_PATCHED 2 -}; - -/* - * This represents a "file", which is an array of "lines". - */ -struct image { - char *buf; - size_t len; - size_t nr; - size_t alloc; - struct line *line_allocated; - struct line *line; -}; - -static uint32_t hash_line(const char *cp, size_t len) -{ - size_t i; - uint32_t h; - for (i = 0, h = 0; i < len; i++) { - if (!isspace(cp[i])) { - h = h * 3 + (cp[i] & 0xff); - } - } - return h; -} - -/* - * Compare lines s1 of length n1 and s2 of length n2, ignoring - * whitespace difference. Returns 1 if they match, 0 otherwise - */ -static int fuzzy_matchlines(const char *s1, size_t n1, - const char *s2, size_t n2) -{ - const char *last1 = s1 + n1 - 1; - const char *last2 = s2 + n2 - 1; - int result = 0; - - /* ignore line endings */ - while ((*last1 == '\r') || (*last1 == '\n')) - last1--; - while ((*last2 == '\r') || (*last2 == '\n')) - last2--; - - /* skip leading whitespaces, if both begin with whitespace */ - if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) { - while (isspace(*s1) && (s1 <= last1)) - s1++; - while (isspace(*s2) && (s2 <= last2)) - s2++; - } - /* early return if both lines are empty */ - if ((s1 > last1) && (s2 > last2)) - return 1; - while (!result) { - result = *s1++ - *s2++; - /* - * Skip whitespace inside. We check for whitespace on - * both buffers because we don't want "a b" to match - * "ab" - */ - if (isspace(*s1) && isspace(*s2)) { - while (isspace(*s1) && s1 <= last1) - s1++; - while (isspace(*s2) && s2 <= last2) - s2++; - } - /* - * If we reached the end on one side only, - * lines don't match - */ - if ( - ((s2 > last2) && (s1 <= last1)) || - ((s1 > last1) && (s2 <= last2))) - return 0; - if ((s1 > last1) && (s2 > last2)) - break; - } - - return !result; -} - -static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag) -{ - ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc); - img->line_allocated[img->nr].len = len; - img->line_allocated[img->nr].hash = hash_line(bol, len); - img->line_allocated[img->nr].flag = flag; - img->nr++; -} - -/* - * "buf" has the file contents to be patched (read from various sources). - * attach it to "image" and add line-based index to it. - * "image" now owns the "buf". - */ -static void prepare_image(struct image *image, char *buf, size_t len, - int prepare_linetable) -{ - const char *cp, *ep; - - memset(image, 0, sizeof(*image)); - image->buf = buf; - image->len = len; - - if (!prepare_linetable) - return; - - ep = image->buf + image->len; - cp = image->buf; - while (cp < ep) { - const char *next; - for (next = cp; next < ep && *next != '\n'; next++) - ; - if (next < ep) - next++; - add_line_info(image, cp, next - cp, 0); - cp = next; - } - image->line = image->line_allocated; -} - -static void clear_image(struct image *image) -{ - free(image->buf); - free(image->line_allocated); - memset(image, 0, sizeof(*image)); -} - -/* fmt must contain _one_ %s and no other substitution */ -static void say_patch_name(FILE *output, const char *fmt, struct patch *patch) -{ - struct strbuf sb = STRBUF_INIT; - - if (patch->old_name && patch->new_name && - strcmp(patch->old_name, patch->new_name)) { - quote_c_style(patch->old_name, &sb, NULL, 0); - strbuf_addstr(&sb, " => "); - quote_c_style(patch->new_name, &sb, NULL, 0); - } else { - const char *n = patch->new_name; - if (!n) - n = patch->old_name; - quote_c_style(n, &sb, NULL, 0); - } - fprintf(output, fmt, sb.buf); - fputc('\n', output); - strbuf_release(&sb); -} - -#define SLOP (16) - -static int read_patch_file(struct strbuf *sb, int fd) -{ - if (strbuf_read(sb, fd, 0) < 0) - return error_errno("git apply: failed to read"); - - /* - * Make sure that we have some slop in the buffer - * so that we can do speculative "memcmp" etc, and - * see to it that it is NUL-filled. - */ - strbuf_grow(sb, SLOP); - memset(sb->buf + sb->len, 0, SLOP); - return 0; -} - -static unsigned long linelen(const char *buffer, unsigned long size) -{ - unsigned long len = 0; - while (size--) { - len++; - if (*buffer++ == '\n') - break; - } - return len; -} - -static int is_dev_null(const char *str) -{ - return skip_prefix(str, "/dev/null", &str) && isspace(*str); -} - -#define TERM_SPACE 1 -#define TERM_TAB 2 - -static int name_terminate(int c, int terminate) -{ - if (c == ' ' && !(terminate & TERM_SPACE)) - return 0; - if (c == '\t' && !(terminate & TERM_TAB)) - return 0; - - return 1; -} - -/* remove double slashes to make --index work with such filenames */ -static char *squash_slash(char *name) -{ - int i = 0, j = 0; - - if (!name) - return NULL; - - while (name[i]) { - if ((name[j++] = name[i++]) == '/') - while (name[i] == '/') - i++; - } - name[j] = '\0'; - return name; -} - -static char *find_name_gnu(struct apply_state *state, - const char *line, - const char *def, - int p_value) -{ - struct strbuf name = STRBUF_INIT; - char *cp; - - /* - * Proposed "new-style" GNU patch/diff format; see - * http://marc.info/?l=git&m=112927316408690&w=2 - */ - if (unquote_c_style(&name, line, NULL)) { - strbuf_release(&name); - return NULL; - } - - for (cp = name.buf; p_value; p_value--) { - cp = strchr(cp, '/'); - if (!cp) { - strbuf_release(&name); - return NULL; - } - cp++; - } - - strbuf_remove(&name, 0, cp - name.buf); - if (state->root.len) - strbuf_insert(&name, 0, state->root.buf, state->root.len); - return squash_slash(strbuf_detach(&name, NULL)); -} - -static size_t sane_tz_len(const char *line, size_t len) -{ - const char *tz, *p; - - if (len < strlen(" +0500") || line[len-strlen(" +0500")] != ' ') - return 0; - tz = line + len - strlen(" +0500"); - - if (tz[1] != '+' && tz[1] != '-') - return 0; - - for (p = tz + 2; p != line + len; p++) - if (!isdigit(*p)) - return 0; - - return line + len - tz; -} - -static size_t tz_with_colon_len(const char *line, size_t len) -{ - const char *tz, *p; - - if (len < strlen(" +08:00") || line[len - strlen(":00")] != ':') - return 0; - tz = line + len - strlen(" +08:00"); - - if (tz[0] != ' ' || (tz[1] != '+' && tz[1] != '-')) - return 0; - p = tz + 2; - if (!isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || - !isdigit(*p++) || !isdigit(*p++)) - return 0; - - return line + len - tz; -} - -static size_t date_len(const char *line, size_t len) -{ - const char *date, *p; - - if (len < strlen("72-02-05") || line[len-strlen("-05")] != '-') - return 0; - p = date = line + len - strlen("72-02-05"); - - if (!isdigit(*p++) || !isdigit(*p++) || *p++ != '-' || - !isdigit(*p++) || !isdigit(*p++) || *p++ != '-' || - !isdigit(*p++) || !isdigit(*p++)) /* Not a date. */ - return 0; - - if (date - line >= strlen("19") && - isdigit(date[-1]) && isdigit(date[-2])) /* 4-digit year */ - date -= strlen("19"); - - return line + len - date; -} - -static size_t short_time_len(const char *line, size_t len) -{ - const char *time, *p; - - if (len < strlen(" 07:01:32") || line[len-strlen(":32")] != ':') - return 0; - p = time = line + len - strlen(" 07:01:32"); - - /* Permit 1-digit hours? */ - if (*p++ != ' ' || - !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || - !isdigit(*p++) || !isdigit(*p++) || *p++ != ':' || - !isdigit(*p++) || !isdigit(*p++)) /* Not a time. */ - return 0; - - return line + len - time; -} - -static size_t fractional_time_len(const char *line, size_t len) -{ - const char *p; - size_t n; - - /* Expected format: 19:41:17.620000023 */ - if (!len || !isdigit(line[len - 1])) - return 0; - p = line + len - 1; - - /* Fractional seconds. */ - while (p > line && isdigit(*p)) - p--; - if (*p != '.') - return 0; - - /* Hours, minutes, and whole seconds. */ - n = short_time_len(line, p - line); - if (!n) - return 0; - - return line + len - p + n; -} - -static size_t trailing_spaces_len(const char *line, size_t len) -{ - const char *p; - - /* Expected format: ' ' x (1 or more) */ - if (!len || line[len - 1] != ' ') - return 0; - - p = line + len; - while (p != line) { - p--; - if (*p != ' ') - return line + len - (p + 1); - } - - /* All spaces! */ - return len; -} - -static size_t diff_timestamp_len(const char *line, size_t len) -{ - const char *end = line + len; - size_t n; - - /* - * Posix: 2010-07-05 19:41:17 - * GNU: 2010-07-05 19:41:17.620000023 -0500 - */ - - if (!isdigit(end[-1])) - return 0; - - n = sane_tz_len(line, end - line); - if (!n) - n = tz_with_colon_len(line, end - line); - end -= n; - - n = short_time_len(line, end - line); - if (!n) - n = fractional_time_len(line, end - line); - end -= n; - - n = date_len(line, end - line); - if (!n) /* No date. Too bad. */ - return 0; - end -= n; - - if (end == line) /* No space before date. */ - return 0; - if (end[-1] == '\t') { /* Success! */ - end--; - return line + len - end; - } - if (end[-1] != ' ') /* No space before date. */ - return 0; - - /* Whitespace damage. */ - end -= trailing_spaces_len(line, end - line); - return line + len - end; -} - -static char *find_name_common(struct apply_state *state, - const char *line, - const char *def, - int p_value, - const char *end, - int terminate) -{ - int len; - const char *start = NULL; - - if (p_value == 0) - start = line; - while (line != end) { - char c = *line; - - if (!end && isspace(c)) { - if (c == '\n') - break; - if (name_terminate(c, terminate)) - break; - } - line++; - if (c == '/' && !--p_value) - start = line; - } - if (!start) - return squash_slash(xstrdup_or_null(def)); - len = line - start; - if (!len) - return squash_slash(xstrdup_or_null(def)); - - /* - * Generally we prefer the shorter name, especially - * if the other one is just a variation of that with - * something else tacked on to the end (ie "file.orig" - * or "file~"). - */ - if (def) { - int deflen = strlen(def); - if (deflen < len && !strncmp(start, def, deflen)) - return squash_slash(xstrdup(def)); - } - - if (state->root.len) { - char *ret = xstrfmt("%s%.*s", state->root.buf, len, start); - return squash_slash(ret); - } - - return squash_slash(xmemdupz(start, len)); -} - -static char *find_name(struct apply_state *state, - const char *line, - char *def, - int p_value, - int terminate) -{ - if (*line == '"') { - char *name = find_name_gnu(state, line, def, p_value); - if (name) - return name; - } - - return find_name_common(state, line, def, p_value, NULL, terminate); -} - -static char *find_name_traditional(struct apply_state *state, - const char *line, - char *def, - int p_value) -{ - size_t len; - size_t date_len; - - if (*line == '"') { - char *name = find_name_gnu(state, line, def, p_value); - if (name) - return name; - } - - len = strchrnul(line, '\n') - line; - date_len = diff_timestamp_len(line, len); - if (!date_len) - return find_name_common(state, line, def, p_value, NULL, TERM_TAB); - len -= date_len; - - return find_name_common(state, line, def, p_value, line + len, 0); -} - -static int count_slashes(const char *cp) -{ - int cnt = 0; - char ch; - - while ((ch = *cp++)) - if (ch == '/') - cnt++; - return cnt; -} - -/* - * Given the string after "--- " or "+++ ", guess the appropriate - * p_value for the given patch. - */ -static int guess_p_value(struct apply_state *state, const char *nameline) -{ - char *name, *cp; - int val = -1; - - if (is_dev_null(nameline)) - return -1; - name = find_name_traditional(state, nameline, NULL, 0); - if (!name) - return -1; - cp = strchr(name, '/'); - if (!cp) - val = 0; - else if (state->prefix) { - /* - * Does it begin with "a/$our-prefix" and such? Then this is - * very likely to apply to our directory. - */ - if (!strncmp(name, state->prefix, state->prefix_length)) - val = count_slashes(state->prefix); - else { - cp++; - if (!strncmp(cp, state->prefix, state->prefix_length)) - val = count_slashes(state->prefix) + 1; - } - } - free(name); - return val; -} - -/* - * Does the ---/+++ line have the POSIX timestamp after the last HT? - * GNU diff puts epoch there to signal a creation/deletion event. Is - * this such a timestamp? - */ -static int has_epoch_timestamp(const char *nameline) -{ - /* - * We are only interested in epoch timestamp; any non-zero - * fraction cannot be one, hence "(\.0+)?" in the regexp below. - * For the same reason, the date must be either 1969-12-31 or - * 1970-01-01, and the seconds part must be "00". - */ - const char stamp_regexp[] = - "^(1969-12-31|1970-01-01)" - " " - "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?" - " " - "([-+][0-2][0-9]:?[0-5][0-9])\n"; - const char *timestamp = NULL, *cp, *colon; - static regex_t *stamp; - regmatch_t m[10]; - int zoneoffset; - int hourminute; - int status; - - for (cp = nameline; *cp != '\n'; cp++) { - if (*cp == '\t') - timestamp = cp + 1; - } - if (!timestamp) - return 0; - if (!stamp) { - stamp = xmalloc(sizeof(*stamp)); - if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) { - warning(_("Cannot prepare timestamp regexp %s"), - stamp_regexp); - return 0; - } - } - - status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0); - if (status) { - if (status != REG_NOMATCH) - warning(_("regexec returned %d for input: %s"), - status, timestamp); - return 0; - } - - zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10); - if (*colon == ':') - zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10); - else - zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100); - if (timestamp[m[3].rm_so] == '-') - zoneoffset = -zoneoffset; - - /* - * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31 - * (west of GMT) or 1970-01-01 (east of GMT) - */ - if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) || - (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10))) - return 0; - - hourminute = (strtol(timestamp + 11, NULL, 10) * 60 + - strtol(timestamp + 14, NULL, 10) - - zoneoffset); - - return ((zoneoffset < 0 && hourminute == 1440) || - (0 <= zoneoffset && !hourminute)); -} - -/* - * Get the name etc info from the ---/+++ lines of a traditional patch header - * - * FIXME! The end-of-filename heuristics are kind of screwy. For existing - * files, we can happily check the index for a match, but for creating a - * new file we should try to match whatever "patch" does. I have no idea. - */ -static int parse_traditional_patch(struct apply_state *state, - const char *first, - const char *second, - struct patch *patch) -{ - char *name; - - first += 4; /* skip "--- " */ - second += 4; /* skip "+++ " */ - if (!state->p_value_known) { - int p, q; - p = guess_p_value(state, first); - q = guess_p_value(state, second); - if (p < 0) p = q; - if (0 <= p && p == q) { - state->p_value = p; - state->p_value_known = 1; - } - } - if (is_dev_null(first)) { - patch->is_new = 1; - patch->is_delete = 0; - name = find_name_traditional(state, second, NULL, state->p_value); - patch->new_name = name; - } else if (is_dev_null(second)) { - patch->is_new = 0; - patch->is_delete = 1; - name = find_name_traditional(state, first, NULL, state->p_value); - patch->old_name = name; - } else { - char *first_name; - first_name = find_name_traditional(state, first, NULL, state->p_value); - name = find_name_traditional(state, second, first_name, state->p_value); - free(first_name); - if (has_epoch_timestamp(first)) { - patch->is_new = 1; - patch->is_delete = 0; - patch->new_name = name; - } else if (has_epoch_timestamp(second)) { - patch->is_new = 0; - patch->is_delete = 1; - patch->old_name = name; - } else { - patch->old_name = name; - patch->new_name = xstrdup_or_null(name); - } - } - if (!name) - return error(_("unable to find filename in patch at line %d"), state->linenr); - - return 0; -} - -static int gitdiff_hdrend(struct apply_state *state, - const char *line, - struct patch *patch) -{ - return 1; -} - -/* - * We're anal about diff header consistency, to make - * sure that we don't end up having strange ambiguous - * patches floating around. - * - * As a result, gitdiff_{old|new}name() will check - * their names against any previous information, just - * to make sure.. - */ -#define DIFF_OLD_NAME 0 -#define DIFF_NEW_NAME 1 - -static int gitdiff_verify_name(struct apply_state *state, - const char *line, - int isnull, - char **name, - int side) -{ - if (!*name && !isnull) { - *name = find_name(state, line, NULL, state->p_value, TERM_TAB); - return 0; - } - - if (*name) { - int len = strlen(*name); - char *another; - if (isnull) - return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), - *name, state->linenr); - another = find_name(state, line, NULL, state->p_value, TERM_TAB); - if (!another || memcmp(another, *name, len + 1)) { - free(another); - return error((side == DIFF_NEW_NAME) ? - _("git apply: bad git-diff - inconsistent new filename on line %d") : - _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr); - } - free(another); - } else { - /* expect "/dev/null" */ - if (memcmp("/dev/null", line, 9) || line[9] != '\n') - return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); - } - - return 0; -} - -static int gitdiff_oldname(struct apply_state *state, - const char *line, - struct patch *patch) -{ - return gitdiff_verify_name(state, line, - patch->is_new, &patch->old_name, - DIFF_OLD_NAME); -} - -static int gitdiff_newname(struct apply_state *state, - const char *line, - struct patch *patch) -{ - return gitdiff_verify_name(state, line, - patch->is_delete, &patch->new_name, - DIFF_NEW_NAME); -} - -static int gitdiff_oldmode(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->old_mode = strtoul(line, NULL, 8); - return 0; -} - -static int gitdiff_newmode(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->new_mode = strtoul(line, NULL, 8); - return 0; -} - -static int gitdiff_delete(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_delete = 1; - free(patch->old_name); - patch->old_name = xstrdup_or_null(patch->def_name); - return gitdiff_oldmode(state, line, patch); -} - -static int gitdiff_newfile(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_new = 1; - free(patch->new_name); - patch->new_name = xstrdup_or_null(patch->def_name); - return gitdiff_newmode(state, line, patch); -} - -static int gitdiff_copysrc(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_copy = 1; - free(patch->old_name); - patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_copydst(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_copy = 1; - free(patch->new_name); - patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_renamesrc(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_rename = 1; - free(patch->old_name); - patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_renamedst(struct apply_state *state, - const char *line, - struct patch *patch) -{ - patch->is_rename = 1; - free(patch->new_name); - patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); - return 0; -} - -static int gitdiff_similarity(struct apply_state *state, - const char *line, - struct patch *patch) -{ - unsigned long val = strtoul(line, NULL, 10); - if (val <= 100) - patch->score = val; - return 0; -} - -static int gitdiff_dissimilarity(struct apply_state *state, - const char *line, - struct patch *patch) -{ - unsigned long val = strtoul(line, NULL, 10); - if (val <= 100) - patch->score = val; - return 0; -} - -static int gitdiff_index(struct apply_state *state, - const char *line, - struct patch *patch) -{ - /* - * index line is N hexadecimal, "..", N hexadecimal, - * and optional space with octal mode. - */ - const char *ptr, *eol; - int len; - - ptr = strchr(line, '.'); - if (!ptr || ptr[1] != '.' || 40 < ptr - line) - return 0; - len = ptr - line; - memcpy(patch->old_sha1_prefix, line, len); - patch->old_sha1_prefix[len] = 0; - - line = ptr + 2; - ptr = strchr(line, ' '); - eol = strchrnul(line, '\n'); - - if (!ptr || eol < ptr) - ptr = eol; - len = ptr - line; - - if (40 < len) - return 0; - memcpy(patch->new_sha1_prefix, line, len); - patch->new_sha1_prefix[len] = 0; - if (*ptr == ' ') - patch->old_mode = strtoul(ptr+1, NULL, 8); - return 0; -} - -/* - * This is normal for a diff that doesn't change anything: we'll fall through - * into the next diff. Tell the parser to break out. - */ -static int gitdiff_unrecognized(struct apply_state *state, - const char *line, - struct patch *patch) -{ - return 1; -} - -/* - * Skip p_value leading components from "line"; as we do not accept - * absolute paths, return NULL in that case. - */ -static const char *skip_tree_prefix(struct apply_state *state, - const char *line, - int llen) -{ - int nslash; - int i; - - if (!state->p_value) - return (llen && line[0] == '/') ? NULL : line; - - nslash = state->p_value; - for (i = 0; i < llen; i++) { - int ch = line[i]; - if (ch == '/' && --nslash <= 0) - return (i == 0) ? NULL : &line[i + 1]; - } - return NULL; -} - -/* - * This is to extract the same name that appears on "diff --git" - * line. We do not find and return anything if it is a rename - * patch, and it is OK because we will find the name elsewhere. - * We need to reliably find name only when it is mode-change only, - * creation or deletion of an empty file. In any of these cases, - * both sides are the same name under a/ and b/ respectively. - */ -static char *git_header_name(struct apply_state *state, - const char *line, - int llen) -{ - const char *name; - const char *second = NULL; - size_t len, line_len; - - line += strlen("diff --git "); - llen -= strlen("diff --git "); - - if (*line == '"') { - const char *cp; - struct strbuf first = STRBUF_INIT; - struct strbuf sp = STRBUF_INIT; - - if (unquote_c_style(&first, line, &second)) - goto free_and_fail1; - - /* strip the a/b prefix including trailing slash */ - cp = skip_tree_prefix(state, first.buf, first.len); - if (!cp) - goto free_and_fail1; - strbuf_remove(&first, 0, cp - first.buf); - - /* - * second points at one past closing dq of name. - * find the second name. - */ - while ((second < line + llen) && isspace(*second)) - second++; - - if (line + llen <= second) - goto free_and_fail1; - if (*second == '"') { - if (unquote_c_style(&sp, second, NULL)) - goto free_and_fail1; - cp = skip_tree_prefix(state, sp.buf, sp.len); - if (!cp) - goto free_and_fail1; - /* They must match, otherwise ignore */ - if (strcmp(cp, first.buf)) - goto free_and_fail1; - strbuf_release(&sp); - return strbuf_detach(&first, NULL); - } - - /* unquoted second */ - cp = skip_tree_prefix(state, second, line + llen - second); - if (!cp) - goto free_and_fail1; - if (line + llen - cp != first.len || - memcmp(first.buf, cp, first.len)) - goto free_and_fail1; - return strbuf_detach(&first, NULL); - - free_and_fail1: - strbuf_release(&first); - strbuf_release(&sp); - return NULL; - } - - /* unquoted first name */ - name = skip_tree_prefix(state, line, llen); - if (!name) - return NULL; - - /* - * since the first name is unquoted, a dq if exists must be - * the beginning of the second name. - */ - for (second = name; second < line + llen; second++) { - if (*second == '"') { - struct strbuf sp = STRBUF_INIT; - const char *np; - - if (unquote_c_style(&sp, second, NULL)) - goto free_and_fail2; - - np = skip_tree_prefix(state, sp.buf, sp.len); - if (!np) - goto free_and_fail2; - - len = sp.buf + sp.len - np; - if (len < second - name && - !strncmp(np, name, len) && - isspace(name[len])) { - /* Good */ - strbuf_remove(&sp, 0, np - sp.buf); - return strbuf_detach(&sp, NULL); - } - - free_and_fail2: - strbuf_release(&sp); - return NULL; - } - } - - /* - * Accept a name only if it shows up twice, exactly the same - * form. - */ - second = strchr(name, '\n'); - if (!second) - return NULL; - line_len = second - name; - for (len = 0 ; ; len++) { - switch (name[len]) { - default: - continue; - case '\n': - return NULL; - case '\t': case ' ': - /* - * Is this the separator between the preimage - * and the postimage pathname? Again, we are - * only interested in the case where there is - * no rename, as this is only to set def_name - * and a rename patch has the names elsewhere - * in an unambiguous form. - */ - if (!name[len + 1]) - return NULL; /* no postimage name */ - second = skip_tree_prefix(state, name + len + 1, - line_len - (len + 1)); - if (!second) - return NULL; - /* - * Does len bytes starting at "name" and "second" - * (that are separated by one HT or SP we just - * found) exactly match? - */ - if (second[len] == '\n' && !strncmp(name, second, len)) - return xmemdupz(name, len); - } - } -} - -/* Verify that we recognize the lines following a git header */ -static int parse_git_header(struct apply_state *state, - const char *line, - int len, - unsigned int size, - struct patch *patch) -{ - unsigned long offset; - - /* A git diff has explicit new/delete information, so we don't guess */ - patch->is_new = 0; - patch->is_delete = 0; - - /* - * Some things may not have the old name in the - * rest of the headers anywhere (pure mode changes, - * or removing or adding empty files), so we get - * the default name from the header. - */ - patch->def_name = git_header_name(state, line, len); - if (patch->def_name && state->root.len) { - char *s = xstrfmt("%s%s", state->root.buf, patch->def_name); - free(patch->def_name); - patch->def_name = s; - } - - line += len; - size -= len; - state->linenr++; - for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) { - static const struct opentry { - const char *str; - int (*fn)(struct apply_state *, const char *, struct patch *); - } optable[] = { - { "@@ -", gitdiff_hdrend }, - { "--- ", gitdiff_oldname }, - { "+++ ", gitdiff_newname }, - { "old mode ", gitdiff_oldmode }, - { "new mode ", gitdiff_newmode }, - { "deleted file mode ", gitdiff_delete }, - { "new file mode ", gitdiff_newfile }, - { "copy from ", gitdiff_copysrc }, - { "copy to ", gitdiff_copydst }, - { "rename old ", gitdiff_renamesrc }, - { "rename new ", gitdiff_renamedst }, - { "rename from ", gitdiff_renamesrc }, - { "rename to ", gitdiff_renamedst }, - { "similarity index ", gitdiff_similarity }, - { "dissimilarity index ", gitdiff_dissimilarity }, - { "index ", gitdiff_index }, - { "", gitdiff_unrecognized }, - }; - int i; - - len = linelen(line, size); - if (!len || line[len-1] != '\n') - break; - for (i = 0; i < ARRAY_SIZE(optable); i++) { - const struct opentry *p = optable + i; - int oplen = strlen(p->str); - int res; - if (len < oplen || memcmp(p->str, line, oplen)) - continue; - res = p->fn(state, line + oplen, patch); - if (res < 0) - return -1; - if (res > 0) - return offset; - break; - } - } - - return offset; -} - -static int parse_num(const char *line, unsigned long *p) -{ - char *ptr; - - if (!isdigit(*line)) - return 0; - *p = strtoul(line, &ptr, 10); - return ptr - line; -} - -static int parse_range(const char *line, int len, int offset, const char *expect, - unsigned long *p1, unsigned long *p2) -{ - int digits, ex; - - if (offset < 0 || offset >= len) - return -1; - line += offset; - len -= offset; - - digits = parse_num(line, p1); - if (!digits) - return -1; - - offset += digits; - line += digits; - len -= digits; - - *p2 = 1; - if (*line == ',') { - digits = parse_num(line+1, p2); - if (!digits) - return -1; - - offset += digits+1; - line += digits+1; - len -= digits+1; - } - - ex = strlen(expect); - if (ex > len) - return -1; - if (memcmp(line, expect, ex)) - return -1; - - return offset + ex; -} - -static void recount_diff(const char *line, int size, struct fragment *fragment) -{ - int oldlines = 0, newlines = 0, ret = 0; - - if (size < 1) { - warning("recount: ignore empty hunk"); - return; - } - - for (;;) { - int len = linelen(line, size); - size -= len; - line += len; - - if (size < 1) - break; - - switch (*line) { - case ' ': case '\n': - newlines++; - /* fall through */ - case '-': - oldlines++; - continue; - case '+': - newlines++; - continue; - case '\\': - continue; - case '@': - ret = size < 3 || !starts_with(line, "@@ "); - break; - case 'd': - ret = size < 5 || !starts_with(line, "diff "); - break; - default: - ret = -1; - break; - } - if (ret) { - warning(_("recount: unexpected line: %.*s"), - (int)linelen(line, size), line); - return; - } - break; - } - fragment->oldlines = oldlines; - fragment->newlines = newlines; -} - -/* - * Parse a unified diff fragment header of the - * form "@@ -a,b +c,d @@" - */ -static int parse_fragment_header(const char *line, int len, struct fragment *fragment) -{ - int offset; - - if (!len || line[len-1] != '\n') - return -1; - - /* Figure out the number of lines in a fragment */ - offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines); - offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines); - - return offset; -} - -/* - * Find file diff header - * - * Returns: - * -1 if no header was found - * -128 in case of error - * the size of the header in bytes (called "offset") otherwise - */ -static int find_header(struct apply_state *state, - const char *line, - unsigned long size, - int *hdrsize, - struct patch *patch) -{ - unsigned long offset, len; - - patch->is_toplevel_relative = 0; - patch->is_rename = patch->is_copy = 0; - patch->is_new = patch->is_delete = -1; - patch->old_mode = patch->new_mode = 0; - patch->old_name = patch->new_name = NULL; - for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) { - unsigned long nextlen; - - len = linelen(line, size); - if (!len) - break; - - /* Testing this early allows us to take a few shortcuts.. */ - if (len < 6) - continue; - - /* - * Make sure we don't find any unconnected patch fragments. - * That's a sign that we didn't find a header, and that a - * patch has become corrupted/broken up. - */ - if (!memcmp("@@ -", line, 4)) { - struct fragment dummy; - if (parse_fragment_header(line, len, &dummy) < 0) - continue; - error(_("patch fragment without header at line %d: %.*s"), - state->linenr, (int)len-1, line); - return -128; - } - - if (size < len + 6) - break; - - /* - * Git patch? It might not have a real patch, just a rename - * or mode change, so we handle that specially - */ - if (!memcmp("diff --git ", line, 11)) { - int git_hdr_len = parse_git_header(state, line, len, size, patch); - if (git_hdr_len < 0) - return -128; - if (git_hdr_len <= len) - continue; - if (!patch->old_name && !patch->new_name) { - if (!patch->def_name) { - error(Q_("git diff header lacks filename information when removing " - "%d leading pathname component (line %d)", - "git diff header lacks filename information when removing " - "%d leading pathname components (line %d)", - state->p_value), - state->p_value, state->linenr); - return -128; - } - patch->old_name = xstrdup(patch->def_name); - patch->new_name = xstrdup(patch->def_name); - } - if (!patch->is_delete && !patch->new_name) { - error("git diff header lacks filename information " - "(line %d)", state->linenr); - return -128; - } - patch->is_toplevel_relative = 1; - *hdrsize = git_hdr_len; - return offset; - } - - /* --- followed by +++ ? */ - if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4)) - continue; - - /* - * We only accept unified patches, so we want it to - * at least have "@@ -a,b +c,d @@\n", which is 14 chars - * minimum ("@@ -0,0 +1 @@\n" is the shortest). - */ - nextlen = linelen(line + len, size - len); - if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4)) - continue; - - /* Ok, we'll consider it a patch */ - if (parse_traditional_patch(state, line, line+len, patch)) - return -128; - *hdrsize = len + nextlen; - state->linenr += 2; - return offset; - } - return -1; -} - -static void record_ws_error(struct apply_state *state, - unsigned result, - const char *line, - int len, - int linenr) -{ - char *err; - - if (!result) - return; - - state->whitespace_error++; - if (state->squelch_whitespace_errors && - state->squelch_whitespace_errors < state->whitespace_error) - return; - - err = whitespace_error_string(result); - fprintf(stderr, "%s:%d: %s.\n%.*s\n", - state->patch_input_file, linenr, err, len, line); - free(err); -} - -static void check_whitespace(struct apply_state *state, - const char *line, - int len, - unsigned ws_rule) -{ - unsigned result = ws_check(line + 1, len - 1, ws_rule); - - record_ws_error(state, result, line + 1, len - 2, state->linenr); -} - -/* - * Parse a unified diff. Note that this really needs to parse each - * fragment separately, since the only way to know the difference - * between a "---" that is part of a patch, and a "---" that starts - * the next patch is to look at the line counts.. - */ -static int parse_fragment(struct apply_state *state, - const char *line, - unsigned long size, - struct patch *patch, - struct fragment *fragment) -{ - int added, deleted; - int len = linelen(line, size), offset; - unsigned long oldlines, newlines; - unsigned long leading, trailing; - - offset = parse_fragment_header(line, len, fragment); - if (offset < 0) - return -1; - if (offset > 0 && patch->recount) - recount_diff(line + offset, size - offset, fragment); - oldlines = fragment->oldlines; - newlines = fragment->newlines; - leading = 0; - trailing = 0; - - /* Parse the thing.. */ - line += len; - size -= len; - state->linenr++; - added = deleted = 0; - for (offset = len; - 0 < size; - offset += len, size -= len, line += len, state->linenr++) { - if (!oldlines && !newlines) - break; - len = linelen(line, size); - if (!len || line[len-1] != '\n') - return -1; - switch (*line) { - default: - return -1; - case '\n': /* newer GNU diff, an empty context line */ - case ' ': - oldlines--; - newlines--; - if (!deleted && !added) - leading++; - trailing++; - if (!state->apply_in_reverse && - state->ws_error_action == correct_ws_error) - check_whitespace(state, line, len, patch->ws_rule); - break; - case '-': - if (state->apply_in_reverse && - state->ws_error_action != nowarn_ws_error) - check_whitespace(state, line, len, patch->ws_rule); - deleted++; - oldlines--; - trailing = 0; - break; - case '+': - if (!state->apply_in_reverse && - state->ws_error_action != nowarn_ws_error) - check_whitespace(state, line, len, patch->ws_rule); - added++; - newlines--; - trailing = 0; - break; - - /* - * We allow "\ No newline at end of file". Depending - * on locale settings when the patch was produced we - * don't know what this line looks like. The only - * thing we do know is that it begins with "\ ". - * Checking for 12 is just for sanity check -- any - * l10n of "\ No newline..." is at least that long. - */ - case '\\': - if (len < 12 || memcmp(line, "\\ ", 2)) - return -1; - break; - } - } - if (oldlines || newlines) - return -1; - if (!deleted && !added) - return -1; - - fragment->leading = leading; - fragment->trailing = trailing; - - /* - * If a fragment ends with an incomplete line, we failed to include - * it in the above loop because we hit oldlines == newlines == 0 - * before seeing it. - */ - if (12 < size && !memcmp(line, "\\ ", 2)) - offset += linelen(line, size); - - patch->lines_added += added; - patch->lines_deleted += deleted; - - if (0 < patch->is_new && oldlines) - return error(_("new file depends on old contents")); - if (0 < patch->is_delete && newlines) - return error(_("deleted file still has contents")); - return offset; -} - -/* - * We have seen "diff --git a/... b/..." header (or a traditional patch - * header). Read hunks that belong to this patch into fragments and hang - * them to the given patch structure. - * - * The (fragment->patch, fragment->size) pair points into the memory given - * by the caller, not a copy, when we return. - * - * Returns: - * -1 in case of error, - * the number of bytes in the patch otherwise. - */ -static int parse_single_patch(struct apply_state *state, - const char *line, - unsigned long size, - struct patch *patch) -{ - unsigned long offset = 0; - unsigned long oldlines = 0, newlines = 0, context = 0; - struct fragment **fragp = &patch->fragments; - - while (size > 4 && !memcmp(line, "@@ -", 4)) { - struct fragment *fragment; - int len; - - fragment = xcalloc(1, sizeof(*fragment)); - fragment->linenr = state->linenr; - len = parse_fragment(state, line, size, patch, fragment); - if (len <= 0) { - free(fragment); - return error(_("corrupt patch at line %d"), state->linenr); - } - fragment->patch = line; - fragment->size = len; - oldlines += fragment->oldlines; - newlines += fragment->newlines; - context += fragment->leading + fragment->trailing; - - *fragp = fragment; - fragp = &fragment->next; - - offset += len; - line += len; - size -= len; - } - - /* - * If something was removed (i.e. we have old-lines) it cannot - * be creation, and if something was added it cannot be - * deletion. However, the reverse is not true; --unified=0 - * patches that only add are not necessarily creation even - * though they do not have any old lines, and ones that only - * delete are not necessarily deletion. - * - * Unfortunately, a real creation/deletion patch do _not_ have - * any context line by definition, so we cannot safely tell it - * apart with --unified=0 insanity. At least if the patch has - * more than one hunk it is not creation or deletion. - */ - if (patch->is_new < 0 && - (oldlines || (patch->fragments && patch->fragments->next))) - patch->is_new = 0; - if (patch->is_delete < 0 && - (newlines || (patch->fragments && patch->fragments->next))) - patch->is_delete = 0; - - if (0 < patch->is_new && oldlines) - return error(_("new file %s depends on old contents"), patch->new_name); - if (0 < patch->is_delete && newlines) - return error(_("deleted file %s still has contents"), patch->old_name); - if (!patch->is_delete && !newlines && context) - fprintf_ln(stderr, - _("** warning: " - "file %s becomes empty but is not deleted"), - patch->new_name); - - return offset; -} - -static inline int metadata_changes(struct patch *patch) -{ - return patch->is_rename > 0 || - patch->is_copy > 0 || - patch->is_new > 0 || - patch->is_delete || - (patch->old_mode && patch->new_mode && - patch->old_mode != patch->new_mode); -} - -static char *inflate_it(const void *data, unsigned long size, - unsigned long inflated_size) -{ - git_zstream stream; - void *out; - int st; - - memset(&stream, 0, sizeof(stream)); - - stream.next_in = (unsigned char *)data; - stream.avail_in = size; - stream.next_out = out = xmalloc(inflated_size); - stream.avail_out = inflated_size; - git_inflate_init(&stream); - st = git_inflate(&stream, Z_FINISH); - git_inflate_end(&stream); - if ((st != Z_STREAM_END) || stream.total_out != inflated_size) { - free(out); - return NULL; - } - return out; -} - -/* - * Read a binary hunk and return a new fragment; fragment->patch - * points at an allocated memory that the caller must free, so - * it is marked as "->free_patch = 1". - */ -static struct fragment *parse_binary_hunk(struct apply_state *state, - char **buf_p, - unsigned long *sz_p, - int *status_p, - int *used_p) -{ - /* - * Expect a line that begins with binary patch method ("literal" - * or "delta"), followed by the length of data before deflating. - * a sequence of 'length-byte' followed by base-85 encoded data - * should follow, terminated by a newline. - * - * Each 5-byte sequence of base-85 encodes up to 4 bytes, - * and we would limit the patch line to 66 characters, - * so one line can fit up to 13 groups that would decode - * to 52 bytes max. The length byte 'A'-'Z' corresponds - * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes. - */ - int llen, used; - unsigned long size = *sz_p; - char *buffer = *buf_p; - int patch_method; - unsigned long origlen; - char *data = NULL; - int hunk_size = 0; - struct fragment *frag; - - llen = linelen(buffer, size); - used = llen; - - *status_p = 0; - - if (starts_with(buffer, "delta ")) { - patch_method = BINARY_DELTA_DEFLATED; - origlen = strtoul(buffer + 6, NULL, 10); - } - else if (starts_with(buffer, "literal ")) { - patch_method = BINARY_LITERAL_DEFLATED; - origlen = strtoul(buffer + 8, NULL, 10); - } - else - return NULL; - - state->linenr++; - buffer += llen; - while (1) { - int byte_length, max_byte_length, newsize; - llen = linelen(buffer, size); - used += llen; - state->linenr++; - if (llen == 1) { - /* consume the blank line */ - buffer++; - size--; - break; - } - /* - * Minimum line is "A00000\n" which is 7-byte long, - * and the line length must be multiple of 5 plus 2. - */ - if ((llen < 7) || (llen-2) % 5) - goto corrupt; - max_byte_length = (llen - 2) / 5 * 4; - byte_length = *buffer; - if ('A' <= byte_length && byte_length <= 'Z') - byte_length = byte_length - 'A' + 1; - else if ('a' <= byte_length && byte_length <= 'z') - byte_length = byte_length - 'a' + 27; - else - goto corrupt; - /* if the input length was not multiple of 4, we would - * have filler at the end but the filler should never - * exceed 3 bytes - */ - if (max_byte_length < byte_length || - byte_length <= max_byte_length - 4) - goto corrupt; - newsize = hunk_size + byte_length; - data = xrealloc(data, newsize); - if (decode_85(data + hunk_size, buffer + 1, byte_length)) - goto corrupt; - hunk_size = newsize; - buffer += llen; - size -= llen; - } - - frag = xcalloc(1, sizeof(*frag)); - frag->patch = inflate_it(data, hunk_size, origlen); - frag->free_patch = 1; - if (!frag->patch) - goto corrupt; - free(data); - frag->size = origlen; - *buf_p = buffer; - *sz_p = size; - *used_p = used; - frag->binary_patch_method = patch_method; - return frag; - - corrupt: - free(data); - *status_p = -1; - error(_("corrupt binary patch at line %d: %.*s"), - state->linenr-1, llen-1, buffer); - return NULL; -} - -/* - * Returns: - * -1 in case of error, - * the length of the parsed binary patch otherwise - */ -static int parse_binary(struct apply_state *state, - char *buffer, - unsigned long size, - struct patch *patch) -{ - /* - * We have read "GIT binary patch\n"; what follows is a line - * that says the patch method (currently, either "literal" or - * "delta") and the length of data before deflating; a - * sequence of 'length-byte' followed by base-85 encoded data - * follows. - * - * When a binary patch is reversible, there is another binary - * hunk in the same format, starting with patch method (either - * "literal" or "delta") with the length of data, and a sequence - * of length-byte + base-85 encoded data, terminated with another - * empty line. This data, when applied to the postimage, produces - * the preimage. - */ - struct fragment *forward; - struct fragment *reverse; - int status; - int used, used_1; - - forward = parse_binary_hunk(state, &buffer, &size, &status, &used); - if (!forward && !status) - /* there has to be one hunk (forward hunk) */ - return error(_("unrecognized binary patch at line %d"), state->linenr-1); - if (status) - /* otherwise we already gave an error message */ - return status; - - reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1); - if (reverse) - used += used_1; - else if (status) { - /* - * Not having reverse hunk is not an error, but having - * a corrupt reverse hunk is. - */ - free((void*) forward->patch); - free(forward); - return status; - } - forward->next = reverse; - patch->fragments = forward; - patch->is_binary = 1; - return used; -} - -static void prefix_one(struct apply_state *state, char **name) -{ - char *old_name = *name; - if (!old_name) - return; - *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name)); - free(old_name); -} - -static void prefix_patch(struct apply_state *state, struct patch *p) -{ - if (!state->prefix || p->is_toplevel_relative) - return; - prefix_one(state, &p->new_name); - prefix_one(state, &p->old_name); -} - -/* - * include/exclude - */ - -static void add_name_limit(struct apply_state *state, - const char *name, - int exclude) -{ - struct string_list_item *it; - - it = string_list_append(&state->limit_by_name, name); - it->util = exclude ? NULL : (void *) 1; -} - -static int use_patch(struct apply_state *state, struct patch *p) -{ - const char *pathname = p->new_name ? p->new_name : p->old_name; - int i; - - /* Paths outside are not touched regardless of "--include" */ - if (0 < state->prefix_length) { - int pathlen = strlen(pathname); - if (pathlen <= state->prefix_length || - memcmp(state->prefix, pathname, state->prefix_length)) - return 0; - } - - /* See if it matches any of exclude/include rule */ - for (i = 0; i < state->limit_by_name.nr; i++) { - struct string_list_item *it = &state->limit_by_name.items[i]; - if (!wildmatch(it->string, pathname, 0, NULL)) - return (it->util != NULL); - } - - /* - * If we had any include, a path that does not match any rule is - * not used. Otherwise, we saw bunch of exclude rules (or none) - * and such a path is used. - */ - return !state->has_include; -} - -/* - * Read the patch text in "buffer" that extends for "size" bytes; stop - * reading after seeing a single patch (i.e. changes to a single file). - * Create fragments (i.e. patch hunks) and hang them to the given patch. - * - * Returns: - * -1 if no header was found or parse_binary() failed, - * -128 on another error, - * the number of bytes consumed otherwise, - * so that the caller can call us again for the next patch. - */ -static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch) -{ - int hdrsize, patchsize; - int offset = find_header(state, buffer, size, &hdrsize, patch); - - if (offset < 0) - return offset; - - prefix_patch(state, patch); - - if (!use_patch(state, patch)) - patch->ws_rule = 0; - else - patch->ws_rule = whitespace_rule(patch->new_name - ? patch->new_name - : patch->old_name); - - patchsize = parse_single_patch(state, - buffer + offset + hdrsize, - size - offset - hdrsize, - patch); - - if (patchsize < 0) - return -128; - - if (!patchsize) { - static const char git_binary[] = "GIT binary patch\n"; - int hd = hdrsize + offset; - unsigned long llen = linelen(buffer + hd, size - hd); - - if (llen == sizeof(git_binary) - 1 && - !memcmp(git_binary, buffer + hd, llen)) { - int used; - state->linenr++; - used = parse_binary(state, buffer + hd + llen, - size - hd - llen, patch); - if (used < 0) - return -1; - if (used) - patchsize = used + llen; - else - patchsize = 0; - } - else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) { - static const char *binhdr[] = { - "Binary files ", - "Files ", - NULL, - }; - int i; - for (i = 0; binhdr[i]; i++) { - int len = strlen(binhdr[i]); - if (len < size - hd && - !memcmp(binhdr[i], buffer + hd, len)) { - state->linenr++; - patch->is_binary = 1; - patchsize = llen; - break; - } - } - } - - /* Empty patch cannot be applied if it is a text patch - * without metadata change. A binary patch appears - * empty to us here. - */ - if ((state->apply || state->check) && - (!patch->is_binary && !metadata_changes(patch))) { - error(_("patch with only garbage at line %d"), state->linenr); - return -128; - } - } - - return offset + hdrsize + patchsize; -} - -#define swap(a,b) myswap((a),(b),sizeof(a)) - -#define myswap(a, b, size) do { \ - unsigned char mytmp[size]; \ - memcpy(mytmp, &a, size); \ - memcpy(&a, &b, size); \ - memcpy(&b, mytmp, size); \ -} while (0) - -static void reverse_patches(struct patch *p) -{ - for (; p; p = p->next) { - struct fragment *frag = p->fragments; - - swap(p->new_name, p->old_name); - swap(p->new_mode, p->old_mode); - swap(p->is_new, p->is_delete); - swap(p->lines_added, p->lines_deleted); - swap(p->old_sha1_prefix, p->new_sha1_prefix); - - for (; frag; frag = frag->next) { - swap(frag->newpos, frag->oldpos); - swap(frag->newlines, frag->oldlines); - } - } -} - -static const char pluses[] = -"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; -static const char minuses[]= -"----------------------------------------------------------------------"; - -static void show_stats(struct apply_state *state, struct patch *patch) -{ - struct strbuf qname = STRBUF_INIT; - char *cp = patch->new_name ? patch->new_name : patch->old_name; - int max, add, del; - - quote_c_style(cp, &qname, NULL, 0); - - /* - * "scale" the filename - */ - max = state->max_len; - if (max > 50) - max = 50; - - if (qname.len > max) { - cp = strchr(qname.buf + qname.len + 3 - max, '/'); - if (!cp) - cp = qname.buf + qname.len + 3 - max; - strbuf_splice(&qname, 0, cp - qname.buf, "...", 3); - } - - if (patch->is_binary) { - printf(" %-*s | Bin\n", max, qname.buf); - strbuf_release(&qname); - return; - } - - printf(" %-*s |", max, qname.buf); - strbuf_release(&qname); - - /* - * scale the add/delete - */ - max = max + state->max_change > 70 ? 70 - max : state->max_change; - add = patch->lines_added; - del = patch->lines_deleted; - - if (state->max_change > 0) { - int total = ((add + del) * max + state->max_change / 2) / state->max_change; - add = (add * max + state->max_change / 2) / state->max_change; - del = total - add; - } - printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted, - add, pluses, del, minuses); -} - -static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) -{ - switch (st->st_mode & S_IFMT) { - case S_IFLNK: - if (strbuf_readlink(buf, path, st->st_size) < 0) - return error(_("unable to read symlink %s"), path); - return 0; - case S_IFREG: - if (strbuf_read_file(buf, path, st->st_size) != st->st_size) - return error(_("unable to open or read %s"), path); - convert_to_git(path, buf->buf, buf->len, buf, 0); - return 0; - default: - return -1; - } -} - -/* - * Update the preimage, and the common lines in postimage, - * from buffer buf of length len. If postlen is 0 the postimage - * is updated in place, otherwise it's updated on a new buffer - * of length postlen - */ - -static void update_pre_post_images(struct image *preimage, - struct image *postimage, - char *buf, - size_t len, size_t postlen) -{ - int i, ctx, reduced; - char *new, *old, *fixed; - struct image fixed_preimage; - - /* - * Update the preimage with whitespace fixes. Note that we - * are not losing preimage->buf -- apply_one_fragment() will - * free "oldlines". - */ - prepare_image(&fixed_preimage, buf, len, 1); - assert(postlen - ? fixed_preimage.nr == preimage->nr - : fixed_preimage.nr <= preimage->nr); - for (i = 0; i < fixed_preimage.nr; i++) - fixed_preimage.line[i].flag = preimage->line[i].flag; - free(preimage->line_allocated); - *preimage = fixed_preimage; - - /* - * Adjust the common context lines in postimage. This can be - * done in-place when we are shrinking it with whitespace - * fixing, but needs a new buffer when ignoring whitespace or - * expanding leading tabs to spaces. - * - * We trust the caller to tell us if the update can be done - * in place (postlen==0) or not. - */ - old = postimage->buf; - if (postlen) - new = postimage->buf = xmalloc(postlen); - else - new = old; - fixed = preimage->buf; - - for (i = reduced = ctx = 0; i < postimage->nr; i++) { - size_t l_len = postimage->line[i].len; - if (!(postimage->line[i].flag & LINE_COMMON)) { - /* an added line -- no counterparts in preimage */ - memmove(new, old, l_len); - old += l_len; - new += l_len; - continue; - } - - /* a common context -- skip it in the original postimage */ - old += l_len; - - /* and find the corresponding one in the fixed preimage */ - while (ctx < preimage->nr && - !(preimage->line[ctx].flag & LINE_COMMON)) { - fixed += preimage->line[ctx].len; - ctx++; - } - - /* - * preimage is expected to run out, if the caller - * fixed addition of trailing blank lines. - */ - if (preimage->nr <= ctx) { - reduced++; - continue; - } - - /* and copy it in, while fixing the line length */ - l_len = preimage->line[ctx].len; - memcpy(new, fixed, l_len); - new += l_len; - fixed += l_len; - postimage->line[i].len = l_len; - ctx++; - } - - if (postlen - ? postlen < new - postimage->buf - : postimage->len < new - postimage->buf) - die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d", - (int)postlen, (int) postimage->len, (int)(new - postimage->buf)); - - /* Fix the length of the whole thing */ - postimage->len = new - postimage->buf; - postimage->nr -= reduced; -} - -static int line_by_line_fuzzy_match(struct image *img, - struct image *preimage, - struct image *postimage, - unsigned long try, - int try_lno, - int preimage_limit) -{ - int i; - size_t imgoff = 0; - size_t preoff = 0; - size_t postlen = postimage->len; - size_t extra_chars; - char *buf; - char *preimage_eof; - char *preimage_end; - struct strbuf fixed; - char *fixed_buf; - size_t fixed_len; - - for (i = 0; i < preimage_limit; i++) { - size_t prelen = preimage->line[i].len; - size_t imglen = img->line[try_lno+i].len; - - if (!fuzzy_matchlines(img->buf + try + imgoff, imglen, - preimage->buf + preoff, prelen)) - return 0; - if (preimage->line[i].flag & LINE_COMMON) - postlen += imglen - prelen; - imgoff += imglen; - preoff += prelen; - } - - /* - * Ok, the preimage matches with whitespace fuzz. - * - * imgoff now holds the true length of the target that - * matches the preimage before the end of the file. - * - * Count the number of characters in the preimage that fall - * beyond the end of the file and make sure that all of them - * are whitespace characters. (This can only happen if - * we are removing blank lines at the end of the file.) - */ - buf = preimage_eof = preimage->buf + preoff; - for ( ; i < preimage->nr; i++) - preoff += preimage->line[i].len; - preimage_end = preimage->buf + preoff; - for ( ; buf < preimage_end; buf++) - if (!isspace(*buf)) - return 0; - - /* - * Update the preimage and the common postimage context - * lines to use the same whitespace as the target. - * If whitespace is missing in the target (i.e. - * if the preimage extends beyond the end of the file), - * use the whitespace from the preimage. - */ - extra_chars = preimage_end - preimage_eof; - strbuf_init(&fixed, imgoff + extra_chars); - strbuf_add(&fixed, img->buf + try, imgoff); - strbuf_add(&fixed, preimage_eof, extra_chars); - fixed_buf = strbuf_detach(&fixed, &fixed_len); - update_pre_post_images(preimage, postimage, - fixed_buf, fixed_len, postlen); - return 1; -} - -static int match_fragment(struct apply_state *state, - struct image *img, - struct image *preimage, - struct image *postimage, - unsigned long try, - int try_lno, - unsigned ws_rule, - int match_beginning, int match_end) -{ - int i; - char *fixed_buf, *buf, *orig, *target; - struct strbuf fixed; - size_t fixed_len, postlen; - int preimage_limit; - - if (preimage->nr + try_lno <= img->nr) { - /* - * The hunk falls within the boundaries of img. - */ - preimage_limit = preimage->nr; - if (match_end && (preimage->nr + try_lno != img->nr)) - return 0; - } else if (state->ws_error_action == correct_ws_error && - (ws_rule & WS_BLANK_AT_EOF)) { - /* - * This hunk extends beyond the end of img, and we are - * removing blank lines at the end of the file. This - * many lines from the beginning of the preimage must - * match with img, and the remainder of the preimage - * must be blank. - */ - preimage_limit = img->nr - try_lno; - } else { - /* - * The hunk extends beyond the end of the img and - * we are not removing blanks at the end, so we - * should reject the hunk at this position. - */ - return 0; - } - - if (match_beginning && try_lno) - return 0; - - /* Quick hash check */ - for (i = 0; i < preimage_limit; i++) - if ((img->line[try_lno + i].flag & LINE_PATCHED) || - (preimage->line[i].hash != img->line[try_lno + i].hash)) - return 0; - - if (preimage_limit == preimage->nr) { - /* - * Do we have an exact match? If we were told to match - * at the end, size must be exactly at try+fragsize, - * otherwise try+fragsize must be still within the preimage, - * and either case, the old piece should match the preimage - * exactly. - */ - if ((match_end - ? (try + preimage->len == img->len) - : (try + preimage->len <= img->len)) && - !memcmp(img->buf + try, preimage->buf, preimage->len)) - return 1; - } else { - /* - * The preimage extends beyond the end of img, so - * there cannot be an exact match. - * - * There must be one non-blank context line that match - * a line before the end of img. - */ - char *buf_end; - - buf = preimage->buf; - buf_end = buf; - for (i = 0; i < preimage_limit; i++) - buf_end += preimage->line[i].len; - - for ( ; buf < buf_end; buf++) - if (!isspace(*buf)) - break; - if (buf == buf_end) - return 0; - } - - /* - * No exact match. If we are ignoring whitespace, run a line-by-line - * fuzzy matching. We collect all the line length information because - * we need it to adjust whitespace if we match. - */ - if (state->ws_ignore_action == ignore_ws_change) - return line_by_line_fuzzy_match(img, preimage, postimage, - try, try_lno, preimage_limit); - - if (state->ws_error_action != correct_ws_error) - return 0; - - /* - * The hunk does not apply byte-by-byte, but the hash says - * it might with whitespace fuzz. We weren't asked to - * ignore whitespace, we were asked to correct whitespace - * errors, so let's try matching after whitespace correction. - * - * While checking the preimage against the target, whitespace - * errors in both fixed, we count how large the corresponding - * postimage needs to be. The postimage prepared by - * apply_one_fragment() has whitespace errors fixed on added - * lines already, but the common lines were propagated as-is, - * which may become longer when their whitespace errors are - * fixed. - */ - - /* First count added lines in postimage */ - postlen = 0; - for (i = 0; i < postimage->nr; i++) { - if (!(postimage->line[i].flag & LINE_COMMON)) - postlen += postimage->line[i].len; - } - - /* - * The preimage may extend beyond the end of the file, - * but in this loop we will only handle the part of the - * preimage that falls within the file. - */ - strbuf_init(&fixed, preimage->len + 1); - orig = preimage->buf; - target = img->buf + try; - for (i = 0; i < preimage_limit; i++) { - size_t oldlen = preimage->line[i].len; - size_t tgtlen = img->line[try_lno + i].len; - size_t fixstart = fixed.len; - struct strbuf tgtfix; - int match; - - /* Try fixing the line in the preimage */ - ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL); - - /* Try fixing the line in the target */ - strbuf_init(&tgtfix, tgtlen); - ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL); - - /* - * If they match, either the preimage was based on - * a version before our tree fixed whitespace breakage, - * or we are lacking a whitespace-fix patch the tree - * the preimage was based on already had (i.e. target - * has whitespace breakage, the preimage doesn't). - * In either case, we are fixing the whitespace breakages - * so we might as well take the fix together with their - * real change. - */ - match = (tgtfix.len == fixed.len - fixstart && - !memcmp(tgtfix.buf, fixed.buf + fixstart, - fixed.len - fixstart)); - - /* Add the length if this is common with the postimage */ - if (preimage->line[i].flag & LINE_COMMON) - postlen += tgtfix.len; - - strbuf_release(&tgtfix); - if (!match) - goto unmatch_exit; - - orig += oldlen; - target += tgtlen; - } - - - /* - * Now handle the lines in the preimage that falls beyond the - * end of the file (if any). They will only match if they are - * empty or only contain whitespace (if WS_BLANK_AT_EOL is - * false). - */ - for ( ; i < preimage->nr; i++) { - size_t fixstart = fixed.len; /* start of the fixed preimage */ - size_t oldlen = preimage->line[i].len; - int j; - - /* Try fixing the line in the preimage */ - ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL); - - for (j = fixstart; j < fixed.len; j++) - if (!isspace(fixed.buf[j])) - goto unmatch_exit; - - orig += oldlen; - } - - /* - * Yes, the preimage is based on an older version that still - * has whitespace breakages unfixed, and fixing them makes the - * hunk match. Update the context lines in the postimage. - */ - fixed_buf = strbuf_detach(&fixed, &fixed_len); - if (postlen < postimage->len) - postlen = 0; - update_pre_post_images(preimage, postimage, - fixed_buf, fixed_len, postlen); - return 1; - - unmatch_exit: - strbuf_release(&fixed); - return 0; -} - -static int find_pos(struct apply_state *state, - struct image *img, - struct image *preimage, - struct image *postimage, - int line, - unsigned ws_rule, - int match_beginning, int match_end) -{ - int i; - unsigned long backwards, forwards, try; - int backwards_lno, forwards_lno, try_lno; - - /* - * If match_beginning or match_end is specified, there is no - * point starting from a wrong line that will never match and - * wander around and wait for a match at the specified end. - */ - if (match_beginning) - line = 0; - else if (match_end) - line = img->nr - preimage->nr; - - /* - * Because the comparison is unsigned, the following test - * will also take care of a negative line number that can - * result when match_end and preimage is larger than the target. - */ - if ((size_t) line > img->nr) - line = img->nr; - - try = 0; - for (i = 0; i < line; i++) - try += img->line[i].len; - - /* - * There's probably some smart way to do this, but I'll leave - * that to the smart and beautiful people. I'm simple and stupid. - */ - backwards = try; - backwards_lno = line; - forwards = try; - forwards_lno = line; - try_lno = line; - - for (i = 0; ; i++) { - if (match_fragment(state, img, preimage, postimage, - try, try_lno, ws_rule, - match_beginning, match_end)) - return try_lno; - - again: - if (backwards_lno == 0 && forwards_lno == img->nr) - break; - - if (i & 1) { - if (backwards_lno == 0) { - i++; - goto again; - } - backwards_lno--; - backwards -= img->line[backwards_lno].len; - try = backwards; - try_lno = backwards_lno; - } else { - if (forwards_lno == img->nr) { - i++; - goto again; - } - forwards += img->line[forwards_lno].len; - forwards_lno++; - try = forwards; - try_lno = forwards_lno; - } - - } - return -1; -} - -static void remove_first_line(struct image *img) -{ - img->buf += img->line[0].len; - img->len -= img->line[0].len; - img->line++; - img->nr--; -} - -static void remove_last_line(struct image *img) -{ - img->len -= img->line[--img->nr].len; -} - -/* - * The change from "preimage" and "postimage" has been found to - * apply at applied_pos (counts in line numbers) in "img". - * Update "img" to remove "preimage" and replace it with "postimage". - */ -static void update_image(struct apply_state *state, - struct image *img, - int applied_pos, - struct image *preimage, - struct image *postimage) -{ - /* - * remove the copy of preimage at offset in img - * and replace it with postimage - */ - int i, nr; - size_t remove_count, insert_count, applied_at = 0; - char *result; - int preimage_limit; - - /* - * If we are removing blank lines at the end of img, - * the preimage may extend beyond the end. - * If that is the case, we must be careful only to - * remove the part of the preimage that falls within - * the boundaries of img. Initialize preimage_limit - * to the number of lines in the preimage that falls - * within the boundaries. - */ - preimage_limit = preimage->nr; - if (preimage_limit > img->nr - applied_pos) - preimage_limit = img->nr - applied_pos; - - for (i = 0; i < applied_pos; i++) - applied_at += img->line[i].len; - - remove_count = 0; - for (i = 0; i < preimage_limit; i++) - remove_count += img->line[applied_pos + i].len; - insert_count = postimage->len; - - /* Adjust the contents */ - result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1)); - memcpy(result, img->buf, applied_at); - memcpy(result + applied_at, postimage->buf, postimage->len); - memcpy(result + applied_at + postimage->len, - img->buf + (applied_at + remove_count), - img->len - (applied_at + remove_count)); - free(img->buf); - img->buf = result; - img->len += insert_count - remove_count; - result[img->len] = '\0'; - - /* Adjust the line table */ - nr = img->nr + postimage->nr - preimage_limit; - if (preimage_limit < postimage->nr) { - /* - * NOTE: this knows that we never call remove_first_line() - * on anything other than pre/post image. - */ - REALLOC_ARRAY(img->line, nr); - img->line_allocated = img->line; - } - if (preimage_limit != postimage->nr) - memmove(img->line + applied_pos + postimage->nr, - img->line + applied_pos + preimage_limit, - (img->nr - (applied_pos + preimage_limit)) * - sizeof(*img->line)); - memcpy(img->line + applied_pos, - postimage->line, - postimage->nr * sizeof(*img->line)); - if (!state->allow_overlap) - for (i = 0; i < postimage->nr; i++) - img->line[applied_pos + i].flag |= LINE_PATCHED; - img->nr = nr; -} - -/* - * Use the patch-hunk text in "frag" to prepare two images (preimage and - * postimage) for the hunk. Find lines that match "preimage" in "img" and - * replace the part of "img" with "postimage" text. - */ -static int apply_one_fragment(struct apply_state *state, - struct image *img, struct fragment *frag, - int inaccurate_eof, unsigned ws_rule, - int nth_fragment) -{ - int match_beginning, match_end; - const char *patch = frag->patch; - int size = frag->size; - char *old, *oldlines; - struct strbuf newlines; - int new_blank_lines_at_end = 0; - int found_new_blank_lines_at_end = 0; - int hunk_linenr = frag->linenr; - unsigned long leading, trailing; - int pos, applied_pos; - struct image preimage; - struct image postimage; - - memset(&preimage, 0, sizeof(preimage)); - memset(&postimage, 0, sizeof(postimage)); - oldlines = xmalloc(size); - strbuf_init(&newlines, size); - - old = oldlines; - while (size > 0) { - char first; - int len = linelen(patch, size); - int plen; - int added_blank_line = 0; - int is_blank_context = 0; - size_t start; - - if (!len) - break; - - /* - * "plen" is how much of the line we should use for - * the actual patch data. Normally we just remove the - * first character on the line, but if the line is - * followed by "\ No newline", then we also remove the - * last one (which is the newline, of course). - */ - plen = len - 1; - if (len < size && patch[len] == '\\') - plen--; - first = *patch; - if (state->apply_in_reverse) { - if (first == '-') - first = '+'; - else if (first == '+') - first = '-'; - } - - switch (first) { - case '\n': - /* Newer GNU diff, empty context line */ - if (plen < 0) - /* ... followed by '\No newline'; nothing */ - break; - *old++ = '\n'; - strbuf_addch(&newlines, '\n'); - add_line_info(&preimage, "\n", 1, LINE_COMMON); - add_line_info(&postimage, "\n", 1, LINE_COMMON); - is_blank_context = 1; - break; - case ' ': - if (plen && (ws_rule & WS_BLANK_AT_EOF) && - ws_blank_line(patch + 1, plen, ws_rule)) - is_blank_context = 1; - case '-': - memcpy(old, patch + 1, plen); - add_line_info(&preimage, old, plen, - (first == ' ' ? LINE_COMMON : 0)); - old += plen; - if (first == '-') - break; - /* Fall-through for ' ' */ - case '+': - /* --no-add does not add new lines */ - if (first == '+' && state->no_add) - break; - - start = newlines.len; - if (first != '+' || - !state->whitespace_error || - state->ws_error_action != correct_ws_error) { - strbuf_add(&newlines, patch + 1, plen); - } - else { - ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws); - } - add_line_info(&postimage, newlines.buf + start, newlines.len - start, - (first == '+' ? 0 : LINE_COMMON)); - if (first == '+' && - (ws_rule & WS_BLANK_AT_EOF) && - ws_blank_line(patch + 1, plen, ws_rule)) - added_blank_line = 1; - break; - case '@': case '\\': - /* Ignore it, we already handled it */ - break; - default: - if (state->apply_verbosely) - error(_("invalid start of line: '%c'"), first); - applied_pos = -1; - goto out; - } - if (added_blank_line) { - if (!new_blank_lines_at_end) - found_new_blank_lines_at_end = hunk_linenr; - new_blank_lines_at_end++; - } - else if (is_blank_context) - ; - else - new_blank_lines_at_end = 0; - patch += len; - size -= len; - hunk_linenr++; - } - if (inaccurate_eof && - old > oldlines && old[-1] == '\n' && - newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') { - old--; - strbuf_setlen(&newlines, newlines.len - 1); - } - - leading = frag->leading; - trailing = frag->trailing; - - /* - * A hunk to change lines at the beginning would begin with - * @@ -1,L +N,M @@ - * but we need to be careful. -U0 that inserts before the second - * line also has this pattern. - * - * And a hunk to add to an empty file would begin with - * @@ -0,0 +N,M @@ - * - * In other words, a hunk that is (frag->oldpos <= 1) with or - * without leading context must match at the beginning. - */ - match_beginning = (!frag->oldpos || - (frag->oldpos == 1 && !state->unidiff_zero)); - - /* - * A hunk without trailing lines must match at the end. - * However, we simply cannot tell if a hunk must match end - * from the lack of trailing lines if the patch was generated - * with unidiff without any context. - */ - match_end = !state->unidiff_zero && !trailing; - - pos = frag->newpos ? (frag->newpos - 1) : 0; - preimage.buf = oldlines; - preimage.len = old - oldlines; - postimage.buf = newlines.buf; - postimage.len = newlines.len; - preimage.line = preimage.line_allocated; - postimage.line = postimage.line_allocated; - - for (;;) { - - applied_pos = find_pos(state, img, &preimage, &postimage, pos, - ws_rule, match_beginning, match_end); - - if (applied_pos >= 0) - break; - - /* Am I at my context limits? */ - if ((leading <= state->p_context) && (trailing <= state->p_context)) - break; - if (match_beginning || match_end) { - match_beginning = match_end = 0; - continue; - } - - /* - * Reduce the number of context lines; reduce both - * leading and trailing if they are equal otherwise - * just reduce the larger context. - */ - if (leading >= trailing) { - remove_first_line(&preimage); - remove_first_line(&postimage); - pos--; - leading--; - } - if (trailing > leading) { - remove_last_line(&preimage); - remove_last_line(&postimage); - trailing--; - } - } - - if (applied_pos >= 0) { - if (new_blank_lines_at_end && - preimage.nr + applied_pos >= img->nr && - (ws_rule & WS_BLANK_AT_EOF) && - state->ws_error_action != nowarn_ws_error) { - record_ws_error(state, WS_BLANK_AT_EOF, "+", 1, - found_new_blank_lines_at_end); - if (state->ws_error_action == correct_ws_error) { - while (new_blank_lines_at_end--) - remove_last_line(&postimage); - } - /* - * We would want to prevent write_out_results() - * from taking place in apply_patch() that follows - * the callchain led us here, which is: - * apply_patch->check_patch_list->check_patch-> - * apply_data->apply_fragments->apply_one_fragment - */ - if (state->ws_error_action == die_on_ws_error) - state->apply = 0; - } - - if (state->apply_verbosely && applied_pos != pos) { - int offset = applied_pos - pos; - if (state->apply_in_reverse) - offset = 0 - offset; - fprintf_ln(stderr, - Q_("Hunk #%d succeeded at %d (offset %d line).", - "Hunk #%d succeeded at %d (offset %d lines).", - offset), - nth_fragment, applied_pos + 1, offset); - } - - /* - * Warn if it was necessary to reduce the number - * of context lines. - */ - if ((leading != frag->leading) || - (trailing != frag->trailing)) - fprintf_ln(stderr, _("Context reduced to (%ld/%ld)" - " to apply fragment at %d"), - leading, trailing, applied_pos+1); - update_image(state, img, applied_pos, &preimage, &postimage); - } else { - if (state->apply_verbosely) - error(_("while searching for:\n%.*s"), - (int)(old - oldlines), oldlines); - } - -out: - free(oldlines); - strbuf_release(&newlines); - free(preimage.line_allocated); - free(postimage.line_allocated); - - return (applied_pos < 0); -} - -static int apply_binary_fragment(struct apply_state *state, - struct image *img, - struct patch *patch) -{ - struct fragment *fragment = patch->fragments; - unsigned long len; - void *dst; - - if (!fragment) - return error(_("missing binary patch data for '%s'"), - patch->new_name ? - patch->new_name : - patch->old_name); - - /* Binary patch is irreversible without the optional second hunk */ - if (state->apply_in_reverse) { - if (!fragment->next) - return error("cannot reverse-apply a binary patch " - "without the reverse hunk to '%s'", - patch->new_name - ? patch->new_name : patch->old_name); - fragment = fragment->next; - } - switch (fragment->binary_patch_method) { - case BINARY_DELTA_DEFLATED: - dst = patch_delta(img->buf, img->len, fragment->patch, - fragment->size, &len); - if (!dst) - return -1; - clear_image(img); - img->buf = dst; - img->len = len; - return 0; - case BINARY_LITERAL_DEFLATED: - clear_image(img); - img->len = fragment->size; - img->buf = xmemdupz(fragment->patch, img->len); - return 0; - } - return -1; -} - -/* - * Replace "img" with the result of applying the binary patch. - * The binary patch data itself in patch->fragment is still kept - * but the preimage prepared by the caller in "img" is freed here - * or in the helper function apply_binary_fragment() this calls. - */ -static int apply_binary(struct apply_state *state, - struct image *img, - struct patch *patch) -{ - const char *name = patch->old_name ? patch->old_name : patch->new_name; - unsigned char sha1[20]; - - /* - * For safety, we require patch index line to contain - * full 40-byte textual SHA1 for old and new, at least for now. - */ - if (strlen(patch->old_sha1_prefix) != 40 || - strlen(patch->new_sha1_prefix) != 40 || - get_sha1_hex(patch->old_sha1_prefix, sha1) || - get_sha1_hex(patch->new_sha1_prefix, sha1)) - return error("cannot apply binary patch to '%s' " - "without full index line", name); - - if (patch->old_name) { - /* - * See if the old one matches what the patch - * applies to. - */ - hash_sha1_file(img->buf, img->len, blob_type, sha1); - if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix)) - return error("the patch applies to '%s' (%s), " - "which does not match the " - "current contents.", - name, sha1_to_hex(sha1)); - } - else { - /* Otherwise, the old one must be empty. */ - if (img->len) - return error("the patch applies to an empty " - "'%s' but it is not empty", name); - } - - get_sha1_hex(patch->new_sha1_prefix, sha1); - if (is_null_sha1(sha1)) { - clear_image(img); - return 0; /* deletion patch */ - } - - if (has_sha1_file(sha1)) { - /* We already have the postimage */ - enum object_type type; - unsigned long size; - char *result; - - result = read_sha1_file(sha1, &type, &size); - if (!result) - return error("the necessary postimage %s for " - "'%s' cannot be read", - patch->new_sha1_prefix, name); - clear_image(img); - img->buf = result; - img->len = size; - } else { - /* - * We have verified buf matches the preimage; - * apply the patch data to it, which is stored - * in the patch->fragments->{patch,size}. - */ - if (apply_binary_fragment(state, img, patch)) - return error(_("binary patch does not apply to '%s'"), - name); - - /* verify that the result matches */ - hash_sha1_file(img->buf, img->len, blob_type, sha1); - if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix)) - return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"), - name, patch->new_sha1_prefix, sha1_to_hex(sha1)); - } - - return 0; -} - -static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch) -{ - struct fragment *frag = patch->fragments; - const char *name = patch->old_name ? patch->old_name : patch->new_name; - unsigned ws_rule = patch->ws_rule; - unsigned inaccurate_eof = patch->inaccurate_eof; - int nth = 0; - - if (patch->is_binary) - return apply_binary(state, img, patch); - - while (frag) { - nth++; - if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) { - error(_("patch failed: %s:%ld"), name, frag->oldpos); - if (!state->apply_with_reject) - return -1; - frag->rejected = 1; - } - frag = frag->next; - } - return 0; -} - -static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode) -{ - if (S_ISGITLINK(mode)) { - strbuf_grow(buf, 100); - strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1)); - } else { - enum object_type type; - unsigned long sz; - char *result; - - result = read_sha1_file(sha1, &type, &sz); - if (!result) - return -1; - /* XXX read_sha1_file NUL-terminates */ - strbuf_attach(buf, result, sz, sz + 1); - } - return 0; -} - -static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf) -{ - if (!ce) - return 0; - return read_blob_object(buf, ce->sha1, ce->ce_mode); -} - -static struct patch *in_fn_table(struct apply_state *state, const char *name) -{ - struct string_list_item *item; - - if (name == NULL) - return NULL; - - item = string_list_lookup(&state->fn_table, name); - if (item != NULL) - return (struct patch *)item->util; - - return NULL; -} - -/* - * item->util in the filename table records the status of the path. - * Usually it points at a patch (whose result records the contents - * of it after applying it), but it could be PATH_WAS_DELETED for a - * path that a previously applied patch has already removed, or - * PATH_TO_BE_DELETED for a path that a later patch would remove. - * - * The latter is needed to deal with a case where two paths A and B - * are swapped by first renaming A to B and then renaming B to A; - * moving A to B should not be prevented due to presence of B as we - * will remove it in a later patch. - */ -#define PATH_TO_BE_DELETED ((struct patch *) -2) -#define PATH_WAS_DELETED ((struct patch *) -1) - -static int to_be_deleted(struct patch *patch) -{ - return patch == PATH_TO_BE_DELETED; -} - -static int was_deleted(struct patch *patch) -{ - return patch == PATH_WAS_DELETED; -} - -static void add_to_fn_table(struct apply_state *state, struct patch *patch) -{ - struct string_list_item *item; - - /* - * Always add new_name unless patch is a deletion - * This should cover the cases for normal diffs, - * file creations and copies - */ - if (patch->new_name != NULL) { - item = string_list_insert(&state->fn_table, patch->new_name); - item->util = patch; - } - - /* - * store a failure on rename/deletion cases because - * later chunks shouldn't patch old names - */ - if ((patch->new_name == NULL) || (patch->is_rename)) { - item = string_list_insert(&state->fn_table, patch->old_name); - item->util = PATH_WAS_DELETED; - } -} - -static void prepare_fn_table(struct apply_state *state, struct patch *patch) -{ - /* - * store information about incoming file deletion - */ - while (patch) { - if ((patch->new_name == NULL) || (patch->is_rename)) { - struct string_list_item *item; - item = string_list_insert(&state->fn_table, patch->old_name); - item->util = PATH_TO_BE_DELETED; - } - patch = patch->next; - } -} - -static int checkout_target(struct index_state *istate, - struct cache_entry *ce, struct stat *st) -{ - struct checkout costate; - - memset(&costate, 0, sizeof(costate)); - costate.base_dir = ""; - costate.refresh_cache = 1; - costate.istate = istate; - if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st)) - return error(_("cannot checkout %s"), ce->name); - return 0; -} - -static struct patch *previous_patch(struct apply_state *state, - struct patch *patch, - int *gone) -{ - struct patch *previous; - - *gone = 0; - if (patch->is_copy || patch->is_rename) - return NULL; /* "git" patches do not depend on the order */ - - previous = in_fn_table(state, patch->old_name); - if (!previous) - return NULL; - - if (to_be_deleted(previous)) - return NULL; /* the deletion hasn't happened yet */ - - if (was_deleted(previous)) - *gone = 1; - - return previous; -} - -static int verify_index_match(const struct cache_entry *ce, struct stat *st) -{ - if (S_ISGITLINK(ce->ce_mode)) { - if (!S_ISDIR(st->st_mode)) - return -1; - return 0; - } - return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); -} - -#define SUBMODULE_PATCH_WITHOUT_INDEX 1 - -static int load_patch_target(struct apply_state *state, - struct strbuf *buf, - const struct cache_entry *ce, - struct stat *st, - const char *name, - unsigned expected_mode) -{ - if (state->cached || state->check_index) { - if (read_file_or_gitlink(ce, buf)) - return error(_("failed to read %s"), name); - } else if (name) { - if (S_ISGITLINK(expected_mode)) { - if (ce) - return read_file_or_gitlink(ce, buf); - else - return SUBMODULE_PATCH_WITHOUT_INDEX; - } else if (has_symlink_leading_path(name, strlen(name))) { - return error(_("reading from '%s' beyond a symbolic link"), name); - } else { - if (read_old_data(st, name, buf)) - return error(_("failed to read %s"), name); - } - } - return 0; -} - -/* - * We are about to apply "patch"; populate the "image" with the - * current version we have, from the working tree or from the index, - * depending on the situation e.g. --cached/--index. If we are - * applying a non-git patch that incrementally updates the tree, - * we read from the result of a previous diff. - */ -static int load_preimage(struct apply_state *state, - struct image *image, - struct patch *patch, struct stat *st, - const struct cache_entry *ce) -{ - struct strbuf buf = STRBUF_INIT; - size_t len; - char *img; - struct patch *previous; - int status; - - previous = previous_patch(state, patch, &status); - if (status) - return error(_("path %s has been renamed/deleted"), - patch->old_name); - if (previous) { - /* We have a patched copy in memory; use that. */ - strbuf_add(&buf, previous->result, previous->resultsize); - } else { - status = load_patch_target(state, &buf, ce, st, - patch->old_name, patch->old_mode); - if (status < 0) - return status; - else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) { - /* - * There is no way to apply subproject - * patch without looking at the index. - * NEEDSWORK: shouldn't this be flagged - * as an error??? - */ - free_fragment_list(patch->fragments); - patch->fragments = NULL; - } else if (status) { - return error(_("failed to read %s"), patch->old_name); - } - } - - img = strbuf_detach(&buf, &len); - prepare_image(image, img, len, !patch->is_binary); - return 0; -} - -static int three_way_merge(struct image *image, - char *path, - const unsigned char *base, - const unsigned char *ours, - const unsigned char *theirs) -{ - mmfile_t base_file, our_file, their_file; - mmbuffer_t result = { NULL }; - int status; - - read_mmblob(&base_file, base); - read_mmblob(&our_file, ours); - read_mmblob(&their_file, theirs); - status = ll_merge(&result, path, - &base_file, "base", - &our_file, "ours", - &their_file, "theirs", NULL); - free(base_file.ptr); - free(our_file.ptr); - free(their_file.ptr); - if (status < 0 || !result.ptr) { - free(result.ptr); - return -1; - } - clear_image(image); - image->buf = result.ptr; - image->len = result.size; - - return status; -} - -/* - * When directly falling back to add/add three-way merge, we read from - * the current contents of the new_name. In no cases other than that - * this function will be called. - */ -static int load_current(struct apply_state *state, - struct image *image, - struct patch *patch) -{ - struct strbuf buf = STRBUF_INIT; - int status, pos; - size_t len; - char *img; - struct stat st; - struct cache_entry *ce; - char *name = patch->new_name; - unsigned mode = patch->new_mode; - - if (!patch->is_new) - die("BUG: patch to %s is not a creation", patch->old_name); - - pos = cache_name_pos(name, strlen(name)); - if (pos < 0) - return error(_("%s: does not exist in index"), name); - ce = active_cache[pos]; - if (lstat(name, &st)) { - if (errno != ENOENT) - return error(_("%s: %s"), name, strerror(errno)); - if (checkout_target(&the_index, ce, &st)) - return -1; - } - if (verify_index_match(ce, &st)) - return error(_("%s: does not match index"), name); - - status = load_patch_target(state, &buf, ce, &st, name, mode); - if (status < 0) - return status; - else if (status) - return -1; - img = strbuf_detach(&buf, &len); - prepare_image(image, img, len, !patch->is_binary); - return 0; -} - -static int try_threeway(struct apply_state *state, - struct image *image, - struct patch *patch, - struct stat *st, - const struct cache_entry *ce) -{ - unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; - struct strbuf buf = STRBUF_INIT; - size_t len; - int status; - char *img; - struct image tmp_image; - - /* No point falling back to 3-way merge in these cases */ - if (patch->is_delete || - S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode)) - return -1; - - /* Preimage the patch was prepared for */ - if (patch->is_new) - write_sha1_file("", 0, blob_type, pre_sha1); - else if (get_sha1(patch->old_sha1_prefix, pre_sha1) || - read_blob_object(&buf, pre_sha1, patch->old_mode)) - return error("repository lacks the necessary blob to fall back on 3-way merge."); - - fprintf(stderr, "Falling back to three-way merge...\n"); - - img = strbuf_detach(&buf, &len); - prepare_image(&tmp_image, img, len, 1); - /* Apply the patch to get the post image */ - if (apply_fragments(state, &tmp_image, patch) < 0) { - clear_image(&tmp_image); - return -1; - } - /* post_sha1[] is theirs */ - write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1); - clear_image(&tmp_image); - - /* our_sha1[] is ours */ - if (patch->is_new) { - if (load_current(state, &tmp_image, patch)) - return error("cannot read the current contents of '%s'", - patch->new_name); - } else { - if (load_preimage(state, &tmp_image, patch, st, ce)) - return error("cannot read the current contents of '%s'", - patch->old_name); - } - write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1); - clear_image(&tmp_image); - - /* in-core three-way merge between post and our using pre as base */ - status = three_way_merge(image, patch->new_name, - pre_sha1, our_sha1, post_sha1); - if (status < 0) { - fprintf(stderr, "Failed to fall back on three-way merge...\n"); - return status; - } - - if (status) { - patch->conflicted_threeway = 1; - if (patch->is_new) - oidclr(&patch->threeway_stage[0]); - else - hashcpy(patch->threeway_stage[0].hash, pre_sha1); - hashcpy(patch->threeway_stage[1].hash, our_sha1); - hashcpy(patch->threeway_stage[2].hash, post_sha1); - fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name); - } else { - fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name); - } - return 0; -} - -static int apply_data(struct apply_state *state, struct patch *patch, - struct stat *st, const struct cache_entry *ce) -{ - struct image image; - - if (load_preimage(state, &image, patch, st, ce) < 0) - return -1; - - if (patch->direct_to_threeway || - apply_fragments(state, &image, patch) < 0) { - /* Note: with --reject, apply_fragments() returns 0 */ - if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0) - return -1; - } - patch->result = image.buf; - patch->resultsize = image.len; - add_to_fn_table(state, patch); - free(image.line_allocated); - - if (0 < patch->is_delete && patch->resultsize) - return error(_("removal patch leaves file contents")); - - return 0; -} - -/* - * If "patch" that we are looking at modifies or deletes what we have, - * we would want it not to lose any local modification we have, either - * in the working tree or in the index. - * - * This also decides if a non-git patch is a creation patch or a - * modification to an existing empty file. We do not check the state - * of the current tree for a creation patch in this function; the caller - * check_patch() separately makes sure (and errors out otherwise) that - * the path the patch creates does not exist in the current tree. - */ -static int check_preimage(struct apply_state *state, - struct patch *patch, - struct cache_entry **ce, - struct stat *st) -{ - const char *old_name = patch->old_name; - struct patch *previous = NULL; - int stat_ret = 0, status; - unsigned st_mode = 0; - - if (!old_name) - return 0; - - assert(patch->is_new <= 0); - previous = previous_patch(state, patch, &status); - - if (status) - return error(_("path %s has been renamed/deleted"), old_name); - if (previous) { - st_mode = previous->new_mode; - } else if (!state->cached) { - stat_ret = lstat(old_name, st); - if (stat_ret && errno != ENOENT) - return error(_("%s: %s"), old_name, strerror(errno)); - } - - if (state->check_index && !previous) { - int pos = cache_name_pos(old_name, strlen(old_name)); - if (pos < 0) { - if (patch->is_new < 0) - goto is_new; - return error(_("%s: does not exist in index"), old_name); - } - *ce = active_cache[pos]; - if (stat_ret < 0) { - if (checkout_target(&the_index, *ce, st)) - return -1; - } - if (!state->cached && verify_index_match(*ce, st)) - return error(_("%s: does not match index"), old_name); - if (state->cached) - st_mode = (*ce)->ce_mode; - } else if (stat_ret < 0) { - if (patch->is_new < 0) - goto is_new; - return error(_("%s: %s"), old_name, strerror(errno)); - } - - if (!state->cached && !previous) - st_mode = ce_mode_from_stat(*ce, st->st_mode); - - if (patch->is_new < 0) - patch->is_new = 0; - if (!patch->old_mode) - patch->old_mode = st_mode; - if ((st_mode ^ patch->old_mode) & S_IFMT) - return error(_("%s: wrong type"), old_name); - if (st_mode != patch->old_mode) - warning(_("%s has type %o, expected %o"), - old_name, st_mode, patch->old_mode); - if (!patch->new_mode && !patch->is_delete) - patch->new_mode = st_mode; - return 0; - - is_new: - patch->is_new = 1; - patch->is_delete = 0; - free(patch->old_name); - patch->old_name = NULL; - return 0; -} - - -#define EXISTS_IN_INDEX 1 -#define EXISTS_IN_WORKTREE 2 - -static int check_to_create(struct apply_state *state, - const char *new_name, - int ok_if_exists) -{ - struct stat nst; - - if (state->check_index && - cache_name_pos(new_name, strlen(new_name)) >= 0 && - !ok_if_exists) - return EXISTS_IN_INDEX; - if (state->cached) - return 0; - - if (!lstat(new_name, &nst)) { - if (S_ISDIR(nst.st_mode) || ok_if_exists) - return 0; - /* - * A leading component of new_name might be a symlink - * that is going to be removed with this patch, but - * still pointing at somewhere that has the path. - * In such a case, path "new_name" does not exist as - * far as git is concerned. - */ - if (has_symlink_leading_path(new_name, strlen(new_name))) - return 0; - - return EXISTS_IN_WORKTREE; - } else if ((errno != ENOENT) && (errno != ENOTDIR)) { - return error("%s: %s", new_name, strerror(errno)); - } - return 0; -} - -static uintptr_t register_symlink_changes(struct apply_state *state, - const char *path, - uintptr_t what) -{ - struct string_list_item *ent; - - ent = string_list_lookup(&state->symlink_changes, path); - if (!ent) { - ent = string_list_insert(&state->symlink_changes, path); - ent->util = (void *)0; - } - ent->util = (void *)(what | ((uintptr_t)ent->util)); - return (uintptr_t)ent->util; -} - -static uintptr_t check_symlink_changes(struct apply_state *state, const char *path) -{ - struct string_list_item *ent; - - ent = string_list_lookup(&state->symlink_changes, path); - if (!ent) - return 0; - return (uintptr_t)ent->util; -} - -static void prepare_symlink_changes(struct apply_state *state, struct patch *patch) -{ - for ( ; patch; patch = patch->next) { - if ((patch->old_name && S_ISLNK(patch->old_mode)) && - (patch->is_rename || patch->is_delete)) - /* the symlink at patch->old_name is removed */ - register_symlink_changes(state, patch->old_name, APPLY_SYMLINK_GOES_AWAY); - - if (patch->new_name && S_ISLNK(patch->new_mode)) - /* the symlink at patch->new_name is created or remains */ - register_symlink_changes(state, patch->new_name, APPLY_SYMLINK_IN_RESULT); - } -} - -static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name) -{ - do { - unsigned int change; - - while (--name->len && name->buf[name->len] != '/') - ; /* scan backwards */ - if (!name->len) - break; - name->buf[name->len] = '\0'; - change = check_symlink_changes(state, name->buf); - if (change & APPLY_SYMLINK_IN_RESULT) - return 1; - if (change & APPLY_SYMLINK_GOES_AWAY) - /* - * This cannot be "return 0", because we may - * see a new one created at a higher level. - */ - continue; - - /* otherwise, check the preimage */ - if (state->check_index) { - struct cache_entry *ce; - - ce = cache_file_exists(name->buf, name->len, ignore_case); - if (ce && S_ISLNK(ce->ce_mode)) - return 1; - } else { - struct stat st; - if (!lstat(name->buf, &st) && S_ISLNK(st.st_mode)) - return 1; - } - } while (1); - return 0; -} - -static int path_is_beyond_symlink(struct apply_state *state, const char *name_) -{ - int ret; - struct strbuf name = STRBUF_INIT; - - assert(*name_ != '\0'); - strbuf_addstr(&name, name_); - ret = path_is_beyond_symlink_1(state, &name); - strbuf_release(&name); - - return ret; -} - -static int check_unsafe_path(struct patch *patch) -{ - const char *old_name = NULL; - const char *new_name = NULL; - if (patch->is_delete) - old_name = patch->old_name; - else if (!patch->is_new && !patch->is_copy) - old_name = patch->old_name; - if (!patch->is_delete) - new_name = patch->new_name; - - if (old_name && !verify_path(old_name)) - return error(_("invalid path '%s'"), old_name); - if (new_name && !verify_path(new_name)) - return error(_("invalid path '%s'"), new_name); - return 0; -} - -/* - * Check and apply the patch in-core; leave the result in patch->result - * for the caller to write it out to the final destination. - */ -static int check_patch(struct apply_state *state, struct patch *patch) -{ - struct stat st; - const char *old_name = patch->old_name; - const char *new_name = patch->new_name; - const char *name = old_name ? old_name : new_name; - struct cache_entry *ce = NULL; - struct patch *tpatch; - int ok_if_exists; - int status; - - patch->rejected = 1; /* we will drop this after we succeed */ - - status = check_preimage(state, patch, &ce, &st); - if (status) - return status; - old_name = patch->old_name; - - /* - * A type-change diff is always split into a patch to delete - * old, immediately followed by a patch to create new (see - * diff.c::run_diff()); in such a case it is Ok that the entry - * to be deleted by the previous patch is still in the working - * tree and in the index. - * - * A patch to swap-rename between A and B would first rename A - * to B and then rename B to A. While applying the first one, - * the presence of B should not stop A from getting renamed to - * B; ask to_be_deleted() about the later rename. Removal of - * B and rename from A to B is handled the same way by asking - * was_deleted(). - */ - if ((tpatch = in_fn_table(state, new_name)) && - (was_deleted(tpatch) || to_be_deleted(tpatch))) - ok_if_exists = 1; - else - ok_if_exists = 0; - - if (new_name && - ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) { - int err = check_to_create(state, new_name, ok_if_exists); - - if (err && state->threeway) { - patch->direct_to_threeway = 1; - } else switch (err) { - case 0: - break; /* happy */ - case EXISTS_IN_INDEX: - return error(_("%s: already exists in index"), new_name); - break; - case EXISTS_IN_WORKTREE: - return error(_("%s: already exists in working directory"), - new_name); - default: - return err; - } - - if (!patch->new_mode) { - if (0 < patch->is_new) - patch->new_mode = S_IFREG | 0644; - else - patch->new_mode = patch->old_mode; - } - } - - if (new_name && old_name) { - int same = !strcmp(old_name, new_name); - if (!patch->new_mode) - patch->new_mode = patch->old_mode; - if ((patch->old_mode ^ patch->new_mode) & S_IFMT) { - if (same) - return error(_("new mode (%o) of %s does not " - "match old mode (%o)"), - patch->new_mode, new_name, - patch->old_mode); - else - return error(_("new mode (%o) of %s does not " - "match old mode (%o) of %s"), - patch->new_mode, new_name, - patch->old_mode, old_name); - } - } - - if (!state->unsafe_paths && check_unsafe_path(patch)) - return -128; - - /* - * An attempt to read from or delete a path that is beyond a - * symbolic link will be prevented by load_patch_target() that - * is called at the beginning of apply_data() so we do not - * have to worry about a patch marked with "is_delete" bit - * here. We however need to make sure that the patch result - * is not deposited to a path that is beyond a symbolic link - * here. - */ - if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name)) - return error(_("affected file '%s' is beyond a symbolic link"), - patch->new_name); - - if (apply_data(state, patch, &st, ce) < 0) - return error(_("%s: patch does not apply"), name); - patch->rejected = 0; - return 0; -} - -static int check_patch_list(struct apply_state *state, struct patch *patch) -{ - int err = 0; - - prepare_symlink_changes(state, patch); - prepare_fn_table(state, patch); - while (patch) { - int res; - if (state->apply_verbosely) - say_patch_name(stderr, - _("Checking patch %s..."), patch); - res = check_patch(state, patch); - if (res == -128) - return -128; - err |= res; - patch = patch->next; - } - return err; -} - -/* This function tries to read the sha1 from the current index */ -static int get_current_sha1(const char *path, unsigned char *sha1) -{ - int pos; - - if (read_cache() < 0) - return -1; - pos = cache_name_pos(path, strlen(path)); - if (pos < 0) - return -1; - hashcpy(sha1, active_cache[pos]->sha1); - return 0; -} - -static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20]) -{ - /* - * A usable gitlink patch has only one fragment (hunk) that looks like: - * @@ -1 +1 @@ - * -Subproject commit - * +Subproject commit - * or - * @@ -1 +0,0 @@ - * -Subproject commit - * for a removal patch. - */ - struct fragment *hunk = p->fragments; - static const char heading[] = "-Subproject commit "; - char *preimage; - - if (/* does the patch have only one hunk? */ - hunk && !hunk->next && - /* is its preimage one line? */ - hunk->oldpos == 1 && hunk->oldlines == 1 && - /* does preimage begin with the heading? */ - (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL && - starts_with(++preimage, heading) && - /* does it record full SHA-1? */ - !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) && - preimage[sizeof(heading) + 40 - 1] == '\n' && - /* does the abbreviated name on the index line agree with it? */ - starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix)) - return 0; /* it all looks fine */ - - /* we may have full object name on the index line */ - return get_sha1_hex(p->old_sha1_prefix, sha1); -} - -/* Build an index that contains the just the files needed for a 3way merge */ -static int build_fake_ancestor(struct patch *list, const char *filename) -{ - struct patch *patch; - struct index_state result = { NULL }; - static struct lock_file lock; - int res; - - /* Once we start supporting the reverse patch, it may be - * worth showing the new sha1 prefix, but until then... - */ - for (patch = list; patch; patch = patch->next) { - unsigned char sha1[20]; - struct cache_entry *ce; - const char *name; - - name = patch->old_name ? patch->old_name : patch->new_name; - if (0 < patch->is_new) - continue; - - if (S_ISGITLINK(patch->old_mode)) { - if (!preimage_sha1_in_gitlink_patch(patch, sha1)) - ; /* ok, the textual part looks sane */ - else - return error("sha1 information is lacking or " - "useless for submodule %s", name); - } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) { - ; /* ok */ - } else if (!patch->lines_added && !patch->lines_deleted) { - /* mode-only change: update the current */ - if (get_current_sha1(patch->old_name, sha1)) - return error("mode change for %s, which is not " - "in current HEAD", name); - } else - return error("sha1 information is lacking or useless " - "(%s).", name); - - ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0); - if (!ce) - return error(_("make_cache_entry failed for path '%s'"), - name); - if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) { - free(ce); - return error("Could not add %s to temporary index", - name); - } - } - - hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR); - res = write_locked_index(&result, &lock, COMMIT_LOCK); - discard_index(&result); - - if (res) - return error("Could not write temporary index to %s", filename); - - return 0; -} - -static void stat_patch_list(struct apply_state *state, struct patch *patch) -{ - int files, adds, dels; - - for (files = adds = dels = 0 ; patch ; patch = patch->next) { - files++; - adds += patch->lines_added; - dels += patch->lines_deleted; - show_stats(state, patch); - } - - print_stat_summary(stdout, files, adds, dels); -} - -static void numstat_patch_list(struct apply_state *state, - struct patch *patch) -{ - for ( ; patch; patch = patch->next) { - const char *name; - name = patch->new_name ? patch->new_name : patch->old_name; - if (patch->is_binary) - printf("-\t-\t"); - else - printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); - write_name_quoted(name, stdout, state->line_termination); - } -} - -static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name) -{ - if (mode) - printf(" %s mode %06o %s\n", newdelete, mode, name); - else - printf(" %s %s\n", newdelete, name); -} - -static void show_mode_change(struct patch *p, int show_name) -{ - if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) { - if (show_name) - printf(" mode change %06o => %06o %s\n", - p->old_mode, p->new_mode, p->new_name); - else - printf(" mode change %06o => %06o\n", - p->old_mode, p->new_mode); - } -} - -static void show_rename_copy(struct patch *p) -{ - const char *renamecopy = p->is_rename ? "rename" : "copy"; - const char *old, *new; - - /* Find common prefix */ - old = p->old_name; - new = p->new_name; - while (1) { - const char *slash_old, *slash_new; - slash_old = strchr(old, '/'); - slash_new = strchr(new, '/'); - if (!slash_old || - !slash_new || - slash_old - old != slash_new - new || - memcmp(old, new, slash_new - new)) - break; - old = slash_old + 1; - new = slash_new + 1; - } - /* p->old_name thru old is the common prefix, and old and new - * through the end of names are renames - */ - if (old != p->old_name) - printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy, - (int)(old - p->old_name), p->old_name, - old, new, p->score); - else - printf(" %s %s => %s (%d%%)\n", renamecopy, - p->old_name, p->new_name, p->score); - show_mode_change(p, 0); -} - -static void summary_patch_list(struct patch *patch) -{ - struct patch *p; - - for (p = patch; p; p = p->next) { - if (p->is_new) - show_file_mode_name("create", p->new_mode, p->new_name); - else if (p->is_delete) - show_file_mode_name("delete", p->old_mode, p->old_name); - else { - if (p->is_rename || p->is_copy) - show_rename_copy(p); - else { - if (p->score) { - printf(" rewrite %s (%d%%)\n", - p->new_name, p->score); - show_mode_change(p, 0); - } - else - show_mode_change(p, 1); - } - } - } -} - -static void patch_stats(struct apply_state *state, struct patch *patch) -{ - int lines = patch->lines_added + patch->lines_deleted; - - if (lines > state->max_change) - state->max_change = lines; - if (patch->old_name) { - int len = quote_c_style(patch->old_name, NULL, NULL, 0); - if (!len) - len = strlen(patch->old_name); - if (len > state->max_len) - state->max_len = len; - } - if (patch->new_name) { - int len = quote_c_style(patch->new_name, NULL, NULL, 0); - if (!len) - len = strlen(patch->new_name); - if (len > state->max_len) - state->max_len = len; - } -} - -static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) -{ - if (state->update_index) { - if (remove_file_from_cache(patch->old_name) < 0) - return error(_("unable to remove %s from index"), patch->old_name); - } - if (!state->cached) { - if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { - remove_path(patch->old_name); - } - } - return 0; -} - -static int add_index_file(struct apply_state *state, - const char *path, - unsigned mode, - void *buf, - unsigned long size) -{ - struct stat st; - struct cache_entry *ce; - int namelen = strlen(path); - unsigned ce_size = cache_entry_size(namelen); - - if (!state->update_index) - return 0; - - ce = xcalloc(1, ce_size); - memcpy(ce->name, path, namelen); - ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = create_ce_flags(0); - ce->ce_namelen = namelen; - if (S_ISGITLINK(mode)) { - const char *s; - - if (!skip_prefix(buf, "Subproject commit ", &s) || - get_sha1_hex(s, ce->sha1)) { - free(ce); - return error(_("corrupt patch for submodule %s"), path); - } - } else { - if (!state->cached) { - if (lstat(path, &st) < 0) { - free(ce); - return error(_("unable to stat newly " - "created file '%s': %s"), - path, strerror(errno)); - } - fill_stat_cache_info(ce, &st); - } - if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) { - free(ce); - return error(_("unable to create backing store " - "for newly created file %s"), path); - } - } - if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { - free(ce); - return error(_("unable to add cache entry for %s"), path); - } - - return 0; -} - -/* - * Returns: - * -1 if an unrecoverable error happened - * 0 if everything went well - * 1 if a recoverable error happened - */ -static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size) -{ - int fd, res; - struct strbuf nbuf = STRBUF_INIT; - - if (S_ISGITLINK(mode)) { - struct stat st; - if (!lstat(path, &st) && S_ISDIR(st.st_mode)) - return 0; - return !!mkdir(path, 0777); - } - - if (has_symlinks && S_ISLNK(mode)) - /* Although buf:size is counted string, it also is NUL - * terminated. - */ - return !!symlink(buf, path); - - fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666); - if (fd < 0) - return 1; - - if (convert_to_working_tree(path, buf, size, &nbuf)) { - size = nbuf.len; - buf = nbuf.buf; - } - - res = write_in_full(fd, buf, size) < 0; - if (res) - error_errno(_("failed to write to '%s'"), path); - strbuf_release(&nbuf); - - if (close(fd) < 0 && !res) - return error_errno(_("closing file '%s'"), path); - - return res ? -1 : 0; -} - -/* - * We optimistically assume that the directories exist, - * which is true 99% of the time anyway. If they don't, - * we create them and try again. - * - * Returns: - * -1 on error - * 0 otherwise - */ -static int create_one_file(struct apply_state *state, - char *path, - unsigned mode, - const char *buf, - unsigned long size) -{ - int res; - - if (state->cached) - return 0; - - res = try_create_file(path, mode, buf, size); - if (res < 0) - return -1; - if (!res) - return 0; - - if (errno == ENOENT) { - if (safe_create_leading_directories(path)) - return 0; - res = try_create_file(path, mode, buf, size); - if (res < 0) - return -1; - if (!res) - return 0; - } - - if (errno == EEXIST || errno == EACCES) { - /* We may be trying to create a file where a directory - * used to be. - */ - struct stat st; - if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path))) - errno = EEXIST; - } - - if (errno == EEXIST) { - unsigned int nr = getpid(); - - for (;;) { - char newpath[PATH_MAX]; - mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr); - res = try_create_file(newpath, mode, buf, size); - if (res < 0) - return -1; - if (!res) { - if (!rename(newpath, path)) - return 0; - unlink_or_warn(newpath); - break; - } - if (errno != EEXIST) - break; - ++nr; - } - } - return error_errno(_("unable to write file '%s' mode %o"), - path, mode); -} - -static int add_conflicted_stages_file(struct apply_state *state, - struct patch *patch) -{ - int stage, namelen; - unsigned ce_size, mode; - struct cache_entry *ce; - - if (!state->update_index) - return 0; - namelen = strlen(patch->new_name); - ce_size = cache_entry_size(namelen); - mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644); - - remove_file_from_cache(patch->new_name); - for (stage = 1; stage < 4; stage++) { - if (is_null_oid(&patch->threeway_stage[stage - 1])) - continue; - ce = xcalloc(1, ce_size); - memcpy(ce->name, patch->new_name, namelen); - ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = create_ce_flags(stage); - ce->ce_namelen = namelen; - hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash); - if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { - free(ce); - return error(_("unable to add cache entry for %s"), - patch->new_name); - } - } - - return 0; -} - -static int create_file(struct apply_state *state, struct patch *patch) -{ - char *path = patch->new_name; - unsigned mode = patch->new_mode; - unsigned long size = patch->resultsize; - char *buf = patch->result; - - if (!mode) - mode = S_IFREG | 0644; - if (create_one_file(state, path, mode, buf, size)) - return -1; - - if (patch->conflicted_threeway) - return add_conflicted_stages_file(state, patch); - else - return add_index_file(state, path, mode, buf, size); -} - -/* phase zero is to remove, phase one is to create */ -static int write_out_one_result(struct apply_state *state, - struct patch *patch, - int phase) -{ - if (patch->is_delete > 0) { - if (phase == 0) - return remove_file(state, patch, 1); - return 0; - } - if (patch->is_new > 0 || patch->is_copy) { - if (phase == 1) - return create_file(state, patch); - return 0; - } - /* - * Rename or modification boils down to the same - * thing: remove the old, write the new - */ - if (phase == 0) - return remove_file(state, patch, patch->is_rename); - if (phase == 1) - return create_file(state, patch); - return 0; -} - -static int write_out_one_reject(struct apply_state *state, struct patch *patch) -{ - FILE *rej; - char namebuf[PATH_MAX]; - struct fragment *frag; - int cnt = 0; - struct strbuf sb = STRBUF_INIT; - - for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) { - if (!frag->rejected) - continue; - cnt++; - } - - if (!cnt) { - if (state->apply_verbosely) - say_patch_name(stderr, - _("Applied patch %s cleanly."), patch); - return 0; - } - - /* This should not happen, because a removal patch that leaves - * contents are marked "rejected" at the patch level. - */ - if (!patch->new_name) - die(_("internal error")); - - /* Say this even without --verbose */ - strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...", - "Applying patch %%s with %d rejects...", - cnt), - cnt); - say_patch_name(stderr, sb.buf, patch); - strbuf_release(&sb); - - cnt = strlen(patch->new_name); - if (ARRAY_SIZE(namebuf) <= cnt + 5) { - cnt = ARRAY_SIZE(namebuf) - 5; - warning(_("truncating .rej filename to %.*s.rej"), - cnt - 1, patch->new_name); - } - memcpy(namebuf, patch->new_name, cnt); - memcpy(namebuf + cnt, ".rej", 5); - - rej = fopen(namebuf, "w"); - if (!rej) - return error(_("cannot open %s: %s"), namebuf, strerror(errno)); - - /* Normal git tools never deal with .rej, so do not pretend - * this is a git patch by saying --git or giving extended - * headers. While at it, maybe please "kompare" that wants - * the trailing TAB and some garbage at the end of line ;-). - */ - fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n", - patch->new_name, patch->new_name); - for (cnt = 1, frag = patch->fragments; - frag; - cnt++, frag = frag->next) { - if (!frag->rejected) { - fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt); - continue; - } - fprintf_ln(stderr, _("Rejected hunk #%d."), cnt); - fprintf(rej, "%.*s", frag->size, frag->patch); - if (frag->patch[frag->size-1] != '\n') - fputc('\n', rej); - } - fclose(rej); - return -1; -} - -/* - * Returns: - * -1 if an error happened - * 0 if the patch applied cleanly - * 1 if the patch did not apply cleanly - */ -static int write_out_results(struct apply_state *state, struct patch *list) -{ - int phase; - int errs = 0; - struct patch *l; - struct string_list cpath = STRING_LIST_INIT_DUP; - - for (phase = 0; phase < 2; phase++) { - l = list; - while (l) { - if (l->rejected) - errs = 1; - else { - if (write_out_one_result(state, l, phase)) { - string_list_clear(&cpath, 0); - return -1; - } - if (phase == 1) { - if (write_out_one_reject(state, l)) - errs = 1; - if (l->conflicted_threeway) { - string_list_append(&cpath, l->new_name); - errs = 1; - } - } - } - l = l->next; - } - } - - if (cpath.nr) { - struct string_list_item *item; - - string_list_sort(&cpath); - for_each_string_list_item(item, &cpath) - fprintf(stderr, "U %s\n", item->string); - string_list_clear(&cpath, 0); - - rerere(0); - } - - return errs; -} - static struct lock_file lock_file; -/* - * Try to apply a patch. - * - * Returns: - * -128 if a bad error happened (like patch unreadable) - * -1 if patch did not apply and user cannot deal with it - * 0 if the patch applied - * 1 if the patch did not apply but user might fix it - */ -static int apply_patch(struct apply_state *state, - int fd, - const char *filename, - int options) -{ - size_t offset; - struct strbuf buf = STRBUF_INIT; /* owns the patch text */ - struct patch *list = NULL, **listp = &list; - int skipped_patch = 0; - int res = 0; - - state->patch_input_file = filename; - if (read_patch_file(&buf, fd) < 0) - return -128; - offset = 0; - while (offset < buf.len) { - struct patch *patch; - int nr; - - patch = xcalloc(1, sizeof(*patch)); - patch->inaccurate_eof = !!(options & APPLY_OPT_INACCURATE_EOF); - patch->recount = !!(options & APPLY_OPT_RECOUNT); - nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); - if (nr < 0) { - free_patch(patch); - if (nr == -128) { - res = -128; - goto end; - } - break; - } - if (state->apply_in_reverse) - reverse_patches(patch); - if (use_patch(state, patch)) { - patch_stats(state, patch); - *listp = patch; - listp = &patch->next; - } - else { - if (state->apply_verbosely) - say_patch_name(stderr, _("Skipped patch '%s'."), patch); - free_patch(patch); - skipped_patch++; - } - offset += nr; - } - - if (!list && !skipped_patch) { - error(_("unrecognized input")); - res = -128; - goto end; - } - - if (state->whitespace_error && (state->ws_error_action == die_on_ws_error)) - state->apply = 0; - - state->update_index = state->check_index && state->apply; - if (state->update_index && state->newfd < 0) - state->newfd = hold_locked_index(state->lock_file, 1); - - if (state->check_index && read_cache() < 0) { - error(_("unable to read index file")); - res = -128; - goto end; - } - - if (state->check || state->apply) { - int r = check_patch_list(state, list); - if (r == -128) { - res = -128; - goto end; - } - if (r < 0 && !state->apply_with_reject) { - res = -1; - goto end; - } - } - - if (state->apply) { - int write_res = write_out_results(state, list); - if (write_res < 0) { - res = -128; - goto end; - } - if (write_res > 0) { - /* with --3way, we still need to write the index out */ - res = state->apply_with_reject ? -1 : 1; - goto end; - } - } - - if (state->fake_ancestor && - build_fake_ancestor(list, state->fake_ancestor)) { - res = -128; - goto end; - } - - if (state->diffstat) - stat_patch_list(state, list); - - if (state->numstat) - numstat_patch_list(state, list); - - if (state->summary) - summary_patch_list(list); - -end: - free_patch_list(list); - strbuf_release(&buf); - string_list_clear(&state->fn_table, 0); - return res; -} - -static int apply_option_parse_exclude(const struct option *opt, - const char *arg, int unset) -{ - struct apply_state *state = opt->value; - add_name_limit(state, arg, 1); - return 0; -} - -static int apply_option_parse_include(const struct option *opt, - const char *arg, int unset) -{ - struct apply_state *state = opt->value; - add_name_limit(state, arg, 0); - state->has_include = 1; - return 0; -} - -static int apply_option_parse_p(const struct option *opt, - const char *arg, - int unset) -{ - struct apply_state *state = opt->value; - state->p_value = atoi(arg); - state->p_value_known = 1; - return 0; -} - -static int apply_option_parse_space_change(const struct option *opt, - const char *arg, int unset) -{ - struct apply_state *state = opt->value; - if (unset) - state->ws_ignore_action = ignore_ws_none; - else - state->ws_ignore_action = ignore_ws_change; - return 0; -} - -static int apply_option_parse_whitespace(const struct option *opt, - const char *arg, int unset) -{ - struct apply_state *state = opt->value; - state->whitespace_option = arg; - if (parse_whitespace_option(state, arg)) - exit(1); - return 0; -} - -static int apply_option_parse_directory(const struct option *opt, - const char *arg, int unset) -{ - struct apply_state *state = opt->value; - strbuf_reset(&state->root); - strbuf_addstr(&state->root, arg); - strbuf_complete(&state->root, '/'); - return 0; -} - -static int apply_all_patches(struct apply_state *state, - int argc, - const char **argv, - int options) -{ - int i; - int res; - int errs = 0; - int read_stdin = 1; - - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - int fd; - - if (!strcmp(arg, "-")) { - res = apply_patch(state, 0, "", options); - if (res < 0) - goto end; - errs |= res; - read_stdin = 0; - continue; - } else if (0 < state->prefix_length) - arg = prefix_filename(state->prefix, - state->prefix_length, - arg); - - fd = open(arg, O_RDONLY); - if (fd < 0) { - error(_("can't open patch '%s': %s"), arg, strerror(errno)); - res = -128; - goto end; - } - read_stdin = 0; - set_default_whitespace_mode(state); - res = apply_patch(state, fd, arg, options); - close(fd); - if (res < 0) - goto end; - errs |= res; - } - set_default_whitespace_mode(state); - if (read_stdin) { - res = apply_patch(state, 0, "", options); - if (res < 0) - goto end; - errs |= res; - } - - if (state->whitespace_error) { - if (state->squelch_whitespace_errors && - state->squelch_whitespace_errors < state->whitespace_error) { - int squelched = - state->whitespace_error - state->squelch_whitespace_errors; - warning(Q_("squelched %d whitespace error", - "squelched %d whitespace errors", - squelched), - squelched); - } - if (state->ws_error_action == die_on_ws_error) { - error(Q_("%d line adds whitespace errors.", - "%d lines add whitespace errors.", - state->whitespace_error), - state->whitespace_error); - res = -128; - goto end; - } - if (state->applied_after_fixing_ws && state->apply) - warning("%d line%s applied after" - " fixing whitespace errors.", - state->applied_after_fixing_ws, - state->applied_after_fixing_ws == 1 ? "" : "s"); - else if (state->whitespace_error) - warning(Q_("%d line adds whitespace errors.", - "%d lines add whitespace errors.", - state->whitespace_error), - state->whitespace_error); - } - - if (state->update_index) { - res = write_locked_index(&the_index, state->lock_file, COMMIT_LOCK); - if (res) { - error(_("Unable to write new index file")); - res = -128; - goto end; - } - state->newfd = -1; - } - - return !!errs; - -end: - if (state->newfd >= 0) { - rollback_lock_file(state->lock_file); - state->newfd = -1; - } - - return (res == -1 ? 1 : 128); -} - int cmd_apply(int argc, const char **argv, const char *prefix) { int force_apply = 0; -- cgit v0.10.2-6-g49f6 From 9123d5ddfe1c701a47d034403d302d57acf3e8bb Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:23 +0200 Subject: apply: make some parsing functions static again Some parsing functions that were used in both "apply.c" and "builtin/apply.c" are now only used in the former, so they can be made static to "apply.c". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index 5e3780d..98c7f6e 100644 --- a/apply.c +++ b/apply.c @@ -27,7 +27,7 @@ static void git_apply_config(void) git_config(git_default_config, NULL); } -int parse_whitespace_option(struct apply_state *state, const char *option) +static int parse_whitespace_option(struct apply_state *state, const char *option) { if (!option) { state->ws_error_action = warn_on_ws_error; @@ -57,8 +57,8 @@ int parse_whitespace_option(struct apply_state *state, const char *option) return error(_("unrecognized whitespace option '%s'"), option); } -int parse_ignorewhitespace_option(struct apply_state *state, - const char *option) +static int parse_ignorewhitespace_option(struct apply_state *state, + const char *option) { if (!option || !strcmp(option, "no") || !strcmp(option, "false") || !strcmp(option, "never") || diff --git a/apply.h b/apply.h index 28cbe6c..51b983b 100644 --- a/apply.h +++ b/apply.h @@ -97,11 +97,6 @@ struct apply_state { int applied_after_fixing_ws; }; -extern int parse_whitespace_option(struct apply_state *state, - const char *option); -extern int parse_ignorewhitespace_option(struct apply_state *state, - const char *option); - extern int apply_option_parse_exclude(const struct option *opt, const char *arg, int unset); extern int apply_option_parse_include(const struct option *opt, -- cgit v0.10.2-6-g49f6 From 90875eca5a0b227e6a1be3ccece0a3da5e72017f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:24 +0200 Subject: apply: use error_errno() where possible To avoid possible mistakes and to uniformly show the errno related messages, let's use error_errno() where possible. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index 98c7f6e..ce09ed9 100644 --- a/apply.c +++ b/apply.c @@ -3497,7 +3497,7 @@ static int load_current(struct apply_state *state, ce = active_cache[pos]; if (lstat(name, &st)) { if (errno != ENOENT) - return error(_("%s: %s"), name, strerror(errno)); + return error_errno("%s", name); if (checkout_target(&the_index, ce, &st)) return -1; } @@ -3647,7 +3647,7 @@ static int check_preimage(struct apply_state *state, } else if (!state->cached) { stat_ret = lstat(old_name, st); if (stat_ret && errno != ENOENT) - return error(_("%s: %s"), old_name, strerror(errno)); + return error_errno("%s", old_name); } if (state->check_index && !previous) { @@ -3669,7 +3669,7 @@ static int check_preimage(struct apply_state *state, } else if (stat_ret < 0) { if (patch->is_new < 0) goto is_new; - return error(_("%s: %s"), old_name, strerror(errno)); + return error_errno("%s", old_name); } if (!state->cached && !previous) @@ -3728,7 +3728,7 @@ static int check_to_create(struct apply_state *state, return EXISTS_IN_WORKTREE; } else if ((errno != ENOENT) && (errno != ENOTDIR)) { - return error("%s: %s", new_name, strerror(errno)); + return error_errno("%s", new_name); } return 0; } @@ -4247,9 +4247,9 @@ static int add_index_file(struct apply_state *state, if (!state->cached) { if (lstat(path, &st) < 0) { free(ce); - return error(_("unable to stat newly " - "created file '%s': %s"), - path, strerror(errno)); + return error_errno(_("unable to stat newly " + "created file '%s'"), + path); } fill_stat_cache_info(ce, &st); } @@ -4503,7 +4503,7 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch) rej = fopen(namebuf, "w"); if (!rej) - return error(_("cannot open %s: %s"), namebuf, strerror(errno)); + return error_errno(_("cannot open %s"), namebuf); /* Normal git tools never deal with .rej, so do not pretend * this is a git patch by saying --git or giving extended -- cgit v0.10.2-6-g49f6 From a46160d27ebdcd609aeae60b6163548af337d280 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:25 +0200 Subject: apply: make it possible to silently apply This changes 'int apply_verbosely' into 'enum apply_verbosity', and changes the possible values of the variable from a bool to a tristate. The previous 'false' state is changed into 'verbosity_normal'. The previous 'true' state is changed into 'verbosity_verbose'. The new added state is 'verbosity_silent'. It should prevent anything to be printed on both stderr and stdout. This is needed because `git am` wants to first call apply functionality silently, if it can then fall back on 3-way merge in case of error. Printing on stdout, and calls to warning() or error() are not taken care of in this patch, as that will be done in following patches. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index ce09ed9..74b450e 100644 --- a/apply.c +++ b/apply.c @@ -125,8 +125,11 @@ int check_apply_state(struct apply_state *state, int force_apply) return error(_("--3way outside a repository")); state->check_index = 1; } - if (state->apply_with_reject) - state->apply = state->apply_verbosely = 1; + if (state->apply_with_reject) { + state->apply = 1; + if (state->apply_verbosity == verbosity_normal) + state->apply_verbosity = verbosity_verbose; + } if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor)) state->apply = 0; if (state->check_index && is_not_gitdir) @@ -1620,8 +1623,9 @@ static void record_ws_error(struct apply_state *state, return; err = whitespace_error_string(result); - fprintf(stderr, "%s:%d: %s.\n%.*s\n", - state->patch_input_file, linenr, err, len, line); + if (state->apply_verbosity > verbosity_silent) + fprintf(stderr, "%s:%d: %s.\n%.*s\n", + state->patch_input_file, linenr, err, len, line); free(err); } @@ -1816,7 +1820,7 @@ static int parse_single_patch(struct apply_state *state, return error(_("new file %s depends on old contents"), patch->new_name); if (0 < patch->is_delete && newlines) return error(_("deleted file %s still has contents"), patch->old_name); - if (!patch->is_delete && !newlines && context) + if (!patch->is_delete && !newlines && context && state->apply_verbosity > verbosity_silent) fprintf_ln(stderr, _("** warning: " "file %s becomes empty but is not deleted"), @@ -2911,7 +2915,7 @@ static int apply_one_fragment(struct apply_state *state, /* Ignore it, we already handled it */ break; default: - if (state->apply_verbosely) + if (state->apply_verbosity > verbosity_normal) error(_("invalid start of line: '%c'"), first); applied_pos = -1; goto out; @@ -3026,7 +3030,7 @@ static int apply_one_fragment(struct apply_state *state, state->apply = 0; } - if (state->apply_verbosely && applied_pos != pos) { + if (state->apply_verbosity > verbosity_normal && applied_pos != pos) { int offset = applied_pos - pos; if (state->apply_in_reverse) offset = 0 - offset; @@ -3041,14 +3045,14 @@ static int apply_one_fragment(struct apply_state *state, * Warn if it was necessary to reduce the number * of context lines. */ - if ((leading != frag->leading) || - (trailing != frag->trailing)) + if ((leading != frag->leading || + trailing != frag->trailing) && state->apply_verbosity > verbosity_silent) fprintf_ln(stderr, _("Context reduced to (%ld/%ld)" " to apply fragment at %d"), leading, trailing, applied_pos+1); update_image(state, img, applied_pos, &preimage, &postimage); } else { - if (state->apply_verbosely) + if (state->apply_verbosity > verbosity_normal) error(_("while searching for:\n%.*s"), (int)(old - oldlines), oldlines); } @@ -3539,7 +3543,8 @@ static int try_threeway(struct apply_state *state, read_blob_object(&buf, pre_sha1, patch->old_mode)) return error("repository lacks the necessary blob to fall back on 3-way merge."); - fprintf(stderr, "Falling back to three-way merge...\n"); + if (state->apply_verbosity > verbosity_silent) + fprintf(stderr, "Falling back to three-way merge...\n"); img = strbuf_detach(&buf, &len); prepare_image(&tmp_image, img, len, 1); @@ -3569,7 +3574,9 @@ static int try_threeway(struct apply_state *state, status = three_way_merge(image, patch->new_name, pre_sha1, our_sha1, post_sha1); if (status < 0) { - fprintf(stderr, "Failed to fall back on three-way merge...\n"); + if (state->apply_verbosity > verbosity_silent) + fprintf(stderr, + "Failed to fall back on three-way merge...\n"); return status; } @@ -3581,9 +3588,15 @@ static int try_threeway(struct apply_state *state, hashcpy(patch->threeway_stage[0].hash, pre_sha1); hashcpy(patch->threeway_stage[1].hash, our_sha1); hashcpy(patch->threeway_stage[2].hash, post_sha1); - fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name); + if (state->apply_verbosity > verbosity_silent) + fprintf(stderr, + "Applied patch to '%s' with conflicts.\n", + patch->new_name); } else { - fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name); + if (state->apply_verbosity > verbosity_silent) + fprintf(stderr, + "Applied patch to '%s' cleanly.\n", + patch->new_name); } return 0; } @@ -3956,7 +3969,7 @@ static int check_patch_list(struct apply_state *state, struct patch *patch) prepare_fn_table(state, patch); while (patch) { int res; - if (state->apply_verbosely) + if (state->apply_verbosity > verbosity_normal) say_patch_name(stderr, _("Checking patch %s..."), patch); res = check_patch(state, patch); @@ -4472,7 +4485,7 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch) } if (!cnt) { - if (state->apply_verbosely) + if (state->apply_verbosity > verbosity_normal) say_patch_name(stderr, _("Applied patch %s cleanly."), patch); return 0; @@ -4489,7 +4502,8 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch) "Applying patch %%s with %d rejects...", cnt), cnt); - say_patch_name(stderr, sb.buf, patch); + if (state->apply_verbosity > verbosity_silent) + say_patch_name(stderr, sb.buf, patch); strbuf_release(&sb); cnt = strlen(patch->new_name); @@ -4516,10 +4530,12 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch) frag; cnt++, frag = frag->next) { if (!frag->rejected) { - fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt); + if (state->apply_verbosity > verbosity_silent) + fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt); continue; } - fprintf_ln(stderr, _("Rejected hunk #%d."), cnt); + if (state->apply_verbosity > verbosity_silent) + fprintf_ln(stderr, _("Rejected hunk #%d."), cnt); fprintf(rej, "%.*s", frag->size, frag->patch); if (frag->patch[frag->size-1] != '\n') fputc('\n', rej); @@ -4568,8 +4584,10 @@ static int write_out_results(struct apply_state *state, struct patch *list) struct string_list_item *item; string_list_sort(&cpath); - for_each_string_list_item(item, &cpath) - fprintf(stderr, "U %s\n", item->string); + if (state->apply_verbosity > verbosity_silent) { + for_each_string_list_item(item, &cpath) + fprintf(stderr, "U %s\n", item->string); + } string_list_clear(&cpath, 0); rerere(0); @@ -4626,7 +4644,7 @@ static int apply_patch(struct apply_state *state, listp = &patch->next; } else { - if (state->apply_verbosely) + if (state->apply_verbosity > verbosity_normal) say_patch_name(stderr, _("Skipped patch '%s'."), patch); free_patch(patch); skipped_patch++; diff --git a/apply.h b/apply.h index 51b983b..f015403 100644 --- a/apply.h +++ b/apply.h @@ -13,6 +13,12 @@ enum apply_ws_ignore { ignore_ws_change }; +enum apply_verbosity { + verbosity_silent = -1, + verbosity_normal = 0, + verbosity_verbose = 1 +}; + /* * We need to keep track of how symlinks in the preimage are * manipulated by the patches. A patch to add a/b/c where a/b @@ -51,13 +57,13 @@ struct apply_state { int allow_overlap; int apply_in_reverse; int apply_with_reject; - int apply_verbosely; int no_add; int threeway; int unidiff_zero; int unsafe_paths; /* Other non boolean parameters */ + enum apply_verbosity apply_verbosity; const char *fake_ancestor; const char *patch_input_file; int line_termination; diff --git a/builtin/apply.c b/builtin/apply.c index 9c66474..7338701 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -74,7 +74,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("leave the rejected hunks in corresponding *.rej files")), OPT_BOOL(0, "allow-overlap", &state.allow_overlap, N_("allow overlapping hunks")), - OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")), + OPT__VERBOSE(&state.apply_verbosity, N_("be verbose")), OPT_BIT(0, "inaccurate-eof", &options, N_("tolerate incorrectly detected missing new-line at the end of file"), APPLY_OPT_INACCURATE_EOF), -- cgit v0.10.2-6-g49f6 From 487beee0c37d48f06f1ccf19889f6c3dcbdbfa28 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:26 +0200 Subject: apply: don't print on stdout in verbosity_silent mode When apply_verbosity is set to verbosity_silent nothing should be printed on both stderr and stdout. To avoid printing on stdout, we can just skip calling the following functions: - stat_patch_list(), - numstat_patch_list(), - summary_patch_list(). It is safe to do that because the above functions have no side effects other than printing: - stat_patch_list() only computes some local values and then call show_stats() and print_stat_summary(), those two functions only compute local values and call printing functions, - numstat_patch_list() also only computes local values and calls printing functions, - summary_patch_list() calls show_file_mode_name(), printf(), show_rename_copy(), show_mode_change() that are only printing. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index 74b450e..a938439 100644 --- a/apply.c +++ b/apply.c @@ -4702,13 +4702,13 @@ static int apply_patch(struct apply_state *state, goto end; } - if (state->diffstat) + if (state->diffstat && state->apply_verbosity > verbosity_silent) stat_patch_list(state, list); - if (state->numstat) + if (state->numstat && state->apply_verbosity > verbosity_silent) numstat_patch_list(state, list); - if (state->summary) + if (state->summary && state->apply_verbosity > verbosity_silent) summary_patch_list(list); end: -- cgit v0.10.2-6-g49f6 From b83f108b082dfd5452d5c1ab03596fa13750d23f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:27 +0200 Subject: usage: add set_warn_routine() There are already set_die_routine() and set_error_routine(), so let's add set_warn_routine() as this will be needed in a following commit. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-compat-util.h b/git-compat-util.h index 590bfdd..c7a51f8 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -440,6 +440,7 @@ static inline int const_error(void) extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params)); extern void set_error_routine(void (*routine)(const char *err, va_list params)); +extern void set_warn_routine(void (*routine)(const char *warn, va_list params)); extern void set_die_is_recursing_routine(int (*routine)(void)); extern void set_error_handle(FILE *); diff --git a/usage.c b/usage.c index 1dad03f..67e5526 100644 --- a/usage.c +++ b/usage.c @@ -70,6 +70,11 @@ void set_error_routine(void (*routine)(const char *err, va_list params)) error_routine = routine; } +void set_warn_routine(void (*routine)(const char *warn, va_list params)) +{ + warn_routine = routine; +} + void set_die_is_recursing_routine(int (*routine)(void)) { die_is_recursing = routine; -- cgit v0.10.2-6-g49f6 From 725149beab088b1368cc01aa3f1a7845db14132d Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:28 +0200 Subject: usage: add get_error_routine() and get_warn_routine() Let's make it possible to get the current error_routine and warn_routine, so that we can store them before using set_error_routine() or set_warn_routine() to use new ones. This way we will be able put back the original routines, when we are done with using new ones. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-compat-util.h b/git-compat-util.h index c7a51f8..de04df1 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -440,7 +440,9 @@ static inline int const_error(void) extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params)); extern void set_error_routine(void (*routine)(const char *err, va_list params)); +extern void (*get_error_routine(void))(const char *err, va_list params); extern void set_warn_routine(void (*routine)(const char *warn, va_list params)); +extern void (*get_warn_routine(void))(const char *warn, va_list params); extern void set_die_is_recursing_routine(int (*routine)(void)); extern void set_error_handle(FILE *); diff --git a/usage.c b/usage.c index 67e5526..2fd3045 100644 --- a/usage.c +++ b/usage.c @@ -70,11 +70,21 @@ void set_error_routine(void (*routine)(const char *err, va_list params)) error_routine = routine; } +void (*get_error_routine(void))(const char *err, va_list params) +{ + return error_routine; +} + void set_warn_routine(void (*routine)(const char *warn, va_list params)) { warn_routine = routine; } +void (*get_warn_routine(void))(const char *warn, va_list params) +{ + return warn_routine; +} + void set_die_is_recursing_routine(int (*routine)(void)) { die_is_recursing = routine; -- cgit v0.10.2-6-g49f6 From 45b78d8ba3c9e542f1375171090fe10baef6b2b2 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:29 +0200 Subject: apply: change error_routine when silent To avoid printing anything when applying with `state->apply_verbosity == verbosity_silent`, let's save the existing warn and error routines before applying, and let's replace them with a routine that does nothing. Then after applying, let's restore the saved routines. Note that, as we need to restore the saved routines in all cases, we cannot return early any more in apply_all_patches(). Helped-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index a938439..3a888c2 100644 --- a/apply.c +++ b/apply.c @@ -112,6 +112,11 @@ void clear_apply_state(struct apply_state *state) /* &state->fn_table is cleared at the end of apply_patch() */ } +static void mute_routine(const char *msg, va_list params) +{ + /* do nothing */ +} + int check_apply_state(struct apply_state *state, int force_apply) { int is_not_gitdir = !startup_info->have_repository; @@ -144,6 +149,13 @@ int check_apply_state(struct apply_state *state, int force_apply) if (!state->lock_file) return error("BUG: state->lock_file should not be NULL"); + if (state->apply_verbosity <= verbosity_silent) { + state->saved_error_routine = get_error_routine(); + state->saved_warn_routine = get_warn_routine(); + set_error_routine(mute_routine); + set_warn_routine(mute_routine); + } + return 0; } @@ -4864,7 +4876,7 @@ int apply_all_patches(struct apply_state *state, state->newfd = -1; } - return !!errs; + res = !!errs; end: if (state->newfd >= 0) { @@ -4872,5 +4884,12 @@ end: state->newfd = -1; } + if (state->apply_verbosity <= verbosity_silent) { + set_error_routine(state->saved_error_routine); + set_warn_routine(state->saved_warn_routine); + } + + if (res > -1) + return res; return (res == -1 ? 1 : 128); } diff --git a/apply.h b/apply.h index f015403..902346b 100644 --- a/apply.h +++ b/apply.h @@ -94,6 +94,14 @@ struct apply_state { */ struct string_list fn_table; + /* + * This is to save reporting routines before using + * set_error_routine() or set_warn_routine() to install muting + * routines when in verbosity_silent mode. + */ + void (*saved_error_routine)(const char *err, va_list params); + void (*saved_warn_routine)(const char *warn, va_list params); + /* These control whitespace errors */ enum apply_ws_error_action ws_error_action; enum apply_ws_ignore ws_ignore_action; -- cgit v0.10.2-6-g49f6 From 7e1bad24e3b7f220813d8a449f19652113edb923 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:30 +0200 Subject: apply: refactor `git apply` option parsing Parsing `git apply` options can be useful to other commands that want to call the libified apply functionality, because this way they can easily pass some options from their own command line to the libified apply functionality. This will be used by `git am` in a following patch. To make this possible, let's refactor the `git apply` option parsing code into a new libified apply_parse_options() function. Doing that makes it possible to remove some functions definitions from "apply.h" and make them static in "apply.c". Helped-by: Ramsay Jones Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index 3a888c2..ae220be 100644 --- a/apply.c +++ b/apply.c @@ -4730,16 +4730,16 @@ end: return res; } -int apply_option_parse_exclude(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_exclude(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; add_name_limit(state, arg, 1); return 0; } -int apply_option_parse_include(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_include(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; add_name_limit(state, arg, 0); @@ -4747,9 +4747,9 @@ int apply_option_parse_include(const struct option *opt, return 0; } -int apply_option_parse_p(const struct option *opt, - const char *arg, - int unset) +static int apply_option_parse_p(const struct option *opt, + const char *arg, + int unset) { struct apply_state *state = opt->value; state->p_value = atoi(arg); @@ -4757,8 +4757,8 @@ int apply_option_parse_p(const struct option *opt, return 0; } -int apply_option_parse_space_change(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_space_change(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; if (unset) @@ -4768,8 +4768,8 @@ int apply_option_parse_space_change(const struct option *opt, return 0; } -int apply_option_parse_whitespace(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_whitespace(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; state->whitespace_option = arg; @@ -4778,8 +4778,8 @@ int apply_option_parse_whitespace(const struct option *opt, return 0; } -int apply_option_parse_directory(const struct option *opt, - const char *arg, int unset) +static int apply_option_parse_directory(const struct option *opt, + const char *arg, int unset) { struct apply_state *state = opt->value; strbuf_reset(&state->root); @@ -4893,3 +4893,80 @@ end: return res; return (res == -1 ? 1 : 128); } + +int apply_parse_options(int argc, const char **argv, + struct apply_state *state, + int *force_apply, int *options, + const char * const *apply_usage) +{ + struct option builtin_apply_options[] = { + { OPTION_CALLBACK, 0, "exclude", state, N_("path"), + N_("don't apply changes matching the given path"), + 0, apply_option_parse_exclude }, + { OPTION_CALLBACK, 0, "include", state, N_("path"), + N_("apply changes matching the given path"), + 0, apply_option_parse_include }, + { OPTION_CALLBACK, 'p', NULL, state, N_("num"), + N_("remove leading slashes from traditional diff paths"), + 0, apply_option_parse_p }, + OPT_BOOL(0, "no-add", &state->no_add, + N_("ignore additions made by the patch")), + OPT_BOOL(0, "stat", &state->diffstat, + N_("instead of applying the patch, output diffstat for the input")), + OPT_NOOP_NOARG(0, "allow-binary-replacement"), + OPT_NOOP_NOARG(0, "binary"), + OPT_BOOL(0, "numstat", &state->numstat, + N_("show number of added and deleted lines in decimal notation")), + OPT_BOOL(0, "summary", &state->summary, + N_("instead of applying the patch, output a summary for the input")), + OPT_BOOL(0, "check", &state->check, + N_("instead of applying the patch, see if the patch is applicable")), + OPT_BOOL(0, "index", &state->check_index, + N_("make sure the patch is applicable to the current index")), + OPT_BOOL(0, "cached", &state->cached, + N_("apply a patch without touching the working tree")), + OPT_BOOL(0, "unsafe-paths", &state->unsafe_paths, + N_("accept a patch that touches outside the working area")), + OPT_BOOL(0, "apply", force_apply, + N_("also apply the patch (use with --stat/--summary/--check)")), + OPT_BOOL('3', "3way", &state->threeway, + N_( "attempt three-way merge if a patch does not apply")), + OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor, + N_("build a temporary index based on embedded index information")), + /* Think twice before adding "--nul" synonym to this */ + OPT_SET_INT('z', NULL, &state->line_termination, + N_("paths are separated with NUL character"), '\0'), + OPT_INTEGER('C', NULL, &state->p_context, + N_("ensure at least lines of context match")), + { OPTION_CALLBACK, 0, "whitespace", state, N_("action"), + N_("detect new or modified lines that have whitespace errors"), + 0, apply_option_parse_whitespace }, + { OPTION_CALLBACK, 0, "ignore-space-change", state, NULL, + N_("ignore changes in whitespace when finding context"), + PARSE_OPT_NOARG, apply_option_parse_space_change }, + { OPTION_CALLBACK, 0, "ignore-whitespace", state, NULL, + N_("ignore changes in whitespace when finding context"), + PARSE_OPT_NOARG, apply_option_parse_space_change }, + OPT_BOOL('R', "reverse", &state->apply_in_reverse, + N_("apply the patch in reverse")), + OPT_BOOL(0, "unidiff-zero", &state->unidiff_zero, + N_("don't expect at least one line of context")), + OPT_BOOL(0, "reject", &state->apply_with_reject, + N_("leave the rejected hunks in corresponding *.rej files")), + OPT_BOOL(0, "allow-overlap", &state->allow_overlap, + N_("allow overlapping hunks")), + OPT__VERBOSE(&state->apply_verbosity, N_("be verbose")), + OPT_BIT(0, "inaccurate-eof", options, + N_("tolerate incorrectly detected missing new-line at the end of file"), + APPLY_OPT_INACCURATE_EOF), + OPT_BIT(0, "recount", options, + N_("do not trust the line counts in the hunk headers"), + APPLY_OPT_RECOUNT), + { OPTION_CALLBACK, 0, "directory", state, N_("root"), + N_("prepend to all filenames"), + 0, apply_option_parse_directory }, + OPT_END() + }; + + return parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0); +} diff --git a/apply.h b/apply.h index 902346b..9fec536 100644 --- a/apply.h +++ b/apply.h @@ -111,20 +111,10 @@ struct apply_state { int applied_after_fixing_ws; }; -extern int apply_option_parse_exclude(const struct option *opt, - const char *arg, int unset); -extern int apply_option_parse_include(const struct option *opt, - const char *arg, int unset); -extern int apply_option_parse_p(const struct option *opt, - const char *arg, - int unset); -extern int apply_option_parse_whitespace(const struct option *opt, - const char *arg, int unset); -extern int apply_option_parse_directory(const struct option *opt, - const char *arg, int unset); -extern int apply_option_parse_space_change(const struct option *opt, - const char *arg, int unset); - +extern int apply_parse_options(int argc, const char **argv, + struct apply_state *state, + int *force_apply, int *options, + const char * const *apply_usage); extern int init_apply_state(struct apply_state *state, const char *prefix, struct lock_file *lock_file); diff --git a/builtin/apply.c b/builtin/apply.c index 7338701..81b9a61 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -18,80 +18,12 @@ int cmd_apply(int argc, const char **argv, const char *prefix) int ret; struct apply_state state; - struct option builtin_apply_options[] = { - { OPTION_CALLBACK, 0, "exclude", &state, N_("path"), - N_("don't apply changes matching the given path"), - 0, apply_option_parse_exclude }, - { OPTION_CALLBACK, 0, "include", &state, N_("path"), - N_("apply changes matching the given path"), - 0, apply_option_parse_include }, - { OPTION_CALLBACK, 'p', NULL, &state, N_("num"), - N_("remove leading slashes from traditional diff paths"), - 0, apply_option_parse_p }, - OPT_BOOL(0, "no-add", &state.no_add, - N_("ignore additions made by the patch")), - OPT_BOOL(0, "stat", &state.diffstat, - N_("instead of applying the patch, output diffstat for the input")), - OPT_NOOP_NOARG(0, "allow-binary-replacement"), - OPT_NOOP_NOARG(0, "binary"), - OPT_BOOL(0, "numstat", &state.numstat, - N_("show number of added and deleted lines in decimal notation")), - OPT_BOOL(0, "summary", &state.summary, - N_("instead of applying the patch, output a summary for the input")), - OPT_BOOL(0, "check", &state.check, - N_("instead of applying the patch, see if the patch is applicable")), - OPT_BOOL(0, "index", &state.check_index, - N_("make sure the patch is applicable to the current index")), - OPT_BOOL(0, "cached", &state.cached, - N_("apply a patch without touching the working tree")), - OPT_BOOL(0, "unsafe-paths", &state.unsafe_paths, - N_("accept a patch that touches outside the working area")), - OPT_BOOL(0, "apply", &force_apply, - N_("also apply the patch (use with --stat/--summary/--check)")), - OPT_BOOL('3', "3way", &state.threeway, - N_( "attempt three-way merge if a patch does not apply")), - OPT_FILENAME(0, "build-fake-ancestor", &state.fake_ancestor, - N_("build a temporary index based on embedded index information")), - /* Think twice before adding "--nul" synonym to this */ - OPT_SET_INT('z', NULL, &state.line_termination, - N_("paths are separated with NUL character"), '\0'), - OPT_INTEGER('C', NULL, &state.p_context, - N_("ensure at least lines of context match")), - { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"), - N_("detect new or modified lines that have whitespace errors"), - 0, apply_option_parse_whitespace }, - { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL, - N_("ignore changes in whitespace when finding context"), - PARSE_OPT_NOARG, apply_option_parse_space_change }, - { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL, - N_("ignore changes in whitespace when finding context"), - PARSE_OPT_NOARG, apply_option_parse_space_change }, - OPT_BOOL('R', "reverse", &state.apply_in_reverse, - N_("apply the patch in reverse")), - OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero, - N_("don't expect at least one line of context")), - OPT_BOOL(0, "reject", &state.apply_with_reject, - N_("leave the rejected hunks in corresponding *.rej files")), - OPT_BOOL(0, "allow-overlap", &state.allow_overlap, - N_("allow overlapping hunks")), - OPT__VERBOSE(&state.apply_verbosity, N_("be verbose")), - OPT_BIT(0, "inaccurate-eof", &options, - N_("tolerate incorrectly detected missing new-line at the end of file"), - APPLY_OPT_INACCURATE_EOF), - OPT_BIT(0, "recount", &options, - N_("do not trust the line counts in the hunk headers"), - APPLY_OPT_RECOUNT), - { OPTION_CALLBACK, 0, "directory", &state, N_("root"), - N_("prepend to all filenames"), - 0, apply_option_parse_directory }, - OPT_END() - }; - if (init_apply_state(&state, prefix, &lock_file)) exit(128); - argc = parse_options(argc, argv, state.prefix, builtin_apply_options, - apply_usage, 0); + argc = apply_parse_options(argc, argv, + &state, &force_apply, &options, + apply_usage); if (check_apply_state(&state, force_apply)) exit(128); -- cgit v0.10.2-6-g49f6 From b4290342dd2d957cbcdb100c7d847802746b0730 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:31 +0200 Subject: apply: pass apply state to build_fake_ancestor() To libify git apply functionality, we will need to read from a different index file in get_current_sha1(). This index file will be stored in "struct apply_state", so let's pass the state to build_fake_ancestor() which will later pass it to get_current_sha1(). Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index ae220be..a9a743c 100644 --- a/apply.c +++ b/apply.c @@ -4042,7 +4042,7 @@ static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20 } /* Build an index that contains the just the files needed for a 3way merge */ -static int build_fake_ancestor(struct patch *list, const char *filename) +static int build_fake_ancestor(struct apply_state *state, struct patch *list) { struct patch *patch; struct index_state result = { NULL }; @@ -4089,12 +4089,13 @@ static int build_fake_ancestor(struct patch *list, const char *filename) } } - hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR); + hold_lock_file_for_update(&lock, state->fake_ancestor, LOCK_DIE_ON_ERROR); res = write_locked_index(&result, &lock, COMMIT_LOCK); discard_index(&result); if (res) - return error("Could not write temporary index to %s", filename); + return error("Could not write temporary index to %s", + state->fake_ancestor); return 0; } @@ -4709,7 +4710,7 @@ static int apply_patch(struct apply_state *state, } if (state->fake_ancestor && - build_fake_ancestor(list, state->fake_ancestor)) { + build_fake_ancestor(state, list)) { res = -128; goto end; } -- cgit v0.10.2-6-g49f6 From 5b0b57fd91ce684679fdac1c3ae3a50c6aa3943e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:32 +0200 Subject: apply: learn to use a different index file Sometimes we want to apply in a different index file. Before the apply functionality was libified it was possible to use the GIT_INDEX_FILE environment variable, for this purpose. But now, as the apply functionality has been libified, it should be possible to do that in a libified way. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/apply.c b/apply.c index a9a743c..289cbd0 100644 --- a/apply.c +++ b/apply.c @@ -3993,12 +3993,21 @@ static int check_patch_list(struct apply_state *state, struct patch *patch) return err; } +static int read_apply_cache(struct apply_state *state) +{ + if (state->index_file) + return read_cache_from(state->index_file); + else + return read_cache(); +} + /* This function tries to read the sha1 from the current index */ -static int get_current_sha1(const char *path, unsigned char *sha1) +static int get_current_sha1(struct apply_state *state, const char *path, + unsigned char *sha1) { int pos; - if (read_cache() < 0) + if (read_apply_cache(state) < 0) return -1; pos = cache_name_pos(path, strlen(path)); if (pos < 0) @@ -4071,7 +4080,7 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list) ; /* ok */ } else if (!patch->lines_added && !patch->lines_deleted) { /* mode-only change: update the current */ - if (get_current_sha1(patch->old_name, sha1)) + if (get_current_sha1(state, patch->old_name, sha1)) return error("mode change for %s, which is not " "in current HEAD", name); } else @@ -4675,10 +4684,16 @@ static int apply_patch(struct apply_state *state, state->apply = 0; state->update_index = state->check_index && state->apply; - if (state->update_index && state->newfd < 0) - state->newfd = hold_locked_index(state->lock_file, 1); + if (state->update_index && state->newfd < 0) { + if (state->index_file) + state->newfd = hold_lock_file_for_update(state->lock_file, + state->index_file, + LOCK_DIE_ON_ERROR); + else + state->newfd = hold_locked_index(state->lock_file, 1); + } - if (state->check_index && read_cache() < 0) { + if (state->check_index && read_apply_cache(state) < 0) { error(_("unable to read index file")); res = -128; goto end; diff --git a/apply.h b/apply.h index 9fec536..b3d6783 100644 --- a/apply.h +++ b/apply.h @@ -63,6 +63,7 @@ struct apply_state { int unsafe_paths; /* Other non boolean parameters */ + const char *index_file; enum apply_verbosity apply_verbosity; const char *fake_ancestor; const char *patch_input_file; -- cgit v0.10.2-6-g49f6 From edfac5ebffd41b1f83b31d042e9534a0254a05fe Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 4 Sep 2016 22:18:33 +0200 Subject: builtin/am: use apply API in run_apply() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces run_apply() implementation with a new one that uses the apply API that has been previously prepared in apply.c and apply.h. This shoud improve performance a lot in certain cases. As the previous implementation was creating a new `git apply` process to apply each patch, it could be slow on systems like Windows where it is costly to create new processes. Also the new `git apply` process had to read the index from disk, and when the process was done the calling process discarded its own index and read back from disk the new index that had been created by the `git apply` process. This could be very inefficient with big repositories that have big index files, especially when the system decided that it was a good idea to run the `git apply` processes on a different processor core. Also eliminating index reads enables further performance improvements by using: `git update-index --split-index` For example here is a benchmark of a multi hundred commit rebase on the Linux kernel on a Debian laptop with SSD: command: git rebase --onto 1993b17 52bef0c 29dde7c Vanilla "next" without split index: 1m54.953s Vanilla "next" with split index: 1m22.476s This series on top of "next" without split index: 1m12.034s This series on top of "next" with split index: 0m15.678s (using branch "next" from mid April 2016.) Benchmarked-by: Ævar Arnfjörð Bjarmason Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin/am.c b/builtin/am.c index b77bf11..1819f4d 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -28,6 +28,7 @@ #include "rerere.h" #include "prompt.h" #include "mailinfo.h" +#include "apply.h" /** * Returns 1 if the file is empty or does not exist, 0 otherwise. @@ -1522,39 +1523,59 @@ static int parse_mail_rebase(struct am_state *state, const char *mail) */ static int run_apply(const struct am_state *state, const char *index_file) { - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - - if (index_file) - argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file); + struct argv_array apply_paths = ARGV_ARRAY_INIT; + struct argv_array apply_opts = ARGV_ARRAY_INIT; + struct apply_state apply_state; + int res, opts_left; + static struct lock_file lock_file; + int force_apply = 0; + int options = 0; + + if (init_apply_state(&apply_state, NULL, &lock_file)) + die("BUG: init_apply_state() failed"); + + argv_array_push(&apply_opts, "apply"); + argv_array_pushv(&apply_opts, state->git_apply_opts.argv); + + opts_left = apply_parse_options(apply_opts.argc, apply_opts.argv, + &apply_state, &force_apply, &options, + NULL); + + if (opts_left != 0) + die("unknown option passed through to git apply"); + + if (index_file) { + apply_state.index_file = index_file; + apply_state.cached = 1; + } else + apply_state.check_index = 1; /* * If we are allowed to fall back on 3-way merge, don't give false * errors during the initial attempt. */ - if (state->threeway && !index_file) { - cp.no_stdout = 1; - cp.no_stderr = 1; - } + if (state->threeway && !index_file) + apply_state.apply_verbosity = verbosity_silent; - argv_array_push(&cp.args, "apply"); + if (check_apply_state(&apply_state, force_apply)) + die("BUG: check_apply_state() failed"); - argv_array_pushv(&cp.args, state->git_apply_opts.argv); + argv_array_push(&apply_paths, am_path(state, "patch")); - if (index_file) - argv_array_push(&cp.args, "--cached"); - else - argv_array_push(&cp.args, "--index"); + res = apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options); - argv_array_push(&cp.args, am_path(state, "patch")); + argv_array_clear(&apply_paths); + argv_array_clear(&apply_opts); + clear_apply_state(&apply_state); - if (run_command(&cp)) - return -1; + if (res) + return res; - /* Reload index as git-apply will have modified it. */ - discard_cache(); - read_cache_from(index_file ? index_file : get_index_file()); + if (index_file) { + /* Reload index as apply_all_patches() will have modified it. */ + discard_cache(); + read_cache_from(index_file); + } return 0; } -- cgit v0.10.2-6-g49f6