/* * Copyright (C) 2010 Michael Buesch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "config.h" #include "razer_private.h" #include #include #include #include #include static inline int strcmp_case(const char *a, const char *b, bool ignorecase) { if (ignorecase) return strcasecmp(a, b); return strcmp(a, b); } static void free_item(struct config_item *i) { if (i) { free(i->name); free(i->value); free(i); } } static void free_items(struct config_item *i) { struct config_item *next; for ( ; i; i = next) { next = i->next; free_item(i); } } static void free_section(struct config_section *s) { if (s) { free_items(s->items); free(s->name); free(s); } } static void free_sections(struct config_section *s) { struct config_section *next; for ( ; s; s = next) { next = s->next; free_section(s); } } void config_for_each_item(struct config_file *f, void *context, void *data, const char *section, bool (*func)(struct config_file *f, void *context, void *data, const char *section, const char *item, const char *value)) { struct config_section *s; struct config_item *i; if (!f || !section) return; for (s = f->sections; s; s = s->next) { if (strcmp(s->name, section) == 0) { for (i = s->items; i; i = i->next) { if (!func(f, context, data, s->name, i->name, i->value)) return; } } } } void config_for_each_section(struct config_file *f, void *context, void *data, bool (*func)(struct config_file *f, void *context, void *data, const char *section)) { struct config_section *s; if (!f) return; for (s = f->sections; s; s = s->next) { if (!func(f, context, data, s->name)) return; } } const char * config_get(struct config_file *f, const char *section, const char *item, const char *_default, unsigned int flags) { struct config_section *s; struct config_item *i; const char *retval = _default; if (!f || !section || !item) return _default; for (s = f->sections; s; s = s->next) { if (strcmp_case(s->name, section, !!(flags & CONF_SECT_NOCASE)) == 0) { for (i = s->items; i; i = i->next) { if (strcmp_case(i->name, item, !!(flags & CONF_ITEM_NOCASE)) == 0) { retval = i->value; break; } } break; } } return retval; } int config_get_int(struct config_file *f, const char *section, const char *item, int _default, unsigned int flags) { const char *value; int i; value = config_get(f, section, item, NULL, flags); if (!value) return _default; if (razer_string_to_int(value, &i)) return _default; return i; } int config_get_bool(struct config_file *f, const char *section, const char *item, int _default, unsigned int flags) { const char *value; bool b; value = config_get(f, section, item, NULL, flags); if (!value) return _default; if (razer_string_to_bool(value, &b)) return _default; return b; } #define list_append(container, baseptr, item) do { \ __typeof__(item) last; \ item->next = NULL; \ if (!(container->baseptr)) { \ container->baseptr = item; \ return; \ } \ for (last = container->baseptr; \ last->next; \ last = last->next) \ ; \ last->next = item; \ } while (0) static void append_section(struct config_file *f, struct config_section *s) { list_append(f, sections, s); } static void append_item(struct config_section *s, struct config_item *i) { list_append(s, items, i); } struct config_file * config_file_parse(const char *path, bool ignore_enoent) { struct config_file *f; struct config_section *s = NULL; struct config_item *i; FILE *fd; char *name, *value; size_t len; unsigned int lineno = 0; ssize_t count; size_t linebuf_size = 0; char *linebuf = NULL, *line; f = zalloc(sizeof(*f)); if (!f) goto error; f->path = strdup(path); if (!f->path) goto err_free_f; fd = fopen(path, "rb"); if (!fd) { if (errno == ENOENT && ignore_enoent) { razer_info("No config file %s present. Ignoring.\n", path); } else { razer_error("Failed to open config file %s: %s\n", path, strerror(errno)); } goto err_free_path; } while (1) { count = getline(&linebuf, &linebuf_size, fd); if (count <= 0) break; line = razer_string_strip(linebuf); lineno++; len = strlen(line); if (!len) continue; if (line[0] == '#') /* comment */ continue; if (len >= 3 && line[0] == '[' && line[len - 1] == ']') { /* New section */ s = zalloc(sizeof(*s)); if (!s) goto error_unwind; s->file = f; line[len - 1] = '\0'; /* strip ] */ s->name = strdup(line + 1); /* strip [ */ if (!s->name) { free(s); goto error_unwind; } append_section(f, s); continue; } if (!s) { razer_error("%s:%u: Stray characters\n", path, lineno); goto error_unwind; } /* Config item in section */ value = strchr(line, '='); if (!value) { razer_error("%s:%u: Invalid config item\n", path, lineno); goto error_unwind; } value[0] = '\0'; value++; name = line; i = zalloc(sizeof(*i)); if (!i) goto error_unwind; i->section = s; i->name = strdup(name); if (!i->name) { free(i); goto error_unwind; } i->value = strdup(value); if (!i->value) { free(i->name); free(i); goto error_unwind; } append_item(s, i); } free(linebuf); fclose(fd); return f; error_unwind: free_sections(f->sections); free(linebuf); fclose(fd); err_free_path: free(f->path); err_free_f: free(f); error: return NULL; } void config_file_free(struct config_file *f) { if (f) { free_sections(f->sections); free(f->path); free(f); } }