diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/config-parser.c | 238 | ||||
| -rw-r--r-- | src/common/config-parser.h | 26 | ||||
| -rw-r--r-- | src/common/error.h | 3 | 
3 files changed, 267 insertions, 0 deletions
diff --git a/src/common/config-parser.c b/src/common/config-parser.c new file mode 100644 index 0000000..9361908 --- /dev/null +++ b/src/common/config-parser.c @@ -0,0 +1,238 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include "error.h" +#include "utils.h" +#include "config-parser.h" + + +typedef enum { +    CONFIG_START = 1, +    CONFIG_SECTION_START, +    CONFIG_SECTION, +    CONFIG_SECTION_KEY, +    CONFIG_SECTION_VALUE_START, +    CONFIG_SECTION_VALUE, +} bc_configparser_state_t; + + +bc_config_t* +bc_config_parse(const char *src, size_t src_len, bc_error_t **err) +{ +    if (err != NULL && *err != NULL) +        return NULL; + +    size_t current = 0; +    size_t start = 0; + +    bc_trie_t *section = NULL; + +    char *section_name = NULL; +    char *key = NULL; +    char *value = NULL; + +    bc_config_t *rv = bc_malloc(sizeof(bc_config_t)); +    rv->root = bc_trie_new((bc_free_func_t) bc_trie_free); + +    bc_configparser_state_t state = CONFIG_START; + +    while (current < src_len) { +        char c = src[current]; +        bool is_last = current == src_len - 1; + +        switch (state) { + +            case CONFIG_START: +                if (c == '#' || c == ';') { +                    while (current < src_len) { +                        if (src[current] == '\r' || src[current] == '\n') +                            break; +                        current++; +                    } +                    break; +                } +                if (c == ' ' || c == '\t' || c == '\r' || c == '\n') +                    break; +                if (c == '[') { +                    state = CONFIG_SECTION_START; +                    break; +                } +                if (section != NULL) { +                    start = current; +                    state = CONFIG_SECTION_KEY; +                    continue; +                } +                if (err != NULL) +                    *err = bc_error_new_printf(BC_ERROR_CONFIG_PARSER, +                        "File must start with section"); +                break; + +            case CONFIG_SECTION_START: +                start = current; +                state = CONFIG_SECTION; +                break; + +            case CONFIG_SECTION: +                if (c == ']') { +                    section_name = bc_strndup(src + start, current - start); +                    section = bc_trie_new(free); +                    bc_trie_insert(rv->root, section_name, section); +                    free(section_name); +                    section_name = NULL; +                    state = CONFIG_START; +                    break; +                } +                if (c != '\r' && c != '\n') +                    break; +                if (err != NULL) +                    *err = bc_error_new_printf(BC_ERROR_CONFIG_PARSER, +                        "Section names can't have new lines"); +                break; + +            case CONFIG_SECTION_KEY: +                if (c == '=') { +                    key = bc_strndup(src + start, current - start); +                    state = CONFIG_SECTION_VALUE_START; +                    break; +                } +                if (c != '\r' && c != '\n' && !is_last) +                    break; +                // key without value, should we support it? +                if (err != NULL) { +                    size_t end = is_last && c != '\n' && c != '\r' ? src_len : +                        current; +                    key = bc_strndup(src + start, end - start); +                    *err = bc_error_new_printf(BC_ERROR_CONFIG_PARSER, +                        "Key without value: %s", key); +                    free(key); +                    key = NULL; +                } +                break; + +            case CONFIG_SECTION_VALUE_START: +                start = current; +                state = CONFIG_SECTION_VALUE; +                break; + +            case CONFIG_SECTION_VALUE: +                if (c == '\r' || c == '\n' || is_last) { +                    size_t end = is_last && c != '\n' && c != '\r' ? src_len : +                        current; +                    value = bc_strndup(src + start, end - start); +                    bc_trie_insert(section, bc_str_strip(key), +                        bc_strdup(bc_str_strip(value))); +                    free(key); +                    key = NULL; +                    free(value); +                    value = NULL; +                    state = CONFIG_START; +                    break; +                } +                break; + +        } + +        if (err != NULL && *err != NULL) { +            bc_config_free(rv); +            rv = NULL; +            break; +        } + +        current++; +    } + +    free(section_name); +    free(key); +    free(value); + +    return rv; +} + + +static void +list_keys(const char *key, const char value, bc_slist_t **l) +{ +    *l = bc_slist_append(*l, bc_strdup(key)); +} + + +char** +bc_config_list_sections(bc_config_t *config) +{ +    if (config == NULL) +        return NULL; + +    bc_slist_t *l = NULL; +    bc_trie_foreach(config->root, (bc_trie_foreach_func_t) list_keys, &l); + +    char **rv = bc_malloc(sizeof(char*) * (bc_slist_length(l) + 1)); + +    unsigned int i = 0; +    for (bc_slist_t *tmp = l; tmp != NULL; tmp = tmp->next, i++) +        rv[i] = tmp->data; +    rv[i] = NULL; + +    bc_slist_free(l); + +    return rv; +} + + +char** +bc_config_list_keys(bc_config_t *config, const char *section) +{ +    if (config == NULL) +        return NULL; + +    bc_trie_t *s = bc_trie_lookup(config->root, section); +    if (s == NULL) +        return NULL; + +    bc_slist_t *l = NULL; +    bc_trie_foreach(s, (bc_trie_foreach_func_t) list_keys, &l); + +    char **rv = bc_malloc(sizeof(char*) * (bc_slist_length(l) + 1)); + +    unsigned int i = 0; +    for (bc_slist_t *tmp = l; tmp != NULL; tmp = tmp->next, i++) +        rv[i] = tmp->data; +    rv[i] = NULL; + +    bc_slist_free(l); + +    return rv; +} + + +const char* +bc_config_get(bc_config_t *config, const char *section, const char *key) +{ +    if (config == NULL) +        return NULL; + +    bc_trie_t *s = bc_trie_lookup(config->root, section); +    if (s == NULL) +        return NULL; + +    return bc_trie_lookup(s, key); +} + + +void +bc_config_free(bc_config_t *config) +{ +    if (config == NULL) +        return; +    bc_trie_free(config->root); +    free(config); +} diff --git a/src/common/config-parser.h b/src/common/config-parser.h new file mode 100644 index 0000000..0d30c49 --- /dev/null +++ b/src/common/config-parser.h @@ -0,0 +1,26 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#ifndef _CONFIG_PARSER_H +#define _CONFIG_PARSER_H + +#include "utils.h" +#include "error.h" + +typedef struct { +    bc_trie_t *root; +} bc_config_t; + +bc_config_t* bc_config_parse(const char *src, size_t src_len, bc_error_t **err); +char** bc_config_list_sections(bc_config_t *config); +char** bc_config_list_keys(bc_config_t *config, const char *section); +const char* bc_config_get(bc_config_t *config, const char *section, +    const char *key); +void bc_config_free(bc_config_t *config); + +#endif /* _CONFIG_PARSER_H */ diff --git a/src/common/error.h b/src/common/error.h index 200f9a7..2569538 100644 --- a/src/common/error.h +++ b/src/common/error.h @@ -14,6 +14,9 @@  // error handling is centralized here for the sake of simplicity :/  typedef enum { +    // errors for src/common +    BC_ERROR_CONFIG_PARSER = 1, +      // errors for src/blogc      BLOGC_ERROR_SOURCE_PARSER = 100,      BLOGC_ERROR_TEMPLATE_PARSER,  | 
