]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
strv: introduce strv_find_closest()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 20 Sep 2024 00:09:28 +0000 (09:09 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 20 Sep 2024 20:06:12 +0000 (05:06 +0900)
Follow-up for 1e1ac5d53b0f126b6c4419506c7c42b67c07537f.

src/basic/strv.c
src/basic/strv.h
src/firstboot/firstboot.c
src/test/test-strv.c

index 5b71c36dbfe74f5f6e7b898a1338600319d04bb6..414123e55862efdcbedc68c62328ddbaee241ce7 100644 (file)
@@ -66,7 +66,28 @@ char* strv_find_startswith(char * const *l, const char *name) {
         return NULL;
 }
 
-char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
+static char* strv_find_closest_prefix(char * const *l, const char *name) {
+        size_t best_distance = SIZE_MAX;
+        char *best = NULL;
+
+        assert(name);
+
+        STRV_FOREACH(s, l) {
+                char *e = startswith(*s, name);
+                if (!e)
+                        continue;
+
+                size_t n = strlen(e);
+                if (n < best_distance) {
+                        best_distance = n;
+                        best = *s;
+                }
+        }
+
+        return best;
+}
+
+static char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
         ssize_t best_distance = SSIZE_MAX;
         char *best = NULL;
 
@@ -93,6 +114,20 @@ char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
         return best;
 }
 
+char* strv_find_closest(char * const *l, const char *name) {
+        assert(name);
+
+        /* Be more helpful to the user, and give a hint what the user might have wanted to type. We search
+         * with two mechanisms: a simple prefix match and – if that didn't yield results –, a Levenshtein
+         * word distance based match. */
+
+        char *found = strv_find_closest_prefix(l, name);
+        if (found)
+                return found;
+
+        return strv_find_closest_by_levenshtein(l, name);
+}
+
 char* strv_find_first_field(char * const *needles, char * const *haystack) {
         STRV_FOREACH(k, needles) {
                 char *value = strv_env_pairs_get((char **)haystack, *k);
index b7845eeeccb27870633a5d860c6d468c524576a7..1afb1bf65963a101224d73fe32c4950c6e5fbbe6 100644 (file)
@@ -17,7 +17,7 @@ char* strv_find(char * const *l, const char *name) _pure_;
 char* strv_find_case(char * const *l, const char *name) _pure_;
 char* strv_find_prefix(char * const *l, const char *name) _pure_;
 char* strv_find_startswith(char * const *l, const char *name) _pure_;
-char* strv_find_closest_by_levenshtein(char * const *l, const char *name) _pure_;
+char* strv_find_closest(char * const *l, const char *name) _pure_;
 /* Given two vectors, the first a list of keys and the second a list of key-value pairs, returns the value
  * of the first key from the first vector that is found in the second vector. */
 char* strv_find_first_field(char * const *needles, char * const *haystack) _pure_;
index 3899835d4c6cc1b87179e947290bf2f18a67376c..236c64634c9a553e39886c31e0c0c896017ca2af 100644 (file)
@@ -187,7 +187,6 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i
 
         for (;;) {
                 _cleanup_free_ char *p = NULL;
-                char *best_match = NULL;
                 unsigned u;
 
                 r = ask_string(&p, "%s %s (empty to skip, \"list\" to list options): ",
@@ -223,13 +222,8 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i
                 if (is_valid(p))
                         return free_and_replace(*ret, p);
 
-                /* Be helperful to the user, and give a hint what the user might have wanted to
-                 * type. We search with two mechanisms: a simple prefix match and – if that didn't
-                 * yield results –, a Levenshtein word distance based match. */
-                best_match = strv_find_prefix(l, p);
-                if (!best_match)
-                        best_match = strv_find_closest_by_levenshtein(l, p);
-
+                /* Be more helpful to the user, and give a hint what the user might have wanted to type. */
+                const char *best_match = strv_find_closest(l, p);
                 if (best_match)
                         log_error("Invalid data '%s', did you mean '%s'?", p, best_match);
                 else
index f7b6ee2bb2d1306d32330dafe346fe3ac2cb0a95..7c25d59bf43a861d452f40ee76225c4d1b36ecfc 100644 (file)
@@ -1188,4 +1188,31 @@ TEST(strv_rebreak_lines) {
         }
 }
 
+TEST(strv_find_closest) {
+        char **l = STRV_MAKE("aaa", "aaaa", "bbb", "ccc");
+
+        /* prefix match */
+        ASSERT_STREQ(strv_find_closest(l, "a"),    "aaa");
+        ASSERT_STREQ(strv_find_closest(l, "aa"),   "aaa");
+        ASSERT_STREQ(strv_find_closest(l, "aaa"),  "aaa");
+        ASSERT_STREQ(strv_find_closest(l, "aaaa"), "aaaa");
+        ASSERT_STREQ(strv_find_closest(l, "b"),    "bbb");
+        ASSERT_STREQ(strv_find_closest(l, "bb"),   "bbb");
+        ASSERT_STREQ(strv_find_closest(l, "bbb"),  "bbb");
+        ASSERT_STREQ(strv_find_closest(l, "c"),    "ccc");
+        ASSERT_STREQ(strv_find_closest(l, "cc"),   "ccc");
+        ASSERT_STREQ(strv_find_closest(l, "ccc"),  "ccc");
+
+        /* levenshtein match */
+        ASSERT_STREQ(strv_find_closest(l, "aab"),  "aaa");
+        ASSERT_STREQ(strv_find_closest(l, "abb"),  "bbb");
+        ASSERT_STREQ(strv_find_closest(l, "cbc"),  "ccc");
+        ASSERT_STREQ(strv_find_closest(l, "aax"),  "aaa");
+        ASSERT_STREQ(strv_find_closest(l, "bbbb"), "bbb");
+        ASSERT_STREQ(strv_find_closest(l, "cbbb"), "bbb");
+        ASSERT_STREQ(strv_find_closest(l, "bbbx"), "bbb");
+
+        ASSERT_NULL(strv_find_closest(l, "sfajosajfosdjaofjdsaf"));
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);