Merge branch 'jk/config-int-range-check'

"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
This commit is contained in:
Junio C Hamano 2013-09-12 14:41:00 -07:00
commit c7c377d83f
5 changed files with 83 additions and 24 deletions

View file

@ -119,7 +119,8 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
must_print_delim = 1; must_print_delim = 1;
} }
if (types == TYPE_INT) 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) else if (types == TYPE_BOOL)
vptr = git_config_bool(key_, value_) ? "true" : "false"; vptr = git_config_bool(key_, value_) ? "true" : "false";
else if (types == TYPE_BOOL_OR_INT) { else if (types == TYPE_BOOL_OR_INT) {
@ -268,8 +269,8 @@ static char *normalize_value(const char *key, const char *value)
else { else {
normalized = xmalloc(64); normalized = xmalloc(64);
if (types == TYPE_INT) { if (types == TYPE_INT) {
int v = git_config_int(key, value); int64_t v = git_config_int64(key, value);
sprintf(normalized, "%d", v); sprintf(normalized, "%"PRId64, v);
} }
else if (types == TYPE_BOOL) else if (types == TYPE_BOOL)
sprintf(normalized, "%s", sprintf(normalized, "%s",

View file

@ -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_config_early(config_fn_t fn, void *, const char *repo_config);
extern int git_parse_ulong(const char *, unsigned long *); extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *); 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 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_or_int(const char *, const char *, int *);
extern int git_config_bool(const char *, const char *); extern int git_config_bool(const char *, const char *);

View file

@ -468,7 +468,7 @@ static int parse_unit_factor(const char *end, uintmax_t *val)
return 0; 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) { if (value && *value) {
char *end; char *end;
@ -480,21 +480,25 @@ static int git_parse_long(const char *value, long *ret)
val = strtoimax(value, &end, 0); val = strtoimax(value, &end, 0);
if (errno == ERANGE) if (errno == ERANGE)
return 0; return 0;
if (!parse_unit_factor(end, &factor)) if (!parse_unit_factor(end, &factor)) {
errno = EINVAL;
return 0; return 0;
}
uval = abs(val); uval = abs(val);
uval *= factor; uval *= factor;
if ((uval > maximum_signed_value_of_type(long)) || if (uval > max || abs(val) > uval) {
(abs(val) > uval)) errno = ERANGE;
return 0; return 0;
}
val *= factor; val *= factor;
*ret = val; *ret = val;
return 1; return 1;
} }
errno = EINVAL;
return 0; 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) { if (value && *value) {
char *end; char *end;
@ -506,29 +510,75 @@ int git_parse_ulong(const char *value, unsigned long *ret)
if (errno == ERANGE) if (errno == ERANGE)
return 0; return 0;
oldval = val; oldval = val;
if (!parse_unit_factor(end, &val)) if (!parse_unit_factor(end, &val)) {
errno = EINVAL;
return 0; return 0;
if ((val > maximum_unsigned_value_of_type(long)) || }
(oldval > val)) if (val > max || oldval > val) {
errno = ERANGE;
return 0; return 0;
}
*ret = val; *ret = val;
return 1; return 1;
} }
errno = EINVAL;
return 0; 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) if (cf && cf->name)
die("bad config value for '%s' in %s", name, cf->name); die("bad numeric config value '%s' for '%s' in %s: %s",
die("bad config value for '%s'", name); 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) int git_config_int(const char *name, const char *value)
{ {
long ret = 0; int ret;
if (!git_parse_long(value, &ret)) if (!git_parse_int(value, &ret))
die_bad_config(name); 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; return ret;
} }
@ -536,7 +586,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
{ {
unsigned long ret; unsigned long ret;
if (!git_parse_ulong(value, &ret)) if (!git_parse_ulong(value, &ret))
die_bad_config(name); die_bad_number(name, value);
return ret; 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) 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) if (0 <= v)
return v; return v;
if (git_parse_long(value, &v)) if (git_parse_int(value, &v))
return !!v; return !!v;
return -1; return -1;
} }

View file

@ -652,16 +652,23 @@ test_expect_success numbers '
test_cmp expect actual 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' ' test_expect_success 'invalid unit' '
git config aninvalid.unit "1auto" && git config aninvalid.unit "1auto" &&
echo 1auto >expect && echo 1auto >expect &&
git config aninvalid.unit >actual && git config aninvalid.unit >actual &&
test_cmp expect actual && test_cmp expect actual &&
cat > expect <<-\EOF cat >expect <<-\EOF
fatal: bad config value for '\''aninvalid.unit'\'' in .git/config fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit
EOF EOF
test_must_fail git config --int --get aninvalid.unit 2>actual && test_must_fail git config --int --get aninvalid.unit 2>actual &&
test_cmp actual expect test_i18ncmp expect actual
' '
cat > expect << EOF cat > expect << EOF

View file

@ -73,7 +73,7 @@ test_expect_success 'plumbing not affected' '
test_expect_success 'non-integer config parsing' ' test_expect_success 'non-integer config parsing' '
git config diff.context no && git config diff.context no &&
test_must_fail git diff 2>output && 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' ' test_expect_success 'negative integer config parsing' '