git config syntax updates
authorLinus Torvalds <torvalds@osdl.org>
Tue, 9 May 2006 19:24:02 +0000 (12:24 -0700)
committerJunio C Hamano <junkio@cox.net>
Sat, 13 May 2006 21:00:16 +0000 (14:00 -0700)
This updates the hierarchical section name syntax to

[section<space>+"<randomstring>"]

where the only rule for "randomstring" is that it can't contain a newline,
and if you really want to insert a double-quote, you do it with \".

It turns that into the section name "secion.randomstring".  The
"section" part is still case insensitive, but the "randomstring"
part is case sensitive.

So you could use this for things like

[email "torvalds@osdl.org"]
name = Linus Torvalds

if you wanted to do the "email->name" conversion as part of the config
file format (I'm not claiming that is sensible, I'm just giving it as an
insane example). That would show up as the association

email.torvalds@osdl.org.name -> Linus Torvalds

which is easy to parse (the "." in the email _looks_ ambiguous, but it
isn't: you know that there will always be a single key-name, so you find
the key name with "strrchr(name, '.')" and things are entirely
unambiguous).

Repo-config is updated to be able to parse the new format, and also
write things out in the new format.

[jc: rolled two patches from Linus and one fix-up from Sean into one,
 with additional adjustments for t/t1300 test to check the case
 insensitiveness of section base and variable and case sensitiveness
 of the extended section part.  Then stripped some part off to make
 the result applicable to the stale 1.3.X series that does not have
 recent enhancements. ]

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>
Signed-off-by: Junio C Hamano <junkio@cox.net>
config.c
repo-config.c
t/t1300-repo-config.sh

index 0f518c9..0248c6d 100644 (file)
--- a/config.c
+++ b/config.c
@@ -134,6 +134,41 @@ static int get_value(config_fn_t fn, char *name, unsigned int len)
        return fn(name, value);
 }
 
+static int get_extended_base_var(char *name, int baselen, int c)
+{
+       do {
+               if (c == '\n')
+                       return -1;
+               c = get_next_char();
+       } while (isspace(c));
+
+       /* We require the format to be '[base "extension"]' */
+       if (c != '"')
+               return -1;
+       name[baselen++] = '.';
+
+       for (;;) {
+               int c = get_next_char();
+               if (c == '\n')
+                       return -1;
+               if (c == '"')
+                       break;
+               if (c == '\\') {
+                       c = get_next_char();
+                       if (c == '\n')
+                               return -1;
+               }
+               name[baselen++] = c;
+               if (baselen > MAXNAME / 2)
+                       return -1;
+       }
+
+       /* Final ']' */
+       if (get_next_char() != ']')
+               return -1;
+       return baselen;
+}
+
 static int get_base_var(char *name)
 {
        int baselen = 0;
@@ -144,6 +179,8 @@ static int get_base_var(char *name)
                        return -1;
                if (c == ']')
                        return baselen;
+               if (isspace(c))
+                       return get_extended_base_var(name, baselen, c);
                if (!isalnum(c) && c != '.')
                        return -1;
                if (baselen > MAXNAME / 2)
@@ -335,10 +372,12 @@ static int store_aux(const char* key, const char* value)
                        store.offset[store.seen] = ftell(config_file);
                        store.state = KEY_SEEN;
                        store.seen++;
-               } else if (strrchr(key, '.') - key == store.baselen &&
+               } else {
+                       if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
                                        store.offset[store.seen] = ftell(config_file);
+                       }
                }
        }
        return 0;
@@ -346,8 +385,30 @@ static int store_aux(const char* key, const char* value)
 
 static void store_write_section(int fd, const char* key)
 {
+       const char *dot = strchr(key, '.');
+       int len1 = store.baselen, len2 = -1;
+
+       dot = strchr(key, '.');
+       if (dot) {
+               int dotlen = dot - key;
+               if (dotlen < len1) {
+                       len2 = len1 - dotlen - 1;
+                       len1 = dotlen;
+               }
+       }
+
        write(fd, "[", 1);
-       write(fd, key, store.baselen);
+       write(fd, key, len1);
+       if (len2 >= 0) {
+               write(fd, " \"", 2);
+               while (--len2 >= 0) {
+                       unsigned char c = *++dot;
+                       if (c == '"')
+                               write(fd, "\\", 1);
+                       write(fd, &c, 1);
+               }
+               write(fd, "\"", 1);
+       }
        write(fd, "]\n", 2);
 }
 
@@ -421,7 +482,7 @@ int git_config_set(const char* key, const char* value)
 int git_config_set_multivar(const char* key, const char* value,
        const char* value_regex, int multi_replace)
 {
-       int i;
+       int i, dot;
        int fd = -1, in_fd;
        int ret;
        char* config_filename = strdup(git_path("config"));
@@ -446,16 +507,23 @@ int git_config_set_multivar(const char* key, const char* value,
         * Validate the key and while at it, lower case it for matching.
         */
        store.key = (char*)malloc(strlen(key)+1);
-       for (i = 0; key[i]; i++)
-               if (i != store.baselen &&
-                               ((!isalnum(key[i]) && key[i] != '.') ||
-                                (i == store.baselen+1 && !isalpha(key[i])))) {
-                       fprintf(stderr, "invalid key: %s\n", key);
-                       free(store.key);
-                       ret = 1;
-                       goto out_free;
-               } else
-                       store.key[i] = tolower(key[i]);
+       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 (!isalnum(c) || (i == store.baselen+1 && !isalpha(c))) {
+                               fprintf(stderr, "invalid key: %s\n", key);
+                               free(store.key);
+                               ret = 1;
+                               goto out_free;
+                       }
+                       c = tolower(c);
+               }
+               store.key[i] = c;
+       }
        store.key[i] = 0;
 
        /*
index c5ebb76..58b6c52 100644 (file)
@@ -48,11 +48,13 @@ static int show_config(const char* key_, const char* value_)
 static int get_value(const char* key_, const char* regex_)
 {
        int i;
+       char *tl;
 
-       key = malloc(strlen(key_)+1);
-       for (i = 0; key_[i]; i++)
-               key[i] = tolower(key_[i]);
-       key[i] = 0;
+       key = strdup(key_);
+       for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
+               *tl = tolower(*tl);
+       for (tl=key; *tl && *tl != '.'; ++tl)
+               *tl = tolower(*tl);
 
        if (regex_) {
                if (regex_[0] == '!') {
index ab4dd5c..028d159 100755 (executable)
@@ -229,7 +229,7 @@ test_expect_failure 'invalid key' 'git-repo-config inval.2key blabla'
 test_expect_success 'correct key' 'git-repo-config 123456.a123 987'
 
 test_expect_success 'hierarchical section' \
-       'git-repo-config 1.2.3.alpha beta'
+       'git-repo-config Version.1.2.3eX.Alpha beta'
 
 cat > expect << EOF
 [beta] ; silly comment # another comment
@@ -241,8 +241,8 @@ noIndent= sillyValue ; 'nother silly comment
        NoNewLine = wow2 for me
 [123456]
        a123 = 987
-[1.2.3]
-       alpha = beta
+[Version "1.2.3eX"]
+       Alpha = beta
 EOF
 
 test_expect_success 'hierarchical section value' 'cmp .git/config expect'