Merge branch 'rs/maint-config-use-labs' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:17:23 +0000 (12:17 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:17:23 +0000 (12:17 -0800)
* rs/maint-config-use-labs:
use labs() for variables of type long instead of abs()

1  2 
config.c
diff --combined config.c
index d5446d2b631e0bc13e78d12fed2c63b0e29aae0a,dd5e9827882c67f20d880bc3c36a143cb0fdf82c..752e2e227f56edfb7b2ad168798c110c14a2c5e8
+++ b/config.c
@@@ -6,12 -6,9 +6,12 @@@
   *
   */
  #include "cache.h"
 +#include "lockfile.h"
  #include "exec_cmd.h"
  #include "strbuf.h"
  #include "quote.h"
 +#include "hashmap.h"
 +#include "string-list.h"
  
  struct config_source {
        struct config_source *prev;
@@@ -40,13 -37,6 +40,13 @@@ static struct config_source *cf
  
  static int zlib_compression_seen;
  
 +/*
 + * Default config_set that contains key-value pairs from the usual set of config
 + * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
 + * config file and the global /etc/gitconfig)
 + */
 +static struct config_set the_config_set;
 +
  static int config_file_fgetc(struct config_source *conf)
  {
        return fgetc(conf->u.file);
@@@ -137,6 -127,7 +137,6 @@@ static int handle_path_include(const ch
  int git_config_include(const char *var, const char *value, void *data)
  {
        struct config_include_data *inc = data;
 -      const char *type;
        int ret;
  
        /*
        if (ret < 0)
                return ret;
  
 -      if (!skip_prefix(var, "include.", &type))
 -              return ret;
 -
 -      if (!strcmp(type, "path"))
 +      if (!strcmp(var, "include.path"))
                ret = handle_path_include(value, inc);
        return ret;
  }
@@@ -243,7 -237,6 +243,7 @@@ static int get_next_char(void
                cf->linenr++;
        if (c == EOF) {
                cf->eof = 1;
 +              cf->linenr++;
                c = '\n';
        }
        return c;
@@@ -319,7 -312,6 +319,7 @@@ static int get_value(config_fn_t fn, vo
  {
        int c;
        char *value;
 +      int ret;
  
        /* Get the full name */
        for (;;) {
                if (!value)
                        return -1;
        }
 -      return fn(name->buf, value, data);
 +      /*
 +       * We already consumed the \n, but we need linenr to point to
 +       * the line we just parsed during the call to fn to get
 +       * accurate line number in error messages.
 +       */
 +      cf->linenr--;
 +      ret = fn(name->buf, value, data);
 +      cf->linenr++;
 +      return ret;
  }
  
  static int get_extended_base_var(struct strbuf *name, int c)
@@@ -466,9 -450,9 +466,9 @@@ static int git_parse_source(config_fn_
                        break;
        }
        if (cf->die_on_error)
 -              die("bad config file line %d in %s", cf->linenr, cf->name);
 +              die(_("bad config file line %d in %s"), cf->linenr, cf->name);
        else
 -              return error("bad config file line %d in %s", cf->linenr, cf->name);
 +              return error(_("bad config file line %d in %s"), cf->linenr, cf->name);
  }
  
  static int parse_unit_factor(const char *end, uintmax_t *val)
@@@ -506,9 -490,9 +506,9 @@@ static int git_parse_signed(const char 
                        errno = EINVAL;
                        return 0;
                }
-               uval = abs(val);
+               uval = labs(val);
                uval *= factor;
-               if (uval > max || abs(val) > uval) {
+               if (uval > max || labs(val) > uval) {
                        errno = ERANGE;
                        return 0;
                }
@@@ -584,9 -568,9 +584,9 @@@ static void die_bad_number(const char *
                value = "";
  
        if (cf && cf->name)
 -              die("bad numeric config value '%s' for '%s' in %s: %s",
 +              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);
 +      die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
  }
  
  int git_config_int(const char *name, const char *value)
@@@ -671,7 -655,7 +671,7 @@@ int git_config_pathname(const char **de
                return config_error_nonbool(var);
        *dest = expand_user_path(value);
        if (!*dest)
 -              die("Failed to expand user dir in: '%s'", value);
 +              die(_("failed to expand user dir in: '%s'"), value);
        return 0;
  }
  
@@@ -749,7 -733,7 +749,7 @@@ static int git_default_core_config(cons
                if (level == -1)
                        level = Z_DEFAULT_COMPRESSION;
                else if (level < 0 || level > Z_BEST_COMPRESSION)
 -                      die("bad zlib compression level %d", level);
 +                      die(_("bad zlib compression level %d"), level);
                zlib_compression_level = level;
                zlib_compression_seen = 1;
                return 0;
                if (level == -1)
                        level = Z_DEFAULT_COMPRESSION;
                else if (level < 0 || level > Z_BEST_COMPRESSION)
 -                      die("bad zlib compression level %d", level);
 +                      die(_("bad zlib compression level %d"), level);
                core_compression_level = level;
                core_compression_seen = 1;
                if (!zlib_compression_seen)
                else if (!strcmp(value, "link"))
                        object_creation_mode = OBJECT_CREATION_USES_HARDLINKS;
                else
 -                      die("Invalid mode for object creation: %s", value);
 +                      die(_("invalid mode for object creation: %s"), value);
                return 0;
        }
  
                return 0;
        }
  
 +      if (!strcmp(var, "core.protecthfs")) {
 +              protect_hfs = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "core.protectntfs")) {
 +              protect_ntfs = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
@@@ -1150,28 -1124,12 +1150,28 @@@ const char *git_etc_gitconfig(void
        return system_wide;
  }
  
 +/*
 + * Parse environment variable 'k' as a boolean (in various
 + * possible spellings); if missing, use the default value 'def'.
 + */
  int git_env_bool(const char *k, int def)
  {
        const char *v = getenv(k);
        return v ? git_config_bool(k, v) : def;
  }
  
 +/*
 + * Parse environment variable 'k' as ulong with possibly a unit
 + * suffix; if missing, use the default value 'val'.
 + */
 +unsigned long git_env_ulong(const char *k, unsigned long val)
 +{
 +      const char *v = getenv(k);
 +      if (v && !git_parse_ulong(v, &val))
 +              die("failed to parse %s", k);
 +      return val;
 +}
 +
  int git_config_system(void)
  {
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
@@@ -1208,7 -1166,7 +1208,7 @@@ int git_config_early(config_fn_t fn, vo
  
        switch (git_config_from_parameters(fn, data)) {
        case -1: /* error */
 -              die("unable to parse command-line config");
 +              die(_("unable to parse command-line config"));
                break;
        case 0: /* found nothing */
                break;
@@@ -1255,365 -1213,9 +1255,365 @@@ int git_config_with_options(config_fn_
        return ret;
  }
  
 -int git_config(config_fn_t fn, void *data)
 +static void git_config_raw(config_fn_t fn, void *data)
 +{
 +      if (git_config_with_options(fn, data, NULL, 1) < 0)
 +              /*
 +               * git_config_with_options() normally returns only
 +               * positive values, as most errors are fatal, and
 +               * non-fatal potential errors are guarded by "if"
 +               * statements that are entered only when no error is
 +               * possible.
 +               *
 +               * If we ever encounter a non-fatal error, it means
 +               * something went really wrong and we should stop
 +               * immediately.
 +               */
 +              die(_("unknown error occured while reading the configuration files"));
 +}
 +
 +static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
 +{
 +      int i, value_index;
 +      struct string_list *values;
 +      struct config_set_element *entry;
 +      struct configset_list *list = &cs->list;
 +      struct key_value_info *kv_info;
 +
 +      for (i = 0; i < list->nr; i++) {
 +              entry = list->items[i].e;
 +              value_index = list->items[i].value_index;
 +              values = &entry->value_list;
 +              if (fn(entry->key, values->items[value_index].string, data) < 0) {
 +                      kv_info = values->items[value_index].util;
 +                      git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr);
 +              }
 +      }
 +}
 +
 +static void git_config_check_init(void);
 +
 +void git_config(config_fn_t fn, void *data)
 +{
 +      git_config_check_init();
 +      configset_iter(&the_config_set, fn, data);
 +}
 +
 +static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
  {
 -      return git_config_with_options(fn, data, NULL, 1);
 +      struct config_set_element k;
 +      struct config_set_element *found_entry;
 +      char *normalized_key;
 +      int ret;
 +      /*
 +       * `key` may come from the user, so normalize it before using it
 +       * for querying entries from the hashmap.
 +       */
 +      ret = git_config_parse_key(key, &normalized_key, NULL);
 +
 +      if (ret)
 +              return NULL;
 +
 +      hashmap_entry_init(&k, strhash(normalized_key));
 +      k.key = normalized_key;
 +      found_entry = hashmap_get(&cs->config_hash, &k, NULL);
 +      free(normalized_key);
 +      return found_entry;
 +}
 +
 +static int configset_add_value(struct config_set *cs, const char *key, const char *value)
 +{
 +      struct config_set_element *e;
 +      struct string_list_item *si;
 +      struct configset_list_item *l_item;
 +      struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
 +
 +      e = configset_find_element(cs, key);
 +      /*
 +       * Since the keys are being fed by git_config*() callback mechanism, they
 +       * are already normalized. So simply add them without any further munging.
 +       */
 +      if (!e) {
 +              e = xmalloc(sizeof(*e));
 +              hashmap_entry_init(e, strhash(key));
 +              e->key = xstrdup(key);
 +              string_list_init(&e->value_list, 1);
 +              hashmap_add(&cs->config_hash, e);
 +      }
 +      si = string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL);
 +
 +      ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
 +      l_item = &cs->list.items[cs->list.nr++];
 +      l_item->e = e;
 +      l_item->value_index = e->value_list.nr - 1;
 +
 +      if (cf) {
 +              kv_info->filename = strintern(cf->name);
 +              kv_info->linenr = cf->linenr;
 +      } else {
 +              /* for values read from `git_config_from_parameters()` */
 +              kv_info->filename = NULL;
 +              kv_info->linenr = -1;
 +      }
 +      si->util = kv_info;
 +
 +      return 0;
 +}
 +
 +static int config_set_element_cmp(const struct config_set_element *e1,
 +                               const struct config_set_element *e2, const void *unused)
 +{
 +      return strcmp(e1->key, e2->key);
 +}
 +
 +void git_configset_init(struct config_set *cs)
 +{
 +      hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0);
 +      cs->hash_initialized = 1;
 +      cs->list.nr = 0;
 +      cs->list.alloc = 0;
 +      cs->list.items = NULL;
 +}
 +
 +void git_configset_clear(struct config_set *cs)
 +{
 +      struct config_set_element *entry;
 +      struct hashmap_iter iter;
 +      if (!cs->hash_initialized)
 +              return;
 +
 +      hashmap_iter_init(&cs->config_hash, &iter);
 +      while ((entry = hashmap_iter_next(&iter))) {
 +              free(entry->key);
 +              string_list_clear(&entry->value_list, 1);
 +      }
 +      hashmap_free(&cs->config_hash, 1);
 +      cs->hash_initialized = 0;
 +      free(cs->list.items);
 +      cs->list.nr = 0;
 +      cs->list.alloc = 0;
 +      cs->list.items = NULL;
 +}
 +
 +static int config_set_callback(const char *key, const char *value, void *cb)
 +{
 +      struct config_set *cs = cb;
 +      configset_add_value(cs, key, value);
 +      return 0;
 +}
 +
 +int git_configset_add_file(struct config_set *cs, const char *filename)
 +{
 +      return git_config_from_file(config_set_callback, filename, cs);
 +}
 +
 +int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
 +{
 +      const struct string_list *values = NULL;
 +      /*
 +       * Follows "last one wins" semantic, i.e., if there are multiple matches for the
 +       * queried key in the files of the configset, the value returned will be the last
 +       * value in the value list for that key.
 +       */
 +      values = git_configset_get_value_multi(cs, key);
 +
 +      if (!values)
 +              return 1;
 +      assert(values->nr > 0);
 +      *value = values->items[values->nr - 1].string;
 +      return 0;
 +}
 +
 +const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
 +{
 +      struct config_set_element *e = configset_find_element(cs, key);
 +      return e ? &e->value_list : NULL;
 +}
 +
 +int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest)
 +{
 +      const char *value;
 +      if (!git_configset_get_value(cs, key, &value))
 +              return git_config_string(dest, key, value);
 +      else
 +              return 1;
 +}
 +
 +int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
 +{
 +      return git_configset_get_string_const(cs, key, (const char **)dest);
 +}
 +
 +int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
 +{
 +      const char *value;
 +      if (!git_configset_get_value(cs, key, &value)) {
 +              *dest = git_config_int(key, value);
 +              return 0;
 +      } else
 +              return 1;
 +}
 +
 +int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
 +{
 +      const char *value;
 +      if (!git_configset_get_value(cs, key, &value)) {
 +              *dest = git_config_ulong(key, value);
 +              return 0;
 +      } else
 +              return 1;
 +}
 +
 +int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
 +{
 +      const char *value;
 +      if (!git_configset_get_value(cs, key, &value)) {
 +              *dest = git_config_bool(key, value);
 +              return 0;
 +      } else
 +              return 1;
 +}
 +
 +int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
 +                              int *is_bool, int *dest)
 +{
 +      const char *value;
 +      if (!git_configset_get_value(cs, key, &value)) {
 +              *dest = git_config_bool_or_int(key, value, is_bool);
 +              return 0;
 +      } else
 +              return 1;
 +}
 +
 +int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
 +{
 +      const char *value;
 +      if (!git_configset_get_value(cs, key, &value)) {
 +              *dest = git_config_maybe_bool(key, value);
 +              if (*dest == -1)
 +                      return -1;
 +              return 0;
 +      } else
 +              return 1;
 +}
 +
 +int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
 +{
 +      const char *value;
 +      if (!git_configset_get_value(cs, key, &value))
 +              return git_config_pathname(dest, key, value);
 +      else
 +              return 1;
 +}
 +
 +static void git_config_check_init(void)
 +{
 +      if (the_config_set.hash_initialized)
 +              return;
 +      git_configset_init(&the_config_set);
 +      git_config_raw(config_set_callback, &the_config_set);
 +}
 +
 +void git_config_clear(void)
 +{
 +      if (!the_config_set.hash_initialized)
 +              return;
 +      git_configset_clear(&the_config_set);
 +}
 +
 +int git_config_get_value(const char *key, const char **value)
 +{
 +      git_config_check_init();
 +      return git_configset_get_value(&the_config_set, key, value);
 +}
 +
 +const struct string_list *git_config_get_value_multi(const char *key)
 +{
 +      git_config_check_init();
 +      return git_configset_get_value_multi(&the_config_set, key);
 +}
 +
 +int git_config_get_string_const(const char *key, const char **dest)
 +{
 +      int ret;
 +      git_config_check_init();
 +      ret = git_configset_get_string_const(&the_config_set, key, dest);
 +      if (ret < 0)
 +              git_die_config(key, NULL);
 +      return ret;
 +}
 +
 +int git_config_get_string(const char *key, char **dest)
 +{
 +      git_config_check_init();
 +      return git_config_get_string_const(key, (const char **)dest);
 +}
 +
 +int git_config_get_int(const char *key, int *dest)
 +{
 +      git_config_check_init();
 +      return git_configset_get_int(&the_config_set, key, dest);
 +}
 +
 +int git_config_get_ulong(const char *key, unsigned long *dest)
 +{
 +      git_config_check_init();
 +      return git_configset_get_ulong(&the_config_set, key, dest);
 +}
 +
 +int git_config_get_bool(const char *key, int *dest)
 +{
 +      git_config_check_init();
 +      return git_configset_get_bool(&the_config_set, key, dest);
 +}
 +
 +int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)
 +{
 +      git_config_check_init();
 +      return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest);
 +}
 +
 +int git_config_get_maybe_bool(const char *key, int *dest)
 +{
 +      git_config_check_init();
 +      return git_configset_get_maybe_bool(&the_config_set, key, dest);
 +}
 +
 +int git_config_get_pathname(const char *key, const char **dest)
 +{
 +      int ret;
 +      git_config_check_init();
 +      ret = git_configset_get_pathname(&the_config_set, key, dest);
 +      if (ret < 0)
 +              git_die_config(key, NULL);
 +      return ret;
 +}
 +
 +NORETURN
 +void git_die_config_linenr(const char *key, const char *filename, int linenr)
 +{
 +      if (!filename)
 +              die(_("unable to parse '%s' from command-line config"), key);
 +      else
 +              die(_("bad config variable '%s' in file '%s' at line %d"),
 +                  key, filename, linenr);
 +}
 +
 +NORETURN __attribute__((format(printf, 2, 3)))
 +void git_die_config(const char *key, const char *err, ...)
 +{
 +      const struct string_list *values;
 +      struct key_value_info *kv_info;
 +
 +      if (err) {
 +              va_list params;
 +              va_start(params, err);
 +              vreportf("error: ", err, params);
 +              va_end(params);
 +      }
 +      values = git_config_get_value_multi(key);
 +      kv_info = values->items[values->nr - 1].util;
 +      git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
  }
  
  /*
@@@ -1654,7 -1256,7 +1654,7 @@@ static int store_aux(const char *key, c
        case KEY_SEEN:
                if (matches(key, value)) {
                        if (store.seen == 1 && store.multi_replace == 0) {
 -                              warning("%s has multiple values", key);
 +                              warning(_("%s has multiple values"), key);
                        }
  
                        ALLOC_GROW(store.offset, store.seen + 1,
@@@ -2051,9 -1653,9 +2051,9 @@@ int git_config_set_multivar_in_file(con
                        MAP_PRIVATE, in_fd, 0);
                close(in_fd);
  
 -              if (chmod(lock->filename, st.st_mode & 07777) < 0) {
 +              if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
                        error("chmod on %s failed: %s",
 -                              lock->filename, strerror(errno));
 +                              lock->filename.buf, strerror(errno));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
        if (commit_lock_file(lock) < 0) {
                error("could not commit config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
 +              lock = NULL;
                goto out_free;
        }
  
        lock = NULL;
        ret = 0;
  
 +      /* Invalidate the config cache */
 +      git_config_clear();
 +
  out_free:
        if (lock)
                rollback_lock_file(lock);
        return ret;
  
  write_err_out:
 -      ret = write_error(lock->filename);
 +      ret = write_error(lock->filename.buf);
        goto out_free;
  
  }
@@@ -2234,9 -1832,9 +2234,9 @@@ int git_config_rename_section_in_file(c
  
        fstat(fileno(config_file), &st);
  
 -      if (chmod(lock->filename, st.st_mode & 07777) < 0) {
 +      if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
                ret = error("chmod on %s failed: %s",
 -                              lock->filename, strerror(errno));
 +                              lock->filename.buf, strerror(errno));
                goto out;
        }
  
                                }
                                store.baselen = strlen(new_name);
                                if (!store_write_section(out_fd, new_name)) {
 -                                      ret = write_error(lock->filename);
 +                                      ret = write_error(lock->filename.buf);
                                        goto out;
                                }
                                /*
                        continue;
                length = strlen(output);
                if (write_in_full(out_fd, output, length) != length) {
 -                      ret = write_error(lock->filename);
 +                      ret = write_error(lock->filename.buf);
                        goto out;
                }
        }