Code

Merge branch 'ef/maint-strbuf-init'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Apr 2011 18:36:43 +0000 (11:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Apr 2011 18:36:43 +0000 (11:36 -0700)
* ef/maint-strbuf-init:
  config: support values longer than 1023 bytes
  strbuf: make sure buffer is zero-terminated

1  2 
config.c
strbuf.c

diff --combined config.c
index d06fb19d511c29e92aa840c664618ca4a6f73fe6,5a1db4ff0b975445ded2cb9365ad1bb681025548..5f9ec2894570d23f8b91327374a4d82dd46cbca3
+++ b/config.c
@@@ -7,8 -7,6 +7,8 @@@
   */
  #include "cache.h"
  #include "exec_cmd.h"
 +#include "strbuf.h"
 +#include "quote.h"
  
  #define MAXNAME (256)
  
@@@ -20,91 -18,6 +20,91 @@@ static int zlib_compression_seen
  
  const char *config_exclusive_filename = NULL;
  
 +struct config_item {
 +      struct config_item *next;
 +      char *name;
 +      char *value;
 +};
 +static struct config_item *config_parameters;
 +static struct config_item **config_parameters_tail = &config_parameters;
 +
 +static void lowercase(char *p)
 +{
 +      for (; *p; p++)
 +              *p = tolower(*p);
 +}
 +
 +void git_config_push_parameter(const char *text)
 +{
 +      struct strbuf env = STRBUF_INIT;
 +      const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
 +      if (old) {
 +              strbuf_addstr(&env, old);
 +              strbuf_addch(&env, ' ');
 +      }
 +      sq_quote_buf(&env, text);
 +      setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1);
 +      strbuf_release(&env);
 +}
 +
 +int git_config_parse_parameter(const char *text)
 +{
 +      struct config_item *ct;
 +      struct strbuf tmp = STRBUF_INIT;
 +      struct strbuf **pair;
 +      strbuf_addstr(&tmp, text);
 +      pair = strbuf_split(&tmp, '=');
 +      if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
 +              strbuf_setlen(pair[0], pair[0]->len - 1);
 +      strbuf_trim(pair[0]);
 +      if (!pair[0]->len) {
 +              strbuf_list_free(pair);
 +              return -1;
 +      }
 +      ct = xcalloc(1, sizeof(struct config_item));
 +      ct->name = strbuf_detach(pair[0], NULL);
 +      if (pair[1]) {
 +              strbuf_trim(pair[1]);
 +              ct->value = strbuf_detach(pair[1], NULL);
 +      }
 +      strbuf_list_free(pair);
 +      lowercase(ct->name);
 +      *config_parameters_tail = ct;
 +      config_parameters_tail = &ct->next;
 +      return 0;
 +}
 +
 +int git_config_parse_environment(void) {
 +      const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
 +      char *envw;
 +      const char **argv = NULL;
 +      int nr = 0, alloc = 0;
 +      int i;
 +
 +      if (!env)
 +              return 0;
 +      /* sq_dequote will write over it */
 +      envw = xstrdup(env);
 +
 +      if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
 +              free(envw);
 +              return error("bogus format in " CONFIG_DATA_ENVIRONMENT);
 +      }
 +
 +      for (i = 0; i < nr; i++) {
 +              if (git_config_parse_parameter(argv[i]) < 0) {
 +                      error("bogus config parameter: %s", argv[i]);
 +                      free(argv);
 +                      free(envw);
 +                      return -1;
 +              }
 +      }
 +
 +      free(argv);
 +      free(envw);
 +      return 0;
 +}
 +
  static int get_next_char(void)
  {
        int c;
  
  static char *parse_value(void)
  {
-       static char value[1024];
-       int quote = 0, comment = 0, len = 0, space = 0;
+       static struct strbuf value = STRBUF_INIT;
+       int quote = 0, comment = 0, space = 0;
  
+       strbuf_reset(&value);
        for (;;) {
                int c = get_next_char();
-               if (len >= sizeof(value) - 1)
-                       return NULL;
                if (c == '\n') {
                        if (quote)
                                return NULL;
-                       value[len] = 0;
-                       return value;
+                       return value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       if (len)
+                       if (value.len)
                                space++;
                        continue;
                }
                        }
                }
                for (; space; space--)
-                       value[len++] = ' ';
+                       strbuf_addch(&value, ' ');
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
                        default:
                                return NULL;
                        }
-                       value[len++] = c;
+                       strbuf_addch(&value, c);
                        continue;
                }
                if (c == '"') {
                        quote = 1-quote;
                        continue;
                }
-               value[len++] = c;
+               strbuf_addch(&value, c);
        }
  }
  
@@@ -409,40 -320,17 +407,40 @@@ unsigned long git_config_ulong(const ch
        return ret;
  }
  
 -int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 +static int git_config_maybe_bool_text(const char *name, const char *value)
  {
 -      *is_bool = 1;
        if (!value)
                return 1;
        if (!*value)
                return 0;
 -      if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
 +      if (!strcasecmp(value, "true")
 +          || !strcasecmp(value, "yes")
 +          || !strcasecmp(value, "on"))
                return 1;
 -      if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
 +      if (!strcasecmp(value, "false")
 +          || !strcasecmp(value, "no")
 +          || !strcasecmp(value, "off"))
                return 0;
 +      return -1;
 +}
 +
 +int git_config_maybe_bool(const char *name, const char *value)
 +{
 +      long v = git_config_maybe_bool_text(name, value);
 +      if (0 <= v)
 +              return v;
 +      if (git_parse_long(value, &v))
 +              return !!v;
 +      return -1;
 +}
 +
 +int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 +{
 +      int v = git_config_maybe_bool_text(name, value);
 +      if (0 <= v) {
 +              *is_bool = 1;
 +              return v;
 +      }
        *is_bool = 0;
        return git_config_int(name, value);
  }
@@@ -523,14 -411,6 +521,14 @@@ static int git_default_core_config(cons
                return 0;
        }
  
 +      if (!strcmp(var, "core.abbrev")) {
 +              int abbrev = git_config_int(var, value);
 +              if (abbrev < minimum_abbrev || abbrev > 40)
 +                      return -1;
 +              default_abbrev = abbrev;
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.loosecompression")) {
                int level = git_config_int(var, value);
                if (level == -1)
                return 0;
        }
  
 +      if (!strcmp(var, "core.bigfilethreshold")) {
 +              long n = git_config_int(var, value);
 +              big_file_threshold = 0 < n ? n : 0;
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.packedgitlimit")) {
                packed_git_limit = git_config_int(var, value);
                return 0;
  
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
 -                      auto_crlf = -1;
 +                      if (eol == EOL_CRLF)
 +                              return error("core.autocrlf=input conflicts with core.eol=crlf");
 +                      auto_crlf = AUTO_CRLF_INPUT;
                        return 0;
                }
                auto_crlf = git_config_bool(var, value);
                return 0;
        }
  
 +      if (!strcmp(var, "core.eol")) {
 +              if (value && !strcasecmp(value, "lf"))
 +                      eol = EOL_LF;
 +              else if (value && !strcasecmp(value, "crlf"))
 +                      eol = EOL_CRLF;
 +              else if (value && !strcasecmp(value, "native"))
 +                      eol = EOL_NATIVE;
 +              else
 +                      eol = EOL_UNSET;
 +              if (eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
 +                      return error("core.autocrlf=input conflicts with core.eol=crlf");
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.notesref")) {
                notes_ref_name = xstrdup(value);
                return 0;
        if (!strcmp(var, "core.editor"))
                return git_config_string(&editor_program, var, value);
  
 +      if (!strcmp(var, "core.askpass"))
 +              return git_config_string(&askpass_program, var, value);
 +
        if (!strcmp(var, "core.excludesfile"))
                return git_config_pathname(&excludes_file, var, value);
  
@@@ -743,10 -598,8 +741,10 @@@ static int git_default_push_config(cons
                        push_default = PUSH_DEFAULT_NOTHING;
                else if (!strcmp(value, "matching"))
                        push_default = PUSH_DEFAULT_MATCHING;
 -              else if (!strcmp(value, "tracking"))
 -                      push_default = PUSH_DEFAULT_TRACKING;
 +              else if (!strcmp(value, "upstream"))
 +                      push_default = PUSH_DEFAULT_UPSTREAM;
 +              else if (!strcmp(value, "tracking")) /* deprecated */
 +                      push_default = PUSH_DEFAULT_UPSTREAM;
                else if (!strcmp(value, "current"))
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
@@@ -828,7 -681,7 +826,7 @@@ const char *git_etc_gitconfig(void
        return system_wide;
  }
  
 -static int git_env_bool(const char *k, int def)
 +int git_env_bool(const char *k, int def)
  {
        const char *v = getenv(k);
        return v ? git_config_bool(k, v) : def;
@@@ -839,25 -692,15 +837,25 @@@ int git_config_system(void
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
  }
  
 -int git_config_global(void)
 +int git_config_from_parameters(config_fn_t fn, void *data)
  {
 -      return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
 +      static int loaded_environment;
 +      const struct config_item *ct;
 +
 +      if (!loaded_environment) {
 +              if (git_config_parse_environment() < 0)
 +                      return -1;
 +              loaded_environment = 1;
 +      }
 +      for (ct = config_parameters; ct; ct = ct->next)
 +              if (fn(ct->name, ct->value, data) < 0)
 +                      return -1;
 +      return 0;
  }
  
 -int git_config(config_fn_t fn, void *data)
 +int git_config_early(config_fn_t fn, void *data, const char *repo_config)
  {
        int ret = 0, found = 0;
 -      char *repo_config = NULL;
        const char *home = NULL;
  
        /* Setting $GIT_CONFIG makes git read _only_ the given config file. */
        }
  
        home = getenv("HOME");
 -      if (git_config_global() && home) {
 +      if (home) {
                char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
                if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
                free(user_config);
        }
  
 -      repo_config = git_pathdup("config");
 -      if (!access(repo_config, R_OK)) {
 +      if (repo_config && !access(repo_config, R_OK)) {
                ret += git_config_from_file(fn, repo_config, data);
                found += 1;
        }
 -      free(repo_config);
 -      if (found == 0)
 -              return -1;
 +
 +      ret += git_config_from_parameters(fn, data);
 +      if (config_parameters)
 +              found += 1;
 +
 +      return ret == 0 ? found : ret;
 +}
 +
 +int git_config(config_fn_t fn, void *data)
 +{
 +      char *repo_config = NULL;
 +      int ret;
 +
 +      repo_config = git_pathdup("config");
 +      ret = git_config_early(fn, data, repo_config);
 +      if (repo_config)
 +              free(repo_config);
        return ret;
  }
  
@@@ -1101,75 -931,6 +1099,75 @@@ int git_config_set(const char *key, con
        return git_config_set_multivar(key, value, NULL, 0);
  }
  
 +/*
 + * Auxiliary function to sanity-check and split the key into the section
 + * identifier and variable name.
 + *
 + * Returns 0 on success, -1 when there is an invalid character in the key and
 + * -2 if there is no section name in the key.
 + *
 + * store_key - pointer to char* which will hold a copy of the key with
 + *             lowercase section and variable name
 + * baselen - pointer to int which will hold the length of the
 + *           section + subsection part, can be NULL
 + */
 +int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 +{
 +      int i, dot, baselen;
 +      const char *last_dot = strrchr(key, '.');
 +
 +      /*
 +       * Since "key" actually contains the section name and the real
 +       * key name separated by a dot, we have to know where the dot is.
 +       */
 +
 +      if (last_dot == NULL || last_dot == key) {
 +              error("key does not contain a section: %s", key);
 +              return -2;
 +      }
 +
 +      if (!last_dot[1]) {
 +              error("key does not contain variable name: %s", key);
 +              return -2;
 +      }
 +
 +      baselen = last_dot - key;
 +      if (baselen_)
 +              *baselen_ = baselen;
 +
 +      /*
 +       * Validate the key and while at it, lower case it for matching.
 +       */
 +      *store_key = xmalloc(strlen(key) + 1);
 +
 +      dot = 0;
 +      for (i = 0; key[i]; i++) {
 +              unsigned char c = key[i];
 +              if (c == '.')
 +                      dot = 1;
 +              /* Leave the extended basename untouched.. */
 +              if (!dot || i > baselen) {
 +                      if (!iskeychar(c) ||
 +                          (i == baselen + 1 && !isalpha(c))) {
 +                              error("invalid key: %s", key);
 +                              goto out_free_ret_1;
 +                      }
 +                      c = tolower(c);
 +              } else if (c == '\n') {
 +                      error("invalid key (newline): %s", key);
 +                      goto out_free_ret_1;
 +              }
 +              (*store_key)[i] = c;
 +      }
 +      (*store_key)[i] = 0;
 +
 +      return 0;
 +
 +out_free_ret_1:
 +      free(*store_key);
 +      return -1;
 +}
 +
  /*
   * If value==NULL, unset in (remove from) config,
   * if value_regex!=NULL, disregard key/value pairs where value does not match.
  int git_config_set_multivar(const char *key, const char *value,
        const char *value_regex, int multi_replace)
  {
 -      int i, dot;
        int fd = -1, in_fd;
        int ret;
        char *config_filename;
        struct lock_file *lock = NULL;
 -      const char *last_dot = strrchr(key, '.');
  
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
        else
                config_filename = git_pathdup("config");
  
 -      /*
 -       * Since "key" actually contains the section name and the real
 -       * key name separated by a dot, we have to know where the dot is.
 -       */
 -
 -      if (last_dot == NULL) {
 -              error("key does not contain a section: %s", key);
 -              ret = 2;
 +      /* parse-key returns negative; flip the sign to feed exit(3) */
 +      ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
 +      if (ret)
                goto out_free;
 -      }
 -      store.baselen = last_dot - key;
  
        store.multi_replace = multi_replace;
  
 -      /*
 -       * Validate the key and while at it, lower case it for matching.
 -       */
 -      store.key = xmalloc(strlen(key) + 1);
 -      dot = 0;
 -      for (i = 0; key[i]; i++) {
 -              unsigned char c = key[i];
 -              if (c == '.')
 -                      dot = 1;
 -              /* Leave the extended basename untouched.. */
 -              if (!dot || i > store.baselen) {
 -                      if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
 -                              error("invalid key: %s", key);
 -                              free(store.key);
 -                              ret = 1;
 -                              goto out_free;
 -                      }
 -                      c = tolower(c);
 -              } else if (c == '\n') {
 -                      error("invalid key (newline): %s", key);
 -                      free(store.key);
 -                      ret = 1;
 -                      goto out_free;
 -              }
 -              store.key[i] = c;
 -      }
 -      store.key[i] = 0;
  
        /*
         * The lock serves a purpose in addition to locking: the new
diff --combined strbuf.c
index 77444a94df3d4a0cda6403957fd13ea262d3ab24,73e0400596558cc37e7342437f3484d6a762c110..09c43ae59a7d4715c26f13d72ca37bc759d1c76d
+++ b/strbuf.c
@@@ -30,8 -30,10 +30,10 @@@ void strbuf_init(struct strbuf *sb, siz
  {
        sb->alloc = sb->len = 0;
        sb->buf = strbuf_slopbuf;
-       if (hint)
+       if (hint) {
                strbuf_grow(sb, hint);
+               sb->buf[0] = '\0';
+       }
  }
  
  void strbuf_release(struct strbuf *sb)
@@@ -63,8 -65,7 +65,8 @@@ void strbuf_attach(struct strbuf *sb, v
  
  void strbuf_grow(struct strbuf *sb, size_t extra)
  {
 -      if (sb->len + extra + 1 <= sb->len)
 +      if (unsigned_add_overflows(extra, 1) ||
 +          unsigned_add_overflows(sb->len, extra + 1))
                die("you want to use way too much memory");
        if (!sb->alloc)
                sb->buf = NULL;
@@@ -153,7 -154,7 +155,7 @@@ int strbuf_cmp(const struct strbuf *a, 
  void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
                                   const void *data, size_t dlen)
  {
 -      if (pos + len < pos)
 +      if (unsigned_add_overflows(pos, len))
                die("you want to use way too much memory");
        if (pos > sb->len)
                die("`pos' is too far after the end of the buffer");
@@@ -195,29 -196,24 +197,29 @@@ void strbuf_adddup(struct strbuf *sb, s
  
  void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
  {
 -      int len;
        va_list ap;
 +      va_start(ap, fmt);
 +      strbuf_vaddf(sb, fmt, ap);
 +      va_end(ap);
 +}
 +
 +void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap)
 +{
 +      int len;
 +      va_list cp;
  
        if (!strbuf_avail(sb))
                strbuf_grow(sb, 64);
 -      va_start(ap, fmt);
 -      len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
 -      va_end(ap);
 +      va_copy(cp, ap);
 +      len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, cp);
 +      va_end(cp);
        if (len < 0)
 -              die("your vsnprintf is broken");
 +              die("BUG: your vsnprintf is broken (returned %d)", len);
        if (len > strbuf_avail(sb)) {
                strbuf_grow(sb, len);
 -              va_start(ap, fmt);
                len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
 -              va_end(ap);
 -              if (len > strbuf_avail(sb)) {
 -                      die("this should not happen, your snprintf is broken");
 -              }
 +              if (len > strbuf_avail(sb))
 +                      die("BUG: your vsnprintf is broken (insatiable)");
        }
        strbuf_setlen(sb, sb->len + len);
  }
@@@ -392,3 -388,19 +394,3 @@@ int strbuf_read_file(struct strbuf *sb
  
        return len;
  }
 -
 -int strbuf_branchname(struct strbuf *sb, const char *name)
 -{
 -      int len = strlen(name);
 -      if (interpret_branch_name(name, sb) == len)
 -              return 0;
 -      strbuf_add(sb, name, len);
 -      return len;
 -}
 -
 -int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 -{
 -      strbuf_branchname(sb, name);
 -      strbuf_splice(sb, 0, 0, "refs/heads/", 11);
 -      return check_ref_format(sb->buf);
 -}