Merge branch 'tr/config-multivalue-lift-max'
authorJunio C Hamano <gitster@pobox.com>
Thu, 12 Dec 2013 22:18:09 +0000 (14:18 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 12 Dec 2013 22:18:09 +0000 (14:18 -0800)
* tr/config-multivalue-lift-max:
config: arbitrary number of matches for --unset and --replace-all

1  2 
config.c
diff --combined config.c
index e1d66a145b756c49c4e4902200c354499532a428,a1e80da7fd3bc7d20ceae1bdd79f66a1a6eedb40..a2c22ab43c912ad1a509c07bd929f34221a0bf99
+++ b/config.c
@@@ -468,7 -468,7 +468,7 @@@ static int parse_unit_factor(const cha
        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;
                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;
                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;
  }
  
@@@ -586,7 -536,7 +586,7 @@@ unsigned long git_config_ulong(const ch
  {
        unsigned long ret;
        if (!git_parse_ulong(value, &ret))
 -              die_bad_config(name);
 +              die_bad_number(name, value);
        return ret;
  }
  
@@@ -609,10 -559,10 +609,10 @@@ static int git_config_maybe_bool_text(c
  
  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;
  }
@@@ -1210,15 -1160,14 +1210,14 @@@ int git_config(config_fn_t fn, void *da
   * Find all the stuff for git_config_set() below.
   */
  
- #define MAX_MATCHES 512
  static struct {
        int baselen;
        char *key;
        int do_not_match;
        regex_t *value_regex;
        int multi_replace;
-       size_t offset[MAX_MATCHES];
+       size_t *offset;
+       unsigned int offset_alloc;
        enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
        int seen;
  } store;
@@@ -1241,11 -1190,11 +1240,11 @@@ static int store_aux(const char *key, c
                if (matches(key, value)) {
                        if (store.seen == 1 && store.multi_replace == 0) {
                                warning("%s has multiple values", key);
-                       } else if (store.seen >= MAX_MATCHES) {
-                               error("too many matches for %s", key);
-                               return 1;
                        }
  
+                       ALLOC_GROW(store.offset, store.seen + 1,
+                                  store.offset_alloc);
                        store.offset[store.seen] = cf->do_ftell(cf);
                        store.seen++;
                }
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
+               ALLOC_GROW(store.offset, store.seen + 1,
+                          store.offset_alloc);
                store.offset[store.seen] = cf->do_ftell(cf);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
+                       ALLOC_GROW(store.offset, store.seen + 1,
+                                  store.offset_alloc);
                        store.offset[store.seen] = cf->do_ftell(cf);
                        store.state = KEY_SEEN;
                        store.seen++;
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
+                                       ALLOC_GROW(store.offset,
+                                                  store.seen + 1,
+                                                  store.offset_alloc);
                                        store.offset[store.seen] = cf->do_ftell(cf);
                        }
                }
@@@ -1583,6 -1539,7 +1589,7 @@@ int git_config_set_multivar_in_file(con
                        }
                }
  
+               ALLOC_GROW(store.offset, 1, store.offset_alloc);
                store.offset[0] = 0;
                store.state = START;
                store.seen = 0;