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;
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);
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_;
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): ",
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
}
}
+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);