changeset 79:fd817a7dbf2f

Switched a lot of things in ini
author David Demelier <markand@malikania.fr>
date Tue, 15 Nov 2011 20:12:48 +0100
parents ad60e5ebd92c
children 820426789f17 f42bcb9e7b4a
files ini.c ini.h inifile.c inifile.h
diffstat 4 files changed, 706 insertions(+), 636 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ini.c	Tue Nov 15 20:12:48 2011 +0100
@@ -0,0 +1,642 @@
+/*
+ * ini.c -- parse .ini like files
+ *
+ * Copyright (c) 2011, David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#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	iniError[1024 + 1];
+
+/* --------------------------------------------------------
+ * 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);
+
+#define I_VERBOSE(file)		((file)->flags & INI_VERBOSE)
+#define I_NOREDEFINE(file)	((file)->flags & INI_NOREDEFINE)
+#define I_FAILERROR(file)	((file)->flags & INI_FAILERROR)
+
+#define SKIP_SPACES(lp)		while (isspace(*lp) && *lp != '\0') ++lp
+#define WARN(file, fmt, ...)						\
+	if (I_VERBOSE((file)))						\
+		fprintf(stderr, fmt, __VA_ARGS__)
+
+/* --------------------------------------------------------
+ * public functions
+ * -------------------------------------------------------- */
+
+struct ini_config *
+ini_load(const char *path, int flags)
+{
+	FILE *fp;
+	struct ini_config *conf;	
+
+	if ((conf = calloc(1, sizeof (struct 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)
+		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;
+
+	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(struct ini_config *conf, int *number)
+{
+	struct ini_section *s;
+	char **list;
+	int i;
+
+	i = 0;
+	STAILQ_FOREACH(s, &conf->sections, next)
+		++ i;
+
+	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.
+ */
+
+struct ini_section **
+ini_get_sections(const struct ini_config *conf, const char *key, int *nb)
+{
+	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;
+
+	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(struct ini_config *conf, const char *section)
+{
+	struct ini_section *s;
+
+	STAILQ_FOREACH(s, &conf->sections, next)
+		if (strcmp(s->key, section) == 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 
+ */
+
+char *
+ini_option_once(struct ini_config *conf, const char *sect, const char *key)
+{
+	struct ini_option *o;
+	struct ini_section *s;
+
+	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;
+
+	return NULL;
+}
+
+/*
+ * Return a NULL list of all available options in the section.
+ */
+
+char **
+ini_get_option_names(struct ini_section *section, int *nb)
+{
+	char **list;
+	struct ini_option *o;
+	int i;
+
+	i = 0;
+	STAILQ_FOREACH(o, &section->options, next)
+		++ 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.
+ */
+
+char *
+ini_get_option(struct ini_section *section, const char *key)
+{
+	struct ini_option *o;
+
+	STAILQ_FOREACH(o, &section->options, next)
+		if (strcmp(o->key, key) == 0)
+			return o->value;
+
+	return NULL;
+}
+
+/*
+ * Return the last error or "No error" if there is not error at all.
+ */
+
+char *
+ini_error(void)
+{
+	if (iniError[0] == '\0')
+		return "No error";
+
+	return iniError;
+}
+
+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
+ * -------------------------------------------------------- */
+
+/*
+ * Read file line per line and try to parse ini like file. Lines
+ * starting with # or empty line are ignored. Returns the file
+ * on success and NULL on failure.
+ */
+
+static void *
+ini_read(struct ini_config *conf, FILE *fp)
+{
+	while (ini_getline(conf, fp) == 0) {
+		if (ini_readline(conf) == NULL) {
+			free(conf->line);
+			ini_free(conf, 1, 1);
+
+			fclose(fp);
+			return NULL;
+		}
+
+		++ conf->lineno;
+	}
+
+	/* Clean up */
+	free(conf->line);
+	fclose(fp);
+
+	return conf;
+}
+
+/*
+ * Read the next line until the next '\n' character is found. Returns 0
+ * if the system had enough memory or -1 on allocation failure or on
+ * end of file.
+ */
+
+static int
+ini_getline(struct ini_config *conf, FILE *fp)
+{
+	int ch, pos;
+
+	memset(conf->line, 0, conf->linesize);
+	pos = 0;
+
+	while ((ch = fgetc(fp)) != '\n') {
+		if (feof(fp) || ferror(fp))
+			return -1;
+
+		/* End of buffer, realloc */
+		if (pos == conf->linesize) {
+			conf->line = realloc(conf->line, conf->linesize + 513);
+			if (conf->line == NULL)
+				return -1;
+
+			conf->linesize += 512;
+		}
+
+		conf->line[pos++] = ch;
+	}
+
+	conf->line[pos] = '\0';
+
+	return 0;
+}
+
+/*
+ * Read the next line that have been successfully read. Returns NULL
+ * on allocation failure (instanciation of new section or option)
+ * or the file on success.
+ */
+
+static void *
+ini_readline(struct ini_config *conf)
+{
+	char *lp;
+	int (*handler)(struct ini_config *, char **);
+
+	lp = conf->line;
+	SKIP_SPACES(lp);
+
+	/* Ignore empty line or comment */
+	if (*lp == '\0' || *lp == '#' || *lp == ';')
+		return conf;
+
+	while (*lp != '\0') {
+		SKIP_SPACES(lp);
+
+		/* Skip comments again and empty lines */
+		if (*lp == '\0' || *lp == '#' || *lp == ';')
+			return conf;
+
+#ifdef INI_DEBUG
+		printf("-- line[%d] == [%s]\n", conf->lineno, conf->line);
+#endif
+
+		if (*lp == '[')
+			handler = &ini_switch;
+		else if (!conf->ignore)
+			handler = &ini_register;
+		else
+			handler = NULL;
+
+		/* Success or not? */
+		if (handler != NULL && handler(conf, &lp) < 0)
+			return (I_FAILERROR(conf)) ? NULL : conf;
+
+		SKIP_SPACES(lp);
+	}
+
+	return conf;
+}
+
+/* On failure, seek next space */
+#define SEEK_NEXT(lp)							\
+	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
+ * allocation failure.
+ */
+
+static int
+ini_switch(struct 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);
+		SEEK_NEXT(lp);
+		return (conf->ignore = 1) - 2;		/* single line rocks */
+	}
+
+	/* 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);
+		SEEK_NEXT(lp);
+		return (conf->ignore = 1) - 2;
+	}
+
+	if ((conf->current = malloc(sizeof (struct ini_section))) == NULL)
+		return -1;
+
+	if ((conf->current->key = xstrndup(*lp, endSection - *lp)) == NULL) {
+		free(conf->current);
+		return -1;
+	}
+
+	*lp = endSection + 1;
+
+#ifdef INI_DEBUG
+	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);
+
+	return (conf->ignore = 0);
+}
+
+/*
+ * Store the new option that have been parsed. Returns 0 on success or
+ * on parse error (to avoid ini_readline from stopping) and -1 on
+ * allocation failure.
+ */
+
+static int
+ini_register(struct ini_config *conf, char **lp)
+{
+	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);
+		SEEK_NEXT(lp);
+		return -1;
+	}
+
+	/* Find end of option */
+	endKey = *lp;
+	for (; !isspace(*endKey) && *endKey != '\0' && *endKey != '='; ++endKey)
+		continue;
+
+	/* Do not register empty option name */
+	if (endKey - *lp <= 0) {
+		WARN(conf, "line %d: 0-length option\n", conf->lineno);
+		SEEK_NL(lp);
+		return -1;
+	}
+
+	memset(&tmp, 0, sizeof (struct ini_option));
+	if ((tmp.key = xstrndup(*lp, endKey - *lp)) == NULL)
+		return -1;
+
+	endValue = &assignKey[1];
+	SKIP_SPACES(endValue);
+
+	/* Find end of option value */
+	token = *endValue;
+	if (token == '\'' || token == '"') {
+		for (*lp = ++endValue; *endValue != token && *endValue != '\0'; ++endValue)
+			continue;
+
+		length = endValue - *lp;
+
+		/* Correctly closed */	
+		if (token != '\0' && *endValue == token)
+			++ endValue;
+		else
+			WARN(conf, "line %d: missing '%c'\n", conf->lineno, token);
+	} else {
+		for (*lp = endValue; !isspace(*endValue) && *endValue != '\0'; ++endValue)
+			continue;
+
+		length = endValue - *lp;
+	}
+
+	if (length != 0)
+		tmp.value = xstrndup(*lp, length);
+
+	if ((option = xoptiondup(&tmp)) == NULL)
+		return -1;
+
+#ifdef INI_DEBUG
+	printf("-- next option [%s] is set to [%s]\n", tmp.key, tmp.value);
+#endif
+
+	/* Finally add the new option to the current section */
+	*lp = endValue;
+	STAILQ_INSERT_TAIL(&conf->current->options, option, next);
+
+	return 0;
+}
+
+static void *
+ini_fatal(struct 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);
+
+		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);
+		}
+
+	}
+
+	if (fp != NULL)
+		fclose(fp);
+
+	va_start(ap, fmt);
+	vsnprintf(iniError, 1024, fmt, ap);
+	va_end(ap);
+
+	/* Directly print error if VERBOSE is enabled */
+	WARN(conf, "%s\n", iniError);
+
+	free(conf);
+
+	return NULL;
+}
+
+static char *
+xstrndup(const char *src, size_t max)
+{
+	char *res;
+	size_t length;
+
+	for (length = 0; length < max && src[length] != '\0'; ++length)
+		continue;
+
+	if ((res = malloc(length + 1)) == NULL)
+		return NULL;
+
+	memcpy(res, src, length);
+	res[length] = '\0';
+
+	return res;
+}
+
+static struct ini_option *
+xoptiondup(const struct ini_option *option)
+{
+	struct ini_option *res;
+
+	/*
+	 * Value may be NULL but not option's key.
+	 */
+	if (option->key == NULL) {
+		if (option->value != NULL)
+			free(option->value);
+
+		return NULL;
+	}
+
+	if ((res = malloc(sizeof (struct ini_option))) == NULL)
+		return NULL;
+
+	res->key	= option->key;
+	res->value	= option->value;
+
+	return res;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ini.h	Tue Nov 15 20:12:48 2011 +0100
@@ -0,0 +1,64 @@
+/*
+ * ini.h -- parse .ini like files
+ *
+ * Copyright (c) 2011, David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _INI_H_
+#define _INI_H_
+
+#define	INI_VERBOSE	(1 << 0)	/* be verbose */
+#define	INI_NOREDEFINE	(1 << 1)	/* do not allow redefinitions */
+#define INI_FAILERROR	(1 << 2)	/* abort parsing on first error */
+
+struct ini_config;
+struct ini_section;
+
+/* --------------------------------------------------------
+ * struct ini_config functions.
+ * -------------------------------------------------------- */
+
+struct ini_config *
+ini_load(const char *, int);
+
+char **
+ini_get_sections_names(struct ini_config *, int *);
+
+struct ini_section **
+ini_get_sections(const struct ini_config *, const char *, int *);
+
+struct ini_section *
+ini_select_section(struct ini_config *, const char *);
+
+char *
+ini_get_option_once(struct ini_config *, const char *, const char *);
+
+void
+ini_free(struct ini_config *, int, int);
+
+char *
+ini_error(void);
+
+/* --------------------------------------------------------
+ * struct ini_section functions.
+ * -------------------------------------------------------- */
+
+char **
+ini_get_option_names(struct ini_section *, int *);
+
+char *
+ini_get_option(struct ini_section *, const char *key);
+
+#endif /* _INI_H_ */
--- a/inifile.c	Mon Nov 14 19:57:53 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,601 +0,0 @@
-/*
- * inifile.c -- parse .ini like files
- *
- * Copyright (c) 2011, David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/queue.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include "inifile.h"
-
-/* --------------------------------------------------------
- * structure definitions
- * -------------------------------------------------------- */
-
-struct option {
-	char		*key;		/* option name */
-	char		*value;		/* option value */
-	STAILQ_ENTRY(option) next;
-};
-
-typedef STAILQ_HEAD(, option) OptionList;
-
-struct section {
-	char		*key;		/* section key */	
-	OptionList	options;	/* list of options */
-	STAILQ_ENTRY(section) next;
-};
-
-typedef STAILQ_HEAD(, section) SectionList;
-
-struct inifile {
-	const char	*path;		/* file path */
-	SectionList	sections;	/* list of sections */
-	struct section	*current;	/* current section */
-	int		flags;		/* inifile_options */
-	int		ignore;		/* must ignore (no redefine) */
-
-	/* Current file line properties */
-	char		*line;		/* line buffer */
-	int		lineno;		/* number of line */
-	int		linesize;	/* initial line size */
-
-	/* For querying functions */
-	struct section	*q_section;	/* current section query */
-};
-
-static char	inifileError[1024 + 1];
-
-/* --------------------------------------------------------
- * prototypes
- * -------------------------------------------------------- */
-
-static void	*inifile_read(struct inifile *, FILE *);
-static int	inifile_getline(struct inifile *, FILE *);
-static void	*inifile_readline(struct inifile *);
-static int	inifile_switch(struct inifile *, char **);
-static int	inifile_register(struct inifile *, char **);
-static void	*inifile_fatal(struct inifile *, FILE *, const char *, ...);
-static char	*xstrndup(const char *, size_t);
-static struct option *xoptiondup(const struct option *option);
-
-#define I_VERBOSE(file)		((file)->flags & INIFILE_VERBOSE)
-#define I_NOREDEFINE(file)	((file)->flags & INIFILE_NOREDEFINE)
-
-#define SKIP_SPACES(lp)		while (isspace(*lp) && *lp != '\0') ++lp
-#define NFREE(p)		if ((p) != NULL) free((p))
-#define WARN(file, fmt, ...)						\
-	if (I_VERBOSE((file)))						\
-		fprintf(stderr, fmt, __VA_ARGS__)
-
-/* --------------------------------------------------------
- * public functions
- * -------------------------------------------------------- */
-
-struct inifile *
-inifile_load(const char *path, int flags)
-{
-	FILE *fp;
-	struct inifile *file;	
-
-	if ((file = calloc(1, sizeof (struct inifile))) == NULL)
-		return inifile_fatal(file, fp, "%s", strerror(errno));
-
-	if ((fp = fopen(path, "r")) == NULL)
-		return inifile_fatal(file, fp, "%s: %s", path, strerror(errno));
-
-	if ((file->line = calloc(1024 + 1, 1)) == NULL)
-		return inifile_fatal(file, fp, "%s", strerror(errno));
-
-	STAILQ_INIT(&file->sections);
-	file->path	= path;
-	file->flags	= flags;
-	file->ignore	= 1;
-	file->lineno	= 1;
-	file->linesize	= 1024;
-
-	return inifile_read(file, fp);
-}
-
-/*
- * Return 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 **
-inifile_sections(struct inifile *file, int *number)
-{
-	char **list;
-	int i;
-	struct section *s;
-
-	i = 0;
-	STAILQ_FOREACH(s, &file->sections, next)
-		++i;
-
-	if (number != NULL)
-		*number = i;
-
-	if ((list = calloc(i + 1, sizeof (char *))) == NULL)
-		return NULL;
-
-	i = 0;
-	STAILQ_FOREACH(s, &file->sections, next)
-		list[i] = s->key;
-
-	return list;
-}
-
-/*
- * Select the section for further query. This improves performance as it does
- * not need to seek each time the section. If there are multiple redefinitions
- * of section then the function seek the next redefined section.
- */
-
-int
-inifile_select(struct inifile *file, const char *section)
-{
-	struct section *s;
-	int found = -1;
-
-	/*
-	 * If multiple redefinition is allowed, seek the next
-	 * section from this first one.
-	 */
-	if (!I_NOREDEFINE(file) && (s = file->q_section) != NULL) {
-		/*
-		 * But if the user want to find an other section we
-		 * need to start from beginning.
-		 */
-		if (strcmp(s->key, section) == 0)
-			s = STAILQ_NEXT(s, next);
-		else
-			s = STAILQ_FIRST(&file->sections);
-	} else
-		s = STAILQ_FIRST(&file->sections);
-
-	while (s != NULL) {
-		if (strcmp(s->key, section) == 0) {
-			file->q_section = s;
-			found = 1;
-			break;
-		}
-
-		s = STAILQ_NEXT(s, next);
-	}
-
-#ifdef INIFILE_DEBUG
-	if (found == 1)
-		printf("-- selected section [%s]\n", s->key);
-#endif
-
-	if (found < 0)
-		file->q_section = NULL;
-
-	return found;
-}
-
-/*
- * Get the option in the current selected option. Note, you must call
- * inifile_select() before using this function. Returns the value of
- * key or NULL if the option does not exists.
- */
-
-char *
-inifile_option(struct inifile *file, const char *key)
-{
-	struct option *o;
-
-	STAILQ_FOREACH(o, &file->q_section->options, next)
-		if (strcmp(o->key, key) == 0)
-			return o->value;
-
-	return NULL;
-}
-
-/*
- * Wrapper for a simpler usage of inifile_select and inifile_option. This
- * does not modify the selector so you can safely continue the
- * call to inifile_option independantly. Warning, this function returns the
- * first occurence of the option so it is not advised to use it 
- */
-
-char *
-inifile_option_once(struct inifile *file, const char *sect, const char *key)
-{
-	struct option *o;
-	struct section *s;
-
-	STAILQ_FOREACH(s, &file->sections, next)
-		if (strcmp(s->key, sect) == 0)
-			break;
-
-	STAILQ_FOREACH(o, &s->options, next)
-		if (strcmp(o->key, key) == 0)
-			return o->value;
-
-	return NULL;
-}
-
-/*
- * Return the last error or "No error" if there is not error at all.
- */
-
-char *
-inifile_error(void)
-{
-	if (inifileError[0] == '\0')
-		return "No error";
-
-	return inifileError;
-}
-
-void
-inifile_free(struct inifile *file, int freeSections, int freeOptions)
-{
-	struct section *s, *stmp;
-	struct option *o, *otmp;
-
-	STAILQ_FOREACH_SAFE(s, &file->sections, next, stmp) {
-		STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) {
-			if (freeOptions) {
-				free(o->key);
-				if (o->value)
-					free(o->value);
-
-				STAILQ_REMOVE(&s->options, o, option, next);
-				free(o);
-			}
-		}
-
-		if (freeSections)
-			free(s->key);
-
-		STAILQ_REMOVE(&file->sections, s, section, next);
-		free(s);
-	}
-
-	free(file);
-}
-
-/* --------------------------------------------------------
- * private functions
- * -------------------------------------------------------- */
-
-/*
- * Read file line per line and try to parse ini like file. Lines
- * starting with # or empty line are ignored. Returns the file
- * on success and NULL on failure.
- */
-
-static void *
-inifile_read(struct inifile *file, FILE *fp)
-{
-	while (inifile_getline(file, fp) == 0) {
-		if (inifile_readline(file) == NULL)
-			return inifile_fatal(file, fp, "%s", strerror(errno));
-
-		++ file->lineno;
-	}
-
-	/* Clean up */
-	free(file->line);
-	fclose(fp);
-
-	return file;
-}
-
-/*
- * Read the next line until the next '\n' character is found. Returns 0
- * if the system had enough memory or -1 on allocation failure or on
- * end of file.
- */
-
-static int
-inifile_getline(struct inifile *file, FILE *fp)
-{
-	int ch, pos;
-
-	memset(file->line, 0, file->linesize);
-	pos = 0;
-
-	while ((ch = fgetc(fp)) != '\n') {
-		if (feof(fp) || ferror(fp))
-			return -1;
-
-		/* End of buffer, realloc */
-		if (pos == file->linesize) {
-			file->line = realloc(file->line, file->linesize + 513);
-			if (file->line == NULL)
-				return -1;
-
-			file->linesize += 512;
-		}
-
-		file->line[pos++] = ch;
-	}
-
-	file->line[pos] = '\0';
-
-	return 0;
-}
-
-/*
- * Read the next line that have been successfully read. Returns NULL
- * on allocation failure (instanciation of new section or option)
- * or the file on success.
- */
-
-static void *
-inifile_readline(struct inifile *file)
-{
-	char *lp;
-	int (*handler)(struct inifile *, char **);
-
-	lp = file->line;
-	SKIP_SPACES(lp);
-
-	/* Ignore empty line or comment */
-	if (*lp == '\0' || *lp == '#' || *lp == ';')
-		return file;
-
-	while (*lp != '\0') {
-		SKIP_SPACES(lp);
-
-		/* Skip comments again and empty lines */
-		if (*lp == '\0' || *lp == '#' || *lp == ';')
-			return file;
-
-#ifdef INIFILE_DEBUG
-		printf("-- line[%d] == [%s]\n", file->lineno, file->line);
-#endif
-
-		if (*lp == '[')
-			handler = &inifile_switch;
-		else if (!file->ignore)
-			handler = &inifile_register;
-
-		/* Success or not? */
-		if (handler(file, &lp) < 0)
-			return NULL;
-
-		SKIP_SPACES(lp);
-	}
-	return file;
-}
-
-/* On failure, seek next space */
-#define SEEK_NEXT(lp)							\
-	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 inifile_readling from stopping) or -1 on
- * allocation failure.
- */
-
-static int
-inifile_switch(struct inifile *file, char **lp)
-{
-	char *endSection;
-
-	/* Section not parsed, ignore next option to prevent breakage */
-	if ((endSection = strchr(*lp, ']')) == NULL) {
-		WARN(file, "line %d: parse error\n", file->lineno);
-		SEEK_NEXT(lp);
-		return (file->ignore = 1) - 1;		/* single line rocks */
-	}
-
-	/* Redefinition of previous and not allowed */
-	++(*lp);
-	if (file->current != NULL && I_NOREDEFINE(file) &&
-	    (strncmp(file->current->key, *lp, endSection - *lp)) == 0) {
-		WARN(file, "line %d: redefining %s\n", file->lineno, file->current->key);
-		SEEK_NEXT(lp);
-		return (file->ignore = 1) - 1;
-	}
-
-	if ((file->current = malloc(sizeof (struct section))) == NULL)
-		return -1;
-
-	if ((file->current->key = xstrndup(*lp, endSection - *lp)) == NULL) {
-		free(file->current);
-		return -1;
-	}
-
-	*lp = endSection + 1;
-
-#ifdef INIFILE_DEBUG
-	printf("-- current section is now [%s]\n", file->current->key);
-#endif
-
-	/* Finally add the new section to the config */
-	STAILQ_INIT(&file->current->options);
-	STAILQ_INSERT_TAIL(&file->sections, file->current, next);
-
-	return (file->ignore = 0);
-}
-
-/*
- * Store the new option that have been parsed. Returns 0 on success or
- * on parse error (to avoid inifile_readline from stopping) and -1 on
- * allocation failure.
- */
-
-static int
-inifile_register(struct inifile *file, char **lp)
-{
-	char *assignKey, *endKey, *endValue, token = '\0';
-	int length = 0;
-	struct option *option, tmp;
-
-	/* No '=' found */
-	if ((assignKey = strchr(*lp, '=')) == NULL) {
-		WARN(file, "line %d: missing '='\n", file->lineno);
-		SEEK_NEXT(lp);
-		return 0;
-	}
-
-	/* Find end of option */
-	for (endKey = *lp; !isspace(*endKey) && *endKey != '\0'; ++endKey)
-		continue;
-
-	/* Do not register empty option name */
-	if (endKey - *lp == 0) {
-		WARN(file, "line %d: 0-length option\n", file->lineno);
-		SEEK_NL(lp);
-		return 0;
-	}
-
-	memset(&tmp, 0, sizeof (struct option));
-	if ((tmp.key = xstrndup(*lp, endKey - *lp)) == NULL)
-		return -1;
-
-	endValue = &assignKey[1];
-	SKIP_SPACES(endValue);
-
-	/* Find end of option value */
-	token = *endValue;
-	if (token == '\'' || token == '"') {
-		for (*lp = ++endValue; *endValue != token && *endValue != '\0'; ++endValue)
-			continue;
-
-		length = endValue - *lp;
-
-		/* Correctly closed */	
-		if (token != '\0' && *endValue == token)
-			++ endValue;
-		else
-			WARN(file, "line %d: missing '%c'\n", file->lineno, token);
-	} else {
-		for (*lp = endValue; !isspace(*endValue) && *endValue != '\0'; ++endValue)
-			continue;
-
-		length = endValue - *lp;
-	}
-
-	if (length != 0)
-		tmp.value = xstrndup(*lp, length);
-
-	if ((option = xoptiondup(&tmp)) == NULL)
-		return -1;
-
-#ifdef INIFILE_DEBUG
-	printf("-- next option [%s] is set to [%s]\n", tmp.key, tmp.value);
-#endif
-
-	/* Finally add the new option to the current section */
-	*lp = endValue;
-	STAILQ_INSERT_TAIL(&file->current->options, option, next);
-
-	return 0;
-}
-
-static void *
-inifile_fatal(struct inifile *file, FILE *fp, const char *fmt, ...)
-{
-	va_list ap;
-
-	if (file) {
-		struct section *s, *stmp;
-		struct option *o, *otmp;
-
-		if (file->line != NULL)
-			free(file->line);
-
-		STAILQ_FOREACH_SAFE(s, &file->sections, next, stmp) {
-			STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) {
-				free(o->key);
-				free(o->value);
-
-				STAILQ_REMOVE(&s->options, o, option, next);
-
-				free(o);
-			}
-
-			free(s->key);
-			free(s);
-		}
-
-	}
-
-	if (fp != NULL)
-		fclose(fp);
-
-	va_start(ap, fmt);
-	vsnprintf(inifileError, 1024, fmt, ap);
-	va_end(ap);
-
-	/* Directly print error if VERBOSE is enabled */
-	WARN(file, "%s\n", inifileError);
-
-	free(file);
-
-	return NULL;
-}
-
-static char *
-xstrndup(const char *src, size_t max)
-{
-	char *res;
-	size_t length;
-
-	for (length = 0; length < max && src[length] != '\0'; ++length)
-		continue;
-
-	if ((res = malloc(length + 1)) == NULL)
-		return NULL;
-
-	memcpy(res, src, length);
-	res[length] = '\0';
-
-	return res;
-}
-
-static struct option *
-xoptiondup(const struct option *option)
-{
-	struct option *res;
-
-	/*
-	 * Value may be NULL but not option's key.
-	 */
-	if (option->key == NULL) {
-		if (option->value != NULL)
-			free(option->value);
-
-		return NULL;
-	}
-
-	if ((res = malloc(sizeof (struct option))) == NULL)
-		return NULL;
-
-	res->key	= option->key;
-	res->value	= option->value;
-
-	return res;
-}
--- a/inifile.h	Mon Nov 14 19:57:53 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * inifile.h -- parse .ini like files
- *
- * Copyright (c) 2011, David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _INIFILE_H_
-#define _INIFILE_H_
-
-#define	INIFILE_VERBOSE		(1 << 0)
-#define	INIFILE_NOREDEFINE	(1 << 1)
-
-struct inifile;
-
-struct inifile	*inifile_load(const char *, int);
-char		**inifile_sections(struct inifile *, int *);
-int		inifile_select(struct inifile *, const char *);
-char		*inifile_option(struct inifile *, const char *);
-char		*inifile_option_once(struct inifile *, const char *, const char *);
-char		*inifile_error(void);
-void		inifile_free(struct inifile *, int, int);
-
-#endif /* _INIFILE_H_ */