aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRafael G. Martins <rafael@rafaelmartins.eng.br>2016-09-09 02:33:19 +0200
committerRafael G. Martins <rafael@rafaelmartins.eng.br>2016-09-09 02:33:19 +0200
commit0dffa9a91ef47fc0ac6b96cb5e96e7e73b8018b8 (patch)
tree8e453d11902ecca74b2057c8924f1a60769f5cb9 /src
parent634a5029931d3a68a44cf6de9c87fd8d547fe7a7 (diff)
downloadblogc-0dffa9a91ef47fc0ac6b96cb5e96e7e73b8018b8.tar.gz
blogc-0dffa9a91ef47fc0ac6b96cb5e96e7e73b8018b8.tar.bz2
blogc-0dffa9a91ef47fc0ac6b96cb5e96e7e73b8018b8.zip
common: added config-parser
Diffstat (limited to 'src')
-rw-r--r--src/common/config-parser.c238
-rw-r--r--src/common/config-parser.h26
-rw-r--r--src/common/error.h3
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,