[git commit] xxd: fix -p -r, closes 13881

Denys Vlasenko vda.linux at googlemail.com
Fri Oct 8 21:03:54 UTC 2021


commit: https://git.busybox.net/busybox/commit/?id=86ba007b84ae1ebe35e88c57e023caac3d2d9903
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
xxd_main                                             893     890      -3

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 testsuite/xxd.tests      |  6 ++++++
 util-linux/hexdump_xxd.c | 51 ++++++++++++++++++++++++++++++++++++------------
 2 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/testsuite/xxd.tests b/testsuite/xxd.tests
index 2e80be5fe..76fa96af9 100755
--- a/testsuite/xxd.tests
+++ b/testsuite/xxd.tests
@@ -31,4 +31,10 @@ testing 'xxd -p with 31 NULs' \
 	'' \
 	'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
 
+testing 'xxd -p -r' \
+	'xxd -p -r' \
+	'01234567765432100123456776543210' \
+	'' \
+	'30313233343536373736353433323130 30313233343536373736353433323130'
+
 exit $FAILCOUNT
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index fe78f6242..76dada983 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -69,7 +69,7 @@
 #define OPT_c (1 << 7)
 #define OPT_o (1 << 8)
 
-static void reverse(unsigned opt, unsigned cols, const char *filename)
+static void reverse(unsigned opt, const char *filename)
 {
 	FILE *fp;
 	char *buf;
@@ -77,9 +77,9 @@ static void reverse(unsigned opt, unsigned cols, const char *filename)
 	fp = filename ? xfopen_for_read(filename) : stdin;
 
 	while ((buf = xmalloc_fgetline(fp)) != NULL) {
-		char *p = buf;
-		unsigned cnt = cols;
+		char *p;
 
+		p = buf;
 		if (!(opt & OPT_p)) {
 			/* skip address */
 			while (isxdigit(*p)) p++;
@@ -92,9 +92,9 @@ static void reverse(unsigned opt, unsigned cols, const char *filename)
 		}
 
 		/* Process hex bytes optionally separated by whitespace */
-		do {
+		for (;;) {
 			uint8_t val, c;
-
+ nibble1:
 			p = skip_whitespace(p);
 
 			c = *p++;
@@ -102,8 +102,19 @@ static void reverse(unsigned opt, unsigned cols, const char *filename)
 				val = c - '0';
 			else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
 				val = (c|0x20) - ('a' - 10);
-			else
+			else {
+				/* xxd V1.10 is inconsistent here.
+				 *  echo -e "31 !3 0a 0a" | xxd -r -p
+				 * is "10<a0>" (no <cr>) - "!" is ignored,
+				 * but
+				 *  echo -e "31 !!343434\n30 0a" | xxd -r -p
+				 * is "10<cr>" - "!!" drops rest of the line.
+				 * We will ignore all invalid chars:
+				 */
+				if (c != '\0')
+					goto nibble1;
 				break;
+			}
 			val <<= 4;
 
 			/* Works the same with xxd V1.10:
@@ -111,6 +122,7 @@ static void reverse(unsigned opt, unsigned cols, const char *filename)
 			 *  echo "31 0 9 32 0a" | xxd -r -p
 			 * thus allow whitespace even within the byte:
 			 */
+ nibble2:
 			p = skip_whitespace(p);
 
 			c = *p++;
@@ -118,10 +130,23 @@ static void reverse(unsigned opt, unsigned cols, const char *filename)
 				val |= c - '0';
 			else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
 				val |= (c|0x20) - ('a' - 10);
-			else
-				break;
+			else {
+				if (c != '\0') {
+					/* "...3<not_hex_char>..." ignores both chars */
+					goto nibble1;
+				}
+				/* Nibbles can join even through newline:
+				 * echo -e "31 3\n2 0a" | xxd -r -p
+				 * is "12<cr>".
+				 */
+				free(buf);
+				p = buf = xmalloc_fgetline(fp);
+				if (!buf)
+					break;
+				goto nibble2;
+			}
 			putchar(val);
-		} while (!(opt & OPT_p) || --cnt != 0);
+		}
 		free(buf);
 	}
 	//fclose(fp);
@@ -174,6 +199,10 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
 		//BUGGY for /proc/version (unseekable?)
 	}
 
+	if (opt & OPT_r) {
+		reverse(opt, argv[0]);
+	}
+
 	if (opt & OPT_o) {
 		/* -o accepts negative numbers too */
 		dumper->xxd_displayoff = xstrtoll(opt_o, /*base:*/ 0);
@@ -194,10 +223,6 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
 			bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: "
 	}
 
-	if (opt & OPT_r) {
-		reverse(opt, cols, argv[0]);
-	}
-
 	if (bytes < 1 || bytes >= cols) {
 		sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "XX"
 		bb_dump_add(dumper, buf);


More information about the busybox-cvs mailing list