Signed-off-by: Sascha Kruse <knopwob@googlemail.com> --- src/pacman/conf.c | 343 ++++++++++++++++++------------------------------------ 1 file changed, 111 insertions(+), 232 deletions(-) diff --git a/src/pacman/conf.c b/src/pacman/conf.c index 3f1b1c3..06a4612 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -36,6 +36,7 @@ #include "util.h" #include "pacman.h" #include "callback.h" +#include "ini.h" /* global config variable */ config_t *config = NULL; @@ -462,9 +463,21 @@ static void setrepeatingoption(char *ptr, const char *option, } } -static int _parse_options(const char *key, char *value, - const char *file, int linenum) +/** Parse one directive from the 'options' section + * @param directive the directive to parse + * @return 0 on success non-zero otherwise + */ +static int parse_option_directive(ini_directive_t *directive) { + const char *key = directive->key; + char *value = NULL; + const char *file = directive->file; + int linenum = directive->linenum; + + if(directive->value != NULL) { + value = strdup(directive->value); + } + if(value == NULL) { /* options without settings */ if(strcmp(key, "UseSyslog") == 0) { @@ -599,6 +612,24 @@ static int _parse_options(const char *key, char *value, return 0; } +/** Parse the 'options' section from the config file + * @param options the option section to parse_options + * @return 0 on success non-zero otherwise + */ +static int parse_options(ini_section_t *options) +{ + alpm_list_t *iter; + int ret; + + for(iter = options->directives; iter; iter = alpm_list_next(iter)) { + ini_directive_t *directive = (ini_directive_t *) iter->data; + if((ret = parse_option_directive(directive))) { + return ret; + } + } + return 0; +} + static int _add_mirror(alpm_db_t *db, char *value) { const char *dbname = alpm_db_get_name(db); @@ -739,43 +770,55 @@ static int setup_libalpm(void) return 0; } -/** - * Allows parsing in advance of an entire config section before we start - * calling library methods. - */ -struct section_t { - /* useful for all sections */ - char *name; - int is_options; - /* db section option gathering */ - alpm_siglevel_t siglevel; - alpm_list_t *servers; -}; - -/** - * Wrap up a section once we have reached the end of it. This should be called - * when a subsequent section is encountered, or when we have reached the end of - * the root config file. Once called, all existing saved config pieces on the - * section struct are freed. - * @param section the current parsed and saved section data - * @param parse_options whether we are parsing options or repo data - * @return 0 on success, 1 on failure +/** Parse an ini section that describes a repository. + * @param section the ini section. + * @return 0 on success non-zero otherwise */ -static int finish_section(struct section_t *section, int parse_options) +static int parse_repository(ini_section_t *section) { int ret = 0; alpm_list_t *i; alpm_db_t *db; + alpm_siglevel_t siglevel = ALPM_SIG_USE_DEFAULT; + alpm_list_t *servers = NULL; - pm_printf(ALPM_LOG_DEBUG, "config: finish section '%s'\n", section->name); + alpm_list_t *directive_iter; + for(directive_iter = section->directives; directive_iter; + directive_iter = alpm_list_next(directive_iter)) { + ini_directive_t *directive = (ini_directive_t *) directive_iter->data; - /* parsing options (or nothing)- nothing to do except free the pieces */ - if(!section->name || parse_options || section->is_options) { - goto cleanup; + if(strcmp(directive->key, "Server") == 0) { + if(directive->value == NULL || strlen(directive->value) == 0) { + pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"), + directive->file, directive->linenum, directive->key); + } else { + servers = alpm_list_add(servers, strdup(directive->value)); + } + }else if(strcmp(directive->key, "SigLevel") == 0) { + alpm_list_t *values = NULL; + char *value = strdup(directive->value); + setrepeatingoption(value, "SigLevel", &values); + if(values) { + if(siglevel == ALPM_SIG_USE_DEFAULT) { + siglevel = config->siglevel; + } + if(process_siglevel(values, &siglevel, directive->file, + directive->linenum)) { + FREELIST(values); + ret = 1; + goto cleanup; + } + free(value); + FREELIST(values); + } + } else { + pm_printf(ALPM_LOG_WARNING, + _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"), + directive->file, directive->linenum, directive->key, section->name); + } } - /* if we are not looking at options sections only, register a db */ - db = alpm_register_syncdb(config->handle, section->name, section->siglevel); + db = alpm_register_syncdb(config->handle, section->name, siglevel); if(db == NULL) { pm_printf(ALPM_LOG_ERROR, _("could not register '%s' database (%s)\n"), section->name, alpm_strerror(alpm_errno(config->handle))); @@ -783,7 +826,7 @@ static int finish_section(struct section_t *section, int parse_options) goto cleanup; } - for(i = section->servers; i; i = alpm_list_next(i)) { + for(i = servers; i; i = alpm_list_next(i)) { char *value = i->data; if(_add_mirror(db, value) != 0) { pm_printf(ALPM_LOG_ERROR, @@ -796,226 +839,62 @@ static int finish_section(struct section_t *section, int parse_options) } cleanup: - alpm_list_free(section->servers); - section->servers = NULL; - section->siglevel = ALPM_SIG_USE_DEFAULT; - free(section->name); - section->name = NULL; + alpm_list_free(servers); return ret; } -/** The "real" parseconfig. Each "Include" directive will recall this method so - * recursion and stack depth are limited to 10 levels. The publicly visible - * parseconfig calls this with a NULL section argument so we can recall from - * within ourself on an include. + +/** Parse a configuration file. * @param file path to the config file - * @param section the current active section - * @param parse_options whether to parse and call methods for the options - * section; if 0, parse and call methods for the repos sections - * @param depth the current recursion depth - * @return 0 on success, 1 on failure + * @return 0 on success, non-zero on error */ -static int _parseconfig(const char *file, struct section_t *section, - int parse_options, int depth) +int parseconfig(const char *file) { - FILE *fp = NULL; - char line[PATH_MAX]; - int linenum = 0; - int ret = 0; - const int max_depth = 10; - - if(depth >= max_depth) { - pm_printf(ALPM_LOG_ERROR, - _("config parsing exceeded max recursion depth of %d.\n"), max_depth); - ret = 1; - goto cleanup; - } + alpm_list_t *sections; + int ret; - pm_printf(ALPM_LOG_DEBUG, "config: attempting to read file %s\n", file); - fp = fopen(file, "r"); - if(fp == NULL) { - pm_printf(ALPM_LOG_ERROR, _("config file %s could not be read: %s\n"), - file, strerror(errno)); - ret = 1; - goto cleanup; + sections = ini_parse_file(file); + if(sections == NULL) { + return 1; } - while(fgets(line, PATH_MAX, fp)) { - char *key, *value, *ptr; - size_t line_len; - - linenum++; - - /* ignore whole line and end of line comments */ - if((ptr = strchr(line, '#'))) { - *ptr = '\0'; - } - - line_len = strtrim(line); - - if(line_len == 0) { - continue; - } - - if(line[0] == '[' && line[line_len - 1] == ']') { - char *name; - /* only possibility here is a line == '[]' */ - if(line_len <= 2) { - pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: bad section name.\n"), - file, linenum); - ret = 1; - goto cleanup; - } - /* new config section, skip the '[' */ - name = strdup(line + 1); - name[line_len - 2] = '\0'; - /* we're at a new section; perform any post-actions for the prior */ - if(finish_section(section, parse_options)) { - ret = 1; - goto cleanup; + /* parse options section */ + pm_printf(ALPM_LOG_DEBUG, "parseconfig: options pass\n"); + alpm_list_t *iter; + for(iter = sections; iter; iter = alpm_list_next(iter)) { + ini_section_t *current = (ini_section_t *) iter->data; + if(strcmp("options", current->name) == 0) { + if((ret = parse_options(current))) { + ini_free(sections); + return ret; } - pm_printf(ALPM_LOG_DEBUG, "config: new section '%s'\n", name); - section->name = name; - section->is_options = (strcmp(name, "options") == 0); - continue; + break; } + } - /* directive */ - /* strsep modifies the 'line' string: 'key \0 value' */ - key = line; - value = line; - strsep(&value, "="); - strtrim(key); - strtrim(value); - - if(key == NULL) { - pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: syntax error in config file- missing key.\n"), - file, linenum); - ret = 1; - goto cleanup; - } - /* For each directive, compare to the camelcase string. */ - if(section->name == NULL) { - pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: All directives must belong to a section.\n"), - file, linenum); - ret = 1; - goto cleanup; - } - /* Include is allowed in both options and repo sections */ - if(strcmp(key, "Include") == 0) { - glob_t globbuf; - int globret; - size_t gindex; + if((ret = setup_libalpm())) { + ini_free(sections); + return ret; + } - if(value == NULL) { - pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"), - file, linenum, key); - ret = 1; - goto cleanup; - } - /* Ignore include failures... assume non-critical */ - globret = glob(value, GLOB_NOCHECK, NULL, &globbuf); - switch(globret) { - case GLOB_NOSPACE: - pm_printf(ALPM_LOG_DEBUG, - "config file %s, line %d: include globbing out of space\n", - file, linenum); - break; - case GLOB_ABORTED: - pm_printf(ALPM_LOG_DEBUG, - "config file %s, line %d: include globbing read error for %s\n", - file, linenum, value); - break; - case GLOB_NOMATCH: - pm_printf(ALPM_LOG_DEBUG, - "config file %s, line %d: no include found for %s\n", - file, linenum, value); - break; - default: - for(gindex = 0; gindex < globbuf.gl_pathc; gindex++) { - pm_printf(ALPM_LOG_DEBUG, "config file %s, line %d: including %s\n", - file, linenum, globbuf.gl_pathv[gindex]); - _parseconfig(globbuf.gl_pathv[gindex], section, parse_options, depth + 1); - } - break; - } - globfree(&globbuf); + /* parse the rest (Repositories) */ + pm_printf(ALPM_LOG_DEBUG, "parseconfig: repo pass\n"); + + for (iter = sections; iter; iter = alpm_list_next(iter)) { + ini_section_t *current = (ini_section_t *) iter->data; + if(strcmp("options", current->name) == 0) { + /* skip options */ continue; } - if(parse_options && section->is_options) { - /* we are either in options ... */ - if((ret = _parse_options(key, value, file, linenum)) != 0) { - goto cleanup; - } - } else if(!parse_options && !section->is_options) { - /* ... or in a repo section */ - if(strcmp(key, "Server") == 0) { - if(value == NULL) { - pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"), - file, linenum, key); - ret = 1; - goto cleanup; - } - section->servers = alpm_list_add(section->servers, strdup(value)); - } else if(strcmp(key, "SigLevel") == 0) { - alpm_list_t *values = NULL; - setrepeatingoption(value, "SigLevel", &values); - if(values) { - if(section->siglevel == ALPM_SIG_USE_DEFAULT) { - section->siglevel = config->siglevel; - } - if(process_siglevel(values, §ion->siglevel, file, linenum)) { - FREELIST(values); - ret = 1; - goto cleanup; - } - FREELIST(values); - } - } else { - pm_printf(ALPM_LOG_WARNING, - _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"), - file, linenum, key, section->name); - } + if((ret = parse_repository(current))) { + ini_free(sections); + return ret; } } - if(depth == 0) { - ret = finish_section(section, parse_options); - } + ini_free(sections); -cleanup: - if(fp) { - fclose(fp); - } - pm_printf(ALPM_LOG_DEBUG, "config: finished parsing %s\n", file); - return ret; -} - -/** Parse a configuration file. - * @param file path to the config file - * @return 0 on success, non-zero on error - */ -int parseconfig(const char *file) -{ - int ret; - struct section_t section; - memset(§ion, 0, sizeof(struct section_t)); - section.siglevel = ALPM_SIG_USE_DEFAULT; - /* the config parse is a two-pass affair. We first parse the entire thing for - * the [options] section so we can get all default and path options set. - * Next, we go back and parse everything but [options]. */ - - /* call the real parseconfig function with a null section & db argument */ - pm_printf(ALPM_LOG_DEBUG, "parseconfig: options pass\n"); - if((ret = _parseconfig(file, §ion, 1, 0))) { - return ret; - } - if((ret = setup_libalpm())) { - return ret; - } - /* second pass, repo section parsing */ - pm_printf(ALPM_LOG_DEBUG, "parseconfig: repo pass\n"); - return _parseconfig(file, §ion, 0, 0); + return 0; } /* vim: set ts=2 sw=2 noet: */ -- 1.8.2