From e2016508e7690cddc789fb28879703082363549d Mon Sep 17 00:00:00 2001 From: Glen Choo Date: Tue, 28 Mar 2023 17:51:53 +0000 Subject: [PATCH] config: report cached filenames in die_bad_number() If, when parsing numbers from config, die_bad_number() is called, it reports the filename and config source type if we were parsing a config file, but not if we were iterating a config_set (it defaults to a less specific error message). Most call sites don't parse config files because config is typically read once and cached, so we only report filename and config source type in "git config --type" (since "git config" always parses config files). This could have been fixed when we taught the current_config_* functions to respect config_set values (0d44a2dacc (config: return configset value for current_config_ functions, 2016-05-26), but it was hard to spot then and we might have just missed it (I didn't find mention of die_bad_number() in the original ML discussion [1].) Fix this by refactoring the current_config_* functions into variants that don't BUG() when we aren't reading config, and using the resulting functions in die_bad_number(). "git config --get[-regexp] --type=int" cannot use the non-refactored version because it parses the int value _after_ parsing the config file, which would run into the BUG(). Since the refactored functions aren't public, they use "struct config_reader". 1. https://lore.kernel.org/git/20160518223712.GA18317@sigill.intra.peff.net/ Signed-off-by: Glen Choo Signed-off-by: Junio C Hamano --- config.c | 71 ++++++++++++++++++++++++++++-------------- config.h | 1 + t/helper/test-config.c | 17 ++++++++++ t/t1308-config-set.sh | 9 ++++++ 4 files changed, 75 insertions(+), 23 deletions(-) diff --git a/config.c b/config.c index 3756322ec9..46c5a9a7d5 100644 --- a/config.c +++ b/config.c @@ -1311,39 +1311,48 @@ int git_parse_ssize_t(const char *value, ssize_t *ret) return 1; } +static int reader_config_name(struct config_reader *reader, const char **out); +static int reader_origin_type(struct config_reader *reader, + enum config_origin_type *type); NORETURN -static void die_bad_number(struct config_source *cf, const char *name, +static void die_bad_number(struct config_reader *reader, const char *name, const char *value) { const char *error_type = (errno == ERANGE) ? N_("out of range") : N_("invalid unit"); const char *bad_numeric = N_("bad numeric config value '%s' for '%s': %s"); + const char *config_name = NULL; + enum config_origin_type config_origin = CONFIG_ORIGIN_UNKNOWN; if (!value) value = ""; - if (!(cf && cf->name)) + /* Ignoring the return value is okay since we handle missing values. */ + reader_config_name(reader, &config_name); + reader_origin_type(reader, &config_origin); + + if (!config_name) die(_(bad_numeric), value, name, _(error_type)); - switch (cf->origin_type) { + switch (config_origin) { case CONFIG_ORIGIN_BLOB: die(_("bad numeric config value '%s' for '%s' in blob %s: %s"), - value, name, cf->name, _(error_type)); + value, name, config_name, _(error_type)); case CONFIG_ORIGIN_FILE: die(_("bad numeric config value '%s' for '%s' in file %s: %s"), - value, name, cf->name, _(error_type)); + value, name, config_name, _(error_type)); case CONFIG_ORIGIN_STDIN: die(_("bad numeric config value '%s' for '%s' in standard input: %s"), value, name, _(error_type)); case CONFIG_ORIGIN_SUBMODULE_BLOB: die(_("bad numeric config value '%s' for '%s' in submodule-blob %s: %s"), - value, name, cf->name, _(error_type)); + value, name, config_name, _(error_type)); case CONFIG_ORIGIN_CMDLINE: die(_("bad numeric config value '%s' for '%s' in command line %s: %s"), - value, name, cf->name, _(error_type)); + value, name, config_name, _(error_type)); default: die(_("bad numeric config value '%s' for '%s' in %s: %s"), - value, name, cf->name, _(error_type)); + value, name, config_name, _(error_type)); } } @@ -1351,7 +1360,7 @@ int git_config_int(const char *name, const char *value) { int ret; if (!git_parse_int(value, &ret)) - die_bad_number(the_reader.source, name, value); + die_bad_number(&the_reader, name, value); return ret; } @@ -1359,7 +1368,7 @@ int64_t git_config_int64(const char *name, const char *value) { int64_t ret; if (!git_parse_int64(value, &ret)) - die_bad_number(the_reader.source, name, value); + die_bad_number(&the_reader, name, value); return ret; } @@ -1367,7 +1376,7 @@ unsigned long git_config_ulong(const char *name, const char *value) { unsigned long ret; if (!git_parse_ulong(value, &ret)) - die_bad_number(the_reader.source, name, value); + die_bad_number(&the_reader, name, value); return ret; } @@ -1375,7 +1384,7 @@ ssize_t git_config_ssize_t(const char *name, const char *value) { ssize_t ret; if (!git_parse_ssize_t(value, &ret)) - die_bad_number(the_reader.source, name, value); + die_bad_number(&the_reader, name, value); return ret; } @@ -3839,14 +3848,23 @@ int parse_config_key(const char *var, return 0; } +static int reader_origin_type(struct config_reader *reader, + enum config_origin_type *type) +{ + if (the_reader.config_kvi) + *type = reader->config_kvi->origin_type; + else if(the_reader.source) + *type = reader->source->origin_type; + else + return 1; + return 0; +} + const char *current_config_origin_type(void) { - int type; - if (the_reader.config_kvi) - type = the_reader.config_kvi->origin_type; - else if(the_reader.source) - type = the_reader.source->origin_type; - else + enum config_origin_type type = CONFIG_ORIGIN_UNKNOWN; + + if (reader_origin_type(&the_reader, &type)) BUG("current_config_origin_type called outside config callback"); switch (type) { @@ -3885,14 +3903,21 @@ const char *config_scope_name(enum config_scope scope) } } +static int reader_config_name(struct config_reader *reader, const char **out) +{ + if (the_reader.config_kvi) + *out = reader->config_kvi->filename; + else if (the_reader.source) + *out = reader->source->name; + else + return 1; + return 0; +} + const char *current_config_name(void) { const char *name; - if (the_reader.config_kvi) - name = the_reader.config_kvi->filename; - else if (the_reader.source) - name = the_reader.source->name; - else + if (reader_config_name(&the_reader, &name)) BUG("current_config_name called outside config callback"); return name ? name : ""; } diff --git a/config.h b/config.h index 7606246531..66c8b996e1 100644 --- a/config.h +++ b/config.h @@ -56,6 +56,7 @@ struct git_config_source { }; enum config_origin_type { + CONFIG_ORIGIN_UNKNOWN = 0, CONFIG_ORIGIN_BLOB, CONFIG_ORIGIN_FILE, CONFIG_ORIGIN_STDIN, diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 4ba9eb6560..26e79168f6 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -30,6 +30,9 @@ * iterate -> iterate over all values using git_config(), and print some * data for each * + * git_config_int -> iterate over all values using git_config() and print the + * integer value for the entered key or die + * * Examples: * * To print the value with highest priority for key "foo.bAr Baz.rock": @@ -54,6 +57,17 @@ static int iterate_cb(const char *var, const char *value, void *data UNUSED) return 0; } +static int parse_int_cb(const char *var, const char *value, void *data) +{ + const char *key_to_match = data; + + if (!strcmp(key_to_match, var)) { + int parsed = git_config_int(value, value); + printf("%d\n", parsed); + } + return 0; +} + static int early_config_cb(const char *var, const char *value, void *vdata) { const char *key = vdata; @@ -176,6 +190,9 @@ int cmd__config(int argc, const char **argv) } else if (!strcmp(argv[1], "iterate")) { git_config(iterate_cb, NULL); goto exit0; + } else if (argc == 3 && !strcmp(argv[1], "git_config_int")) { + git_config(parse_int_cb, (void *) argv[2]); + goto exit0; } die("%s: Please check the syntax and the function name", argv[0]); diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index b38e158d3b..9733bed30a 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -120,6 +120,10 @@ test_expect_success 'find integer value for a key' ' check_config get_int lamb.chop 65 ' +test_expect_success 'parse integer value during iteration' ' + check_config git_config_int lamb.chop 65 +' + test_expect_success 'find string value for a key' ' check_config get_string case.baz hask && check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\"" @@ -134,6 +138,11 @@ test_expect_success 'find integer if value is non parse-able' ' check_config expect_code 128 get_int lamb.head ' +test_expect_success 'non parse-able integer value during iteration' ' + check_config expect_code 128 git_config_int lamb.head 2>result && + grep "fatal: bad numeric config value .* in file \.git/config" result +' + test_expect_success 'find bool value for the entered key' ' check_config get_bool goat.head 1 && check_config get_bool goat.skin 0 &&