CONTRIB: debug: also support reading values from stdin

This is convenient when processing large dumps, it allows to copy-paste
values to inspect from one window to another, or to directly transfer
a "show fd"/"show stream" output through sed. In order to do this, simply
pass "-" alone instead of the value and they will all be read one line at
a time from stdin. For example, in order to quickly print the different
set of connection flags from "show fd", this is sufficient:

     sed -ne 's/^.* cflg=\([^ ]*\).*/\1/p' | contrib/debug/flags conn -

(cherry picked from commit e4f80a076c2d52a8b73f2f7350e047f6e39acd69)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 5399df74ed4ca5e7d847575beb4ad5817f0da9b3)
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/contrib/debug/flags.c b/contrib/debug/flags.c
index 92facac..0a2fccf 100644
--- a/contrib/debug/flags.c
+++ b/contrib/debug/flags.c
@@ -412,7 +412,7 @@
 
 void usage_exit(const char *name)
 {
-	fprintf(stderr, "Usage: %s [ana|chn|conn|cs|si|sierr|strm|task|txn]* [0x]value*\n", name);
+	fprintf(stderr, "Usage: %s [ana|chn|conn|cs|si|sierr|strm|task|txn]* { [+-][0x]value* | - }\n", name);
 	exit(1);
 }
 
@@ -422,7 +422,10 @@
 	unsigned int show_as = 0;
 	unsigned int f;
 	const char *name = argv[0];
+	char line[20];
+	char *value;
 	int multi = 0;
+	int use_stdin = 0;
 	char *err;
 
 	while (argc > 0) {
@@ -442,14 +445,38 @@
 	if (argc > 1)
 		multi = 1;
 
+	if (strcmp(argv[0], "-") == 0)
+		use_stdin = 1;
+
 	while (argc > 0) {
-		flags = strtoul(argv[0], &err, 0);
-		if (!*argv[0] || *err) {
-			fprintf(stderr, "Unparsable value: <%s>\n", argv[0]);
+		if (use_stdin) {
+			value = fgets(line, sizeof(line), stdin);
+			if (!value)
+				break;
+
+			/* skip common leading delimitors that slip from copy-paste */
+			while (*value == ' ' || *value == '\t' || *value == ':' || *value == '=')
+				value++;
+
+			/* stop at the end of the number and trim any C suffix like "UL" */
+			err = value;
+			while (*err == '-' || *err == '+' ||
+			       (isalnum(*err) && toupper(*err) != 'U' && toupper(*err) != 'L'))
+				err++;
+			if (err)
+				*err = 0;
+		} else {
+			value = argv[0];
+			argv++; argc--;
+		}
+
+		flags = strtoul(value, &err, 0);
+		if (!*value || *err) {
+			fprintf(stderr, "Unparsable value: <%s>\n", value);
 			usage_exit(name);
 		}
 
-		if (multi)
+		if (multi || use_stdin)
 			printf("### 0x%08x:\n", flags);
 
 		if (show_as & SHOW_AS_ANA)   show_chn_ana(flags);
@@ -461,8 +488,6 @@
 		if (show_as & SHOW_AS_STRM)  show_strm_flags(flags);
 		if (show_as & SHOW_AS_TASK)  show_task_state(flags);
 		if (show_as & SHOW_AS_TXN)   show_txn_flags(flags);
-
-		argv++; argc--;
 	}
 	return 0;
 }