MINOR: cfgparse: sanitize the output a little bit

With the rework of the config line parser, we've started to emit a dump
of the initial line underlined by a caret character indicating the error
location. But with extremely large lines it starts to take time and can
even cause trouble to slow terminals (e.g. over ssh), and this becomes
useless. In addition, control characters could be dumped as-is which is
bad, especially when the input file is accidently wrong (an executable).

This patch adds a string sanitization function which isolates an area
around the error position in order to report only that area if the string
is too large. The limit was set to 80 characters, which will result in
roughly 40 chars around the error being reported only, prefixed and suffixed
with "..." as needed. In addition, non-printable characters in the line are
now replaced with '?' so as not to corrupt the terminal. This way invalid
variable names, unmatched quotes etc will be easier to spot.

A typical output is now:

  [ALERT] 176/092336 (23852) : parsing [bad.cfg:8]: forbidden first char in environment variable name at position 811957:
    ...c$PATH$PATH$d(xlc`%?$PATH$PATH$dgc?T$%$P?AH?$PATH$PATH$d(?$PATH$PATH$dgc?%...
                                            ^
diff --git a/src/tools.c b/src/tools.c
index 9641700..fa92db5 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -4990,6 +4990,51 @@
 }
 #undef EMIT_CHAR
 
+/* This is used to sanitize an input line that's about to be used for error reporting.
+ * It will adjust <line> to print approximately <width> chars around <pos>, trying to
+ * preserve the beginning, with leading or trailing "..." when the line is truncated.
+ * If non-printable chars are present in the output. It returns the new offset <pos>
+ * in the modified line. Non-printable characters are replaced with '?'. <width> must
+ * be at least 6 to support two "..." otherwise the result is undefined. The line
+ * itself must have at least 7 chars allocated for the same reason.
+ */
+size_t sanitize_for_printing(char *line, size_t pos, size_t width)
+{
+	size_t shift = 0;
+	char *out = line;
+	char *in = line;
+	char *end = line + width;
+
+	if (pos >= width) {
+		/* if we have to shift, we'll be out of context, so let's
+		 * try to put <pos> at the center of width.
+		 */
+		shift = pos - width / 2;
+		in += shift + 3;
+		end = out + width - 3;
+		out[0] = out[1] = out[2] = '.';
+		out += 3;
+	}
+
+	while (out < end && *in) {
+		if (isspace((unsigned char)*in))
+			*out++ = ' ';
+		else if (isprint((unsigned char)*in))
+			*out++ = *in;
+		else
+			*out++ = '?';
+		in++;
+	}
+
+	if (end < line + width) {
+		out[0] = out[1] = out[2] = '.';
+		out += 3;
+	}
+
+	*out++ = 0;
+	return pos - shift;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8