return NULL;
}
+char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
+ ssize_t best_distance = SSIZE_MAX;
+ char *best = NULL;
+
+ assert(name);
+
+ STRV_FOREACH(i, l) {
+ ssize_t distance;
+
+ distance = strlevenshtein(*i, name);
+ if (distance < 0) {
+ log_debug_errno(distance, "Failed to determine Levenshtein distance between %s and %s: %m", *i, name);
+ return NULL;
+ }
+
+ if (distance > 5) /* If the distance is just too far off, don't make a bad suggestion */
+ continue;
+
+ if (distance < best_distance) {
+ best_distance = distance;
+ best = *i;
+ }
+ }
+
+ return best;
+}
+
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_;
/* 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): ",
return free_and_strdup_warn(ret, l[u-1]);
}
- if (!is_valid(p)) {
- log_error("Entered data invalid.");
- continue;
- }
+ 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);
- return free_and_replace(*ret, p);
+ if (best_match)
+ log_error("Invalid data '%s', did you mean '%s'?", p, best_match);
+ else
+ log_error("Invalid data '%s'.", p);
}
}