Merge branch 'jk/config-int-range-check'
authorJunio C Hamano <gitster@pobox.com>
Thu, 12 Sep 2013 21:41:00 +0000 (14:41 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 12 Sep 2013 21:41:00 +0000 (14:41 -0700)
"git config" did not provide a way to set or access numbers larger
than a native "int" on the platform; it now provides 64-bit signed
integers on all platforms.

* jk/config-int-range-check:
git-config: always treat --int as 64-bit internally
config: make numeric parsing errors more clear
config: set errno in numeric git_parse_* functions
config: properly range-check integer values
config: factor out integer parsing from range checks

builtin/config.c
cache.h
config.c
t/t1300-repo-config.sh
t/t4055-diff-context.sh
index fc8d8820cbd2c0fcddb69aeb3fa258cf87e96814..20e89fe4e0c7db50267e8e9358c38faea2259fab 100644 (file)
@@ -119,7 +119,8 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
                must_print_delim = 1;
        }
        if (types == TYPE_INT)
-               sprintf(value, "%d", git_config_int(key_, value_ ? value_ : ""));
+               sprintf(value, "%"PRId64,
+                       git_config_int64(key_, value_ ? value_ : ""));
        else if (types == TYPE_BOOL)
                vptr = git_config_bool(key_, value_) ? "true" : "false";
        else if (types == TYPE_BOOL_OR_INT) {
@@ -268,8 +269,8 @@ static char *normalize_value(const char *key, const char *value)
        else {
                normalized = xmalloc(64);
                if (types == TYPE_INT) {
-                       int v = git_config_int(key, value);
-                       sprintf(normalized, "%d", v);
+                       int64_t v = git_config_int64(key, value);
+                       sprintf(normalized, "%"PRId64, v);
                }
                else if (types == TYPE_BOOL)
                        sprintf(normalized, "%s",
diff --git a/cache.h b/cache.h
index 9ef778a5a9ab1e88e595ea3b8fef1656a783093b..a47b9c03036d41e8067a843dda305bc9b6161255 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1115,6 +1115,7 @@ extern int git_config_with_options(config_fn_t fn, void *,
 extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
+extern int64_t git_config_int64(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
 extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
index 9f9bf0cc9a1405e938fddbedc44468782ad61021..6588cf579f2e44533192027c739b4823562b2275 100644 (file)
--- a/config.c
+++ b/config.c
@@ -468,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;
@@ -480,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)
+int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
 {
        if (value && *value) {
                char *end;
@@ -506,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;
 }
 
@@ -536,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;
 }
 
@@ -559,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;
 }
index c23f4781e102425d3bcc1de69cf48fc79e15509b..967359344dab8118dfd55816ef08f4809a9f33ad 100755 (executable)
@@ -652,16 +652,23 @@ test_expect_success numbers '
        test_cmp expect actual
 '
 
+test_expect_success '--int is at least 64 bits' '
+       git config giga.watts 121g &&
+       echo 129922760704 >expect &&
+       git config --int --get giga.watts >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'invalid unit' '
        git config aninvalid.unit "1auto" &&
        echo 1auto >expect &&
        git config aninvalid.unit >actual &&
        test_cmp expect actual &&
-       cat > expect <<-\EOF
-       fatal: bad config value for '\''aninvalid.unit'\'' in .git/config
+       cat >expect <<-\EOF
+       fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit
        EOF
        test_must_fail git config --int --get aninvalid.unit 2>actual &&
-       test_cmp actual expect
+       test_i18ncmp expect actual
 '
 
 cat > expect << EOF
index 97172b46b2afd52ca8f278f9525da01a56823e59..cd0454356a6ea26c630f76748dd2c4f73bed8526 100755 (executable)
@@ -73,7 +73,7 @@ test_expect_success 'plumbing not affected' '
 test_expect_success 'non-integer config parsing' '
        git config diff.context no &&
        test_must_fail git diff 2>output &&
-       test_i18ngrep "bad config value" output
+       test_i18ngrep "bad numeric config value" output
 '
 
 test_expect_success 'negative integer config parsing' '