summaryrefslogtreecommitdiffstats
path: root/src/config-ini.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/config-ini.c')
-rw-r--r--src/config-ini.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/config-ini.c b/src/config-ini.c
new file mode 100644
index 0000000..7cfa1ff
--- /dev/null
+++ b/src/config-ini.c
@@ -0,0 +1,147 @@
+#include "config-ini.h"
+#include "types.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void free_field(ini_field_t *field) {
+ free(field->key);
+ free(field->value);
+ free(field);
+}
+
+static void free_section(ini_section_t *section) {
+ ini_field_t *field, *tmp;
+
+ list_for_each_entry_safe(field, tmp, &section->fields, node)
+ free_field(field);
+
+ free(section->name);
+ free(section);
+}
+
+void free_ini_file(ini_file_t *file) {
+ ini_section_t *section, *tmp;
+
+ list_for_each_entry_safe(section, tmp, &file->sections, node)
+ free_section(section);
+
+ free(file);
+}
+
+static bool add_field(ini_section_t *section, const char *key, const char *value) {
+ ini_field_t *field = calloc(1, sizeof(*field));
+ if (!field)
+ return false;
+
+ field->key = strdup(key);
+ field->value = strdup(value);
+
+ if (!field->key || !field->value) {
+ free_field(field);
+ return false;
+ }
+
+ list_add_tail(&field->node, &section->fields);
+
+ return true;
+}
+
+static ini_section_t * add_section(ini_file_t *file, const char *name) {
+ ini_section_t *section = calloc(1, sizeof(*section));
+ if (!section)
+ return NULL;
+
+ section->name = strdup(name);
+ if (!section->name) {
+ free(section);
+ return false;
+ }
+
+ INIT_LIST_HEAD(&section->fields);
+ list_add_tail(&section->node, &file->sections);
+
+ return section;
+}
+
+ini_file_t * read_ini_file(const char *filename) {
+ FILE *f = fopen(filename, "r");
+ if (!f)
+ return NULL;
+
+ ini_file_t *file = calloc(1, sizeof(*file));
+ if (!file)
+ goto error;
+
+ INIT_LIST_HEAD(&file->sections);
+
+ ini_section_t *section = NULL;
+ int err = 0;
+
+ char *line = NULL;
+ size_t n = 0;
+
+ while (getline(&line, &n, f) >= 0) {
+ char *input = line;
+
+ while (isspace(input[0]))
+ input++;
+
+ if (input[0] == '#' || input[0] == '\0')
+ continue;
+
+ size_t len = strlen(input);
+
+ while (isspace(input[len-1]))
+ len--;
+
+ if (input[0] == '[') {
+ if (input[len-1] != ']') {
+ err = EINVAL;
+ goto error;
+ }
+
+ input[len-1] = '\0';
+
+ section = add_section(file, input+1);
+ if (!section)
+ goto error;
+ } else {
+ if (!section) {
+ err = EINVAL;
+ goto error;
+ }
+
+ input[len] = '\0';
+
+ char *delim = strchr(input, '=');
+ if (!delim) {
+ err = EINVAL;
+ goto error;
+ }
+
+ *delim = '\0';
+
+ if (!add_field(section, input, delim+1))
+ goto error;
+ }
+ }
+
+ if (ferror(f))
+ err = EIO;
+
+error:
+ free(line);
+ fclose(f);
+
+ if (err) {
+ free_ini_file(file);
+ errno = err;
+ return NULL;
+ }
+
+ return file;
+}