aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael G. Martins <rafael@rafaelmartins.eng.br>2017-01-01 03:41:41 +0100
committerRafael G. Martins <rafael@rafaelmartins.eng.br>2017-01-01 03:41:41 +0100
commit2442ee791e0ab49e1897b45191e021b81dd8be3b (patch)
tree836ee111058e7680b523281e33c533da099bac45
parent30a54138d73341d1be861d03b3532ea094332a86 (diff)
downloadblogc-2442ee791e0ab49e1897b45191e021b81dd8be3b.tar.gz
blogc-2442ee791e0ab49e1897b45191e021b81dd8be3b.tar.bz2
blogc-2442ee791e0ab49e1897b45191e021b81dd8be3b.zip
config-parser: allow quoted values
-rw-r--r--src/common/config-parser.c152
-rw-r--r--tests/common/check_config_parser.c115
2 files changed, 249 insertions, 18 deletions
diff --git a/src/common/config-parser.c b/src/common/config-parser.c
index 8d6c10e..7c19d78 100644
--- a/src/common/config-parser.c
+++ b/src/common/config-parser.c
@@ -20,7 +20,14 @@ typedef enum {
CONFIG_SECTION,
CONFIG_SECTION_KEY,
CONFIG_SECTION_VALUE_START,
+ CONFIG_SECTION_VALUE_QUOTE,
+ CONFIG_SECTION_VALUE_SQUOTE,
+ CONFIG_SECTION_VALUE_POST_QUOTED,
CONFIG_SECTION_VALUE,
+ CONFIG_SECTION_LIST_START,
+ CONFIG_SECTION_LIST_QUOTE,
+ CONFIG_SECTION_LIST_SQUOTE,
+ CONFIG_SECTION_LIST_POST_QUOTED,
CONFIG_SECTION_LIST,
} bc_configparser_state_t;
@@ -67,7 +74,8 @@ bc_config_parse(const char *src, size_t src_len, const char *list_sections[],
char *section_name = NULL;
char *key = NULL;
- char *value = NULL;
+ bc_string_t *value = NULL;
+ bool escaped = false;
bc_config_t *rv = bc_malloc(sizeof(bc_config_t));
rv->root = bc_trie_new((bc_free_func_t) free_section);
@@ -78,6 +86,19 @@ bc_config_parse(const char *src, size_t src_len, const char *list_sections[],
char c = src[current];
bool is_last = current == src_len - 1;
+ if (escaped) {
+ bc_string_append_c(value, c);
+ escaped = false;
+ current++;
+ continue;
+ }
+
+ if (value != NULL && c == '\\') {
+ escaped = true;
+ current++;
+ continue;
+ }
+
switch (state) {
case CONFIG_START:
@@ -102,7 +123,9 @@ bc_config_parse(const char *src, size_t src_len, const char *list_sections[],
state = CONFIG_SECTION_KEY;
break;
case CONFIG_SECTION_TYPE_LIST:
- state = CONFIG_SECTION_LIST;
+ state = CONFIG_SECTION_LIST_START;
+ if (value == NULL)
+ value = bc_string_new();
break;
}
continue;
@@ -153,6 +176,8 @@ bc_config_parse(const char *src, size_t src_len, const char *list_sections[],
if (c == '=') {
key = bc_strndup(src + start, current - start);
state = CONFIG_SECTION_VALUE_START;
+ if (value == NULL)
+ value = bc_string_new();
break;
}
if (c != '\r' && c != '\n' && !is_last)
@@ -168,39 +193,138 @@ bc_config_parse(const char *src, size_t src_len, const char *list_sections[],
break;
case CONFIG_SECTION_VALUE_START:
- start = current;
+ if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
+ break;
+ if (c == '"') {
+ state = CONFIG_SECTION_VALUE_QUOTE;
+ break;
+ }
+ if (c == '\'') {
+ state = CONFIG_SECTION_VALUE_SQUOTE;
+ break;
+ }
+ bc_string_append_c(value, c);
state = CONFIG_SECTION_VALUE;
break;
+ case CONFIG_SECTION_VALUE_QUOTE:
+ if (c == '"') {
+ bc_trie_insert(section->data, bc_str_strip(key),
+ bc_string_free(value, false));
+ free(key);
+ key = NULL;
+ value = NULL;
+ state = CONFIG_SECTION_VALUE_POST_QUOTED;
+ break;
+ }
+ bc_string_append_c(value, c);
+ break;
+
+ case CONFIG_SECTION_VALUE_SQUOTE:
+ if (c == '\'') {
+ bc_trie_insert(section->data, bc_str_strip(key),
+ bc_string_free(value, false));
+ free(key);
+ key = NULL;
+ value = NULL;
+ state = CONFIG_SECTION_VALUE_POST_QUOTED;
+ break;
+ }
+ bc_string_append_c(value, c);
+ break;
+
+ case CONFIG_SECTION_VALUE_POST_QUOTED:
+ if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
+ break;
+ if (c == '\r' || c == '\n' || is_last) {
+ state = CONFIG_START;
+ break;
+ }
+ *err = bc_error_parser(BC_ERROR_CONFIG_PARSER, src, src_len,
+ current, "Invalid value for key, should not have anything "
+ "after quotes.");
+ 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);
+ if (is_last && c != '\r' && c != '\n')
+ bc_string_append_c(value, c);
bc_trie_insert(section->data, bc_str_strip(key),
- bc_strdup(bc_str_strip(value)));
+ bc_strdup(bc_str_rstrip(value->str)));
free(key);
key = NULL;
- free(value);
+ bc_string_free(value, true);
value = NULL;
state = CONFIG_START;
break;
}
+ bc_string_append_c(value, c);
+ break;
+
+ case CONFIG_SECTION_LIST_START:
+ if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
+ break;
+ if (c == '"') {
+ state = CONFIG_SECTION_LIST_QUOTE;
+ break;
+ }
+ if (c == '\'') {
+ state = CONFIG_SECTION_LIST_SQUOTE;
+ break;
+ }
+ bc_string_append_c(value, c);
+ state = CONFIG_SECTION_LIST;
+ break;
+
+ case CONFIG_SECTION_LIST_QUOTE:
+ if (c == '"') {
+ section->data = bc_slist_append(section->data,
+ bc_string_free(value, false));
+ value = NULL;
+ state = CONFIG_SECTION_LIST_POST_QUOTED;
+ break;
+
+ }
+ bc_string_append_c(value, c);
+ break;
+
+ case CONFIG_SECTION_LIST_SQUOTE:
+ if (c == '\'') {
+ section->data = bc_slist_append(section->data,
+ bc_string_free(value, false));
+ value = NULL;
+ state = CONFIG_SECTION_LIST_POST_QUOTED;
+ break;
+
+ }
+ bc_string_append_c(value, c);
+ break;
+
+ case CONFIG_SECTION_LIST_POST_QUOTED:
+ if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
+ break;
+ if (c == '\r' || c == '\n' || is_last) {
+ state = CONFIG_START;
+ break;
+ }
+ *err = bc_error_parser(BC_ERROR_CONFIG_PARSER, src, src_len,
+ current, "Invalid value for list item, should not have "
+ "anything after quotes.");
break;
case CONFIG_SECTION_LIST:
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);
+ if (is_last && c != '\r' && c != '\n')
+ bc_string_append_c(value, c);
section->data = bc_slist_append(section->data,
- bc_strdup(bc_str_strip(value)));
- free(value);
+ bc_strdup(bc_str_strip(value->str)));
+ bc_string_free(value, true);
value = NULL;
state = CONFIG_START;
break;
}
+ bc_string_append_c(value, c);
break;
}
@@ -216,7 +340,7 @@ bc_config_parse(const char *src, size_t src_len, const char *list_sections[],
free(section_name);
free(key);
- free(value);
+ bc_string_free(value, true);
return rv;
}
diff --git a/tests/common/check_config_parser.c b/tests/common/check_config_parser.c
index a04dc6d..7c3cb4a 100644
--- a/tests/common/check_config_parser.c
+++ b/tests/common/check_config_parser.c
@@ -142,7 +142,7 @@ test_config_section_multiple_keys(void **state)
const char *a =
"[foo]\n"
"asd = zxc\n"
- "qwe = rty\n"
+ "qwe = rty \n"
"zxc = vbn";
bc_error_t *err = NULL;
bc_config_t *c = bc_config_parse(a, strlen(a), NULL, &err);
@@ -172,7 +172,7 @@ test_config_section_multiple_keys(void **state)
a =
"[foo]\n"
"asd = zxc\n"
- "qwe = rty\n"
+ "qwe = rty \n"
"zxc = vbn\n";
err = NULL;
c = bc_config_parse(a, strlen(a), NULL, &err);
@@ -202,7 +202,7 @@ test_config_section_multiple_keys(void **state)
a =
"[foo]\r\n"
"asd = zxc\r\n"
- "qwe = rty\r\n"
+ "qwe = rty \r\n"
"zxc = vbn\r\n";
err = NULL;
c = bc_config_parse(a, strlen(a), NULL, &err);
@@ -370,7 +370,7 @@ test_config_section_list(void **state)
"\n"
"[bar]\n"
"lol = hehe\n"
- "asdasdadssad";
+ " asdasdadssad ";
bc_error_t *err = NULL;
const char *sections[] = {"bar", NULL};
bc_config_t *c = bc_config_parse(a, strlen(a), sections, &err);
@@ -495,6 +495,112 @@ test_config_section_list(void **state)
static void
+test_config_quoted_values(void **state)
+{
+ const char *a =
+ "[foo]\n"
+ "a = \"lol\"\n"
+ "b = \"lo\\\"l\"\n"
+ "c = \"lo'l\"\n"
+ "d = 'lol'\n"
+ "e = 'lo\\'l'\n"
+ "f = 'lo\"l'\n"
+ "g = \\\\asd\n"
+ "h = \"\\\\asd\"\n"
+ "i = '\\\\asd'\n";
+ bc_error_t *err = NULL;
+ bc_config_t *c = bc_config_parse(a, strlen(a), NULL, &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), 9);
+ assert_string_equal(k[0], "a");
+ assert_string_equal(k[1], "b");
+ assert_string_equal(k[2], "c");
+ assert_string_equal(k[3], "d");
+ assert_string_equal(k[4], "e");
+ assert_string_equal(k[5], "f");
+ assert_string_equal(k[6], "g");
+ assert_string_equal(k[7], "h");
+ assert_string_equal(k[8], "i");
+ assert_null(k[9]);
+ bc_strv_free(k);
+ assert_string_equal(bc_config_get(c, "foo", "a"), "lol");
+ assert_string_equal(bc_config_get(c, "foo", "b"), "lo\"l");
+ assert_string_equal(bc_config_get(c, "foo", "c"), "lo'l");
+ assert_string_equal(bc_config_get(c, "foo", "d"), "lol");
+ assert_string_equal(bc_config_get(c, "foo", "e"), "lo'l");
+ assert_string_equal(bc_config_get(c, "foo", "f"), "lo\"l");
+ assert_string_equal(bc_config_get(c, "foo", "g"), "\\asd");
+ assert_string_equal(bc_config_get(c, "foo", "h"), "\\asd");
+ assert_string_equal(bc_config_get(c, "foo", "i"), "\\asd");
+ bc_config_free(c);
+
+ a =
+ "[foo]\n"
+ "\"lol\"\n"
+ "\"lo\\\"l\"\n"
+ "\"lo'l\"\n"
+ "'lol'\n"
+ "'lo\\'l'\n"
+ "'lo\"l'\n"
+ "\\\\asd\n"
+ "\"\\\\asd\"\n"
+ "'\\\\asd'\n"
+ "\n"
+ "[bar]\n"
+ "'lol = hehe'\n"
+ "\" asdasdadssad \"";
+ err = NULL;
+ const char *sections[] = {"foo", "bar", NULL};
+ c = bc_config_parse(a, strlen(a), sections, &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);
+ char **bar = bc_config_get_list(c, "foo");
+ assert_string_equal(bar[0], "lol");
+ assert_string_equal(bar[1], "lo\"l");
+ assert_string_equal(bar[2], "lo'l");
+ assert_string_equal(bar[3], "lol");
+ assert_string_equal(bar[4], "lo'l");
+ assert_string_equal(bar[5], "lo\"l");
+ assert_string_equal(bar[6], "\\asd");
+ assert_string_equal(bar[7], "\\asd");
+ assert_string_equal(bar[8], "\\asd");
+ assert_null(bar[9]);
+ bc_strv_free(bar);
+ bar = bc_config_get_list(c, "bar");
+ assert_non_null(bar);
+ assert_string_equal(bar[0], "lol = hehe");
+ assert_string_equal(bar[1], " asdasdadssad ");
+ assert_null(bar[2]);
+ bc_strv_free(bar);
+ k = bc_config_list_keys(c, "foo");
+ assert_null(k);
+ k = bc_config_list_keys(c, "bar");
+ assert_null(k);
+ bc_config_free(c);
+}
+
+
+static void
test_config_error_start(void **state)
{
const char *a =
@@ -571,6 +677,7 @@ main(void)
unit_test(test_config_section_multiple_keys),
unit_test(test_config_section_multiple_sections),
unit_test(test_config_section_list),
+ unit_test(test_config_quoted_values),
unit_test(test_config_error_start),
unit_test(test_config_error_section_with_newline),
unit_test(test_config_error_key_without_value),