Commit 11d0e705 by Carlos Martín Nieto

Add support for subsections

A variable name is stored internally with its section the way it
appeared in the configuration file in order to have the information
about what parts are case-sensitive inline.

Really implement parse_section_header_ext and move the assignment of
variables to config_parse.

The variable name matching is now done in a case-away way by
cvar_name_match and cvar_section_match. Before the user sees it, it's
normalized to the two- or three-dot version.

Signed-off-by: Carlos Martín Nieto <cmn@elego.de>
parent 6482929b
......@@ -34,7 +34,7 @@
* Forward declarations
***********************/
static int config_parse(git_config *cfg_file);
static int parse_variable(git_config *cfg, const char *section_name, const char *line);
static int parse_variable(const char *line, char **var_name, char **var_value);
void git_config_free(git_config *cfg);
static git_cvar *cvar_free(git_cvar *var)
......@@ -56,20 +56,112 @@ static void cvar_list_free(git_cvar *start)
}
/*
* FIXME: Only the section name is case-insensitive
* The order is importart. The first parameter is the name we want to
* match against, and the second one is what we're looking for
*/
static int cvar_section_match(const char *local, const char *input)
{
char *input_dot = strrchr(input, '.');
char *local_last_dot = strrchr(local, '.');
char *local_sp = strchr(local, ' ');
int comparison_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, local_last_dot - local);
/* Anything before the space in local is case-insensitive */
if (strncasecmp(local, input, local_sp - local)) {
fprintf(stderr, "copmparison of %s and %s failed\n", local, input);
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 first dot.
* The length is given by the length between the quotation marks.
*
* this "that".var
* ^ ^
* a b
*
* where a is (local_sp + 2) and b is local_last_dot. The comparison
* length is given by b - 1 - a.
*/
input_dot = strchr(input, '.');
comparison_len = local_last_dot - 1 - (local_sp + 2);
return !strncmp(local_sp + 2, input_dot + 1, comparison_len);
}
static int cvar_name_match(const char *local, const char *input)
{
char *input_dot = strrchr(input, '.');
char *local_dot = strrchr(local, '.');
/*
* First try to match the section name
*/
if (!cvar_section_match(local, input))
return 0;
/*
* Anything after the last (possibly only) dot is case-insensitive
*/
if (!strcmp(input_dot, local_dot))
return 1;
return 0;
}
static git_cvar *cvar_list_find(git_cvar *start, const char *name)
{
git_cvar *iter;
CVAR_LIST_FOREACH (start, iter) {
if (!strcasecmp(name, iter->name))
if (cvar_name_match(iter->name, name))
return iter;
}
return NULL;
}
static int cvar_name_normalize(const char *input, char **output)
{
char *input_sp = strchr(input, ' ');
char *quote, *str;
int i;
/* We need to make a copy anyway */
str = git__strdup(input);
if (str == NULL)
return GIT_ENOMEM;
*output = str;
/* If there aren't any spaces, we don't need to do anything */
if (input_sp == NULL)
return GIT_SUCCESS;
/*
* If there are spaces, we replace the space by a dot, move the
* variable name so that the dot before it replaces the last
* quotation mark and repeat so that the first quotation mark
* disappears.
*/
str[input_sp - input] = '.';
for (i = 0; i < 2; ++i) {
quote = strrchr(str, '"');
memmove(quote, quote + 1, strlen(quote));
}
return GIT_SUCCESS;
}
void strntolower(char *str, int len)
{
int i;
......@@ -145,9 +237,15 @@ int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *d
{
int ret = GIT_SUCCESS;
git_cvar *var;
char *normalized;
CVAR_LIST_FOREACH(cfg->vars, var) {
ret = fn(var->name, data);
ret = cvar_name_normalize(var->name, &normalized);
if (ret < GIT_SUCCESS)
return ret;
ret = fn(normalized, data);
free(normalized);
if (ret)
break;
}
......@@ -518,16 +616,88 @@ static char *build_varname(const char *section, const char *name)
return NULL;
ret = snprintf(varname, total_len, "%s.%s", section, name);
if(ret >= 0){
strtolower(varname + section_len + 1);
if(ret >= 0){ /* lowercase from the last dot onwards */
char *dot = strrchr(varname, '.');
if (dot != NULL)
strtolower(dot);
}
return varname;
}
static char *parse_section_header_ext(char *base_name, git_config *cfg)
static int parse_section_header_ext(git_config *cfg, const char *base_name, const char *line, char **section_name)
{
return base_name;
int buf_len, total_len, pos;
int c;
char *subsection;
int error = GIT_SUCCESS;
int quote_marks;
/*
* base_name is what came before the space. We should be at the
* first quotation mark, except for now, line isn't being kept in
* sync so we only really use it to calculate the length.
*/
buf_len = strrchr(line, '"') - strchr(line, '"') + 2;
if(!buf_len)
return GIT_EOBJCORRUPTED;
subsection = git__malloc(buf_len + 2);
if(subsection == NULL)
return GIT_ENOMEM;
pos = 0;
c = cfg_getchar(cfg, 0);
quote_marks = 0;
/*
* At the end of each iteration, whatever is stored in c will be
* added to the string. In case of error, jump to out
*/
do {
switch(c) {
case '"':
if (quote_marks++ >= 2)
return GIT_EOBJCORRUPTED;
break;
case '\\':
c = cfg_getchar(cfg, 0);
switch (c) {
case '"':
case '\\':
break;
default:
error = GIT_EOBJCORRUPTED;
goto out;
}
default:
break;
}
subsection[pos++] = c;
} while ((c = cfg_getchar(cfg, 0)) != ']');
subsection[pos] = '\0';
if (cfg_getchar(cfg, 0) != '\n'){
error = GIT_EOBJCORRUPTED;
goto out;
}
total_len = strlen(base_name) + strlen(subsection) + 2;
*section_name = git__malloc(total_len);
if (*section_name == NULL) {
error = GIT_ENOMEM;
goto out;
}
sprintf(*section_name, "%s %s", base_name, subsection);
strntolower(*section_name, strchr(*section_name, ' ') - *section_name);
out:
free(subsection);
return error;
}
static int parse_section_header(git_config *cfg, char **section_out, const char *line)
......@@ -563,8 +733,10 @@ static int parse_section_header(git_config *cfg, char **section_out, const char
}
if (isspace(c)){
*section_out = parse_section_header_ext(name, cfg);
return GIT_SUCCESS;
name[name_length] = '\0';
error = parse_section_header_ext(cfg, name, line, section_out);
free(name);
return error;
}
if (!config_keychar(c) && c != '.'){
......@@ -672,6 +844,9 @@ static int config_parse(git_config *cfg_file)
{
int error = GIT_SUCCESS;
char *current_section = NULL;
char *var_name;
char *var_value;
char *full_name;
/* Initialise the reading position */
cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
......@@ -699,8 +874,25 @@ static int config_parse(git_config *cfg_file)
break;
default: /* assume variable declaration */
error = parse_variable(cfg_file, current_section, line);
error = parse_variable(line, &var_name, &var_value);
cfg_consume_line(cfg_file);
if (error < GIT_SUCCESS)
break;
full_name = build_varname(current_section, var_name);
if (full_name == NULL) {
error = GIT_ENOMEM;
free(var_name);
free(var_value);
break;
}
config_set(cfg_file, full_name, var_value);
free(var_name);
free(var_value);
free(full_name);
break;
}
......@@ -713,11 +905,9 @@ static int config_parse(git_config *cfg_file)
return error;
}
static int parse_variable(git_config *cfg, const char *section_name, const char *line)
static int parse_variable(const char *line, char **var_name, char **var_value)
{
int error = GIT_SUCCESS;
int has_value = 1;
char *varname;
char *tmp;
const char *var_end = NULL;
const char *value_start = NULL;
......@@ -743,15 +933,23 @@ static int parse_variable(git_config *cfg, const char *section_name, const char
goto error;
}
varname = build_varname(section_name, line, var_end - line + 1);
if(varname == NULL)
tmp = strndup(line, var_end - line + 1);
if (tmp == NULL)
return GIT_ENOMEM;
config_set(cfg, varname, value_start);
*var_name = tmp;
if (value_start != NULL) {
tmp = strdup(value_start);
if (tmp == NULL) {
free(*var_name);
return GIT_ENOMEM;
}
free(varname);
*var_value = tmp;
}
return error;
return GIT_SUCCESS;
error:
return GIT_EOBJCORRUPTED;
......
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