Merge branch 'sb/submodule-helper-clone-regression-fix' into sb/submodule-init
[gitweb.git] / config.c
index 66c0a51bce529e4c027f11017697a62dd737b3bd..86a5eb2571fb282a1d7bad9e6d097b2374b550dc 100644 (file)
--- a/config.c
+++ b/config.c
@@ -12,6 +12,7 @@
 #include "quote.h"
 #include "hashmap.h"
 #include "string-list.h"
+#include "utf8.h"
 
 struct config_source {
        struct config_source *prev;
@@ -49,7 +50,7 @@ static struct config_set the_config_set;
 
 static int config_file_fgetc(struct config_source *conf)
 {
-       return fgetc(conf->u.file);
+       return getc_unlocked(conf->u.file);
 }
 
 static int config_file_ungetc(int c, struct config_source *conf)
@@ -417,8 +418,7 @@ static int git_parse_source(config_fn_t fn, void *data)
        struct strbuf *var = &cf->var;
 
        /* U+FEFF Byte Order Mark in UTF8 */
-       static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
-       const unsigned char *bomptr = utf8_bom;
+       const char *bomptr = utf8_bom;
 
        for (;;) {
                int c = get_next_char();
@@ -426,7 +426,7 @@ static int git_parse_source(config_fn_t fn, void *data)
                        /* 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) {
+                       if (c == (*bomptr & 0377)) {
                                bomptr++;
                                continue;
                        } else {
@@ -618,7 +618,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
        return ret;
 }
 
-static int git_config_maybe_bool_text(const char *name, const char *value)
+int git_parse_maybe_bool(const char *value)
 {
        if (!value)
                return 1;
@@ -637,7 +637,7 @@ static int git_config_maybe_bool_text(const char *name, const char *value)
 
 int git_config_maybe_bool(const char *name, const char *value)
 {
-       int v = git_config_maybe_bool_text(name, value);
+       int v = git_parse_maybe_bool(value);
        if (0 <= v)
                return v;
        if (git_parse_int(value, &v))
@@ -647,7 +647,7 @@ int git_config_maybe_bool(const char *name, const char *value)
 
 int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 {
-       int v = git_config_maybe_bool_text(name, value);
+       int v = git_parse_maybe_bool(value);
        if (0 <= v) {
                *is_bool = 1;
                return v;
@@ -1088,7 +1088,9 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 
        f = fopen(filename, "r");
        if (f) {
+               flockfile(f);
                ret = do_config_from_file(fn, filename, filename, f, data);
+               funlockfile(f);
                fclose(f);
        }
        return ret;
@@ -1185,10 +1187,8 @@ int git_config_system(void)
 int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
        int ret = 0, found = 0;
-       char *xdg_config = NULL;
-       char *user_config = NULL;
-
-       home_config_paths(&user_config, &xdg_config, "config");
+       char *xdg_config = xdg_config_home("config");
+       char *user_config = expand_user_path("~/.gitconfig");
 
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
@@ -1848,7 +1848,7 @@ int git_config_set(const char *key, const char *value)
  * 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_)
+static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
 {
        int i, dot, baselen;
        const char *last_dot = strrchr(key, '.');
@@ -1859,12 +1859,14 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
         */
 
        if (last_dot == NULL || last_dot == key) {
-               error("key does not contain a section: %s", key);
+               if (!quiet)
+                       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);
+               if (!quiet)
+                       error("key does not contain variable name: %s", key);
                return -CONFIG_NO_SECTION_OR_NAME;
        }
 
@@ -1875,7 +1877,8 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
        /*
         * Validate the key and while at it, lower case it for matching.
         */
-       *store_key = xmalloc(strlen(key) + 1);
+       if (store_key)
+               *store_key = xmalloc(strlen(key) + 1);
 
        dot = 0;
        for (i = 0; key[i]; i++) {
@@ -1886,26 +1889,42 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
                if (!dot || i > baselen) {
                        if (!iskeychar(c) ||
                            (i == baselen + 1 && !isalpha(c))) {
-                               error("invalid key: %s", key);
+                               if (!quiet)
+                                       error("invalid key: %s", key);
                                goto out_free_ret_1;
                        }
                        c = tolower(c);
                } else if (c == '\n') {
-                       error("invalid key (newline): %s", key);
+                       if (!quiet)
+                               error("invalid key (newline): %s", key);
                        goto out_free_ret_1;
                }
-               (*store_key)[i] = c;
+               if (store_key)
+                       (*store_key)[i] = c;
        }
-       (*store_key)[i] = 0;
+       if (store_key)
+               (*store_key)[i] = 0;
 
        return 0;
 
 out_free_ret_1:
-       free(*store_key);
-       *store_key = NULL;
+       if (store_key) {
+               free(*store_key);
+               *store_key = NULL;
+       }
        return -CONFIG_INVALID_KEY;
 }
 
+int git_config_parse_key(const char *key, char **store_key, int *baselen)
+{
+       return git_config_parse_key_1(key, store_key, baselen, 0);
+}
+
+int git_config_key_is_valid(const char *key)
+{
+       return !git_config_parse_key_1(key, NULL, NULL, 1);
+}
+
 /*
  * If value==NULL, unset in (remove from) config,
  * if value_regex!=NULL, disregard key/value pairs where value does not match.
@@ -1935,10 +1954,12 @@ int git_config_set_multivar_in_file(const char *config_filename,
                                const char *key, const char *value,
                                const char *value_regex, int multi_replace)
 {
-       int fd = -1, in_fd;
+       int fd = -1, in_fd = -1;
        int ret;
        struct lock_file *lock = NULL;
        char *filename_buf = NULL;
+       char *contents = NULL;
+       size_t contents_sz;
 
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
@@ -1988,8 +2009,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
                        goto write_err_out;
        } else {
                struct stat st;
-               char *contents;
-               size_t contents_sz, copy_begin, copy_end;
+               size_t copy_begin, copy_end;
                int i, new_line = 0;
 
                if (value_regex == NULL)
@@ -2052,13 +2072,23 @@ int git_config_set_multivar_in_file(const char *config_filename,
 
                fstat(in_fd, &st);
                contents_sz = xsize_t(st.st_size);
-               contents = xmmap(NULL, contents_sz, PROT_READ,
-                       MAP_PRIVATE, in_fd, 0);
+               contents = xmmap_gently(NULL, contents_sz, PROT_READ,
+                                       MAP_PRIVATE, in_fd, 0);
+               if (contents == MAP_FAILED) {
+                       if (errno == ENODEV && S_ISDIR(st.st_mode))
+                               errno = EISDIR;
+                       error("unable to mmap '%s': %s",
+                             config_filename, strerror(errno));
+                       ret = CONFIG_INVALID_FILE;
+                       contents = NULL;
+                       goto out_free;
+               }
                close(in_fd);
+               in_fd = -1;
 
-               if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
+               if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
                        error("chmod on %s failed: %s",
-                               lock->filename.buf, strerror(errno));
+                             get_lock_file_path(lock), strerror(errno));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2110,10 +2140,12 @@ int git_config_set_multivar_in_file(const char *config_filename,
                                goto write_err_out;
 
                munmap(contents, contents_sz);
+               contents = NULL;
        }
 
        if (commit_lock_file(lock) < 0) {
-               error("could not commit config file %s", config_filename);
+               error("could not write config file %s: %s", config_filename,
+                     strerror(errno));
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@ -2135,10 +2167,14 @@ int git_config_set_multivar_in_file(const char *config_filename,
        if (lock)
                rollback_lock_file(lock);
        free(filename_buf);
+       if (contents)
+               munmap(contents, contents_sz);
+       if (in_fd >= 0)
+               close(in_fd);
        return ret;
 
 write_err_out:
-       ret = write_error(lock->filename.buf);
+       ret = write_error(get_lock_file_path(lock));
        goto out_free;
 
 }
@@ -2239,9 +2275,9 @@ int git_config_rename_section_in_file(const char *config_filename,
 
        fstat(fileno(config_file), &st);
 
-       if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
+       if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
                ret = error("chmod on %s failed: %s",
-                               lock->filename.buf, strerror(errno));
+                           get_lock_file_path(lock), strerror(errno));
                goto out;
        }
 
@@ -2262,7 +2298,7 @@ int git_config_rename_section_in_file(const char *config_filename,
                                }
                                store.baselen = strlen(new_name);
                                if (!store_write_section(out_fd, new_name)) {
-                                       ret = write_error(lock->filename.buf);
+                                       ret = write_error(get_lock_file_path(lock));
                                        goto out;
                                }
                                /*
@@ -2288,14 +2324,15 @@ int git_config_rename_section_in_file(const char *config_filename,
                        continue;
                length = strlen(output);
                if (write_in_full(out_fd, output, length) != length) {
-                       ret = write_error(lock->filename.buf);
+                       ret = write_error(get_lock_file_path(lock));
                        goto out;
                }
        }
        fclose(config_file);
 unlock_and_out:
        if (commit_lock_file(lock) < 0)
-               ret = error("could not commit config file %s", config_filename);
+               ret = error("could not write config file %s: %s",
+                           config_filename, strerror(errno));
 out:
        free(filename_buf);
        return ret;