Teach git var about GIT_EDITOR
[gitweb.git] / config.c
index 37e3c74861cb335d4bacab86c2f5d03cd95f43bf..c64406113686e8886c0719206ce57d73fee13b29 100644 (file)
--- a/config.c
+++ b/config.c
@@ -62,7 +62,8 @@ static char *parse_value(void)
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       space = 1;
+                       if (len)
+                               space++;
                        continue;
                }
                if (!quote) {
@@ -71,11 +72,8 @@ static char *parse_value(void)
                                continue;
                        }
                }
-               if (space) {
-                       if (len)
-                               value[len++] = ' ';
-                       space = 0;
-               }
+               for (; space; space--)
+                       value[len++] = ' ';
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
@@ -331,9 +329,9 @@ int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
                return 1;
        if (!*value)
                return 0;
-       if (!strcasecmp(value, "true") || !strcasecmp(value, "yes"))
+       if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
                return 1;
-       if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
+       if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
                return 0;
        *is_bool = 0;
        return git_config_int(name, value);
@@ -495,6 +493,16 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.createobject")) {
+               if (!strcmp(value, "rename"))
+                       object_creation_mode = OBJECT_CREATION_USES_RENAMES;
+               else if (!strcmp(value, "link"))
+                       object_creation_mode = OBJECT_CREATION_USES_HARDLINKS;
+               else
+                       die("Invalid mode for object creation: %s", value);
+               return 0;
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
@@ -565,6 +573,40 @@ static int git_default_branch_config(const char *var, const char *value)
        return 0;
 }
 
+static int git_default_push_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "push.default")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               else if (!strcmp(value, "nothing"))
+                       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, "current"))
+                       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 0;
+       }
+
+       /* Add other config variables here and to Documentation/config.txt. */
+       return 0;
+}
+
+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);
+
+       /* Add other config variables here and to Documentation/config.txt. */
+       return 0;
+}
+
 int git_default_config(const char *var, const char *value, void *dummy)
 {
        if (!prefixcmp(var, "core."))
@@ -579,6 +621,15 @@ int git_default_config(const char *var, const char *value, void *dummy)
        if (!prefixcmp(var, "branch."))
                return git_default_branch_config(var, value);
 
+       if (!prefixcmp(var, "push."))
+               return git_default_push_config(var, value);
+
+       if (!prefixcmp(var, "mailmap."))
+               return git_default_mailmap_config(var, value);
+
+       if (!prefixcmp(var, "advice."))
+               return git_default_advice_config(var, value);
+
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
                pager_use_color = git_config_bool(var,value);
                return 0;
@@ -632,28 +683,37 @@ int git_config_global(void)
 
 int git_config(config_fn_t fn, void *data)
 {
-       int ret = 0;
+       int ret = 0, found = 0;
        char *repo_config = NULL;
        const char *home = 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))
+       if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
+               found += 1;
+       }
 
        home = getenv("HOME");
        if (git_config_global() && home) {
                char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
-               if (!access(user_config, R_OK))
+               if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
+                       found += 1;
+               }
                free(user_config);
        }
 
        repo_config = git_pathdup("config");
-       ret += git_config_from_file(fn, repo_config, data);
+       if (!access(repo_config, R_OK)) {
+               ret += git_config_from_file(fn, repo_config, data);
+               found += 1;
+       }
        free(repo_config);
+       if (found == 0)
+               return -1;
        return ret;
 }
 
@@ -665,16 +725,16 @@ int git_config(config_fn_t fn, void *data)
 
 static struct {
        int baselen;
-       charkey;
+       char *key;
        int do_not_match;
-       regex_tvalue_regex;
+       regex_t *value_regex;
        int multi_replace;
        size_t offset[MAX_MATCHES];
        enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
        int seen;
 } store;
 
-static int matches(const char* key, const char* value)
+static int matches(const char *key, const char *value)
 {
        return !strcmp(key, store.key) &&
                (store.value_regex == NULL ||
@@ -682,7 +742,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, void *cb)
+static int store_aux(const char *key, const char *value, void *cb)
 {
        const char *ep;
        size_t section_len;
@@ -751,7 +811,7 @@ static int write_error(const char *filename)
        return 4;
 }
 
-static int store_write_section(int fd, const charkey)
+static int store_write_section(int fd, const char *key)
 {
        const char *dot;
        int i, success;
@@ -776,7 +836,7 @@ static int store_write_section(int fd, const char* key)
        return success;
 }
 
-static int store_write_pair(int fd, const char* key, const char* value)
+static int store_write_pair(int fd, const char *key, const char *value)
 {
        int i, success;
        int length = strlen(key + store.baselen + 1);
@@ -824,8 +884,8 @@ static int store_write_pair(int fd, const char* key, const char* value)
        return success;
 }
 
-static ssize_t find_beginning_of_line(const charcontents, size_t size,
-       size_t offset_, intfound_bracket)
+static ssize_t find_beginning_of_line(const char *contents, size_t size,
+       size_t offset_, int *found_bracket)
 {
        size_t equal_offset = size, bracket_offset = size;
        ssize_t offset;
@@ -850,7 +910,7 @@ static ssize_t find_beginning_of_line(const char* contents, size_t size,
        return offset;
 }
 
-int git_config_set(const char* key, const char* value)
+int git_config_set(const char *key, const char *value)
 {
        return git_config_set_multivar(key, value, NULL, 0);
 }
@@ -878,15 +938,15 @@ int git_config_set(const char* key, const char* value)
  * - the config file is removed and the lock file rename()d to it.
  *
  */
-int git_config_set_multivar(const char* key, const char* value,
-       const charvalue_regex, int multi_replace)
+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;
-       charconfig_filename;
+       char *config_filename;
        struct lock_file *lock = NULL;
-       const charlast_dot = strrchr(key, '.');
+       const char *last_dot = strrchr(key, '.');
 
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
@@ -942,7 +1002,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) {
-               error("could not lock config file %s", config_filename);
+               error("could not lock config file %s: %s", config_filename, strerror(errno));
                free(store.key);
                ret = -1;
                goto out_free;
@@ -967,13 +1027,13 @@ int git_config_set_multivar(const char* key, const char* value,
                        goto out_free;
                }
 
-               store.key = (char*)key;
+               store.key = (char *)key;
                if (!store_write_section(fd, key) ||
                    !store_write_pair(fd, key, value))
                        goto write_err_out;
        } else {
                struct stat st;
-               charcontents;
+               char *contents;
                size_t contents_sz, copy_begin, copy_end;
                int i, new_line = 0;
 
@@ -1059,7 +1119,7 @@ int git_config_set_multivar(const char* key, const char* value,
                                    copy_end - copy_begin)
                                        goto write_err_out;
                                if (new_line &&
-                                   write_in_full(fd, "\n", 1) != 1)
+                                   write_str_in_full(fd, "\n") != 1)
                                        goto write_err_out;
                        }
                        copy_begin = store.offset[i];
@@ -1115,7 +1175,9 @@ int git_config_set_multivar(const char* key, const char* value,
 static int section_name_match (const char *buf, const char *name)
 {
        int i = 0, j = 0, dot = 0;
-       for (; buf[i] && buf[i] != ']'; i++) {
+       if (buf[i] != '[')
+               return 0;
+       for (i = 1; buf[i] && buf[i] != ']'; i++) {
                if (!dot && isspace(buf[i])) {
                        dot = 1;
                        if (name[j++] != '.')
@@ -1136,7 +1198,17 @@ static int section_name_match (const char *buf, const char *name)
                if (buf[i] != name[j++])
                        break;
        }
-       return (buf[i] == ']' && name[j] == 0);
+       if (buf[i] == ']' && name[j] == 0) {
+               /*
+                * We match, now just find the right length offset by
+                * gobbling up any whitespace after it, as well
+                */
+               i++;
+               for (; buf[i] && isspace(buf[i]); i++)
+                       ; /* do nothing */
+               return i;
+       }
+       return 0;
 }
 
 /* if new_name == NULL, the section is removed instead */
@@ -1166,11 +1238,13 @@ int git_config_rename_section(const char *old_name, const char *new_name)
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
                int length;
+               char *output = buf;
                for (i = 0; buf[i] && isspace(buf[i]); i++)
                        ; /* do nothing */
                if (buf[i] == '[') {
                        /* it's a section */
-                       if (section_name_match (&buf[i+1], old_name)) {
+                       int offset = section_name_match(&buf[i], old_name);
+                       if (offset > 0) {
                                ret++;
                                if (new_name == NULL) {
                                        remove = 1;
@@ -1181,14 +1255,29 @@ int git_config_rename_section(const char *old_name, const char *new_name)
                                        ret = write_error(lock->filename);
                                        goto out;
                                }
-                               continue;
+                               /*
+                                * We wrote out the new section, with
+                                * a newline, now skip the old
+                                * section's length
+                                */
+                               output += offset + i;
+                               if (strlen(output) > 0) {
+                                       /*
+                                        * More content means there's
+                                        * a declaration to put on the
+                                        * next line; indent with a
+                                        * tab
+                                        */
+                                       output -= 1;
+                                       output[0] = '\t';
+                               }
                        }
                        remove = 0;
                }
                if (remove)
                        continue;
-               length = strlen(buf);
-               if (write_in_full(out_fd, buf, length) != length) {
+               length = strlen(output);
+               if (write_in_full(out_fd, output, length) != length) {
                        ret = write_error(lock->filename);
                        goto out;
                }