transport-helper.c: do not overwrite forced bit
[gitweb.git] / config.c
index 10a015be4a7f5f10ac32f4a9aab0fba86b70fe0f..e1d66a145b756c49c4e4902200c354499532a428 100644 (file)
--- a/config.c
+++ b/config.c
@@ -21,14 +21,15 @@ struct config_source {
                } buf;
        } u;
        const char *name;
+       int die_on_error;
        int linenr;
        int eof;
        struct strbuf value;
        struct strbuf var;
 
-       int (*fgetc)(struct config_source *c);
-       int (*ungetc)(int c, struct config_source *conf);
-       long (*ftell)(struct config_source *c);
+       int (*do_fgetc)(struct config_source *c);
+       int (*do_ungetc)(int c, struct config_source *conf);
+       long (*do_ftell)(struct config_source *c);
 };
 
 static struct config_source *cf;
@@ -106,7 +107,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
                path = buf.buf;
        }
 
-       if (!access_or_die(path, R_OK)) {
+       if (!access_or_die(path, R_OK, 0)) {
                if (++inc->depth > MAX_INCLUDE_DEPTH)
                        die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
                            cf && cf->name ? cf->name : "the command line");
@@ -216,13 +217,13 @@ int git_config_from_parameters(config_fn_t fn, void *data)
 
 static int get_next_char(void)
 {
-       int c = cf->fgetc(cf);
+       int c = cf->do_fgetc(cf);
 
        if (c == '\r') {
                /* DOS like systems */
-               c = cf->fgetc(cf);
+               c = cf->do_fgetc(cf);
                if (c != '\n') {
-                       cf->ungetc(c, cf);
+                       cf->do_ungetc(c, cf);
                        c = '\r';
                }
        }
@@ -442,7 +443,10 @@ static int git_parse_source(config_fn_t fn, void *data)
                if (get_value(fn, data, var) < 0)
                        break;
        }
-       die("bad config file line %d in %s", cf->linenr, cf->name);
+       if (cf->die_on_error)
+               die("bad config file line %d in %s", cf->linenr, cf->name);
+       else
+               return error("bad config file line %d in %s", cf->linenr, cf->name);
 }
 
 static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -464,7 +468,7 @@ static int parse_unit_factor(const char *end, uintmax_t *val)
        return 0;
 }
 
-static int git_parse_long(const char *value, long *ret)
+static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
 {
        if (value && *value) {
                char *end;
@@ -476,21 +480,25 @@ static int git_parse_long(const char *value, long *ret)
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               if (!parse_unit_factor(end, &factor))
+               if (!parse_unit_factor(end, &factor)) {
+                       errno = EINVAL;
                        return 0;
+               }
                uval = abs(val);
                uval *= factor;
-               if ((uval > maximum_signed_value_of_type(long)) ||
-                   (abs(val) > uval))
+               if (uval > max || abs(val) > uval) {
+                       errno = ERANGE;
                        return 0;
+               }
                val *= factor;
                *ret = val;
                return 1;
        }
+       errno = EINVAL;
        return 0;
 }
 
-int git_parse_ulong(const char *value, unsigned long *ret)
+static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
 {
        if (value && *value) {
                char *end;
@@ -502,29 +510,75 @@ int git_parse_ulong(const char *value, unsigned long *ret)
                if (errno == ERANGE)
                        return 0;
                oldval = val;
-               if (!parse_unit_factor(end, &val))
+               if (!parse_unit_factor(end, &val)) {
+                       errno = EINVAL;
                        return 0;
-               if ((val > maximum_unsigned_value_of_type(long)) ||
-                   (oldval > val))
+               }
+               if (val > max || oldval > val) {
+                       errno = ERANGE;
                        return 0;
+               }
                *ret = val;
                return 1;
        }
+       errno = EINVAL;
        return 0;
 }
 
-static void die_bad_config(const char *name)
+static int git_parse_int(const char *value, int *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+static int git_parse_int64(const char *value, int64_t *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_ulong(const char *value, unsigned long *ret)
+{
+       uintmax_t tmp;
+       if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+static void die_bad_number(const char *name, const char *value)
 {
+       const char *reason = errno == ERANGE ?
+                            "out of range" :
+                            "invalid unit";
+       if (!value)
+               value = "";
+
        if (cf && cf->name)
-               die("bad config value for '%s' in %s", name, cf->name);
-       die("bad config value for '%s'", name);
+               die("bad numeric config value '%s' for '%s' in %s: %s",
+                   value, name, cf->name, reason);
+       die("bad numeric config value '%s' for '%s': %s", value, name, reason);
 }
 
 int git_config_int(const char *name, const char *value)
 {
-       long ret = 0;
-       if (!git_parse_long(value, &ret))
-               die_bad_config(name);
+       int ret;
+       if (!git_parse_int(value, &ret))
+               die_bad_number(name, value);
+       return ret;
+}
+
+int64_t git_config_int64(const char *name, const char *value)
+{
+       int64_t ret;
+       if (!git_parse_int64(value, &ret))
+               die_bad_number(name, value);
        return ret;
 }
 
@@ -532,7 +586,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
 {
        unsigned long ret;
        if (!git_parse_ulong(value, &ret))
-               die_bad_config(name);
+               die_bad_number(name, value);
        return ret;
 }
 
@@ -555,10 +609,10 @@ static int git_config_maybe_bool_text(const char *name, const char *value)
 
 int git_config_maybe_bool(const char *name, const char *value)
 {
-       long v = git_config_maybe_bool_text(name, value);
+       int v = git_config_maybe_bool_text(name, value);
        if (0 <= v)
                return v;
-       if (git_parse_long(value, &v))
+       if (git_parse_int(value, &v))
                return !!v;
        return -1;
 }
@@ -609,7 +663,20 @@ 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")) {
+       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 (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
@@ -718,9 +785,6 @@ static int git_default_core_config(const char *var, const char *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)
@@ -940,7 +1004,7 @@ int git_default_config(const char *var, const char *value, void *dummy)
 }
 
 /*
- * All source specific fields in the union, name and the callbacks
+ * All source specific fields in the union, die_on_error, name and the callbacks
  * fgetc, ungetc, ftell of top need to be initialized before calling
  * this function.
  */
@@ -977,9 +1041,10 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 
                top.u.file = f;
                top.name = filename;
-               top.fgetc = config_file_fgetc;
-               top.ungetc = config_file_ungetc;
-               top.ftell = config_file_ftell;
+               top.die_on_error = 1;
+               top.do_fgetc = config_file_fgetc;
+               top.do_ungetc = config_file_ungetc;
+               top.do_ftell = config_file_ftell;
 
                ret = do_config_from(&top, fn, data);
 
@@ -997,9 +1062,10 @@ 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.fgetc = config_buf_fgetc;
-       top.ungetc = config_buf_ungetc;
-       top.ftell = config_buf_ftell;
+       top.die_on_error = 0;
+       top.do_fgetc = config_buf_fgetc;
+       top.do_ungetc = config_buf_ungetc;
+       top.do_ftell = config_buf_ftell;
 
        return do_config_from(&top, fn, data);
 }
@@ -1066,23 +1132,23 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 
        home_config_paths(&user_config, &xdg_config, "config");
 
-       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK)) {
+       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
                found += 1;
        }
 
-       if (xdg_config && !access_or_die(xdg_config, R_OK)) {
+       if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) {
                ret += git_config_from_file(fn, xdg_config, data);
                found += 1;
        }
 
-       if (user_config && !access_or_die(user_config, R_OK)) {
+       if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) {
                ret += git_config_from_file(fn, user_config, data);
                found += 1;
        }
 
-       if (repo_config && !access_or_die(repo_config, R_OK)) {
+       if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
                ret += git_config_from_file(fn, repo_config, data);
                found += 1;
        }
@@ -1180,7 +1246,7 @@ static int store_aux(const char *key, const char *value, void *cb)
                                return 1;
                        }
 
-                       store.offset[store.seen] = cf->ftell(cf);
+                       store.offset[store.seen] = cf->do_ftell(cf);
                        store.seen++;
                }
                break;
@@ -1207,19 +1273,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] = cf->ftell(cf);
+               store.offset[store.seen] = cf->do_ftell(cf);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
-                       store.offset[store.seen] = cf->ftell(cf);
+                       store.offset[store.seen] = cf->do_ftell(cf);
                        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] = cf->ftell(cf);
+                                       store.offset[store.seen] = cf->do_ftell(cf);
                        }
                }
        }