summaryrefslogtreecommitdiff
path: root/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'config.c')
-rw-r--r--config.c1228
1 files changed, 662 insertions, 566 deletions
diff --git a/config.c b/config.c
index 9b0e9c9..ae3652b 100644
--- a/config.c
+++ b/config.c
@@ -5,24 +5,39 @@
* Copyright (C) Johannes Schindelin, 2005
*
*/
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "advice.h"
#include "date.h"
#include "branch.h"
#include "config.h"
+#include "parse.h"
+#include "convert.h"
#include "environment.h"
+#include "gettext.h"
+#include "ident.h"
#include "repository.h"
#include "lockfile.h"
+#include "mailmap.h"
+#include "attr.h"
#include "exec-cmd.h"
#include "strbuf.h"
#include "quote.h"
#include "hashmap.h"
#include "string-list.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
+#include "pager.h"
+#include "path.h"
#include "utf8.h"
-#include "dir.h"
#include "color.h"
#include "refs.h"
-#include "worktree.h"
+#include "setup.h"
+#include "strvec.h"
+#include "trace2.h"
+#include "wildmatch.h"
+#include "ws.h"
+#include "write-or-die.h"
struct config_source {
struct config_source *prev;
@@ -49,37 +64,21 @@ 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
- * 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 "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 config_source *cf;
-static struct key_value_info *current_config_kvi;
+static int pack_compression_seen;
+static int zlib_compression_seen;
/*
- * 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).
+ * Config that comes from trusted scopes, namely:
+ * - CONFIG_SCOPE_SYSTEM (e.g. /etc/gitconfig)
+ * - CONFIG_SCOPE_GLOBAL (e.g. $HOME/.gitconfig, $XDG_CONFIG_HOME/git)
+ * - CONFIG_SCOPE_COMMAND (e.g. "-c" option, environment variables)
+ *
+ * This is declared here for code cleanliness, but unlike the other
+ * static variables, this does not hold config parser state.
*/
-static enum config_scope current_parsing_scope;
-
-static int pack_compression_seen;
-static int zlib_compression_seen;
+static struct config_set protected_config;
static int config_file_fgetc(struct config_source *conf)
{
@@ -96,7 +95,6 @@ static long config_file_ftell(struct config_source *conf)
return ftell(conf->u.file);
}
-
static int config_buf_fgetc(struct config_source *conf)
{
if (conf->u.buf.pos < conf->u.buf.len)
@@ -128,6 +126,7 @@ struct config_include_data {
void *data;
const struct config_options *opts;
struct git_config_source *config_source;
+ struct repository *repo;
/*
* All remote URLs discovered when reading all config files.
@@ -136,7 +135,8 @@ struct config_include_data {
};
#define CONFIG_INCLUDE_INIT { 0 }
-static int git_config_include(const char *var, const char *value, void *data);
+static int git_config_include(const char *var, const char *value,
+ const struct config_context *ctx, void *data);
#define MAX_INCLUDE_DEPTH 10
static const char include_depth_advice[] = N_(
@@ -145,7 +145,9 @@ 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(const struct key_value_info *kvi,
+ const char *path,
+ struct config_include_data *inc)
{
int ret = 0;
struct strbuf buf = STRBUF_INIT;
@@ -166,14 +168,14 @@ static int handle_path_include(const char *path, struct config_include_data *inc
if (!is_absolute_path(path)) {
char *slash;
- if (!cf || !cf->path) {
+ if (!kvi || !kvi->path) {
ret = error(_("relative config includes must come from files"));
goto cleanup;
}
- slash = find_last_dir_sep(cf->path);
+ slash = find_last_dir_sep(kvi->path);
if (slash)
- strbuf_add(&buf, cf->path, slash - cf->path + 1);
+ strbuf_add(&buf, kvi->path, slash - kvi->path + 1);
strbuf_addstr(&buf, path);
path = buf.buf;
}
@@ -181,10 +183,11 @@ static int handle_path_include(const char *path, struct config_include_data *inc
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 :
+ !kvi ? "<unknown>" :
+ kvi->filename ? kvi->filename :
"the command line");
- ret = git_config_from_file(git_config_include, path, inc);
+ ret = git_config_from_file_with_options(git_config_include, path, inc,
+ kvi->scope, NULL);
inc->depth--;
}
cleanup:
@@ -199,7 +202,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(const struct key_value_info *kvi,
+ struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
char *expanded;
@@ -215,11 +219,11 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
const char *slash;
- if (!cf || !cf->path)
+ if (!kvi || !kvi->path)
return error(_("relative config include "
"conditionals must come from files"));
- strbuf_realpath(&path, cf->path, 1);
+ strbuf_realpath(&path, kvi->path, 1);
slash = find_last_dir_sep(path.buf);
if (!slash)
BUG("how is this possible?");
@@ -234,7 +238,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(const struct key_value_info *kvi,
+ const struct config_options *opts,
const char *cond, size_t cond_len, int icase)
{
struct strbuf text = STRBUF_INIT;
@@ -250,7 +255,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(kvi, &pattern);
again:
if (prefix < 0)
@@ -312,7 +317,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
return ret;
}
-static int add_remote_url(const char *var, const char *value, void *data)
+static int add_remote_url(const char *var, const char *value,
+ const struct config_context *ctx UNUSED, void *data)
{
struct string_list *remote_urls = data;
const char *remote_name;
@@ -331,27 +337,18 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct config_source *store_cf = cf;
- 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;
- 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);
-
- cf = store_cf;
- current_config_kvi = store_kvi;
- current_parsing_scope = store_scope;
+ config_with_options(add_remote_url, inc->remote_urls,
+ inc->config_source, inc->repo, &opts);
}
-static int forbid_remote_url(const char *var, const char *value, void *data)
+static int forbid_remote_url(const char *var, const char *value UNUSED,
+ const struct config_context *ctx UNUSED,
+ void *data UNUSED)
{
const char *remote_name;
size_t remote_name_len;
@@ -394,15 +391,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(const struct key_value_info *kvi,
+ 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(kvi, 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(kvi, 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,
@@ -413,7 +411,9 @@ static int include_condition_is_true(struct config_include_data *inc,
return 0;
}
-static int git_config_include(const char *var, const char *value, void *data)
+static int git_config_include(const char *var, const char *value,
+ const struct config_context *ctx,
+ void *data)
{
struct config_include_data *inc = data;
const char *cond, *key;
@@ -424,21 +424,21 @@ static int git_config_include(const char *var, const char *value, void *data)
* Pass along all values, including "include" directives; this makes it
* possible to query information on the includes themselves.
*/
- ret = inc->fn(var, value, inc->data);
+ ret = inc->fn(var, value, ctx, inc->data);
if (ret < 0)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(value, inc);
+ ret = handle_path_include(ctx->kvi, 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(ctx->kvi, 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(ctx->kvi, value, inc);
inc->fn = old_fn;
}
@@ -596,27 +596,45 @@ out_free_ret_1:
}
static int config_parse_pair(const char *key, const char *value,
- config_fn_t fn, void *data)
+ struct key_value_info *kvi,
+ config_fn_t fn, void *data)
{
char *canonical_name;
int ret;
+ struct config_context ctx = {
+ .kvi = kvi,
+ };
if (!strlen(key))
return error(_("empty config key"));
if (git_config_parse_key(key, &canonical_name, NULL))
return -1;
- ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+ ret = (fn(canonical_name, value, &ctx, data) < 0) ? -1 : 0;
free(canonical_name);
return ret;
}
+
+/* for values read from `git_config_from_parameters()` */
+void kvi_from_param(struct key_value_info *out)
+{
+ out->filename = NULL;
+ out->linenr = -1;
+ out->origin_type = CONFIG_ORIGIN_CMDLINE;
+ out->scope = CONFIG_SCOPE_COMMAND;
+ out->path = NULL;
+}
+
int git_config_parse_parameter(const char *text,
config_fn_t fn, void *data)
{
const char *value;
struct strbuf **pair;
int ret;
+ struct key_value_info kvi = KVI_INIT;
+
+ kvi_from_param(&kvi);
pair = strbuf_split_str(text, '=', 2);
if (!pair[0])
@@ -635,12 +653,13 @@ int git_config_parse_parameter(const char *text,
return error(_("bogus config parameter: %s"), text);
}
- ret = config_parse_pair(pair[0]->buf, value, fn, data);
+ ret = config_parse_pair(pair[0]->buf, value, &kvi, fn, data);
strbuf_list_free(pair);
return ret;
}
-static int parse_config_env_list(char *env, config_fn_t fn, void *data)
+static int parse_config_env_list(char *env, struct key_value_info *kvi,
+ config_fn_t fn, void *data)
{
char *cur = env;
while (cur && *cur) {
@@ -674,7 +693,7 @@ static int parse_config_env_list(char *env, config_fn_t fn, void *data)
CONFIG_DATA_ENVIRONMENT);
}
- if (config_parse_pair(key, value, fn, data) < 0)
+ if (config_parse_pair(key, value, kvi, fn, data) < 0)
return -1;
}
else {
@@ -698,13 +717,9 @@ 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;
-
- memset(&source, 0, sizeof(source));
- source.prev = cf;
- source.origin_type = CONFIG_ORIGIN_CMDLINE;
- cf = &source;
+ struct key_value_info kvi = KVI_INIT;
+ kvi_from_param(&kvi);
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
unsigned long count;
@@ -740,7 +755,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
}
strbuf_reset(&envvar);
- if (config_parse_pair(key, value, fn, data) < 0) {
+ if (config_parse_pair(key, value, &kvi, fn, data) < 0) {
ret = -1;
goto out;
}
@@ -751,7 +766,7 @@ int git_config_from_parameters(config_fn_t fn, void *data)
if (env) {
/* sq_dequote will write over it */
envw = xstrdup(env);
- if (parse_config_env_list(envw, fn, data) < 0) {
+ if (parse_config_env_list(envw, &kvi, fn, data) < 0) {
ret = -1;
goto out;
}
@@ -761,25 +776,24 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- cf = source.prev;
return ret;
}
-static int get_next_char(void)
+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
@@ -787,39 +801,44 @@ static int get_next_char(void)
* 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(void)
+static char *parse_value(struct config_source *cs)
{
- int quote = 0, comment = 0, space = 0;
+ int quote = 0, comment = 0;
+ size_t trim_len = 0;
- strbuf_reset(&cf->value);
+ strbuf_reset(&cs->value);
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cs);
if (c == '\n') {
if (quote) {
- cf->linenr--;
+ cs->linenr--;
return NULL;
}
- return cf->value.buf;
+ if (trim_len)
+ strbuf_setlen(&cs->value, trim_len);
+ return cs->value.buf;
}
if (comment)
continue;
if (isspace(c) && !quote) {
- if (cf->value.len)
- space++;
+ if (!trim_len)
+ trim_len = cs->value.len;
+ if (cs->value.len)
+ strbuf_addch(&cs->value, c);
continue;
}
if (!quote) {
@@ -828,10 +847,10 @@ static char *parse_value(void)
continue;
}
}
- for (; space; space--)
- strbuf_addch(&cf->value, ' ');
+ if (trim_len)
+ trim_len = 0;
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cs);
switch (c) {
case '\n':
continue;
@@ -851,27 +870,31 @@ static char *parse_value(void)
default:
return NULL;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
continue;
}
if (c == '"') {
- quote = 1-quote;
+ quote = 1 - quote;
continue;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
}
}
-static int get_value(config_fn_t fn, void *data, struct strbuf *name)
+static int get_value(struct config_source *cs, struct key_value_info *kvi,
+ config_fn_t fn, void *data, struct strbuf *name)
{
int c;
char *value;
int ret;
+ struct config_context ctx = {
+ .kvi = kvi,
+ };
/* Get the full name */
for (;;) {
- c = get_next_char();
- if (cf->eof)
+ c = get_next_char(cs);
+ if (cs->eof)
break;
if (!iskeychar(c))
break;
@@ -879,13 +902,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(cs);
value = NULL;
if (c != '\n') {
if (c != '=')
return -1;
- value = parse_value();
+ value = parse_value(cs);
if (!value)
return -1;
}
@@ -894,20 +917,22 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
* the line we just parsed during the call to fn to get
* accurate line number in error messages.
*/
- cf->linenr--;
- ret = fn(name->buf, value, data);
+ cs->linenr--;
+ kvi->linenr = cs->linenr;
+ ret = fn(name->buf, value, &ctx, data);
if (ret >= 0)
- cf->linenr++;
+ cs->linenr++;
return ret;
}
-static int get_extended_base_var(struct strbuf *name, int c)
+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();
+ c = get_next_char(cs);
} while (isspace(c));
/* We require the format to be '[base "extension"]' */
@@ -916,13 +941,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(cs);
if (c == '\n')
goto error_incomplete_line;
if (c == '"')
break;
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cs);
if (c == '\n')
goto error_incomplete_line;
}
@@ -930,25 +955,25 @@ static int get_extended_base_var(struct strbuf *name, int c)
}
/* Final ']' */
- if (get_next_char() != ']')
+ if (get_next_char(cs) != ']')
return -1;
return 0;
error_incomplete_line:
- cf->linenr--;
+ cs->linenr--;
return -1;
}
-static int get_base_var(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();
- 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(name, c);
+ return get_extended_base_var(cs, name, c);
if (!iskeychar(c) && c != '.')
return -1;
strbuf_addch(name, tolower(c));
@@ -961,7 +986,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 *cs, enum config_event_t type,
+ struct parse_event_data *data)
{
size_t offset;
@@ -972,7 +998,7 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
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
@@ -983,7 +1009,7 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
if (data->previous_type != CONFIG_EVENT_EOF &&
data->opts->event_fn(data->previous_type, data->previous_offset,
- offset, data->opts->event_fn_data) < 0)
+ offset, cs, data->opts->event_fn_data) < 0)
return -1;
data->previous_type = type;
@@ -992,12 +1018,24 @@ 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,
+static void kvi_from_source(struct config_source *cs,
+ enum config_scope scope,
+ struct key_value_info *out)
+{
+ out->filename = strintern(cs->name);
+ out->origin_type = cs->origin_type;
+ out->linenr = cs->linenr;
+ out->scope = scope;
+ out->path = cs->path;
+}
+
+static int git_parse_source(struct config_source *cs, config_fn_t fn,
+ struct key_value_info *kvi, 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;
@@ -1012,7 +1050,7 @@ static int git_parse_source(config_fn_t fn, void *data,
for (;;) {
int c;
- c = get_next_char();
+ 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
@@ -1029,12 +1067,12 @@ 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 (cs->eof) {
+ if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0)
return -1;
return 0;
}
- if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
comment = 0;
continue;
@@ -1042,23 +1080,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(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
continue;
}
if (c == '#' || c == ';') {
- if (do_event(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(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(var) < 0 || var->len < 1)
+ if (get_base_var(cs, var) < 0 || var->len < 1)
break;
strbuf_addch(var, '.');
baselen = var->len;
@@ -1067,7 +1105,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(cs, CONFIG_EVENT_ENTRY, &event_data) < 0)
return -1;
/*
@@ -1077,42 +1115,42 @@ 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(cs, kvi, fn, data, var) < 0)
break;
}
- if (do_event(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;
@@ -1130,199 +1168,81 @@ static int git_parse_source(config_fn_t fn, void *data,
return error_return;
}
-static uintmax_t get_unit_factor(const char *end)
-{
- if (!*end)
- return 1;
- else if (!strcasecmp(end, "k"))
- return 1024;
- else if (!strcasecmp(end, "m"))
- return 1024 * 1024;
- else if (!strcasecmp(end, "g"))
- return 1024 * 1024 * 1024;
- return 0;
-}
-
-static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
-{
- if (value && *value) {
- char *end;
- intmax_t val;
- uintmax_t uval;
- uintmax_t factor;
-
- errno = 0;
- val = strtoimax(value, &end, 0);
- if (errno == ERANGE)
- return 0;
- factor = get_unit_factor(end);
- if (!factor) {
- errno = EINVAL;
- return 0;
- }
- uval = val < 0 ? -val : val;
- if (unsigned_mult_overflows(factor, uval) ||
- factor * uval > max) {
- errno = ERANGE;
- return 0;
- }
- val *= factor;
- *ret = val;
- return 1;
- }
- errno = EINVAL;
- return 0;
-}
-
-static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
-{
- if (value && *value) {
- char *end;
- uintmax_t val;
- uintmax_t factor;
-
- errno = 0;
- val = strtoumax(value, &end, 0);
- if (errno == ERANGE)
- return 0;
- factor = get_unit_factor(end);
- if (!factor) {
- errno = EINVAL;
- return 0;
- }
- if (unsigned_mult_overflows(factor, val) ||
- factor * val > max) {
- errno = ERANGE;
- return 0;
- }
- val *= factor;
- *ret = val;
- return 1;
- }
- errno = EINVAL;
- return 0;
-}
-
-static int git_parse_int(const char *value, int *ret)
-{
- intmax_t tmp;
- if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
- return 0;
- *ret = tmp;
- return 1;
-}
-
-static int git_parse_int64(const char *value, int64_t *ret)
-{
- intmax_t tmp;
- if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
- return 0;
- *ret = tmp;
- return 1;
-}
-
-int git_parse_ulong(const char *value, unsigned long *ret)
-{
- uintmax_t tmp;
- if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
- return 0;
- *ret = tmp;
- return 1;
-}
-
-int git_parse_ssize_t(const char *value, ssize_t *ret)
-{
- intmax_t tmp;
- if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
- return 0;
- *ret = tmp;
- return 1;
-}
-
NORETURN
-static void die_bad_number(const char *name, const char *value)
+static void die_bad_number(const char *name, const char *value,
+ const struct key_value_info *kvi)
{
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");
+ if (!kvi)
+ BUG("kvi should not be NULL");
+
if (!value)
value = "";
- if (!(cf && cf->name))
+ if (!kvi->filename)
die(_(bad_numeric), value, name, _(error_type));
- switch (cf->origin_type) {
+ switch (kvi->origin_type) {
case CONFIG_ORIGIN_BLOB:
die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
- value, name, cf->name, _(error_type));
+ value, name, kvi->filename, _(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, kvi->filename, _(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, kvi->filename, _(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, kvi->filename, _(error_type));
default:
die(_("bad numeric config value '%s' for '%s' in %s: %s"),
- value, name, cf->name, _(error_type));
+ value, name, kvi->filename, _(error_type));
}
}
-int git_config_int(const char *name, const char *value)
+int git_config_int(const char *name, const char *value,
+ const struct key_value_info *kvi)
{
int ret;
if (!git_parse_int(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(name, value, kvi);
return ret;
}
-int64_t git_config_int64(const char *name, const char *value)
+int64_t git_config_int64(const char *name, const char *value,
+ const struct key_value_info *kvi)
{
int64_t ret;
if (!git_parse_int64(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(name, value, kvi);
return ret;
}
-unsigned long git_config_ulong(const char *name, const char *value)
+unsigned long git_config_ulong(const char *name, const char *value,
+ const struct key_value_info *kvi)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(name, value, kvi);
return ret;
}
-ssize_t git_config_ssize_t(const char *name, const char *value)
+ssize_t git_config_ssize_t(const char *name, const char *value,
+ const struct key_value_info *kvi)
{
ssize_t ret;
if (!git_parse_ssize_t(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(name, value, kvi);
return ret;
}
-static int git_parse_maybe_bool_text(const char *value)
-{
- if (!value)
- return 1;
- if (!*value)
- return 0;
- if (!strcasecmp(value, "true")
- || !strcasecmp(value, "yes")
- || !strcasecmp(value, "on"))
- return 1;
- if (!strcasecmp(value, "false")
- || !strcasecmp(value, "no")
- || !strcasecmp(value, "off"))
- return 0;
- return -1;
-}
-
static const struct fsync_component_name {
const char *name;
enum fsync_component component_bits;
@@ -1397,17 +1317,8 @@ next_name:
return (current & ~negative) | positive;
}
-int git_parse_maybe_bool(const char *value)
-{
- int v = git_parse_maybe_bool_text(value);
- if (0 <= v)
- return v;
- if (git_parse_int(value, &v))
- return !!v;
- return -1;
-}
-
-int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+int git_config_bool_or_int(const char *name, const char *value,
+ const struct key_value_info *kvi, int *is_bool)
{
int v = git_parse_maybe_bool_text(value);
if (0 <= v) {
@@ -1415,7 +1326,7 @@ int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
return v;
}
*is_bool = 0;
- return git_config_int(name, value);
+ return git_config_int(name, value, kvi);
}
int git_config_bool(const char *name, const char *value)
@@ -1463,7 +1374,8 @@ int git_config_color(char *dest, const char *var, const char *value)
return 0;
}
-static int git_default_core_config(const char *var, const char *value, void *cb)
+static int git_default_core_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
/* This needs a better name */
if (!strcmp(var, "core.filemode")) {
@@ -1475,10 +1387,15 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "core.checkstat")) {
+ if (!value)
+ return config_error_nonbool(var);
if (!strcasecmp(value, "default"))
check_stat = 1;
else if (!strcasecmp(value, "minimal"))
check_stat = 0;
+ else
+ return error(_("invalid value for '%s': '%s'"),
+ var, value);
}
if (!strcmp(var, "core.quotepath")) {
@@ -1540,7 +1457,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
else if (!git_parse_maybe_bool_text(value))
default_abbrev = the_hash_algo->hexsz;
else {
- int abbrev = git_config_int(var, value);
+ int abbrev = git_config_int(var, value, ctx->kvi);
if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
return error(_("abbrev length out of range: %d"), abbrev);
default_abbrev = abbrev;
@@ -1552,7 +1469,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
return set_disambiguate_hint_config(var, value);
if (!strcmp(var, "core.loosecompression")) {
- int level = git_config_int(var, value);
+ int level = git_config_int(var, value, ctx->kvi);
if (level == -1)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1563,7 +1480,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "core.compression")) {
- int level = git_config_int(var, value);
+ int level = git_config_int(var, value, ctx->kvi);
if (level == -1)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1577,7 +1494,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "core.packedgitwindowsize")) {
int pgsz_x2 = getpagesize() * 2;
- packed_git_window_size = git_config_ulong(var, value);
+ packed_git_window_size = git_config_ulong(var, value, ctx->kvi);
/* This value must be multiple of (pagesize * 2) */
packed_git_window_size /= pgsz_x2;
@@ -1588,17 +1505,17 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "core.bigfilethreshold")) {
- big_file_threshold = git_config_ulong(var, value);
+ big_file_threshold = git_config_ulong(var, value, ctx->kvi);
return 0;
}
if (!strcmp(var, "core.packedgitlimit")) {
- packed_git_limit = git_config_ulong(var, value);
+ packed_git_limit = git_config_ulong(var, value, ctx->kvi);
return 0;
}
if (!strcmp(var, "core.deltabasecachelimit")) {
- delta_base_cache_limit = git_config_ulong(var, value);
+ delta_base_cache_limit = git_config_ulong(var, value, ctx->kvi);
return 0;
}
@@ -1635,12 +1552,12 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
return 0;
}
- if (!strcmp(var, "core.checkroundtripencoding")) {
- check_roundtrip_encoding = xstrdup(value);
- return 0;
- }
+ if (!strcmp(var, "core.checkroundtripencoding"))
+ return git_config_string(&check_roundtrip_encoding, var, value);
if (!strcmp(var, "core.notesref")) {
+ if (!value)
+ return config_error_nonbool(var);
notes_ref_name = xstrdup(value);
return 0;
}
@@ -1648,24 +1565,29 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "core.editor"))
return git_config_string(&editor_program, var, value);
- if (!strcmp(var, "core.commentchar")) {
+ if (!strcmp(var, "core.commentchar") ||
+ !strcmp(var, "core.commentstring")) {
if (!value)
return config_error_nonbool(var);
else if (!strcasecmp(value, "auto"))
auto_comment_line_char = 1;
- else if (value[0] && !value[1]) {
- comment_line_char = value[0];
+ else if (value[0]) {
+ if (strchr(value, '\n'))
+ return error(_("%s cannot contain newline"), var);
+ comment_line_str = xstrdup(value);
auto_comment_line_char = 0;
} else
- return error(_("core.commentChar should only be one character"));
+ return error(_("%s must have at least one character"), var);
return 0;
}
if (!strcmp(var, "core.askpass"))
return git_config_string(&askpass_program, var, value);
- if (!strcmp(var, "core.excludesfile"))
+ if (!strcmp(var, "core.excludesfile")) {
+ free((char *)excludes_file);
return git_config_pathname(&excludes_file, var, value);
+ }
if (!strcmp(var, "core.whitespace")) {
if (!value)
@@ -1708,6 +1630,8 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "core.createobject")) {
+ if (!value)
+ return config_error_nonbool(var);
if (!strcmp(value, "rename"))
object_creation_mode = OBJECT_CREATION_USES_RENAMES;
else if (!strcmp(value, "link"))
@@ -1742,13 +1666,13 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
return 0;
}
- if (!strcmp(var, "core.usereplacerefs")) {
- read_replace_refs = git_config_bool(var, value);
+ if (!strcmp(var, "core.maxtreedepth")) {
+ max_allowed_tree_depth = git_config_int(var, value, ctx->kvi);
return 0;
}
/* Add other config variables here and to Documentation/config.txt. */
- return platform_core_config(var, value, cb);
+ return platform_core_config(var, value, ctx, cb);
}
static int git_default_sparse_config(const char *var, const char *value)
@@ -1850,15 +1774,28 @@ static int git_default_mailmap_config(const char *var, const char *value)
return 0;
}
-int git_default_config(const char *var, const char *value, void *cb)
+static int git_default_attr_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "attr.tree"))
+ return git_config_string(&git_attr_tree, var, value);
+
+ /*
+ * Add other attribute related config variables here and to
+ * Documentation/config/attr.txt.
+ */
+ return 0;
+}
+
+int git_default_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
if (starts_with(var, "core."))
- return git_default_core_config(var, value, cb);
+ return git_default_core_config(var, value, ctx, cb);
if (starts_with(var, "user.") ||
starts_with(var, "author.") ||
starts_with(var, "committer."))
- return git_ident_config(var, value, cb);
+ return git_ident_config(var, value, ctx, cb);
if (starts_with(var, "i18n."))
return git_default_i18n_config(var, value);
@@ -1872,6 +1809,9 @@ int git_default_config(const char *var, const char *value, void *cb)
if (starts_with(var, "mailmap."))
return git_default_mailmap_config(var, value);
+ if (starts_with(var, "attr."))
+ return git_default_attr_config(var, value);
+
if (starts_with(var, "advice.") || starts_with(var, "color.advice"))
return git_default_advice_config(var, value);
@@ -1881,12 +1821,12 @@ int git_default_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "pack.packsizelimit")) {
- pack_size_limit_cfg = git_config_ulong(var, value);
+ pack_size_limit_cfg = git_config_ulong(var, value, ctx->kvi);
return 0;
}
if (!strcmp(var, "pack.compression")) {
- int level = git_config_int(var, value);
+ int level = git_config_int(var, value, ctx->kvi);
if (level == -1)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
@@ -1908,36 +1848,36 @@ 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_source *top, config_fn_t fn,
+ void *data, enum config_scope scope,
const struct config_options *opts)
{
+ struct key_value_info kvi = KVI_INIT;
int ret;
/* push config-file parsing state stack */
- top->prev = cf;
top->linenr = 1;
top->eof = 0;
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- cf = top;
+ kvi_from_source(top, scope, &kvi);
- ret = git_parse_source(fn, data, opts);
+ ret = git_parse_source(top, fn, &kvi, data, opts);
- /* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- cf = top->prev;
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)
+ const enum config_origin_type origin_type,
+ const char *name, const char *path, FILE *f,
+ void *data, enum config_scope scope,
+ const struct config_options *opts)
{
- struct config_source top;
+ struct config_source top = CONFIG_SOURCE_INIT;
int ret;
top.u.file = f;
@@ -1950,28 +1890,31 @@ 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(&top, fn, data, scope, opts);
funlockfile(f);
return ret;
}
-static int git_config_from_stdin(config_fn_t fn, void *data)
+static int git_config_from_stdin(config_fn_t fn, void *data,
+ enum config_scope scope)
{
return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
- data, NULL);
+ data, scope, NULL);
}
int git_config_from_file_with_options(config_fn_t fn, const char *filename,
- void *data,
+ void *data, enum config_scope scope,
const struct config_options *opts)
{
int ret = -1;
FILE *f;
+ if (!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);
+ filename, f, data, scope, opts);
fclose(f);
}
return ret;
@@ -1979,15 +1922,17 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
int git_config_from_file(config_fn_t fn, const char *filename, void *data)
{
- return git_config_from_file_with_options(fn, filename, data, NULL);
+ return git_config_from_file_with_options(fn, filename, data,
+ CONFIG_SCOPE_UNKNOWN, NULL);
}
int git_config_from_mem(config_fn_t fn,
const enum config_origin_type origin_type,
const char *name, const char *buf, size_t len,
- void *data, const struct config_options *opts)
+ void *data, enum config_scope scope,
+ 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;
@@ -2000,14 +1945,15 @@ 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(&top, fn, data, scope, opts);
}
int git_config_from_blob_oid(config_fn_t fn,
const char *name,
struct repository *repo,
const struct object_id *oid,
- void *data)
+ void *data,
+ enum config_scope scope)
{
enum object_type type;
char *buf;
@@ -2023,7 +1969,7 @@ int git_config_from_blob_oid(config_fn_t fn,
}
ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size,
- data, NULL);
+ data, scope, NULL);
free(buf);
return ret;
@@ -2032,13 +1978,14 @@ int git_config_from_blob_oid(config_fn_t fn,
static int git_config_from_blob_ref(config_fn_t fn,
struct repository *repo,
const char *name,
- void *data)
+ void *data,
+ enum config_scope scope)
{
struct object_id oid;
if (repo_get_oid(repo, name, &oid) < 0)
return error(_("unable to resolve config blob '%s'"), name);
- return git_config_from_blob_oid(fn, name, repo, &oid, data);
+ return git_config_from_blob_oid(fn, name, repo, &oid, data, scope);
}
char *git_system_config(void)
@@ -2050,7 +1997,27 @@ char *git_system_config(void)
return system_config;
}
-void git_global_config(char **user_out, char **xdg_out)
+char *git_global_config(void)
+{
+ char *user_config, *xdg_config;
+
+ git_global_config_paths(&user_config, &xdg_config);
+ if (!user_config) {
+ free(xdg_config);
+ return NULL;
+ }
+
+ if (access_or_warn(user_config, R_OK, 0) && xdg_config &&
+ !access_or_warn(xdg_config, R_OK, 0)) {
+ free(user_config);
+ return xdg_config;
+ } else {
+ free(xdg_config);
+ return user_config;
+ }
+}
+
+void git_global_config_paths(char **user_out, char **xdg_out)
{
char *user_config = xstrdup_or_null(getenv("GIT_CONFIG_GLOBAL"));
char *xdg_config = NULL;
@@ -2064,34 +2031,13 @@ void git_global_config(char **user_out, char **xdg_out)
*xdg_out = xdg_config;
}
-/*
- * Parse environment variable 'k' as a boolean (in various
- * possible spellings); if missing, use the default value 'def'.
- */
-int git_env_bool(const char *k, int def)
-{
- const char *v = getenv(k);
- return v ? git_config_bool(k, v) : def;
-}
-
-/*
- * Parse environment variable 'k' as ulong with possibly a unit
- * suffix; if missing, use the default value 'val'.
- */
-unsigned long git_env_ulong(const char *k, unsigned long val)
-{
- const char *v = getenv(k);
- if (v && !git_parse_ulong(v, &val))
- die(_("failed to parse %s"), k);
- return val;
-}
-
int git_config_system(void)
{
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
}
static int do_git_config_sequence(const struct config_options *opts,
+ const struct repository *repo,
config_fn_t fn, void *data)
{
int ret = 0;
@@ -2099,57 +2045,68 @@ 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;
+ char *worktree_config;
+
+ /*
+ * Ensure that either:
+ * - the git_dir and commondir are both set, or
+ * - the git_dir and commondir are both NULL
+ */
+ if (!opts->git_dir != !opts->commondir)
+ BUG("only one of commondir and git_dir is non-NULL");
- if (opts->commondir)
+ if (opts->commondir) {
repo_config = mkpathdup("%s/config", opts->commondir);
- else if (opts->git_dir)
- BUG("git_dir without commondir");
- else
+ worktree_config = mkpathdup("%s/config.worktree", opts->git_dir);
+ } else {
repo_config = NULL;
+ worktree_config = NULL;
+ }
- current_parsing_scope = 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);
+ ret += git_config_from_file_with_options(fn, system_config,
+ data, CONFIG_SCOPE_SYSTEM,
+ NULL);
- current_parsing_scope = CONFIG_SCOPE_GLOBAL;
- git_global_config(&user_config, &xdg_config);
+ git_global_config_paths(&user_config, &xdg_config);
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
- ret += git_config_from_file(fn, xdg_config, data);
+ ret += git_config_from_file_with_options(fn, xdg_config, data,
+ CONFIG_SCOPE_GLOBAL, NULL);
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
- ret += git_config_from_file(fn, user_config, data);
+ ret += git_config_from_file_with_options(fn, user_config, data,
+ CONFIG_SCOPE_GLOBAL, NULL);
- current_parsing_scope = 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);
+ ret += git_config_from_file_with_options(fn, repo_config, data,
+ CONFIG_SCOPE_LOCAL, NULL);
- current_parsing_scope = 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))
- ret += git_config_from_file(fn, path, data);
- free(path);
+ if (!opts->ignore_worktree && worktree_config &&
+ repo && repo->repository_format_worktree_config &&
+ !access_or_die(worktree_config, R_OK, 0)) {
+ ret += git_config_from_file_with_options(fn, worktree_config, data,
+ CONFIG_SCOPE_WORKTREE,
+ NULL);
}
- current_parsing_scope = 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;
free(system_config);
free(xdg_config);
free(user_config);
free(repo_config);
+ free(worktree_config);
return ret;
}
int config_with_options(config_fn_t fn, void *data,
struct git_config_source *config_source,
+ struct repository *repo,
const struct config_options *opts)
{
struct config_include_data inc = CONFIG_INCLUDE_INIT;
@@ -2159,29 +2116,27 @@ int config_with_options(config_fn_t fn, void *data,
inc.fn = fn;
inc.data = data;
inc.opts = opts;
+ inc.repo = repo;
inc.config_source = config_source;
fn = git_config_include;
data = &inc;
}
- if (config_source)
- current_parsing_scope = config_source->scope;
-
/*
* If we have a specific filename, use it. Otherwise, follow the
* regular lookup sequence.
*/
if (config_source && config_source->use_stdin) {
- ret = git_config_from_stdin(fn, data);
+ ret = git_config_from_stdin(fn, data, config_source->scope);
} else if (config_source && config_source->file) {
- ret = git_config_from_file(fn, config_source->file, data);
+ ret = git_config_from_file_with_options(fn, config_source->file,
+ data, config_source->scope,
+ NULL);
} else if (config_source && config_source->blob) {
- struct repository *repo = config_source->repo ?
- config_source->repo : the_repository;
ret = git_config_from_blob_ref(fn, repo, config_source->blob,
- data);
+ data, config_source->scope);
} else {
- ret = do_git_config_sequence(opts, fn, data);
+ ret = do_git_config_sequence(opts, repo, fn, data);
}
if (inc.remote_urls) {
@@ -2191,26 +2146,24 @@ 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 *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;
+ struct config_context ctx = CONFIG_CONTEXT_INIT;
for (i = 0; i < list->nr; i++) {
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)
+ ctx.kvi = values->items[value_index].util;
+ if (fn(entry->key, values->items[value_index].string, &ctx, data) < 0)
git_die_config_linenr(entry->key,
- current_config_kvi->filename,
- current_config_kvi->linenr);
-
- current_config_kvi = NULL;
+ ctx.kvi->filename,
+ ctx.kvi->linenr);
}
}
@@ -2238,7 +2191,7 @@ void read_early_config(config_fn_t cb, void *data)
opts.git_dir = gitdir.buf;
}
- config_with_options(cb, data, NULL, &opts);
+ config_with_options(cb, data, NULL, NULL, &opts);
strbuf_release(&commondir);
strbuf_release(&gitdir);
@@ -2258,36 +2211,47 @@ void read_very_early_config(config_fn_t cb, void *data)
opts.ignore_cmdline = 1;
opts.system_gently = 1;
- config_with_options(cb, data, NULL, &opts);
+ config_with_options(cb, data, NULL, NULL, &opts);
}
-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 *set, const char *key,
+ struct config_set_element **dest)
{
struct config_set_element k;
struct config_set_element *found_entry;
char *normalized_key;
+ int ret;
+
/*
* `key` may come from the user, so normalize it before using it
* for querying entries from the hashmap.
*/
- if (git_config_parse_key(key, &normalized_key, NULL))
- return NULL;
+ ret = git_config_parse_key(key, &normalized_key, NULL);
+ if (ret)
+ return ret;
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;
+ *dest = found_entry;
+ return 0;
}
-static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+static int configset_add_value(const struct key_value_info *kvi_p,
+ struct config_set *set, const char *key,
+ const char *value)
{
struct config_set_element *e;
struct string_list_item *si;
struct configset_list_item *l_item;
struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
+ int ret;
- e = configset_find_element(cs, key);
+ ret = configset_find_element(set, key, &e);
+ if (ret)
+ return ret;
/*
* Since the keys are being fed by git_config*() callback mechanism, they
* are already normalized. So simply add them without any further munging.
@@ -2297,37 +2261,25 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
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;
- if (!cf)
- 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;
- } else {
- /* for values read from `git_config_from_parameters()` */
- kv_info->filename = NULL;
- kv_info->linenr = -1;
- kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
- }
- kv_info->scope = current_parsing_scope;
+ *kv_info = *kvi_p;
si->util = kv_info;
return 0;
}
-static int config_set_element_cmp(const void *unused_cmp_data,
+static int config_set_element_cmp(const void *cmp_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *unused_keydata)
+ const void *keydata UNUSED)
{
const struct config_set_element *e1, *e2;
@@ -2337,84 +2289,131 @@ static int config_set_element_cmp(const void *unused_cmp_data,
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;
}
-static int config_set_callback(const char *key, const char *value, void *cb)
+static int config_set_callback(const char *key, const char *value,
+ const struct config_context *ctx,
+ void *cb)
{
- struct config_set *cs = cb;
- configset_add_value(cs, key, value);
+ struct config_set *set = cb;
+ configset_add_value(ctx->kvi, set, key, value);
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)
{
- return git_config_from_file(config_set_callback, filename, cs);
+ return git_config_from_file(config_set_callback, filename, set);
}
-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, struct key_value_info *kvi)
{
const struct string_list *values = NULL;
+ int ret;
+ struct string_list_item item;
/*
* Follows "last one wins" semantic, i.e., if there are multiple matches for the
* 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);
+ if ((ret = git_configset_get_value_multi(set, key, &values)))
+ return ret;
- if (!values)
- return 1;
assert(values->nr > 0);
- *value = values->items[values->nr - 1].string;
+ item = values->items[values->nr - 1];
+ *value = item.string;
+ if (kvi)
+ *kvi = *((struct key_value_info *)item.util);
+ return 0;
+}
+
+int git_configset_get_value_multi(struct config_set *set, const char *key,
+ const struct string_list **dest)
+{
+ struct config_set_element *e;
+ int ret;
+
+ if ((ret = configset_find_element(set, key, &e)))
+ return ret;
+ else if (!e)
+ return 1;
+ *dest = &e->value_list;
+
return 0;
}
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+static int check_multi_string(struct string_list_item *item, void *util)
{
- struct config_set_element *e = configset_find_element(cs, key);
- return e ? &e->value_list : NULL;
+ return item->string ? 0 : config_error_nonbool(util);
}
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+int git_configset_get_string_multi(struct config_set *cs, const char *key,
+ const struct string_list **dest)
+{
+ int ret;
+
+ if ((ret = git_configset_get_value_multi(cs, key, dest)))
+ return ret;
+ if ((ret = for_each_string_list((struct string_list *)*dest,
+ check_multi_string, (void *)key)))
+ return ret;
+
+ return 0;
+}
+
+int git_configset_get(struct config_set *set, const char *key)
+{
+ struct config_set_element *e;
+ int ret;
+
+ if ((ret = configset_find_element(set, key, &e)))
+ return ret;
+ else if (!e)
+ return 1;
+ return 0;
+}
+
+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, NULL))
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, NULL)) {
if (!value)
return config_error_nonbool(key);
*dest = value;
@@ -2424,51 +2423,57 @@ 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)) {
- *dest = git_config_int(key, value);
+ struct key_value_info kvi;
+
+ if (!git_configset_get_value(set, key, &value, &kvi)) {
+ *dest = git_config_int(key, value, &kvi);
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)) {
- *dest = git_config_ulong(key, value);
+ struct key_value_info kvi;
+
+ if (!git_configset_get_value(set, key, &value, &kvi)) {
+ *dest = git_config_ulong(key, value, &kvi);
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, NULL)) {
*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)) {
- *dest = git_config_bool_or_int(key, value, is_bool);
+ struct key_value_info kvi;
+
+ if (!git_configset_get_value(set, key, &value, &kvi)) {
+ *dest = git_config_bool_or_int(key, value, &kvi, 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, NULL)) {
*dest = git_parse_maybe_bool(value);
if (*dest == -1)
return -1;
@@ -2477,10 +2482,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, NULL))
return git_config_pathname(dest, key, value);
else
return 1;
@@ -2501,8 +2506,8 @@ static void repo_read_config(struct repository *repo)
git_configset_clear(repo->config);
git_configset_init(repo->config);
-
- if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+ if (config_with_options(config_set_callback, repo->config, NULL,
+ repo, &opts) < 0)
/*
* config_with_options() normally returns only
* zero, as most errors are fatal, and
@@ -2537,18 +2542,31 @@ void repo_config(struct repository *repo, config_fn_t fn, void *data)
configset_iter(repo->config, fn, data);
}
+int repo_config_get(struct repository *repo, const char *key)
+{
+ git_config_check_init(repo);
+ return git_configset_get(repo->config, key);
+}
+
int repo_config_get_value(struct repository *repo,
const char *key, const char **value)
{
git_config_check_init(repo);
- return git_configset_get_value(repo->config, key, value);
+ return git_configset_get_value(repo->config, key, value, NULL);
+}
+
+int repo_config_get_value_multi(struct repository *repo, const char *key,
+ const struct string_list **dest)
+{
+ git_config_check_init(repo);
+ return git_configset_get_value_multi(repo->config, key, dest);
}
-const struct string_list *repo_config_get_value_multi(struct repository *repo,
- const char *key)
+int repo_config_get_string_multi(struct repository *repo, const char *key,
+ const struct string_list **dest)
{
git_config_check_init(repo);
- return git_configset_get_value_multi(repo->config, key);
+ return git_configset_get_string_multi(repo->config, key, dest);
}
int repo_config_get_string(struct repository *repo,
@@ -2619,6 +2637,28 @@ int repo_config_get_pathname(struct repository *repo,
return ret;
}
+/* Read values into protected_config. */
+static void read_protected_config(void)
+{
+ struct config_options opts = {
+ .respect_includes = 1,
+ .ignore_repo = 1,
+ .ignore_worktree = 1,
+ .system_gently = 1,
+ };
+
+ git_configset_init(&protected_config);
+ config_with_options(config_set_callback, &protected_config, NULL,
+ NULL, &opts);
+}
+
+void git_protected_config(config_fn_t fn, void *data)
+{
+ if (!protected_config.hash_initialized)
+ read_protected_config();
+ configset_iter(&protected_config, fn, data);
+}
+
/* Functions used historically to read configuration from 'the_repository' */
void git_config(config_fn_t fn, void *data)
{
@@ -2630,14 +2670,25 @@ void git_config_clear(void)
repo_config_clear(the_repository);
}
+int git_config_get(const char *key)
+{
+ return repo_config_get(the_repository, key);
+}
+
int git_config_get_value(const char *key, const char **value)
{
return repo_config_get_value(the_repository, key, value);
}
-const struct string_list *git_config_get_value_multi(const char *key)
+int git_config_get_value_multi(const char *key, const struct string_list **dest)
+{
+ return repo_config_get_value_multi(the_repository, key, dest);
+}
+
+int git_config_get_string_multi(const char *key,
+ const struct string_list **dest)
{
- return repo_config_get_value_multi(the_repository, key);
+ return repo_config_get_string_multi(the_repository, key, dest);
}
int git_config_get_string(const char *key, char **dest)
@@ -2784,7 +2835,8 @@ void git_die_config(const char *key, const char *err, ...)
error_fn(err, params);
va_end(params);
}
- values = git_config_get_value_multi(key);
+ if (git_config_get_value_multi(key, &values))
+ BUG("for key '%s' we must have a value to report on", key);
kv_info = values->items[values->nr - 1].util;
git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
}
@@ -2808,6 +2860,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)
{
@@ -2838,8 +2891,8 @@ static int matches(const char *key, const char *value,
(value && !regexec(store->value_pattern, value, 0, NULL, 0));
}
-static int store_aux_event(enum config_event_t type,
- size_t begin, size_t end, void *data)
+static int store_aux_event(enum config_event_t type, size_t begin, size_t end,
+ struct config_source *cs, void *data)
{
struct config_store_data *store = data;
@@ -2851,10 +2904,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;
@@ -2862,8 +2915,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,
@@ -2877,7 +2930,8 @@ static int store_aux_event(enum config_event_t type,
return 0;
}
-static int store_aux(const char *key, const char *value, void *cb)
+static int store_aux(const char *key, const char *value,
+ const struct config_context *ctx UNUSED, void *cb)
{
struct config_store_data *store = cb;
@@ -2957,6 +3011,7 @@ static ssize_t write_section(int fd, const char *key,
}
static ssize_t write_pair(int fd, const char *key, const char *value,
+ const char *comment,
const struct config_store_data *store)
{
int i;
@@ -2997,7 +3052,11 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
strbuf_addch(&sb, value[i]);
break;
}
- strbuf_addf(&sb, "%s\n", quote);
+
+ if (comment)
+ strbuf_addf(&sb, "%s%s\n", quote, comment);
+ else
+ strbuf_addf(&sb, "%s\n", quote);
ret = write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb);
@@ -3086,9 +3145,9 @@ static void maybe_remove_section(struct config_store_data *store,
}
int git_config_set_in_file_gently(const char *config_filename,
- const char *key, const char *value)
+ const char *key, const char *comment, const char *value)
{
- return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);
+ return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, comment, 0);
}
void git_config_set_in_file(const char *config_filename,
@@ -3105,11 +3164,11 @@ int git_config_set_gently(const char *key, const char *value)
int repo_config_set_worktree_gently(struct repository *r,
const char *key, const char *value)
{
- /* Only use worktree-specific config if it is is already enabled. */
- if (repository_format_worktree_config) {
+ /* Only use worktree-specific config if it is already enabled. */
+ if (r->repository_format_worktree_config) {
char *file = repo_git_path(r, "config.worktree");
int ret = git_config_set_multivar_in_file_gently(
- file, key, value, NULL, 0);
+ file, key, value, NULL, NULL, 0);
free(file);
return ret;
}
@@ -3124,6 +3183,62 @@ void git_config_set(const char *key, const char *value)
}
/*
+ * The ownership rule is that the caller will own the string
+ * if it receives a piece of memory different from what it passed
+ * as the parameter.
+ */
+const char *git_config_prepare_comment_string(const char *comment)
+{
+ size_t leading_blanks;
+
+ if (!comment)
+ return NULL;
+
+ if (strchr(comment, '\n'))
+ die(_("no multi-line comment allowed: '%s'"), comment);
+
+ /*
+ * If it begins with one or more leading whitespace characters
+ * followed by '#", the comment string is used as-is.
+ *
+ * If it begins with '#', a SP is inserted between the comment
+ * and the value the comment is about.
+ *
+ * Otherwise, the value is followed by a SP followed by '#'
+ * followed by SP and then the comment string comes.
+ */
+
+ leading_blanks = strspn(comment, " \t");
+ if (leading_blanks && comment[leading_blanks] == '#')
+ ; /* use it as-is */
+ else if (comment[0] == '#')
+ comment = xstrfmt(" %s", comment);
+ else
+ comment = xstrfmt(" # %s", comment);
+
+ return comment;
+}
+
+static void validate_comment_string(const char *comment)
+{
+ size_t leading_blanks;
+
+ if (!comment)
+ return;
+ /*
+ * The front-end must have massaged the comment string
+ * properly before calling us.
+ */
+ if (strchr(comment, '\n'))
+ BUG("multi-line comments are not permitted: '%s'", comment);
+
+ leading_blanks = strspn(comment, " \t");
+ if (!leading_blanks || comment[leading_blanks] != '#')
+ BUG("comment must begin with one or more SP followed by '#': '%s'",
+ comment);
+}
+
+/*
* If value==NULL, unset in (remove from) config,
* if value_pattern!=NULL, disregard key/value pairs where value does not match.
* if value_pattern==CONFIG_REGEX_NONE, do not match any existing values
@@ -3151,6 +3266,7 @@ void git_config_set(const char *key, const char *value)
int git_config_set_multivar_in_file_gently(const char *config_filename,
const char *key, const char *value,
const char *value_pattern,
+ const char *comment,
unsigned flags)
{
int fd = -1, in_fd = -1;
@@ -3159,9 +3275,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));
+ validate_comment_string(comment);
/* parse-key returns negative; flip the sign to feed exit(3) */
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
@@ -3203,7 +3319,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
free(store.key);
store.key = xstrdup(key);
if (write_section(fd, key, &store) < 0 ||
- write_pair(fd, key, value, &store) < 0)
+ write_pair(fd, key, value, comment, &store) < 0)
goto write_err_out;
} else {
struct stat st;
@@ -3251,7 +3367,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
*/
if (git_config_from_file_with_options(store_aux,
config_filename,
- &store, &opts)) {
+ &store, CONFIG_SCOPE_UNKNOWN,
+ &opts)) {
error(_("invalid config file %s"), config_filename);
ret = CONFIG_INVALID_FILE;
goto out_free;
@@ -3356,7 +3473,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
if (write_section(fd, key, &store) < 0)
goto write_err_out;
}
- if (write_pair(fd, key, value, &store) < 0)
+ if (write_pair(fd, key, value, comment, &store) < 0)
goto write_err_out;
}
@@ -3394,7 +3511,6 @@ out_free:
write_err_out:
ret = write_error(get_lock_file_path(&lock));
goto out_free;
-
}
void git_config_set_multivar_in_file(const char *config_filename,
@@ -3402,7 +3518,7 @@ void git_config_set_multivar_in_file(const char *config_filename,
const char *value_pattern, unsigned flags)
{
if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
- value_pattern, flags))
+ value_pattern, NULL, flags))
return;
if (value)
die(_("could not set '%s' to '%s'"), key, value);
@@ -3425,7 +3541,7 @@ int repo_config_set_multivar_gently(struct repository *r, const char *key,
int res = git_config_set_multivar_in_file_gently(file,
key, value,
value_pattern,
- flags);
+ NULL, flags);
free(file);
return res;
}
@@ -3438,9 +3554,10 @@ void git_config_set_multivar(const char *key, const char *value,
flags);
}
-static int section_name_match (const char *buf, const char *name)
+static size_t section_name_match (const char *buf, const char *name)
{
- int i = 0, j = 0, dot = 0;
+ size_t i = 0, j = 0;
+ int dot = 0;
if (buf[i] != '[')
return 0;
for (i = 1; buf[i] && buf[i] != ']'; i++) {
@@ -3493,6 +3610,8 @@ static int section_name_is_ok(const char *name)
return 1;
}
+#define GIT_CONFIG_MAX_LINE_LEN (512 * 1024)
+
/* if new_name == NULL, the section is removed instead */
static int git_config_copy_or_rename_section_in_file(const char *config_filename,
const char *old_name,
@@ -3502,11 +3621,12 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
char *filename_buf = NULL;
struct lock_file lock = LOCK_INIT;
int out_fd;
- char buf[1024];
+ struct strbuf buf = STRBUF_INIT;
FILE *config_file = NULL;
struct stat st;
struct strbuf copystr = STRBUF_INIT;
struct config_store_data store;
+ uint32_t line_nr = 0;
memset(&store, 0, sizeof(store));
@@ -3543,16 +3663,25 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
goto out;
}
- while (fgets(buf, sizeof(buf), config_file)) {
- unsigned i;
- int length;
+ while (!strbuf_getwholeline(&buf, config_file, '\n')) {
+ size_t i, length;
int is_section = 0;
- char *output = buf;
- for (i = 0; buf[i] && isspace(buf[i]); i++)
+ char *output = buf.buf;
+
+ line_nr++;
+
+ if (buf.len >= GIT_CONFIG_MAX_LINE_LEN) {
+ ret = error(_("refusing to work with overly long line "
+ "in '%s' on line %"PRIuMAX),
+ config_filename, (uintmax_t)line_nr);
+ goto out;
+ }
+
+ for (i = 0; buf.buf[i] && isspace(buf.buf[i]); i++)
; /* do nothing */
- if (buf[i] == '[') {
+ if (buf.buf[i] == '[') {
/* it's a section */
- int offset;
+ size_t offset;
is_section = 1;
/*
@@ -3569,7 +3698,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
strbuf_reset(&copystr);
}
- offset = section_name_match(&buf[i], old_name);
+ offset = section_name_match(&buf.buf[i], old_name);
if (offset > 0) {
ret++;
if (!new_name) {
@@ -3599,6 +3728,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
output[0] = '\t';
}
} else {
+ strbuf_release(&copystr);
copystr = store_create_section(new_name, &store);
}
}
@@ -3644,6 +3774,8 @@ out:
out_no_rollback:
free(filename_buf);
config_store_data_clear(&store);
+ strbuf_release(&buf);
+ strbuf_release(&copystr);
return ret;
}
@@ -3717,16 +3849,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)
{
- int 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";
@@ -3763,34 +3887,6 @@ 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;
- else if (cf)
- name = cf->name;
- else
- BUG("current_config_name called outside config callback");
- return name ? name : "";
-}
-
-enum config_scope current_config_scope(void)
-{
- if (current_config_kvi)
- return current_config_kvi->scope;
- else
- 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;