* [PATCH v2 1/8] config.c: plumb config_source through static fns
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 21:16 ` Jonathan Tan
2023-03-16 0:11 ` [PATCH v2 2/8] config.c: don't assign to "cf_global" directly Glen Choo via GitGitGadget
` (10 subsequent siblings)
11 siblings, 1 reply; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
This reduces the direct dependence on the global "struct config_source",
which will make it easier to remove in a later commit.
To minimize the changes we need to make, we rename the current variable
from "cf" to "cf_global", and the plumbed arg uses the old name "cf".
This is a little unfortunate, since we now have the confusingly named
"struct config_source cf" everywhere (which is a holdover from before
4d8dd1494e (config: make parsing stack struct independent from actual
data source, 2013-07-12), when the struct used to be called
"config_file"), but we will rename "cf" to "cs" by the end of the
series.
In some cases (public functions and config callback functions), there
isn't an obvious way to plumb "struct config_source" through function
args. As a workaround, add references to "cf_global" that we'll address
in later commits.
The remaining references to "cf_global" are direct assignments to
"cf_global", which we'll also address in a later commit.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 153 ++++++++++++++++++++++++++++++-------------------------
1 file changed, 84 insertions(+), 69 deletions(-)
diff --git a/config.c b/config.c
index 00090a32fc3..e4a76739365 100644
--- a/config.c
+++ b/config.c
@@ -54,8 +54,8 @@ struct config_source {
* These variables record the "current" config source, which
* can be accessed by parsing callbacks.
*
- * The "cf" variable will be non-NULL only when we are actually parsing a real
- * config source (file, blob, cmdline, etc).
+ * The "cf_global" variable will be non-NULL only when we are actually
+ * parsing a real config source (file, blob, cmdline, etc).
*
* The "current_config_kvi" variable will be non-NULL only when we are feeding
* cached config from a configset into a callback.
@@ -66,15 +66,16 @@ struct config_source {
* or it's a function which can be reused for non-config purposes, and should
* fall back to some sane behavior).
*/
-static struct config_source *cf;
+static struct config_source *cf_global;
static struct key_value_info *current_config_kvi;
/*
* Similar to the variables above, this gives access to the "scope" of the
* current value (repo, global, etc). For cached values, it can be found via
* the current_config_kvi as above. During parsing, the current value can be
- * found in this variable. It's not part of "cf" because it transcends a single
- * file (i.e., a file included from .git/config is still in "repo" scope).
+ * found in this variable. It's not part of "cf_global" because it transcends a
+ * single file (i.e., a file included from .git/config is still in "repo"
+ * scope).
*/
static enum config_scope current_parsing_scope;
@@ -156,7 +157,8 @@ static const char include_depth_advice[] = N_(
"from\n"
" %s\n"
"This might be due to circular includes.");
-static int handle_path_include(const char *path, struct config_include_data *inc)
+static int handle_path_include(struct config_source *cf, const char *path,
+ struct config_include_data *inc)
{
int ret = 0;
struct strbuf buf = STRBUF_INIT;
@@ -210,7 +212,8 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
strbuf_addstr(pat, "**");
}
-static int prepare_include_condition_pattern(struct strbuf *pat)
+static int prepare_include_condition_pattern(struct config_source *cf,
+ struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
char *expanded;
@@ -245,7 +248,8 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
return prefix;
}
-static int include_by_gitdir(const struct config_options *opts,
+static int include_by_gitdir(struct config_source *cf,
+ const struct config_options *opts,
const char *cond, size_t cond_len, int icase)
{
struct strbuf text = STRBUF_INIT;
@@ -261,7 +265,7 @@ static int include_by_gitdir(const struct config_options *opts,
strbuf_realpath(&text, git_dir, 1);
strbuf_add(&pattern, cond, cond_len);
- prefix = prepare_include_condition_pattern(&pattern);
+ prefix = prepare_include_condition_pattern(cf, &pattern);
again:
if (prefix < 0)
@@ -342,14 +346,14 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct config_source *store_cf = cf;
+ struct config_source *store_cf = cf_global;
struct key_value_info *store_kvi = current_config_kvi;
enum config_scope store_scope = current_parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- cf = NULL;
+ cf_global = NULL;
current_config_kvi = NULL;
current_parsing_scope = 0;
@@ -357,7 +361,7 @@ static void populate_remote_urls(struct config_include_data *inc)
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- cf = store_cf;
+ cf_global = store_cf;
current_config_kvi = store_kvi;
current_parsing_scope = store_scope;
}
@@ -406,15 +410,16 @@ static int include_by_remote_url(struct config_include_data *inc,
inc->remote_urls);
}
-static int include_condition_is_true(struct config_include_data *inc,
+static int include_condition_is_true(struct config_source *cf,
+ struct config_include_data *inc,
const char *cond, size_t cond_len)
{
const struct config_options *opts = inc->opts;
if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
- return include_by_gitdir(opts, cond, cond_len, 0);
+ return include_by_gitdir(cf, opts, cond, cond_len, 0);
else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
- return include_by_gitdir(opts, cond, cond_len, 1);
+ return include_by_gitdir(cf, opts, cond, cond_len, 1);
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
return include_by_branch(cond, cond_len);
else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -441,16 +446,16 @@ static int git_config_include(const char *var, const char *value, void *data)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(value, inc);
+ ret = handle_path_include(cf_global, value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(inc, cond, cond_len) &&
+ cond && include_condition_is_true(cf_global, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(value, inc);
+ ret = handle_path_include(cf_global, value, inc);
inc->fn = old_fn;
}
@@ -713,9 +718,9 @@ int git_config_from_parameters(config_fn_t fn, void *data)
struct config_source source;
memset(&source, 0, sizeof(source));
- source.prev = cf;
+ source.prev = cf_global;
source.origin_type = CONFIG_ORIGIN_CMDLINE;
- cf = &source;
+ cf_global = &source;
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
@@ -773,11 +778,11 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- cf = source.prev;
+ cf_global = source.prev;
return ret;
}
-static int get_next_char(void)
+static int get_next_char(struct config_source *cf)
{
int c = cf->do_fgetc(cf);
@@ -813,13 +818,13 @@ static int get_next_char(void)
return c;
}
-static char *parse_value(void)
+static char *parse_value(struct config_source *cf)
{
int quote = 0, comment = 0, space = 0;
strbuf_reset(&cf->value);
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cf);
if (c == '\n') {
if (quote) {
cf->linenr--;
@@ -843,7 +848,7 @@ static char *parse_value(void)
for (; space; space--)
strbuf_addch(&cf->value, ' ');
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cf);
switch (c) {
case '\n':
continue;
@@ -874,7 +879,8 @@ static char *parse_value(void)
}
}
-static int get_value(config_fn_t fn, void *data, struct strbuf *name)
+static int get_value(struct config_source *cf, config_fn_t fn, void *data,
+ struct strbuf *name)
{
int c;
char *value;
@@ -882,7 +888,7 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
/* Get the full name */
for (;;) {
- c = get_next_char();
+ c = get_next_char(cf);
if (cf->eof)
break;
if (!iskeychar(c))
@@ -891,13 +897,13 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
}
while (c == ' ' || c == '\t')
- c = get_next_char();
+ c = get_next_char(cf);
value = NULL;
if (c != '\n') {
if (c != '=')
return -1;
- value = parse_value();
+ value = parse_value(cf);
if (!value)
return -1;
}
@@ -913,13 +919,14 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
return ret;
}
-static int get_extended_base_var(struct strbuf *name, int c)
+static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
+ int c)
{
cf->subsection_case_sensitive = 0;
do {
if (c == '\n')
goto error_incomplete_line;
- c = get_next_char();
+ c = get_next_char(cf);
} while (isspace(c));
/* We require the format to be '[base "extension"]' */
@@ -928,13 +935,13 @@ static int get_extended_base_var(struct strbuf *name, int c)
strbuf_addch(name, '.');
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cf);
if (c == '\n')
goto error_incomplete_line;
if (c == '"')
break;
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cf);
if (c == '\n')
goto error_incomplete_line;
}
@@ -942,7 +949,7 @@ static int get_extended_base_var(struct strbuf *name, int c)
}
/* Final ']' */
- if (get_next_char() != ']')
+ if (get_next_char(cf) != ']')
return -1;
return 0;
error_incomplete_line:
@@ -950,17 +957,17 @@ error_incomplete_line:
return -1;
}
-static int get_base_var(struct strbuf *name)
+static int get_base_var(struct config_source *cf, struct strbuf *name)
{
cf->subsection_case_sensitive = 1;
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cf);
if (cf->eof)
return -1;
if (c == ']')
return 0;
if (isspace(c))
- return get_extended_base_var(name, c);
+ return get_extended_base_var(cf, name, c);
if (!iskeychar(c) && c != '.')
return -1;
strbuf_addch(name, tolower(c));
@@ -973,7 +980,8 @@ struct parse_event_data {
const struct config_options *opts;
};
-static int do_event(enum config_event_t type, struct parse_event_data *data)
+static int do_event(struct config_source *cf, enum config_event_t type,
+ struct parse_event_data *data)
{
size_t offset;
@@ -1004,8 +1012,8 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
return 0;
}
-static int git_parse_source(config_fn_t fn, void *data,
- const struct config_options *opts)
+static int git_parse_source(struct config_source *cf, config_fn_t fn,
+ void *data, const struct config_options *opts)
{
int comment = 0;
size_t baselen = 0;
@@ -1024,7 +1032,7 @@ static int git_parse_source(config_fn_t fn, void *data,
for (;;) {
int c;
- c = get_next_char();
+ c = get_next_char(cf);
if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their
@@ -1042,11 +1050,11 @@ static int git_parse_source(config_fn_t fn, void *data,
}
if (c == '\n') {
if (cf->eof) {
- if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_EOF, &event_data) < 0)
return -1;
return 0;
}
- if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
comment = 0;
continue;
@@ -1054,23 +1062,23 @@ static int git_parse_source(config_fn_t fn, void *data,
if (comment)
continue;
if (isspace(c)) {
- if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
continue;
}
if (c == '#' || c == ';') {
- if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_COMMENT, &event_data) < 0)
return -1;
comment = 1;
continue;
}
if (c == '[') {
- if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_SECTION, &event_data) < 0)
return -1;
/* Reset prior to determining a new stem */
strbuf_reset(var);
- if (get_base_var(var) < 0 || var->len < 1)
+ if (get_base_var(cf, var) < 0 || var->len < 1)
break;
strbuf_addch(var, '.');
baselen = var->len;
@@ -1079,7 +1087,7 @@ static int git_parse_source(config_fn_t fn, void *data,
if (!isalpha(c))
break;
- if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_ENTRY, &event_data) < 0)
return -1;
/*
@@ -1089,11 +1097,11 @@ static int git_parse_source(config_fn_t fn, void *data,
*/
strbuf_setlen(var, baselen);
strbuf_addch(var, tolower(c));
- if (get_value(fn, data, var) < 0)
+ if (get_value(cf, fn, data, var) < 0)
break;
}
- if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_ERROR, &event_data) < 0)
return -1;
switch (cf->origin_type) {
@@ -1266,7 +1274,8 @@ int git_parse_ssize_t(const char *value, ssize_t *ret)
}
NORETURN
-static void die_bad_number(const char *name, const char *value)
+static void die_bad_number(struct config_source *cf, const char *name,
+ const char *value)
{
const char *error_type = (errno == ERANGE) ?
N_("out of range") : N_("invalid unit");
@@ -1304,7 +1313,7 @@ int git_config_int(const char *name, const char *value)
{
int ret;
if (!git_parse_int(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1312,7 +1321,7 @@ int64_t git_config_int64(const char *name, const char *value)
{
int64_t ret;
if (!git_parse_int64(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1320,7 +1329,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1328,7 +1337,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(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1940,20 +1949,20 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
int ret;
/* push config-file parsing state stack */
- top->prev = cf;
+ top->prev = cf_global;
top->linenr = 1;
top->eof = 0;
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- cf = top;
+ cf_global = top;
- ret = git_parse_source(fn, data, opts);
+ ret = git_parse_source(top, fn, data, opts);
/* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- cf = top->prev;
+ cf_global = top->prev;
return ret;
}
@@ -2334,12 +2343,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
- if (!cf)
+ if (!cf_global)
BUG("configset_add_value has no source");
- if (cf->name) {
- kv_info->filename = strintern(cf->name);
- kv_info->linenr = cf->linenr;
- kv_info->origin_type = cf->origin_type;
+ if (cf_global->name) {
+ kv_info->filename = strintern(cf_global->name);
+ kv_info->linenr = cf_global->linenr;
+ kv_info->origin_type = cf_global->origin_type;
} else {
/* for values read from `git_config_from_parameters()` */
kv_info->filename = NULL;
@@ -2891,6 +2900,12 @@ static int store_aux_event(enum config_event_t type,
size_t begin, size_t end, void *data)
{
struct config_store_data *store = data;
+ /*
+ * FIXME Keep using "cf" so that we can avoid rewrapping a
+ * really long line below. Remove this when "cf" gets plumbed
+ * correctly.
+ */
+ struct config_source *cf = cf_global;
ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
store->parsed[store->parsed_nr].begin = begin;
@@ -3771,8 +3786,8 @@ const char *current_config_origin_type(void)
int type;
if (current_config_kvi)
type = current_config_kvi->origin_type;
- else if(cf)
- type = cf->origin_type;
+ else if(cf_global)
+ type = cf_global->origin_type;
else
BUG("current_config_origin_type called outside config callback");
@@ -3817,8 +3832,8 @@ const char *current_config_name(void)
const char *name;
if (current_config_kvi)
name = current_config_kvi->filename;
- else if (cf)
- name = cf->name;
+ else if (cf_global)
+ name = cf_global->name;
else
BUG("current_config_name called outside config callback");
return name ? name : "";
@@ -3837,7 +3852,7 @@ int current_config_line(void)
if (current_config_kvi)
return current_config_kvi->linenr;
else
- return cf->linenr;
+ return cf_global->linenr;
}
int lookup_config(const char **mapping, int nr_mapping, const char *var)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 2/8] config.c: don't assign to "cf_global" directly
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
2023-03-16 0:11 ` [PATCH v2 1/8] config.c: plumb config_source through static fns Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 21:18 ` Jonathan Tan
2023-03-16 0:11 ` [PATCH v2 3/8] config.c: create config_reader and the_reader Glen Choo via GitGitGadget
` (9 subsequent siblings)
11 siblings, 1 reply; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
To make "cf_global" easier to remove, replace all direct assignments to
it with function calls. This refactor has an additional maintainability
benefit: all of these functions were manually implementing stack
pop/push semantics on "struct config_source", so replacing them with
function calls allows us to only implement this logic once.
In this process, perform some now-obvious clean ups:
- Drop some unnecessary "cf_global" assignments in
populate_remote_urls(). Since it was introduced in 399b198489 (config:
include file if remote URL matches a glob, 2022-01-18), it has stored
and restored the value of "cf_global" to ensure that it doesn't get
accidentally mutated. However, this was never necessary since
"do_config_from()" already pushes/pops "cf_global" further down the
call chain.
- Zero out every "struct config_source" with a dedicated initializer.
This matters because the "struct config_source" is assigned to
"cf_global" and we later 'pop the stack' by assigning "cf_global =
cf_global->prev", but "cf_global->prev" could be pointing to
uninitialized garbage.
Fortunately, this has never bothered us since we never try to read
"cf_global" except while iterating through config, in which case,
"cf_global" is either set to a sensible value (when parsing a file),
or it is ignored (when iterating a configset). Later in the series,
zero-ing out memory will also let us enforce the constraint that
"cf_global" and "current_config_kvi" are never non-NULL together.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 38 +++++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 13 deletions(-)
diff --git a/config.c b/config.c
index e4a76739365..517b8f64038 100644
--- a/config.c
+++ b/config.c
@@ -49,6 +49,7 @@ struct config_source {
int (*do_ungetc)(int c, struct config_source *conf);
long (*do_ftell)(struct config_source *c);
};
+#define CONFIG_SOURCE_INIT { 0 }
/*
* These variables record the "current" config source, which
@@ -79,6 +80,23 @@ static struct key_value_info *current_config_kvi;
*/
static enum config_scope current_parsing_scope;
+static inline void config_reader_push_source(struct config_source *top)
+{
+ if (cf_global)
+ top->prev = cf_global;
+ cf_global = top;
+}
+
+static inline struct config_source *config_reader_pop_source()
+{
+ struct config_source *ret;
+ if (!cf_global)
+ BUG("tried to pop config source, but we weren't reading config");
+ ret = cf_global;
+ cf_global = cf_global->prev;
+ return ret;
+}
+
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -346,14 +364,12 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct config_source *store_cf = cf_global;
struct key_value_info *store_kvi = current_config_kvi;
enum config_scope store_scope = current_parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- cf_global = NULL;
current_config_kvi = NULL;
current_parsing_scope = 0;
@@ -361,7 +377,6 @@ static void populate_remote_urls(struct config_include_data *inc)
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- cf_global = store_cf;
current_config_kvi = store_kvi;
current_parsing_scope = store_scope;
}
@@ -715,12 +730,10 @@ int git_config_from_parameters(config_fn_t fn, void *data)
struct strvec to_free = STRVEC_INIT;
int ret = 0;
char *envw = NULL;
- struct config_source source;
+ struct config_source source = CONFIG_SOURCE_INIT;
- memset(&source, 0, sizeof(source));
- source.prev = cf_global;
source.origin_type = CONFIG_ORIGIN_CMDLINE;
- cf_global = &source;
+ config_reader_push_source(&source);
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
@@ -778,7 +791,7 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- cf_global = source.prev;
+ config_reader_pop_source();
return ret;
}
@@ -1949,20 +1962,19 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
int ret;
/* push config-file parsing state stack */
- top->prev = cf_global;
top->linenr = 1;
top->eof = 0;
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- cf_global = top;
+ config_reader_push_source(top);
ret = git_parse_source(top, fn, data, opts);
/* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- cf_global = top->prev;
+ config_reader_pop_source();
return ret;
}
@@ -1972,7 +1984,7 @@ static int do_config_from_file(config_fn_t fn,
const char *name, const char *path, FILE *f,
void *data, const struct config_options *opts)
{
- struct config_source top;
+ struct config_source top = CONFIG_SOURCE_INIT;
int ret;
top.u.file = f;
@@ -2024,7 +2036,7 @@ int git_config_from_mem(config_fn_t fn,
const char *name, const char *buf, size_t len,
void *data, const struct config_options *opts)
{
- struct config_source top;
+ struct config_source top = CONFIG_SOURCE_INIT;
top.u.buf.buf = buf;
top.u.buf.len = len;
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v2 2/8] config.c: don't assign to "cf_global" directly
2023-03-16 0:11 ` [PATCH v2 2/8] config.c: don't assign to "cf_global" directly Glen Choo via GitGitGadget
@ 2023-03-16 21:18 ` Jonathan Tan
2023-03-16 21:31 ` Junio C Hamano
2023-03-16 22:56 ` Glen Choo
0 siblings, 2 replies; 72+ messages in thread
From: Jonathan Tan @ 2023-03-16 21:18 UTC (permalink / raw)
To: Glen Choo via GitGitGadget
Cc: Jonathan Tan, git, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo
"Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
> +static inline void config_reader_push_source(struct config_source *top)
> +{
> + if (cf_global)
> + top->prev = cf_global;
Don't we want to set prev unconditionally here (i.e. set it to NULL if
cf_global was NULL)?
> + cf_global = top;
> +}
> +
> +static inline struct config_source *config_reader_pop_source()
> +{
> + struct config_source *ret;
> + if (!cf_global)
> + BUG("tried to pop config source, but we weren't reading config");
> + ret = cf_global;
> + cf_global = cf_global->prev;
> + return ret;
> +}
...since we use it unconditionally here.
The rest looks good.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v2 2/8] config.c: don't assign to "cf_global" directly
2023-03-16 21:18 ` Jonathan Tan
@ 2023-03-16 21:31 ` Junio C Hamano
2023-03-16 22:56 ` Glen Choo
1 sibling, 0 replies; 72+ messages in thread
From: Junio C Hamano @ 2023-03-16 21:31 UTC (permalink / raw)
To: Jonathan Tan
Cc: Glen Choo via GitGitGadget, git, Emily Shaffer, Jeff King,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo
Jonathan Tan <jonathantanmy@google.com> writes:
> "Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
>> +static inline void config_reader_push_source(struct config_source *top)
>> +{
>> + if (cf_global)
>> + top->prev = cf_global;
>
> Don't we want to set prev unconditionally here (i.e. set it to NULL if
> cf_global was NULL)?
Good eyes. You are absolutely right.
Thanks.
>
>> + cf_global = top;
>> +}
>> +
>> +static inline struct config_source *config_reader_pop_source()
>> +{
>> + struct config_source *ret;
>> + if (!cf_global)
>> + BUG("tried to pop config source, but we weren't reading config");
>> + ret = cf_global;
>> + cf_global = cf_global->prev;
>> + return ret;
>> +}
>
> ...since we use it unconditionally here.
>
> The rest looks good.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v2 2/8] config.c: don't assign to "cf_global" directly
2023-03-16 21:18 ` Jonathan Tan
2023-03-16 21:31 ` Junio C Hamano
@ 2023-03-16 22:56 ` Glen Choo
1 sibling, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-16 22:56 UTC (permalink / raw)
To: Jonathan Tan, Glen Choo via GitGitGadget
Cc: Jonathan Tan, git, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason
Jonathan Tan <jonathantanmy@google.com> writes:
> "Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
>> +static inline void config_reader_push_source(struct config_source *top)
>> +{
>> + if (cf_global)
>> + top->prev = cf_global;
>
> Don't we want to set prev unconditionally here (i.e. set it to NULL if
> cf_global was NULL)?
>
>> + cf_global = top;
>> +}
>> +
>> +static inline struct config_source *config_reader_pop_source()
>> +{
>> + struct config_source *ret;
>> + if (!cf_global)
>> + BUG("tried to pop config source, but we weren't reading config");
>> + ret = cf_global;
>> + cf_global = cf_global->prev;
>> + return ret;
>> +}
>
> ...since we use it unconditionally here.
Ah, thanks. It's safe as of this patch, since we always zero-initialize
"struct config_source" and nothing else sets ".prev", but this asymmetry
is a bug waiting to happen.
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v2 3/8] config.c: create config_reader and the_reader
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
2023-03-16 0:11 ` [PATCH v2 1/8] config.c: plumb config_source through static fns Glen Choo via GitGitGadget
2023-03-16 0:11 ` [PATCH v2 2/8] config.c: don't assign to "cf_global" directly Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 21:22 ` Jonathan Tan
2023-03-16 0:11 ` [PATCH v2 4/8] config.c: plumb the_reader through callbacks Glen Choo via GitGitGadget
` (8 subsequent siblings)
11 siblings, 1 reply; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
Create "struct config_reader" to hold the state of the config source
currently being read. Then, create a static instance of it,
"the_reader", and use "the_reader.source" to replace references to
"cf_global" in public functions.
This doesn't create much immediate benefit (since we're mostly replacing
static variables with a bigger static variable), but it prepares us for
a future where this state doesn't have to be global; "struct
config_reader" (or a similar struct) could be provided by the caller, or
constructed internally by a function like "do_config_from()".
A more typical approach would be to put this struct on "the_repository",
but that's a worse fit for this use case since config reading is not
scoped to a repository. E.g. we can read config before the repository is
known ("read_very_early_config()"), blatantly ignore the repo
("read_protected_config()"), or read only from a file
("git_config_from_file()"). This is especially evident in t5318 and
t9210, where test-tool and scalar parse config but don't fully
initialize "the_repository".
We could have also replaced the references to "cf_global" in callback
functions (which are the only ones left), but we'll eventually plumb
"the_reader" through the callback "*data" arg, so that would be
unnecessary churn. Until we remove "cf_global" altogether, add logic to
"config_reader_*_source()" to keep "cf_global" and "the_reader.source"
in sync.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 84 +++++++++++++++++++++++++++++++++++---------------------
1 file changed, 52 insertions(+), 32 deletions(-)
diff --git a/config.c b/config.c
index 517b8f64038..7de25515818 100644
--- a/config.c
+++ b/config.c
@@ -51,6 +51,16 @@ struct config_source {
};
#define CONFIG_SOURCE_INIT { 0 }
+struct config_reader {
+ struct config_source *source;
+};
+/*
+ * Where possible, prefer to accept "struct config_reader" as an arg than to use
+ * "the_reader". "the_reader" should only be used if that is infeasible, e.g. in
+ * a public function.
+ */
+static struct config_reader the_reader;
+
/*
* These variables record the "current" config source, which
* can be accessed by parsing callbacks.
@@ -66,6 +76,9 @@ struct config_source {
* at the variables, it's either a bug for it to be called in the first place,
* or it's a function which can be reused for non-config purposes, and should
* fall back to some sane behavior).
+ *
+ * FIXME "cf_global" has been replaced by "the_reader.source", remove
+ * "cf_global" once we plumb "the_reader" through all of the callback functions.
*/
static struct config_source *cf_global;
static struct key_value_info *current_config_kvi;
@@ -80,20 +93,25 @@ static struct key_value_info *current_config_kvi;
*/
static enum config_scope current_parsing_scope;
-static inline void config_reader_push_source(struct config_source *top)
+static inline void config_reader_push_source(struct config_reader *reader,
+ struct config_source *top)
{
- if (cf_global)
- top->prev = cf_global;
- cf_global = top;
+ if (reader->source)
+ top->prev = reader->source;
+ reader->source = top;
+ /* FIXME remove this when cf_global is removed. */
+ cf_global = reader->source;
}
-static inline struct config_source *config_reader_pop_source()
+static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
{
struct config_source *ret;
- if (!cf_global)
+ if (!reader->source)
BUG("tried to pop config source, but we weren't reading config");
- ret = cf_global;
- cf_global = cf_global->prev;
+ ret = reader->source;
+ reader->source = reader->source->prev;
+ /* FIXME remove this when cf is removed. */
+ cf_global = reader->source;
return ret;
}
@@ -733,7 +751,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
struct config_source source = CONFIG_SOURCE_INIT;
source.origin_type = CONFIG_ORIGIN_CMDLINE;
- config_reader_push_source(&source);
+ config_reader_push_source(&the_reader, &source);
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
@@ -791,7 +809,7 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- config_reader_pop_source();
+ config_reader_pop_source(&the_reader);
return ret;
}
@@ -1326,7 +1344,7 @@ int git_config_int(const char *name, const char *value)
{
int ret;
if (!git_parse_int(value, &ret))
- die_bad_number(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1334,7 +1352,7 @@ int64_t git_config_int64(const char *name, const char *value)
{
int64_t ret;
if (!git_parse_int64(value, &ret))
- die_bad_number(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1342,7 +1360,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
- die_bad_number(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1350,7 +1368,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(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1956,7 +1974,8 @@ int git_default_config(const char *var, const char *value, void *cb)
* fgetc, ungetc, ftell of top need to be initialized before calling
* this function.
*/
-static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
+static int do_config_from(struct config_reader *reader,
+ struct config_source *top, config_fn_t fn, void *data,
const struct config_options *opts)
{
int ret;
@@ -1967,22 +1986,23 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- config_reader_push_source(top);
+ config_reader_push_source(reader, top);
ret = git_parse_source(top, fn, data, opts);
/* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- config_reader_pop_source();
+ config_reader_pop_source(reader);
return ret;
}
-static int do_config_from_file(config_fn_t fn,
- const enum config_origin_type origin_type,
- const char *name, const char *path, FILE *f,
- void *data, const struct config_options *opts)
+static int do_config_from_file(struct config_reader *reader,
+ config_fn_t fn,
+ const enum config_origin_type origin_type,
+ const char *name, const char *path, FILE *f,
+ void *data, const struct config_options *opts)
{
struct config_source top = CONFIG_SOURCE_INIT;
int ret;
@@ -1997,15 +2017,15 @@ static int do_config_from_file(config_fn_t fn,
top.do_ftell = config_file_ftell;
flockfile(f);
- ret = do_config_from(&top, fn, data, opts);
+ ret = do_config_from(reader, &top, fn, data, opts);
funlockfile(f);
return ret;
}
static int git_config_from_stdin(config_fn_t fn, void *data)
{
- return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
- data, NULL);
+ return do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_STDIN, "",
+ NULL, stdin, data, NULL);
}
int git_config_from_file_with_options(config_fn_t fn, const char *filename,
@@ -2019,8 +2039,8 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
BUG("filename cannot be NULL");
f = fopen_or_warn(filename, "r");
if (f) {
- ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
- filename, f, data, opts);
+ ret = do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_FILE,
+ filename, filename, f, data, opts);
fclose(f);
}
return ret;
@@ -2049,7 +2069,7 @@ int git_config_from_mem(config_fn_t fn,
top.do_ungetc = config_buf_ungetc;
top.do_ftell = config_buf_ftell;
- return do_config_from(&top, fn, data, opts);
+ return do_config_from(&the_reader, &top, fn, data, opts);
}
int git_config_from_blob_oid(config_fn_t fn,
@@ -3798,8 +3818,8 @@ const char *current_config_origin_type(void)
int type;
if (current_config_kvi)
type = current_config_kvi->origin_type;
- else if(cf_global)
- type = cf_global->origin_type;
+ else if(the_reader.source)
+ type = the_reader.source->origin_type;
else
BUG("current_config_origin_type called outside config callback");
@@ -3844,8 +3864,8 @@ const char *current_config_name(void)
const char *name;
if (current_config_kvi)
name = current_config_kvi->filename;
- else if (cf_global)
- name = cf_global->name;
+ else if (the_reader.source)
+ name = the_reader.source->name;
else
BUG("current_config_name called outside config callback");
return name ? name : "";
@@ -3864,7 +3884,7 @@ int current_config_line(void)
if (current_config_kvi)
return current_config_kvi->linenr;
else
- return cf_global->linenr;
+ return the_reader.source->linenr;
}
int lookup_config(const char **mapping, int nr_mapping, const char *var)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v2 3/8] config.c: create config_reader and the_reader
2023-03-16 0:11 ` [PATCH v2 3/8] config.c: create config_reader and the_reader Glen Choo via GitGitGadget
@ 2023-03-16 21:22 ` Jonathan Tan
0 siblings, 0 replies; 72+ messages in thread
From: Jonathan Tan @ 2023-03-16 21:22 UTC (permalink / raw)
To: Glen Choo via GitGitGadget
Cc: Jonathan Tan, git, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo
"Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
> -static inline struct config_source *config_reader_pop_source()
> +static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
> {
> struct config_source *ret;
> - if (!cf_global)
> + if (!reader->source)
> BUG("tried to pop config source, but we weren't reading config");
> - ret = cf_global;
> - cf_global = cf_global->prev;
> + ret = reader->source;
> + reader->source = reader->source->prev;
> + /* FIXME remove this when cf is removed. */
> + cf_global = reader->source;
> return ret;
> }
In the FIXME, it's cf_global not cf.
Everything else looks good.
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v2 4/8] config.c: plumb the_reader through callbacks
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (2 preceding siblings ...)
2023-03-16 0:11 ` [PATCH v2 3/8] config.c: create config_reader and the_reader Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 0:11 ` [PATCH v2 5/8] config.c: remove current_config_kvi Glen Choo via GitGitGadget
` (7 subsequent siblings)
11 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
The remaining references to "cf_global" are in config callback
functions. Remove them by plumbing "struct config_reader" via the
"*data" arg.
In both of the callbacks here, we are only reading from
"reader->source". So in the long run, if we had a way to expose readonly
information from "reader->source" (probably in the form of "struct
key_value_info"), we could undo this patch (i.e. remove "struct
config_reader" fom "*data").
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 74 ++++++++++++++++++++++++++++++++------------------------
1 file changed, 43 insertions(+), 31 deletions(-)
diff --git a/config.c b/config.c
index 7de25515818..b75cde42ca6 100644
--- a/config.c
+++ b/config.c
@@ -62,6 +62,9 @@ struct config_reader {
static struct config_reader the_reader;
/*
+ * FIXME The comments are temporarily out of date since "cf_global" has been
+ * moved to the_reader, but not current_*.
+ *
* These variables record the "current" config source, which
* can be accessed by parsing callbacks.
*
@@ -76,11 +79,7 @@ static struct config_reader the_reader;
* at the variables, it's either a bug for it to be called in the first place,
* or it's a function which can be reused for non-config purposes, and should
* fall back to some sane behavior).
- *
- * FIXME "cf_global" has been replaced by "the_reader.source", remove
- * "cf_global" once we plumb "the_reader" through all of the callback functions.
*/
-static struct config_source *cf_global;
static struct key_value_info *current_config_kvi;
/*
@@ -99,8 +98,6 @@ static inline void config_reader_push_source(struct config_reader *reader,
if (reader->source)
top->prev = reader->source;
reader->source = top;
- /* FIXME remove this when cf_global is removed. */
- cf_global = reader->source;
}
static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
@@ -110,8 +107,6 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
BUG("tried to pop config source, but we weren't reading config");
ret = reader->source;
reader->source = reader->source->prev;
- /* FIXME remove this when cf is removed. */
- cf_global = reader->source;
return ret;
}
@@ -176,6 +171,7 @@ struct config_include_data {
void *data;
const struct config_options *opts;
struct git_config_source *config_source;
+ struct config_reader *config_reader;
/*
* All remote URLs discovered when reading all config files.
@@ -466,6 +462,7 @@ static int include_condition_is_true(struct config_source *cf,
static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
+ struct config_source *cf = inc->config_reader->source;
const char *cond, *key;
size_t cond_len;
int ret;
@@ -479,16 +476,16 @@ static int git_config_include(const char *var, const char *value, void *data)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(cf_global, value, inc);
+ ret = handle_path_include(cf, value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(cf_global, inc, cond, cond_len) &&
+ cond && include_condition_is_true(cf, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(cf_global, value, inc);
+ ret = handle_path_include(cf, value, inc);
inc->fn = old_fn;
}
@@ -2229,6 +2226,7 @@ int config_with_options(config_fn_t fn, void *data,
inc.data = data;
inc.opts = opts;
inc.config_source = config_source;
+ inc.config_reader = &the_reader;
fn = git_config_include;
data = &inc;
}
@@ -2349,7 +2347,9 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
return found_entry;
}
-static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+static int configset_add_value(struct config_reader *reader,
+ struct config_set *cs, const char *key,
+ const char *value)
{
struct config_set_element *e;
struct string_list_item *si;
@@ -2375,12 +2375,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
- if (!cf_global)
+ if (!reader->source)
BUG("configset_add_value has no source");
- if (cf_global->name) {
- kv_info->filename = strintern(cf_global->name);
- kv_info->linenr = cf_global->linenr;
- kv_info->origin_type = cf_global->origin_type;
+ if (reader->source->name) {
+ kv_info->filename = strintern(reader->source->name);
+ kv_info->linenr = reader->source->linenr;
+ kv_info->origin_type = reader->source->origin_type;
} else {
/* for values read from `git_config_from_parameters()` */
kv_info->filename = NULL;
@@ -2435,16 +2435,25 @@ void git_configset_clear(struct config_set *cs)
cs->list.items = NULL;
}
+struct configset_add_data {
+ struct config_set *config_set;
+ struct config_reader *config_reader;
+};
+#define CONFIGSET_ADD_INIT { 0 }
+
static int config_set_callback(const char *key, const char *value, void *cb)
{
- struct config_set *cs = cb;
- configset_add_value(cs, key, value);
+ struct configset_add_data *data = cb;
+ configset_add_value(data->config_reader, data->config_set, key, value);
return 0;
}
int git_configset_add_file(struct config_set *cs, const char *filename)
{
- return git_config_from_file(config_set_callback, filename, cs);
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
+ data.config_reader = &the_reader;
+ data.config_set = cs;
+ return git_config_from_file(config_set_callback, filename, &data);
}
int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
@@ -2559,6 +2568,7 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
static void repo_read_config(struct repository *repo)
{
struct config_options opts = { 0 };
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
opts.respect_includes = 1;
opts.commondir = repo->commondir;
@@ -2570,8 +2580,10 @@ static void repo_read_config(struct repository *repo)
git_configset_clear(repo->config);
git_configset_init(repo->config);
+ data.config_set = repo->config;
+ data.config_reader = &the_reader;
- if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+ if (config_with_options(config_set_callback, &data, NULL, &opts) < 0)
/*
* config_with_options() normally returns only
* zero, as most errors are fatal, and
@@ -2697,9 +2709,12 @@ static void read_protected_config(void)
.ignore_worktree = 1,
.system_gently = 1,
};
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
+
git_configset_init(&protected_config);
- config_with_options(config_set_callback, &protected_config,
- NULL, &opts);
+ data.config_set = &protected_config;
+ data.config_reader = &the_reader;
+ config_with_options(config_set_callback, &data, NULL, &opts);
}
void git_protected_config(config_fn_t fn, void *data)
@@ -2884,6 +2899,7 @@ void git_die_config(const char *key, const char *err, ...)
*/
struct config_store_data {
+ struct config_reader *config_reader;
size_t baselen;
char *key;
int do_not_match;
@@ -2898,6 +2914,7 @@ struct config_store_data {
unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
unsigned int key_seen:1, section_seen:1, is_keys_section:1;
};
+#define CONFIG_STORE_INIT { 0 }
static void config_store_data_clear(struct config_store_data *store)
{
@@ -2932,12 +2949,7 @@ static int store_aux_event(enum config_event_t type,
size_t begin, size_t end, void *data)
{
struct config_store_data *store = data;
- /*
- * FIXME Keep using "cf" so that we can avoid rewrapping a
- * really long line below. Remove this when "cf" gets plumbed
- * correctly.
- */
- struct config_source *cf = cf_global;
+ struct config_source *cf = store->config_reader->source;
ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
store->parsed[store->parsed_nr].begin = begin;
@@ -3255,9 +3267,9 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
char *filename_buf = NULL;
char *contents = NULL;
size_t contents_sz;
- struct config_store_data store;
+ struct config_store_data store = CONFIG_STORE_INIT;
- memset(&store, 0, sizeof(store));
+ store.config_reader = &the_reader;
/* parse-key returns negative; flip the sign to feed exit(3) */
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 5/8] config.c: remove current_config_kvi
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (3 preceding siblings ...)
2023-03-16 0:11 ` [PATCH v2 4/8] config.c: plumb the_reader through callbacks Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 0:11 ` [PATCH v2 6/8] config.c: remove current_parsing_scope Glen Choo via GitGitGadget
` (6 subsequent siblings)
11 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
Add ".config_kvi" to "struct config_reader" and replace
"current_config_kvi" with "the_reader.config_kvi", plumbing "struct
config_reader" where necesssary.
Also, introduce a setter function for ".config_kvi", which allows us to
enforce the contraint that only one of ".source" and ".config_kvi" can
be set at a time (as documented in the comments). Because of this
constraint, we know that "populate_remote_urls()" was never touching
"current_config_kvi" when iterating through config files, so it doesn't
need to store and restore that value.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 82 +++++++++++++++++++++++++++++---------------------------
1 file changed, 43 insertions(+), 39 deletions(-)
diff --git a/config.c b/config.c
index b75cde42ca6..ba9b488c40d 100644
--- a/config.c
+++ b/config.c
@@ -52,7 +52,24 @@ struct config_source {
#define CONFIG_SOURCE_INIT { 0 }
struct config_reader {
+ /*
+ * These members record the "current" config source, which can be
+ * accessed by parsing callbacks.
+ *
+ * The "source" variable will be non-NULL only when we are actually
+ * parsing a real config source (file, blob, cmdline, etc).
+ *
+ * The "config_kvi" variable will be non-NULL only when we are feeding
+ * cached config from a configset into a callback.
+ *
+ * They cannot be non-NULL at the same time. If they are both NULL, then
+ * we aren't parsing anything (and depending on the function looking at
+ * the variables, it's either a bug for it to be called in the first
+ * place, or it's a function which can be reused for non-config
+ * purposes, and should fall back to some sane behavior).
+ */
struct config_source *source;
+ struct key_value_info *config_kvi;
};
/*
* Where possible, prefer to accept "struct config_reader" as an arg than to use
@@ -61,27 +78,6 @@ struct config_reader {
*/
static struct config_reader the_reader;
-/*
- * FIXME The comments are temporarily out of date since "cf_global" has been
- * moved to the_reader, but not current_*.
- *
- * These variables record the "current" config source, which
- * can be accessed by parsing callbacks.
- *
- * The "cf_global" variable will be non-NULL only when we are actually
- * parsing a real config source (file, blob, cmdline, etc).
- *
- * The "current_config_kvi" variable will be non-NULL only when we are feeding
- * cached config from a configset into a callback.
- *
- * They should generally never be non-NULL at the same time. If they are both
- * NULL, then we aren't parsing anything (and depending on the function looking
- * at the variables, it's either a bug for it to be called in the first place,
- * or it's a function which can be reused for non-config purposes, and should
- * fall back to some sane behavior).
- */
-static struct key_value_info *current_config_kvi;
-
/*
* Similar to the variables above, this gives access to the "scope" of the
* current value (repo, global, etc). For cached values, it can be found via
@@ -95,6 +91,8 @@ static enum config_scope current_parsing_scope;
static inline void config_reader_push_source(struct config_reader *reader,
struct config_source *top)
{
+ if (reader->config_kvi)
+ BUG("source should not be set while iterating a config set");
if (reader->source)
top->prev = reader->source;
reader->source = top;
@@ -110,6 +108,14 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
return ret;
}
+static inline void config_reader_set_kvi(struct config_reader *reader,
+ struct key_value_info *kvi)
+{
+ if (kvi && reader->source)
+ BUG("kvi should not be set while parsing a config source");
+ reader->config_kvi = kvi;
+}
+
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -378,20 +384,17 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct key_value_info *store_kvi = current_config_kvi;
enum config_scope store_scope = current_parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- current_config_kvi = NULL;
current_parsing_scope = 0;
inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- current_config_kvi = store_kvi;
current_parsing_scope = store_scope;
}
@@ -2258,7 +2261,8 @@ int config_with_options(config_fn_t fn, void *data,
return ret;
}
-static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+static void configset_iter(struct config_reader *reader, struct config_set *cs,
+ config_fn_t fn, void *data)
{
int i, value_index;
struct string_list *values;
@@ -2270,14 +2274,14 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
value_index = list->items[i].value_index;
values = &entry->value_list;
- current_config_kvi = values->items[value_index].util;
+ config_reader_set_kvi(reader, values->items[value_index].util);
if (fn(entry->key, values->items[value_index].string, data) < 0)
git_die_config_linenr(entry->key,
- current_config_kvi->filename,
- current_config_kvi->linenr);
+ reader->config_kvi->filename,
+ reader->config_kvi->linenr);
- current_config_kvi = NULL;
+ config_reader_set_kvi(reader, NULL);
}
}
@@ -2615,7 +2619,7 @@ static void repo_config_clear(struct repository *repo)
void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
git_config_check_init(repo);
- configset_iter(repo->config, fn, data);
+ configset_iter(&the_reader, repo->config, fn, data);
}
int repo_config_get_value(struct repository *repo,
@@ -2721,7 +2725,7 @@ void git_protected_config(config_fn_t fn, void *data)
{
if (!protected_config.hash_initialized)
read_protected_config();
- configset_iter(&protected_config, fn, data);
+ configset_iter(&the_reader, &protected_config, fn, data);
}
/* Functions used historically to read configuration from 'the_repository' */
@@ -3828,8 +3832,8 @@ int parse_config_key(const char *var,
const char *current_config_origin_type(void)
{
int type;
- if (current_config_kvi)
- type = current_config_kvi->origin_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
@@ -3874,8 +3878,8 @@ const char *config_scope_name(enum config_scope scope)
const char *current_config_name(void)
{
const char *name;
- if (current_config_kvi)
- name = current_config_kvi->filename;
+ if (the_reader.config_kvi)
+ name = the_reader.config_kvi->filename;
else if (the_reader.source)
name = the_reader.source->name;
else
@@ -3885,16 +3889,16 @@ const char *current_config_name(void)
enum config_scope current_config_scope(void)
{
- if (current_config_kvi)
- return current_config_kvi->scope;
+ if (the_reader.config_kvi)
+ return the_reader.config_kvi->scope;
else
return current_parsing_scope;
}
int current_config_line(void)
{
- if (current_config_kvi)
- return current_config_kvi->linenr;
+ if (the_reader.config_kvi)
+ return the_reader.config_kvi->linenr;
else
return the_reader.source->linenr;
}
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 6/8] config.c: remove current_parsing_scope
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (4 preceding siblings ...)
2023-03-16 0:11 ` [PATCH v2 5/8] config.c: remove current_config_kvi Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 0:11 ` [PATCH v2 7/8] config: report cached filenames in die_bad_number() Glen Choo via GitGitGadget
` (5 subsequent siblings)
11 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
Add ".parsing_scope" to "struct config_reader" and replace
"current_parsing_scope" with "the_reader.parsing_scope. Adjust the
comment slightly to make it clearer that the scope applies to the config
source (not the current value), and should only be set when parsing a
config source.
As such, ".parsing_scope" (only set when parsing config sources) and
".config_kvi" (only set when iterating a config set) should not be
set together, so enforce this with a setter function.
Unlike previous commits, "populate_remote_urls()" still needs to store
and restore the 'scope' value because it could have touched
"current_parsing_scope" ("config_with_options()" can set the scope).
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 63 +++++++++++++++++++++++++++++++++-----------------------
1 file changed, 37 insertions(+), 26 deletions(-)
diff --git a/config.c b/config.c
index ba9b488c40d..460326ae21e 100644
--- a/config.c
+++ b/config.c
@@ -70,6 +70,16 @@ struct config_reader {
*/
struct config_source *source;
struct key_value_info *config_kvi;
+ /*
+ * The "scope" of the current config source being parsed (repo, global,
+ * etc). Like "source", this is only set when parsing a config source.
+ * It's not part of "source" because it transcends a single file (i.e.,
+ * a file included from .git/config is still in "repo" scope).
+ *
+ * When iterating through a configset, the equivalent value is
+ * "config_kvi.scope" (see above).
+ */
+ enum config_scope parsing_scope;
};
/*
* Where possible, prefer to accept "struct config_reader" as an arg than to use
@@ -78,16 +88,6 @@ struct config_reader {
*/
static struct config_reader the_reader;
-/*
- * Similar to the variables above, this gives access to the "scope" of the
- * current value (repo, global, etc). For cached values, it can be found via
- * the current_config_kvi as above. During parsing, the current value can be
- * found in this variable. It's not part of "cf_global" because it transcends a
- * single file (i.e., a file included from .git/config is still in "repo"
- * scope).
- */
-static enum config_scope current_parsing_scope;
-
static inline void config_reader_push_source(struct config_reader *reader,
struct config_source *top)
{
@@ -111,11 +111,19 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
static inline void config_reader_set_kvi(struct config_reader *reader,
struct key_value_info *kvi)
{
- if (kvi && reader->source)
+ if (kvi && (reader->source || reader->parsing_scope))
BUG("kvi should not be set while parsing a config source");
reader->config_kvi = kvi;
}
+static inline void config_reader_set_scope(struct config_reader *reader,
+ enum config_scope scope)
+{
+ if (scope && reader->config_kvi)
+ BUG("scope should only be set when iterating through a config source");
+ reader->parsing_scope = scope;
+}
+
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -384,18 +392,18 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- enum config_scope store_scope = current_parsing_scope;
+ enum config_scope store_scope = inc->config_reader->parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- current_parsing_scope = 0;
+ config_reader_set_scope(inc->config_reader, 0);
inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- current_parsing_scope = store_scope;
+ config_reader_set_scope(inc->config_reader, store_scope);
}
static int forbid_remote_url(const char *var, const char *value UNUSED,
@@ -2160,7 +2168,8 @@ int git_config_system(void)
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
}
-static int do_git_config_sequence(const struct config_options *opts,
+static int do_git_config_sequence(struct config_reader *reader,
+ const struct config_options *opts,
config_fn_t fn, void *data)
{
int ret = 0;
@@ -2168,7 +2177,7 @@ static int do_git_config_sequence(const struct config_options *opts,
char *xdg_config = NULL;
char *user_config = NULL;
char *repo_config;
- enum config_scope prev_parsing_scope = current_parsing_scope;
+ enum config_scope prev_parsing_scope = reader->parsing_scope;
if (opts->commondir)
repo_config = mkpathdup("%s/config", opts->commondir);
@@ -2177,13 +2186,13 @@ static int do_git_config_sequence(const struct config_options *opts,
else
repo_config = NULL;
- current_parsing_scope = CONFIG_SCOPE_SYSTEM;
+ config_reader_set_scope(reader, CONFIG_SCOPE_SYSTEM);
if (git_config_system() && system_config &&
!access_or_die(system_config, R_OK,
opts->system_gently ? ACCESS_EACCES_OK : 0))
ret += git_config_from_file(fn, system_config, data);
- current_parsing_scope = CONFIG_SCOPE_GLOBAL;
+ config_reader_set_scope(reader, CONFIG_SCOPE_GLOBAL);
git_global_config(&user_config, &xdg_config);
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
@@ -2192,12 +2201,12 @@ static int do_git_config_sequence(const struct config_options *opts,
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
ret += git_config_from_file(fn, user_config, data);
- current_parsing_scope = CONFIG_SCOPE_LOCAL;
+ config_reader_set_scope(reader, CONFIG_SCOPE_LOCAL);
if (!opts->ignore_repo && repo_config &&
!access_or_die(repo_config, R_OK, 0))
ret += git_config_from_file(fn, repo_config, data);
- current_parsing_scope = CONFIG_SCOPE_WORKTREE;
+ config_reader_set_scope(reader, CONFIG_SCOPE_WORKTREE);
if (!opts->ignore_worktree && repository_format_worktree_config) {
char *path = git_pathdup("config.worktree");
if (!access_or_die(path, R_OK, 0))
@@ -2205,11 +2214,11 @@ static int do_git_config_sequence(const struct config_options *opts,
free(path);
}
- current_parsing_scope = CONFIG_SCOPE_COMMAND;
+ config_reader_set_scope(reader, CONFIG_SCOPE_COMMAND);
if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
die(_("unable to parse command-line config"));
- current_parsing_scope = prev_parsing_scope;
+ config_reader_set_scope(reader, prev_parsing_scope);
free(system_config);
free(xdg_config);
free(user_config);
@@ -2222,6 +2231,7 @@ int config_with_options(config_fn_t fn, void *data,
const struct config_options *opts)
{
struct config_include_data inc = CONFIG_INCLUDE_INIT;
+ enum config_scope prev_scope = the_reader.parsing_scope;
int ret;
if (opts->respect_includes) {
@@ -2235,7 +2245,7 @@ int config_with_options(config_fn_t fn, void *data,
}
if (config_source)
- current_parsing_scope = config_source->scope;
+ config_reader_set_scope(&the_reader, config_source->scope);
/*
* If we have a specific filename, use it. Otherwise, follow the
@@ -2251,13 +2261,14 @@ int config_with_options(config_fn_t fn, void *data,
ret = git_config_from_blob_ref(fn, repo, config_source->blob,
data);
} else {
- ret = do_git_config_sequence(opts, fn, data);
+ ret = do_git_config_sequence(&the_reader, opts, fn, data);
}
if (inc.remote_urls) {
string_list_clear(inc.remote_urls, 0);
FREE_AND_NULL(inc.remote_urls);
}
+ config_reader_set_scope(&the_reader, prev_scope);
return ret;
}
@@ -2391,7 +2402,7 @@ static int configset_add_value(struct config_reader *reader,
kv_info->linenr = -1;
kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
}
- kv_info->scope = current_parsing_scope;
+ kv_info->scope = reader->parsing_scope;
si->util = kv_info;
return 0;
@@ -3892,7 +3903,7 @@ enum config_scope current_config_scope(void)
if (the_reader.config_kvi)
return the_reader.config_kvi->scope;
else
- return current_parsing_scope;
+ return the_reader.parsing_scope;
}
int current_config_line(void)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v2 7/8] config: report cached filenames in die_bad_number()
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (5 preceding siblings ...)
2023-03-16 0:11 ` [PATCH v2 6/8] config.c: remove current_parsing_scope Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 22:22 ` Jonathan Tan
2023-03-16 0:11 ` [PATCH v2 8/8] config.c: rename "struct config_source cf" Glen Choo via GitGitGadget
` (4 subsequent siblings)
11 siblings, 1 reply; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
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(). Refactoring is needed because "git config
--get[-regexp] --type=int" parses the int value _after_ parsing the
config file, which will run into the BUG().
Also, plumb "struct config_reader" into the new functions. This isn't
necessary per se, but this generalizes better, so it might help us avoid
yet another refactor.
1. https://lore.kernel.org/git/20160518223712.GA18317@sigill.intra.peff.net/
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 65 +++++++++++++++++++++++++++++-------------
config.h | 1 +
t/helper/test-config.c | 17 +++++++++++
t/t1308-config-set.sh | 9 ++++++
4 files changed, 72 insertions(+), 20 deletions(-)
diff --git a/config.c b/config.c
index 460326ae21e..da5f6381cde 100644
--- a/config.c
+++ b/config.c
@@ -1312,39 +1312,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));
}
}
@@ -1352,7 +1361,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;
}
@@ -1360,7 +1369,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;
}
@@ -1368,7 +1377,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;
}
@@ -1376,7 +1385,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;
}
@@ -3840,14 +3849,23 @@ int parse_config_key(const char *var,
return 0;
}
-const char *current_config_origin_type(void)
+static int reader_origin_type(struct config_reader *reader,
+ enum config_origin_type *type)
{
- int type;
if (the_reader.config_kvi)
- type = the_reader.config_kvi->origin_type;
+ *type = reader->config_kvi->origin_type;
else if(the_reader.source)
- type = the_reader.source->origin_type;
+ *type = reader->source->origin_type;
else
+ return 1;
+ return 0;
+}
+
+const char *current_config_origin_type(void)
+{
+ 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) {
@@ -3886,14 +3904,21 @@ const char *config_scope_name(enum config_scope scope)
}
}
-const char *current_config_name(void)
+static int reader_config_name(struct config_reader *reader, const char **out)
{
- const char *name;
if (the_reader.config_kvi)
- name = the_reader.config_kvi->filename;
+ *out = reader->config_kvi->filename;
else if (the_reader.source)
- name = the_reader.source->name;
+ *out = reader->source->name;
else
+ return 1;
+ return 0;
+}
+
+const char *current_config_name(void)
+{
+ const char *name;
+ 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 7606246531a..66c8b996e15 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 4ba9eb65606..26e79168f6a 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 b38e158d3b2..9733bed30a9 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 &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v2 7/8] config: report cached filenames in die_bad_number()
2023-03-16 0:11 ` [PATCH v2 7/8] config: report cached filenames in die_bad_number() Glen Choo via GitGitGadget
@ 2023-03-16 22:22 ` Jonathan Tan
2023-03-16 23:05 ` Glen Choo
0 siblings, 1 reply; 72+ messages in thread
From: Jonathan Tan @ 2023-03-16 22:22 UTC (permalink / raw)
To: Glen Choo via GitGitGadget
Cc: Jonathan Tan, git, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo
Ah, thanks for spotting this bug! It is a minor one, but this now makes
me think that we should definitely do this refactoring of a struct
containing all the relevant config state and passing it to functions as
much as possible (as opposed to merely leaning towards the idea).
"Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 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(). Refactoring is needed because "git config
> --get[-regexp] --type=int" parses the int value _after_ parsing the
> config file, which will run into the BUG().
You say "fix this", but are there actually 2 bugs (so, "fix these")?
Firstly, that BUG() is run into when invoking "git config" the way
you describe, and secondly, die_bad_number() only reading cf and not
checking kvi to see if anything's there. (I'm not sure how to reproduce
the latter, though.)
> Also, plumb "struct config_reader" into the new functions. This isn't
> necessary per se, but this generalizes better, so it might help us avoid
> yet another refactor.
Hmm...I thought this would be desired because we don't want the_reader
to be used from non-public functions anyway, so we can just state
that that is the reason (and not worry about using future refactors as
a justification).
The code itself looks good.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v2 7/8] config: report cached filenames in die_bad_number()
2023-03-16 22:22 ` Jonathan Tan
@ 2023-03-16 23:05 ` Glen Choo
0 siblings, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-16 23:05 UTC (permalink / raw)
To: Jonathan Tan, Glen Choo via GitGitGadget
Cc: Jonathan Tan, git, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason
Jonathan Tan <jonathantanmy@google.com> writes:
>> Refactoring is needed because "git config
>> --get[-regexp] --type=int" parses the int value _after_ parsing the
>> config file, which will run into the BUG().
>
> You say "fix this", but are there actually 2 bugs (so, "fix these")?
> Firstly, that BUG() is run into when invoking "git config" the way
> you describe, and secondly, die_bad_number() only reading cf and not
> checking kvi to see if anything's there. (I'm not sure how to reproduce
> the latter, though.)
There is actually only one bug (the latter). That is tested by the new
test I added in this patch. To reproduce it, we need:
- To iterate a config_set (git_config() or repo_config() will suffice),
in which case the config_kvi is set, but not cf.
- Then in the config_fn_t we pass to it, we call git_parse_int() on an
invalid number, which will result in die_bad_number(), which prints
the less specific message.
The former case isn't a bug. We never ran into the BUG() when invoking
"git config" because die_bad_number() doesn't use current_* prior to
this patch (which is where the BUG() is). t1300:'invalid unit'
demonstrates that we print the correct message (and that we don't
BUG()):
test_expect_success 'invalid unit' '
git config aninvalid.unit "1auto" &&
test_cmp_config 1auto aninvalid.unit &&
test_must_fail git config --int --get aninvalid.unit 2>actual &&
test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
'
(which is a good signal that I should probably reword the commit
message)
>
>> Also, plumb "struct config_reader" into the new functions. This isn't
>> necessary per se, but this generalizes better, so it might help us avoid
>> yet another refactor.
>
> Hmm...I thought this would be desired because we don't want the_reader
> to be used from non-public functions anyway, so we can just state
> that that is the reason (and not worry about using future refactors as
> a justification).
Ah, good point, thanks.
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v2 8/8] config.c: rename "struct config_source cf"
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (6 preceding siblings ...)
2023-03-16 0:11 ` [PATCH v2 7/8] config: report cached filenames in die_bad_number() Glen Choo via GitGitGadget
@ 2023-03-16 0:11 ` Glen Choo via GitGitGadget
2023-03-16 0:15 ` [PATCH v2 0/8] config.c: use struct for config reading state Glen Choo
` (3 subsequent siblings)
11 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-16 0:11 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo,
Glen Choo
From: Glen Choo <chooglen@google.com>
The "cf" name is a holdover from before 4d8dd1494e (config: make parsing
stack struct independent from actual data source, 2013-07-12), when the
struct was named config_file. Since that acronym no longer makes sense,
rename "cf" to "cs". In some places, we have "struct config_set cs", so
to avoid conflict, rename those "cs" to "set" ("config_set" would be
more descriptive, but it's much longer and would require us to rewrap
several lines).
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 262 +++++++++++++++++++++++++++----------------------------
1 file changed, 131 insertions(+), 131 deletions(-)
diff --git a/config.c b/config.c
index da5f6381cde..ae171ab11c3 100644
--- a/config.c
+++ b/config.c
@@ -203,7 +203,7 @@ static const char include_depth_advice[] = N_(
"from\n"
" %s\n"
"This might be due to circular includes.");
-static int handle_path_include(struct config_source *cf, const char *path,
+static int handle_path_include(struct config_source *cs, const char *path,
struct config_include_data *inc)
{
int ret = 0;
@@ -225,14 +225,14 @@ static int handle_path_include(struct config_source *cf, const char *path,
if (!is_absolute_path(path)) {
char *slash;
- if (!cf || !cf->path) {
+ if (!cs || !cs->path) {
ret = error(_("relative config includes must come from files"));
goto cleanup;
}
- slash = find_last_dir_sep(cf->path);
+ slash = find_last_dir_sep(cs->path);
if (slash)
- strbuf_add(&buf, cf->path, slash - cf->path + 1);
+ strbuf_add(&buf, cs->path, slash - cs->path + 1);
strbuf_addstr(&buf, path);
path = buf.buf;
}
@@ -240,8 +240,8 @@ static int handle_path_include(struct config_source *cf, const char *path,
if (!access_or_die(path, R_OK, 0)) {
if (++inc->depth > MAX_INCLUDE_DEPTH)
die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
- !cf ? "<unknown>" :
- cf->name ? cf->name :
+ !cs ? "<unknown>" :
+ cs->name ? cs->name :
"the command line");
ret = git_config_from_file(git_config_include, path, inc);
inc->depth--;
@@ -258,7 +258,7 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
strbuf_addstr(pat, "**");
}
-static int prepare_include_condition_pattern(struct config_source *cf,
+static int prepare_include_condition_pattern(struct config_source *cs,
struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
@@ -275,11 +275,11 @@ static int prepare_include_condition_pattern(struct config_source *cf,
if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
const char *slash;
- if (!cf || !cf->path)
+ if (!cs || !cs->path)
return error(_("relative config include "
"conditionals must come from files"));
- strbuf_realpath(&path, cf->path, 1);
+ strbuf_realpath(&path, cs->path, 1);
slash = find_last_dir_sep(path.buf);
if (!slash)
BUG("how is this possible?");
@@ -294,7 +294,7 @@ static int prepare_include_condition_pattern(struct config_source *cf,
return prefix;
}
-static int include_by_gitdir(struct config_source *cf,
+static int include_by_gitdir(struct config_source *cs,
const struct config_options *opts,
const char *cond, size_t cond_len, int icase)
{
@@ -311,7 +311,7 @@ static int include_by_gitdir(struct config_source *cf,
strbuf_realpath(&text, git_dir, 1);
strbuf_add(&pattern, cond, cond_len);
- prefix = prepare_include_condition_pattern(cf, &pattern);
+ prefix = prepare_include_condition_pattern(cs, &pattern);
again:
if (prefix < 0)
@@ -450,16 +450,16 @@ static int include_by_remote_url(struct config_include_data *inc,
inc->remote_urls);
}
-static int include_condition_is_true(struct config_source *cf,
+static int include_condition_is_true(struct config_source *cs,
struct config_include_data *inc,
const char *cond, size_t cond_len)
{
const struct config_options *opts = inc->opts;
if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
- return include_by_gitdir(cf, opts, cond, cond_len, 0);
+ return include_by_gitdir(cs, opts, cond, cond_len, 0);
else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
- return include_by_gitdir(cf, opts, cond, cond_len, 1);
+ return include_by_gitdir(cs, opts, cond, cond_len, 1);
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
return include_by_branch(cond, cond_len);
else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -473,7 +473,7 @@ static int include_condition_is_true(struct config_source *cf,
static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
- struct config_source *cf = inc->config_reader->source;
+ struct config_source *cs = inc->config_reader->source;
const char *cond, *key;
size_t cond_len;
int ret;
@@ -487,16 +487,16 @@ static int git_config_include(const char *var, const char *value, void *data)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(cf, value, inc);
+ ret = handle_path_include(cs, value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(cf, inc, cond, cond_len) &&
+ cond && include_condition_is_true(cs, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(cf, value, inc);
+ ret = handle_path_include(cs, value, inc);
inc->fn = old_fn;
}
@@ -821,21 +821,21 @@ out:
return ret;
}
-static int get_next_char(struct config_source *cf)
+static int get_next_char(struct config_source *cs)
{
- int c = cf->do_fgetc(cf);
+ int c = cs->do_fgetc(cs);
if (c == '\r') {
/* DOS like systems */
- c = cf->do_fgetc(cf);
+ c = cs->do_fgetc(cs);
if (c != '\n') {
if (c != EOF)
- cf->do_ungetc(c, cf);
+ cs->do_ungetc(c, cs);
c = '\r';
}
}
- if (c != EOF && ++cf->total_len > INT_MAX) {
+ if (c != EOF && ++cs->total_len > INT_MAX) {
/*
* This is an absurdly long config file; refuse to parse
* further in order to protect downstream code from integer
@@ -843,38 +843,38 @@ static int get_next_char(struct config_source *cf)
* but we can mark EOF and put trash in the return value,
* which will trigger a parse error.
*/
- cf->eof = 1;
+ cs->eof = 1;
return 0;
}
if (c == '\n')
- cf->linenr++;
+ cs->linenr++;
if (c == EOF) {
- cf->eof = 1;
- cf->linenr++;
+ cs->eof = 1;
+ cs->linenr++;
c = '\n';
}
return c;
}
-static char *parse_value(struct config_source *cf)
+static char *parse_value(struct config_source *cs)
{
int quote = 0, comment = 0, space = 0;
- strbuf_reset(&cf->value);
+ strbuf_reset(&cs->value);
for (;;) {
- int c = get_next_char(cf);
+ int c = get_next_char(cs);
if (c == '\n') {
if (quote) {
- cf->linenr--;
+ cs->linenr--;
return NULL;
}
- return cf->value.buf;
+ return cs->value.buf;
}
if (comment)
continue;
if (isspace(c) && !quote) {
- if (cf->value.len)
+ if (cs->value.len)
space++;
continue;
}
@@ -885,9 +885,9 @@ static char *parse_value(struct config_source *cf)
}
}
for (; space; space--)
- strbuf_addch(&cf->value, ' ');
+ strbuf_addch(&cs->value, ' ');
if (c == '\\') {
- c = get_next_char(cf);
+ c = get_next_char(cs);
switch (c) {
case '\n':
continue;
@@ -907,18 +907,18 @@ static char *parse_value(struct config_source *cf)
default:
return NULL;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
continue;
}
if (c == '"') {
quote = 1-quote;
continue;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
}
}
-static int get_value(struct config_source *cf, config_fn_t fn, void *data,
+static int get_value(struct config_source *cs, config_fn_t fn, void *data,
struct strbuf *name)
{
int c;
@@ -927,8 +927,8 @@ static int get_value(struct config_source *cf, config_fn_t fn, void *data,
/* Get the full name */
for (;;) {
- c = get_next_char(cf);
- if (cf->eof)
+ c = get_next_char(cs);
+ if (cs->eof)
break;
if (!iskeychar(c))
break;
@@ -936,13 +936,13 @@ static int get_value(struct config_source *cf, config_fn_t fn, void *data,
}
while (c == ' ' || c == '\t')
- c = get_next_char(cf);
+ c = get_next_char(cs);
value = NULL;
if (c != '\n') {
if (c != '=')
return -1;
- value = parse_value(cf);
+ value = parse_value(cs);
if (!value)
return -1;
}
@@ -951,21 +951,21 @@ static int get_value(struct config_source *cf, config_fn_t fn, void *data,
* the line we just parsed during the call to fn to get
* accurate line number in error messages.
*/
- cf->linenr--;
+ cs->linenr--;
ret = fn(name->buf, value, data);
if (ret >= 0)
- cf->linenr++;
+ cs->linenr++;
return ret;
}
-static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
+static int get_extended_base_var(struct config_source *cs, struct strbuf *name,
int c)
{
- cf->subsection_case_sensitive = 0;
+ cs->subsection_case_sensitive = 0;
do {
if (c == '\n')
goto error_incomplete_line;
- c = get_next_char(cf);
+ c = get_next_char(cs);
} while (isspace(c));
/* We require the format to be '[base "extension"]' */
@@ -974,13 +974,13 @@ static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
strbuf_addch(name, '.');
for (;;) {
- int c = get_next_char(cf);
+ int c = get_next_char(cs);
if (c == '\n')
goto error_incomplete_line;
if (c == '"')
break;
if (c == '\\') {
- c = get_next_char(cf);
+ c = get_next_char(cs);
if (c == '\n')
goto error_incomplete_line;
}
@@ -988,25 +988,25 @@ static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
}
/* Final ']' */
- if (get_next_char(cf) != ']')
+ if (get_next_char(cs) != ']')
return -1;
return 0;
error_incomplete_line:
- cf->linenr--;
+ cs->linenr--;
return -1;
}
-static int get_base_var(struct config_source *cf, struct strbuf *name)
+static int get_base_var(struct config_source *cs, struct strbuf *name)
{
- cf->subsection_case_sensitive = 1;
+ cs->subsection_case_sensitive = 1;
for (;;) {
- int c = get_next_char(cf);
- if (cf->eof)
+ int c = get_next_char(cs);
+ if (cs->eof)
return -1;
if (c == ']')
return 0;
if (isspace(c))
- return get_extended_base_var(cf, name, c);
+ return get_extended_base_var(cs, name, c);
if (!iskeychar(c) && c != '.')
return -1;
strbuf_addch(name, tolower(c));
@@ -1019,7 +1019,7 @@ struct parse_event_data {
const struct config_options *opts;
};
-static int do_event(struct config_source *cf, enum config_event_t type,
+static int do_event(struct config_source *cs, enum config_event_t type,
struct parse_event_data *data)
{
size_t offset;
@@ -1031,7 +1031,7 @@ static int do_event(struct config_source *cf, enum config_event_t type,
data->previous_type == type)
return 0;
- offset = cf->do_ftell(cf);
+ offset = cs->do_ftell(cs);
/*
* At EOF, the parser always "inserts" an extra '\n', therefore
* the end offset of the event is the current file position, otherwise
@@ -1051,12 +1051,12 @@ static int do_event(struct config_source *cf, enum config_event_t type,
return 0;
}
-static int git_parse_source(struct config_source *cf, config_fn_t fn,
+static int git_parse_source(struct config_source *cs, config_fn_t fn,
void *data, const struct config_options *opts)
{
int comment = 0;
size_t baselen = 0;
- struct strbuf *var = &cf->var;
+ struct strbuf *var = &cs->var;
int error_return = 0;
char *error_msg = NULL;
@@ -1071,7 +1071,7 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
for (;;) {
int c;
- c = get_next_char(cf);
+ c = get_next_char(cs);
if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their
@@ -1088,12 +1088,12 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
}
}
if (c == '\n') {
- if (cf->eof) {
- if (do_event(cf, CONFIG_EVENT_EOF, &event_data) < 0)
+ if (cs->eof) {
+ if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0)
return -1;
return 0;
}
- if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
comment = 0;
continue;
@@ -1101,23 +1101,23 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
if (comment)
continue;
if (isspace(c)) {
- if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
continue;
}
if (c == '#' || c == ';') {
- if (do_event(cf, CONFIG_EVENT_COMMENT, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_COMMENT, &event_data) < 0)
return -1;
comment = 1;
continue;
}
if (c == '[') {
- if (do_event(cf, CONFIG_EVENT_SECTION, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_SECTION, &event_data) < 0)
return -1;
/* Reset prior to determining a new stem */
strbuf_reset(var);
- if (get_base_var(cf, var) < 0 || var->len < 1)
+ if (get_base_var(cs, var) < 0 || var->len < 1)
break;
strbuf_addch(var, '.');
baselen = var->len;
@@ -1126,7 +1126,7 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
if (!isalpha(c))
break;
- if (do_event(cf, CONFIG_EVENT_ENTRY, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_ENTRY, &event_data) < 0)
return -1;
/*
@@ -1136,42 +1136,42 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
*/
strbuf_setlen(var, baselen);
strbuf_addch(var, tolower(c));
- if (get_value(cf, fn, data, var) < 0)
+ if (get_value(cs, fn, data, var) < 0)
break;
}
- if (do_event(cf, CONFIG_EVENT_ERROR, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_ERROR, &event_data) < 0)
return -1;
- switch (cf->origin_type) {
+ switch (cs->origin_type) {
case CONFIG_ORIGIN_BLOB:
error_msg = xstrfmt(_("bad config line %d in blob %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_FILE:
error_msg = xstrfmt(_("bad config line %d in file %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_STDIN:
error_msg = xstrfmt(_("bad config line %d in standard input"),
- cf->linenr);
+ cs->linenr);
break;
case CONFIG_ORIGIN_SUBMODULE_BLOB:
error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_CMDLINE:
error_msg = xstrfmt(_("bad config line %d in command line %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
default:
error_msg = xstrfmt(_("bad config line %d in %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
}
switch (opts && opts->error_action ?
opts->error_action :
- cf->default_error_action) {
+ cs->default_error_action) {
case CONFIG_ERROR_DIE:
die("%s", error_msg);
break;
@@ -2281,13 +2281,13 @@ int config_with_options(config_fn_t fn, void *data,
return ret;
}
-static void configset_iter(struct config_reader *reader, struct config_set *cs,
+static void configset_iter(struct config_reader *reader, struct config_set *set,
config_fn_t fn, void *data)
{
int i, value_index;
struct string_list *values;
struct config_set_element *entry;
- struct configset_list *list = &cs->list;
+ struct configset_list *list = &set->list;
for (i = 0; i < list->nr; i++) {
entry = list->items[i].e;
@@ -2352,7 +2352,7 @@ void read_very_early_config(config_fn_t cb, void *data)
config_with_options(cb, data, NULL, &opts);
}
-static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+static struct config_set_element *configset_find_element(struct config_set *set, const char *key)
{
struct config_set_element k;
struct config_set_element *found_entry;
@@ -2366,13 +2366,13 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
hashmap_entry_init(&k.ent, strhash(normalized_key));
k.key = normalized_key;
- found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
+ found_entry = hashmap_get_entry(&set->config_hash, &k, ent, NULL);
free(normalized_key);
return found_entry;
}
static int configset_add_value(struct config_reader *reader,
- struct config_set *cs, const char *key,
+ struct config_set *set, const char *key,
const char *value)
{
struct config_set_element *e;
@@ -2380,7 +2380,7 @@ static int configset_add_value(struct config_reader *reader,
struct configset_list_item *l_item;
struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
- e = configset_find_element(cs, key);
+ e = configset_find_element(set, key);
/*
* Since the keys are being fed by git_config*() callback mechanism, they
* are already normalized. So simply add them without any further munging.
@@ -2390,12 +2390,12 @@ static int configset_add_value(struct config_reader *reader,
hashmap_entry_init(&e->ent, strhash(key));
e->key = xstrdup(key);
string_list_init_dup(&e->value_list);
- hashmap_add(&cs->config_hash, &e->ent);
+ hashmap_add(&set->config_hash, &e->ent);
}
si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
- ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
- l_item = &cs->list.items[cs->list.nr++];
+ ALLOC_GROW(set->list.items, set->list.nr + 1, set->list.alloc);
+ l_item = &set->list.items[set->list.nr++];
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
@@ -2430,33 +2430,33 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,
return strcmp(e1->key, e2->key);
}
-void git_configset_init(struct config_set *cs)
+void git_configset_init(struct config_set *set)
{
- hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
- cs->hash_initialized = 1;
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
+ set->hash_initialized = 1;
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
-void git_configset_clear(struct config_set *cs)
+void git_configset_clear(struct config_set *set)
{
struct config_set_element *entry;
struct hashmap_iter iter;
- if (!cs->hash_initialized)
+ if (!set->hash_initialized)
return;
- hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+ hashmap_for_each_entry(&set->config_hash, &iter, entry,
ent /* member name */) {
free(entry->key);
string_list_clear(&entry->value_list, 1);
}
- hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
- cs->hash_initialized = 0;
- free(cs->list.items);
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_clear_and_free(&set->config_hash, struct config_set_element, ent);
+ set->hash_initialized = 0;
+ free(set->list.items);
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
struct configset_add_data {
@@ -2472,15 +2472,15 @@ static int config_set_callback(const char *key, const char *value, void *cb)
return 0;
}
-int git_configset_add_file(struct config_set *cs, const char *filename)
+int git_configset_add_file(struct config_set *set, const char *filename)
{
struct configset_add_data data = CONFIGSET_ADD_INIT;
data.config_reader = &the_reader;
- data.config_set = cs;
+ data.config_set = set;
return git_config_from_file(config_set_callback, filename, &data);
}
-int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+int git_configset_get_value(struct config_set *set, const char *key, const char **value)
{
const struct string_list *values = NULL;
/*
@@ -2488,7 +2488,7 @@ int git_configset_get_value(struct config_set *cs, const char *key, const char *
* queried key in the files of the configset, the value returned will be the last
* value in the value list for that key.
*/
- values = git_configset_get_value_multi(cs, key);
+ values = git_configset_get_value_multi(set, key);
if (!values)
return 1;
@@ -2497,26 +2497,26 @@ int git_configset_get_value(struct config_set *cs, const char *key, const char *
return 0;
}
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+const struct string_list *git_configset_get_value_multi(struct config_set *set, const char *key)
{
- struct config_set_element *e = configset_find_element(cs, key);
+ struct config_set_element *e = configset_find_element(set, key);
return e ? &e->value_list : NULL;
}
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+int git_configset_get_string(struct config_set *set, const char *key, char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_string((const char **)dest, key, value);
else
return 1;
}
-static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+static int git_configset_get_string_tmp(struct config_set *set, const char *key,
const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
if (!value)
return config_error_nonbool(key);
*dest = value;
@@ -2526,51 +2526,51 @@ static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
}
}
-int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_int(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_int(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+int git_configset_get_ulong(struct config_set *set, const char *key, unsigned long *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_ulong(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+int git_configset_get_bool_or_int(struct config_set *set, const char *key,
int *is_bool, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool_or_int(key, value, is_bool);
return 0;
} else
return 1;
}
-int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_parse_maybe_bool(value);
if (*dest == -1)
return -1;
@@ -2579,10 +2579,10 @@ int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *de
return 1;
}
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_pathname(dest, key, value);
else
return 1;
@@ -2973,7 +2973,7 @@ static int store_aux_event(enum config_event_t type,
size_t begin, size_t end, void *data)
{
struct config_store_data *store = data;
- struct config_source *cf = store->config_reader->source;
+ struct config_source *cs = store->config_reader->source;
ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
store->parsed[store->parsed_nr].begin = begin;
@@ -2983,10 +2983,10 @@ static int store_aux_event(enum config_event_t type,
if (type == CONFIG_EVENT_SECTION) {
int (*cmpfn)(const char *, const char *, size_t);
- if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
- return error(_("invalid section name '%s'"), cf->var.buf);
+ if (cs->var.len < 2 || cs->var.buf[cs->var.len - 1] != '.')
+ return error(_("invalid section name '%s'"), cs->var.buf);
- if (cf->subsection_case_sensitive)
+ if (cs->subsection_case_sensitive)
cmpfn = strncasecmp;
else
cmpfn = strncmp;
@@ -2994,8 +2994,8 @@ static int store_aux_event(enum config_event_t type,
/* Is this the section we were looking for? */
store->is_keys_section =
store->parsed[store->parsed_nr].is_keys_section =
- cf->var.len - 1 == store->baselen &&
- !cmpfn(cf->var.buf, store->key, store->baselen);
+ cs->var.len - 1 == store->baselen &&
+ !cmpfn(cs->var.buf, store->key, store->baselen);
if (store->is_keys_section) {
store->section_seen = 1;
ALLOC_GROW(store->seen, store->seen_nr + 1,
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v2 0/8] config.c: use struct for config reading state
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (7 preceding siblings ...)
2023-03-16 0:11 ` [PATCH v2 8/8] config.c: rename "struct config_source cf" Glen Choo via GitGitGadget
@ 2023-03-16 0:15 ` Glen Choo
2023-03-16 22:29 ` Jonathan Tan
` (2 subsequent siblings)
11 siblings, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-16 0:15 UTC (permalink / raw)
To: Glen Choo via GitGitGadget, git
Cc: Jonathan Tan, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason
"Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
> As a result of moving the rename, the range-diff is quite noisy. The diff
> between the final commits is might be helpful instead [2] (I'll also send a
> diff to the ML).
diff --git a/config.c b/config.c
index 19bab84c47..ae171ab11c 100644
--- a/config.c
+++ b/config.c
@@ -81,14 +81,18 @@ struct config_reader {
*/
enum config_scope parsing_scope;
};
-/* Only public functions should reference the_reader. */
+/*
+ * Where possible, prefer to accept "struct config_reader" as an arg than to use
+ * "the_reader". "the_reader" should only be used if that is infeasible, e.g. in
+ * a public function.
+ */
static struct config_reader the_reader;
static inline void config_reader_push_source(struct config_reader *reader,
struct config_source *top)
{
if (reader->config_kvi)
- BUG("source should only be set when parsing a config source");
+ BUG("source should not be set while iterating a config set");
if (reader->source)
top->prev = reader->source;
reader->source = top;
@@ -108,7 +112,7 @@ static inline void config_reader_set_kvi(struct config_reader *reader,
struct key_value_info *kvi)
{
if (kvi && (reader->source || reader->parsing_scope))
- BUG("kvi should only be set when iterating through configset");
+ BUG("kvi should not be set while parsing a config source");
reader->config_kvi = kvi;
}
@@ -1308,39 +1312,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 *cs, 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 (!(cs && cs->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 (cs->origin_type) {
+ switch (config_origin) {
case CONFIG_ORIGIN_BLOB:
die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
- value, name, cs->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, cs->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, cs->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, cs->name, _(error_type));
+ value, name, config_name, _(error_type));
default:
die(_("bad numeric config value '%s' for '%s' in %s: %s"),
- value, name, cs->name, _(error_type));
+ value, name, config_name, _(error_type));
}
}
@@ -1348,7 +1361,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;
}
@@ -1356,7 +1369,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;
}
@@ -1364,7 +1377,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;
}
@@ -1372,7 +1385,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;
}
@@ -2268,34 +2281,27 @@ int config_with_options(config_fn_t fn, void *data,
return ret;
}
-struct configset_iter_data {
- struct config_reader *config_reader;
- void *inner;
-};
-#define CONFIGSET_ITER_INIT { 0 }
-
-static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+static void configset_iter(struct config_reader *reader, struct config_set *set,
+ config_fn_t fn, void *data)
{
int i, value_index;
struct string_list *values;
struct config_set_element *entry;
- struct configset_list *list = &cs->list;
- struct configset_iter_data *iter_data = data;
+ struct configset_list *list = &set->list;
for (i = 0; i < list->nr; i++) {
- struct key_value_info *kvi;
entry = list->items[i].e;
value_index = list->items[i].value_index;
values = &entry->value_list;
- kvi = values->items[value_index].util;
- config_reader_set_kvi(iter_data->config_reader, kvi);
+ config_reader_set_kvi(reader, values->items[value_index].util);
- if (fn(entry->key, values->items[value_index].string, iter_data->inner) < 0)
- git_die_config_linenr(entry->key, kvi->filename,
- kvi->linenr);
+ if (fn(entry->key, values->items[value_index].string, data) < 0)
+ git_die_config_linenr(entry->key,
+ reader->config_kvi->filename,
+ reader->config_kvi->linenr);
- config_reader_set_kvi(iter_data->config_reader, NULL);
+ config_reader_set_kvi(reader, NULL);
}
}
@@ -2346,7 +2352,7 @@ void read_very_early_config(config_fn_t cb, void *data)
config_with_options(cb, data, NULL, &opts);
}
-static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+static struct config_set_element *configset_find_element(struct config_set *set, const char *key)
{
struct config_set_element k;
struct config_set_element *found_entry;
@@ -2360,13 +2366,13 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
hashmap_entry_init(&k.ent, strhash(normalized_key));
k.key = normalized_key;
- found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
+ found_entry = hashmap_get_entry(&set->config_hash, &k, ent, NULL);
free(normalized_key);
return found_entry;
}
static int configset_add_value(struct config_reader *reader,
- struct config_set *cs, const char *key,
+ struct config_set *set, const char *key,
const char *value)
{
struct config_set_element *e;
@@ -2374,7 +2380,7 @@ static int configset_add_value(struct config_reader *reader,
struct configset_list_item *l_item;
struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
- e = configset_find_element(cs, key);
+ e = configset_find_element(set, key);
/*
* Since the keys are being fed by git_config*() callback mechanism, they
* are already normalized. So simply add them without any further munging.
@@ -2384,12 +2390,12 @@ static int configset_add_value(struct config_reader *reader,
hashmap_entry_init(&e->ent, strhash(key));
e->key = xstrdup(key);
string_list_init_dup(&e->value_list);
- hashmap_add(&cs->config_hash, &e->ent);
+ hashmap_add(&set->config_hash, &e->ent);
}
si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
- ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
- l_item = &cs->list.items[cs->list.nr++];
+ ALLOC_GROW(set->list.items, set->list.nr + 1, set->list.alloc);
+ l_item = &set->list.items[set->list.nr++];
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
@@ -2424,33 +2430,33 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,
return strcmp(e1->key, e2->key);
}
-void git_configset_init(struct config_set *cs)
+void git_configset_init(struct config_set *set)
{
- hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
- cs->hash_initialized = 1;
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
+ set->hash_initialized = 1;
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
-void git_configset_clear(struct config_set *cs)
+void git_configset_clear(struct config_set *set)
{
struct config_set_element *entry;
struct hashmap_iter iter;
- if (!cs->hash_initialized)
+ if (!set->hash_initialized)
return;
- hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+ hashmap_for_each_entry(&set->config_hash, &iter, entry,
ent /* member name */) {
free(entry->key);
string_list_clear(&entry->value_list, 1);
}
- hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
- cs->hash_initialized = 0;
- free(cs->list.items);
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_clear_and_free(&set->config_hash, struct config_set_element, ent);
+ set->hash_initialized = 0;
+ free(set->list.items);
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
struct configset_add_data {
@@ -2466,15 +2472,15 @@ static int config_set_callback(const char *key, const char *value, void *cb)
return 0;
}
-int git_configset_add_file(struct config_set *cs, const char *filename)
+int git_configset_add_file(struct config_set *set, const char *filename)
{
struct configset_add_data data = CONFIGSET_ADD_INIT;
data.config_reader = &the_reader;
- data.config_set = cs;
+ data.config_set = set;
return git_config_from_file(config_set_callback, filename, &data);
}
-int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+int git_configset_get_value(struct config_set *set, const char *key, const char **value)
{
const struct string_list *values = NULL;
/*
@@ -2482,7 +2488,7 @@ int git_configset_get_value(struct config_set *cs, const char *key, const char *
* queried key in the files of the configset, the value returned will be the last
* value in the value list for that key.
*/
- values = git_configset_get_value_multi(cs, key);
+ values = git_configset_get_value_multi(set, key);
if (!values)
return 1;
@@ -2491,26 +2497,26 @@ int git_configset_get_value(struct config_set *cs, const char *key, const char *
return 0;
}
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+const struct string_list *git_configset_get_value_multi(struct config_set *set, const char *key)
{
- struct config_set_element *e = configset_find_element(cs, key);
+ struct config_set_element *e = configset_find_element(set, key);
return e ? &e->value_list : NULL;
}
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+int git_configset_get_string(struct config_set *set, const char *key, char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_string((const char **)dest, key, value);
else
return 1;
}
-static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+static int git_configset_get_string_tmp(struct config_set *set, const char *key,
const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
if (!value)
return config_error_nonbool(key);
*dest = value;
@@ -2520,51 +2526,51 @@ static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
}
}
-int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_int(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_int(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+int git_configset_get_ulong(struct config_set *set, const char *key, unsigned long *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_ulong(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+int git_configset_get_bool_or_int(struct config_set *set, const char *key,
int *is_bool, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool_or_int(key, value, is_bool);
return 0;
} else
return 1;
}
-int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_parse_maybe_bool(value);
if (*dest == -1)
return -1;
@@ -2573,10 +2579,10 @@ int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *de
return 1;
}
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_pathname(dest, key, value);
else
return 1;
@@ -2630,14 +2636,10 @@ static void repo_config_clear(struct repository *repo)
git_configset_clear(repo->config);
}
-void repo_config(struct repository *repo, config_fn_t fn, void *data_inner)
+void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
- struct configset_iter_data data = CONFIGSET_ITER_INIT;
- data.inner = data_inner;
- data.config_reader = &the_reader;
-
git_config_check_init(repo);
- configset_iter(repo->config, fn, &data);
+ configset_iter(&the_reader, repo->config, fn, data);
}
int repo_config_get_value(struct repository *repo,
@@ -2739,15 +2741,11 @@ static void read_protected_config(void)
config_with_options(config_set_callback, &data, NULL, &opts);
}
-void git_protected_config(config_fn_t fn, void *data_inner)
+void git_protected_config(config_fn_t fn, void *data)
{
- struct configset_iter_data data = CONFIGSET_ITER_INIT;
if (!protected_config.hash_initialized)
read_protected_config();
- data.inner = data_inner;
- data.config_reader = &the_reader;
-
- configset_iter(&protected_config, fn, &data);
+ configset_iter(&the_reader, &protected_config, fn, data);
}
/* Functions used historically to read configuration from 'the_repository' */
@@ -3851,14 +3849,23 @@ int parse_config_key(const char *var,
return 0;
}
-const char *current_config_origin_type(void)
+static int reader_origin_type(struct config_reader *reader,
+ enum config_origin_type *type)
{
- int type;
if (the_reader.config_kvi)
- type = the_reader.config_kvi->origin_type;
+ *type = reader->config_kvi->origin_type;
else if(the_reader.source)
- type = the_reader.source->origin_type;
+ *type = reader->source->origin_type;
else
+ return 1;
+ return 0;
+}
+
+const char *current_config_origin_type(void)
+{
+ 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) {
@@ -3897,14 +3904,21 @@ const char *config_scope_name(enum config_scope scope)
}
}
-const char *current_config_name(void)
+static int reader_config_name(struct config_reader *reader, const char **out)
{
- const char *name;
if (the_reader.config_kvi)
- name = the_reader.config_kvi->filename;
+ *out = reader->config_kvi->filename;
else if (the_reader.source)
- name = the_reader.source->name;
+ *out = reader->source->name;
else
+ return 1;
+ return 0;
+}
+
+const char *current_config_name(void)
+{
+ const char *name;
+ 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 &&
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v2 0/8] config.c: use struct for config reading state
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (8 preceding siblings ...)
2023-03-16 0:15 ` [PATCH v2 0/8] config.c: use struct for config reading state Glen Choo
@ 2023-03-16 22:29 ` Jonathan Tan
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
11 siblings, 0 replies; 72+ messages in thread
From: Jonathan Tan @ 2023-03-16 22:29 UTC (permalink / raw)
To: Glen Choo via GitGitGadget
Cc: Jonathan Tan, git, Emily Shaffer, Jeff King, Derrick Stolee,
Calvin Wan, Ævar Arnfjörð Bjarmason, Glen Choo
So overall what this series does is to create a struct that contains
config parsing state (roughly speaking...it's either parsing state or
a "kvi" that contains some of the parsing state that existed when its
value was parsed), starts a pattern in config.c in which that struct
is passed between functions as much as possible, and establishes a few
functions like reader_origin_type() that make it convenient to work
on the basis of this struct as opposed to on the config_source and kvi
individually. And following this pattern, I think, likely would have
enabled us to avoid the bug in patch 7/8 and enable us to avoid similar
bugs in the future, so overall I'm happy with this series.
"Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
> Introduce 8/8 to get rid of the confusing acronym "struct config_source
> cf", but I don't mind ejecting it if it's too much churn.
I think patch 8/8 is not worth including, so I didn't leave comments on
it, but I'm fine if it ends up merged too.
^ permalink raw reply [flat|nested] 72+ messages in thread
* [RFC PATCH 0/5] bypass config.c global state with configset
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (9 preceding siblings ...)
2023-03-16 22:29 ` Jonathan Tan
@ 2023-03-17 5:01 ` Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 1/5] config.h: move up "struct key_value_info" Ævar Arnfjörð Bjarmason
` (7 more replies)
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
11 siblings, 8 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-17 5:01 UTC (permalink / raw)
To: git
Cc: Glen Choo, Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
On Thu, Mar 16 2023, Glen Choo via GitGitGadget wrote:
> After reflecting on Ævar's responses on v1, I'm fairly convinced that
> "struct config_reader" shouldn't exist in the long term. I've written my
> thoughts on a good long term direction in the "Leftover bits" section. Based
> on that, I've also updated my WIP libification patches [1] to remove "struct
> config_reader" from the library interface, and think it looks a lot better
> as a result.
That libification url
(https://github.com/git/git/compare/master...chooglen:git:config-lib-parsing)
doesn't work for me, and I didn't find a branch with that name in your
published repo. So maybe you've already done all this
post-libification work...
> = Leftover bits
>
> We still need a global "the_reader" because config callbacks are reading
> auxiliary information about the config (e.g. line number, file name) via
> global functions (e.g. current_config_line(), current_config_name()). This
> is either because the callback uses this info directly (like
> builtin/config.c printing the filename and scope of the value) or for error
> reporting (like git_parse_int() reporting the filename of the value it
> failed to parse).
>
> If we had a way to plumb the state from "struct config_reader" to the config
> callback functions, we could initialize "struct config_reader" in the config
> machinery whenever we read config (instead of asking the caller to
> initialize "struct config_reader" themselves), and config reading could
> become a thread-safe operation. There isn't an obvious way to plumb this
> state to config callbacks without adding an additional arg to config_fn_t
> and incurring a lot of churn, but if we start replacing "config_fn_t" with
> the configset API (which we've independently wanted for some time), this may
> become feasible.
...in any case. This RFC expands a bit on my comments on the v1 (at
[1] and upthread). It doesn't get all the way there, but with the
small change in 5/5 we've gotten rid of current_config_line(), the
1-4/5 are trivial pre-refactorings to make that diff smaller
(e.g. moving the "struct key_value_info" around in config.h).
Maybe it still makes sense to go for this "the_reader" intermediate
step, but I can't help but think that we could just go for it all in
one leap, and that you've just got stuck on thinking that you needed
to change "config_fn_t" for all its callers.
As the 5/5 here shows we have various orthagonal uses of the
"config_fn_t" in config.c, and can just implement a new callback type
for the edge cases where we need the file & line info.
This still leave the current_config_name() etc, which
e.g. builtin/config.c still uses. In your series you've needed to add
the new "reader" parameter for everything from do_config_from(), but
if we're doing that can't we instead just go straight to passing a
"struct key_value_info *" (perhaps with an added "name" field) all the
way down, replacing "cf->linenr" etc?
Instead you end up extending "the_reader" everywhere, including to
e.g. configset_iter, which I think as the 5/5 here shows isn't needed,
but maybe I've missed something.
Similarly, you mention git_parse_int() wanting to report a filename
and/or line number. I'm aware that it can do that, but it doesn't do
so in the common case, e.g.:
git -c format.filenameMaxLength=abc log
fatal: bad numeric config value 'abc' for 'format.filenamemaxlength': invalid unit
And the same goes for writing it to e.g. ~/.gitconfig. It's only if
you use "git config --file" or similar that we'll report a filename.
So just as with the current_config_line() I wonder if you just grepped
for e.g. git_config_int() and thought because we have a lot of users
of it that all of them would require this data, but for e.g. this
log.c caller (and most or all of the others) we'll be reading the
normal config, and aren't getting any useful info from
die_bad_number() that we wouldn't get from an error function that
didn't need the "linenr" etc.
> And if we do this, "struct config_reader" itself will probably become
> obsolete, because we'd be able to plumb only the relevant state for the
> current operation, e.g. if we are parsing a config file, we'd pass only the
> config file parsing state, instead of "struct config_reader", which also
> contains config set iterating state. In such a scenario, we'd probably want
> to pass "struct key_value_info" to the config callback, since that's all the
> callback should be interested in anyway. Interestingly, this was proposed by
> Junio back in [4], and we didn't do this back then out of concern for the
> churn (just like in v1).
I think we can make it even simpler than that, and from playing around
with builtin/config.c a bit after the 5/5 here I got a POC working
(but am not posting it here, didn't have time to clean it up).
We can just make config_set_callback() and configset_iter()
non-static, so e.g. the builtin/config.c caller that implements
"--show-origin" can keep its config_with_options(...) call, but
instead of "streaming" the config, it'll buffer it up into a
configset.
The advantage of that is that with the configset API we'll get a
"struct key_value_info *" for free on the other end. I.e. we'll
configset_iter() with a fn=NULL, but with a defined "config_kvi_fn_t",
which 5/5 is adding.
But I haven't done that work (and am not planning to finish this), but
maybe this helps.
We'll also need to track the equivalent of "cf->linenr" etc. while we
do the actual initial parse. I think it might be simpler to start by
converting those "linenr" to a "struct key_value_info" that's placed
in the "config_source" right away.
I.e. when we pass it to the error handlers we'll need to give them
access to the "linenr", but we don't want to provide e.g. "eof" (which
is internal-only state).
I wonder how much else you're converting here is actually dead code in
the end (or can trivially be made dead). E.g. the
current_config_line() change you make in 1/8 is never going to use the
"cf_global" if combined with the 5/5 change here.
1. https://lore.kernel.org/git/230308.867cvrziac.gmgdl@evledraar.gmail.com/
Ævar Arnfjörð Bjarmason (5):
config.h: move up "struct key_value_info"
config.c: use "enum config_origin_type", not "int"
config API: add a config_origin_type_name() helper
config.c: refactor configset_iter()
config API: add and use a repo_config_kvi()
builtin/remote.c | 11 +++----
config.c | 67 ++++++++++++++++++++++++++----------------
config.h | 29 +++++++++++++-----
t/helper/test-config.c | 13 ++++----
t/t5505-remote.sh | 7 +++--
5 files changed, 80 insertions(+), 47 deletions(-)
--
2.40.0.rc1.1034.g5867a1b10c5
^ permalink raw reply [flat|nested] 72+ messages in thread
* [RFC PATCH 1/5] config.h: move up "struct key_value_info"
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
@ 2023-03-17 5:01 ` Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 2/5] config.c: use "enum config_origin_type", not "int" Ævar Arnfjörð Bjarmason
` (6 subsequent siblings)
7 siblings, 0 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-17 5:01 UTC (permalink / raw)
To: git
Cc: Glen Choo, Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Move the declaration of the "struct key_value_info" earlier in the
file, in a subsequent commit we'll need it shortly after this line.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
config.h | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/config.h b/config.h
index 7606246531a..571b92d674a 100644
--- a/config.h
+++ b/config.h
@@ -110,6 +110,13 @@ struct config_options {
} error_action;
};
+struct key_value_info {
+ const char *filename;
+ int linenr;
+ enum config_origin_type origin_type;
+ enum config_scope scope;
+};
+
/**
* A config callback function takes three parameters:
*
@@ -612,13 +619,6 @@ int git_config_get_expiry(const char *key, const char **output);
/* parse either "this many days" integer, or "5.days.ago" approxidate */
int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
-struct key_value_info {
- const char *filename;
- int linenr;
- enum config_origin_type origin_type;
- enum config_scope scope;
-};
-
/**
* First prints the error message specified by the caller in `err` and then
* dies printing the line number and the file name of the highest priority
--
2.40.0.rc1.1034.g5867a1b10c5
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [RFC PATCH 2/5] config.c: use "enum config_origin_type", not "int"
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 1/5] config.h: move up "struct key_value_info" Ævar Arnfjörð Bjarmason
@ 2023-03-17 5:01 ` Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 3/5] config API: add a config_origin_type_name() helper Ævar Arnfjörð Bjarmason
` (5 subsequent siblings)
7 siblings, 0 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-17 5:01 UTC (permalink / raw)
To: git
Cc: Glen Choo, Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Change the current_config_origin_type() function to use the
appropriate enum type for its "type" variable.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
config.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/config.c b/config.c
index 00090a32fc3..a4105c456c3 100644
--- a/config.c
+++ b/config.c
@@ -3768,7 +3768,8 @@ int parse_config_key(const char *var,
const char *current_config_origin_type(void)
{
- int type;
+ enum config_origin_type type;
+
if (current_config_kvi)
type = current_config_kvi->origin_type;
else if(cf)
--
2.40.0.rc1.1034.g5867a1b10c5
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [RFC PATCH 3/5] config API: add a config_origin_type_name() helper
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 1/5] config.h: move up "struct key_value_info" Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 2/5] config.c: use "enum config_origin_type", not "int" Ævar Arnfjörð Bjarmason
@ 2023-03-17 5:01 ` Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 4/5] config.c: refactor configset_iter() Ævar Arnfjörð Bjarmason
` (4 subsequent siblings)
7 siblings, 0 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-17 5:01 UTC (permalink / raw)
To: git
Cc: Glen Choo, Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Add a config_origin_type_name() helper function. In a subsequent
commit we'll want to invoke this part of current_config_origin_type()
without requiring the global "current_config_kvi" or "cf" state.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
config.c | 25 +++++++++++++++----------
config.h | 6 ++++++
2 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/config.c b/config.c
index a4105c456c3..a65e7bb36d3 100644
--- a/config.c
+++ b/config.c
@@ -3766,17 +3766,8 @@ int parse_config_key(const char *var,
return 0;
}
-const char *current_config_origin_type(void)
+const char *config_origin_type_name(enum config_origin_type type)
{
- enum config_origin_type type;
-
- if (current_config_kvi)
- type = current_config_kvi->origin_type;
- else if(cf)
- type = cf->origin_type;
- else
- BUG("current_config_origin_type called outside config callback");
-
switch (type) {
case CONFIG_ORIGIN_BLOB:
return "blob";
@@ -3793,6 +3784,20 @@ const char *current_config_origin_type(void)
}
}
+const char *current_config_origin_type(void)
+{
+ enum config_origin_type type;
+
+ if (current_config_kvi)
+ type = current_config_kvi->origin_type;
+ else if(cf)
+ type = cf->origin_type;
+ else
+ BUG("current_config_origin_type called outside config callback");
+
+ return config_origin_type_name(type);
+}
+
const char *config_scope_name(enum config_scope scope)
{
switch (scope) {
diff --git a/config.h b/config.h
index 571b92d674a..a9cb01e9405 100644
--- a/config.h
+++ b/config.h
@@ -117,6 +117,12 @@ struct key_value_info {
enum config_scope scope;
};
+/**
+ * Given the "enum config_origin_type origin_type"
+ * (e.g. CONFIG_ORIGIN_BLOB) return a string (e.g. "blob").
+ */
+const char *config_origin_type_name(enum config_origin_type type);
+
/**
* A config callback function takes three parameters:
*
--
2.40.0.rc1.1034.g5867a1b10c5
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [RFC PATCH 4/5] config.c: refactor configset_iter()
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
` (2 preceding siblings ...)
2023-03-17 5:01 ` [RFC PATCH 3/5] config API: add a config_origin_type_name() helper Ævar Arnfjörð Bjarmason
@ 2023-03-17 5:01 ` Ævar Arnfjörð Bjarmason
2023-03-17 5:01 ` [RFC PATCH 5/5] config API: add and use a repo_config_kvi() Ævar Arnfjörð Bjarmason
` (3 subsequent siblings)
7 siblings, 0 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-17 5:01 UTC (permalink / raw)
To: git
Cc: Glen Choo, Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Use variables rather than long lines in configset_iter(), and use our
own "kvi" rather than relying on the global "current_config_kvi"
within this function.
There's no functional change here, but doing this will make a
subsequent functional change smaller.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
config.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/config.c b/config.c
index a65e7bb36d3..230a98b0631 100644
--- a/config.c
+++ b/config.c
@@ -2227,17 +2227,22 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
struct configset_list *list = &cs->list;
for (i = 0; i < list->nr; i++) {
+ const char *key;
+ const char *val;
+ struct key_value_info *kvi;
+
entry = list->items[i].e;
value_index = list->items[i].value_index;
values = &entry->value_list;
- current_config_kvi = values->items[value_index].util;
-
- if (fn(entry->key, values->items[value_index].string, data) < 0)
- git_die_config_linenr(entry->key,
- current_config_kvi->filename,
- current_config_kvi->linenr);
+ key = entry->key;
+ val = values->items[value_index].string;
+ kvi = values->items[value_index].util;
+ current_config_kvi = kvi;
+ if (fn(key, val, data) < 0)
+ git_die_config_linenr(entry->key, kvi->filename,
+ kvi->linenr);
current_config_kvi = NULL;
}
}
--
2.40.0.rc1.1034.g5867a1b10c5
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [RFC PATCH 5/5] config API: add and use a repo_config_kvi()
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
` (3 preceding siblings ...)
2023-03-17 5:01 ` [RFC PATCH 4/5] config.c: refactor configset_iter() Ævar Arnfjörð Bjarmason
@ 2023-03-17 5:01 ` Ævar Arnfjörð Bjarmason
2023-03-17 17:17 ` Junio C Hamano
2023-03-17 20:59 ` Jonathan Tan
2023-03-17 16:21 ` [RFC PATCH 0/5] bypass config.c global state with configset Junio C Hamano
` (2 subsequent siblings)
7 siblings, 2 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-17 5:01 UTC (permalink / raw)
To: git
Cc: Glen Choo, Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Introduce a repo_config_kvi(), which is a repo_config() which calls a
"config_kvi_fn_t", rather than the "config_fn_t" that repo_config()
uses.
This allows us to pass along the "struct key_value_info *" directly,
rather than having the callback grab it from the global
"current_config_kvi" that we've been maintaining in
"configset_iter()".
This change is an alternate direction to the topic at [1], this
expands on the vague suggestions I made in [2] to go in this
direction.
As this shows we can split apart the "config_fn_t", and thus avoid
having to change the hundreds of existing "config_fn_t" callers. By
doing this we can already get rid of the current_config_kvi()
function, as "builtin/remote.c" and "t/helper/test-config.c" were the
only users of it.
The change to "t/t5505-remote.sh" ensures that the change here to
config_read_push_default() isn't breaking things. It's the only test
that would go through that codepath, but nothing asserted that we'd
get the correct line number. Let's sanity check that, as well as the
other callback data.
This leaves the other current_config_*() functions. Subsequent commits
will need to deal with those.
1. https://lore.kernel.org/git/pull.1463.v2.git.git.1678925506.gitgitgadget@gmail.com/
2. https://lore.kernel.org/git/230307.86wn3szrzu.gmgdl@evledraar.gmail.com/
3. https://lore.kernel.org/git/230308.867cvrziac.gmgdl@evledraar.gmail.com/
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
builtin/remote.c | 11 ++++++-----
config.c | 32 ++++++++++++++++++--------------
config.h | 9 ++++++++-
t/helper/test-config.c | 13 +++++++------
t/t5505-remote.sh | 7 +++++--
5 files changed, 44 insertions(+), 28 deletions(-)
diff --git a/builtin/remote.c b/builtin/remote.c
index 729f6f3643a..c65bce05034 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -644,17 +644,18 @@ struct push_default_info
};
static int config_read_push_default(const char *key, const char *value,
- void *cb)
+ struct key_value_info *kvi, void *cb)
{
struct push_default_info* info = cb;
if (strcmp(key, "remote.pushdefault") ||
!value || strcmp(value, info->old_name))
return 0;
- info->scope = current_config_scope();
+ info->scope = kvi->scope;
strbuf_reset(&info->origin);
- strbuf_addstr(&info->origin, current_config_name());
- info->linenr = current_config_line();
+ if (kvi->filename)
+ strbuf_addstr(&info->origin, kvi->filename);
+ info->linenr = kvi->linenr;
return 0;
}
@@ -663,7 +664,7 @@ static void handle_push_default(const char* old_name, const char* new_name)
{
struct push_default_info push_default = {
old_name, CONFIG_SCOPE_UNKNOWN, STRBUF_INIT, -1 };
- git_config(config_read_push_default, &push_default);
+ repo_config_kvi(the_repository, config_read_push_default, &push_default);
if (push_default.scope >= CONFIG_SCOPE_COMMAND)
; /* pass */
else if (push_default.scope >= CONFIG_SCOPE_LOCAL) {
diff --git a/config.c b/config.c
index 230a98b0631..1b3f534757c 100644
--- a/config.c
+++ b/config.c
@@ -2219,7 +2219,8 @@ int config_with_options(config_fn_t fn, void *data,
return ret;
}
-static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+static void configset_iter(struct config_set *cs, config_fn_t fn,
+ config_kvi_fn_t fn_kvi, void *data)
{
int i, value_index;
struct string_list *values;
@@ -2230,6 +2231,7 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
const char *key;
const char *val;
struct key_value_info *kvi;
+ int ret;
entry = list->items[i].e;
value_index = list->items[i].value_index;
@@ -2239,11 +2241,15 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
val = values->items[value_index].string;
kvi = values->items[value_index].util;
- current_config_kvi = kvi;
- if (fn(key, val, data) < 0)
+ if (!fn_kvi)
+ current_config_kvi = kvi;
+ ret = fn_kvi ? fn_kvi(key, val, kvi, data) :
+ fn(key, val, data);
+ current_config_kvi = NULL;
+
+ if (ret < 0)
git_die_config_linenr(entry->key, kvi->filename,
kvi->linenr);
- current_config_kvi = NULL;
}
}
@@ -2567,7 +2573,13 @@ static void repo_config_clear(struct repository *repo)
void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
git_config_check_init(repo);
- configset_iter(repo->config, fn, data);
+ configset_iter(repo->config, fn, NULL, data);
+}
+
+void repo_config_kvi(struct repository *repo, config_kvi_fn_t fn, void *data)
+{
+ git_config_check_init(repo);
+ configset_iter(repo->config, NULL, fn, data);
}
int repo_config_get_value(struct repository *repo,
@@ -2670,7 +2682,7 @@ void git_protected_config(config_fn_t fn, void *data)
{
if (!protected_config.hash_initialized)
read_protected_config();
- configset_iter(&protected_config, fn, data);
+ configset_iter(&protected_config, fn, NULL, data);
}
/* Functions used historically to read configuration from 'the_repository' */
@@ -3843,14 +3855,6 @@ enum config_scope current_config_scope(void)
return current_parsing_scope;
}
-int current_config_line(void)
-{
- if (current_config_kvi)
- return current_config_kvi->linenr;
- else
- return cf->linenr;
-}
-
int lookup_config(const char **mapping, int nr_mapping, const char *var)
{
int i;
diff --git a/config.h b/config.h
index a9cb01e9405..de5350dbee5 100644
--- a/config.h
+++ b/config.h
@@ -143,6 +143,13 @@ const char *config_origin_type_name(enum config_origin_type type);
*/
typedef int (*config_fn_t)(const char *, const char *, void *);
+/**
+ * Like config_fn_t, but before the callback-specific data we'll get a
+ * "struct key_value_info" indicating the origin of the config.
+ */
+typedef int (*config_kvi_fn_t)(const char *key, const char *var,
+ struct key_value_info *kvi, void *data);
+
int git_default_config(const char *, const char *, void *);
/**
@@ -371,7 +378,6 @@ int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
enum config_scope current_config_scope(void);
const char *current_config_origin_type(void);
const char *current_config_name(void);
-int current_config_line(void);
/*
* Match and parse a config key of the form:
@@ -498,6 +504,7 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
/* Functions for reading a repository's config */
struct repository;
void repo_config(struct repository *repo, config_fn_t fn, void *data);
+void repo_config_kvi(struct repository *repo, config_kvi_fn_t fn, void *data);
int repo_config_get_value(struct repository *repo,
const char *key, const char **value);
const struct string_list *repo_config_get_value_multi(struct repository *repo,
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index 4ba9eb65606..2ef67b18a0b 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -37,7 +37,8 @@
*
*/
-static int iterate_cb(const char *var, const char *value, void *data UNUSED)
+static int iterate_cb(const char *var, const char *value,
+ struct key_value_info *kvi, void *data UNUSED)
{
static int nr;
@@ -46,10 +47,10 @@ static int iterate_cb(const char *var, const char *value, void *data UNUSED)
printf("key=%s\n", var);
printf("value=%s\n", value ? value : "(null)");
- printf("origin=%s\n", current_config_origin_type());
- printf("name=%s\n", current_config_name());
- printf("lno=%d\n", current_config_line());
- printf("scope=%s\n", config_scope_name(current_config_scope()));
+ printf("origin=%s\n", config_origin_type_name(kvi->origin_type));
+ printf("name=%s\n", kvi->filename ? kvi->filename : "");
+ printf("lno=%d\n", kvi->linenr);
+ printf("scope=%s\n", config_scope_name(kvi->scope));
return 0;
}
@@ -174,7 +175,7 @@ int cmd__config(int argc, const char **argv)
goto exit1;
}
} else if (!strcmp(argv[1], "iterate")) {
- git_config(iterate_cb, NULL);
+ repo_config_kvi(the_repository, iterate_cb, NULL);
goto exit0;
}
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 43b7bcd7159..d9659b9c65e 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -853,8 +853,11 @@ test_expect_success 'rename a remote' '
(
cd four &&
git config branch.main.pushRemote origin &&
- GIT_TRACE2_EVENT=$(pwd)/trace \
- git remote rename --progress origin upstream &&
+ GIT_TRACE2_EVENT=$PWD/trace \
+ git remote rename --progress origin upstream 2>warn &&
+ grep -F "The global configuration remote.pushDefault" warn &&
+ grep "/\.gitconfig:2$" warn &&
+ grep "remote '\''origin'\''" warn &&
test_region progress "Renaming remote references" trace &&
grep "pushRemote" .git/config &&
test -z "$(git for-each-ref refs/remotes/origin)" &&
--
2.40.0.rc1.1034.g5867a1b10c5
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [RFC PATCH 5/5] config API: add and use a repo_config_kvi()
2023-03-17 5:01 ` [RFC PATCH 5/5] config API: add and use a repo_config_kvi() Ævar Arnfjörð Bjarmason
@ 2023-03-17 17:17 ` Junio C Hamano
2023-03-17 20:59 ` Jonathan Tan
1 sibling, 0 replies; 72+ messages in thread
From: Junio C Hamano @ 2023-03-17 17:17 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason
Cc: git, Glen Choo, Jonathan Tan, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> Introduce a repo_config_kvi(), which is a repo_config() which calls a
> "config_kvi_fn_t", rather than the "config_fn_t" that repo_config()
> uses.
>
> This allows us to pass along the "struct key_value_info *" directly,
> rather than having the callback grab it from the global
> "current_config_kvi" that we've been maintaining in
> "configset_iter()".
Nice that you can plumb kvi through the callchain but without having
to touch the end-user-visible callback data *cb or the function
signature of the cllaback functions.
> This leaves the other current_config_*() functions. Subsequent commits
> will need to deal with those.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [RFC PATCH 5/5] config API: add and use a repo_config_kvi()
2023-03-17 5:01 ` [RFC PATCH 5/5] config API: add and use a repo_config_kvi() Ævar Arnfjörð Bjarmason
2023-03-17 17:17 ` Junio C Hamano
@ 2023-03-17 20:59 ` Jonathan Tan
1 sibling, 0 replies; 72+ messages in thread
From: Jonathan Tan @ 2023-03-17 20:59 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason
Cc: Jonathan Tan, git, Glen Choo, Junio C Hamano, Jeff King,
Emily Shaffer, Derrick Stolee, Calvin Wan
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> This change is an alternate direction to the topic at [1], this
> expands on the vague suggestions I made in [2] to go in this
> direction.
Are you suggesting that we merge your patches instead of Glen's? (Or
maybe merge your patches and a subset of Glen's, but I'm not sure which
subset.)
> As this shows we can split apart the "config_fn_t", and thus avoid
> having to change the hundreds of existing "config_fn_t" callers. By
> doing this we can already get rid of the current_config_kvi()
> function, as "builtin/remote.c" and "t/helper/test-config.c" were the
> only users of it.
[snip]
> This leaves the other current_config_*() functions. Subsequent commits
> will need to deal with those.
I think we already know that splitting config_fn_t is possible but the
change will be quite widespread.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [RFC PATCH 0/5] bypass config.c global state with configset
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
` (4 preceding siblings ...)
2023-03-17 5:01 ` [RFC PATCH 5/5] config API: add and use a repo_config_kvi() Ævar Arnfjörð Bjarmason
@ 2023-03-17 16:21 ` Junio C Hamano
2023-03-17 16:28 ` Glen Choo
2023-03-17 19:20 ` Glen Choo
7 siblings, 0 replies; 72+ messages in thread
From: Junio C Hamano @ 2023-03-17 16:21 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason
Cc: git, Glen Choo, Jonathan Tan, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> Ævar Arnfjörð Bjarmason (5):
> config.h: move up "struct key_value_info"
> config.c: use "enum config_origin_type", not "int"
> config API: add a config_origin_type_name() helper
> config.c: refactor configset_iter()
> config API: add and use a repo_config_kvi()
I haven't reached the end of 5/5 but it is a shame to see that these
obviously good and trivial clean-up patches like [1-4/5] have to be
buried in an RFC patch and benefit of applying them alone becomes
unclear only because it is done in an area that is actively worked
on by others. The latter is unfortunately inherent to the approach
of "commenting by patches" but hopefully we can see them reappear
when the tree is quiescent.
Thanks.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [RFC PATCH 0/5] bypass config.c global state with configset
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
` (5 preceding siblings ...)
2023-03-17 16:21 ` [RFC PATCH 0/5] bypass config.c global state with configset Junio C Hamano
@ 2023-03-17 16:28 ` Glen Choo
2023-03-17 19:20 ` Glen Choo
7 siblings, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-17 16:28 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason, git
Cc: Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> That libification url
> (https://github.com/git/git/compare/master...chooglen:git:config-lib-parsing)
> doesn't work for me, and I didn't find a branch with that name in your
> published repo. So maybe you've already done all this
> post-libification work...
Argh, sorry, I renamed the branch.
https://github.com/git/git/compare/master...chooglen:git:config/read-without-globals
> ...in any case. This RFC expands a bit on my comments on the v1 (at
> [1] and upthread). It doesn't get all the way there, but with the
> small change in 5/5 we've gotten rid of current_config_line(), the
> 1-4/5 are trivial pre-refactorings to make that diff smaller
> (e.g. moving the "struct key_value_info" around in config.h).
Thanks. I haven't read through it yet, but it sounds promising :)
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [RFC PATCH 0/5] bypass config.c global state with configset
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
` (6 preceding siblings ...)
2023-03-17 16:28 ` Glen Choo
@ 2023-03-17 19:20 ` Glen Choo
2023-03-17 23:32 ` Glen Choo
2023-03-29 11:53 ` Ævar Arnfjörð Bjarmason
7 siblings, 2 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-17 19:20 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason, git
Cc: Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> Maybe it still makes sense to go for this "the_reader" intermediate
> step, but I can't help but think that we could just go for it all in
> one leap, and that you've just got stuck on thinking that you needed
> to change "config_fn_t" for all its callers.
>
> As the 5/5 here shows we have various orthagonal uses of the
> "config_fn_t" in config.c, and can just implement a new callback type
> for the edge cases where we need the file & line info.
>
> This still leave the current_config_name() etc, which
> e.g. builtin/config.c still uses. In your series you've needed to add
> the new "reader" parameter for everything from do_config_from(), but
> if we're doing that can't we instead just go straight to passing a
> "struct key_value_info *" (perhaps with an added "name" field) all the
> way down, replacing "cf->linenr" etc?
In the end state, I also think we should be passing "struct
key_value_info *" around instead of "cf", so I think we are seeing
"the_reader" in the same way (as a transitional state).
I considered the "repo_config_kvi() + config_fn_kvi_t" as well, but I
rejected it (before discussion on the list, whoops) because I didn't
want to add _yet another_ set of parallel config APIs, e.g. we already
have repo_config(), git_config(), configset*(),
git_config_from_file*(). Multiplying that by 2 to add *_kvi() seems like
way too much, especially when it seems clear that our current definition
of config_fn_t has some problems.
Maybe we could deprecate the non-*_kvi(), and have both as a
transitional state? It might work, but I think biting the bullet and
changing config_fn_t would be easier actually.
I'll try applying your series on top of my 1/8 (and maybe 7/8, see next
reply) and extending it to cover "cf" (instead of just
current_config_kvi()) to see whether the *_kvi() approach saves a lot of
unnecessary plumbing. I'd still feel very strongly about getting rid of
all of the non *_kvi() versions, though, but maybe that would happen in
a cleanup topic.
> Similarly, you mention git_parse_int() wanting to report a filename
> and/or line number. I'm aware that it can do that, but it doesn't do
> so in the common case, e.g.:
>
> git -c format.filenameMaxLength=abc log
> fatal: bad numeric config value 'abc' for 'format.filenamemaxlength': invalid unit
>
> And the same goes for writing it to e.g. ~/.gitconfig. It's only if
> you use "git config --file" or similar that we'll report a filename.
That's true, but I think that's a bug, not a feature. See 7/8 [1] where
I addressed it.
1. https://lore.kernel.org/git/3c83d9535a037653c7de2d462a4df3a3c43a9308.1678925506.git.gitgitgadget@gmail.com/
> We can just make config_set_callback() and configset_iter()
> non-static, so e.g. the builtin/config.c caller that implements
> "--show-origin" can keep its config_with_options(...) call, but
> instead of "streaming" the config, it'll buffer it up into a
> configset.
Hm, so to extrapolate, we could make it so that nobody outside of
config.c uses the *config_from_file() APIs directly. Instead, all reads
get buffered up into a configset. That might not be a bad idea. It would
definitely help with some of your goals of config API surface reduction.
This would be friendlier in cases where we were already creating custom
configsets (I know we have some of those, but I don't recall where), but
in cases where we were reading the file directly (e.g.
builtin/config.c), we'd be taking a memory and runtime hit. I'm not sure
how I (or others) feel about that yet.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [RFC PATCH 0/5] bypass config.c global state with configset
2023-03-17 19:20 ` Glen Choo
@ 2023-03-17 23:32 ` Glen Choo
2023-03-29 11:53 ` Ævar Arnfjörð Bjarmason
1 sibling, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-17 23:32 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason, git
Cc: Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan,
Ævar Arnfjörð Bjarmason
Glen Choo <chooglen@google.com> writes:
>> This still leave the current_config_name() etc, which
>> e.g. builtin/config.c still uses. In your series you've needed to add
>> the new "reader" parameter for everything from do_config_from(), but
>> if we're doing that can't we instead just go straight to passing a
>> "struct key_value_info *" (perhaps with an added "name" field) all the
>> way down, replacing "cf->linenr" etc?
>
> In the end state, I also think we should be passing "struct
> key_value_info *" around instead of "cf", so I think we are seeing
> "the_reader" in the same way (as a transitional state).
>
> I considered the "repo_config_kvi() + config_fn_kvi_t" as well, but I
> rejected it (before discussion on the list, whoops) because I didn't
> want to add _yet another_ set of parallel config APIs, e.g. we already
> have repo_config(), git_config(), configset*(),
> git_config_from_file*(). Multiplying that by 2 to add *_kvi() seems like
> way too much, especially when it seems clear that our current definition
> of config_fn_t has some problems.
>
> Maybe we could deprecate the non-*_kvi(), and have both as a
> transitional state? It might work, but I think biting the bullet and
> changing config_fn_t would be easier actually.
>
> I'll try applying your series on top of my 1/8 (and maybe 7/8, see next
> reply) and extending it to cover "cf" (instead of just
> current_config_kvi()) to see whether the *_kvi() approach saves a lot of
> unnecessary plumbing. I'd still feel very strongly about getting rid of
> all of the non *_kvi() versions, though, but maybe that would happen in
> a cleanup topic.
As mentioned above, I think having both "config_fn_t" and
config_kvi_fn_t" would make sense if we found a good way to extend it to
the functions that use "cf" (parsing config syntax), not the ones that
use "current_config_kvi" (iterating a config set).
I think technical difficulty is not a barrier here:
- Constructing a "struct key_value_info" out of "cf" is trivial
- Supporting both "config_fn_t" and "config_kvi_fn_t" with one
implementation is also doable in theory. One approach would be to use
only *_kvi() internally, and then adapt the external "config_fn_t"
like:
struct adapt_nonkvi {
config_fn_t fn;
void *data;
};
static int adapt_nonkvi_fn(const char *key, const char *value,
struct key_value_info *kvi UNUSED, void *cb)
{
struct adapt_nonkvi *adapt = cb;
return adapt->fn(key, value, adapt->data);
}
The real cost is that there are so many functions we'd need to adapt (I
counted 12 functions that accept config_fn_t in config.h). I think I got
through about 30% of it before thinking that it was too much work to try
to avoid adjusting config_fn_t.
I still strongly believe that we shouldn't have both config_fn_t and
config_kvi_fn_t in the long run, and we should converge on one.
It's plausible that if we support both as an intermediate state, we'll
never do the actual cleanup, so I think the extra cost is not worth it.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [RFC PATCH 0/5] bypass config.c global state with configset
2023-03-17 19:20 ` Glen Choo
2023-03-17 23:32 ` Glen Choo
@ 2023-03-29 11:53 ` Ævar Arnfjörð Bjarmason
1 sibling, 0 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-29 11:53 UTC (permalink / raw)
To: Glen Choo
Cc: git, Jonathan Tan, Junio C Hamano, Jeff King, Emily Shaffer,
Derrick Stolee, Calvin Wan
On Fri, Mar 17 2023, Glen Choo wrote:
> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> [...]
>> Similarly, you mention git_parse_int() wanting to report a filename
>> and/or line number. I'm aware that it can do that, but it doesn't do
>> so in the common case, e.g.:
>>
>> git -c format.filenameMaxLength=abc log
>> fatal: bad numeric config value 'abc' for 'format.filenamemaxlength': invalid unit
>>
>> And the same goes for writing it to e.g. ~/.gitconfig. It's only if
>> you use "git config --file" or similar that we'll report a filename.
>
> That's true, but I think that's a bug, not a feature. See 7/8 [1] where
> I addressed it.
>
> 1. https://lore.kernel.org/git/3c83d9535a037653c7de2d462a4df3a3c43a9308.1678925506.git.gitgitgadget@gmail.com/
>
>> We can just make config_set_callback() and configset_iter()
>> non-static, so e.g. the builtin/config.c caller that implements
>> "--show-origin" can keep its config_with_options(...) call, but
>> instead of "streaming" the config, it'll buffer it up into a
>> configset.
>
> Hm, so to extrapolate, we could make it so that nobody outside of
> config.c uses the *config_from_file() APIs directly. Instead, all reads
> get buffered up into a configset. That might not be a bad idea. It would
> definitely help with some of your goals of config API surface reduction.
>
> This would be friendlier in cases where we were already creating custom
> configsets (I know we have some of those, but I don't recall where), but
> in cases where we were reading the file directly (e.g.
> builtin/config.c), we'd be taking a memory and runtime hit. I'm not sure
> how I (or others) feel about that yet.
I'm pretty sure that in the end this wouldn't matter, i.e. the time it
takes to parse the config is trivial, and the users of these APIs like
"git config -l --show-origin" aren't performance-senitive.
But for the general case & if you're concerned about this a trivial
addition on top of what I suggested would be to pass a streaming
callback to config_set_callback(), i.e. you could get the values you'd
get from configset_iter() as we parse them.
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v3 0/8] config.c: use struct for config reading state
2023-03-16 0:11 ` [PATCH v2 0/8] " Glen Choo via GitGitGadget
` (10 preceding siblings ...)
2023-03-17 5:01 ` [RFC PATCH 0/5] bypass config.c global state with configset Ævar Arnfjörð Bjarmason
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 1/8] config.c: plumb config_source through static fns Glen Choo via GitGitGadget
` (8 more replies)
11 siblings, 9 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo
Note to Junio: 8/8 (which renames "cs" -> "set") conflicts with
ab/config-multi-and-nonbool. I previously said that I'd rebase this, but
presumably a remerge-diff is more ergonomic + flexible (let me know if I'm
mistaken), so I'll send a remerge-diff in a reply (I don't trust GGG not to
mangle the patch :/).
After sending this out, I'll see if cocci can make it easy enough to change
config_fn_t. If so, I'll probably go with that approach for the future
'libified' patches, which should also line up nicely with what Ævar
suggested.
= Changes in v3
* Adjust the *_push() function to be unconditional (like the *_pop()
function)
* Various commit message and comment cleanups
= Changes in v2
* To reduce churn, don't rename "struct config_source cf" to "cs" early in
the series. Instead, rename the global "cf" to "cf_global", and leave the
existing "cf"s untouched.
Introduce 8/8 to get rid of the confusing acronym "struct config_source
cf", but I don't mind ejecting it if it's too much churn.
* Adjust 5/8 so to pass "struct config_reader" through args instead of
"*data". v1 made the mistake of thinking "*data" was being passed to a
callback, but it wasn't.
* Add a 7/8 to fix a bug in die_bad_number(). I included this because it
overlaps a little bit with the refactor here, but I don't mind ejecting
this either.
* Assorted BUG() message clarifications.
= Description
This series prepares for config.[ch] to be libified as as part of the
libification effort that Emily described in [1]. One of the first goals is
to read config from a file, but the trouble with how config.c is written
today is that all reading operations rely on global state, so before turning
that into a library, we'd want to make that state non-global.
This series doesn't remove all of the global state, but it gets us closer to
that goal by extracting the global config reading state into "struct
config_reader" and plumbing it through the config reading machinery. This
makes it possible to reuse the config machinery without global state, and to
enforce some constraints on "struct config_reader", which makes it more
predictable and easier to remove in the long run.
This process is very similar to how we've plumbed "struct repository" and
other 'context objects' in the past, except:
* The global state (named "the_reader") for the git process lives in a
config.c static variable, and not on "the_repository". See 3/6 for the
rationale.
* I've stopped short of adding "struct config_reader" to config.h public
functions, since that would affect non-config.c callers.
Additionally, I've included a bugfix for die_bad_number() that became clear
as I did this refactor.
= Leftover bits
We still need a global "the_reader" because config callbacks are reading
auxiliary information about the config (e.g. line number, file name) via
global functions (e.g. current_config_line(), current_config_name()). This
is either because the callback uses this info directly (like
builtin/config.c printing the filename and scope of the value) or for error
reporting (like git_parse_int() reporting the filename of the value it
failed to parse).
If we had a way to plumb the state from "struct config_reader" to the config
callback functions, we could initialize "struct config_reader" in the config
machinery whenever we read config (instead of asking the caller to
initialize "struct config_reader" themselves), and config reading could
become a thread-safe operation. There isn't an obvious way to plumb this
state to config callbacks without adding an additional arg to config_fn_t
and incurring a lot of churn, but if we start replacing "config_fn_t" with
the configset API (which we've independently wanted for some time), this may
become feasible.
And if we do this, "struct config_reader" itself will probably become
obsolete, because we'd be able to plumb only the relevant state for the
current operation, e.g. if we are parsing a config file, we'd pass only the
config file parsing state, instead of "struct config_reader", which also
contains config set iterating state. In such a scenario, we'd probably want
to pass "struct key_value_info" to the config callback, since that's all the
callback should be interested in anyway. Interestingly, this was proposed by
Junio back in [2], and we didn't do this back then out of concern for the
churn (just like in v1).
[1]
https://lore.kernel.org/git/CAJoAoZ=Cig_kLocxKGax31sU7Xe4==BGzC__Bg2_pr7krNq6MA@mail.gmail.com
[2]
https://lore.kernel.org/git/CAPc5daV6bdUKS-ExHmpT4Ppy2S832NXoyPw7aOLP7fG=WrBPgg@mail.gmail.com/
Glen Choo (8):
config.c: plumb config_source through static fns
config.c: don't assign to "cf_global" directly
config.c: create config_reader and the_reader
config.c: plumb the_reader through callbacks
config.c: remove current_config_kvi
config.c: remove current_parsing_scope
config: report cached filenames in die_bad_number()
config.c: rename "struct config_source cf"
config.c | 584 ++++++++++++++++++++++++-----------------
config.h | 1 +
t/helper/test-config.c | 17 ++
t/t1308-config-set.sh | 9 +
4 files changed, 368 insertions(+), 243 deletions(-)
base-commit: dadc8e6dacb629f46aee39bde90b6f09b73722eb
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1463%2Fchooglen%2Fconfig%2Fstructify-reading-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1463/chooglen/config/structify-reading-v3
Pull-Request: https://github.com/git/git/pull/1463
Range-diff vs v2:
1: 75d0f0efb79 = 1: 75d0f0efb79 config.c: plumb config_source through static fns
2: 7555da0b0e0 ! 2: 39db7d8596a config.c: don't assign to "cf_global" directly
@@ config.c: static struct key_value_info *current_config_kvi;
+static inline void config_reader_push_source(struct config_source *top)
+{
-+ if (cf_global)
-+ top->prev = cf_global;
++ top->prev = cf_global;
+ cf_global = top;
+}
+
3: 4347896f0a4 ! 3: 72774fd08f3 config.c: create config_reader and the_reader
@@ config.c: static struct key_value_info *current_config_kvi;
+static inline void config_reader_push_source(struct config_reader *reader,
+ struct config_source *top)
{
-- if (cf_global)
-- top->prev = cf_global;
+- top->prev = cf_global;
- cf_global = top;
-+ if (reader->source)
-+ top->prev = reader->source;
++ top->prev = reader->source;
+ reader->source = top;
+ /* FIXME remove this when cf_global is removed. */
+ cf_global = reader->source;
@@ config.c: static struct key_value_info *current_config_kvi;
- cf_global = cf_global->prev;
+ ret = reader->source;
+ reader->source = reader->source->prev;
-+ /* FIXME remove this when cf is removed. */
++ /* FIXME remove this when cf_global is removed. */
+ cf_global = reader->source;
return ret;
}
4: 22b69971749 ! 4: e02dddd560f config.c: plumb the_reader through callbacks
@@ config.c: static struct config_reader the_reader;
/*
@@ config.c: static inline void config_reader_push_source(struct config_reader *reader,
- if (reader->source)
- top->prev = reader->source;
+ {
+ top->prev = reader->source;
reader->source = top;
- /* FIXME remove this when cf_global is removed. */
- cf_global = reader->source;
@@ config.c: static inline struct config_source *config_reader_pop_source(struct co
BUG("tried to pop config source, but we weren't reading config");
ret = reader->source;
reader->source = reader->source->prev;
-- /* FIXME remove this when cf is removed. */
+- /* FIXME remove this when cf_global is removed. */
- cf_global = reader->source;
return ret;
}
5: afb6e3e318d ! 5: c79eaf74f89 config.c: remove current_config_kvi
@@ config.c: static enum config_scope current_parsing_scope;
{
+ if (reader->config_kvi)
+ BUG("source should not be set while iterating a config set");
- if (reader->source)
- top->prev = reader->source;
+ top->prev = reader->source;
reader->source = top;
+ }
@@ config.c: static inline struct config_source *config_reader_pop_source(struct config_reade
return ret;
}
6: a57e35163ae = 6: 05d9ffa21f6 config.c: remove current_parsing_scope
7: 3c83d9535a0 ! 7: eb843e6f08d config: report cached filenames in die_bad_number()
@@ Commit message
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(). Refactoring is needed because "git config
- --get[-regexp] --type=int" parses the int value _after_ parsing the
- config file, which will run into the BUG().
+ 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().
- Also, plumb "struct config_reader" into the new functions. This isn't
- necessary per se, but this generalizes better, so it might help us avoid
- yet another refactor.
+ Since the refactored functions aren't public, they use "struct
+ config_reader".
1. https://lore.kernel.org/git/20160518223712.GA18317@sigill.intra.peff.net/
8: 9aec9092fdf = 8: ab800aa104c config.c: rename "struct config_source cf"
--
gitgitgadget
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v3 1/8] config.c: plumb config_source through static fns
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 2/8] config.c: don't assign to "cf_global" directly Glen Choo via GitGitGadget
` (7 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
This reduces the direct dependence on the global "struct config_source",
which will make it easier to remove in a later commit.
To minimize the changes we need to make, we rename the current variable
from "cf" to "cf_global", and the plumbed arg uses the old name "cf".
This is a little unfortunate, since we now have the confusingly named
"struct config_source cf" everywhere (which is a holdover from before
4d8dd1494e (config: make parsing stack struct independent from actual
data source, 2013-07-12), when the struct used to be called
"config_file"), but we will rename "cf" to "cs" by the end of the
series.
In some cases (public functions and config callback functions), there
isn't an obvious way to plumb "struct config_source" through function
args. As a workaround, add references to "cf_global" that we'll address
in later commits.
The remaining references to "cf_global" are direct assignments to
"cf_global", which we'll also address in a later commit.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 153 ++++++++++++++++++++++++++++++-------------------------
1 file changed, 84 insertions(+), 69 deletions(-)
diff --git a/config.c b/config.c
index 00090a32fc3..e4a76739365 100644
--- a/config.c
+++ b/config.c
@@ -54,8 +54,8 @@ struct config_source {
* These variables record the "current" config source, which
* can be accessed by parsing callbacks.
*
- * The "cf" variable will be non-NULL only when we are actually parsing a real
- * config source (file, blob, cmdline, etc).
+ * The "cf_global" variable will be non-NULL only when we are actually
+ * parsing a real config source (file, blob, cmdline, etc).
*
* The "current_config_kvi" variable will be non-NULL only when we are feeding
* cached config from a configset into a callback.
@@ -66,15 +66,16 @@ struct config_source {
* or it's a function which can be reused for non-config purposes, and should
* fall back to some sane behavior).
*/
-static struct config_source *cf;
+static struct config_source *cf_global;
static struct key_value_info *current_config_kvi;
/*
* Similar to the variables above, this gives access to the "scope" of the
* current value (repo, global, etc). For cached values, it can be found via
* the current_config_kvi as above. During parsing, the current value can be
- * found in this variable. It's not part of "cf" because it transcends a single
- * file (i.e., a file included from .git/config is still in "repo" scope).
+ * found in this variable. It's not part of "cf_global" because it transcends a
+ * single file (i.e., a file included from .git/config is still in "repo"
+ * scope).
*/
static enum config_scope current_parsing_scope;
@@ -156,7 +157,8 @@ static const char include_depth_advice[] = N_(
"from\n"
" %s\n"
"This might be due to circular includes.");
-static int handle_path_include(const char *path, struct config_include_data *inc)
+static int handle_path_include(struct config_source *cf, const char *path,
+ struct config_include_data *inc)
{
int ret = 0;
struct strbuf buf = STRBUF_INIT;
@@ -210,7 +212,8 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
strbuf_addstr(pat, "**");
}
-static int prepare_include_condition_pattern(struct strbuf *pat)
+static int prepare_include_condition_pattern(struct config_source *cf,
+ struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
char *expanded;
@@ -245,7 +248,8 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
return prefix;
}
-static int include_by_gitdir(const struct config_options *opts,
+static int include_by_gitdir(struct config_source *cf,
+ const struct config_options *opts,
const char *cond, size_t cond_len, int icase)
{
struct strbuf text = STRBUF_INIT;
@@ -261,7 +265,7 @@ static int include_by_gitdir(const struct config_options *opts,
strbuf_realpath(&text, git_dir, 1);
strbuf_add(&pattern, cond, cond_len);
- prefix = prepare_include_condition_pattern(&pattern);
+ prefix = prepare_include_condition_pattern(cf, &pattern);
again:
if (prefix < 0)
@@ -342,14 +346,14 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct config_source *store_cf = cf;
+ struct config_source *store_cf = cf_global;
struct key_value_info *store_kvi = current_config_kvi;
enum config_scope store_scope = current_parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- cf = NULL;
+ cf_global = NULL;
current_config_kvi = NULL;
current_parsing_scope = 0;
@@ -357,7 +361,7 @@ static void populate_remote_urls(struct config_include_data *inc)
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- cf = store_cf;
+ cf_global = store_cf;
current_config_kvi = store_kvi;
current_parsing_scope = store_scope;
}
@@ -406,15 +410,16 @@ static int include_by_remote_url(struct config_include_data *inc,
inc->remote_urls);
}
-static int include_condition_is_true(struct config_include_data *inc,
+static int include_condition_is_true(struct config_source *cf,
+ struct config_include_data *inc,
const char *cond, size_t cond_len)
{
const struct config_options *opts = inc->opts;
if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
- return include_by_gitdir(opts, cond, cond_len, 0);
+ return include_by_gitdir(cf, opts, cond, cond_len, 0);
else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
- return include_by_gitdir(opts, cond, cond_len, 1);
+ return include_by_gitdir(cf, opts, cond, cond_len, 1);
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
return include_by_branch(cond, cond_len);
else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -441,16 +446,16 @@ static int git_config_include(const char *var, const char *value, void *data)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(value, inc);
+ ret = handle_path_include(cf_global, value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(inc, cond, cond_len) &&
+ cond && include_condition_is_true(cf_global, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(value, inc);
+ ret = handle_path_include(cf_global, value, inc);
inc->fn = old_fn;
}
@@ -713,9 +718,9 @@ int git_config_from_parameters(config_fn_t fn, void *data)
struct config_source source;
memset(&source, 0, sizeof(source));
- source.prev = cf;
+ source.prev = cf_global;
source.origin_type = CONFIG_ORIGIN_CMDLINE;
- cf = &source;
+ cf_global = &source;
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
@@ -773,11 +778,11 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- cf = source.prev;
+ cf_global = source.prev;
return ret;
}
-static int get_next_char(void)
+static int get_next_char(struct config_source *cf)
{
int c = cf->do_fgetc(cf);
@@ -813,13 +818,13 @@ static int get_next_char(void)
return c;
}
-static char *parse_value(void)
+static char *parse_value(struct config_source *cf)
{
int quote = 0, comment = 0, space = 0;
strbuf_reset(&cf->value);
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cf);
if (c == '\n') {
if (quote) {
cf->linenr--;
@@ -843,7 +848,7 @@ static char *parse_value(void)
for (; space; space--)
strbuf_addch(&cf->value, ' ');
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cf);
switch (c) {
case '\n':
continue;
@@ -874,7 +879,8 @@ static char *parse_value(void)
}
}
-static int get_value(config_fn_t fn, void *data, struct strbuf *name)
+static int get_value(struct config_source *cf, config_fn_t fn, void *data,
+ struct strbuf *name)
{
int c;
char *value;
@@ -882,7 +888,7 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
/* Get the full name */
for (;;) {
- c = get_next_char();
+ c = get_next_char(cf);
if (cf->eof)
break;
if (!iskeychar(c))
@@ -891,13 +897,13 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
}
while (c == ' ' || c == '\t')
- c = get_next_char();
+ c = get_next_char(cf);
value = NULL;
if (c != '\n') {
if (c != '=')
return -1;
- value = parse_value();
+ value = parse_value(cf);
if (!value)
return -1;
}
@@ -913,13 +919,14 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
return ret;
}
-static int get_extended_base_var(struct strbuf *name, int c)
+static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
+ int c)
{
cf->subsection_case_sensitive = 0;
do {
if (c == '\n')
goto error_incomplete_line;
- c = get_next_char();
+ c = get_next_char(cf);
} while (isspace(c));
/* We require the format to be '[base "extension"]' */
@@ -928,13 +935,13 @@ static int get_extended_base_var(struct strbuf *name, int c)
strbuf_addch(name, '.');
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cf);
if (c == '\n')
goto error_incomplete_line;
if (c == '"')
break;
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cf);
if (c == '\n')
goto error_incomplete_line;
}
@@ -942,7 +949,7 @@ static int get_extended_base_var(struct strbuf *name, int c)
}
/* Final ']' */
- if (get_next_char() != ']')
+ if (get_next_char(cf) != ']')
return -1;
return 0;
error_incomplete_line:
@@ -950,17 +957,17 @@ error_incomplete_line:
return -1;
}
-static int get_base_var(struct strbuf *name)
+static int get_base_var(struct config_source *cf, struct strbuf *name)
{
cf->subsection_case_sensitive = 1;
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cf);
if (cf->eof)
return -1;
if (c == ']')
return 0;
if (isspace(c))
- return get_extended_base_var(name, c);
+ return get_extended_base_var(cf, name, c);
if (!iskeychar(c) && c != '.')
return -1;
strbuf_addch(name, tolower(c));
@@ -973,7 +980,8 @@ struct parse_event_data {
const struct config_options *opts;
};
-static int do_event(enum config_event_t type, struct parse_event_data *data)
+static int do_event(struct config_source *cf, enum config_event_t type,
+ struct parse_event_data *data)
{
size_t offset;
@@ -1004,8 +1012,8 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
return 0;
}
-static int git_parse_source(config_fn_t fn, void *data,
- const struct config_options *opts)
+static int git_parse_source(struct config_source *cf, config_fn_t fn,
+ void *data, const struct config_options *opts)
{
int comment = 0;
size_t baselen = 0;
@@ -1024,7 +1032,7 @@ static int git_parse_source(config_fn_t fn, void *data,
for (;;) {
int c;
- c = get_next_char();
+ c = get_next_char(cf);
if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their
@@ -1042,11 +1050,11 @@ static int git_parse_source(config_fn_t fn, void *data,
}
if (c == '\n') {
if (cf->eof) {
- if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_EOF, &event_data) < 0)
return -1;
return 0;
}
- if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
comment = 0;
continue;
@@ -1054,23 +1062,23 @@ static int git_parse_source(config_fn_t fn, void *data,
if (comment)
continue;
if (isspace(c)) {
- if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
continue;
}
if (c == '#' || c == ';') {
- if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_COMMENT, &event_data) < 0)
return -1;
comment = 1;
continue;
}
if (c == '[') {
- if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_SECTION, &event_data) < 0)
return -1;
/* Reset prior to determining a new stem */
strbuf_reset(var);
- if (get_base_var(var) < 0 || var->len < 1)
+ if (get_base_var(cf, var) < 0 || var->len < 1)
break;
strbuf_addch(var, '.');
baselen = var->len;
@@ -1079,7 +1087,7 @@ static int git_parse_source(config_fn_t fn, void *data,
if (!isalpha(c))
break;
- if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_ENTRY, &event_data) < 0)
return -1;
/*
@@ -1089,11 +1097,11 @@ static int git_parse_source(config_fn_t fn, void *data,
*/
strbuf_setlen(var, baselen);
strbuf_addch(var, tolower(c));
- if (get_value(fn, data, var) < 0)
+ if (get_value(cf, fn, data, var) < 0)
break;
}
- if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
+ if (do_event(cf, CONFIG_EVENT_ERROR, &event_data) < 0)
return -1;
switch (cf->origin_type) {
@@ -1266,7 +1274,8 @@ int git_parse_ssize_t(const char *value, ssize_t *ret)
}
NORETURN
-static void die_bad_number(const char *name, const char *value)
+static void die_bad_number(struct config_source *cf, const char *name,
+ const char *value)
{
const char *error_type = (errno == ERANGE) ?
N_("out of range") : N_("invalid unit");
@@ -1304,7 +1313,7 @@ int git_config_int(const char *name, const char *value)
{
int ret;
if (!git_parse_int(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1312,7 +1321,7 @@ int64_t git_config_int64(const char *name, const char *value)
{
int64_t ret;
if (!git_parse_int64(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1320,7 +1329,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1328,7 +1337,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(name, value);
+ die_bad_number(cf_global, name, value);
return ret;
}
@@ -1940,20 +1949,20 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
int ret;
/* push config-file parsing state stack */
- top->prev = cf;
+ top->prev = cf_global;
top->linenr = 1;
top->eof = 0;
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- cf = top;
+ cf_global = top;
- ret = git_parse_source(fn, data, opts);
+ ret = git_parse_source(top, fn, data, opts);
/* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- cf = top->prev;
+ cf_global = top->prev;
return ret;
}
@@ -2334,12 +2343,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
- if (!cf)
+ if (!cf_global)
BUG("configset_add_value has no source");
- if (cf->name) {
- kv_info->filename = strintern(cf->name);
- kv_info->linenr = cf->linenr;
- kv_info->origin_type = cf->origin_type;
+ if (cf_global->name) {
+ kv_info->filename = strintern(cf_global->name);
+ kv_info->linenr = cf_global->linenr;
+ kv_info->origin_type = cf_global->origin_type;
} else {
/* for values read from `git_config_from_parameters()` */
kv_info->filename = NULL;
@@ -2891,6 +2900,12 @@ static int store_aux_event(enum config_event_t type,
size_t begin, size_t end, void *data)
{
struct config_store_data *store = data;
+ /*
+ * FIXME Keep using "cf" so that we can avoid rewrapping a
+ * really long line below. Remove this when "cf" gets plumbed
+ * correctly.
+ */
+ struct config_source *cf = cf_global;
ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
store->parsed[store->parsed_nr].begin = begin;
@@ -3771,8 +3786,8 @@ const char *current_config_origin_type(void)
int type;
if (current_config_kvi)
type = current_config_kvi->origin_type;
- else if(cf)
- type = cf->origin_type;
+ else if(cf_global)
+ type = cf_global->origin_type;
else
BUG("current_config_origin_type called outside config callback");
@@ -3817,8 +3832,8 @@ const char *current_config_name(void)
const char *name;
if (current_config_kvi)
name = current_config_kvi->filename;
- else if (cf)
- name = cf->name;
+ else if (cf_global)
+ name = cf_global->name;
else
BUG("current_config_name called outside config callback");
return name ? name : "";
@@ -3837,7 +3852,7 @@ int current_config_line(void)
if (current_config_kvi)
return current_config_kvi->linenr;
else
- return cf->linenr;
+ return cf_global->linenr;
}
int lookup_config(const char **mapping, int nr_mapping, const char *var)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v3 2/8] config.c: don't assign to "cf_global" directly
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 1/8] config.c: plumb config_source through static fns Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 3/8] config.c: create config_reader and the_reader Glen Choo via GitGitGadget
` (6 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
To make "cf_global" easier to remove, replace all direct assignments to
it with function calls. This refactor has an additional maintainability
benefit: all of these functions were manually implementing stack
pop/push semantics on "struct config_source", so replacing them with
function calls allows us to only implement this logic once.
In this process, perform some now-obvious clean ups:
- Drop some unnecessary "cf_global" assignments in
populate_remote_urls(). Since it was introduced in 399b198489 (config:
include file if remote URL matches a glob, 2022-01-18), it has stored
and restored the value of "cf_global" to ensure that it doesn't get
accidentally mutated. However, this was never necessary since
"do_config_from()" already pushes/pops "cf_global" further down the
call chain.
- Zero out every "struct config_source" with a dedicated initializer.
This matters because the "struct config_source" is assigned to
"cf_global" and we later 'pop the stack' by assigning "cf_global =
cf_global->prev", but "cf_global->prev" could be pointing to
uninitialized garbage.
Fortunately, this has never bothered us since we never try to read
"cf_global" except while iterating through config, in which case,
"cf_global" is either set to a sensible value (when parsing a file),
or it is ignored (when iterating a configset). Later in the series,
zero-ing out memory will also let us enforce the constraint that
"cf_global" and "current_config_kvi" are never non-NULL together.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 37 ++++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/config.c b/config.c
index e4a76739365..6627fad71cf 100644
--- a/config.c
+++ b/config.c
@@ -49,6 +49,7 @@ struct config_source {
int (*do_ungetc)(int c, struct config_source *conf);
long (*do_ftell)(struct config_source *c);
};
+#define CONFIG_SOURCE_INIT { 0 }
/*
* These variables record the "current" config source, which
@@ -79,6 +80,22 @@ static struct key_value_info *current_config_kvi;
*/
static enum config_scope current_parsing_scope;
+static inline void config_reader_push_source(struct config_source *top)
+{
+ top->prev = cf_global;
+ cf_global = top;
+}
+
+static inline struct config_source *config_reader_pop_source()
+{
+ struct config_source *ret;
+ if (!cf_global)
+ BUG("tried to pop config source, but we weren't reading config");
+ ret = cf_global;
+ cf_global = cf_global->prev;
+ return ret;
+}
+
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -346,14 +363,12 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct config_source *store_cf = cf_global;
struct key_value_info *store_kvi = current_config_kvi;
enum config_scope store_scope = current_parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- cf_global = NULL;
current_config_kvi = NULL;
current_parsing_scope = 0;
@@ -361,7 +376,6 @@ static void populate_remote_urls(struct config_include_data *inc)
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- cf_global = store_cf;
current_config_kvi = store_kvi;
current_parsing_scope = store_scope;
}
@@ -715,12 +729,10 @@ int git_config_from_parameters(config_fn_t fn, void *data)
struct strvec to_free = STRVEC_INIT;
int ret = 0;
char *envw = NULL;
- struct config_source source;
+ struct config_source source = CONFIG_SOURCE_INIT;
- memset(&source, 0, sizeof(source));
- source.prev = cf_global;
source.origin_type = CONFIG_ORIGIN_CMDLINE;
- cf_global = &source;
+ config_reader_push_source(&source);
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
@@ -778,7 +790,7 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- cf_global = source.prev;
+ config_reader_pop_source();
return ret;
}
@@ -1949,20 +1961,19 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
int ret;
/* push config-file parsing state stack */
- top->prev = cf_global;
top->linenr = 1;
top->eof = 0;
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- cf_global = top;
+ config_reader_push_source(top);
ret = git_parse_source(top, fn, data, opts);
/* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- cf_global = top->prev;
+ config_reader_pop_source();
return ret;
}
@@ -1972,7 +1983,7 @@ static int do_config_from_file(config_fn_t fn,
const char *name, const char *path, FILE *f,
void *data, const struct config_options *opts)
{
- struct config_source top;
+ struct config_source top = CONFIG_SOURCE_INIT;
int ret;
top.u.file = f;
@@ -2024,7 +2035,7 @@ int git_config_from_mem(config_fn_t fn,
const char *name, const char *buf, size_t len,
void *data, const struct config_options *opts)
{
- struct config_source top;
+ struct config_source top = CONFIG_SOURCE_INIT;
top.u.buf.buf = buf;
top.u.buf.len = len;
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v3 3/8] config.c: create config_reader and the_reader
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 1/8] config.c: plumb config_source through static fns Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 2/8] config.c: don't assign to "cf_global" directly Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-29 10:41 ` Ævar Arnfjörð Bjarmason
2023-03-28 17:51 ` [PATCH v3 4/8] config.c: plumb the_reader through callbacks Glen Choo via GitGitGadget
` (5 subsequent siblings)
8 siblings, 1 reply; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
Create "struct config_reader" to hold the state of the config source
currently being read. Then, create a static instance of it,
"the_reader", and use "the_reader.source" to replace references to
"cf_global" in public functions.
This doesn't create much immediate benefit (since we're mostly replacing
static variables with a bigger static variable), but it prepares us for
a future where this state doesn't have to be global; "struct
config_reader" (or a similar struct) could be provided by the caller, or
constructed internally by a function like "do_config_from()".
A more typical approach would be to put this struct on "the_repository",
but that's a worse fit for this use case since config reading is not
scoped to a repository. E.g. we can read config before the repository is
known ("read_very_early_config()"), blatantly ignore the repo
("read_protected_config()"), or read only from a file
("git_config_from_file()"). This is especially evident in t5318 and
t9210, where test-tool and scalar parse config but don't fully
initialize "the_repository".
We could have also replaced the references to "cf_global" in callback
functions (which are the only ones left), but we'll eventually plumb
"the_reader" through the callback "*data" arg, so that would be
unnecessary churn. Until we remove "cf_global" altogether, add logic to
"config_reader_*_source()" to keep "cf_global" and "the_reader.source"
in sync.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 82 +++++++++++++++++++++++++++++++++++---------------------
1 file changed, 51 insertions(+), 31 deletions(-)
diff --git a/config.c b/config.c
index 6627fad71cf..3a28b397c4d 100644
--- a/config.c
+++ b/config.c
@@ -51,6 +51,16 @@ struct config_source {
};
#define CONFIG_SOURCE_INIT { 0 }
+struct config_reader {
+ struct config_source *source;
+};
+/*
+ * Where possible, prefer to accept "struct config_reader" as an arg than to use
+ * "the_reader". "the_reader" should only be used if that is infeasible, e.g. in
+ * a public function.
+ */
+static struct config_reader the_reader;
+
/*
* These variables record the "current" config source, which
* can be accessed by parsing callbacks.
@@ -66,6 +76,9 @@ struct config_source {
* at the variables, it's either a bug for it to be called in the first place,
* or it's a function which can be reused for non-config purposes, and should
* fall back to some sane behavior).
+ *
+ * FIXME "cf_global" has been replaced by "the_reader.source", remove
+ * "cf_global" once we plumb "the_reader" through all of the callback functions.
*/
static struct config_source *cf_global;
static struct key_value_info *current_config_kvi;
@@ -80,19 +93,24 @@ static struct key_value_info *current_config_kvi;
*/
static enum config_scope current_parsing_scope;
-static inline void config_reader_push_source(struct config_source *top)
+static inline void config_reader_push_source(struct config_reader *reader,
+ struct config_source *top)
{
- top->prev = cf_global;
- cf_global = top;
+ top->prev = reader->source;
+ reader->source = top;
+ /* FIXME remove this when cf_global is removed. */
+ cf_global = reader->source;
}
-static inline struct config_source *config_reader_pop_source()
+static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
{
struct config_source *ret;
- if (!cf_global)
+ if (!reader->source)
BUG("tried to pop config source, but we weren't reading config");
- ret = cf_global;
- cf_global = cf_global->prev;
+ ret = reader->source;
+ reader->source = reader->source->prev;
+ /* FIXME remove this when cf_global is removed. */
+ cf_global = reader->source;
return ret;
}
@@ -732,7 +750,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
struct config_source source = CONFIG_SOURCE_INIT;
source.origin_type = CONFIG_ORIGIN_CMDLINE;
- config_reader_push_source(&source);
+ config_reader_push_source(&the_reader, &source);
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
@@ -790,7 +808,7 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- config_reader_pop_source();
+ config_reader_pop_source(&the_reader);
return ret;
}
@@ -1325,7 +1343,7 @@ int git_config_int(const char *name, const char *value)
{
int ret;
if (!git_parse_int(value, &ret))
- die_bad_number(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1333,7 +1351,7 @@ int64_t git_config_int64(const char *name, const char *value)
{
int64_t ret;
if (!git_parse_int64(value, &ret))
- die_bad_number(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1341,7 +1359,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
- die_bad_number(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1349,7 +1367,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(cf_global, name, value);
+ die_bad_number(the_reader.source, name, value);
return ret;
}
@@ -1955,7 +1973,8 @@ int git_default_config(const char *var, const char *value, void *cb)
* fgetc, ungetc, ftell of top need to be initialized before calling
* this function.
*/
-static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
+static int do_config_from(struct config_reader *reader,
+ struct config_source *top, config_fn_t fn, void *data,
const struct config_options *opts)
{
int ret;
@@ -1966,22 +1985,23 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- config_reader_push_source(top);
+ config_reader_push_source(reader, top);
ret = git_parse_source(top, fn, data, opts);
/* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- config_reader_pop_source();
+ config_reader_pop_source(reader);
return ret;
}
-static int do_config_from_file(config_fn_t fn,
- const enum config_origin_type origin_type,
- const char *name, const char *path, FILE *f,
- void *data, const struct config_options *opts)
+static int do_config_from_file(struct config_reader *reader,
+ config_fn_t fn,
+ const enum config_origin_type origin_type,
+ const char *name, const char *path, FILE *f,
+ void *data, const struct config_options *opts)
{
struct config_source top = CONFIG_SOURCE_INIT;
int ret;
@@ -1996,15 +2016,15 @@ static int do_config_from_file(config_fn_t fn,
top.do_ftell = config_file_ftell;
flockfile(f);
- ret = do_config_from(&top, fn, data, opts);
+ ret = do_config_from(reader, &top, fn, data, opts);
funlockfile(f);
return ret;
}
static int git_config_from_stdin(config_fn_t fn, void *data)
{
- return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
- data, NULL);
+ return do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_STDIN, "",
+ NULL, stdin, data, NULL);
}
int git_config_from_file_with_options(config_fn_t fn, const char *filename,
@@ -2018,8 +2038,8 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
BUG("filename cannot be NULL");
f = fopen_or_warn(filename, "r");
if (f) {
- ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
- filename, f, data, opts);
+ ret = do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_FILE,
+ filename, filename, f, data, opts);
fclose(f);
}
return ret;
@@ -2048,7 +2068,7 @@ int git_config_from_mem(config_fn_t fn,
top.do_ungetc = config_buf_ungetc;
top.do_ftell = config_buf_ftell;
- return do_config_from(&top, fn, data, opts);
+ return do_config_from(&the_reader, &top, fn, data, opts);
}
int git_config_from_blob_oid(config_fn_t fn,
@@ -3797,8 +3817,8 @@ const char *current_config_origin_type(void)
int type;
if (current_config_kvi)
type = current_config_kvi->origin_type;
- else if(cf_global)
- type = cf_global->origin_type;
+ else if(the_reader.source)
+ type = the_reader.source->origin_type;
else
BUG("current_config_origin_type called outside config callback");
@@ -3843,8 +3863,8 @@ const char *current_config_name(void)
const char *name;
if (current_config_kvi)
name = current_config_kvi->filename;
- else if (cf_global)
- name = cf_global->name;
+ else if (the_reader.source)
+ name = the_reader.source->name;
else
BUG("current_config_name called outside config callback");
return name ? name : "";
@@ -3863,7 +3883,7 @@ int current_config_line(void)
if (current_config_kvi)
return current_config_kvi->linenr;
else
- return cf_global->linenr;
+ return the_reader.source->linenr;
}
int lookup_config(const char **mapping, int nr_mapping, const char *var)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v3 3/8] config.c: create config_reader and the_reader
2023-03-28 17:51 ` [PATCH v3 3/8] config.c: create config_reader and the_reader Glen Choo via GitGitGadget
@ 2023-03-29 10:41 ` Ævar Arnfjörð Bjarmason
2023-03-29 18:57 ` Junio C Hamano
2023-03-30 17:51 ` Glen Choo
0 siblings, 2 replies; 72+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2023-03-29 10:41 UTC (permalink / raw)
To: Glen Choo via GitGitGadget
Cc: git, Jonathan Tan, Emily Shaffer, Calvin Wan, Glen Choo
On Tue, Mar 28 2023, Glen Choo via GitGitGadget wrote:
> From: Glen Choo <chooglen@google.com>
>
> Create "struct config_reader" to hold the state of the config source
> currently being read. Then, create a static instance of it,
> "the_reader", and use "the_reader.source" to replace references to
> "cf_global" in public functions.
>
> This doesn't create much immediate benefit (since we're mostly replacing
> static variables with a bigger static variable), but it prepares us for
> a future where this state doesn't have to be global; "struct
> config_reader" (or a similar struct) could be provided by the caller, or
> constructed internally by a function like "do_config_from()".
>
> A more typical approach would be to put this struct on "the_repository",
> but that's a worse fit for this use case since config reading is not
> scoped to a repository. E.g. we can read config before the repository is
> known ("read_very_early_config()"), blatantly ignore the repo
> ("read_protected_config()"), or read only from a file
> ("git_config_from_file()"). This is especially evident in t5318 and
> t9210, where test-tool and scalar parse config but don't fully
> initialize "the_repository".
I don't mean to just rehash previous discussion
(i.e. https://lore.kernel.org/git/230307.86wn3szrzu.gmgdl@evledraar.gmail.com/
and downthread). I get that you think sticking this in a "struct
repository *" here isn't clean, and would prefer to not conflate the
two.
Fair enough.
But I think this paragraph still does a bad job of justifying this
direction with reference to existing code.
Why? Because from reading it you get the impression that with
read_very_early_config() and read_protected_config() "config reading is
not scoped to a repository", but "scoped to" is doing a *lot* of work
here.
At the start of read_very_early_config() we do:
struct config_options opts = { 0 };
[...]
opts.ignore_repo = 1;
opts.ignore_worktree = 1;
And then call config_with_options(), which does:
struct config_include_data inc = CONFIG_INCLUDE_INIT;
And that struct has:
struct git_config_source *config_source;
Which in turn has:
/* The repository if blob is not NULL; leave blank for the_repository */
struct repository *repo;
const char *blob;
The read_protected_config() is then another thin wrapper for
config_with_options().
So, so far the reader might be genuinely confused, since we already have
a "repo" in scope why can't we use it for this cache? Even if just
reading the system config etc.
For *those* cases I think what I *think* you're going for is that while
we have a "struct repository" already, we don't want to use it for our
"cache", and instead have a file-scoped one.
Personally, I don't see how it's cleaner to always use a file-scope
rather than piggy-back on the global we almost always have (or provide a
fallback), but let's not get on that topic again :)
Now, the case that *is* special on the other hand is
git_config_from_file(), there we really don't have a "repository" at
all, as it never gets the "struct config_include_data inc", or a
"git_config_source".
But if we dig a bit into those cases there's 3x users of
git_config_from_file() outside of config.c itself:
* setup.c, to read only repo's "config.worktree"
* setup.c, to read only repo "config"
* sequencer.c, to read "sequencer/opts"
For the former two, I think the only thing that's needed is something
like this, along with a corresponding change to
do_git_config_sequence():
diff --git a/config.h b/config.h
index 7606246531a..b8a3de4eb93 100644
--- a/config.h
+++ b/config.h
@@ -85,7 +85,10 @@ typedef int (*config_parser_event_fn_t)(enum config_event_t type,
struct config_options {
unsigned int respect_includes : 1;
+ unsigned int ignore_system : 1;
+ unsigned int ignore_global : 1;
unsigned int ignore_repo : 1;
+ unsigned int ignore_local : 1;
unsigned int ignore_worktree : 1;
unsigned int ignore_cmdline : 1;
unsigned int system_gently : 1;
I.e. we actually *do* have a repo there, we just haven't bridged the gap
of "ignore most of its config" so we can use config_with_options()
there.
The sequencer.c case is trickier, but presumably for such isolated
reading we could have a lower-level function which would return the
equivalent of a "key_value_info" on errors or whatever.
Anyway, I'm fine with this direction for now, but given the above & my
previous RFC
https://lore.kernel.org/git/RFC-cover-0.5-00000000000-20230317T042408Z-avarab@gmail.com/
I can't help but think we're taking two steps forward & one step
backwards for some of this.
I.e. are we assuming no "repo", but per the above we really do have one,
but we just don't pass it because we don't have a "read only the
worktree config part", or whatever?
Ditto the line number relaying for builtin/config.c, which as my RFC
showed we have one or two API users that care, which we can just
convert...
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v3 3/8] config.c: create config_reader and the_reader
2023-03-29 10:41 ` Ævar Arnfjörð Bjarmason
@ 2023-03-29 18:57 ` Junio C Hamano
2023-03-29 20:02 ` Glen Choo
2023-03-30 17:51 ` Glen Choo
1 sibling, 1 reply; 72+ messages in thread
From: Junio C Hamano @ 2023-03-29 18:57 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason
Cc: Glen Choo via GitGitGadget, git, Jonathan Tan, Emily Shaffer,
Calvin Wan, Glen Choo
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> But I think this paragraph still does a bad job of justifying this
> direction with reference to existing code.
I thought it read reasonably well, if not perfect, and do not think
I am capable of rewriting it better, unfortunately.
Care to suggest a better rewrite?
> struct config_options {
> unsigned int respect_includes : 1;
> + unsigned int ignore_system : 1;
> + unsigned int ignore_global : 1;
> unsigned int ignore_repo : 1;
> + unsigned int ignore_local : 1;
> unsigned int ignore_worktree : 1;
> unsigned int ignore_cmdline : 1;
> unsigned int system_gently : 1;
That does look (I am not sure about _local bit, though) well
organized, but I suspect that it can be left for a follow-on
clean-up series, perhaps?
Thanks.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v3 3/8] config.c: create config_reader and the_reader
2023-03-29 18:57 ` Junio C Hamano
@ 2023-03-29 20:02 ` Glen Choo
0 siblings, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-29 20:02 UTC (permalink / raw)
To: Junio C Hamano, Ævar Arnfjörð Bjarmason
Cc: Glen Choo via GitGitGadget, git, Jonathan Tan, Emily Shaffer,
Calvin Wan
Junio C Hamano <gitster@pobox.com> writes:
>>> I.e. we actually *do* have a repo there, we just haven't bridged the gap
>>> of "ignore most of its config" so we can use config_with_options()
>>> there.
>> struct config_options {
>> unsigned int respect_includes : 1;
>> + unsigned int ignore_system : 1;
>> + unsigned int ignore_global : 1;
>> unsigned int ignore_repo : 1;
>> + unsigned int ignore_local : 1;
>> unsigned int ignore_worktree : 1;
>> unsigned int ignore_cmdline : 1;
>> unsigned int system_gently : 1;
>
> That does look (I am not sure about _local bit, though) well
> organized, but I suspect that it can be left for a follow-on
> clean-up series, perhaps?
Makes sense, I did suggest something similar previously:
https://lore.kernel.org/git/kl6ly1oze7wb.fsf@chooglen-macbookpro.roam.corp.google.com
But I think that's a follow up series for sure.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v3 3/8] config.c: create config_reader and the_reader
2023-03-29 10:41 ` Ævar Arnfjörð Bjarmason
2023-03-29 18:57 ` Junio C Hamano
@ 2023-03-30 17:51 ` Glen Choo
1 sibling, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-30 17:51 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason,
Glen Choo via GitGitGadget
Cc: git, Jonathan Tan, Emily Shaffer, Calvin Wan
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> On Tue, Mar 28 2023, Glen Choo via GitGitGadget wrote:
>> A more typical approach would be to put this struct on "the_repository",
>> but that's a worse fit for this use case since config reading is not
>> scoped to a repository. E.g. we can read config before the repository is
>> known ("read_very_early_config()"), blatantly ignore the repo
>> ("read_protected_config()"), or read only from a file
>> ("git_config_from_file()"). This is especially evident in t5318 and
>> t9210, where test-tool and scalar parse config but don't fully
>> initialize "the_repository".
>
> [...]
>
> But I think this paragraph still does a bad job of justifying this
> direction with reference to existing code.
>
> Why? Because from reading it you get the impression that with
> read_very_early_config() and read_protected_config() "config reading is
> not scoped to a repository", but "scoped to" is doing a *lot* of work
> here.
>
> [...]
>
> So, so far the reader might be genuinely confused, since we already have
> a "repo" in scope why can't we use it for this cache? Even if just
> reading the system config etc.
>
> For *those* cases I think what I *think* you're going for is that while
> we have a "struct repository" already, we don't want to use it for our
> "cache", and instead have a file-scoped one.
I was probably unclear, bleh. I intended "repository" to mean 'the thing
users interact with', not "struct repository". At any rate, the major
use case I'm concerned with is 'reading config from a file', where the
repository really isn't relevant at all (more on that later).
> Personally, I don't see how it's cleaner to always use a file-scope
> rather than piggy-back on the global we almost always have (or provide a
> fallback), but let's not get on that topic again :)
Piggybacking is probably less intrusive, but I'm not sure it results in
a coherent interface. The _only_ use of git_config_source.repo is to
read config from blobs in config_with_options() (which we need to read
.gitmodules from commits, not the working copy). After that, we don't
actually propagate the "struct repository" at all (because it's not
needed), and I think it makes sense to keep it that way.
> Now, the case that *is* special on the other hand is
> git_config_from_file(), there we really don't have a "repository" at
> all, as it never gets the "struct config_include_data inc", or a
> "git_config_source".
>
> But if we dig a bit into those cases there's 3x users of
> git_config_from_file() outside of config.c itself:
>
> * setup.c, to read only repo's "config.worktree"
> * setup.c, to read only repo "config"
> * sequencer.c, to read "sequencer/opts"
We should also include git_config_from_file_with_options() (which is
basically the same thing), which adds one more caller:
* bundle-uri.c, to read bundle URI files
> For the former two, I think the only thing that's needed is something
> like this, along with a corresponding change to
> do_git_config_sequence():
>
> diff --git a/config.h b/config.h
> index 7606246531a..b8a3de4eb93 100644
> --- a/config.h
> +++ b/config.h
> @@ -85,7 +85,10 @@ typedef int (*config_parser_event_fn_t)(enum config_event_t type,
>
> struct config_options {
> unsigned int respect_includes : 1;
> + unsigned int ignore_system : 1;
> + unsigned int ignore_global : 1;
> unsigned int ignore_repo : 1;
> + unsigned int ignore_local : 1;
> unsigned int ignore_worktree : 1;
> unsigned int ignore_cmdline : 1;
> unsigned int system_gently : 1;
>
> I.e. we actually *do* have a repo there, we just haven't bridged the gap
> of "ignore most of its config" so we can use config_with_options()
> there.
I'm ambivalent on this. On the one hand, you're not wrong to say that
there probably _is_ a repository that we just happen to not care about,
and maybe it makes sense for config_with_options() to see a "struct
repository". On the other, I'm still quite convinced that the "struct
repository" that we already have just happens to be there by accident
(because "struct git_config_source" is a union of unrelated things), and
I don't think we should be piggybacking onto that.
> The sequencer.c case is trickier, but presumably for such isolated
> reading we could have a lower-level function which would return the
> equivalent of a "key_value_info" on errors or whatever.
bundle-uri.c falls into this case of 'read a file in config syntax' too.
For this reason, I see at least two layers to the config API:
- Parsing a file in config syntax, i.e. the "lower level" API
- Reading Git-specific config (understanding where config is located,
caching it, etc), i.e. the "higher level" API
We have in-tree callers for _both_ of these layers, and I think that's
appropriate. IOW I don't think we necessarily need to hide the "lower
level" API inside of config.c and expose only the "higher level" API
in-tree [1], which was the impression I got from some of your RFC
patches.
Separating the layers like this also makes it possible to expose the
"lower" level to out-of-tree callers in a sensible way. To parse a
file in a given syntax, a caller shouldn't need to know about
repositories and whatnot. That's exactly what this series is trying to
prepare for, and being principled about the 'config reading cache' is
essential to get this sort of separation.
> I.e. are we assuming no "repo", but per the above we really do have one,
> but we just don't pass it because we don't have a "read only the
> worktree config part", or whatever?
This was addressed above.
> Ditto the line number relaying for builtin/config.c, which as my RFC
> showed we have one or two API users that care, which we can just
> convert...
builtin/config.c is a weird case that I think needs some refactoring,
e.g. there's
- git config -l, which will list all of the git config ("higher level")
- git config -l -f <file>, which lists config from just a file ("lower
level")
but it uses config_with_options() in both cases! It works because
config_with_options() can switch between "read a subset the git config"
and "read just this file", but it's pretty gross, and we sometimes get
it wrong. (see https://lore.kernel.org/git/xmqqzg9kew1q.fsf@gitster.g/
as an example of how --global is a bit broken).
Your suggestion to convert that (also made upthread, but I can't find
the link for some reason...) to something that uses config_set sounds
pretty reasonable [1].
> Anyway, I'm fine with this direction for now, but given the above & my
> previous RFC
> https://lore.kernel.org/git/RFC-cover-0.5-00000000000-20230317T042408Z-avarab@gmail.com/
> I can't help but think we're taking two steps forward & one step
> backwards for some of this.
Thanks. I appreciate the feedback, nevertheless; I think it's bringing
us closer to a good conclusion.
FWIW I'm working on a followup that will take _many_ steps forward by
adjusting config_fn_t. I don't know how that will pan out, so I
appreciate checking in this series, which is at least a marginal
improvement over the status quo.
[1] "struct config_set" doesn't fall very neatly into the "lower" and
"higher" level API discussion. It's useful to be able to read config
into some in-memory cache (in-tree and out-of-tree), though that isn't
as "low level" as parsing config (without caching). That will probably
be a good follow up to my work to _just_ parse config.
^ permalink raw reply [flat|nested] 72+ messages in thread
* [PATCH v3 4/8] config.c: plumb the_reader through callbacks
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
` (2 preceding siblings ...)
2023-03-28 17:51 ` [PATCH v3 3/8] config.c: create config_reader and the_reader Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 5/8] config.c: remove current_config_kvi Glen Choo via GitGitGadget
` (4 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
The remaining references to "cf_global" are in config callback
functions. Remove them by plumbing "struct config_reader" via the
"*data" arg.
In both of the callbacks here, we are only reading from
"reader->source". So in the long run, if we had a way to expose readonly
information from "reader->source" (probably in the form of "struct
key_value_info"), we could undo this patch (i.e. remove "struct
config_reader" fom "*data").
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 74 ++++++++++++++++++++++++++++++++------------------------
1 file changed, 43 insertions(+), 31 deletions(-)
diff --git a/config.c b/config.c
index 3a28b397c4d..cb6ff134f5f 100644
--- a/config.c
+++ b/config.c
@@ -62,6 +62,9 @@ struct config_reader {
static struct config_reader the_reader;
/*
+ * FIXME The comments are temporarily out of date since "cf_global" has been
+ * moved to the_reader, but not current_*.
+ *
* These variables record the "current" config source, which
* can be accessed by parsing callbacks.
*
@@ -76,11 +79,7 @@ static struct config_reader the_reader;
* at the variables, it's either a bug for it to be called in the first place,
* or it's a function which can be reused for non-config purposes, and should
* fall back to some sane behavior).
- *
- * FIXME "cf_global" has been replaced by "the_reader.source", remove
- * "cf_global" once we plumb "the_reader" through all of the callback functions.
*/
-static struct config_source *cf_global;
static struct key_value_info *current_config_kvi;
/*
@@ -98,8 +97,6 @@ static inline void config_reader_push_source(struct config_reader *reader,
{
top->prev = reader->source;
reader->source = top;
- /* FIXME remove this when cf_global is removed. */
- cf_global = reader->source;
}
static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
@@ -109,8 +106,6 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
BUG("tried to pop config source, but we weren't reading config");
ret = reader->source;
reader->source = reader->source->prev;
- /* FIXME remove this when cf_global is removed. */
- cf_global = reader->source;
return ret;
}
@@ -175,6 +170,7 @@ struct config_include_data {
void *data;
const struct config_options *opts;
struct git_config_source *config_source;
+ struct config_reader *config_reader;
/*
* All remote URLs discovered when reading all config files.
@@ -465,6 +461,7 @@ static int include_condition_is_true(struct config_source *cf,
static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
+ struct config_source *cf = inc->config_reader->source;
const char *cond, *key;
size_t cond_len;
int ret;
@@ -478,16 +475,16 @@ static int git_config_include(const char *var, const char *value, void *data)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(cf_global, value, inc);
+ ret = handle_path_include(cf, value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(cf_global, inc, cond, cond_len) &&
+ cond && include_condition_is_true(cf, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(cf_global, value, inc);
+ ret = handle_path_include(cf, value, inc);
inc->fn = old_fn;
}
@@ -2228,6 +2225,7 @@ int config_with_options(config_fn_t fn, void *data,
inc.data = data;
inc.opts = opts;
inc.config_source = config_source;
+ inc.config_reader = &the_reader;
fn = git_config_include;
data = &inc;
}
@@ -2348,7 +2346,9 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
return found_entry;
}
-static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+static int configset_add_value(struct config_reader *reader,
+ struct config_set *cs, const char *key,
+ const char *value)
{
struct config_set_element *e;
struct string_list_item *si;
@@ -2374,12 +2374,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
- if (!cf_global)
+ if (!reader->source)
BUG("configset_add_value has no source");
- if (cf_global->name) {
- kv_info->filename = strintern(cf_global->name);
- kv_info->linenr = cf_global->linenr;
- kv_info->origin_type = cf_global->origin_type;
+ if (reader->source->name) {
+ kv_info->filename = strintern(reader->source->name);
+ kv_info->linenr = reader->source->linenr;
+ kv_info->origin_type = reader->source->origin_type;
} else {
/* for values read from `git_config_from_parameters()` */
kv_info->filename = NULL;
@@ -2434,16 +2434,25 @@ void git_configset_clear(struct config_set *cs)
cs->list.items = NULL;
}
+struct configset_add_data {
+ struct config_set *config_set;
+ struct config_reader *config_reader;
+};
+#define CONFIGSET_ADD_INIT { 0 }
+
static int config_set_callback(const char *key, const char *value, void *cb)
{
- struct config_set *cs = cb;
- configset_add_value(cs, key, value);
+ struct configset_add_data *data = cb;
+ configset_add_value(data->config_reader, data->config_set, key, value);
return 0;
}
int git_configset_add_file(struct config_set *cs, const char *filename)
{
- return git_config_from_file(config_set_callback, filename, cs);
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
+ data.config_reader = &the_reader;
+ data.config_set = cs;
+ return git_config_from_file(config_set_callback, filename, &data);
}
int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
@@ -2558,6 +2567,7 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
static void repo_read_config(struct repository *repo)
{
struct config_options opts = { 0 };
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
opts.respect_includes = 1;
opts.commondir = repo->commondir;
@@ -2569,8 +2579,10 @@ static void repo_read_config(struct repository *repo)
git_configset_clear(repo->config);
git_configset_init(repo->config);
+ data.config_set = repo->config;
+ data.config_reader = &the_reader;
- if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+ if (config_with_options(config_set_callback, &data, NULL, &opts) < 0)
/*
* config_with_options() normally returns only
* zero, as most errors are fatal, and
@@ -2696,9 +2708,12 @@ static void read_protected_config(void)
.ignore_worktree = 1,
.system_gently = 1,
};
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
+
git_configset_init(&protected_config);
- config_with_options(config_set_callback, &protected_config,
- NULL, &opts);
+ data.config_set = &protected_config;
+ data.config_reader = &the_reader;
+ config_with_options(config_set_callback, &data, NULL, &opts);
}
void git_protected_config(config_fn_t fn, void *data)
@@ -2883,6 +2898,7 @@ void git_die_config(const char *key, const char *err, ...)
*/
struct config_store_data {
+ struct config_reader *config_reader;
size_t baselen;
char *key;
int do_not_match;
@@ -2897,6 +2913,7 @@ struct config_store_data {
unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
unsigned int key_seen:1, section_seen:1, is_keys_section:1;
};
+#define CONFIG_STORE_INIT { 0 }
static void config_store_data_clear(struct config_store_data *store)
{
@@ -2931,12 +2948,7 @@ static int store_aux_event(enum config_event_t type,
size_t begin, size_t end, void *data)
{
struct config_store_data *store = data;
- /*
- * FIXME Keep using "cf" so that we can avoid rewrapping a
- * really long line below. Remove this when "cf" gets plumbed
- * correctly.
- */
- struct config_source *cf = cf_global;
+ struct config_source *cf = store->config_reader->source;
ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
store->parsed[store->parsed_nr].begin = begin;
@@ -3254,9 +3266,9 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
char *filename_buf = NULL;
char *contents = NULL;
size_t contents_sz;
- struct config_store_data store;
+ struct config_store_data store = CONFIG_STORE_INIT;
- memset(&store, 0, sizeof(store));
+ store.config_reader = &the_reader;
/* parse-key returns negative; flip the sign to feed exit(3) */
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v3 5/8] config.c: remove current_config_kvi
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
` (3 preceding siblings ...)
2023-03-28 17:51 ` [PATCH v3 4/8] config.c: plumb the_reader through callbacks Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 6/8] config.c: remove current_parsing_scope Glen Choo via GitGitGadget
` (3 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
Add ".config_kvi" to "struct config_reader" and replace
"current_config_kvi" with "the_reader.config_kvi", plumbing "struct
config_reader" where necesssary.
Also, introduce a setter function for ".config_kvi", which allows us to
enforce the contraint that only one of ".source" and ".config_kvi" can
be set at a time (as documented in the comments). Because of this
constraint, we know that "populate_remote_urls()" was never touching
"current_config_kvi" when iterating through config files, so it doesn't
need to store and restore that value.
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 82 +++++++++++++++++++++++++++++---------------------------
1 file changed, 43 insertions(+), 39 deletions(-)
diff --git a/config.c b/config.c
index cb6ff134f5f..71ee36f069b 100644
--- a/config.c
+++ b/config.c
@@ -52,7 +52,24 @@ struct config_source {
#define CONFIG_SOURCE_INIT { 0 }
struct config_reader {
+ /*
+ * These members record the "current" config source, which can be
+ * accessed by parsing callbacks.
+ *
+ * The "source" variable will be non-NULL only when we are actually
+ * parsing a real config source (file, blob, cmdline, etc).
+ *
+ * The "config_kvi" variable will be non-NULL only when we are feeding
+ * cached config from a configset into a callback.
+ *
+ * They cannot be non-NULL at the same time. If they are both NULL, then
+ * we aren't parsing anything (and depending on the function looking at
+ * the variables, it's either a bug for it to be called in the first
+ * place, or it's a function which can be reused for non-config
+ * purposes, and should fall back to some sane behavior).
+ */
struct config_source *source;
+ struct key_value_info *config_kvi;
};
/*
* Where possible, prefer to accept "struct config_reader" as an arg than to use
@@ -61,27 +78,6 @@ struct config_reader {
*/
static struct config_reader the_reader;
-/*
- * FIXME The comments are temporarily out of date since "cf_global" has been
- * moved to the_reader, but not current_*.
- *
- * These variables record the "current" config source, which
- * can be accessed by parsing callbacks.
- *
- * The "cf_global" variable will be non-NULL only when we are actually
- * parsing a real config source (file, blob, cmdline, etc).
- *
- * The "current_config_kvi" variable will be non-NULL only when we are feeding
- * cached config from a configset into a callback.
- *
- * They should generally never be non-NULL at the same time. If they are both
- * NULL, then we aren't parsing anything (and depending on the function looking
- * at the variables, it's either a bug for it to be called in the first place,
- * or it's a function which can be reused for non-config purposes, and should
- * fall back to some sane behavior).
- */
-static struct key_value_info *current_config_kvi;
-
/*
* Similar to the variables above, this gives access to the "scope" of the
* current value (repo, global, etc). For cached values, it can be found via
@@ -95,6 +91,8 @@ static enum config_scope current_parsing_scope;
static inline void config_reader_push_source(struct config_reader *reader,
struct config_source *top)
{
+ if (reader->config_kvi)
+ BUG("source should not be set while iterating a config set");
top->prev = reader->source;
reader->source = top;
}
@@ -109,6 +107,14 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
return ret;
}
+static inline void config_reader_set_kvi(struct config_reader *reader,
+ struct key_value_info *kvi)
+{
+ if (kvi && reader->source)
+ BUG("kvi should not be set while parsing a config source");
+ reader->config_kvi = kvi;
+}
+
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -377,20 +383,17 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct key_value_info *store_kvi = current_config_kvi;
enum config_scope store_scope = current_parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- current_config_kvi = NULL;
current_parsing_scope = 0;
inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- current_config_kvi = store_kvi;
current_parsing_scope = store_scope;
}
@@ -2257,7 +2260,8 @@ int config_with_options(config_fn_t fn, void *data,
return ret;
}
-static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+static void configset_iter(struct config_reader *reader, struct config_set *cs,
+ config_fn_t fn, void *data)
{
int i, value_index;
struct string_list *values;
@@ -2269,14 +2273,14 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
value_index = list->items[i].value_index;
values = &entry->value_list;
- current_config_kvi = values->items[value_index].util;
+ config_reader_set_kvi(reader, values->items[value_index].util);
if (fn(entry->key, values->items[value_index].string, data) < 0)
git_die_config_linenr(entry->key,
- current_config_kvi->filename,
- current_config_kvi->linenr);
+ reader->config_kvi->filename,
+ reader->config_kvi->linenr);
- current_config_kvi = NULL;
+ config_reader_set_kvi(reader, NULL);
}
}
@@ -2614,7 +2618,7 @@ static void repo_config_clear(struct repository *repo)
void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
git_config_check_init(repo);
- configset_iter(repo->config, fn, data);
+ configset_iter(&the_reader, repo->config, fn, data);
}
int repo_config_get_value(struct repository *repo,
@@ -2720,7 +2724,7 @@ void git_protected_config(config_fn_t fn, void *data)
{
if (!protected_config.hash_initialized)
read_protected_config();
- configset_iter(&protected_config, fn, data);
+ configset_iter(&the_reader, &protected_config, fn, data);
}
/* Functions used historically to read configuration from 'the_repository' */
@@ -3827,8 +3831,8 @@ int parse_config_key(const char *var,
const char *current_config_origin_type(void)
{
int type;
- if (current_config_kvi)
- type = current_config_kvi->origin_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
@@ -3873,8 +3877,8 @@ const char *config_scope_name(enum config_scope scope)
const char *current_config_name(void)
{
const char *name;
- if (current_config_kvi)
- name = current_config_kvi->filename;
+ if (the_reader.config_kvi)
+ name = the_reader.config_kvi->filename;
else if (the_reader.source)
name = the_reader.source->name;
else
@@ -3884,16 +3888,16 @@ const char *current_config_name(void)
enum config_scope current_config_scope(void)
{
- if (current_config_kvi)
- return current_config_kvi->scope;
+ if (the_reader.config_kvi)
+ return the_reader.config_kvi->scope;
else
return current_parsing_scope;
}
int current_config_line(void)
{
- if (current_config_kvi)
- return current_config_kvi->linenr;
+ if (the_reader.config_kvi)
+ return the_reader.config_kvi->linenr;
else
return the_reader.source->linenr;
}
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v3 6/8] config.c: remove current_parsing_scope
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
` (4 preceding siblings ...)
2023-03-28 17:51 ` [PATCH v3 5/8] config.c: remove current_config_kvi Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 7/8] config: report cached filenames in die_bad_number() Glen Choo via GitGitGadget
` (2 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
Add ".parsing_scope" to "struct config_reader" and replace
"current_parsing_scope" with "the_reader.parsing_scope. Adjust the
comment slightly to make it clearer that the scope applies to the config
source (not the current value), and should only be set when parsing a
config source.
As such, ".parsing_scope" (only set when parsing config sources) and
".config_kvi" (only set when iterating a config set) should not be
set together, so enforce this with a setter function.
Unlike previous commits, "populate_remote_urls()" still needs to store
and restore the 'scope' value because it could have touched
"current_parsing_scope" ("config_with_options()" can set the scope).
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 63 +++++++++++++++++++++++++++++++++-----------------------
1 file changed, 37 insertions(+), 26 deletions(-)
diff --git a/config.c b/config.c
index 71ee36f069b..3756322ec96 100644
--- a/config.c
+++ b/config.c
@@ -70,6 +70,16 @@ struct config_reader {
*/
struct config_source *source;
struct key_value_info *config_kvi;
+ /*
+ * The "scope" of the current config source being parsed (repo, global,
+ * etc). Like "source", this is only set when parsing a config source.
+ * It's not part of "source" because it transcends a single file (i.e.,
+ * a file included from .git/config is still in "repo" scope).
+ *
+ * When iterating through a configset, the equivalent value is
+ * "config_kvi.scope" (see above).
+ */
+ enum config_scope parsing_scope;
};
/*
* Where possible, prefer to accept "struct config_reader" as an arg than to use
@@ -78,16 +88,6 @@ struct config_reader {
*/
static struct config_reader the_reader;
-/*
- * Similar to the variables above, this gives access to the "scope" of the
- * current value (repo, global, etc). For cached values, it can be found via
- * the current_config_kvi as above. During parsing, the current value can be
- * found in this variable. It's not part of "cf_global" because it transcends a
- * single file (i.e., a file included from .git/config is still in "repo"
- * scope).
- */
-static enum config_scope current_parsing_scope;
-
static inline void config_reader_push_source(struct config_reader *reader,
struct config_source *top)
{
@@ -110,11 +110,19 @@ static inline struct config_source *config_reader_pop_source(struct config_reade
static inline void config_reader_set_kvi(struct config_reader *reader,
struct key_value_info *kvi)
{
- if (kvi && reader->source)
+ if (kvi && (reader->source || reader->parsing_scope))
BUG("kvi should not be set while parsing a config source");
reader->config_kvi = kvi;
}
+static inline void config_reader_set_scope(struct config_reader *reader,
+ enum config_scope scope)
+{
+ if (scope && reader->config_kvi)
+ BUG("scope should only be set when iterating through a config source");
+ reader->parsing_scope = scope;
+}
+
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -383,18 +391,18 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- enum config_scope store_scope = current_parsing_scope;
+ enum config_scope store_scope = inc->config_reader->parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- current_parsing_scope = 0;
+ config_reader_set_scope(inc->config_reader, 0);
inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- current_parsing_scope = store_scope;
+ config_reader_set_scope(inc->config_reader, store_scope);
}
static int forbid_remote_url(const char *var, const char *value UNUSED,
@@ -2159,7 +2167,8 @@ int git_config_system(void)
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
}
-static int do_git_config_sequence(const struct config_options *opts,
+static int do_git_config_sequence(struct config_reader *reader,
+ const struct config_options *opts,
config_fn_t fn, void *data)
{
int ret = 0;
@@ -2167,7 +2176,7 @@ static int do_git_config_sequence(const struct config_options *opts,
char *xdg_config = NULL;
char *user_config = NULL;
char *repo_config;
- enum config_scope prev_parsing_scope = current_parsing_scope;
+ enum config_scope prev_parsing_scope = reader->parsing_scope;
if (opts->commondir)
repo_config = mkpathdup("%s/config", opts->commondir);
@@ -2176,13 +2185,13 @@ static int do_git_config_sequence(const struct config_options *opts,
else
repo_config = NULL;
- current_parsing_scope = CONFIG_SCOPE_SYSTEM;
+ config_reader_set_scope(reader, CONFIG_SCOPE_SYSTEM);
if (git_config_system() && system_config &&
!access_or_die(system_config, R_OK,
opts->system_gently ? ACCESS_EACCES_OK : 0))
ret += git_config_from_file(fn, system_config, data);
- current_parsing_scope = CONFIG_SCOPE_GLOBAL;
+ config_reader_set_scope(reader, CONFIG_SCOPE_GLOBAL);
git_global_config(&user_config, &xdg_config);
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
@@ -2191,12 +2200,12 @@ static int do_git_config_sequence(const struct config_options *opts,
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
ret += git_config_from_file(fn, user_config, data);
- current_parsing_scope = CONFIG_SCOPE_LOCAL;
+ config_reader_set_scope(reader, CONFIG_SCOPE_LOCAL);
if (!opts->ignore_repo && repo_config &&
!access_or_die(repo_config, R_OK, 0))
ret += git_config_from_file(fn, repo_config, data);
- current_parsing_scope = CONFIG_SCOPE_WORKTREE;
+ config_reader_set_scope(reader, CONFIG_SCOPE_WORKTREE);
if (!opts->ignore_worktree && repository_format_worktree_config) {
char *path = git_pathdup("config.worktree");
if (!access_or_die(path, R_OK, 0))
@@ -2204,11 +2213,11 @@ static int do_git_config_sequence(const struct config_options *opts,
free(path);
}
- current_parsing_scope = CONFIG_SCOPE_COMMAND;
+ config_reader_set_scope(reader, CONFIG_SCOPE_COMMAND);
if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
die(_("unable to parse command-line config"));
- current_parsing_scope = prev_parsing_scope;
+ config_reader_set_scope(reader, prev_parsing_scope);
free(system_config);
free(xdg_config);
free(user_config);
@@ -2221,6 +2230,7 @@ int config_with_options(config_fn_t fn, void *data,
const struct config_options *opts)
{
struct config_include_data inc = CONFIG_INCLUDE_INIT;
+ enum config_scope prev_scope = the_reader.parsing_scope;
int ret;
if (opts->respect_includes) {
@@ -2234,7 +2244,7 @@ int config_with_options(config_fn_t fn, void *data,
}
if (config_source)
- current_parsing_scope = config_source->scope;
+ config_reader_set_scope(&the_reader, config_source->scope);
/*
* If we have a specific filename, use it. Otherwise, follow the
@@ -2250,13 +2260,14 @@ int config_with_options(config_fn_t fn, void *data,
ret = git_config_from_blob_ref(fn, repo, config_source->blob,
data);
} else {
- ret = do_git_config_sequence(opts, fn, data);
+ ret = do_git_config_sequence(&the_reader, opts, fn, data);
}
if (inc.remote_urls) {
string_list_clear(inc.remote_urls, 0);
FREE_AND_NULL(inc.remote_urls);
}
+ config_reader_set_scope(&the_reader, prev_scope);
return ret;
}
@@ -2390,7 +2401,7 @@ static int configset_add_value(struct config_reader *reader,
kv_info->linenr = -1;
kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
}
- kv_info->scope = current_parsing_scope;
+ kv_info->scope = reader->parsing_scope;
si->util = kv_info;
return 0;
@@ -3891,7 +3902,7 @@ enum config_scope current_config_scope(void)
if (the_reader.config_kvi)
return the_reader.config_kvi->scope;
else
- return current_parsing_scope;
+ return the_reader.parsing_scope;
}
int current_config_line(void)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v3 7/8] config: report cached filenames in die_bad_number()
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
` (5 preceding siblings ...)
2023-03-28 17:51 ` [PATCH v3 6/8] config.c: remove current_parsing_scope Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 17:51 ` [PATCH v3 8/8] config.c: rename "struct config_source cf" Glen Choo via GitGitGadget
2023-03-28 18:00 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo
8 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
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 <chooglen@google.com>
---
config.c | 65 +++++++++++++++++++++++++++++-------------
config.h | 1 +
t/helper/test-config.c | 17 +++++++++++
t/t1308-config-set.sh | 9 ++++++
4 files changed, 72 insertions(+), 20 deletions(-)
diff --git a/config.c b/config.c
index 3756322ec96..46c5a9a7d51 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;
}
-const char *current_config_origin_type(void)
+static int reader_origin_type(struct config_reader *reader,
+ enum config_origin_type *type)
{
- int type;
if (the_reader.config_kvi)
- type = the_reader.config_kvi->origin_type;
+ *type = reader->config_kvi->origin_type;
else if(the_reader.source)
- type = the_reader.source->origin_type;
+ *type = reader->source->origin_type;
else
+ return 1;
+ return 0;
+}
+
+const char *current_config_origin_type(void)
+{
+ 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)
}
}
-const char *current_config_name(void)
+static int reader_config_name(struct config_reader *reader, const char **out)
{
- const char *name;
if (the_reader.config_kvi)
- name = the_reader.config_kvi->filename;
+ *out = reader->config_kvi->filename;
else if (the_reader.source)
- name = the_reader.source->name;
+ *out = reader->source->name;
else
+ return 1;
+ return 0;
+}
+
+const char *current_config_name(void)
+{
+ const char *name;
+ 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 7606246531a..66c8b996e15 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 4ba9eb65606..26e79168f6a 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 b38e158d3b2..9733bed30a9 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 &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* [PATCH v3 8/8] config.c: rename "struct config_source cf"
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
` (6 preceding siblings ...)
2023-03-28 17:51 ` [PATCH v3 7/8] config: report cached filenames in die_bad_number() Glen Choo via GitGitGadget
@ 2023-03-28 17:51 ` Glen Choo via GitGitGadget
2023-03-28 18:00 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo
8 siblings, 0 replies; 72+ messages in thread
From: Glen Choo via GitGitGadget @ 2023-03-28 17:51 UTC (permalink / raw)
To: git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason, Glen Choo, Glen Choo
From: Glen Choo <chooglen@google.com>
The "cf" name is a holdover from before 4d8dd1494e (config: make parsing
stack struct independent from actual data source, 2013-07-12), when the
struct was named config_file. Since that acronym no longer makes sense,
rename "cf" to "cs". In some places, we have "struct config_set cs", so
to avoid conflict, rename those "cs" to "set" ("config_set" would be
more descriptive, but it's much longer and would require us to rewrap
several lines).
Signed-off-by: Glen Choo <chooglen@google.com>
---
config.c | 262 +++++++++++++++++++++++++++----------------------------
1 file changed, 131 insertions(+), 131 deletions(-)
diff --git a/config.c b/config.c
index 46c5a9a7d51..cf571d45282 100644
--- a/config.c
+++ b/config.c
@@ -202,7 +202,7 @@ static const char include_depth_advice[] = N_(
"from\n"
" %s\n"
"This might be due to circular includes.");
-static int handle_path_include(struct config_source *cf, const char *path,
+static int handle_path_include(struct config_source *cs, const char *path,
struct config_include_data *inc)
{
int ret = 0;
@@ -224,14 +224,14 @@ static int handle_path_include(struct config_source *cf, const char *path,
if (!is_absolute_path(path)) {
char *slash;
- if (!cf || !cf->path) {
+ if (!cs || !cs->path) {
ret = error(_("relative config includes must come from files"));
goto cleanup;
}
- slash = find_last_dir_sep(cf->path);
+ slash = find_last_dir_sep(cs->path);
if (slash)
- strbuf_add(&buf, cf->path, slash - cf->path + 1);
+ strbuf_add(&buf, cs->path, slash - cs->path + 1);
strbuf_addstr(&buf, path);
path = buf.buf;
}
@@ -239,8 +239,8 @@ static int handle_path_include(struct config_source *cf, const char *path,
if (!access_or_die(path, R_OK, 0)) {
if (++inc->depth > MAX_INCLUDE_DEPTH)
die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
- !cf ? "<unknown>" :
- cf->name ? cf->name :
+ !cs ? "<unknown>" :
+ cs->name ? cs->name :
"the command line");
ret = git_config_from_file(git_config_include, path, inc);
inc->depth--;
@@ -257,7 +257,7 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
strbuf_addstr(pat, "**");
}
-static int prepare_include_condition_pattern(struct config_source *cf,
+static int prepare_include_condition_pattern(struct config_source *cs,
struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
@@ -274,11 +274,11 @@ static int prepare_include_condition_pattern(struct config_source *cf,
if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
const char *slash;
- if (!cf || !cf->path)
+ if (!cs || !cs->path)
return error(_("relative config include "
"conditionals must come from files"));
- strbuf_realpath(&path, cf->path, 1);
+ strbuf_realpath(&path, cs->path, 1);
slash = find_last_dir_sep(path.buf);
if (!slash)
BUG("how is this possible?");
@@ -293,7 +293,7 @@ static int prepare_include_condition_pattern(struct config_source *cf,
return prefix;
}
-static int include_by_gitdir(struct config_source *cf,
+static int include_by_gitdir(struct config_source *cs,
const struct config_options *opts,
const char *cond, size_t cond_len, int icase)
{
@@ -310,7 +310,7 @@ static int include_by_gitdir(struct config_source *cf,
strbuf_realpath(&text, git_dir, 1);
strbuf_add(&pattern, cond, cond_len);
- prefix = prepare_include_condition_pattern(cf, &pattern);
+ prefix = prepare_include_condition_pattern(cs, &pattern);
again:
if (prefix < 0)
@@ -449,16 +449,16 @@ static int include_by_remote_url(struct config_include_data *inc,
inc->remote_urls);
}
-static int include_condition_is_true(struct config_source *cf,
+static int include_condition_is_true(struct config_source *cs,
struct config_include_data *inc,
const char *cond, size_t cond_len)
{
const struct config_options *opts = inc->opts;
if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
- return include_by_gitdir(cf, opts, cond, cond_len, 0);
+ return include_by_gitdir(cs, opts, cond, cond_len, 0);
else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
- return include_by_gitdir(cf, opts, cond, cond_len, 1);
+ return include_by_gitdir(cs, opts, cond, cond_len, 1);
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
return include_by_branch(cond, cond_len);
else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -472,7 +472,7 @@ static int include_condition_is_true(struct config_source *cf,
static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
- struct config_source *cf = inc->config_reader->source;
+ struct config_source *cs = inc->config_reader->source;
const char *cond, *key;
size_t cond_len;
int ret;
@@ -486,16 +486,16 @@ static int git_config_include(const char *var, const char *value, void *data)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(cf, value, inc);
+ ret = handle_path_include(cs, value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(cf, inc, cond, cond_len) &&
+ cond && include_condition_is_true(cs, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(cf, value, inc);
+ ret = handle_path_include(cs, value, inc);
inc->fn = old_fn;
}
@@ -820,21 +820,21 @@ out:
return ret;
}
-static int get_next_char(struct config_source *cf)
+static int get_next_char(struct config_source *cs)
{
- int c = cf->do_fgetc(cf);
+ int c = cs->do_fgetc(cs);
if (c == '\r') {
/* DOS like systems */
- c = cf->do_fgetc(cf);
+ c = cs->do_fgetc(cs);
if (c != '\n') {
if (c != EOF)
- cf->do_ungetc(c, cf);
+ cs->do_ungetc(c, cs);
c = '\r';
}
}
- if (c != EOF && ++cf->total_len > INT_MAX) {
+ if (c != EOF && ++cs->total_len > INT_MAX) {
/*
* This is an absurdly long config file; refuse to parse
* further in order to protect downstream code from integer
@@ -842,38 +842,38 @@ static int get_next_char(struct config_source *cf)
* but we can mark EOF and put trash in the return value,
* which will trigger a parse error.
*/
- cf->eof = 1;
+ cs->eof = 1;
return 0;
}
if (c == '\n')
- cf->linenr++;
+ cs->linenr++;
if (c == EOF) {
- cf->eof = 1;
- cf->linenr++;
+ cs->eof = 1;
+ cs->linenr++;
c = '\n';
}
return c;
}
-static char *parse_value(struct config_source *cf)
+static char *parse_value(struct config_source *cs)
{
int quote = 0, comment = 0, space = 0;
- strbuf_reset(&cf->value);
+ strbuf_reset(&cs->value);
for (;;) {
- int c = get_next_char(cf);
+ int c = get_next_char(cs);
if (c == '\n') {
if (quote) {
- cf->linenr--;
+ cs->linenr--;
return NULL;
}
- return cf->value.buf;
+ return cs->value.buf;
}
if (comment)
continue;
if (isspace(c) && !quote) {
- if (cf->value.len)
+ if (cs->value.len)
space++;
continue;
}
@@ -884,9 +884,9 @@ static char *parse_value(struct config_source *cf)
}
}
for (; space; space--)
- strbuf_addch(&cf->value, ' ');
+ strbuf_addch(&cs->value, ' ');
if (c == '\\') {
- c = get_next_char(cf);
+ c = get_next_char(cs);
switch (c) {
case '\n':
continue;
@@ -906,18 +906,18 @@ static char *parse_value(struct config_source *cf)
default:
return NULL;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
continue;
}
if (c == '"') {
quote = 1-quote;
continue;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
}
}
-static int get_value(struct config_source *cf, config_fn_t fn, void *data,
+static int get_value(struct config_source *cs, config_fn_t fn, void *data,
struct strbuf *name)
{
int c;
@@ -926,8 +926,8 @@ static int get_value(struct config_source *cf, config_fn_t fn, void *data,
/* Get the full name */
for (;;) {
- c = get_next_char(cf);
- if (cf->eof)
+ c = get_next_char(cs);
+ if (cs->eof)
break;
if (!iskeychar(c))
break;
@@ -935,13 +935,13 @@ static int get_value(struct config_source *cf, config_fn_t fn, void *data,
}
while (c == ' ' || c == '\t')
- c = get_next_char(cf);
+ c = get_next_char(cs);
value = NULL;
if (c != '\n') {
if (c != '=')
return -1;
- value = parse_value(cf);
+ value = parse_value(cs);
if (!value)
return -1;
}
@@ -950,21 +950,21 @@ static int get_value(struct config_source *cf, config_fn_t fn, void *data,
* the line we just parsed during the call to fn to get
* accurate line number in error messages.
*/
- cf->linenr--;
+ cs->linenr--;
ret = fn(name->buf, value, data);
if (ret >= 0)
- cf->linenr++;
+ cs->linenr++;
return ret;
}
-static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
+static int get_extended_base_var(struct config_source *cs, struct strbuf *name,
int c)
{
- cf->subsection_case_sensitive = 0;
+ cs->subsection_case_sensitive = 0;
do {
if (c == '\n')
goto error_incomplete_line;
- c = get_next_char(cf);
+ c = get_next_char(cs);
} while (isspace(c));
/* We require the format to be '[base "extension"]' */
@@ -973,13 +973,13 @@ static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
strbuf_addch(name, '.');
for (;;) {
- int c = get_next_char(cf);
+ int c = get_next_char(cs);
if (c == '\n')
goto error_incomplete_line;
if (c == '"')
break;
if (c == '\\') {
- c = get_next_char(cf);
+ c = get_next_char(cs);
if (c == '\n')
goto error_incomplete_line;
}
@@ -987,25 +987,25 @@ static int get_extended_base_var(struct config_source *cf, struct strbuf *name,
}
/* Final ']' */
- if (get_next_char(cf) != ']')
+ if (get_next_char(cs) != ']')
return -1;
return 0;
error_incomplete_line:
- cf->linenr--;
+ cs->linenr--;
return -1;
}
-static int get_base_var(struct config_source *cf, struct strbuf *name)
+static int get_base_var(struct config_source *cs, struct strbuf *name)
{
- cf->subsection_case_sensitive = 1;
+ cs->subsection_case_sensitive = 1;
for (;;) {
- int c = get_next_char(cf);
- if (cf->eof)
+ int c = get_next_char(cs);
+ if (cs->eof)
return -1;
if (c == ']')
return 0;
if (isspace(c))
- return get_extended_base_var(cf, name, c);
+ return get_extended_base_var(cs, name, c);
if (!iskeychar(c) && c != '.')
return -1;
strbuf_addch(name, tolower(c));
@@ -1018,7 +1018,7 @@ struct parse_event_data {
const struct config_options *opts;
};
-static int do_event(struct config_source *cf, enum config_event_t type,
+static int do_event(struct config_source *cs, enum config_event_t type,
struct parse_event_data *data)
{
size_t offset;
@@ -1030,7 +1030,7 @@ static int do_event(struct config_source *cf, enum config_event_t type,
data->previous_type == type)
return 0;
- offset = cf->do_ftell(cf);
+ offset = cs->do_ftell(cs);
/*
* At EOF, the parser always "inserts" an extra '\n', therefore
* the end offset of the event is the current file position, otherwise
@@ -1050,12 +1050,12 @@ static int do_event(struct config_source *cf, enum config_event_t type,
return 0;
}
-static int git_parse_source(struct config_source *cf, config_fn_t fn,
+static int git_parse_source(struct config_source *cs, config_fn_t fn,
void *data, const struct config_options *opts)
{
int comment = 0;
size_t baselen = 0;
- struct strbuf *var = &cf->var;
+ struct strbuf *var = &cs->var;
int error_return = 0;
char *error_msg = NULL;
@@ -1070,7 +1070,7 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
for (;;) {
int c;
- c = get_next_char(cf);
+ c = get_next_char(cs);
if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their
@@ -1087,12 +1087,12 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
}
}
if (c == '\n') {
- if (cf->eof) {
- if (do_event(cf, CONFIG_EVENT_EOF, &event_data) < 0)
+ if (cs->eof) {
+ if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0)
return -1;
return 0;
}
- if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
comment = 0;
continue;
@@ -1100,23 +1100,23 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
if (comment)
continue;
if (isspace(c)) {
- if (do_event(cf, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
continue;
}
if (c == '#' || c == ';') {
- if (do_event(cf, CONFIG_EVENT_COMMENT, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_COMMENT, &event_data) < 0)
return -1;
comment = 1;
continue;
}
if (c == '[') {
- if (do_event(cf, CONFIG_EVENT_SECTION, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_SECTION, &event_data) < 0)
return -1;
/* Reset prior to determining a new stem */
strbuf_reset(var);
- if (get_base_var(cf, var) < 0 || var->len < 1)
+ if (get_base_var(cs, var) < 0 || var->len < 1)
break;
strbuf_addch(var, '.');
baselen = var->len;
@@ -1125,7 +1125,7 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
if (!isalpha(c))
break;
- if (do_event(cf, CONFIG_EVENT_ENTRY, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_ENTRY, &event_data) < 0)
return -1;
/*
@@ -1135,42 +1135,42 @@ static int git_parse_source(struct config_source *cf, config_fn_t fn,
*/
strbuf_setlen(var, baselen);
strbuf_addch(var, tolower(c));
- if (get_value(cf, fn, data, var) < 0)
+ if (get_value(cs, fn, data, var) < 0)
break;
}
- if (do_event(cf, CONFIG_EVENT_ERROR, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_ERROR, &event_data) < 0)
return -1;
- switch (cf->origin_type) {
+ switch (cs->origin_type) {
case CONFIG_ORIGIN_BLOB:
error_msg = xstrfmt(_("bad config line %d in blob %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_FILE:
error_msg = xstrfmt(_("bad config line %d in file %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_STDIN:
error_msg = xstrfmt(_("bad config line %d in standard input"),
- cf->linenr);
+ cs->linenr);
break;
case CONFIG_ORIGIN_SUBMODULE_BLOB:
error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_CMDLINE:
error_msg = xstrfmt(_("bad config line %d in command line %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
default:
error_msg = xstrfmt(_("bad config line %d in %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
}
switch (opts && opts->error_action ?
opts->error_action :
- cf->default_error_action) {
+ cs->default_error_action) {
case CONFIG_ERROR_DIE:
die("%s", error_msg);
break;
@@ -2280,13 +2280,13 @@ int config_with_options(config_fn_t fn, void *data,
return ret;
}
-static void configset_iter(struct config_reader *reader, struct config_set *cs,
+static void configset_iter(struct config_reader *reader, struct config_set *set,
config_fn_t fn, void *data)
{
int i, value_index;
struct string_list *values;
struct config_set_element *entry;
- struct configset_list *list = &cs->list;
+ struct configset_list *list = &set->list;
for (i = 0; i < list->nr; i++) {
entry = list->items[i].e;
@@ -2351,7 +2351,7 @@ void read_very_early_config(config_fn_t cb, void *data)
config_with_options(cb, data, NULL, &opts);
}
-static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+static struct config_set_element *configset_find_element(struct config_set *set, const char *key)
{
struct config_set_element k;
struct config_set_element *found_entry;
@@ -2365,13 +2365,13 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
hashmap_entry_init(&k.ent, strhash(normalized_key));
k.key = normalized_key;
- found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
+ found_entry = hashmap_get_entry(&set->config_hash, &k, ent, NULL);
free(normalized_key);
return found_entry;
}
static int configset_add_value(struct config_reader *reader,
- struct config_set *cs, const char *key,
+ struct config_set *set, const char *key,
const char *value)
{
struct config_set_element *e;
@@ -2379,7 +2379,7 @@ static int configset_add_value(struct config_reader *reader,
struct configset_list_item *l_item;
struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
- e = configset_find_element(cs, key);
+ e = configset_find_element(set, key);
/*
* Since the keys are being fed by git_config*() callback mechanism, they
* are already normalized. So simply add them without any further munging.
@@ -2389,12 +2389,12 @@ static int configset_add_value(struct config_reader *reader,
hashmap_entry_init(&e->ent, strhash(key));
e->key = xstrdup(key);
string_list_init_dup(&e->value_list);
- hashmap_add(&cs->config_hash, &e->ent);
+ hashmap_add(&set->config_hash, &e->ent);
}
si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
- ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
- l_item = &cs->list.items[cs->list.nr++];
+ ALLOC_GROW(set->list.items, set->list.nr + 1, set->list.alloc);
+ l_item = &set->list.items[set->list.nr++];
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
@@ -2429,33 +2429,33 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,
return strcmp(e1->key, e2->key);
}
-void git_configset_init(struct config_set *cs)
+void git_configset_init(struct config_set *set)
{
- hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
- cs->hash_initialized = 1;
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
+ set->hash_initialized = 1;
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
-void git_configset_clear(struct config_set *cs)
+void git_configset_clear(struct config_set *set)
{
struct config_set_element *entry;
struct hashmap_iter iter;
- if (!cs->hash_initialized)
+ if (!set->hash_initialized)
return;
- hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+ hashmap_for_each_entry(&set->config_hash, &iter, entry,
ent /* member name */) {
free(entry->key);
string_list_clear(&entry->value_list, 1);
}
- hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
- cs->hash_initialized = 0;
- free(cs->list.items);
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_clear_and_free(&set->config_hash, struct config_set_element, ent);
+ set->hash_initialized = 0;
+ free(set->list.items);
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
struct configset_add_data {
@@ -2471,15 +2471,15 @@ static int config_set_callback(const char *key, const char *value, void *cb)
return 0;
}
-int git_configset_add_file(struct config_set *cs, const char *filename)
+int git_configset_add_file(struct config_set *set, const char *filename)
{
struct configset_add_data data = CONFIGSET_ADD_INIT;
data.config_reader = &the_reader;
- data.config_set = cs;
+ data.config_set = set;
return git_config_from_file(config_set_callback, filename, &data);
}
-int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+int git_configset_get_value(struct config_set *set, const char *key, const char **value)
{
const struct string_list *values = NULL;
/*
@@ -2487,7 +2487,7 @@ int git_configset_get_value(struct config_set *cs, const char *key, const char *
* queried key in the files of the configset, the value returned will be the last
* value in the value list for that key.
*/
- values = git_configset_get_value_multi(cs, key);
+ values = git_configset_get_value_multi(set, key);
if (!values)
return 1;
@@ -2496,26 +2496,26 @@ int git_configset_get_value(struct config_set *cs, const char *key, const char *
return 0;
}
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+const struct string_list *git_configset_get_value_multi(struct config_set *set, const char *key)
{
- struct config_set_element *e = configset_find_element(cs, key);
+ struct config_set_element *e = configset_find_element(set, key);
return e ? &e->value_list : NULL;
}
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+int git_configset_get_string(struct config_set *set, const char *key, char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_string((const char **)dest, key, value);
else
return 1;
}
-static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+static int git_configset_get_string_tmp(struct config_set *set, const char *key,
const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
if (!value)
return config_error_nonbool(key);
*dest = value;
@@ -2525,51 +2525,51 @@ static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
}
}
-int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_int(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_int(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+int git_configset_get_ulong(struct config_set *set, const char *key, unsigned long *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_ulong(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+int git_configset_get_bool_or_int(struct config_set *set, const char *key,
int *is_bool, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool_or_int(key, value, is_bool);
return 0;
} else
return 1;
}
-int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_parse_maybe_bool(value);
if (*dest == -1)
return -1;
@@ -2578,10 +2578,10 @@ int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *de
return 1;
}
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_pathname(dest, key, value);
else
return 1;
@@ -2972,7 +2972,7 @@ static int store_aux_event(enum config_event_t type,
size_t begin, size_t end, void *data)
{
struct config_store_data *store = data;
- struct config_source *cf = store->config_reader->source;
+ struct config_source *cs = store->config_reader->source;
ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
store->parsed[store->parsed_nr].begin = begin;
@@ -2982,10 +2982,10 @@ static int store_aux_event(enum config_event_t type,
if (type == CONFIG_EVENT_SECTION) {
int (*cmpfn)(const char *, const char *, size_t);
- if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
- return error(_("invalid section name '%s'"), cf->var.buf);
+ if (cs->var.len < 2 || cs->var.buf[cs->var.len - 1] != '.')
+ return error(_("invalid section name '%s'"), cs->var.buf);
- if (cf->subsection_case_sensitive)
+ if (cs->subsection_case_sensitive)
cmpfn = strncasecmp;
else
cmpfn = strncmp;
@@ -2993,8 +2993,8 @@ static int store_aux_event(enum config_event_t type,
/* Is this the section we were looking for? */
store->is_keys_section =
store->parsed[store->parsed_nr].is_keys_section =
- cf->var.len - 1 == store->baselen &&
- !cmpfn(cf->var.buf, store->key, store->baselen);
+ cs->var.len - 1 == store->baselen &&
+ !cmpfn(cs->var.buf, store->key, store->baselen);
if (store->is_keys_section) {
store->section_seen = 1;
ALLOC_GROW(store->seen, store->seen_nr + 1,
--
gitgitgadget
^ permalink raw reply related [flat|nested] 72+ messages in thread
* Re: [PATCH v3 0/8] config.c: use struct for config reading state
2023-03-28 17:51 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo via GitGitGadget
` (7 preceding siblings ...)
2023-03-28 17:51 ` [PATCH v3 8/8] config.c: rename "struct config_source cf" Glen Choo via GitGitGadget
@ 2023-03-28 18:00 ` Glen Choo
2023-03-28 20:14 ` Junio C Hamano
8 siblings, 1 reply; 72+ messages in thread
From: Glen Choo @ 2023-03-28 18:00 UTC (permalink / raw)
To: Glen Choo via GitGitGadget, git
Cc: Jonathan Tan, Emily Shaffer, Calvin Wan,
Ævar Arnfjörð Bjarmason
"Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
> Note to Junio: 8/8 (which renames "cs" -> "set") conflicts with
> ab/config-multi-and-nonbool. I previously said that I'd rebase this, but
> presumably a remerge-diff is more ergonomic + flexible (let me know if I'm
> mistaken), so I'll send a remerge-diff in a reply (I don't trust GGG not to
> mangle the patch :/).
diff --git a/config.c b/config.c
remerge CONFLICT (content): Merge conflict in config.c
index b17658e1ba..159c404d0c 100644
--- a/config.c
+++ b/config.c
@@ -2351,15 +2351,9 @@ void read_very_early_config(config_fn_t cb, void *data)
config_with_options(cb, data, NULL, &opts);
}
-<<<<<<< HEAD
-static struct config_set_element *configset_find_element(struct config_set *set, const char *key)
-||||||| c000d91638
-static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
-=======
RESULT_MUST_BE_USED
-static int configset_find_element(struct config_set *cs, const char *key,
+static int configset_find_element(struct config_set *set, const char *key,
struct config_set_element **dest)
->>>>>>> gitster/ab/config-multi-and-nonbool
{
struct config_set_element k;
struct config_set_element *found_entry;
@@ -2392,15 +2386,9 @@ static int configset_add_value(struct config_reader *reader,
struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
int ret;
-<<<<<<< HEAD
- e = configset_find_element(set, key);
-||||||| c000d91638
- e = configset_find_element(cs, key);
-=======
- ret = configset_find_element(cs, key, &e);
+ ret = configset_find_element(set, key, &e);
if (ret)
return ret;
->>>>>>> gitster/ab/config-multi-and-nonbool
/*
* Since the keys are being fed by git_config*() callback mechanism, they
* are already normalized. So simply add them without any further munging.
@@ -2510,40 +2498,21 @@ int git_configset_get_value(struct config_set *set, const char *key, const char
* queried key in the files of the configset, the value returned will be the last
* value in the value list for that key.
*/
-<<<<<<< HEAD
- values = git_configset_get_value_multi(set, key);
-||||||| c000d91638
- values = git_configset_get_value_multi(cs, key);
-=======
- if ((ret = git_configset_get_value_multi(cs, key, &values)))
+ if ((ret = git_configset_get_value_multi(set, key, &values)))
return ret;
->>>>>>> gitster/ab/config-multi-and-nonbool
assert(values->nr > 0);
*value = values->items[values->nr - 1].string;
return 0;
}
-<<<<<<< HEAD
-const struct string_list *git_configset_get_value_multi(struct config_set *set, const char *key)
-||||||| c000d91638
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
-=======
-int git_configset_get_value_multi(struct config_set *cs, const char *key,
+int git_configset_get_value_multi(struct config_set *set, const char *key,
const struct string_list **dest)
->>>>>>> gitster/ab/config-multi-and-nonbool
-{
-<<<<<<< HEAD
- struct config_set_element *e = configset_find_element(set, key);
- return e ? &e->value_list : NULL;
-||||||| c000d91638
- struct config_set_element *e = configset_find_element(cs, key);
- return e ? &e->value_list : NULL;
-=======
+{
struct config_set_element *e;
int ret;
- if ((ret = configset_find_element(cs, key, &e)))
+ if ((ret = configset_find_element(set, key, &e)))
return ret;
else if (!e)
return 1;
@@ -2557,12 +2526,12 @@ static int check_multi_string(struct string_list_item *item, void *util)
return item->string ? 0 : config_error_nonbool(util);
}
-int git_configset_get_string_multi(struct config_set *cs, const char *key,
+int git_configset_get_string_multi(struct config_set *set, const char *key,
const struct string_list **dest)
{
int ret;
- if ((ret = git_configset_get_value_multi(cs, key, dest)))
+ if ((ret = git_configset_get_value_multi(set, key, dest)))
return ret;
if ((ret = for_each_string_list((struct string_list *)*dest,
check_multi_string, (void *)key)))
@@ -2571,17 +2540,16 @@ int git_configset_get_string_multi(struct config_set *cs, const char *key,
return 0;
}
-int git_configset_get(struct config_set *cs, const char *key)
+int git_configset_get(struct config_set *set, const char *key)
{
struct config_set_element *e;
int ret;
- if ((ret = configset_find_element(cs, key, &e)))
+ if ((ret = configset_find_element(set, key, &e)))
return ret;
else if (!e)
return 1;
return 0;
->>>>>>> gitster/ab/config-multi-and-nonbool
}
int git_configset_get_string(struct config_set *set, const char *key, char **dest)
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v3 0/8] config.c: use struct for config reading state
2023-03-28 18:00 ` [PATCH v3 0/8] config.c: use struct for config reading state Glen Choo
@ 2023-03-28 20:14 ` Junio C Hamano
2023-03-28 21:24 ` Glen Choo
0 siblings, 1 reply; 72+ messages in thread
From: Junio C Hamano @ 2023-03-28 20:14 UTC (permalink / raw)
To: Glen Choo
Cc: Glen Choo via GitGitGadget, git, Jonathan Tan, Emily Shaffer,
Calvin Wan, Ævar Arnfjörð Bjarmason
Glen Choo <chooglen@google.com> writes:
> "Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> Note to Junio: 8/8 (which renames "cs" -> "set") conflicts with
>> ab/config-multi-and-nonbool. I previously said that I'd rebase this, but
>> presumably a remerge-diff is more ergonomic + flexible (let me know if I'm
>> mistaken), so I'll send a remerge-diff in a reply (I don't trust GGG not to
>> mangle the patch :/).
A patch that is indented by two places would not be mechanically
applicable anyway and even without such indentation, the hunk-side
markers like c000d91638, gitster/ab/config-multi-and-nonbool, etc.
you have in the patch would be different from conflicts I would see,
so it wouldn't be very useful. There should probably be a mode
where remerge-diff would omit these labels to help mechanical
application of such a "patch".
In any case, remerge-diff certainly helps eyeballing the result.
In this case, because the difference between v2 and v3 does not
overlap with the area that v2 (or v3 for that matter) would conflict
with other topics in flight, my rerere database can clealy reuse the
recorded resolution for conflicts between this iteration and the
topics in flight just fine. I'll push out the integration result
after I am done with other topics.
Thanks.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [PATCH v3 0/8] config.c: use struct for config reading state
2023-03-28 20:14 ` Junio C Hamano
@ 2023-03-28 21:24 ` Glen Choo
0 siblings, 0 replies; 72+ messages in thread
From: Glen Choo @ 2023-03-28 21:24 UTC (permalink / raw)
To: Junio C Hamano
Cc: Glen Choo via GitGitGadget, git, Jonathan Tan, Emily Shaffer,
Calvin Wan, Ævar Arnfjörð Bjarmason
Junio C Hamano <gitster@pobox.com> writes:
> Glen Choo <chooglen@google.com> writes:
>
>> "Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
>>
>>> Note to Junio: 8/8 (which renames "cs" -> "set") conflicts with
>>> ab/config-multi-and-nonbool. I previously said that I'd rebase this, but
>>> presumably a remerge-diff is more ergonomic + flexible (let me know if I'm
>>> mistaken), so I'll send a remerge-diff in a reply (I don't trust GGG not to
>>> mangle the patch :/).
>
> A patch that is indented by two places would not be mechanically
> applicable anyway and even without such indentation, the hunk-side
> markers like c000d91638, gitster/ab/config-multi-and-nonbool, etc.
> you have in the patch would be different from conflicts I would see,
> so it wouldn't be very useful. There should probably be a mode
> where remerge-diff would omit these labels to help mechanical
> application of such a "patch".
Ah, bummer :(
I was thinking about that as well. remerge-diff actually outputs
left and right in "--format=reference", which I then manually modified
to reflect the conflicted version in the working tree, giving the final
result here. I wonder what remerge-diff or "git apply" could do instead.
^ permalink raw reply [flat|nested] 72+ messages in thread