diff ini.c @ 96:dcaf2c61c902

Big change in ini, much cleaner and simpler
author David Demelier <markand@malikania.fr>
date Fri, 13 Jan 2012 19:05:16 +0100
parents 145493469aa0
children 0321fc3c0972
line wrap: on
line diff
--- a/ini.c	Thu Jan 12 20:12:48 2012 +0100
+++ b/ini.c	Fri Jan 13 19:05:16 2012 +0100
@@ -16,7 +16,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/queue.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -26,53 +25,20 @@
 
 #include "ini.h"
 
-/* -------------------------------------------------------- */
-/* structure definitions				    */
-/* -------------------------------------------------------- */
-
-struct ini_option {
-	char		*key;		/* option name */
-	char		*value;		/* option value */
-	STAILQ_ENTRY(ini_option) next;
-};
-
-struct ini_section {
-	char		*key;		/* section key */	
-	STAILQ_HEAD(, ini_option) options;
-	STAILQ_ENTRY(ini_section) next;
-};
-
-struct ini_config {
-	const char	*path;		/* file path */
-	int		flags;		/* optional flags */
-	int		ignore;		/* must ignore (no redefine) */
-
-	/* Current section in file */
-	struct ini_section *current;
-
-	/* Current file line properties */
-	char		*line;		/* line buffer */
-	int		lineno;		/* number of line */
-	int		linesize;	/* initial line size */
-
-	/* For querying functions */
-	STAILQ_HEAD(, ini_section) sections;
-};
-
 static char	ini_error[1024 + 1];
 
-/* -------------------------------------------------------- */
-/* prototypes						    */
-/* -------------------------------------------------------- */
+/* --------------------------------------------------------
+ * prototypes
+ * -------------------------------------------------------- */
 
-static void	*ini_read(struct ini_config *, FILE *);
-static int	ini_getline(struct ini_config *, FILE *);
-static void	*ini_readline(struct ini_config *);
-static int	ini_switch(struct ini_config *, char **);
-static int	ini_register(struct ini_config *, char **);
-static void	*ini_fatal(struct ini_config *, FILE *, const char *, ...);
-static char	*xstrndup(const char *, size_t);
-static struct ini_option *xoptiondup(const struct ini_option *option);
+static void		*ini_read(INI_Config *, FILE *);
+static int		ini_getline(INI_Config *, FILE *);
+static void		*ini_readline(INI_Config *);
+static int		ini_switch(INI_Config *, char **);
+static int		ini_register(INI_Config *, char **);
+static void		*ini_fatal(INI_Config *, FILE *, const char *, ...);
+static char		*xstrndup(const char *, size_t);
+static INI_Option	*xoptiondup(const INI_Option *option);
 
 #define I_VERBOSE(file)		((file)->flags & INI_VERBOSE)
 #define I_NOREDEFINE(file)	((file)->flags & INI_NOREDEFINE)
@@ -87,193 +53,104 @@
  * public functions
  * -------------------------------------------------------- */
 
-struct ini_config *
+INI_Config *
 ini_load(const char *path, int flags)
 {
+	INI_Config *conf;	
 	FILE *fp;
-	struct ini_config *conf;	
 
-	if ((conf = calloc(1, sizeof (struct ini_config))) == NULL)
+	if ((conf = calloc(1, sizeof (INI_Config))) == NULL)
 		return ini_fatal(conf, fp, "%s", strerror(errno));
 
 	if ((fp = fopen(path, "r")) == NULL)
 		return ini_fatal(conf, fp, "%s: %s", path, strerror(errno));
 
-	if ((conf->line = calloc(1024 + 1, 1)) == NULL)
+	if ((conf->_line = calloc(1024 + 1, 1)) == NULL)
 		return ini_fatal(conf, fp, "%s", strerror(errno));
 
-	STAILQ_INIT(&conf->sections);
 	conf->path	= path;
 	conf->flags	= flags;
-	conf->ignore	= 1;
-	conf->lineno	= 1;
-	conf->linesize	= 1024;
+	conf->_ignore	= 1;
+	conf->_lineno	= 1;
+	conf->_linesize	= 1024;
 
 	return ini_read(conf, fp);
 }
 
 /*
- * Returns a NULL terminated list of section names. If parameter
- * number is not NULL, number is set to the total number of
- * section correctly parsed.
- */
-
-char **
-ini_get_sections_names(const struct ini_config *conf, int *number)
-{
-	struct ini_section *s;
-	char **list;
-	int i;
-
-	i = 0;
-	STAILQ_FOREACH(s, &conf->sections, next)
-		++ i;
-
-	/* For safety */
-	if (number != NULL)
-		*number = 0;
-
-	if ((list = calloc(i + 1, sizeof (char *))) == NULL)
-		return NULL;
-
-	if (number != NULL)
-		*number = i;
-
-	i = 0;
-	STAILQ_FOREACH(s, &conf->sections, next)
-		list[i++] = s->key;
-
-	return list;
-}
-
-/*
- * Returns a NULL terminated list of struct ini_section that could be used
- * with ini_get_option(). This function makes sense only when the config
- * has multiple definition enabled.
+ * Search the section `sectname' in the config structure. If the section
+ * is not found, this functions returns NULL.
  */
 
-struct ini_section **
-ini_get_sections(const struct ini_config *conf, const char *key, int *nb)
+INI_Section *
+ini_select_section(const INI_Config *conf, const char *sectname)
 {
-	struct ini_section **list;
-	struct ini_section *s;
-	int i;
-
-	/* Do not use this function on simple config */
-	if (I_NOREDEFINE(conf)) {
-		errno = EINVAL;
-		return NULL;
-	}
-
-	i = 0;
-	STAILQ_FOREACH(s, &conf->sections, next)
-		if (strcmp(s->key, key) == 0)
-			++ i;
-
-	if ((list = calloc(i + 1, sizeof (struct ini_section *))) == NULL)
-		return NULL;
+	INI_Section *s;
 
-	if (nb != NULL)
-		*nb = i;
-
-	i = 0;
-	STAILQ_FOREACH(s, &conf->sections, next)
-		if (strcmp(s->key, key) == 0)
-			list[i++] = s;
-
-	return list;
-}
-
-/*
- * Select the section for further query. This improves performance as it does
- * not need to seek each time the section. It can't be used when multiple
- * definition is enabled because it seek from the beginning, you should use
- * ini_get_sections() and then ini_get_option() for each section.
- */
-
-struct ini_section *
-ini_select_section(const struct ini_config *conf, const char *section)
-{
-	struct ini_section *s;
-
-	STAILQ_FOREACH(s, &conf->sections, next)
-		if (strcmp(s->key, section) == 0)
+	for (s = conf->sections; s != NULL; s = s->next)
+		if (strcmp(s->key, sectname) == 0)
 			return s;
 
 	return NULL;
 }
 
 /*
- * Wrapper for a simpler usage of ini_select and ini_option. This
- * does not modify the selector so you can safely continue the
- * call to ini_option independantly. Warning, this function returns the
- * first occurence of the option so it is not advised to use it 
+ * Search the option `optname' in the section structure, argument
+ * section musn't be NULL. If the option is not found, this function
+ * returns NULL.
  */
 
-char *
-ini_get_option_once(const struct ini_config *conf, const char *sect, const char *key)
+INI_Option *
+ini_select_option(const INI_Section *section, const char *optname)
 {
-	struct ini_option *o;
-	struct ini_section *s;
+	INI_Option *o;
 
-	STAILQ_FOREACH(s, &conf->sections, next)
-		if (strcmp(s->key, sect) == 0)
-			break;
-
-	STAILQ_FOREACH(o, &s->options, next)
-		if (strcmp(o->key, key) == 0)
-			return o->value;
+	for (o = section->options; o != NULL; o = o->next)
+		if (strcmp(o->key, optname) == 0)
+			return o;
 
 	return NULL;
 }
 
 /*
- * Return a NULL list of all available options in the section.
- */
-
-char **
-ini_get_option_names(const struct ini_section *section, int *nb)
-{
-	char **list;
-	struct ini_option *o;
-	int i;
-
-	i = 0;
-	STAILQ_FOREACH(o, &section->options, next)
-		++ i;
-
-	if (nb != NULL)
-		*nb = i;
-
-	if ((list = calloc(i + 1, sizeof (char *))) == NULL)
-		return NULL;
-
-	if (nb != NULL)
-		*nb = i;
-
-	i = 0;
-	STAILQ_FOREACH(o, &section->options, next)
-		list[i++] = o->key;
-
-	return list;
-}
-
-/*
- * Get the option in the current selected option. Note, you must call
- * ini_select() before using this function. Returns the value of
- * key or NULL if the option does not exists.
+ * Return the last error or "No error" if there is not error at all.
  */
 
 char *
-ini_get_option(const struct ini_section *section, const char *key)
+ini_get_error(void)
 {
-	struct ini_option *o;
+	if (ini_error[0] == '\0')
+		return "No error";
+
+	return ini_error;
+}
+
+void
+ini_free(INI_Config *conf, int freeSections, int freeOptions)
+{
+	INI_Section *s, *snext;
+	INI_Option *o, *onext;
+
+	for (s = snext = conf->sections; snext != NULL; s = snext) {
+		snext = s->next;
 
-	STAILQ_FOREACH(o, &section->options, next)
-		if (strcmp(o->key, key) == 0)
-			return o->value;
+		for (o = onext = s->options; o != NULL; o = onext) {
+			onext = o->next;
 
-	return NULL;
+			if (freeOptions) {
+				free(o->key);
+				free(o->value);
+				free(o);
+			}
+		}
+
+		if (freeSections)
+			free(s->key);
+
+		free(s);
+	}
+
+	free(conf);
 }
 
 /*
@@ -283,27 +160,17 @@
  */
 
 void
-ini_value_dispatch(struct ini_config *config, struct ini_handler *hdrs, int length)
+ini_dispatch(INI_Section *section, INI_Handler *hdrs, int length)
 {
-	const char *sectionName;
+	INI_Option *option;
 	int i;
-	char *value;
-	struct ini_section *section;
 
-	sectionName = NULL;
 	for (i = 0; i < length; ++i) {
-		/* Do not select the same section for performance. */
-		if (sectionName == NULL || strcmp(hdrs[i].section, sectionName) != 0) {
-			sectionName	= hdrs[i].section;
-			section		= ini_select_section(config, hdrs[i].section);
-		}
+		option = ini_select_option(section, hdrs[i].key);
 
-		/* Skip the section if does not exists in the config */
-		if (section == NULL)
-			continue;
-
-		value = ini_get_option(section, hdrs[i].option);
-		hdrs[i].handler(hdrs[i].dst, value, hdrs[i].userdata);
+		if (option != NULL)
+			hdrs[i].handler(hdrs[i].dst, option->value,
+			    hdrs[i].userdata);
 	}
 }
 
@@ -318,9 +185,9 @@
 	if (strcmp(value, "yes") == 0 ||
 	    strcmp(value, "true") == 0 ||
 	    strcmp(value, "1") == 0)
-		*p = 1;
+		*p = (char)1;
 	else
-		*p = 0;
+		*p = (char)0;
 	(void)dummy;
 }
 
@@ -361,46 +228,6 @@
 	(void)dummy;
 }
 
-/*
- * Return the last error or "No error" if there is not error at all.
- */
-
-char *
-ini_get_error(void)
-{
-	if (ini_error[0] == '\0')
-		return "No error";
-
-	return ini_error;
-}
-
-void
-ini_free(struct ini_config *conf, int freeSections, int freeOptions)
-{
-	struct ini_section *s, *stmp;
-	struct ini_option *o, *otmp;
-
-	STAILQ_FOREACH_SAFE(s, &conf->sections, next, stmp) {
-		STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) {
-			if (freeOptions) {
-				free(o->key);
-				free(o->value);
-
-				STAILQ_REMOVE(&s->options, o, ini_option, next);
-				free(o);
-			}
-		}
-
-		if (freeSections)
-			free(s->key);
-
-		STAILQ_REMOVE(&conf->sections, s, ini_section, next);
-		free(s);
-	}
-
-	free(conf);
-}
-
 /* --------------------------------------------------------
  * private functions
  * -------------------------------------------------------- */
@@ -412,22 +239,22 @@
  */
 
 static void *
-ini_read(struct ini_config *conf, FILE *fp)
+ini_read(INI_Config *conf, FILE *fp)
 {
 	while (ini_getline(conf, fp) == 0) {
 		if (ini_readline(conf) == NULL) {
-			free(conf->line);
+			free(conf->_line);
 			ini_free(conf, 1, 1);
 
 			fclose(fp);
 			return NULL;
 		}
 
-		++ conf->lineno;
+		++ conf->_lineno;
 	}
 
 	/* Clean up */
-	free(conf->line);
+	free(conf->_line);
 	fclose(fp);
 
 	return conf;
@@ -440,11 +267,11 @@
  */
 
 static int
-ini_getline(struct ini_config *conf, FILE *fp)
+ini_getline(INI_Config *conf, FILE *fp)
 {
 	int ch, pos;
 
-	memset(conf->line, 0, conf->linesize);
+	memset(conf->_line, 0, conf->_linesize);
 	pos = 0;
 
 	while ((ch = fgetc(fp)) != '\n') {
@@ -452,18 +279,18 @@
 			return -1;
 
 		/* End of buffer, realloc */
-		if (pos == conf->linesize) {
-			conf->line = realloc(conf->line, conf->linesize + 513);
-			if (conf->line == NULL)
+		if (pos == conf->_linesize) {
+			conf->_line = realloc(conf->_line, conf->_linesize + 513);
+			if (conf->_line == NULL)
 				return -1;
 
-			conf->linesize += 512;
+			conf->_linesize += 512;
 		}
 
-		conf->line[pos++] = ch;
+		conf->_line[pos++] = ch;
 	}
 
-	conf->line[pos] = '\0';
+	conf->_line[pos] = '\0';
 
 	return 0;
 }
@@ -475,12 +302,12 @@
  */
 
 static void *
-ini_readline(struct ini_config *conf)
+ini_readline(INI_Config *conf)
 {
 	char *lp;
-	int (*handler)(struct ini_config *, char **);
+	int (*handler)(INI_Config *, char **);
 
-	lp = conf->line;
+	lp = conf->_line;
 	SKIP_SPACES(lp);
 
 	/* Ignore empty line or comment */
@@ -495,12 +322,12 @@
 			return conf;
 
 #ifdef INI_DEBUG
-		printf("-- line[%d] == [%s]\n", conf->lineno, conf->line);
+		printf("-- line[%d] == [%s]\n", conf->_lineno, conf->_line);
 #endif
 
 		if (*lp == '[')
 			handler = &ini_switch;
-		else if (!conf->ignore)
+		else if (!conf->_ignore)
 			handler = &ini_register;
 		else {
 			handler = NULL;
@@ -522,10 +349,6 @@
 	for (; !isspace(**lp) && **lp != '\0'; ++(*lp))			\
 		continue;
 
-/* On other failure seek until the next line '\0' */
-#define SEEK_NL(lp)							\
-	for (; !isspace(**lp) && **lp != '\0'; ++(*lp))
-
 /*
  * Parse the new section (function called when '[' is found). Returns
  * 0 or parse error (to avoid ini_readling from stopping) or -1 on
@@ -533,45 +356,50 @@
  */
 
 static int
-ini_switch(struct ini_config *conf, char **lp)
+ini_switch(INI_Config *conf, char **lp)
 {
 	char *endSection;
 
 	/* Section not parsed, ignore next option to prevent breakage */
 	if ((endSection = strchr(*lp, ']')) == NULL) {
-		WARN(conf, "line %d: parse error\n", conf->lineno);
+		WARN(conf, "line %d: parse error\n", conf->_lineno);
 		SEEK_NEXT(lp);
-		return (conf->ignore = 1) - 2;		/* single line rocks */
+		return (conf->_ignore = 1) - 2;		/* single line rocks */
 	}
 
-	/* Redefinition of previous and not allowed */
+	/* Redefinition of previous and not allowed? */
 	++(*lp);
-	if (conf->current != NULL && I_NOREDEFINE(conf) &&
-	    (strncmp(conf->current->key, *lp, endSection - *lp)) == 0) {
-		WARN(conf, "line %d: redefining %s\n", conf->lineno, conf->current->key);
+	if (conf->_current != NULL
+	    && I_NOREDEFINE(conf)
+	    && strncmp(conf->_current->key, *lp, endSection - *lp) == 0)
+	{
+		WARN(conf, "line %d: redefining %s\n", conf->_lineno,
+		    conf->_current->key);
 		SEEK_NEXT(lp);
-		return (conf->ignore = 1) - 2;
+		return (conf->_ignore = 1) - 2;
 	}
 
-	if ((conf->current = malloc(sizeof (struct ini_section))) == NULL)
+	if ((conf->_current = malloc(sizeof (INI_Section))) == NULL)
 		return -1;
 
-	if ((conf->current->key = xstrndup(*lp, endSection - *lp)) == NULL) {
-		free(conf->current);
+	memset(conf->_current, 0, sizeof (INI_Section));
+	if ((conf->_current->key = xstrndup(*lp, endSection - *lp)) == NULL) {
+		free(conf->_current);
+		conf->_current = NULL;
 		return -1;
 	}
 
 	*lp = endSection + 1;
 
 #ifdef INI_DEBUG
-	printf("-- current section is now [%s]\n", conf->current->key);
+	printf("-- current section is now [%s]\n", conf->_current->key);
 #endif
 
 	/* Finally add the new section to the config */
-	STAILQ_INIT(&conf->current->options);
-	STAILQ_INSERT_TAIL(&conf->sections, conf->current, next);
+	conf->_current->next	= conf->sections;
+	conf->sections		= conf->_current;
 
-	return (conf->ignore = 0);
+	return (conf->_ignore = 0);
 }
 
 /*
@@ -581,15 +409,15 @@
  */
 
 static int
-ini_register(struct ini_config *conf, char **lp)
+ini_register(INI_Config *conf, char **lp)
 {
+	INI_Option *option, tmp;
 	char *assignKey, *endKey, *endValue, token = '\0';
 	int length = 0;
-	struct ini_option *option, tmp;
 
 	/* No '=' found */
 	if ((assignKey = strchr(*lp, '=')) == NULL) {
-		WARN(conf, "line %d: missing '='\n", conf->lineno);
+		WARN(conf, "line %d: missing '='\n", conf->_lineno);
 		SEEK_NEXT(lp);
 		return -1;
 	}
@@ -601,12 +429,12 @@
 
 	/* Do not register empty option name */
 	if (endKey - *lp <= 0) {
-		WARN(conf, "line %d: 0-length option\n", conf->lineno);
-		SEEK_NL(lp);
+		WARN(conf, "line %d: 0-length option\n", conf->_lineno);
+		SEEK_NEXT(lp);
 		return -1;
 	}
 
-	memset(&tmp, 0, sizeof (struct ini_option));
+	memset(&tmp, 0, sizeof (INI_Option));
 	if ((tmp.key = xstrndup(*lp, endKey - *lp)) == NULL)
 		return -1;
 
@@ -626,7 +454,7 @@
 		if (token != '\0' && *endValue == token)
 			++ endValue;
 		else
-			WARN(conf, "line %d: missing '%c'\n", conf->lineno, token);
+			WARN(conf, "line %d: missing '%c'\n", conf->_lineno, token);
 	} else {
 		for (*lp = endValue; !isspace(*endValue) && *endValue != '\0' && 
 		    *endValue != '#' && *endValue != ';'; ++endValue)
@@ -647,37 +475,22 @@
 
 	/* Finally add the new option to the current section */
 	*lp = endValue;
-	STAILQ_INSERT_TAIL(&conf->current->options, option, next);
+	option->next = conf->_current->options;
+	conf->_current->options	= option;
 
 	return 0;
 }
 
 static void *
-ini_fatal(struct ini_config *conf, FILE *fp, const char *fmt, ...)
+ini_fatal(INI_Config *conf, FILE *fp, const char *fmt, ...)
 {
 	va_list ap;
 
 	if (conf != NULL) {
-		struct ini_section *s, *stmp;
-		struct ini_option *o, *otmp;
-
-		if (conf->line != NULL)
-			free(conf->line);
+		if (conf->_line != NULL)
+			free(conf->_line);
 
-		STAILQ_FOREACH_SAFE(s, &conf->sections, next, stmp) {
-			STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) {
-				free(o->key);
-				free(o->value);
-
-				STAILQ_REMOVE(&s->options, o, ini_option, next);
-
-				free(o);
-			}
-
-			free(s->key);
-			free(s);
-		}
-
+		ini_free(conf, 1, 1);
 	}
 
 	if (fp != NULL)
@@ -690,8 +503,6 @@
 	/* Directly print error if VERBOSE is enabled */
 	WARN(conf, "%s\n", ini_error);
 
-	free(conf);
-
 	return NULL;
 }
 
@@ -713,10 +524,10 @@
 	return res;
 }
 
-static struct ini_option *
-xoptiondup(const struct ini_option *option)
+static INI_Option *
+xoptiondup(const INI_Option *option)
 {
-	struct ini_option *res;
+	INI_Option *res;
 
 	/*
 	 * Value may be NULL but not option's key.
@@ -728,11 +539,10 @@
 		return NULL;
 	}
 
-	if ((res = malloc(sizeof (struct ini_option))) == NULL)
+	if ((res = malloc(sizeof (INI_Option))) == NULL)
 		return NULL;
 
-	res->key	= option->key;
-	res->value	= option->value;
+	memcpy(res, option, sizeof (INI_Option));
 
 	return res;
 }