test/send-email: --[no-]xmailer tests
[gitweb.git] / config.c
index 314d8ee740bea488d8c79d80b3d91530ad15252d..9e42d3832bbcce428705ef616d5b2008c47ec91a 100644 (file)
--- a/config.c
+++ b/config.c
@@ -21,6 +21,7 @@ struct config_source {
                } buf;
        } u;
        const char *name;
+       const char *path;
        int die_on_error;
        int linenr;
        int eof;
@@ -101,12 +102,12 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!is_absolute_path(path)) {
                char *slash;
 
-               if (!cf || !cf->name)
+               if (!cf || !cf->path)
                        return error("relative config includes must come from files");
 
-               slash = find_last_dir_sep(cf->name);
+               slash = find_last_dir_sep(cf->path);
                if (slash)
-                       strbuf_add(&buf, cf->name, slash - cf->name + 1);
+                       strbuf_add(&buf, cf->path, slash - cf->path + 1);
                strbuf_addstr(&buf, path);
                path = buf.buf;
        }
@@ -137,8 +138,7 @@ int git_config_include(const char *var, const char *value, void *data)
        if (ret < 0)
                return ret;
 
-       type = skip_prefix(var, "include.");
-       if (!type)
+       if (!skip_prefix(var, "include.", &type))
                return ret;
 
        if (!strcmp(type, "path"))
@@ -146,12 +146,6 @@ int git_config_include(const char *var, const char *value, void *data)
        return ret;
 }
 
-static void lowercase(char *p)
-{
-       for (; *p; p++)
-               *p = tolower(*p);
-}
-
 void git_config_push_parameter(const char *text)
 {
        struct strbuf env = STRBUF_INIT;
@@ -168,19 +162,27 @@ void git_config_push_parameter(const char *text)
 int git_config_parse_parameter(const char *text,
                               config_fn_t fn, void *data)
 {
+       const char *value;
        struct strbuf **pair;
+
        pair = strbuf_split_str(text, '=', 2);
        if (!pair[0])
                return error("bogus config parameter: %s", text);
-       if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
+
+       if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') {
                strbuf_setlen(pair[0], pair[0]->len - 1);
+               value = pair[1] ? pair[1]->buf : "";
+       } else {
+               value = NULL;
+       }
+
        strbuf_trim(pair[0]);
        if (!pair[0]->len) {
                strbuf_list_free(pair);
                return error("bogus config parameter: %s", text);
        }
-       lowercase(pair[0]->buf);
-       if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
+       strbuf_tolower(pair[0]);
+       if (fn(pair[0]->buf, value, data) < 0) {
                strbuf_list_free(pair);
                return -1;
        }
@@ -556,6 +558,7 @@ int git_parse_ulong(const char *value, unsigned long *ret)
        return 1;
 }
 
+NORETURN
 static void die_bad_number(const char *name, const char *value)
 {
        const char *reason = errno == ERANGE ?
@@ -667,20 +670,7 @@ static int git_default_core_config(const char *var, const char *value)
                trust_ctime = git_config_bool(var, value);
                return 0;
        }
-       if (!strcmp(var, "core.statinfo") ||
-           !strcmp(var, "core.checkstat")) {
-               /*
-                * NEEDSWORK: statinfo was a typo in v1.8.2 that has
-                * never been advertised.  we will remove it at Git
-                * 2.0 boundary.
-                */
-               if (!strcmp(var, "core.statinfo")) {
-                       static int warned;
-                       if (!warned++) {
-                               warning("'core.statinfo' will be removed in Git 2.0; "
-                                       "use 'core.checkstat' instead.");
-                       }
-               }
+       if (!strcmp(var, "core.checkstat")) {
                if (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
@@ -835,11 +825,16 @@ static int git_default_core_config(const char *var, const char *value)
                return git_config_string(&editor_program, var, value);
 
        if (!strcmp(var, "core.commentchar")) {
-               const char *comment;
-               int ret = git_config_string(&comment, var, value);
-               if (!ret)
-                       comment_line_char = comment[0];
-               return ret;
+               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];
+                       auto_comment_line_char = 0;
+               } else
+                       return error("core.commentChar should only be one character");
+               return 0;
        }
 
        if (!strcmp(var, "core.askpass"))
@@ -963,7 +958,7 @@ static int git_default_push_config(const char *var, const char *value)
 static int git_default_mailmap_config(const char *var, const char *value)
 {
        if (!strcmp(var, "mailmap.file"))
-               return git_config_string(&git_mailmap_file, var, value);
+               return git_config_pathname(&git_mailmap_file, var, value);
        if (!strcmp(var, "mailmap.blob"))
                return git_config_string(&git_mailmap_blob, var, value);
 
@@ -1034,24 +1029,35 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
        return ret;
 }
 
-int git_config_from_file(config_fn_t fn, const char *filename, void *data)
+static int do_config_from_file(config_fn_t fn,
+               const char *name, const char *path, FILE *f, void *data)
 {
-       int ret;
-       FILE *f = fopen(filename, "r");
+       struct config_source top;
 
-       ret = -1;
-       if (f) {
-               struct config_source top;
+       top.u.file = f;
+       top.name = name;
+       top.path = path;
+       top.die_on_error = 1;
+       top.do_fgetc = config_file_fgetc;
+       top.do_ungetc = config_file_ungetc;
+       top.do_ftell = config_file_ftell;
 
-               top.u.file = f;
-               top.name = filename;
-               top.die_on_error = 1;
-               top.do_fgetc = config_file_fgetc;
-               top.do_ungetc = config_file_ungetc;
-               top.do_ftell = config_file_ftell;
+       return do_config_from(&top, fn, data);
+}
 
-               ret = do_config_from(&top, fn, data);
+static int git_config_from_stdin(config_fn_t fn, void *data)
+{
+       return do_config_from_file(fn, "<stdin>", NULL, stdin, data);
+}
+
+int git_config_from_file(config_fn_t fn, const char *filename, void *data)
+{
+       int ret = -1;
+       FILE *f;
 
+       f = fopen(filename, "r");
+       if (f) {
+               ret = do_config_from_file(fn, filename, filename, f, data);
                fclose(f);
        }
        return ret;
@@ -1066,6 +1072,7 @@ int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
        top.u.buf.len = len;
        top.u.buf.pos = 0;
        top.name = name;
+       top.path = NULL;
        top.die_on_error = 0;
        top.do_fgetc = config_buf_fgetc;
        top.do_ungetc = config_buf_ungetc;
@@ -1174,8 +1181,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 }
 
 int git_config_with_options(config_fn_t fn, void *data,
-                           const char *filename,
-                           const char *blob_ref,
+                           struct git_config_source *config_source,
                            int respect_includes)
 {
        char *repo_config = NULL;
@@ -1193,10 +1199,12 @@ int git_config_with_options(config_fn_t fn, void *data,
         * If we have a specific filename, use it. Otherwise, follow the
         * regular lookup sequence.
         */
-       if (filename)
-               return git_config_from_file(fn, filename, data);
-       else if (blob_ref)
-               return git_config_from_blob_ref(fn, blob_ref, data);
+       if (config_source && config_source->use_stdin)
+               return git_config_from_stdin(fn, data);
+       else if (config_source && config_source->file)
+               return git_config_from_file(fn, config_source->file, data);
+       else if (config_source && config_source->blob)
+               return git_config_from_blob_ref(fn, config_source->blob, data);
 
        repo_config = git_pathdup("config");
        ret = git_config_early(fn, data, repo_config);
@@ -1207,7 +1215,7 @@ int git_config_with_options(config_fn_t fn, void *data,
 
 int git_config(config_fn_t fn, void *data)
 {
-       return git_config_with_options(fn, data, NULL, NULL, 1);
+       return git_config_with_options(fn, data, NULL, 1);
 }
 
 /*
@@ -1228,10 +1236,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)
@@ -1493,6 +1506,8 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 /*
  * 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.
@@ -1536,7 +1551,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
         * The lock serves a purpose in addition to locking: the new
         * contents of .git/config will be written into it.
         */
-       lock = xcalloc(sizeof(struct lock_file), 1);
+       lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
                error("could not lock config file %s: %s", config_filename, strerror(errno));
@@ -1576,6 +1591,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;
@@ -1607,7 +1624,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);
                        }
@@ -1616,7 +1634,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);
                }
@@ -1634,6 +1653,13 @@ 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) {
+                       error("chmod on %s failed: %s",
+                               lock->filename, strerror(errno));
+                       ret = CONFIG_NO_WRITE;
+                       goto out_free;
+               }
+
                if (store.seen == 0)
                        store.seen = 1;
 
@@ -1782,6 +1808,7 @@ int git_config_rename_section_in_file(const char *config_filename,
        int out_fd;
        char buf[1024];
        FILE *config_file;
+       struct stat st;
 
        if (new_name && !section_name_is_ok(new_name)) {
                ret = error("invalid section name: %s", new_name);
@@ -1791,7 +1818,7 @@ int git_config_rename_section_in_file(const char *config_filename,
        if (!config_filename)
                config_filename = filename_buf = git_pathdup("config");
 
-       lock = xcalloc(sizeof(struct lock_file), 1);
+       lock = xcalloc(1, sizeof(struct lock_file));
        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);
@@ -1803,6 +1830,14 @@ int git_config_rename_section_in_file(const char *config_filename,
                goto unlock_and_out;
        }
 
+       fstat(fileno(config_file), &st);
+
+       if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+               ret = error("chmod on %s failed: %s",
+                               lock->filename, strerror(errno));
+               goto out;
+       }
+
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
                int length;