MINOR: cfgparse/proxy: also support spelling fixes on options

Some are not always easy to spot with "chk" vs "check" or hyphens at
some places and not at others. Now entering "option http-close" properly
suggests "httpclose" and "option tcp-chk" suggests "tcp-check". There's
no need to consider the proxy's capabilities, what matters is to figure
what related word the user tried to spell, and there are not that many
options anyway.
diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h
index 69cde1d..b81783a 100644
--- a/include/haproxy/proxy.h
+++ b/include/haproxy/proxy.h
@@ -49,6 +49,7 @@
 
 const char *proxy_cap_str(int cap);
 const char *proxy_mode_str(int mode);
+const char *proxy_find_best_option(const char *word, const char **extra);
 void proxy_store_name(struct proxy *px);
 struct proxy *proxy_find_by_id(int id, int cap, int table);
 struct proxy *proxy_find_by_name(const char *name, int cap, int table);
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index ac50025..0787833 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -54,6 +54,15 @@
 	NULL /* must be last */
 };
 
+static const char *common_options[] = {
+	"httpclose", "forceclose", "http-server-close", "http-keep-alive",
+	"http-tunnel", "redispatch", "httplog", "tcplog", "tcpka", "httpchk",
+	"ssl-hello-chk", "smtpchk", "pgsql-check", "redis-check",
+	"mysql-check", "ldap-check", "spop-check", "tcp-check",
+	"external-check", "forwardfor", "original-to",
+	NULL /* must be last */
+};
+
 /* Report a warning if a rule is placed after a 'tcp-request session' rule.
  * Return 1 if the warning has been emitted, otherwise 0.
  */
@@ -2238,7 +2247,13 @@
 			} /* end while loop */
 		}
 		else {
-			ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
+			const char *best = proxy_find_best_option(args[1], common_options);
+
+			if (best)
+				ha_alert("parsing [%s:%d] : unknown option '%s'; did you mean '%s' maybe ?\n", file, linenum, args[1], best);
+			else
+				ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
+
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
diff --git a/src/proxy.c b/src/proxy.c
index a9a4943..843941e 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -152,6 +152,55 @@
 		return "unknown";
 }
 
+/* try to find among known options the one that looks closest to <word> by
+ * counting transitions between letters, digits and other characters. Will
+ * return the best matching word if found, otherwise NULL. An optional array
+ * of extra words to compare may be passed in <extra>, but it must then be
+ * terminated by a NULL entry. If unused it may be NULL.
+ */
+const char *proxy_find_best_option(const char *word, const char **extra)
+{
+	uint8_t word_sig[1024];
+	uint8_t list_sig[1024];
+	const char *best_ptr = NULL;
+	int dist, best_dist = INT_MAX;
+	int index;
+
+	make_word_fingerprint(word_sig, word);
+
+	for (index = 0; cfg_opts[index].name; index++) {
+		make_word_fingerprint(list_sig, cfg_opts[index].name);
+		dist = word_fingerprint_distance(word_sig, list_sig);
+		if (dist < best_dist) {
+			best_dist = dist;
+			best_ptr = cfg_opts[index].name;
+		}
+	}
+
+	for (index = 0; cfg_opts2[index].name; index++) {
+		make_word_fingerprint(list_sig, cfg_opts2[index].name);
+		dist = word_fingerprint_distance(word_sig, list_sig);
+		if (dist < best_dist) {
+			best_dist = dist;
+			best_ptr = cfg_opts2[index].name;
+		}
+	}
+
+	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_ptr = *extra;
+		}
+		extra++;
+	}
+
+	if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr)))
+		best_ptr = NULL;
+	return best_ptr;
+}
+
 /*
  * This function scans the list of backends and servers to retrieve the first
  * backend and the first server with the given names, and sets them in both