From 9a93c6686f56086fe5280a85513041bbfebf41d0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 29 Dec 2015 01:35:46 -0500 Subject: avoid shifting signed integers 31 bits We sometimes use 32-bit unsigned integers as bit-fields. It's fine to access the MSB, because it's unsigned. However, doing so as "1 << 31" is wrong, because the constant "1" is a signed int, and we shift into the sign bit, causing undefined behavior. We can fix this by using "1U" as the constant. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.h') diff --git a/diff.h b/diff.h index f7208ad103..893f446517 100644 --- a/diff.h +++ b/diff.h @@ -91,7 +91,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data) #define DIFF_OPT_DIRSTAT_BY_LINE (1 << 28) #define DIFF_OPT_FUNCCONTEXT (1 << 29) #define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30) -#define DIFF_OPT_DEFAULT_FOLLOW_RENAMES (1 << 31) +#define DIFF_OPT_DEFAULT_FOLLOW_RENAMES (1U << 31) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_TOUCHED(opts, flag) ((opts)->touched_flags & DIFF_OPT_##flag) -- cgit v1.3 From e5f7a5d16f2c890e7dda96e5681ee8f6687b45e4 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Wed, 20 Jan 2016 18:06:02 +0700 Subject: diff-no-index: do not take a redundant prefix argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefix is already set up in "revs". The same prefix should be used for all options parsing. So kill the last argument. This patch does not actually change anything because the only caller does use the same prefix for init_revisions() and diff_no_index(). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/diff.c | 2 +- diff-no-index.c | 4 ++-- diff.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'diff.h') diff --git a/builtin/diff.c b/builtin/diff.c index 4326fa56bf..7b9917b03e 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -341,7 +341,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } if (no_index) /* If this is a no-index diff, just run it and exit there. */ - diff_no_index(&rev, argc, argv, prefix); + diff_no_index(&rev, argc, argv); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; diff --git a/diff-no-index.c b/diff-no-index.c index 0320605a84..8edc6f3690 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -239,12 +239,12 @@ static void fixup_paths(const char **path, struct strbuf *replacement) } void diff_no_index(struct rev_info *revs, - int argc, const char **argv, - const char *prefix) + int argc, const char **argv) { int i, prefixlen; const char *paths[2]; struct strbuf replacement = STRBUF_INIT; + const char *prefix = revs->prefix; diff_setup(&revs->diffopt); for (i = 1; i < argc - 2; ) { diff --git a/diff.h b/diff.h index f7208ad103..f61ee5494b 100644 --- a/diff.h +++ b/diff.h @@ -345,7 +345,7 @@ extern int diff_flush_patch_id(struct diff_options *, unsigned char *); extern int diff_result_code(struct diff_options *, int); -extern void diff_no_index(struct rev_info *, int, const char **, const char *); +extern void diff_no_index(struct rev_info *, int, const char **); extern int index_differs_from(const char *def, int diff_flags); -- cgit v1.3 From a97262c62f1a31fcc7edf7629d313058bc7d66b5 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Thu, 21 Jan 2016 18:48:44 +0700 Subject: diff: make -O and --output work in subdirectory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/am.c | 2 +- diff-no-index.c | 3 ++- diff.c | 14 ++++++++++---- diff.h | 2 +- revision.c | 2 +- t/t4056-diff-order.sh | 6 ++++++ 6 files changed, 21 insertions(+), 8 deletions(-) (limited to 'diff.h') diff --git a/builtin/am.c b/builtin/am.c index 4e396c8321..919bd91b0f 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1653,7 +1653,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa init_revisions(&rev_info, NULL); rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; - diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1); + diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); add_pending_sha1(&rev_info, "HEAD", our_tree, 0); diff_setup_done(&rev_info.diffopt); run_diff_index(&rev_info, 1); diff --git a/diff-no-index.c b/diff-no-index.c index 8edc6f3690..95057b7a7f 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -254,7 +254,8 @@ void diff_no_index(struct rev_info *revs, else if (!strcmp(argv[i], "--")) i++; else { - j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); + j = diff_opt_parse(&revs->diffopt, argv + i, argc - i, + revs->prefix); if (j <= 0) die("invalid diff option/value: %s", argv[i]); i += j; diff --git a/diff.c b/diff.c index 46260ed7a1..93e9533389 100644 --- a/diff.c +++ b/diff.c @@ -3695,12 +3695,16 @@ static int parse_ws_error_highlight(struct diff_options *opt, const char *arg) return 1; } -int diff_opt_parse(struct diff_options *options, const char **av, int ac) +int diff_opt_parse(struct diff_options *options, + const char **av, int ac, const char *prefix) { const char *arg = av[0]; const char *optarg; int argcount; + if (!prefix) + prefix = ""; + /* Output format options */ if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch") || opt_arg(arg, 'U', "unified", &options->context)) @@ -3917,7 +3921,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--pickaxe-regex")) options->pickaxe_opts |= DIFF_PICKAXE_REGEX; else if ((argcount = short_opt('O', av, &optarg))) { - options->orderfile = optarg; + const char *path = prefix_filename(prefix, strlen(prefix), optarg); + options->orderfile = xstrdup(path); return argcount; } else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) { @@ -3956,9 +3961,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--no-function-context")) DIFF_OPT_CLR(options, FUNCCONTEXT); else if ((argcount = parse_long_opt("output", av, &optarg))) { - options->file = fopen(optarg, "w"); + const char *path = prefix_filename(prefix, strlen(prefix), optarg); + options->file = fopen(path, "w"); if (!options->file) - die_errno("Could not open '%s'", optarg); + die_errno("Could not open '%s'", path); options->close_file = 1; return argcount; } else diff --git a/diff.h b/diff.h index f61ee5494b..76b5536edb 100644 --- a/diff.h +++ b/diff.h @@ -268,7 +268,7 @@ extern int parse_long_opt(const char *opt, const char **argv, extern int git_diff_basic_config(const char *var, const char *value, void *cb); extern int git_diff_ui_config(const char *var, const char *value, void *cb); extern void diff_setup(struct diff_options *); -extern int diff_opt_parse(struct diff_options *, const char **, int); +extern int diff_opt_parse(struct diff_options *, const char **, int, const char *); extern void diff_setup_done(struct diff_options *); #define DIFF_DETECT_RENAME 1 diff --git a/revision.c b/revision.c index e0107738b7..47e38b6c46 100644 --- a/revision.c +++ b/revision.c @@ -2049,7 +2049,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--ignore-missing")) { revs->ignore_missing = 1; } else { - int opts = diff_opt_parse(&revs->diffopt, argv, argc); + int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix); if (!opts) unkv[(*unkc)++] = arg; return opts; diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh index c0460bb0e5..43dd474a12 100755 --- a/t/t4056-diff-order.sh +++ b/t/t4056-diff-order.sh @@ -68,6 +68,12 @@ test_expect_success POSIXPERM,SANITY 'unreadable orderfile' ' test_must_fail git diff -Ounreadable_file --name-only HEAD^..HEAD ' +test_expect_success "orderfile using option from subdir with --output" ' + mkdir subdir && + git -C subdir diff -O../order_file_1 --output ../actual --name-only HEAD^..HEAD && + test_cmp expect_1 actual +' + for i in 1 2 do test_expect_success "orderfile using option ($i)" ' -- cgit v1.3 From 5b442c4f2723211ce0d862571e88ee206bfd51bf Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 Feb 2016 06:21:30 -0500 Subject: tree-diff: catch integer overflow in combine_diff_path allocation A combine_diff_path struct has two "flex" members allocated alongside the struct: a string to hold the pathname, and an array of parent pointers. We use an "int" to compute this, meaning we may easily overflow it if the pathname is extremely long. We can fix this by using size_t, and checking for overflow with the st_add helper. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.h | 4 ++-- tree-diff.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'diff.h') diff --git a/diff.h b/diff.h index 70b2d70d64..beafbbdec7 100644 --- a/diff.h +++ b/diff.h @@ -222,8 +222,8 @@ struct combine_diff_path { } parent[FLEX_ARRAY]; }; #define combine_diff_path_size(n, l) \ - (sizeof(struct combine_diff_path) + \ - sizeof(struct combine_diff_parent) * (n) + (l) + 1) + st_add4(sizeof(struct combine_diff_path), (l), 1, \ + st_mult(sizeof(struct combine_diff_parent), (n))) extern void show_combined_diff(struct combine_diff_path *elem, int num_parent, int dense, struct rev_info *); diff --git a/tree-diff.c b/tree-diff.c index 290a1da4ce..4dda9a14ab 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -124,8 +124,8 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last, unsigned mode, const unsigned char *sha1) { struct combine_diff_path *p; - int len = base->len + pathlen; - int alloclen = combine_diff_path_size(nparent, len); + size_t len = st_add(base->len, pathlen); + size_t alloclen = combine_diff_path_size(nparent, len); /* if last->next is !NULL - it is a pre-allocated memory, we can reuse */ p = last->next; -- cgit v1.3 From a64e6a44c63a965c5bc26242ddd3ed049b42e117 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 22 Feb 2016 13:28:54 -0500 Subject: diff: clarify textconv interface The memory allocation scheme for the textconv interface is a bit tricky, and not well documented. It was originally designed as an internal part of diff.c (matching fill_mmfile), but gradually was made public. Refactoring it is difficult, but we can at least improve the situation by documenting the intended flow and enforcing it with an in-code assertion. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 5 ++++- diff.h | 16 ++++++++++++++++ userdiff.h | 4 ++++ 3 files changed, 24 insertions(+), 1 deletion(-) (limited to 'diff.h') diff --git a/diff.c b/diff.c index f62b7f73d8..a09b0b6ae3 100644 --- a/diff.c +++ b/diff.c @@ -4996,7 +4996,7 @@ size_t fill_textconv(struct userdiff_driver *driver, { size_t size; - if (!driver || !driver->textconv) { + if (!driver) { if (!DIFF_FILE_VALID(df)) { *outbuf = ""; return 0; @@ -5007,6 +5007,9 @@ size_t fill_textconv(struct userdiff_driver *driver, return df->size; } + if (!driver->textconv) + die("BUG: fill_textconv called with non-textconv driver"); + if (driver->textconv_cache && df->sha1_valid) { *outbuf = notes_cache_get(driver->textconv_cache, df->sha1, &size); diff --git a/diff.h b/diff.h index 1ac0582228..65a5e78f64 100644 --- a/diff.h +++ b/diff.h @@ -342,10 +342,26 @@ extern void diff_no_index(struct rev_info *, int, const char **, const char *); extern int index_differs_from(const char *def, int diff_flags); +/* + * Fill the contents of the filespec "df", respecting any textconv defined by + * its userdiff driver. The "driver" parameter must come from a + * previous call to get_textconv(), and therefore should either be NULL or have + * textconv enabled. + * + * Note that the memory ownership of the resulting buffer depends on whether + * the driver field is NULL. If it is, then the memory belongs to the filespec + * struct. If it is non-NULL, then "outbuf" points to a newly allocated buffer + * that should be freed by the caller. + */ extern size_t fill_textconv(struct userdiff_driver *driver, struct diff_filespec *df, char **outbuf); +/* + * Look up the userdiff driver for the given filespec, and return it if + * and only if it has textconv enabled (otherwise return NULL). The result + * can be passed to fill_textconv(). + */ extern struct userdiff_driver *get_textconv(struct diff_filespec *one); extern int parse_rename_score(const char **cp_p); diff --git a/userdiff.h b/userdiff.h index 4a7e78ffbc..2ef0ce5452 100644 --- a/userdiff.h +++ b/userdiff.h @@ -23,6 +23,10 @@ int userdiff_config(const char *k, const char *v); struct userdiff_driver *userdiff_find_by_name(const char *name); struct userdiff_driver *userdiff_find_by_path(const char *path); +/* + * Initialize any textconv-related fields in the driver and return it, or NULL + * if it does not have textconv enabled at all. + */ struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver); #endif /* USERDIFF */ -- cgit v1.3