[pacman-dev] [PATCH 2/2] use new ini parsing in pacman/conf.c

Sascha Kruse knopwob at googlemail.com
Fri Mar 15 07:06:27 EDT 2013


Signed-off-by: Sascha Kruse <knopwob at 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, &section->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(&section, 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, &section, 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, &section, 0, 0);
+	return 0;
 }
 
 /* vim: set ts=2 sw=2 noet: */
-- 
1.8.2



More information about the pacman-dev mailing list