diff options
Diffstat (limited to 'src/config-ini.c')
-rw-r--r-- | src/config-ini.c | 147 |
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, §ion->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, §ion->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(§ion->fields); + list_add_tail(§ion->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; +} |