From 5ff6aecbdb7f85a8e7a2ea7fd63c03b4bdd42076 Mon Sep 17 00:00:00 2001 From: Mark Lindner Date: Tue, 22 Nov 2022 14:10:16 -0700 Subject: [PATCH] Fixed various bugs in setting lookups by name/path. --- lib/libconfig.c | 79 ++++++++++++++------------------ tests/testdata/nesting.cfg | 6 +++ tests/tests.c | 94 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 44 deletions(-) create mode 100644 tests/testdata/nesting.cfg diff --git a/lib/libconfig.c b/lib/libconfig.c index 1a935be..e25ed26 100644 --- a/lib/libconfig.c +++ b/lib/libconfig.c @@ -125,32 +125,6 @@ static void __config_locale_restore(void) /* ------------------------------------------------------------------------- */ -static int __config_name_compare(const char *a, const char *b) -{ - const char *p, *q; - - for(p = a, q = b; ; p++, q++) - { - int pd = ((! *p) || strchr(PATH_TOKENS, *p)); - int qd = ((! *q) || strchr(PATH_TOKENS, *q)); - - if(pd && qd) - break; - else if(pd) - return(-1); - else if(qd) - return(1); - else if(*p < *q) - return(-1); - else if(*p > *q) - return(1); - } - - return(0); -} - -/* ------------------------------------------------------------------------- */ - static void __config_indent(FILE *stream, int depth, unsigned short w) { if(w) @@ -385,8 +359,12 @@ static void __config_list_add(config_list_t *list, config_setting_t *setting) /* ------------------------------------------------------------------------- */ +/* This function takes the length of the name to be searched for, so that one + * component of a longer path can be passed in. + */ static config_setting_t *__config_list_search(config_list_t *list, const char *name, + size_t namelen, unsigned int *idx) { config_setting_t **found = NULL; @@ -400,7 +378,8 @@ static config_setting_t *__config_list_search(config_list_t *list, if(! (*found)->name) continue; - if(! __config_name_compare(name, (*found)->name)) + if((strlen((*found)->name) == namelen) + && !strncmp(name, (*found)->name, namelen)) { if(idx) *idx = i; @@ -1226,27 +1205,37 @@ config_setting_t *config_setting_lookup(config_setting_t *setting, const char *p = path; config_setting_t *found = setting; - for(;;) + while(*p && found) { - while(*p && strchr(PATH_TOKENS, *p)) - p++; - - if(! *p) - break; + if(strchr(PATH_TOKENS, *p)) + ++p; if(*p == '[') - found = config_setting_get_elem(found, atoi(++p)); - else - found = config_setting_get_member(found, p); + { + char *q; + long index = strtol(++p, &q, 10); + if(*q != ']') + return NULL; - if(! found) - break; + p = ++q; + found = config_setting_get_elem(found, index); + } + else if(found->type == CONFIG_TYPE_GROUP) + { + const char *q = p; - while(! strchr(PATH_TOKENS, *p)) - p++; + while(*q && !strchr(PATH_TOKENS, *q)) + ++q; + + found = __config_list_search(found->value.list, p, (size_t)(q - p), + NULL); + p = q; + } + else + break; } - return(*p || (found == setting) ? NULL : found); + return((*p || (found == setting)) ? NULL : found); } /* ------------------------------------------------------------------------- */ @@ -1565,7 +1554,7 @@ config_setting_t *config_setting_get_member(const config_setting_t *setting, if(setting->type != CONFIG_TYPE_GROUP) return(NULL); - return(__config_list_search(setting->value.list, name, NULL)); + return(__config_list_search(setting->value.list, name, strlen(name), NULL)); } /* ------------------------------------------------------------------------- */ @@ -1676,9 +1665,11 @@ int config_setting_remove(config_setting_t *parent, const char *name) break; } - }while(*++settingName); + } + while(*++settingName); - if(!(setting = __config_list_search(setting->parent->value.list, settingName, &idx))) + if(!(setting = __config_list_search(setting->parent->value.list, settingName, + strlen(settingName), &idx))) return(CONFIG_FALSE); __config_list_remove(setting->parent->value.list, idx); diff --git a/tests/testdata/nesting.cfg b/tests/testdata/nesting.cfg new file mode 100644 index 0000000..3e37234 --- /dev/null +++ b/tests/testdata/nesting.cfg @@ -0,0 +1,6 @@ + +foo = ( + { string: "orange", number: 3, flag: false, array: [7, 13] }, + { string: "blue", number: 11, flag: true, array: [1, 2, 3, 4] }, + { string: "red", number: 7, flag: false, array: [77, 88] } +) diff --git a/tests/tests.c b/tests/tests.c index 7d37655..2b6d5e8 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -482,6 +482,15 @@ TT_TEST(EscapedStrings) TT_ASSERT_TRUE(ok); TT_ASSERT_STR_EQ("abc\"def\"", str); + ok = config_lookup_string(&cfg, "escape_seqs.dquote.[0]", &str); + TT_ASSERT_FALSE(ok); + + ok = config_lookup_string(&cfg, "escape_seqs.dquote.extrajunk", &str); + TT_ASSERT_FALSE(ok); + + ok = config_lookup_string(&cfg, "escape_seqs.dquote.", &str); + TT_ASSERT_TRUE(ok); + config_destroy(&cfg); } @@ -545,6 +554,90 @@ TT_TEST(OverrideSetting) /* ------------------------------------------------------------------------- */ +TT_TEST(SettingLookups) +{ + config_t cfg; + int ok; + int ival; + const char *str; + config_setting_t *setting, *parent; + + config_init(&cfg); + config_set_options(&cfg, CONFIG_OPTION_ALLOW_OVERRIDES); + config_set_include_dir(&cfg, "./testdata"); + + ok = config_read_file(&cfg, "testdata/nesting.cfg"); + if(!ok) + { + printf("error: %s:%d\n", config_error_text(&cfg), + config_error_line(&cfg)); + } + TT_ASSERT_TRUE(ok); + + ok = config_lookup_string(&cfg, "foo.[0].string", &str); + TT_ASSERT_TRUE(ok); + TT_ASSERT_STR_EQ("orange", str); + + ok = config_lookup_int(&cfg, "foo.[1].array.[3]", &ival); + TT_ASSERT_TRUE(ok); + TT_ASSERT_INT_EQ(4, ival); + + ok = config_lookup_bool(&cfg, "foo.[1].flag", &ival); + TT_ASSERT_TRUE(ok); + TT_ASSERT_INT_EQ(CONFIG_TRUE, ival); + + ok = config_lookup_int(&cfg, "foo.[2].number", &ival); + TT_ASSERT_TRUE(ok); + TT_ASSERT_INT_EQ(7, ival); + + ok = config_lookup_string(&cfg, "foo.[0].string.blah", &str); + TT_ASSERT_FALSE(ok); + + ok = config_lookup_string(&cfg, "foo.[0].string.[0]", &str); + TT_ASSERT_FALSE(ok); + + ok = config_lookup_string(&cfg, "foo.[0].[1]", &str); + TT_ASSERT_FALSE(ok); + + ok = config_lookup_string(&cfg, "foo.[0].array.[0].blah", &str); + TT_ASSERT_FALSE(ok); + + ok = config_lookup_string(&cfg, "[0]", &str); + TT_ASSERT_FALSE(ok); + + setting = config_lookup(&cfg, "foo.[0].array.[0]"); + TT_ASSERT_PTR_NOTNULL(setting); + + setting = config_lookup(&cfg, "foo.[0].array.[0"); + TT_ASSERT_PTR_NULL(setting); + + setting = config_lookup(&cfg, "/foo.[0].array.[0]"); + TT_ASSERT_PTR_NOTNULL(setting); + + setting = config_lookup(&cfg, "/foo/[0]/array/[0]"); + TT_ASSERT_PTR_NOTNULL(setting); + + parent = config_lookup(&cfg, ".foo"); + TT_ASSERT_PTR_NOTNULL(parent); + + setting = config_setting_lookup(parent, ".[0]"); + TT_ASSERT_PTR_NOTNULL(setting); + + setting = config_setting_lookup(parent, ".[0].array"); + TT_ASSERT_PTR_NOTNULL(setting); + + setting = config_setting_lookup(parent, ".[0].array.[1]"); + TT_ASSERT_PTR_NOTNULL(setting); + + setting = config_setting_lookup(parent, "[0].array.[1000]"); + TT_ASSERT_PTR_NULL(setting); + + setting = config_setting_lookup(parent, "[0].array.[0].blah"); + TT_ASSERT_PTR_NULL(setting); +} + +/* ------------------------------------------------------------------------- */ + int main(int argc, char **argv) { int failures; @@ -563,6 +656,7 @@ int main(int argc, char **argv) TT_SUITE_TEST(LibConfigTests, RemoveSetting); TT_SUITE_TEST(LibConfigTests, EscapedStrings); TT_SUITE_TEST(LibConfigTests, OverrideSetting); + TT_SUITE_TEST(LibConfigTests, SettingLookups); TT_SUITE_RUN(LibConfigTests); failures = TT_SUITE_NUM_FAILURES(LibConfigTests); TT_SUITE_END(LibConfigTests); -- 2.27.0