Compare commits
3 Commits
a43f8ef333
...
25e9f95960
Author | SHA1 | Date |
---|---|---|
Michael Ferrari | 25e9f95960 | |
Michael Ferrari | 17393505b1 | |
Michael Ferrari | 17c53ebf20 |
|
@ -66,6 +66,33 @@ char* strv_find_startswith(char * const *l, const char *name) {
|
|||
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);
|
||||
|
|
|
@ -17,6 +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_;
|
||||
/* 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_;
|
||||
|
|
|
@ -178,24 +178,31 @@ static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned perc
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*is_valid)(const char *name), char **ret) {
|
||||
static int prompt_loop(const char *text, char **l, const char* fallback, unsigned percentage, bool (*is_valid)(const char *name), char **ret) {
|
||||
int r;
|
||||
|
||||
assert(text);
|
||||
assert(is_valid);
|
||||
assert(is_valid(fallback));
|
||||
assert(ret);
|
||||
|
||||
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): ",
|
||||
special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), text);
|
||||
r = ask_string(&p, "%s %s (empty to default to \"%s\", \"-\" to skip, \"list\" to list options): ",
|
||||
special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), text, fallback);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query user: %m");
|
||||
|
||||
if (isempty(p)) {
|
||||
log_info("No data entered, skipping.");
|
||||
log_info("No data entered, using default.");
|
||||
return free_and_strdup_warn(ret, fallback);
|
||||
}
|
||||
|
||||
if (streq(p, "-")) {
|
||||
log_info("Skip requested, continuing.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -219,12 +226,20 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i
|
|||
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);
|
||||
|
||||
if (best_match)
|
||||
log_error("Invalid data '%s', did you mean '%s'?", p, best_match);
|
||||
else
|
||||
log_error("Invalid data '%s'.", p);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,7 +380,7 @@ static int prompt_locale(int rfd) {
|
|||
print_welcome(rfd);
|
||||
|
||||
r = prompt_loop("Please enter system locale name or number",
|
||||
locales, 60, is_valid, &arg_locale);
|
||||
locales, SYSTEMD_DEFAULT_LOCALE, 60, is_valid, &arg_locale);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -373,7 +388,7 @@ static int prompt_locale(int rfd) {
|
|||
return 0;
|
||||
|
||||
r = prompt_loop("Please enter system message locale name or number",
|
||||
locales, 60, is_valid, &arg_locale_messages);
|
||||
locales, arg_locale, 60, is_valid, &arg_locale_messages);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -488,7 +503,7 @@ static int prompt_keymap(int rfd) {
|
|||
print_welcome(rfd);
|
||||
|
||||
return prompt_loop("Please enter system keymap name or number",
|
||||
kmaps, 60, determine_keymap_validity_func(rfd), &arg_keymap);
|
||||
kmaps, SYSTEMD_DEFAULT_KEYMAP, 60, determine_keymap_validity_func(rfd), &arg_keymap);
|
||||
}
|
||||
|
||||
static int process_keymap(int rfd) {
|
||||
|
@ -578,7 +593,7 @@ static int prompt_timezone(int rfd) {
|
|||
print_welcome(rfd);
|
||||
|
||||
r = prompt_loop("Please enter timezone name or number",
|
||||
zones, 30, timezone_is_valid_log_debug, &arg_timezone);
|
||||
zones, "UTC", 30, timezone_is_valid_log_debug, &arg_timezone);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -660,14 +675,24 @@ static int prompt_hostname(int rfd) {
|
|||
putchar('\n');
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *h = NULL;
|
||||
_cleanup_free_ char *h = NULL, *fallback = NULL;
|
||||
|
||||
r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
|
||||
fallback = get_default_hostname();
|
||||
if (!fallback)
|
||||
return log_oom();
|
||||
|
||||
r = ask_string(&h, "%s Please enter hostname for new system (empty to default to \"%s\", \"-\" to skip): ",
|
||||
special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), fallback);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query hostname: %m");
|
||||
|
||||
if (isempty(h)) {
|
||||
log_info("No hostname entered, skipping.");
|
||||
log_info("No hostname entered, using default.");
|
||||
return free_and_strdup_warn(&arg_hostname, fallback);
|
||||
}
|
||||
|
||||
if (streq(h, "-")) {
|
||||
log_info("Skip requested, continuing.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -869,12 +894,18 @@ static int prompt_root_shell(int rfd) {
|
|||
for (;;) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
r = ask_string(&s, "%s Please enter root shell for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
|
||||
r = ask_string(&s, "%s Please enter root shell for new system (empty to default to \"%s\", \"-\" to skip): ",
|
||||
special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), DEFAULT_USER_SHELL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query root shell: %m");
|
||||
|
||||
if (isempty(s)) {
|
||||
log_info("No shell entered, skipping.");
|
||||
log_info("No shell entered, using default.");
|
||||
return free_and_strdup_warn(&arg_root_shell, DEFAULT_USER_SHELL);
|
||||
}
|
||||
|
||||
if (streq(s, "-")) {
|
||||
log_info("Skip requested, continuing.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue