MINOR: cfgparse: suggest correct spelling for unknown words in global section

The global section also knows a large number of keywords that are not
referenced in any list, so this needed them to be specifically listed.
It becomes particularly handy now because some tunables are never easy
to remember, but now it works remarkably well:

  $ printf "global\nsched.queue_depth\n" | ./haproxy -c -f /dev/stdin
  [NOTICE] 070/093007 (23457) : haproxy version is 2.4-dev11-dd8ee5-24
  [NOTICE] 070/093007 (23457) : path to executable is ./haproxy
  [ALERT] 070/093007 (23457) : parsing [/dev/stdin:2] : unknown keyword 'sched.queue_depth' in 'global' section; did you mean 'tune.runqueue-depth' maybe ?
  [ALERT] 070/093007 (23457) : Error(s) found in configuration file : /dev/stdin
  [ALERT] 070/093007 (23457) : Fatal errors found in configuration.
diff --git a/src/cfgparse-global.c b/src/cfgparse-global.c
index ddb5e4c..fc56fbf 100644
--- a/src/cfgparse-global.c
+++ b/src/cfgparse-global.c
@@ -19,6 +19,32 @@
 #include <haproxy/peers.h>
 #include <haproxy/tools.h>
 
+/* some keywords that are still being parsed using strcmp() and are not
+ * registered anywhere. They are used as suggestions for mistyped words.
+ */
+static const char *common_kw_list[] = {
+	"global", "daemon", "master-worker", "noepoll", "nokqueue",
+	"noevports", "nopoll", "busy-polling", "set-dumpable",
+	"insecure-fork-wanted", "insecure-setuid-wanted", "nosplice",
+	"nogetaddrinfo", "noreuseport", "quiet", "zero-warning",
+	"tune.runqueue-depth", "tune.maxpollevents", "tune.maxaccept",
+	"tune.chksize", "tune.recv_enough", "tune.buffers.limit",
+	"tune.buffers.reserve", "tune.bufsize", "tune.maxrewrite",
+	"tune.idletimer", "tune.rcvbuf.client", "tune.rcvbuf.server",
+	"tune.sndbuf.client", "tune.sndbuf.server", "tune.pipesize",
+	"tune.http.cookielen", "tune.http.logurilen", "tune.http.maxhdr",
+	"tune.comp.maxlevel", "tune.pattern.cache-size", "uid", "gid",
+	"external-check", "user", "group", "nbproc", "nbthread", "maxconn",
+	"ssl-server-verify", "maxconnrate", "maxsessrate", "maxsslrate",
+	"maxcomprate", "maxpipes", "maxzlibmem", "maxcompcpuusage", "ulimit-n",
+	"chroot", "description", "node", "pidfile", "unix-bind", "log",
+	"log-send-hostname", "server-state-base", "server-state-file",
+	"log-tag", "spread-checks", "max-spread-checks", "cpu-map", "setenv",
+	"presetenv", "unsetenv", "resetenv", "strict-limits", "localpeer",
+	"defaults", "listen", "frontend", "backend", "peers", "resolvers",
+	NULL /* must be last */
+};
+
 /*
  * parse a line in a <global> section. Returns the error code, 0 if OK, or
  * any combination of :
@@ -1235,6 +1261,7 @@
 	}
 	else {
 		struct cfg_kw_list *kwl;
+		const char *best;
 		int index;
 		int rc;
 
@@ -1258,7 +1285,11 @@
 			}
 		}
 		
-		ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "global");
+		best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_LISTEN, common_kw_list);
+		if (best)
+			ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
+		else
+			ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "global");
 		err_code |= ERR_ALERT | ERR_FATAL;
 	}