aboutsummaryrefslogtreecommitdiffstats
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
parent634a5029931d3a68a44cf6de9c87fd8d547fe7a7 (diff)
downloadblogc-0dffa9a91ef47fc0ac6b96cb5e96e7e73b8018b8.tar.gz
blogc-0dffa9a91ef47fc0ac6b96cb5e96e7e73b8018b8.tar.bz2
blogc-0dffa9a91ef47fc0ac6b96cb5e96e7e73b8018b8.zip
common: added config-parser
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am20
-rw-r--r--src/common/config-parser.c238
-rw-r--r--src/common/config-parser.h26
-rw-r--r--src/common/error.h3
-rw-r--r--tests/common/check_config_parser.c434
6 files changed, 722 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 22ac84a..60fd880 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,7 @@ blogc*.html
/tests/blogc/check_renderer
/tests/blogc/check_source_parser
/tests/blogc/check_template_parser
+/tests/common/check_config_parser
/tests/common/check_error
/tests/common/check_utf8
/tests/common/check_utils
diff --git a/Makefile.am b/Makefile.am
index bf07f5c..d025b16 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,6 +40,7 @@ noinst_HEADERS = \
src/blogc/renderer.h \
src/blogc/source-parser.h \
src/blogc/template-parser.h \
+ src/common/config-parser.h \
src/common/error.h \
src/common/utf8.h \
src/common/utils.h \
@@ -93,6 +94,7 @@ libblogc_la_LIBADD = \
libblogc_common_la_SOURCES = \
+ src/common/config-parser.c \
src/common/error.c \
src/common/utf8.c \
src/common/utils.c \
@@ -273,6 +275,7 @@ check_PROGRAMS += \
tests/blogc/check_renderer \
tests/blogc/check_source_parser \
tests/blogc/check_template_parser \
+ tests/common/check_config_parser \
tests/common/check_error \
tests/common/check_utf8 \
tests/common/check_utils \
@@ -388,6 +391,23 @@ tests_blogc_check_template_parser_LDADD = \
libblogc_common.la \
$(NULL)
+tests_common_check_config_parser_SOURCES = \
+ tests/common/check_config_parser.c \
+ $(NULL)
+
+tests_common_check_config_parser_CFLAGS = \
+ $(CMOCKA_CFLAGS) \
+ $(NULL)
+
+tests_common_check_config_parser_LDFLAGS = \
+ -no-install \
+ $(NULL)
+
+tests_common_check_config_parser_LDADD = \
+ $(CMOCKA_LIBS) \
+ libblogc_common.la \
+ $(NULL)
+
tests_common_check_error_SOURCES = \
tests/common/check_error.c \
$(NULL)
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,
diff --git a/tests/common/check_config_parser.c b/tests/common/check_config_parser.c
new file mode 100644
index 0000000..2a6ad67
--- /dev/null
+++ b/tests/common/check_config_parser.c
@@ -0,0 +1,434 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../src/common/config-parser.h"
+#include "../../src/common/error.h"
+#include "../../src/common/utils.h"
+
+
+static void
+test_config_empty(void **state)
+{
+ const char *a = "";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 0);
+ bc_config_free(c);
+}
+
+
+static void
+test_config_section_empty(void **state)
+{
+ const char *a = "[foo]";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 1);
+ char **s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 1);
+ assert_string_equal(s[0], "foo");
+ assert_null(s[1]);
+ bc_strv_free(s);
+ char **k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 0);
+ assert_null(k[0]);
+ bc_strv_free(k);
+ bc_config_free(c);
+}
+
+
+static void
+test_config_section(void **state)
+{
+ const char *a =
+ "[foo]\n"
+ "asd = zxc";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 1);
+ char **s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 1);
+ assert_string_equal(s[0], "foo");
+ assert_null(s[1]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ char **k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 1);
+ assert_string_equal(k[0], "asd");
+ assert_null(k[1]);
+ bc_strv_free(k);
+ bc_config_free(c);
+
+ a =
+ "[foo]\n"
+ "asd = zxc\n";
+ err = NULL;
+ c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 1);
+ s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 1);
+ assert_string_equal(s[0], "foo");
+ assert_null(s[1]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 1);
+ assert_string_equal(k[0], "asd");
+ assert_null(k[1]);
+ bc_strv_free(k);
+ bc_config_free(c);
+
+ a =
+ "[foo]\r\n"
+ "asd = zxc\r\n";
+ err = NULL;
+ c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 1);
+ s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 1);
+ assert_string_equal(s[0], "foo");
+ assert_null(s[1]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 1);
+ assert_string_equal(k[0], "asd");
+ assert_null(k[1]);
+ bc_strv_free(k);
+ bc_config_free(c);
+}
+
+
+static void
+test_config_section_multiple_keys(void **state)
+{
+ const char *a =
+ "[foo]\n"
+ "asd = zxc\n"
+ "qwe = rty\n"
+ "zxc = vbn";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 1);
+ char **s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 1);
+ assert_string_equal(s[0], "foo");
+ assert_null(s[1]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ assert_string_equal(bc_config_get(c, "foo", "qwe"), "rty");
+ assert_string_equal(bc_config_get(c, "foo", "zxc"), "vbn");
+ char **k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 3);
+ assert_string_equal(k[0], "asd");
+ assert_string_equal(k[1], "qwe");
+ assert_string_equal(k[2], "zxc");
+ assert_null(k[3]);
+ bc_strv_free(k);
+ bc_config_free(c);
+
+ a =
+ "[foo]\n"
+ "asd = zxc\n"
+ "qwe = rty\n"
+ "zxc = vbn\n";
+ err = NULL;
+ c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 1);
+ s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 1);
+ assert_string_equal(s[0], "foo");
+ assert_null(s[1]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ assert_string_equal(bc_config_get(c, "foo", "qwe"), "rty");
+ assert_string_equal(bc_config_get(c, "foo", "zxc"), "vbn");
+ k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 3);
+ assert_string_equal(k[0], "asd");
+ assert_string_equal(k[1], "qwe");
+ assert_string_equal(k[2], "zxc");
+ assert_null(k[3]);
+ bc_strv_free(k);
+ bc_config_free(c);
+
+ a =
+ "[foo]\r\n"
+ "asd = zxc\r\n"
+ "qwe = rty\r\n"
+ "zxc = vbn\r\n";
+ err = NULL;
+ c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 1);
+ s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 1);
+ assert_string_equal(s[0], "foo");
+ assert_null(s[1]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ assert_string_equal(bc_config_get(c, "foo", "qwe"), "rty");
+ assert_string_equal(bc_config_get(c, "foo", "zxc"), "vbn");
+ k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 3);
+ assert_string_equal(k[0], "asd");
+ assert_string_equal(k[1], "qwe");
+ assert_string_equal(k[2], "zxc");
+ assert_null(k[3]);
+ bc_strv_free(k);
+ bc_config_free(c);
+}
+
+
+static void
+test_config_section_multiple_sections(void **state)
+{
+ const char *a =
+ "[foo]\n"
+ "asd = zxc\n"
+ "qwe = rty\n"
+ "zxc = vbn\n"
+ "\n"
+ "[bar]\n"
+ "lol = hehe";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 2);
+ char **s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 2);
+ assert_string_equal(s[0], "foo");
+ assert_string_equal(s[1], "bar");
+ assert_null(s[2]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ assert_string_equal(bc_config_get(c, "foo", "qwe"), "rty");
+ assert_string_equal(bc_config_get(c, "foo", "zxc"), "vbn");
+ assert_string_equal(bc_config_get(c, "bar", "lol"), "hehe");
+ char **k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 3);
+ assert_string_equal(k[0], "asd");
+ assert_string_equal(k[1], "qwe");
+ assert_string_equal(k[2], "zxc");
+ assert_null(k[3]);
+ bc_strv_free(k);
+ k = bc_config_list_keys(c, "bar");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 1);
+ assert_string_equal(k[0], "lol");
+ assert_null(k[1]);
+ bc_strv_free(k);
+ bc_config_free(c);
+
+ a =
+ "[foo]\n"
+ "asd = zxc\n"
+ "qwe = rty\n"
+ "zxc = vbn\n"
+ "\n"
+ "[bar]\n"
+ "lol = hehe\n";
+ err = NULL;
+ c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 2);
+ s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 2);
+ assert_string_equal(s[0], "foo");
+ assert_string_equal(s[1], "bar");
+ assert_null(s[2]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ assert_string_equal(bc_config_get(c, "foo", "qwe"), "rty");
+ assert_string_equal(bc_config_get(c, "foo", "zxc"), "vbn");
+ assert_string_equal(bc_config_get(c, "bar", "lol"), "hehe");
+ k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 3);
+ assert_string_equal(k[0], "asd");
+ assert_string_equal(k[1], "qwe");
+ assert_string_equal(k[2], "zxc");
+ assert_null(k[3]);
+ bc_strv_free(k);
+ k = bc_config_list_keys(c, "bar");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 1);
+ assert_string_equal(k[0], "lol");
+ assert_null(k[1]);
+ bc_strv_free(k);
+ bc_config_free(c);
+
+ a =
+ "[foo]\r\n"
+ "asd = zxc\r\n"
+ "qwe = rty\r\n"
+ "zxc = vbn\r\n"
+ "\r\n"
+ "[bar]\r\n"
+ "lol = hehe\r\n";
+ err = NULL;
+ c = bc_config_parse(a, strlen(a), &err);
+ assert_null(err);
+ assert_non_null(c);
+ assert_non_null(c->root);
+ assert_int_equal(bc_trie_size(c->root), 2);
+ s = bc_config_list_sections(c);
+ assert_non_null(s);
+ assert_int_equal(bc_strv_length(s), 2);
+ assert_string_equal(s[0], "foo");
+ assert_string_equal(s[1], "bar");
+ assert_null(s[2]);
+ bc_strv_free(s);
+ assert_string_equal(bc_config_get(c, "foo", "asd"), "zxc");
+ assert_string_equal(bc_config_get(c, "foo", "qwe"), "rty");
+ assert_string_equal(bc_config_get(c, "foo", "zxc"), "vbn");
+ assert_string_equal(bc_config_get(c, "bar", "lol"), "hehe");
+ k = bc_config_list_keys(c, "foo");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 3);
+ assert_string_equal(k[0], "asd");
+ assert_string_equal(k[1], "qwe");
+ assert_string_equal(k[2], "zxc");
+ assert_null(k[3]);
+ bc_strv_free(k);
+ k = bc_config_list_keys(c, "bar");
+ assert_non_null(k);
+ assert_int_equal(bc_strv_length(k), 1);
+ assert_string_equal(k[0], "lol");
+ assert_null(k[1]);
+ bc_strv_free(k);
+ bc_config_free(c);
+}
+
+
+static void
+test_config_error_start(void **state)
+{
+ const char *a =
+ "asd\n"
+ "[foo]";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_non_null(err);
+ assert_null(c);
+ assert_int_equal(err->type, BC_ERROR_CONFIG_PARSER);
+ assert_string_equal(err->msg, "File must start with section");
+ bc_error_free(err);
+}
+
+
+static void
+test_config_error_section_with_newline(void **state)
+{
+ const char *a =
+ "[foo\nbar]";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_non_null(err);
+ assert_null(c);
+ assert_int_equal(err->type, BC_ERROR_CONFIG_PARSER);
+ assert_string_equal(err->msg, "Section names can't have new lines");
+ bc_error_free(err);
+}
+
+
+static void
+test_config_error_key_without_value(void **state)
+{
+ const char *a =
+ "[foobar]\n"
+ "asd = 12\n"
+ "foo";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), &err);
+ assert_non_null(err);
+ assert_null(c);
+ assert_int_equal(err->type, BC_ERROR_CONFIG_PARSER);
+ assert_string_equal(err->msg, "Key without value: foo");
+ bc_error_free(err);
+ a =
+ "[foobar]\n"
+ "asd = 12\n"
+ "foo\n";
+ err = NULL;
+ c = bc_config_parse(a, strlen(a), &err);
+ assert_non_null(err);
+ assert_null(c);
+ assert_int_equal(err->type, BC_ERROR_CONFIG_PARSER);
+ assert_string_equal(err->msg, "Key without value: foo");
+ bc_error_free(err);
+}
+
+
+int
+main(void)
+{
+ const UnitTest tests[] = {
+ unit_test(test_config_empty),
+ unit_test(test_config_section_empty),
+ unit_test(test_config_section),
+ unit_test(test_config_section_multiple_keys),
+ unit_test(test_config_section_multiple_sections),
+ unit_test(test_config_error_start),
+ unit_test(test_config_error_section_with_newline),
+ unit_test(test_config_error_key_without_value),
+ };
+ return run_tests(tests);
+}