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

1  2 
builtin/config.c
cache.h
config.c
t/t1300-repo-config.sh
diff --combined builtin/config.c
index fc8d8820cbd2c0fcddb69aeb3fa258cf87e96814,8b182d2a9f281605fa0e53c1e39972020e64b557..20e89fe4e0c7db50267e8e9358c38faea2259fab
@@@ -2,7 -2,6 +2,7 @@@
  #include "cache.h"
  #include "color.h"
  #include "parse-options.h"
 +#include "urlmatch.h"
  
  static const char *const builtin_config_usage[] = {
        N_("git config [options]"),
@@@ -43,7 -42,6 +43,7 @@@ static int respect_includes = -1
  #define ACTION_SET_ALL (1<<12)
  #define ACTION_GET_COLOR (1<<13)
  #define ACTION_GET_COLORBOOL (1<<14)
 +#define ACTION_GET_URLMATCH (1<<15)
  
  #define TYPE_BOOL (1<<0)
  #define TYPE_INT (1<<1)
  
  static struct option builtin_config_options[] = {
        OPT_GROUP(N_("Config file location")),
 -      OPT_BOOLEAN(0, "global", &use_global_config, N_("use global config file")),
 -      OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
 -      OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
 +      OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
 +      OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
 +      OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
        OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
        OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
        OPT_GROUP(N_("Action")),
        OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
        OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
        OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
 +      OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
        OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
        OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
        OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
@@@ -78,7 -75,7 +78,7 @@@
        OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
        OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
        OPT_GROUP(N_("Other")),
 -      OPT_BOOLEAN('z', "null", &end_null, N_("terminate values with NUL byte")),
 +      OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
        OPT_END(),
  };
@@@ -105,13 -102,25 +105,13 @@@ struct strbuf_list 
        int alloc;
  };
  
 -static int collect_config(const char *key_, const char *value_, void *cb)
 +static int format_config(struct strbuf *buf, const char *key_, const char *value_)
  {
 -      struct strbuf_list *values = cb;
 -      struct strbuf *buf;
 -      char value[256];
 -      const char *vptr = value;
        int must_free_vptr = 0;
        int must_print_delim = 0;
 +      char value[256];
 +      const char *vptr = value;
  
 -      if (!use_key_regexp && strcmp(key_, key))
 -              return 0;
 -      if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
 -              return 0;
 -      if (regexp != NULL &&
 -          (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
 -              return 0;
 -
 -      ALLOC_GROW(values->items, values->nr + 1, values->alloc);
 -      buf = &values->items[values->nr++];
        strbuf_init(buf, 0);
  
        if (show_keys) {
                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) {
        strbuf_addch(buf, term);
  
        if (must_free_vptr)
 -              /* If vptr must be freed, it's a pointer to a
 -               * dynamically allocated buffer, it's safe to cast to
 -               * const.
 -              */
                free((char *)vptr);
 -
        return 0;
  }
  
 +static int collect_config(const char *key_, const char *value_, void *cb)
 +{
 +      struct strbuf_list *values = cb;
 +
 +      if (!use_key_regexp && strcmp(key_, key))
 +              return 0;
 +      if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
 +              return 0;
 +      if (regexp != NULL &&
 +          (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
 +              return 0;
 +
 +      ALLOC_GROW(values->items, values->nr + 1, values->alloc);
 +
 +      return format_config(&values->items[values->nr++], key_, value_);
 +}
 +
  static int get_value(const char *key_, const char *regex_)
  {
        int ret = CONFIG_GENERIC_ERROR;
@@@ -268,8 -266,8 +269,8 @@@ static char *normalize_value(const cha
        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",
@@@ -367,97 -365,6 +368,97 @@@ static void check_blob_write(void
                die("writing config blobs is not supported");
  }
  
 +struct urlmatch_current_candidate_value {
 +      char value_is_null;
 +      struct strbuf value;
 +};
 +
 +static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
 +{
 +      struct string_list *values = cb;
 +      struct string_list_item *item = string_list_insert(values, var);
 +      struct urlmatch_current_candidate_value *matched = item->util;
 +
 +      if (!matched) {
 +              matched = xmalloc(sizeof(*matched));
 +              strbuf_init(&matched->value, 0);
 +              item->util = matched;
 +      } else {
 +              strbuf_reset(&matched->value);
 +      }
 +
 +      if (value) {
 +              strbuf_addstr(&matched->value, value);
 +              matched->value_is_null = 0;
 +      } else {
 +              matched->value_is_null = 1;
 +      }
 +      return 0;
 +}
 +
 +static char *dup_downcase(const char *string)
 +{
 +      char *result;
 +      size_t len, i;
 +
 +      len = strlen(string);
 +      result = xmalloc(len + 1);
 +      for (i = 0; i < len; i++)
 +              result[i] = tolower(string[i]);
 +      result[i] = '\0';
 +      return result;
 +}
 +
 +static int get_urlmatch(const char *var, const char *url)
 +{
 +      char *section_tail;
 +      struct string_list_item *item;
 +      struct urlmatch_config config = { STRING_LIST_INIT_DUP };
 +      struct string_list values = STRING_LIST_INIT_DUP;
 +
 +      config.collect_fn = urlmatch_collect_fn;
 +      config.cascade_fn = NULL;
 +      config.cb = &values;
 +
 +      if (!url_normalize(url, &config.url))
 +              die("%s", config.url.err);
 +
 +      config.section = dup_downcase(var);
 +      section_tail = strchr(config.section, '.');
 +      if (section_tail) {
 +              *section_tail = '\0';
 +              config.key = section_tail + 1;
 +              show_keys = 0;
 +      } else {
 +              config.key = NULL;
 +              show_keys = 1;
 +      }
 +
 +      git_config_with_options(urlmatch_config_entry, &config,
 +                              given_config_file, NULL, respect_includes);
 +
 +      for_each_string_list_item(item, &values) {
 +              struct urlmatch_current_candidate_value *matched = item->util;
 +              struct strbuf key = STRBUF_INIT;
 +              struct strbuf buf = STRBUF_INIT;
 +
 +              strbuf_addstr(&key, item->string);
 +              format_config(&buf, key.buf,
 +                            matched->value_is_null ? NULL : matched->value.buf);
 +              fwrite(buf.buf, 1, buf.len, stdout);
 +              strbuf_release(&key);
 +              strbuf_release(&buf);
 +
 +              strbuf_release(&matched->value);
 +      }
 +      string_list_clear(&config.vars, 1);
 +      string_list_clear(&values, 1);
 +      free(config.url.url);
 +
 +      free((void *)config.section);
 +      return 0;
 +}
 +
  int cmd_config(int argc, const char **argv, const char *prefix)
  {
        int nongit = !startup_info->have_repository;
                check_argc(argc, 1, 2);
                return get_value(argv[0], argv[1]);
        }
 +      else if (actions == ACTION_GET_URLMATCH) {
 +              check_argc(argc, 2, 2);
 +              return get_urlmatch(argv[0], argv[1]);
 +      }
        else if (actions == ACTION_UNSET) {
                check_blob_write();
                check_argc(argc, 1, 2);
diff --combined cache.h
index 9ef778a5a9ab1e88e595ea3b8fef1656a783093b,ac4525aabb72eb3ad40baaf900b0353defaa30b8..a47b9c03036d41e8067a843dda305bc9b6161255
+++ b/cache.h
@@@ -101,9 -101,9 +101,9 @@@ unsigned long git_deflate_bound(git_zst
  
  #define CACHE_SIGNATURE 0x44495243    /* "DIRC" */
  struct cache_header {
 -      unsigned int hdr_signature;
 -      unsigned int hdr_version;
 -      unsigned int hdr_entries;
 +      uint32_t hdr_signature;
 +      uint32_t hdr_version;
 +      uint32_t hdr_entries;
  };
  
  #define INDEX_FORMAT_LB 2
   * check it for equality in the 32 bits we save.
   */
  struct cache_time {
 -      unsigned int sec;
 -      unsigned int nsec;
 +      uint32_t sec;
 +      uint32_t nsec;
  };
  
  struct stat_data {
@@@ -189,8 -189,6 +189,8 @@@ struct cache_entry 
  #error "CE_EXTENDED_FLAGS out of range"
  #endif
  
 +struct pathspec;
 +
  /*
   * Copy the sha1 and stat state of a cache entry from one to
   * another. But we never change the name, or the hash state!
@@@ -367,9 -365,6 +367,9 @@@ static inline enum object_type object_t
  #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
  #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
  #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
 +#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
 +#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
 +#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
  
  /*
   * This environment variable is expected to contain a boolean indicating
@@@ -417,7 -412,6 +417,7 @@@ extern void setup_work_tree(void)
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
  extern char *prefix_path(const char *prefix, int len, const char *path);
 +extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix,
@@@ -455,7 -449,7 +455,7 @@@ extern void sanitize_stdfds(void)
  
  /* Initialize and use the cache information */
  extern int read_index(struct index_state *);
 -extern int read_index_preload(struct index_state *, const char **pathspec);
 +extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
@@@ -497,8 -491,28 +497,8 @@@ extern void *read_blob_data_from_index(
  extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
 -#define PATHSPEC_ONESTAR 1    /* the pathspec pattern satisfies GFNM_ONESTAR */
 -
 -struct pathspec {
 -      const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
 -      int nr;
 -      unsigned int has_wildcard:1;
 -      unsigned int recursive:1;
 -      int max_depth;
 -      struct pathspec_item {
 -              const char *match;
 -              int len;
 -              int nowildcard_len;
 -              int flags;
 -      } *items;
 -};
 -
 -extern int init_pathspec(struct pathspec *, const char **);
 -extern void free_pathspec(struct pathspec *);
  extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
  
 -extern int limit_pathspec_to_literal(void);
 -
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@@ -526,7 -540,7 +526,7 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
 +extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  
  struct lock_file {
        struct lock_file *next;
@@@ -748,7 -762,6 +748,7 @@@ const char *real_path(const char *path)
  const char *real_path_if_valid(const char *path);
  const char *absolute_path(const char *path);
  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
  int normalize_path_copy(char *dst, const char *src);
  int longest_ancestor_length(const char *path, struct string_list *prefixes);
  char *strip_path_suffix(const char *path, const char *suffix);
@@@ -1025,6 -1038,68 +1025,6 @@@ struct pack_entry 
        struct packed_git *p;
  };
  
 -struct ref {
 -      struct ref *next;
 -      unsigned char old_sha1[20];
 -      unsigned char new_sha1[20];
 -      char *symref;
 -      unsigned int
 -              force:1,
 -              forced_update:1,
 -              deletion:1,
 -              matched:1;
 -
 -      /*
 -       * Order is important here, as we write to FETCH_HEAD
 -       * in numeric order. And the default NOT_FOR_MERGE
 -       * should be 0, so that xcalloc'd structures get it
 -       * by default.
 -       */
 -      enum {
 -              FETCH_HEAD_MERGE = -1,
 -              FETCH_HEAD_NOT_FOR_MERGE = 0,
 -              FETCH_HEAD_IGNORE = 1
 -      } fetch_head_status;
 -
 -      enum {
 -              REF_STATUS_NONE = 0,
 -              REF_STATUS_OK,
 -              REF_STATUS_REJECT_NONFASTFORWARD,
 -              REF_STATUS_REJECT_ALREADY_EXISTS,
 -              REF_STATUS_REJECT_NODELETE,
 -              REF_STATUS_REJECT_FETCH_FIRST,
 -              REF_STATUS_REJECT_NEEDS_FORCE,
 -              REF_STATUS_UPTODATE,
 -              REF_STATUS_REMOTE_REJECT,
 -              REF_STATUS_EXPECTING_REPORT
 -      } status;
 -      char *remote_status;
 -      struct ref *peer_ref; /* when renaming */
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -#define REF_NORMAL    (1u << 0)
 -#define REF_HEADS     (1u << 1)
 -#define REF_TAGS      (1u << 2)
 -
 -extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
 -
 -#define CONNECT_VERBOSE       (1u << 0)
 -extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 -extern int finish_connect(struct child_process *conn);
 -extern int git_connection_is_socket(struct child_process *conn);
 -struct extra_have_objects {
 -      int nr, alloc;
 -      unsigned char (*array)[20];
 -};
 -extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 -                                   struct ref **list, unsigned int flags,
 -                                   struct extra_have_objects *);
 -extern int server_supports(const char *feature);
 -extern int parse_feature_request(const char *features, const char *feature);
 -extern const char *server_feature_value(const char *feature, int *len_ret);
 -extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
 -
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
  /* A hook for count-objects to report invalid files in pack directory */
@@@ -1115,6 -1190,7 +1115,7 @@@ extern int git_config_with_options(conf
  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 *);
@@@ -1230,7 -1306,7 +1231,7 @@@ void packet_trace_identity(const char *
   * return 0 if success, 1 - if addition of a file failed and
   * ADD_FILES_IGNORE_ERRORS was specified in flags
   */
 -int add_files_to_cache(const char *prefix, const char **pathspec, int flags);
 +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
@@@ -1264,7 -1340,7 +1265,7 @@@ extern int ws_blank_line(const char *li
  #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
 -int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
 +int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
  char *alias_lookup(const char *alias);
diff --combined config.c
index 9f9bf0cc9a1405e938fddbedc44468782ad61021,3ffe134c57f6076aeb13761ea2e741cb1df40dd1..6588cf579f2e44533192027c739b4823562b2275
+++ b/config.c
@@@ -27,9 -27,9 +27,9 @@@ struct config_source 
        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;
@@@ -217,13 -217,13 +217,13 @@@ int git_config_from_parameters(config_f
  
  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';
                }
        }
@@@ -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)
+ 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;
  }
  
@@@ -536,7 -586,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;
  }
  
@@@ -559,10 -609,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;
  }
@@@ -992,9 -1042,9 +1042,9 @@@ int git_config_from_file(config_fn_t fn
                top.u.file = f;
                top.name = filename;
                top.die_on_error = 1;
 -              top.fgetc = config_file_fgetc;
 -              top.ungetc = config_file_ungetc;
 -              top.ftell = config_file_ftell;
 +              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);
  
@@@ -1013,9 -1063,9 +1063,9 @@@ int git_config_from_buf(config_fn_t fn
        top.u.buf.pos = 0;
        top.name = name;
        top.die_on_error = 0;
 -      top.fgetc = config_buf_fgetc;
 -      top.ungetc = config_buf_ungetc;
 -      top.ftell = config_buf_ftell;
 +      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);
  }
@@@ -1196,7 -1246,7 +1246,7 @@@ static int store_aux(const char *key, c
                                return 1;
                        }
  
 -                      store.offset[store.seen] = cf->ftell(cf);
 +                      store.offset[store.seen] = cf->do_ftell(cf);
                        store.seen++;
                }
                break;
                 * 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);
                        }
                }
        }
diff --combined t/t1300-repo-config.sh
index c23f4781e102425d3bcc1de69cf48fc79e15509b,b66c63262162e54377f975ac153d448ae05557d1..967359344dab8118dfd55816ef08f4809a9f33ad
@@@ -652,16 -652,23 +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
@@@ -1087,31 -1094,6 +1094,31 @@@ test_expect_success 'barf on incomplet
        grep " line 3 " error
  '
  
 +test_expect_success 'urlmatch' '
 +      cat >.git/config <<-\EOF &&
 +      [http]
 +              sslVerify
 +      [http "https://weak.example.com"]
 +              sslVerify = false
 +              cookieFile = /tmp/cookie.txt
 +      EOF
 +
 +      echo true >expect &&
 +      git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
 +      test_cmp expect actual &&
 +
 +      echo false >expect &&
 +      git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
 +      test_cmp expect actual &&
 +
 +      {
 +              echo http.cookiefile /tmp/cookie.txt &&
 +              echo http.sslverify false
 +      } >expect &&
 +      git config --get-urlmatch HTTP https://weak.example.com >actual &&
 +      test_cmp expect actual
 +'
 +
  # good section hygiene
  test_expect_failure 'unsetting the last key in a section removes header' '
        cat >.git/config <<-\EOF &&