summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko2022-08-22 15:40:47 +0200
committerDenys Vlasenko2022-08-22 15:40:47 +0200
commitf318adaaab3288fe72cb853bf7ede56790a13182 (patch)
tree417db541833ecac1843e0c8f7534b5d23e9bb83e
parent5a9d2b6e024e6c20d4d7b8c170985554c0df043d (diff)
downloadbusybox-f318adaaab3288fe72cb853bf7ede56790a13182.zip
busybox-f318adaaab3288fe72cb853bf7ede56790a13182.tar.gz
xxd -r: without -p, stop at more than one whitespace, closes 14786
function old new delta xxd_main 888 1076 +188 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rwxr-xr-xtestsuite/xxd.tests9
-rw-r--r--util-linux/hexdump_xxd.c56
2 files changed, 48 insertions, 17 deletions
diff --git a/testsuite/xxd.tests b/testsuite/xxd.tests
index 76fa96a..359e7f8 100755
--- a/testsuite/xxd.tests
+++ b/testsuite/xxd.tests
@@ -37,4 +37,13 @@ testing 'xxd -p -r' \
'' \
'30313233343536373736353433323130 30313233343536373736353433323130'
+testing 'xxd -r skips leading whitespace and truncates at two spaces' \
+ 'xxd -r' \
+ '0123456789:;<=>?@' \
+ '' \
+"\
+ 00000000: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
+ 00000010: 40 @
+"
+
exit $FAILCOUNT
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index 4372ac7..dbda34b 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -76,12 +76,14 @@ static void reverse(unsigned opt, const char *filename)
fp = filename ? xfopen_for_read(filename) : stdin;
+ get_new_line:
while ((buf = xmalloc_fgetline(fp)) != NULL) {
char *p;
p = buf;
if (!(opt & OPT_p)) {
- /* skip address */
+ skip_address:
+ p = skip_whitespace(p);
while (isxdigit(*p)) p++;
/* NB: for xxd -r, first hex portion is address even without colon */
/* If it's there, skip it: */
@@ -94,36 +96,45 @@ static void reverse(unsigned opt, const char *filename)
/* Process hex bytes optionally separated by whitespace */
for (;;) {
uint8_t val, c;
+ int badchar = 0;
nibble1:
- p = skip_whitespace(p);
-
+ if (opt & OPT_p)
+ p = skip_whitespace(p);
c = *p++;
if (isdigit(c))
val = c - '0';
else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
val = (c|0x20) - ('a' - 10);
else {
- /* xxd V1.10 is inconsistent here.
+ /* xxd V1.10 allows one non-hexnum char:
* echo -e "31 !3 0a 0a" | xxd -r -p
* is "10<a0>" (no <cr>) - "!" is ignored,
- * but
+ * but stops for more than one:
* echo -e "31 !!343434\n30 0a" | xxd -r -p
* is "10<cr>" - "!!" drops rest of the line.
- * We will ignore all invalid chars:
+ * Note: this also covers whitespace chars:
+ * xxxxxxxx: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
+ * detects this ^ - skips this one space
+ * xxxxxxxx: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
+ * detects this ^^ - skips the rest
*/
- if (c != '\0')
- goto nibble1;
- break;
+ if (c == '\0' || badchar)
+ break;
+ badchar++;
+ goto nibble1;
}
val <<= 4;
- /* Works the same with xxd V1.10:
- * echo "31 09 32 0a" | xxd -r -p
- * echo "31 0 9 32 0a" | xxd -r -p
- * thus allow whitespace even within the byte:
- */
nibble2:
- p = skip_whitespace(p);
+ if (opt & OPT_p) {
+ /* Works the same with xxd V1.10:
+ * echo "31 09 32 0a" | xxd -r -p
+ * echo "31 0 9 32 0a" | xxd -r -p
+ * thus allow whitespace (even multiple chars)
+ * after byte's 1st char:
+ */
+ p = skip_whitespace(p);
+ }
c = *p++;
if (isdigit(c))
@@ -132,7 +143,16 @@ static void reverse(unsigned opt, const char *filename)
val |= (c|0x20) - ('a' - 10);
else {
if (c != '\0') {
- /* "...3<not_hex_char>..." ignores both chars */
+ /* "...3<not_hex_char>...": ignore "3",
+ * skip everything up to next hexchar or newline:
+ */
+ while (!isxdigit(*p)) {
+ if (*p == '\0') {
+ free(buf);
+ goto get_new_line;
+ }
+ p++;
+ }
goto nibble1;
}
/* Nibbles can join even through newline:
@@ -143,10 +163,12 @@ static void reverse(unsigned opt, const char *filename)
p = buf = xmalloc_fgetline(fp);
if (!buf)
break;
+ if (!(opt & OPT_p)) /* -p and !-p: different behavior */
+ goto skip_address;
goto nibble2;
}
putchar(val);
- }
+ } /* for(;;) */
free(buf);
}
//fclose(fp);