config: set up config_source for command-line config

When we parse a config file, we set up the global "cf" variable as a pointer to a "struct config_source" describing the file we are parsing. This is used for error messages, as well as for lookup functions like current_config_name(). The "cf" variable is NULL in two cases: 1. When we are parsing command-line config, in which case there is no source file. 2. When we are not parsing any config at all. Callers like current_config_name() must assume we are in case 1 if they see a NULL "cf". However, this means that if they are accidentally used outside of a config parsing callback, they will quietly return a bogus answer. This might seem like an unlikely accident (why would you ask for the current config file if you are not parsing config?), but it's actually an easy mistake to make due to the configset caching. git_config() serves the answers from a configset cache, and any calls to current_config_name() will claim that we are parsing command-line config, no matter what the original source. So let's distinguish these cases by having the command-line config parser set up a config_source with a NULL name (which callers already handle properly). We can use this to catch programming errors in some cases, and to give better messages to the user in others. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed May 18, 2016 at 18:41 UTC 3258258f51f45efeff5ce0e9f825f347a1404efa
1 file changed +19 -4
config.c
+19 -4
@@ -131,7 +131,9 @@ static int handle_path_include(const char *path, struct config_include_data *inc
131 if (!access_or_die(path, R_OK, 0)) {
132 if (++inc->depth > MAX_INCLUDE_DEPTH)
133 die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
134 - cf && cf->name ? cf->name : "the command line");
134 + !cf ? "<unknown>" :
135 + cf->name ? cf->name :
136 + "the command line");
137 ret = git_config_from_file(git_config_include, path, inc);
138 inc->depth--;
139 }
@@ -210,9 +212,15 @@ int git_config_from_parameters(config_fn_t fn, void *data)
212 const char **argv = NULL;
213 int nr = 0, alloc = 0;
214 int i;
215 + struct config_source source;
216
217 if (!env)
218 return 0;
219 +
220 + memset(&source, 0, sizeof(source));
221 + source.prev = cf;
222 + cf = &source;
223 +
224 /* sq_dequote will write over it */
225 envw = xstrdup(env);
226
@@ -231,6 +239,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
239 out:
240 free(argv);
241 free(envw);
242 + cf = source.prev;
243 return ret;
244 }
245
@@ -1341,7 +1350,9 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
1350 l_item->e = e;
1351 l_item->value_index = e->value_list.nr - 1;
1352
1344 - if (cf) {
1353 + if (!cf)
1354 + die("BUG: configset_add_value has no source");
1355 + if (cf->name) {
1356 kv_info->filename = strintern(cf->name);
1357 kv_info->linenr = cf->linenr;
1358 } else {
@@ -2427,10 +2438,14 @@ int parse_config_key(const char *var,
2438
2439 const char *current_config_origin_type(void)
2440 {
2430 - return cf && cf->origin_type ? cf->origin_type : "command line";
2441 + if (!cf)
2442 + die("BUG: current_config_origin_type called outside config callback");
2443 + return cf->origin_type ? cf->origin_type : "command line";
2444 }
2445
2446 const char *current_config_name(void)
2447 {
2435 - return cf && cf->name ? cf->name : "";
2448 + if (!cf)
2449 + die("BUG: current_config_name called outside config callback");
2450 + return cf->name ? cf->name : "";
2451 }