MINOR: cfgparse/bind: suggest correct spelling for unknown bind keywords
Just like with the server keywords, now's the turn of "bind" keywords.
The difference is that 100% of the bind keywords are registered, thus
we do not need the list of extra keywords.
There are multiple bind line parsers today, all were updated:
- peers
- log
- dgram-bind
- cli
$ printf "listen f\nbind :8000 tcut\n" | ./haproxy -c -f /dev/stdin
[NOTICE] 070/101358 (25146) : haproxy version is 2.4-dev11-7b8787-26
[NOTICE] 070/101358 (25146) : path to executable is ./haproxy
[ALERT] 070/101358 (25146) : parsing [/dev/stdin:2] : 'bind :8000' unknown keyword 'tcut'; did you mean 'tcp-ut' maybe ?
[ALERT] 070/101358 (25146) : Error(s) found in configuration file : /dev/stdin
[ALERT] 070/101358 (25146) : Fatal errors found in configuration.
diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h
index ee02b0c..e4cea50 100644
--- a/include/haproxy/listener.h
+++ b/include/haproxy/listener.h
@@ -167,6 +167,7 @@
/* Dumps all registered "bind" keywords to the <out> string pointer. */
void bind_dump_kws(char **out);
+const char *bind_find_best_kw(const char *word);
void bind_recount_thread_bits(struct bind_conf *conf);
unsigned int bind_map_thread_id(const struct bind_conf *conf, unsigned int r);
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index b60741a..ac50025 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -389,9 +389,8 @@
cur_arg = 2;
while (*(args[cur_arg])) {
- static int bind_dumped;
struct bind_kw *kw;
- char *err;
+ const char *best;
kw = bind_find_kw(args[cur_arg]);
if (kw) {
@@ -431,17 +430,13 @@
continue;
}
- err = NULL;
- if (!bind_dumped) {
- bind_dump_kws(&err);
- indent_msg(&err, 4);
- bind_dumped = 1;
- }
-
- ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.%s%s\n",
- file, linenum, args[0], args[1], args[cur_arg],
- err ? " Registered keywords :" : "", err ? err : "");
- free(err);
+ best = bind_find_best_kw(args[cur_arg]);
+ if (best)
+ ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'; did you mean '%s' maybe ?\n",
+ file, linenum, args[0], args[1], args[cur_arg], best);
+ else
+ ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.\n",
+ file, linenum, args[0], args[1], args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 9c8efa2..269b47d 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -609,7 +609,6 @@
if (strcmp(args[0], "bind") == 0 || strcmp(args[0], "default-bind") == 0) {
int cur_arg;
- static int kws_dumped;
struct bind_conf *bind_conf;
struct bind_kw *kw;
@@ -689,17 +688,13 @@
cur_arg += 1 + kw->skip;
}
if (*args[cur_arg] != 0) {
- char *kws = NULL;
-
- if (!kws_dumped) {
- kws_dumped = 1;
- bind_dump_kws(&kws);
- indent_msg(&kws, 4);
- }
- ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.%s%s\n",
- file, linenum, args[cur_arg], cursection,
- kws ? " Registered keywords :" : "", kws ? kws: "");
- free(kws);
+ const char *best = bind_find_best_kw(args[cur_arg]);
+ if (best)
+ ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
+ file, linenum, args[cur_arg], cursection, best);
+ else
+ ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
+ file, linenum, args[cur_arg], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
diff --git a/src/cli.c b/src/cli.c
index 72919ce..04c4e7a 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -290,8 +290,8 @@
cur_arg = 3;
while (*args[cur_arg]) {
- static int bind_dumped;
struct bind_kw *kw;
+ const char *best;
kw = bind_find_kw(args[cur_arg]);
if (kw) {
@@ -314,15 +314,13 @@
continue;
}
- if (!bind_dumped) {
- bind_dump_kws(err);
- indent_msg(err, 4);
- bind_dumped = 1;
- }
-
- memprintf(err, "'%s %s' : unknown keyword '%s'.%s%s",
- args[0], args[1], args[cur_arg],
- err && *err ? " Registered keywords :" : "", err && *err ? *err : "");
+ best = bind_find_best_kw(args[cur_arg]);
+ if (best)
+ memprintf(err, "'%s %s' : unknown keyword '%s'. Did you mean '%s' maybe ?",
+ args[0], args[1], args[cur_arg], best);
+ else
+ memprintf(err, "'%s %s' : unknown keyword '%s'.",
+ args[0], args[1], args[cur_arg]);
return -1;
}
@@ -2656,8 +2654,8 @@
cur_arg = 1;
while (*args[cur_arg]) {
- static int bind_dumped;
struct bind_kw *kw;
+ const char *best;
kw = bind_find_kw(args[cur_arg]);
if (kw) {
@@ -2680,15 +2678,13 @@
continue;
}
- if (!bind_dumped) {
- bind_dump_kws(&err);
- indent_msg(&err, 4);
- bind_dumped = 1;
- }
-
- memprintf(&err, "'%s %s' : unknown keyword '%s'.%s%s",
- args[0], args[1], args[cur_arg],
- err ? " Registered keywords :" : "", err ? err : "");
+ best = bind_find_best_kw(args[cur_arg]);
+ if (best)
+ memprintf(&err, "'%s %s' : unknown keyword '%s'. Did you mean '%s' maybe ?",
+ args[0], args[1], args[cur_arg], best);
+ else
+ memprintf(&err, "'%s %s' : unknown keyword '%s'.",
+ args[0], args[1], args[cur_arg]);
goto err;
}
diff --git a/src/listener.c b/src/listener.c
index 4dfaa7f..882d17f 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -1261,6 +1261,37 @@
}
}
+/* Try to find in srv_keyword the word that looks closest to <word> by counting
+ * transitions between letters, digits and other characters. Will return the
+ * best matching word if found, otherwise NULL.
+ */
+const char *bind_find_best_kw(const char *word)
+{
+ uint8_t word_sig[1024];
+ uint8_t list_sig[1024];
+ const struct bind_kw_list *kwl;
+ const char *best_ptr = NULL;
+ int dist, best_dist = INT_MAX;
+ int index;
+
+ make_word_fingerprint(word_sig, word);
+ list_for_each_entry(kwl, &bind_keywords.list, 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_ptr = kwl->kw[index].kw;
+ }
+ }
+ }
+
+ if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr)))
+ best_ptr = NULL;
+
+ return best_ptr;
+}
+
/************************************************************************/
/* All supported sample and ACL keywords must be declared here. */
/************************************************************************/
diff --git a/src/log.c b/src/log.c
index 2bf7f15..c771218 100644
--- a/src/log.c
+++ b/src/log.c
@@ -3894,7 +3894,6 @@
}
else if (strcmp(args[0], "bind") == 0) {
int cur_arg;
- static int kws_dumped;
struct bind_conf *bind_conf;
struct bind_kw *kw;
struct listener *l;
@@ -3949,24 +3948,19 @@
cur_arg += 1 + kw->skip;
}
if (*args[cur_arg] != 0) {
- char *kws = NULL;
-
- if (!kws_dumped) {
- kws_dumped = 1;
- bind_dump_kws(&kws);
- indent_msg(&kws, 4);
- }
- ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.%s%s\n",
- file, linenum, args[cur_arg], cursection,
- kws ? " Registered keywords :" : "", kws ? kws: "");
- free(kws);
+ const char *best = bind_find_best_kw(args[cur_arg]);
+ if (best)
+ ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
+ file, linenum, args[cur_arg], cursection, best);
+ else
+ ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
+ file, linenum, args[cur_arg], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
}
else if (strcmp(args[0], "dgram-bind") == 0) {
int cur_arg;
- static int kws_dumped;
struct bind_conf *bind_conf;
struct bind_kw *kw;
struct listener *l;
@@ -4015,17 +4009,13 @@
cur_arg += 1 + kw->skip;
}
if (*args[cur_arg] != 0) {
- char *kws = NULL;
-
- if (!kws_dumped) {
- kws_dumped = 1;
- bind_dump_kws(&kws);
- indent_msg(&kws, 4);
- }
- ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.%s%s\n",
- file, linenum, args[cur_arg], cursection,
- kws ? " Registered keywords :" : "", kws ? kws: "");
- free(kws);
+ const char *best = bind_find_best_kw(args[cur_arg]);
+ if (best)
+ ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n",
+ file, linenum, args[cur_arg], cursection, best);
+ else
+ ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n",
+ file, linenum, args[cur_arg], cursection);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}