MINOR: debug: add a special converter which display its input sample content.

This converter displays its input sample type and content. It is useful
for debugging some complex configurations.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 59425aa..163e873 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -10930,6 +10930,11 @@
 	default_backend servers
 	http-request set-header X-DeviceAtlas-Data %[req.fhdr(User-Agent),da-csv(primaryHardwareType,osName,osVersion,browserName,browserVersion)]
 
+debug
+  This converter is used as debug tool. It dumps on screen the content and the
+  type of the input sample. The sample is returned as is on its output. This
+  converter only exists when haproxy was built with debugging enabled.
+
 div(<value>)
   Divides the input value of type unsigned integer by <value>, and returns the
   result as an unsigned integer. If <value> is null, the largest unsigned
diff --git a/src/sample.c b/src/sample.c
index cf247c2..1ecc266 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1389,6 +1389,44 @@
 /*    These functions set the data type on return.               */
 /*****************************************************************/
 
+#ifdef DEBUG_EXPR
+static int sample_conv_debug(const struct arg *arg_p, struct sample *smp, void *private)
+{
+	int i;
+	struct sample tmp;
+
+	if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
+		fprintf(stderr, "[debug converter] type: %s ", smp_to_type[smp->type]);
+		if (!sample_casts[smp->type][SMP_T_STR]) {
+			fprintf(stderr, "(undisplayable)");
+		} else {
+
+			/* Copy sample fetch. This put the sample as const, the
+			 * cast will copy data if a transformation is required.
+			 */
+			memcpy(&tmp, smp, sizeof(struct sample));
+			tmp.flags = SMP_F_CONST;
+
+			if (!sample_casts[smp->type][SMP_T_STR](&tmp))
+				fprintf(stderr, "(undisplayable)");
+
+			else {
+				/* Display the displayable chars*. */
+				fprintf(stderr, "<");
+				for (i = 0; i < tmp.data.str.len; i++) {
+					if (isprint(tmp.data.str.str[i]))
+						fputc(tmp.data.str.str[i], stderr);
+					else
+						fputc('.', stderr);
+				}
+			}
+			fprintf(stderr, ">\n");
+		}
+	}
+	return 1;
+}
+#endif
+
 static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp, void *private)
 {
 	struct chunk *trash = get_trash_chunk();
@@ -2387,6 +2425,10 @@
 
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct sample_conv_kw_list sample_conv_kws = {ILH, {
+#ifdef DEBUG_EXPR
+	{ "debug",  sample_conv_debug,     0,            NULL, SMP_T_ANY,  SMP_T_ANY  },
+#endif
+
 	{ "base64", sample_conv_bin2base64,0,            NULL, SMP_T_BIN,  SMP_T_STR  },
 	{ "upper",  sample_conv_str2upper, 0,            NULL, SMP_T_STR,  SMP_T_STR  },
 	{ "lower",  sample_conv_str2lower, 0,            NULL, SMP_T_STR,  SMP_T_STR  },