const char *config_exclusive_filename = NULL;
-struct config_item
-{
- struct config_item *next;
- char *name;
- char *value;
-};
-static struct config_item *config_parameters;
-static struct config_item **config_parameters_tail = &config_parameters;
-
static void lowercase(char *p)
{
for (; *p; p++)
strbuf_release(&env);
}
-static int git_config_parse_parameter(const char *text)
+static int git_config_parse_parameter(const char *text,
+ config_fn_t fn, void *data)
{
- struct config_item *ct;
struct strbuf tmp = STRBUF_INIT;
struct strbuf **pair;
strbuf_addstr(&tmp, text);
strbuf_trim(pair[0]);
if (!pair[0]->len) {
strbuf_list_free(pair);
- return -1;
+ return error("bogus config parameter: %s", text);
}
- ct = xcalloc(1, sizeof(struct config_item));
- ct->name = strbuf_detach(pair[0], NULL);
- if (pair[1]) {
- strbuf_trim(pair[1]);
- ct->value = strbuf_detach(pair[1], NULL);
+ lowercase(pair[0]->buf);
+ if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
+ strbuf_list_free(pair);
+ return -1;
}
strbuf_list_free(pair);
- lowercase(ct->name);
- *config_parameters_tail = ct;
- config_parameters_tail = &ct->next;
return 0;
}
-static int git_config_parse_environment(void) {
+int git_config_from_parameters(config_fn_t fn, void *data)
+{
const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
char *envw;
const char **argv = NULL;
}
for (i = 0; i < nr; i++) {
- if (git_config_parse_parameter(argv[i]) < 0) {
- error("bogus config parameter: %s", argv[i]);
+ if (git_config_parse_parameter(argv[i], fn, data) < 0) {
free(argv);
free(envw);
return -1;
free(argv);
free(envw);
- return 0;
+ return nr > 0;
}
static int get_next_char(void)
static char *parse_value(void)
{
- static char value[1024];
- int quote = 0, comment = 0, len = 0, space = 0;
+ static struct strbuf value = STRBUF_INIT;
+ int quote = 0, comment = 0, space = 0;
+ strbuf_reset(&value);
for (;;) {
int c = get_next_char();
- if (len >= sizeof(value) - 1)
- return NULL;
if (c == '\n') {
if (quote)
return NULL;
- value[len] = 0;
- return value;
+ return value.buf;
}
if (comment)
continue;
if (isspace(c) && !quote) {
- if (len)
+ if (value.len)
space++;
continue;
}
}
}
for (; space; space--)
- value[len++] = ' ';
+ strbuf_addch(&value, ' ');
if (c == '\\') {
c = get_next_char();
switch (c) {
default:
return NULL;
}
- value[len++] = c;
+ strbuf_addch(&value, c);
continue;
}
if (c == '"') {
quote = 1-quote;
continue;
}
- value[len++] = c;
+ strbuf_addch(&value, c);
}
}
return ret;
}
-int git_config_maybe_bool(const char *name, const char *value)
+static int git_config_maybe_bool_text(const char *name, const char *value)
{
if (!value)
return 1;
return -1;
}
+int git_config_maybe_bool(const char *name, const char *value)
+{
+ long v = git_config_maybe_bool_text(name, value);
+ if (0 <= v)
+ return v;
+ if (git_parse_long(value, &v))
+ return !!v;
+ return -1;
+}
+
int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
{
- int v = git_config_maybe_bool(name, value);
+ int v = git_config_maybe_bool_text(name, value);
if (0 <= v) {
*is_bool = 1;
return v;
return 0;
}
+ if (!strcmp(var, "core.abbrev")) {
+ int abbrev = git_config_int(var, value);
+ if (abbrev < minimum_abbrev || abbrev > 40)
+ return -1;
+ default_abbrev = abbrev;
+ return 0;
+ }
+
if (!strcmp(var, "core.loosecompression")) {
int level = git_config_int(var, value);
if (level == -1)
return 0;
}
+ if (!strcmp(var, "core.bigfilethreshold")) {
+ long n = git_config_int(var, value);
+ big_file_threshold = 0 < n ? n : 0;
+ return 0;
+ }
+
if (!strcmp(var, "core.packedgitlimit")) {
packed_git_limit = git_config_int(var, value);
return 0;
if (!strcmp(var, "core.autocrlf")) {
if (value && !strcasecmp(value, "input")) {
- if (eol == EOL_CRLF)
+ if (core_eol == EOL_CRLF)
return error("core.autocrlf=input conflicts with core.eol=crlf");
auto_crlf = AUTO_CRLF_INPUT;
return 0;
if (!strcmp(var, "core.eol")) {
if (value && !strcasecmp(value, "lf"))
- eol = EOL_LF;
+ core_eol = EOL_LF;
else if (value && !strcasecmp(value, "crlf"))
- eol = EOL_CRLF;
+ core_eol = EOL_CRLF;
else if (value && !strcasecmp(value, "native"))
- eol = EOL_NATIVE;
+ core_eol = EOL_NATIVE;
else
- eol = EOL_UNSET;
- if (eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
+ core_eol = EOL_UNSET;
+ if (core_eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
return error("core.autocrlf=input conflicts with core.eol=crlf");
return 0;
}
push_default = PUSH_DEFAULT_NOTHING;
else if (!strcmp(value, "matching"))
push_default = PUSH_DEFAULT_MATCHING;
- else if (!strcmp(value, "tracking"))
- push_default = PUSH_DEFAULT_TRACKING;
+ else if (!strcmp(value, "upstream"))
+ push_default = PUSH_DEFAULT_UPSTREAM;
+ else if (!strcmp(value, "tracking")) /* deprecated */
+ push_default = PUSH_DEFAULT_UPSTREAM;
else if (!strcmp(value, "current"))
push_default = PUSH_DEFAULT_CURRENT;
else {
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
}
-int git_config_global(void)
-{
- return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
-}
-
-int git_config_from_parameters(config_fn_t fn, void *data)
-{
- static int loaded_environment;
- const struct config_item *ct;
-
- if (!loaded_environment) {
- if (git_config_parse_environment() < 0)
- return -1;
- loaded_environment = 1;
- }
- for (ct = config_parameters; ct; ct = ct->next)
- if (fn(ct->name, ct->value, data) < 0)
- return -1;
- return 0;
-}
-
int git_config_early(config_fn_t fn, void *data, const char *repo_config)
{
int ret = 0, found = 0;
}
home = getenv("HOME");
- if (git_config_global() && home) {
+ if (home) {
char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
if (!access(user_config, R_OK)) {
ret += git_config_from_file(fn, user_config, data);
found += 1;
}
- ret += git_config_from_parameters(fn, data);
- if (config_parameters)
- found += 1;
+ switch (git_config_from_parameters(fn, data)) {
+ case -1: /* error */
+ ret--;
+ break;
+ case 0: /* found nothing */
+ break;
+ default: /* found at least one item */
+ found++;
+ break;
+ }
- if (found == 0)
- return -1;
- return ret;
+ return ret == 0 ? found : ret;
}
int git_config(config_fn_t fn, void *data)
return git_config_set_multivar(key, value, NULL, 0);
}
+/*
+ * Auxiliary function to sanity-check and split the key into the section
+ * identifier and variable name.
+ *
+ * Returns 0 on success, -1 when there is an invalid character in the key and
+ * -2 if there is no section name in the key.
+ *
+ * store_key - pointer to char* which will hold a copy of the key with
+ * lowercase section and variable name
+ * baselen - pointer to int which will hold the length of the
+ * section + subsection part, can be NULL
+ */
+int git_config_parse_key(const char *key, char **store_key, int *baselen_)
+{
+ int i, dot, baselen;
+ const char *last_dot = strrchr(key, '.');
+
+ /*
+ * Since "key" actually contains the section name and the real
+ * key name separated by a dot, we have to know where the dot is.
+ */
+
+ if (last_dot == NULL || last_dot == key) {
+ error("key does not contain a section: %s", key);
+ return -CONFIG_NO_SECTION_OR_NAME;
+ }
+
+ if (!last_dot[1]) {
+ error("key does not contain variable name: %s", key);
+ return -CONFIG_NO_SECTION_OR_NAME;
+ }
+
+ baselen = last_dot - key;
+ if (baselen_)
+ *baselen_ = baselen;
+
+ /*
+ * Validate the key and while at it, lower case it for matching.
+ */
+ *store_key = xmalloc(strlen(key) + 1);
+
+ dot = 0;
+ for (i = 0; key[i]; i++) {
+ unsigned char c = key[i];
+ if (c == '.')
+ dot = 1;
+ /* Leave the extended basename untouched.. */
+ if (!dot || i > baselen) {
+ if (!iskeychar(c) ||
+ (i == baselen + 1 && !isalpha(c))) {
+ error("invalid key: %s", key);
+ goto out_free_ret_1;
+ }
+ c = tolower(c);
+ } else if (c == '\n') {
+ error("invalid key (newline): %s", key);
+ goto out_free_ret_1;
+ }
+ (*store_key)[i] = c;
+ }
+ (*store_key)[i] = 0;
+
+ return 0;
+
+out_free_ret_1:
+ free(*store_key);
+ return -CONFIG_INVALID_KEY;
+}
+
/*
* If value==NULL, unset in (remove from) config,
* if value_regex!=NULL, disregard key/value pairs where value does not match.
int git_config_set_multivar(const char *key, const char *value,
const char *value_regex, int multi_replace)
{
- int i, dot;
int fd = -1, in_fd;
int ret;
char *config_filename;
struct lock_file *lock = NULL;
- const char *last_dot = strrchr(key, '.');
if (config_exclusive_filename)
config_filename = xstrdup(config_exclusive_filename);
else
config_filename = git_pathdup("config");
- /*
- * Since "key" actually contains the section name and the real
- * key name separated by a dot, we have to know where the dot is.
- */
-
- if (last_dot == NULL) {
- error("key does not contain a section: %s", key);
- ret = 2;
+ /* parse-key returns negative; flip the sign to feed exit(3) */
+ ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
+ if (ret)
goto out_free;
- }
- store.baselen = last_dot - key;
store.multi_replace = multi_replace;
- /*
- * Validate the key and while at it, lower case it for matching.
- */
- store.key = xmalloc(strlen(key) + 1);
- dot = 0;
- for (i = 0; key[i]; i++) {
- unsigned char c = key[i];
- if (c == '.')
- dot = 1;
- /* Leave the extended basename untouched.. */
- if (!dot || i > store.baselen) {
- if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
- error("invalid key: %s", key);
- free(store.key);
- ret = 1;
- goto out_free;
- }
- c = tolower(c);
- } else if (c == '\n') {
- error("invalid key (newline): %s", key);
- free(store.key);
- ret = 1;
- goto out_free;
- }
- store.key[i] = c;
- }
- store.key[i] = 0;
/*
* The lock serves a purpose in addition to locking: the new
if (fd < 0) {
error("could not lock config file %s: %s", config_filename, strerror(errno));
free(store.key);
- ret = -1;
+ ret = CONFIG_NO_LOCK;
goto out_free;
}
if ( ENOENT != errno ) {
error("opening %s: %s", config_filename,
strerror(errno));
- ret = 3; /* same as "invalid config file" */
+ ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
goto out_free;
}
/* if nothing to unset, error out */
if (value == NULL) {
- ret = 5;
+ ret = CONFIG_NOTHING_SET;
goto out_free;
}
REG_EXTENDED)) {
error("invalid pattern: %s", value_regex);
free(store.value_regex);
- ret = 6;
+ ret = CONFIG_INVALID_PATTERN;
goto out_free;
}
}
regfree(store.value_regex);
free(store.value_regex);
}
- ret = 3;
+ ret = CONFIG_INVALID_FILE;
goto out_free;
}
/* if nothing to unset, or too many matches, error out */
if ((store.seen == 0 && value == NULL) ||
(store.seen > 1 && multi_replace == 0)) {
- ret = 5;
+ ret = CONFIG_NOTHING_SET;
goto out_free;
}
if (commit_lock_file(lock) < 0) {
error("could not commit config file %s", config_filename);
- ret = 4;
+ ret = CONFIG_NO_WRITE;
goto out_free;
}
}
}
fclose(config_file);
- unlock_and_out:
+unlock_and_out:
if (commit_lock_file(lock) < 0)
ret = error("could not commit config file %s", config_filename);
- out:
+out:
free(config_filename);
return ret;
}