diff --git a/include/haproxy/action.h b/include/haproxy/action.h index 7338a75e1..a4bec01eb 100644 --- a/include/haproxy/action.h +++ b/include/haproxy/action.h @@ -29,6 +29,7 @@ int act_resolution_cb(struct resolv_requester *requester, struct dns_counters *counters); int act_resolution_error_cb(struct resolv_requester *requester, int error_code); +const char *action_suggest(const char *word, const struct list *keywords, const char **extra); static inline struct action_kw *action_lookup(struct list *keywords, const char *kw) { diff --git a/src/action.c b/src/action.c index 8292fe90c..c81737642 100644 --- a/src/action.c +++ b/src/action.c @@ -206,3 +206,56 @@ int cfg_parse_rule_set_timeout(const char **args, int idx, int *out_timeout, return 0; } + +/* tries to find in list a similar looking action as the one in + * , and returns it otherwise NULL. may be NULL or empty. An + * optional array of extra words to compare may be passed in , but it + * must then be terminated by a NULL entry. If unused it may be NULL. + */ +const char *action_suggest(const char *word, const struct list *keywords, const char **extra) +{ + uint8_t word_sig[1024]; + uint8_t list_sig[1024]; + const struct action_kw_list *kwl; + const struct action_kw *best_kw = NULL; + const char *best_ptr = NULL; + int dist, best_dist = INT_MAX; + int index; + + if (!word || !*word) + return NULL; + + make_word_fingerprint(word_sig, word); + list_for_each_entry(kwl, keywords, list) { + for (index = 0; kwl->kw[index].kw != NULL; index++) { + make_word_fingerprint(list_sig, kwl->kw[index].kw); + dist = word_fingerprint_distance(word_sig, list_sig); + if (dist < best_dist) { + best_dist = dist; + best_kw = &kwl->kw[index]; + best_ptr = best_kw->kw; + } + } + } + + while (extra && *extra) { + make_word_fingerprint(list_sig, *extra); + dist = word_fingerprint_distance(word_sig, list_sig); + if (dist < best_dist) { + best_dist = dist; + best_kw = NULL; + best_ptr = *extra; + } + extra++; + } + + /* eliminate too different ones, with more tolerance for prefixes + * when they're known to exist (not from extra list). + */ + if (best_ptr && + (best_dist > (2 + (best_kw && best_kw->match_pfx)) * strlen(word) || + best_dist > (2 + (best_kw && best_kw->match_pfx)) * strlen(best_ptr))) + best_ptr = NULL; + + return best_ptr; +}