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" (no ) - "!" is ignored, + * but + * echo -e "31 !!343434\n30 0a" | xxd -r -p + * is "10" - "!!" 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..." ignores both chars */ + goto nibble1; + } + /* Nibbles can join even through newline: + * echo -e "31 3\n2 0a" | xxd -r -p + * is "12". + */ + 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);