mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-11-04 07:09:14 +00:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									92be7908bd
								
							
						
					
					
						commit
						9ac581bf7f
					
				@ -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;
 | 
			
		||||
 | 
			
		||||
	sdot = strchr(section, '.');
 | 
			
		||||
 | 
			
		||||
	/* If the section doesn't have any dots, it's easy */
 | 
			
		||||
	if (sdot == NULL)
 | 
			
		||||
		return !strncasecmp(section, query, strlen(section));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the local section name doesn't contain a space, then we can
 | 
			
		||||
	 * just do a case-insensitive compare.
 | 
			
		||||
	 * If it does have dots, compare the sections
 | 
			
		||||
	 * case-insensitively. The comparison includes the dots.
 | 
			
		||||
	 */
 | 
			
		||||
	if (local_sp == NULL)
 | 
			
		||||
		return !strncasecmp(local, input, strlen(local));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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))
 | 
			
		||||
	section_len = sdot - section + 1;
 | 
			
		||||
	if (strncasecmp(section, query, sdot - section))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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.
 | 
			
		||||
	 */
 | 
			
		||||
	qsub = query + section_len;
 | 
			
		||||
	qdot = strchr(qsub, '.');
 | 
			
		||||
	/* Make sure the subsections are the same length */
 | 
			
		||||
	if (strlen(sdot + 1) != qdot - qsub)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	first_dot = strchr(input, '.');
 | 
			
		||||
	comparison_len = strlen(local_sp + 2) - 1;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
 | 
			
		||||
	git_buf_printf(&buf, "%s.", base_name);
 | 
			
		||||
 | 
			
		||||
	subsection = git__malloc(buf_len + 2);
 | 
			
		||||
	if (subsection == NULL)
 | 
			
		||||
		return GIT_ENOMEM;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user