diff options
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 213 |
1 files changed, 154 insertions, 59 deletions
@@ -26,7 +26,74 @@ static config_file *cf; static int zlib_compression_seen; -const char *config_exclusive_filename = NULL; +#define MAX_INCLUDE_DEPTH 10 +static const char include_depth_advice[] = +"exceeded maximum include depth (%d) while including\n" +" %s\n" +"from\n" +" %s\n" +"Do you have circular includes?"; +static int handle_path_include(const char *path, struct config_include_data *inc) +{ + int ret = 0; + struct strbuf buf = STRBUF_INIT; + char *expanded = expand_user_path(path); + + if (!expanded) + return error("Could not expand include path '%s'", path); + path = expanded; + + /* + * Use an absolute path as-is, but interpret relative paths + * based on the including config file. + */ + if (!is_absolute_path(path)) { + char *slash; + + if (!cf || !cf->name) + return error("relative config includes must come from files"); + + slash = find_last_dir_sep(cf->name); + if (slash) + strbuf_add(&buf, cf->name, slash - cf->name + 1); + strbuf_addstr(&buf, path); + path = buf.buf; + } + + if (!access_or_warn(path, R_OK)) { + if (++inc->depth > MAX_INCLUDE_DEPTH) + die(include_depth_advice, MAX_INCLUDE_DEPTH, path, + cf && cf->name ? cf->name : "the command line"); + ret = git_config_from_file(git_config_include, path, inc); + inc->depth--; + } + strbuf_release(&buf); + free(expanded); + return ret; +} + +int git_config_include(const char *var, const char *value, void *data) +{ + struct config_include_data *inc = data; + const char *type; + int ret; + + /* + * 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); + if (ret < 0) + return ret; + + type = skip_prefix(var, "include."); + if (!type) + return ret; + + if (!strcmp(type, "path")) + ret = handle_path_include(value, inc); + return ret; +} static void lowercase(char *p) { @@ -691,25 +758,8 @@ static int git_default_core_config(const char *var, const char *value) return 0; } - /* Add other config variables here and to Documentation/config.txt. */ - return 0; -} - -static int git_default_user_config(const char *var, const char *value) -{ - if (!strcmp(var, "user.name")) { - if (!value) - return config_error_nonbool(var); - strlcpy(git_default_name, value, sizeof(git_default_name)); - user_ident_explicitly_given |= IDENT_NAME_GIVEN; - return 0; - } - - if (!strcmp(var, "user.email")) { - if (!value) - return config_error_nonbool(var); - strlcpy(git_default_email, value, sizeof(git_default_email)); - user_ident_explicitly_given |= IDENT_MAIL_GIVEN; + if (!strcmp(var, "core.precomposeunicode")) { + precomposed_unicode = git_config_bool(var, value); return 0; } @@ -768,6 +818,8 @@ static int git_default_push_config(const char *var, const char *value) push_default = PUSH_DEFAULT_NOTHING; else if (!strcmp(value, "matching")) push_default = PUSH_DEFAULT_MATCHING; + else if (!strcmp(value, "simple")) + push_default = PUSH_DEFAULT_SIMPLE; else if (!strcmp(value, "upstream")) push_default = PUSH_DEFAULT_UPSTREAM; else if (!strcmp(value, "tracking")) /* deprecated */ @@ -776,8 +828,8 @@ static int git_default_push_config(const char *var, const char *value) push_default = PUSH_DEFAULT_CURRENT; else { error("Malformed value for %s: %s", var, value); - return error("Must be one of nothing, matching, " - "tracking or current."); + return error("Must be one of nothing, matching, simple, " + "upstream or current."); } return 0; } @@ -801,7 +853,7 @@ int git_default_config(const char *var, const char *value, void *dummy) return git_default_core_config(var, value); if (!prefixcmp(var, "user.")) - return git_default_user_config(var, value); + return git_ident_config(var, value, dummy); if (!prefixcmp(var, "i18n.")) return git_default_i18n_config(var, value); @@ -882,28 +934,28 @@ int git_config_system(void) int git_config_early(config_fn_t fn, void *data, const char *repo_config) { int ret = 0, found = 0; - const char *home = NULL; + char *xdg_config = NULL; + char *user_config = NULL; - /* Setting $GIT_CONFIG makes git read _only_ the given config file. */ - if (config_exclusive_filename) - return git_config_from_file(fn, config_exclusive_filename, data); - if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) { + home_config_paths(&user_config, &xdg_config, "config"); + + if (git_config_system() && !access_or_warn(git_etc_gitconfig(), R_OK)) { ret += git_config_from_file(fn, git_etc_gitconfig(), data); found += 1; } - home = getenv("HOME"); - if (home) { - char buf[PATH_MAX]; - char *user_config = mksnpath(buf, sizeof(buf), "%s/.gitconfig", home); - if (!access(user_config, R_OK)) { - ret += git_config_from_file(fn, user_config, data); - found += 1; - } + if (xdg_config && !access_or_warn(xdg_config, R_OK)) { + ret += git_config_from_file(fn, xdg_config, data); + found += 1; } - if (repo_config && !access(repo_config, R_OK)) { + if (user_config && !access_or_warn(user_config, R_OK)) { + ret += git_config_from_file(fn, user_config, data); + found += 1; + } + + if (repo_config && !access_or_warn(repo_config, R_OK)) { ret += git_config_from_file(fn, repo_config, data); found += 1; } @@ -919,13 +971,31 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) break; } + free(xdg_config); + free(user_config); return ret == 0 ? found : ret; } -int git_config(config_fn_t fn, void *data) +int git_config_with_options(config_fn_t fn, void *data, + const char *filename, int respect_includes) { char *repo_config = NULL; int ret; + struct config_include_data inc = CONFIG_INCLUDE_INIT; + + if (respect_includes) { + inc.fn = fn; + inc.data = data; + fn = git_config_include; + data = &inc; + } + + /* + * If we have a specific filename, use it. Otherwise, follow the + * regular lookup sequence. + */ + if (filename) + return git_config_from_file(fn, filename, data); repo_config = git_pathdup("config"); ret = git_config_early(fn, data, repo_config); @@ -934,6 +1004,11 @@ int git_config(config_fn_t fn, void *data) return ret; } +int git_config(config_fn_t fn, void *data) +{ + return git_config_with_options(fn, data, NULL, 1); +} + /* * Find all the stuff for git_config_set() below. */ @@ -1238,6 +1313,7 @@ int git_config_set_multivar_in_file(const char *config_filename, int fd = -1, in_fd; int ret; struct lock_file *lock = NULL; + char *filename_buf = NULL; /* parse-key returns negative; flip the sign to feed exit(3) */ ret = 0 - git_config_parse_key(key, &store.key, &store.baselen); @@ -1246,6 +1322,8 @@ int git_config_set_multivar_in_file(const char *config_filename, store.multi_replace = multi_replace; + if (!config_filename) + config_filename = filename_buf = git_pathdup("config"); /* * The lock serves a purpose in addition to locking: the new @@ -1415,6 +1493,7 @@ int git_config_set_multivar_in_file(const char *config_filename, out_free: if (lock) rollback_lock_file(lock); + free(filename_buf); return ret; write_err_out: @@ -1426,19 +1505,8 @@ write_err_out: int git_config_set_multivar(const char *key, const char *value, const char *value_regex, int multi_replace) { - const char *config_filename; - char *buf = NULL; - int ret; - - if (config_exclusive_filename) - config_filename = config_exclusive_filename; - else - config_filename = buf = git_pathdup("config"); - - ret = git_config_set_multivar_in_file(config_filename, key, value, - value_regex, multi_replace); - free(buf); - return ret; + return git_config_set_multivar_in_file(NULL, key, value, value_regex, + multi_replace); } static int section_name_match (const char *buf, const char *name) @@ -1480,20 +1548,42 @@ static int section_name_match (const char *buf, const char *name) return 0; } +static int section_name_is_ok(const char *name) +{ + /* Empty section names are bogus. */ + if (!*name) + return 0; + + /* + * Before a dot, we must be alphanumeric or dash. After the first dot, + * anything goes, so we can stop checking. + */ + for (; *name && *name != '.'; name++) + if (*name != '-' && !isalnum(*name)) + return 0; + return 1; +} + /* if new_name == NULL, the section is removed instead */ -int git_config_rename_section(const char *old_name, const char *new_name) +int git_config_rename_section_in_file(const char *config_filename, + const char *old_name, const char *new_name) { int ret = 0, remove = 0; - char *config_filename; - struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1); + char *filename_buf = NULL; + struct lock_file *lock; int out_fd; char buf[1024]; FILE *config_file; - if (config_exclusive_filename) - config_filename = xstrdup(config_exclusive_filename); - else - config_filename = git_pathdup("config"); + if (new_name && !section_name_is_ok(new_name)) { + ret = error("invalid section name: %s", new_name); + goto out; + } + + if (!config_filename) + config_filename = filename_buf = git_pathdup("config"); + + lock = xcalloc(sizeof(struct lock_file), 1); out_fd = hold_lock_file_for_update(lock, config_filename, 0); if (out_fd < 0) { ret = error("could not lock config file %s", config_filename); @@ -1557,10 +1647,15 @@ unlock_and_out: if (commit_lock_file(lock) < 0) ret = error("could not commit config file %s", config_filename); out: - free(config_filename); + free(filename_buf); return ret; } +int git_config_rename_section(const char *old_name, const char *new_name) +{ + return git_config_rename_section_in_file(NULL, old_name, new_name); +} + /* * Call this to report error for your variable that should not * get a boolean value (i.e. "[my] var" means "true"). |