X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=config.c;h=c2f2bbb000cd003002c1bd346e6be6c9d3ef5c2d;hb=e4fff5ce65b80fed050be7c761466e16105a32c5;hp=cf2bfd35c8394579beed8fa565bfcfe33dbd7540;hpb=0a02186f924aee1bd69f18ed01f645aa332ce0d1;p=git.git diff --git a/config.c b/config.c index cf2bfd35c..18d305c89 100644 --- a/config.c +++ b/config.c @@ -16,6 +16,8 @@ static int config_linenr; static int config_file_eof; static int zlib_compression_seen; +const char *config_exclusive_filename = NULL; + static int get_next_char(void) { int c; @@ -111,7 +113,7 @@ static inline int iskeychar(int c) return isalnum(c) || c == '-'; } -static int get_value(config_fn_t fn, char *name, unsigned int len) +static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) { int c; char *value; @@ -139,7 +141,7 @@ static int get_value(config_fn_t fn, char *name, unsigned int len) if (!value) return -1; } - return fn(name, value); + return fn(name, value, data); } static int get_extended_base_var(char *name, int baselen, int c) @@ -197,14 +199,33 @@ static int get_base_var(char *name) } } -static int git_parse_file(config_fn_t fn) +static int git_parse_file(config_fn_t fn, void *data) { int comment = 0; int baselen = 0; static char var[MAXNAME]; + /* U+FEFF Byte Order Mark in UTF8 */ + static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; + const unsigned char *bomptr = utf8_bom; + for (;;) { int c = get_next_char(); + if (bomptr && *bomptr) { + /* 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) { + bomptr++; + continue; + } else { + /* Do not tolerate partial BOM. */ + if (bomptr != utf8_bom) + break; + /* No BOM at file beginning. Cool. */ + bomptr = NULL; + } + } if (c == '\n') { if (config_file_eof) return 0; @@ -228,7 +249,7 @@ static int git_parse_file(config_fn_t fn) if (!isalpha(c)) break; var[baselen] = tolower(c); - if (get_value(fn, var, baselen+1) < 0) + if (get_value(fn, data, var, baselen+1) < 0) break; } die("bad config file line %d in %s", config_linenr, config_file_name); @@ -253,7 +274,7 @@ static int parse_unit_factor(const char *end, unsigned long *val) return 0; } -int git_parse_long(const char *value, long *ret) +static int git_parse_long(const char *value, long *ret) { if (value && *value) { char *end; @@ -289,7 +310,7 @@ static void die_bad_config(const char *name) int git_config_int(const char *name, const char *value) { - long ret; + long ret = 0; if (!git_parse_long(value, &ret)) die_bad_config(name); return ret; @@ -332,13 +353,17 @@ int git_config_string(const char **dest, const char *var, const char *value) return 0; } -int git_default_config(const char *var, const char *value) +static int git_default_core_config(const char *var, const char *value) { /* This needs a better name */ if (!strcmp(var, "core.filemode")) { trust_executable_bit = git_config_bool(var, value); return 0; } + if (!strcmp(var, "core.trustctime")) { + trust_ctime = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "core.quotepath")) { quote_path_fully = git_config_bool(var, value); @@ -350,6 +375,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.ignorecase")) { + ignore_case = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "core.bare")) { is_bare_repository_cfg = git_config_bool(var, value); return 0; @@ -439,10 +469,39 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.pager")) + return git_config_string(&pager_program, var, value); + + if (!strcmp(var, "core.editor")) + return git_config_string(&editor_program, var, value); + + if (!strcmp(var, "core.excludesfile")) + return git_config_string(&excludes_file, var, value); + + if (!strcmp(var, "core.whitespace")) { + if (!value) + return config_error_nonbool(var); + whitespace_rule_cfg = parse_whitespace_rule(value); + return 0; + } + + if (!strcmp(var, "core.fsyncobjectfiles")) { + fsync_object_files = git_config_bool(var, 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)); + if (git_default_email[0]) + user_ident_explicitly_given = 1; return 0; } @@ -450,35 +509,29 @@ int git_default_config(const char *var, const char *value) if (!value) return config_error_nonbool(var); strlcpy(git_default_email, value, sizeof(git_default_email)); + if (git_default_name[0]) + user_ident_explicitly_given = 1; return 0; } + /* Add other config variables here and to Documentation/config.txt. */ + return 0; +} + +static int git_default_i18n_config(const char *var, const char *value) +{ if (!strcmp(var, "i18n.commitencoding")) return git_config_string(&git_commit_encoding, var, value); if (!strcmp(var, "i18n.logoutputencoding")) return git_config_string(&git_log_output_encoding, var, value); - if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { - pager_use_color = git_config_bool(var,value); - return 0; - } - - if (!strcmp(var, "core.pager")) - return git_config_string(&pager_program, var, value); - - if (!strcmp(var, "core.editor")) - return git_config_string(&editor_program, var, value); - - if (!strcmp(var, "core.excludesfile")) - return git_config_string(&excludes_file, var, value); + /* Add other config variables here and to Documentation/config.txt. */ + return 0; +} - if (!strcmp(var, "core.whitespace")) { - if (!value) - return config_error_nonbool(var); - whitespace_rule_cfg = parse_whitespace_rule(value); - return 0; - } +static int git_default_branch_config(const char *var, const char *value) +{ if (!strcmp(var, "branch.autosetupmerge")) { if (value && !strcasecmp(value, "always")) { git_branch_track = BRANCH_TRACK_ALWAYS; @@ -507,7 +560,30 @@ int git_default_config(const char *var, const char *value) return 0; } -int git_config_from_file(config_fn_t fn, const char *filename) +int git_default_config(const char *var, const char *value, void *dummy) +{ + if (!prefixcmp(var, "core.")) + return git_default_core_config(var, value); + + if (!prefixcmp(var, "user.")) + return git_default_user_config(var, value); + + if (!prefixcmp(var, "i18n.")) + return git_default_i18n_config(var, value); + + if (!prefixcmp(var, "branch.")) + return git_default_branch_config(var, value); + + if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { + pager_use_color = git_config_bool(var,value); + return 0; + } + + /* Add other config variables here and to Documentation/config.txt. */ + return 0; +} + +int git_config_from_file(config_fn_t fn, const char *filename, void *data) { int ret; FILE *f = fopen(filename, "r"); @@ -518,7 +594,7 @@ int git_config_from_file(config_fn_t fn, const char *filename) config_file_name = filename; config_linenr = 1; config_file_eof = 0; - ret = git_parse_file(fn); + ret = git_parse_file(fn, data); fclose(f); config_file_name = NULL; } @@ -528,19 +604,12 @@ int git_config_from_file(config_fn_t fn, const char *filename) const char *git_etc_gitconfig(void) { static const char *system_wide; - if (!system_wide) { - system_wide = ETC_GITCONFIG; - if (!is_absolute_path(system_wide)) { - /* interpret path relative to exec-dir */ - struct strbuf d = STRBUF_INIT; - strbuf_addf(&d, "%s/%s", git_exec_path(), system_wide); - system_wide = strbuf_detach(&d, NULL); - } - } + if (!system_wide) + system_wide = system_path(ETC_GITCONFIG); return system_wide; } -int git_env_bool(const char *k, int def) +static int git_env_bool(const char *k, int def) { const char *v = getenv(k); return v ? git_config_bool(k, v) : def; @@ -556,34 +625,32 @@ int git_config_global(void) return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0); } -int git_config(config_fn_t fn) +int git_config(config_fn_t fn, void *data) { int ret = 0; char *repo_config = NULL; - const char *home = NULL, *filename; + const char *home = NULL; /* $GIT_CONFIG makes git read _only_ the given config file, * $GIT_CONFIG_LOCAL will make it process it in addition to the * global config file, the same way it would the per-repository * config file otherwise. */ - filename = getenv(CONFIG_ENVIRONMENT); - if (!filename) { - if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) - ret += git_config_from_file(fn, git_etc_gitconfig()); - home = getenv("HOME"); - filename = getenv(CONFIG_LOCAL_ENVIRONMENT); - if (!filename) - filename = repo_config = xstrdup(git_path("config")); - } + if (config_exclusive_filename) + return git_config_from_file(fn, config_exclusive_filename, data); + if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) + ret += git_config_from_file(fn, git_etc_gitconfig(), + data); + home = getenv("HOME"); if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) - ret = git_config_from_file(fn, user_config); + ret += git_config_from_file(fn, user_config, data); free(user_config); } - ret += git_config_from_file(fn, filename); + repo_config = xstrdup(git_path("config")); + ret += git_config_from_file(fn, repo_config, data); free(repo_config); return ret; } @@ -613,7 +680,7 @@ static int matches(const char* key, const char* value) !regexec(store.value_regex, value, 0, NULL, 0))); } -static int store_aux(const char* key, const char* value) +static int store_aux(const char* key, const char* value, void *cb) { const char *ep; size_t section_len; @@ -622,11 +689,9 @@ static int store_aux(const char* key, const char* value) case KEY_SEEN: if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { - fprintf(stderr, - "Warning: %s has multiple values\n", - key); + warning("%s has multiple values", key); } else if (store.seen >= MAX_MATCHES) { - fprintf(stderr, "Too many matches\n"); + error("too many matches for %s", key); return 1; } @@ -676,9 +741,9 @@ static int store_aux(const char* key, const char* value) return 0; } -static int write_error(void) +static int write_error(const char *filename) { - fprintf(stderr, "Failed to write new configuration file\n"); + error("failed to write new configuration file %s", filename); /* Same error code as "failed to rename". */ return 4; @@ -695,7 +760,7 @@ static int store_write_section(int fd, const char* key) if (dot) { strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key); for (i = dot - key + 1; i < store.baselen; i++) { - if (key[i] == '"') + if (key[i] == '"' || key[i] == '\\') strbuf_addch(&sb, '\\'); strbuf_addch(&sb, key[i]); } @@ -823,13 +888,10 @@ int git_config_set_multivar(const char* key, const char* value, struct lock_file *lock = NULL; const char* last_dot = strrchr(key, '.'); - config_filename = getenv(CONFIG_ENVIRONMENT); - if (!config_filename) { - config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT); - if (!config_filename) - config_filename = git_path("config"); - } - config_filename = xstrdup(config_filename); + if (config_exclusive_filename) + config_filename = xstrdup(config_exclusive_filename); + else + config_filename = xstrdup(git_path("config")); /* * Since "key" actually contains the section name and the real @@ -837,7 +899,7 @@ int git_config_set_multivar(const char* key, const char* value, */ if (last_dot == NULL) { - fprintf(stderr, "key does not contain a section: %s\n", key); + error("key does not contain a section: %s", key); ret = 2; goto out_free; } @@ -857,14 +919,14 @@ int git_config_set_multivar(const char* key, const char* value, /* Leave the extended basename untouched.. */ if (!dot || i > store.baselen) { if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) { - fprintf(stderr, "invalid key: %s\n", key); + error("invalid key: %s", key); free(store.key); ret = 1; goto out_free; } c = tolower(c); } else if (c == '\n') { - fprintf(stderr, "invalid key (newline): %s\n", key); + error("invalid key (newline): %s", key); free(store.key); ret = 1; goto out_free; @@ -880,7 +942,7 @@ int git_config_set_multivar(const char* key, const char* value, lock = xcalloc(sizeof(struct lock_file), 1); fd = hold_lock_file_for_update(lock, config_filename, 0); if (fd < 0) { - fprintf(stderr, "could not lock config file\n"); + error("could not lock config file %s", config_filename); free(store.key); ret = -1; goto out_free; @@ -927,8 +989,7 @@ int git_config_set_multivar(const char* key, const char* value, store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { - fprintf(stderr, "Invalid pattern: %s\n", - value_regex); + error("invalid pattern: %s", value_regex); free(store.value_regex); ret = 6; goto out_free; @@ -945,8 +1006,8 @@ int git_config_set_multivar(const char* key, const char* value, * As a side effect, we make sure to transform only a valid * existing config file. */ - if (git_config_from_file(store_aux, config_filename)) { - fprintf(stderr, "invalid config file\n"); + 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) { regfree(store.value_regex); @@ -1025,7 +1086,7 @@ int git_config_set_multivar(const char* key, const char* value, } if (commit_lock_file(lock) < 0) { - fprintf(stderr, "Cannot commit config file!\n"); + error("could not commit config file %s", config_filename); ret = 4; goto out_free; } @@ -1046,7 +1107,7 @@ out_free: return ret; write_err_out: - ret = write_error(); + ret = write_error(lock->filename); goto out_free; } @@ -1087,16 +1148,13 @@ int git_config_rename_section(const char *old_name, const char *new_name) int out_fd; char buf[1024]; - config_filename = getenv(CONFIG_ENVIRONMENT); - if (!config_filename) { - config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT); - if (!config_filename) - config_filename = git_path("config"); - } - config_filename = xstrdup(config_filename); + if (config_exclusive_filename) + config_filename = xstrdup(config_exclusive_filename); + else + config_filename = xstrdup(git_path("config")); out_fd = hold_lock_file_for_update(lock, config_filename, 0); if (out_fd < 0) { - ret = error("Could not lock config file!"); + ret = error("could not lock config file %s", config_filename); goto out; } @@ -1120,7 +1178,7 @@ int git_config_rename_section(const char *old_name, const char *new_name) } store.baselen = strlen(new_name); if (!store_write_section(out_fd, new_name)) { - ret = write_error(); + ret = write_error(lock->filename); goto out; } continue; @@ -1131,14 +1189,14 @@ int git_config_rename_section(const char *old_name, const char *new_name) continue; length = strlen(buf); if (write_in_full(out_fd, buf, length) != length) { - ret = write_error(); + ret = write_error(lock->filename); goto out; } } fclose(config_file); unlock_and_out: if (commit_lock_file(lock) < 0) - ret = error("Cannot commit config file!"); + ret = error("could not commit config file %s", config_filename); out: free(config_filename); return ret;