From 5ff6aecbdb7f85a8e7a2ea7fd63c03b4bdd42076 Mon Sep 17 00:00:00 2001
From: Mark Lindner <mark.a.lindner@gmail.com>
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

