Code

Merge branch 'jk/maint-config-param'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2011 16:45:21 +0000 (09:45 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2011 16:45:21 +0000 (09:45 -0700)
* jk/maint-config-param:
  config: use strbuf_split_str instead of a temporary strbuf
  strbuf: allow strbuf_split to work on non-strbufs
  config: avoid segfault when parsing command-line config
  config: die on error in command-line config
  fix "git -c" parsing of values with equals signs
  strbuf_split: add a max parameter

1  2 
config.c
strbuf.c
strbuf.h

diff --combined config.c
index 1fc063b2562101687b9215e5b697a91fcffdd5bb,44b2c93b2425a4ed5ca3a2cf8f042aca8e275c56..9340dfe5a831fb13c3a15a01976a22bb194eeb58
+++ b/config.c
  
  #define MAXNAME (256)
  
 -static FILE *config_file;
 -static const char *config_file_name;
 -static int config_linenr;
 -static int config_file_eof;
 +typedef struct config_file {
 +      struct config_file *prev;
 +      FILE *f;
 +      const char *name;
 +      int linenr;
 +      int eof;
 +      struct strbuf value;
 +      char var[MAXNAME];
 +} config_file;
 +
 +static config_file *cf;
 +
  static int zlib_compression_seen;
  
  const char *config_exclusive_filename = NULL;
@@@ -50,10 -42,10 +50,10 @@@ void git_config_push_parameter(const ch
  static int git_config_parse_parameter(const char *text,
                                      config_fn_t fn, void *data)
  {
-       struct strbuf tmp = STRBUF_INIT;
        struct strbuf **pair;
-       strbuf_addstr(&tmp, text);
-       pair = strbuf_split(&tmp, '=');
+       pair = strbuf_split_str(text, '=', 2);
+       if (!pair[0])
+               return error("bogus config parameter: %s", text);
        if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
                strbuf_setlen(pair[0], pair[0]->len - 1);
        strbuf_trim(pair[0]);
@@@ -107,7 -99,7 +107,7 @@@ static int get_next_char(void
        FILE *f;
  
        c = '\n';
 -      if ((f = config_file) != NULL) {
 +      if (cf && ((f = cf->f) != NULL)) {
                c = fgetc(f);
                if (c == '\r') {
                        /* DOS like systems */
                        }
                }
                if (c == '\n')
 -                      config_linenr++;
 +                      cf->linenr++;
                if (c == EOF) {
 -                      config_file_eof = 1;
 +                      cf->eof = 1;
                        c = '\n';
                }
        }
  
  static char *parse_value(void)
  {
 -      static char value[1024];
 -      int quote = 0, comment = 0, len = 0, space = 0;
 +      int quote = 0, comment = 0, space = 0;
  
 +      strbuf_reset(&cf->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 cf->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
 -                      if (len)
 +                      if (cf->value.len)
                                space++;
                        continue;
                }
                        }
                }
                for (; space; space--)
 -                      value[len++] = ' ';
 +                      strbuf_addch(&cf->value, ' ');
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
                        default:
                                return NULL;
                        }
 -                      value[len++] = c;
 +                      strbuf_addch(&cf->value, c);
                        continue;
                }
                if (c == '"') {
                        quote = 1-quote;
                        continue;
                }
 -              value[len++] = c;
 +              strbuf_addch(&cf->value, c);
        }
  }
  
@@@ -199,7 -194,7 +199,7 @@@ static int get_value(config_fn_t fn, vo
        /* Get the full name */
        for (;;) {
                c = get_next_char();
 -              if (config_file_eof)
 +              if (cf->eof)
                        break;
                if (!iskeychar(c))
                        break;
@@@ -263,7 -258,7 +263,7 @@@ static int get_base_var(char *name
  
        for (;;) {
                int c = get_next_char();
 -              if (config_file_eof)
 +              if (cf->eof)
                        return -1;
                if (c == ']')
                        return baselen;
@@@ -281,7 -276,7 +281,7 @@@ static int git_parse_file(config_fn_t f
  {
        int comment = 0;
        int baselen = 0;
 -      static char var[MAXNAME];
 +      char *var = cf->var;
  
        /* U+FEFF Byte Order Mark in UTF8 */
        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
                        }
                }
                if (c == '\n') {
 -                      if (config_file_eof)
 +                      if (cf->eof)
                                return 0;
                        comment = 0;
                        continue;
                if (get_value(fn, data, var, baselen+1) < 0)
                        break;
        }
 -      die("bad config file line %d in %s", config_linenr, config_file_name);
 +      die("bad config file line %d in %s", cf->linenr, cf->name);
  }
  
  static int parse_unit_factor(const char *end, unsigned long *val)
@@@ -381,8 -376,8 +381,8 @@@ int git_parse_ulong(const char *value, 
  
  static void die_bad_config(const char *name)
  {
 -      if (config_file_name)
 -              die("bad config value for '%s' in %s", name, config_file_name);
 +      if (cf && cf->name)
 +              die("bad config value for '%s' in %s", name, cf->name);
        die("bad config value for '%s'", name);
  }
  
@@@ -578,7 -573,7 +578,7 @@@ static int git_default_core_config(cons
  
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
 -                      if (eol == EOL_CRLF)
 +                      if (core_eol == EOL_CRLF)
                                return error("core.autocrlf=input conflicts with core.eol=crlf");
                        auto_crlf = AUTO_CRLF_INPUT;
                        return 0;
  
        if (!strcmp(var, "core.eol")) {
                if (value && !strcasecmp(value, "lf"))
 -                      eol = EOL_LF;
 +                      core_eol = EOL_LF;
                else if (value && !strcasecmp(value, "crlf"))
 -                      eol = EOL_CRLF;
 +                      core_eol = EOL_CRLF;
                else if (value && !strcasecmp(value, "native"))
 -                      eol = EOL_NATIVE;
 +                      core_eol = EOL_NATIVE;
                else
 -                      eol = EOL_UNSET;
 -              if (eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
 +                      core_eol = EOL_UNSET;
 +              if (core_eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
                        return error("core.autocrlf=input conflicts with core.eol=crlf");
                return 0;
        }
@@@ -802,24 -797,13 +802,24 @@@ int git_config_from_file(config_fn_t fn
  
        ret = -1;
        if (f) {
 -              config_file = f;
 -              config_file_name = filename;
 -              config_linenr = 1;
 -              config_file_eof = 0;
 +              config_file top;
 +
 +              /* push config-file parsing state stack */
 +              top.prev = cf;
 +              top.f = f;
 +              top.name = filename;
 +              top.linenr = 1;
 +              top.eof = 0;
 +              strbuf_init(&top.value, 1024);
 +              cf = &top;
 +
                ret = git_parse_file(fn, data);
 +
 +              /* pop config-file parsing state stack */
 +              strbuf_release(&top.value);
 +              cf = top.prev;
 +
                fclose(f);
 -              config_file_name = NULL;
        }
        return ret;
  }
@@@ -874,7 -858,7 +874,7 @@@ int git_config_early(config_fn_t fn, vo
  
        switch (git_config_from_parameters(fn, data)) {
        case -1: /* error */
-               ret--;
+               die("unable to parse command-line config");
                break;
        case 0: /* found nothing */
                break;
@@@ -927,7 -911,6 +927,7 @@@ static int store_aux(const char *key, c
  {
        const char *ep;
        size_t section_len;
 +      FILE *f = cf->f;
  
        switch (store.state) {
        case KEY_SEEN:
                                return 1;
                        }
  
 -                      store.offset[store.seen] = ftell(config_file);
 +                      store.offset[store.seen] = ftell(f);
                        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] = ftell(config_file);
 +              store.offset[store.seen] = ftell(f);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
 -                      store.offset[store.seen] = ftell(config_file);
 +                      store.offset[store.seen] = ftell(f);
                        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] = ftell(config_file);
 +                                      store.offset[store.seen] = ftell(f);
                        }
                }
        }
@@@ -1121,12 -1104,12 +1121,12 @@@ int git_config_parse_key(const char *ke
  
        if (last_dot == NULL || last_dot == key) {
                error("key does not contain a section: %s", key);
 -              return -2;
 +              return -CONFIG_NO_SECTION_OR_NAME;
        }
  
        if (!last_dot[1]) {
                error("key does not contain variable name: %s", key);
 -              return -2;
 +              return -CONFIG_NO_SECTION_OR_NAME;
        }
  
        baselen = last_dot - key;
  
  out_free_ret_1:
        free(*store_key);
 -      return -1;
 +      return -CONFIG_INVALID_KEY;
  }
  
  /*
@@@ -1219,7 -1202,7 +1219,7 @@@ int git_config_set_multivar(const char 
        if (fd < 0) {
                error("could not lock config file %s: %s", config_filename, strerror(errno));
                free(store.key);
 -              ret = -1;
 +              ret = CONFIG_NO_LOCK;
                goto out_free;
        }
  
                if ( ENOENT != errno ) {
                        error("opening %s: %s", config_filename,
                              strerror(errno));
 -                      ret = 3; /* same as "invalid config file" */
 +                      ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
                /* if nothing to unset, error out */
                if (value == NULL) {
 -                      ret = 5;
 +                      ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
  
                                        REG_EXTENDED)) {
                                error("invalid pattern: %s", value_regex);
                                free(store.value_regex);
 -                              ret = 6;
 +                              ret = CONFIG_INVALID_PATTERN;
                                goto out_free;
                        }
                }
                                regfree(store.value_regex);
                                free(store.value_regex);
                        }
 -                      ret = 3;
 +                      ret = CONFIG_INVALID_FILE;
                        goto out_free;
                }
  
                /* if nothing to unset, or too many matches, error out */
                if ((store.seen == 0 && value == NULL) ||
                                (store.seen > 1 && multi_replace == 0)) {
 -                      ret = 5;
 +                      ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
  
  
        if (commit_lock_file(lock) < 0) {
                error("could not commit config file %s", config_filename);
 -              ret = 4;
 +              ret = CONFIG_NO_WRITE;
                goto out_free;
        }
  
@@@ -1434,7 -1417,6 +1434,7 @@@ int git_config_rename_section(const cha
        struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
        int out_fd;
        char buf[1024];
 +      FILE *config_file;
  
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
diff --combined strbuf.c
index 09c43ae59a7d4715c26f13d72ca37bc759d1c76d,a71de9cd63a695ba67b3ca3175926f5280827a63..1a7df12e8f233863cd931a960d453d54050f5584
+++ b/strbuf.c
@@@ -30,10 -30,8 +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)
@@@ -103,24 -101,27 +103,27 @@@ void strbuf_ltrim(struct strbuf *sb
        sb->buf[sb->len] = '\0';
  }
  
- struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
+ struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max)
  {
        int alloc = 2, pos = 0;
-       char *n, *p;
+       const char *n, *p;
        struct strbuf **ret;
        struct strbuf *t;
  
        ret = xcalloc(alloc, sizeof(struct strbuf *));
-       p = n = sb->buf;
-       while (n < sb->buf + sb->len) {
+       p = n = str;
+       while (n < str + slen) {
                int len;
-               n = memchr(n, delim, sb->len - (n - sb->buf));
+               if (max <= 0 || pos + 1 < max)
+                       n = memchr(n, delim, slen - (n - str));
+               else
+                       n = NULL;
                if (pos + 1 >= alloc) {
                        alloc = alloc * 2;
                        ret = xrealloc(ret, sizeof(struct strbuf *) * alloc);
                }
                if (!n)
-                       n = sb->buf + sb->len - 1;
+                       n = str + slen - 1;
                len = n - p + 1;
                t = xmalloc(sizeof(struct strbuf));
                strbuf_init(t, len);
diff --combined strbuf.h
index 9e6d9fa53fc04156452bf850af1a9b2d3ba830f2,e7e674bf1f9a2981e9f6dfbf6e51ba277ebcdc67..46a33f8c46985c4377071011d6ea48d6d3fe5331
+++ b/strbuf.h
@@@ -3,6 -3,8 +3,6 @@@
  
  /* See Documentation/technical/api-strbuf.txt */
  
 -#include <assert.h>
 -
  extern char strbuf_slopbuf[];
  struct strbuf {
        size_t alloc;
@@@ -31,8 -33,9 +31,8 @@@ static inline size_t strbuf_avail(cons
  extern void strbuf_grow(struct strbuf *, size_t);
  
  static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
 -      if (!sb->alloc)
 -              strbuf_grow(sb, 0);
 -      assert(len < sb->alloc);
 +      if (len > (sb->alloc ? sb->alloc - 1 : 0))
 +              die("BUG: strbuf_setlen() beyond buffer");
        sb->len = len;
        sb->buf[len] = '\0';
  }
@@@ -44,7 -47,22 +44,22 @@@ extern void strbuf_rtrim(struct strbuf 
  extern void strbuf_ltrim(struct strbuf *);
  extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
  
- extern struct strbuf **strbuf_split(const struct strbuf *, int delim);
+ extern struct strbuf **strbuf_split_buf(const char *, size_t,
+                                       int delim, int max);
+ static inline struct strbuf **strbuf_split_str(const char *str,
+                                              int delim, int max)
+ {
+       return strbuf_split_buf(str, strlen(str), delim, max);
+ }
+ static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
+                                               int delim, int max)
+ {
+       return strbuf_split_buf(sb->buf, sb->len, delim, max);
+ }
+ static inline struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
+ {
+       return strbuf_split_max(sb, delim, 0);
+ }
  extern void strbuf_list_free(struct strbuf **);
  
  /*----- add data in your buffer -----*/