diff options
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 244 |
1 files changed, 194 insertions, 50 deletions
@@ -6,11 +6,13 @@ * */ #include "cache.h" +#include "lockfile.h" #include "exec_cmd.h" #include "strbuf.h" #include "quote.h" #include "hashmap.h" #include "string-list.h" +#include "utf8.h" struct config_source { struct config_source *prev; @@ -35,12 +37,6 @@ struct config_source { long (*do_ftell)(struct config_source *c); }; -struct config_set_element { - struct hashmap_entry ent; - char *key; - struct string_list value_list; -}; - static struct config_source *cf; static int zlib_compression_seen; @@ -78,8 +74,12 @@ static int config_buf_fgetc(struct config_source *conf) static int config_buf_ungetc(int c, struct config_source *conf) { - if (conf->u.buf.pos > 0) - return conf->u.buf.buf[--conf->u.buf.pos]; + if (conf->u.buf.pos > 0) { + conf->u.buf.pos--; + if (conf->u.buf.buf[conf->u.buf.pos] != c) + die("BUG: config_buf can only ungetc the same character"); + return c; + } return EOF; } @@ -142,7 +142,6 @@ static int handle_path_include(const char *path, struct config_include_data *inc int git_config_include(const char *var, const char *value, void *data) { struct config_include_data *inc = data; - const char *type; int ret; /* @@ -153,10 +152,7 @@ int git_config_include(const char *var, const char *value, void *data) if (ret < 0) return ret; - if (!skip_prefix(var, "include.", &type)) - return ret; - - if (!strcmp(type, "path")) + if (!strcmp(var, "include.path")) ret = handle_path_include(value, inc); return ret; } @@ -244,7 +240,8 @@ static int get_next_char(void) /* DOS like systems */ c = cf->do_fgetc(cf); if (c != '\n') { - cf->do_ungetc(c, cf); + if (c != EOF) + cf->do_ungetc(c, cf); c = '\r'; } } @@ -252,6 +249,7 @@ static int get_next_char(void) cf->linenr++; if (c == EOF) { cf->eof = 1; + cf->linenr++; c = '\n'; } return c; @@ -327,6 +325,7 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name) { int c; char *value; + int ret; /* Get the full name */ for (;;) { @@ -349,7 +348,15 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name) if (!value) return -1; } - return fn(name->buf, value, data); + /* + * We already consumed the \n, but we need linenr to point to + * 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); + cf->linenr++; + return ret; } static int get_extended_base_var(struct strbuf *name, int c) @@ -411,8 +418,7 @@ static int git_parse_source(config_fn_t fn, void *data) struct strbuf *var = &cf->var; /* U+FEFF Byte Order Mark in UTF8 */ - static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; - const unsigned char *bomptr = utf8_bom; + const char *bomptr = utf8_bom; for (;;) { int c = get_next_char(); @@ -420,7 +426,7 @@ static int git_parse_source(config_fn_t fn, void *data) /* We are at the file beginning; skip UTF8-encoded BOM * if present. Sane editors won't put this in on their * own, but e.g. Windows Notepad will do it happily. */ - if ((unsigned char) c == *bomptr) { + if (c == (*bomptr & 0377)) { bomptr++; continue; } else { @@ -465,9 +471,9 @@ static int git_parse_source(config_fn_t fn, void *data) break; } if (cf->die_on_error) - die("bad config file line %d in %s", cf->linenr, cf->name); + die(_("bad config file line %d in %s"), cf->linenr, cf->name); else - return error("bad config file line %d in %s", cf->linenr, cf->name); + return error(_("bad config file line %d in %s"), cf->linenr, cf->name); } static int parse_unit_factor(const char *end, uintmax_t *val) @@ -505,9 +511,9 @@ static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max) errno = EINVAL; return 0; } - uval = abs(val); + uval = labs(val); uval *= factor; - if (uval > max || abs(val) > uval) { + if (uval > max || labs(val) > uval) { errno = ERANGE; return 0; } @@ -583,9 +589,9 @@ static void die_bad_number(const char *name, const char *value) value = ""; if (cf && cf->name) - die("bad numeric config value '%s' for '%s' in %s: %s", + die(_("bad numeric config value '%s' for '%s' in %s: %s"), value, name, cf->name, reason); - die("bad numeric config value '%s' for '%s': %s", value, name, reason); + die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason); } int git_config_int(const char *name, const char *value) @@ -670,7 +676,7 @@ int git_config_pathname(const char **dest, const char *var, const char *value) return config_error_nonbool(var); *dest = expand_user_path(value); if (!*dest) - die("Failed to expand user dir in: '%s'", value); + die(_("failed to expand user dir in: '%s'"), value); return 0; } @@ -748,7 +754,7 @@ static int git_default_core_config(const char *var, const char *value) if (level == -1) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) - die("bad zlib compression level %d", level); + die(_("bad zlib compression level %d"), level); zlib_compression_level = level; zlib_compression_seen = 1; return 0; @@ -759,7 +765,7 @@ static int git_default_core_config(const char *var, const char *value) if (level == -1) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) - die("bad zlib compression level %d", level); + die(_("bad zlib compression level %d"), level); core_compression_level = level; core_compression_seen = 1; if (!zlib_compression_seen) @@ -881,7 +887,7 @@ static int git_default_core_config(const char *var, const char *value) else if (!strcmp(value, "link")) object_creation_mode = OBJECT_CREATION_USES_HARDLINKS; else - die("Invalid mode for object creation: %s", value); + die(_("invalid mode for object creation: %s"), value); return 0; } @@ -895,6 +901,16 @@ static int git_default_core_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.protecthfs")) { + protect_hfs = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.protectntfs")) { + protect_ntfs = git_config_bool(var, value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } @@ -1139,12 +1155,28 @@ const char *git_etc_gitconfig(void) return system_wide; } +/* + * 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); @@ -1181,7 +1213,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) switch (git_config_from_parameters(fn, data)) { case -1: /* error */ - die("unable to parse command-line config"); + die(_("unable to parse command-line config")); break; case 0: /* found nothing */ break; @@ -1228,9 +1260,48 @@ int git_config_with_options(config_fn_t fn, void *data, return ret; } -int git_config(config_fn_t fn, void *data) +static void git_config_raw(config_fn_t fn, void *data) { - return git_config_with_options(fn, data, NULL, 1); + if (git_config_with_options(fn, data, NULL, 1) < 0) + /* + * git_config_with_options() normally returns only + * positive values, as most errors are fatal, and + * non-fatal potential errors are guarded by "if" + * statements that are entered only when no error is + * possible. + * + * If we ever encounter a non-fatal error, it means + * something went really wrong and we should stop + * immediately. + */ + die(_("unknown error occured while reading the configuration files")); +} + +static void configset_iter(struct config_set *cs, 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 key_value_info *kv_info; + + for (i = 0; i < list->nr; i++) { + entry = list->items[i].e; + value_index = list->items[i].value_index; + values = &entry->value_list; + if (fn(entry->key, values->items[value_index].string, data) < 0) { + kv_info = values->items[value_index].util; + git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr); + } + } +} + +static void git_config_check_init(void); + +void git_config(config_fn_t fn, void *data) +{ + git_config_check_init(); + configset_iter(&the_config_set, fn, data); } static struct config_set_element *configset_find_element(struct config_set *cs, const char *key) @@ -1258,6 +1329,10 @@ static struct config_set_element *configset_find_element(struct config_set *cs, static int configset_add_value(struct config_set *cs, 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)); + e = configset_find_element(cs, key); /* * Since the keys are being fed by git_config*() callback mechanism, they @@ -1270,7 +1345,22 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha string_list_init(&e->value_list, 1); hashmap_add(&cs->config_hash, e); } - string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL); + 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++]; + l_item->e = e; + l_item->value_index = e->value_list.nr - 1; + + if (cf) { + kv_info->filename = strintern(cf->name); + kv_info->linenr = cf->linenr; + } else { + /* for values read from `git_config_from_parameters()` */ + kv_info->filename = NULL; + kv_info->linenr = -1; + } + si->util = kv_info; return 0; } @@ -1285,6 +1375,9 @@ void git_configset_init(struct config_set *cs) { hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0); cs->hash_initialized = 1; + cs->list.nr = 0; + cs->list.alloc = 0; + cs->list.items = NULL; } void git_configset_clear(struct config_set *cs) @@ -1297,10 +1390,14 @@ void git_configset_clear(struct config_set *cs) hashmap_iter_init(&cs->config_hash, &iter); while ((entry = hashmap_iter_next(&iter))) { free(entry->key); - string_list_clear(&entry->value_list, 0); + string_list_clear(&entry->value_list, 1); } hashmap_free(&cs->config_hash, 1); cs->hash_initialized = 0; + free(cs->list.items); + cs->list.nr = 0; + cs->list.alloc = 0; + cs->list.items = NULL; } static int config_set_callback(const char *key, const char *value, void *cb) @@ -1419,7 +1516,7 @@ static void git_config_check_init(void) if (the_config_set.hash_initialized) return; git_configset_init(&the_config_set); - git_config(config_set_callback, &the_config_set); + git_config_raw(config_set_callback, &the_config_set); } void git_config_clear(void) @@ -1443,8 +1540,12 @@ const struct string_list *git_config_get_value_multi(const char *key) int git_config_get_string_const(const char *key, const char **dest) { + int ret; git_config_check_init(); - return git_configset_get_string_const(&the_config_set, key, dest); + ret = git_configset_get_string_const(&the_config_set, key, dest); + if (ret < 0) + git_die_config(key, NULL); + return ret; } int git_config_get_string(const char *key, char **dest) @@ -1485,8 +1586,39 @@ int git_config_get_maybe_bool(const char *key, int *dest) int git_config_get_pathname(const char *key, const char **dest) { + int ret; git_config_check_init(); - return git_configset_get_pathname(&the_config_set, key, dest); + ret = git_configset_get_pathname(&the_config_set, key, dest); + if (ret < 0) + git_die_config(key, NULL); + return ret; +} + +NORETURN +void git_die_config_linenr(const char *key, const char *filename, int linenr) +{ + if (!filename) + die(_("unable to parse '%s' from command-line config"), key); + else + die(_("bad config variable '%s' in file '%s' at line %d"), + key, filename, linenr); +} + +NORETURN __attribute__((format(printf, 2, 3))) +void git_die_config(const char *key, const char *err, ...) +{ + const struct string_list *values; + struct key_value_info *kv_info; + + if (err) { + va_list params; + va_start(params, err); + vreportf("error: ", err, params); + va_end(params); + } + values = git_config_get_value_multi(key); + kv_info = values->items[values->nr - 1].util; + git_die_config_linenr(key, kv_info->filename, kv_info->linenr); } /* @@ -1507,10 +1639,15 @@ static struct { static int matches(const char *key, const char *value) { - return !strcmp(key, store.key) && - (store.value_regex == NULL || - (store.do_not_match ^ - !regexec(store.value_regex, value, 0, NULL, 0))); + if (strcmp(key, store.key)) + return 0; /* not ours */ + if (!store.value_regex) + return 1; /* always matches */ + if (store.value_regex == CONFIG_REGEX_NONE) + return 0; /* never matches */ + + return store.do_not_match ^ + (value && !regexec(store.value_regex, value, 0, NULL, 0)); } static int store_aux(const char *key, const char *value, void *cb) @@ -1522,7 +1659,7 @@ static int store_aux(const char *key, const char *value, void *cb) case KEY_SEEN: if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { - warning("%s has multiple values", key); + warning(_("%s has multiple values"), key); } ALLOC_GROW(store.offset, store.seen + 1, @@ -1772,6 +1909,8 @@ out_free_ret_1: /* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. + * if value_regex==CONFIG_REGEX_NONE, do not match any existing values + * (only add a new one) * if multi_replace==0, nothing, or only one matching key/value is replaced, * else all matching key/values (regardless how many) are removed, * before the new pair is written. @@ -1855,6 +1994,8 @@ int git_config_set_multivar_in_file(const char *config_filename, if (value_regex == NULL) store.value_regex = NULL; + else if (value_regex == CONFIG_REGEX_NONE) + store.value_regex = CONFIG_REGEX_NONE; else { if (value_regex[0] == '!') { store.do_not_match = 1; @@ -1886,7 +2027,8 @@ int git_config_set_multivar_in_file(const char *config_filename, if (git_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); - if (store.value_regex != NULL) { + if (store.value_regex != NULL && + store.value_regex != CONFIG_REGEX_NONE) { regfree(store.value_regex); free(store.value_regex); } @@ -1895,7 +2037,8 @@ int git_config_set_multivar_in_file(const char *config_filename, } free(store.key); - if (store.value_regex != NULL) { + if (store.value_regex != NULL && + store.value_regex != CONFIG_REGEX_NONE) { regfree(store.value_regex); free(store.value_regex); } @@ -1913,9 +2056,9 @@ int git_config_set_multivar_in_file(const char *config_filename, MAP_PRIVATE, in_fd, 0); close(in_fd); - if (chmod(lock->filename, st.st_mode & 07777) < 0) { + if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) { error("chmod on %s failed: %s", - lock->filename, strerror(errno)); + lock->filename.buf, strerror(errno)); ret = CONFIG_NO_WRITE; goto out_free; } @@ -1972,6 +2115,7 @@ int git_config_set_multivar_in_file(const char *config_filename, if (commit_lock_file(lock) < 0) { error("could not commit config file %s", config_filename); ret = CONFIG_NO_WRITE; + lock = NULL; goto out_free; } @@ -1994,7 +2138,7 @@ out_free: return ret; write_err_out: - ret = write_error(lock->filename); + ret = write_error(lock->filename.buf); goto out_free; } @@ -2095,9 +2239,9 @@ int git_config_rename_section_in_file(const char *config_filename, fstat(fileno(config_file), &st); - if (chmod(lock->filename, st.st_mode & 07777) < 0) { + if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) { ret = error("chmod on %s failed: %s", - lock->filename, strerror(errno)); + lock->filename.buf, strerror(errno)); goto out; } @@ -2118,7 +2262,7 @@ int git_config_rename_section_in_file(const char *config_filename, } store.baselen = strlen(new_name); if (!store_write_section(out_fd, new_name)) { - ret = write_error(lock->filename); + ret = write_error(lock->filename.buf); goto out; } /* @@ -2144,7 +2288,7 @@ int git_config_rename_section_in_file(const char *config_filename, continue; length = strlen(output); if (write_in_full(out_fd, output, length) != length) { - ret = write_error(lock->filename); + ret = write_error(lock->filename.buf); goto out; } } |