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;
}
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",

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_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 *);

View file

@ -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;
}

View file

@ -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

View file

@ -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' '