teach convert_to_git a "dry run" mode
[gitweb.git] / config.c
index 49e5250dc5384a012dc828d156cbf8686db9d0bb..5ea101fb251d27eadac20c665a7f01fb210c20d1 100644 (file)
--- a/config.c
+++ b/config.c
 
 #define MAXNAME (256)
 
-static FILE *config_file;
-static const char *config_file_name;
-static int config_linenr;
-static int config_file_eof;
+typedef struct config_file {
+       struct config_file *prev;
+       FILE *f;
+       const char *name;
+       int linenr;
+       int eof;
+       struct strbuf value;
+       char var[MAXNAME];
+} config_file;
+
+static config_file *cf;
+
 static int zlib_compression_seen;
 
 const char *config_exclusive_filename = NULL;
@@ -39,8 +47,8 @@ void git_config_push_parameter(const char *text)
        strbuf_release(&env);
 }
 
-static int git_config_parse_parameter(const char *text,
-                                     config_fn_t fn, void *data)
+int git_config_parse_parameter(const char *text,
+                              config_fn_t fn, void *data)
 {
        struct strbuf **pair;
        pair = strbuf_split_str(text, '=', 2);
@@ -99,7 +107,7 @@ static int get_next_char(void)
        FILE *f;
 
        c = '\n';
-       if ((f = config_file) != NULL) {
+       if (cf && ((f = cf->f) != NULL)) {
                c = fgetc(f);
                if (c == '\r') {
                        /* DOS like systems */
@@ -110,9 +118,9 @@ static int get_next_char(void)
                        }
                }
                if (c == '\n')
-                       config_linenr++;
+                       cf->linenr++;
                if (c == EOF) {
-                       config_file_eof = 1;
+                       cf->eof = 1;
                        c = '\n';
                }
        }
@@ -121,21 +129,20 @@ static int get_next_char(void)
 
 static char *parse_value(void)
 {
-       static struct strbuf value = STRBUF_INIT;
        int quote = 0, comment = 0, space = 0;
 
-       strbuf_reset(&value);
+       strbuf_reset(&cf->value);
        for (;;) {
                int c = get_next_char();
                if (c == '\n') {
                        if (quote)
                                return NULL;
-                       return value.buf;
+                       return cf->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       if (value.len)
+                       if (cf->value.len)
                                space++;
                        continue;
                }
@@ -146,7 +153,7 @@ static char *parse_value(void)
                        }
                }
                for (; space; space--)
-                       strbuf_addch(&value, ' ');
+                       strbuf_addch(&cf->value, ' ');
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
@@ -168,14 +175,14 @@ static char *parse_value(void)
                        default:
                                return NULL;
                        }
-                       strbuf_addch(&value, c);
+                       strbuf_addch(&cf->value, c);
                        continue;
                }
                if (c == '"') {
                        quote = 1-quote;
                        continue;
                }
-               strbuf_addch(&value, c);
+               strbuf_addch(&cf->value, c);
        }
 }
 
@@ -192,7 +199,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
        /* Get the full name */
        for (;;) {
                c = get_next_char();
-               if (config_file_eof)
+               if (cf->eof)
                        break;
                if (!iskeychar(c))
                        break;
@@ -256,7 +263,7 @@ static int get_base_var(char *name)
 
        for (;;) {
                int c = get_next_char();
-               if (config_file_eof)
+               if (cf->eof)
                        return -1;
                if (c == ']')
                        return baselen;
@@ -274,7 +281,7 @@ static int git_parse_file(config_fn_t fn, void *data)
 {
        int comment = 0;
        int baselen = 0;
-       static char var[MAXNAME];
+       char *var = cf->var;
 
        /* U+FEFF Byte Order Mark in UTF8 */
        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
@@ -298,7 +305,7 @@ static int git_parse_file(config_fn_t fn, void *data)
                        }
                }
                if (c == '\n') {
-                       if (config_file_eof)
+                       if (cf->eof)
                                return 0;
                        comment = 0;
                        continue;
@@ -323,10 +330,10 @@ static int git_parse_file(config_fn_t fn, void *data)
                if (get_value(fn, data, var, baselen+1) < 0)
                        break;
        }
-       die("bad config file line %d in %s", config_linenr, config_file_name);
+       die("bad config file line %d in %s", cf->linenr, cf->name);
 }
 
-static int parse_unit_factor(const char *end, unsigned long *val)
+static int parse_unit_factor(const char *end, uintmax_t *val)
 {
        if (!*end)
                return 1;
@@ -349,11 +356,23 @@ static int git_parse_long(const char *value, long *ret)
 {
        if (value && *value) {
                char *end;
-               long val = strtol(value, &end, 0);
-               unsigned long factor = 1;
+               intmax_t val;
+               uintmax_t uval;
+               uintmax_t factor = 1;
+
+               errno = 0;
+               val = strtoimax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
                if (!parse_unit_factor(end, &factor))
                        return 0;
-               *ret = val * factor;
+               uval = abs(val);
+               uval *= factor;
+               if ((uval > maximum_signed_value_of_type(long)) ||
+                   (abs(val) > uval))
+                       return 0;
+               val *= factor;
+               *ret = val;
                return 1;
        }
        return 0;
@@ -363,9 +382,19 @@ int git_parse_ulong(const char *value, unsigned long *ret)
 {
        if (value && *value) {
                char *end;
-               unsigned long val = strtoul(value, &end, 0);
+               uintmax_t val;
+               uintmax_t oldval;
+
+               errno = 0;
+               val = strtoumax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
+               oldval = val;
                if (!parse_unit_factor(end, &val))
                        return 0;
+               if ((val > maximum_unsigned_value_of_type(long)) ||
+                   (oldval > val))
+                       return 0;
                *ret = val;
                return 1;
        }
@@ -374,8 +403,8 @@ int git_parse_ulong(const char *value, unsigned long *ret)
 
 static void die_bad_config(const char *name)
 {
-       if (config_file_name)
-               die("bad config value for '%s' in %s", name, config_file_name);
+       if (cf && cf->name)
+               die("bad config value for '%s' in %s", name, cf->name);
        die("bad config value for '%s'", name);
 }
 
@@ -484,6 +513,9 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.attributesfile"))
+               return git_config_pathname(&git_attributes_file, var, value);
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@ -543,7 +575,7 @@ static int git_default_core_config(const char *var, const char *value)
 
        if (!strcmp(var, "core.packedgitwindowsize")) {
                int pgsz_x2 = getpagesize() * 2;
-               packed_git_window_size = git_config_int(var, value);
+               packed_git_window_size = git_config_ulong(var, value);
 
                /* This value must be multiple of (pagesize * 2) */
                packed_git_window_size /= pgsz_x2;
@@ -554,21 +586,23 @@ static int git_default_core_config(const char *var, const char *value)
        }
 
        if (!strcmp(var, "core.bigfilethreshold")) {
-               long n = git_config_int(var, value);
-               big_file_threshold = 0 < n ? n : 0;
+               big_file_threshold = git_config_ulong(var, value);
                return 0;
        }
 
        if (!strcmp(var, "core.packedgitlimit")) {
-               packed_git_limit = git_config_int(var, value);
+               packed_git_limit = git_config_ulong(var, value);
                return 0;
        }
 
        if (!strcmp(var, "core.deltabasecachelimit")) {
-               delta_base_cache_limit = git_config_int(var, value);
+               delta_base_cache_limit = git_config_ulong(var, value);
                return 0;
        }
 
+       if (!strcmp(var, "core.logpackaccess"))
+               return git_config_string(&log_pack_access, var, value);
+
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
                        if (core_eol == EOL_CRLF)
@@ -795,13 +829,24 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 
        ret = -1;
        if (f) {
-               config_file = f;
-               config_file_name = filename;
-               config_linenr = 1;
-               config_file_eof = 0;
+               config_file top;
+
+               /* push config-file parsing state stack */
+               top.prev = cf;
+               top.f = f;
+               top.name = filename;
+               top.linenr = 1;
+               top.eof = 0;
+               strbuf_init(&top.value, 1024);
+               cf = &top;
+
                ret = git_parse_file(fn, data);
+
+               /* pop config-file parsing state stack */
+               strbuf_release(&top.value);
+               cf = top.prev;
+
                fclose(f);
-               config_file_name = NULL;
        }
        return ret;
 }
@@ -841,12 +886,12 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 
        home = getenv("HOME");
        if (home) {
-               char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
+               char buf[PATH_MAX];
+               char *user_config = mksnpath(buf, sizeof(buf), "%s/.gitconfig", home);
                if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
                        found += 1;
                }
-               free(user_config);
        }
 
        if (repo_config && !access(repo_config, R_OK)) {
@@ -909,6 +954,7 @@ static int store_aux(const char *key, const char *value, void *cb)
 {
        const char *ep;
        size_t section_len;
+       FILE *f = cf->f;
 
        switch (store.state) {
        case KEY_SEEN:
@@ -920,7 +966,7 @@ static int store_aux(const char *key, const char *value, void *cb)
                                return 1;
                        }
 
-                       store.offset[store.seen] = ftell(config_file);
+                       store.offset[store.seen] = ftell(f);
                        store.seen++;
                }
                break;
@@ -947,19 +993,19 @@ static int store_aux(const char *key, const char *value, void *cb)
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
-               store.offset[store.seen] = ftell(config_file);
+               store.offset[store.seen] = ftell(f);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
-                       store.offset[store.seen] = ftell(config_file);
+                       store.offset[store.seen] = ftell(f);
                        store.state = KEY_SEEN;
                        store.seen++;
                } else {
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
-                                       store.offset[store.seen] = ftell(config_file);
+                                       store.offset[store.seen] = ftell(f);
                        }
                }
        }
@@ -1073,6 +1119,12 @@ static ssize_t find_beginning_of_line(const char *contents, size_t size,
        return offset;
 }
 
+int git_config_set_in_file(const char *config_filename,
+                       const char *key, const char *value)
+{
+       return git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
+}
+
 int git_config_set(const char *key, const char *value)
 {
        return git_config_set_multivar(key, value, NULL, 0);
@@ -1170,19 +1222,14 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
  * - 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 char *value_regex, int multi_replace)
+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 ret;
-       char *config_filename;
        struct lock_file *lock = NULL;
 
-       if (config_exclusive_filename)
-               config_filename = xstrdup(config_exclusive_filename);
-       else
-               config_filename = git_pathdup("config");
-
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
        if (ret)
@@ -1359,7 +1406,6 @@ int git_config_set_multivar(const char *key, const char *value,
 out_free:
        if (lock)
                rollback_lock_file(lock);
-       free(config_filename);
        return ret;
 
 write_err_out:
@@ -1368,6 +1414,24 @@ int git_config_set_multivar(const char *key, const char *value,
 
 }
 
+int git_config_set_multivar(const char *key, const char *value,
+                       const char *value_regex, int multi_replace)
+{
+       const char *config_filename;
+       char *buf = NULL;
+       int ret;
+
+       if (config_exclusive_filename)
+               config_filename = config_exclusive_filename;
+       else
+               config_filename = buf = git_pathdup("config");
+
+       ret = git_config_set_multivar_in_file(config_filename, key, value,
+                                       value_regex, multi_replace);
+       free(buf);
+       return ret;
+}
+
 static int section_name_match (const char *buf, const char *name)
 {
        int i = 0, j = 0, dot = 0;
@@ -1415,6 +1479,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
        struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
        int out_fd;
        char buf[1024];
+       FILE *config_file;
 
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);