Commit 9ac581bf by Carlos Martín Nieto

config: behave like git with [section.subsection]

The documentation is a bit misleading. The subsection name is always
case-sensitive, but with a [section.subsection] header, the subsection
is transformed to lowercase when the configuration is parsed.

Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>
parent 92be7908
......@@ -9,6 +9,7 @@
#include "config.h"
#include "fileops.h"
#include "filebuf.h"
#include "buffer.h"
#include "git2/config.h"
#include "git2/types.h"
......@@ -107,46 +108,37 @@ static void cvar_list_free(cvar_t_list *list)
}
/*
* Compare two strings according to the git section-subsection
* rules. The order of the strings is important because local is
* assumed to have the internal format (only the section name and with
* case information) and input the normalized one (only dots, no case
* information).
* Compare according to the git rules. Section contains the section as
* it's stored internally. query is the full name as would be given to
* 'git config'.
*/
static int cvar_match_section(const char *local, const char *input)
static int cvar_match_section(const char *section, const char *query)
{
char *first_dot;
char *local_sp = strchr(local, ' ');
size_t comparison_len;
const char *sdot, *qdot, *qsub;
size_t section_len;
/*
* If the local section name doesn't contain a space, then we can
* just do a case-insensitive compare.
*/
if (local_sp == NULL)
return !strncasecmp(local, input, strlen(local));
sdot = strchr(section, '.');
/*
* From here onwards, there is a space diving the section and the
* subsection. Anything before the space in local is
* case-insensitive.
*/
if (strncasecmp(local, input, local_sp - local))
return 0;
/* If the section doesn't have any dots, it's easy */
if (sdot == NULL)
return !strncasecmp(section, query, strlen(section));
/*
* We compare starting from the first character after the
* quotation marks, which is two characters beyond the space. For
* the input, we start one character beyond the dot. If the names
* have different lengths, then we can fail early, as we know they
* can't be the same.
* The length is given by the length between the quotation marks.
* If it does have dots, compare the sections
* case-insensitively. The comparison includes the dots.
*/
section_len = sdot - section + 1;
if (strncasecmp(section, query, sdot - section))
return 0;
first_dot = strchr(input, '.');
comparison_len = strlen(local_sp + 2) - 1;
qsub = query + section_len;
qdot = strchr(qsub, '.');
/* Make sure the subsections are the same length */
if (strlen(sdot + 1) != qdot - qsub)
return 0;
return !strncmp(local_sp + 2, first_dot + 1, comparison_len);
/* The subsection is case-sensitive */
return !strncmp(sdot + 1, qsub, strlen(sdot + 1));
}
static int cvar_match_name(const cvar_t *var, const char *str)
......@@ -581,10 +573,9 @@ GIT_INLINE(int) config_keychar(int c)
static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
{
size_t buf_len, total_len;
int pos, rpos;
int c, ret;
char *subsection, *first_quote, *last_quote;
int c, rpos;
char *first_quote, *last_quote;
git_buf buf = GIT_BUF_INIT;
int error = GIT_SUCCESS;
int quote_marks;
/*
......@@ -599,13 +590,9 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
if (last_quote - first_quote == 0)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark");
buf_len = last_quote - first_quote + 2;
subsection = git__malloc(buf_len + 2);
if (subsection == NULL)
return GIT_ENOMEM;
git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
git_buf_printf(&buf, "%s.", base_name);
pos = 0;
rpos = 0;
quote_marks = 0;
......@@ -626,7 +613,7 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
switch (c) {
case '"':
++quote_marks;
break;
continue;
case '\\':
c = line[rpos++];
switch (c) {
......@@ -642,28 +629,12 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
break;
}
subsection[pos++] = (char) c;
git_buf_putc(&buf, c);
} while ((c = line[rpos++]) != ']');
subsection[pos] = '\0';
total_len = strlen(base_name) + strlen(subsection) + 2;
*section_name = git__malloc(total_len);
if (*section_name == NULL) {
error = GIT_ENOMEM;
goto out;
}
ret = p_snprintf(*section_name, total_len, "%s %s", base_name, subsection);
if (ret < 0) {
error = git__throw(GIT_EOSERR, "Failed to parse ext header. OS error: %s", strerror(errno));
goto out;
}
git__strntolower(*section_name, strchr(*section_name, ' ') - *section_name);
*section_name = git__strdup(git_buf_cstr(&buf));
out:
free(subsection);
git_buf_free(&buf);
return error;
}
......
......@@ -2,3 +2,8 @@
dummy2 = 42
verylong = 1
dummy = 1
[remote "ab"]
url = http://example.com/git/ab
[remote "abba"]
url = http://example.com/git/abba
......@@ -107,10 +107,8 @@ BEGIN_TEST(config3, "parse a [section.subsection] header")
must_pass(git_config_get_string(cfg, "section.subsection.var", &str));
must_be_true(!strcmp(str, "hello"));
/* Avoid a false positive */
str = "nohello";
must_pass(git_config_get_string(cfg, "section.subSectIon.var", &str));
must_be_true(!strcmp(str, "hello"));
/* The subsection is transformed to lower-case */
must_fail(git_config_get_string(cfg, "section.subSectIon.var", &str));
git_config_free(cfg);
END_TEST
......@@ -324,6 +322,20 @@ BEGIN_TEST(config16, "add a variable in a new section")
must_pass(git_filebuf_commit(&buf));
END_TEST
BEGIN_TEST(config17, "prefixes aren't broken")
git_config *cfg;
const char *str;
must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9"));
must_pass(git_config_get_string(cfg, "remote.ab.url", &str));
must_be_true(strcmp(str, "http://example.com/git/ab") == 0);
must_pass(git_config_get_string(cfg, "remote.abba.url", &str));
must_be_true(strcmp(str, "http://example.com/git/abba") == 0);
git_config_free(cfg);
END_TEST
BEGIN_SUITE(config)
ADD_TEST(config0);
ADD_TEST(config1);
......@@ -342,4 +354,5 @@ BEGIN_SUITE(config)
ADD_TEST(config14);
ADD_TEST(config15);
ADD_TEST(config16);
ADD_TEST(config17);
END_SUITE
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment