From 2442ee791e0ab49e1897b45191e021b81dd8be3b Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sun, 1 Jan 2017 03:41:41 +0100 Subject: config-parser: allow quoted values --- src/common/config-parser.c | 152 +++++++++++++++++++++++++++++++++---- tests/common/check_config_parser.c | 115 +++++++++++++++++++++++++++- 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); @@ -494,6 +494,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) { @@ -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), -- cgit v1.2.3-18-g5258