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>
This commit is contained in:
Denys Vlasenko 2022-08-22 15:40:47 +02:00
parent 5a9d2b6e02
commit f318adaaab
2 changed files with 48 additions and 17 deletions

View File

@ -37,4 +37,13 @@ testing 'xxd -p -r' \
'' \ '' \
'30313233343536373736353433323130 30313233343536373736353433323130' '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 exit $FAILCOUNT

View File

@ -76,12 +76,14 @@ static void reverse(unsigned opt, const char *filename)
fp = filename ? xfopen_for_read(filename) : stdin; fp = filename ? xfopen_for_read(filename) : stdin;
get_new_line:
while ((buf = xmalloc_fgetline(fp)) != NULL) { while ((buf = xmalloc_fgetline(fp)) != NULL) {
char *p; char *p;
p = buf; p = buf;
if (!(opt & OPT_p)) { if (!(opt & OPT_p)) {
/* skip address */ skip_address:
p = skip_whitespace(p);
while (isxdigit(*p)) p++; while (isxdigit(*p)) p++;
/* NB: for xxd -r, first hex portion is address even without colon */ /* NB: for xxd -r, first hex portion is address even without colon */
/* If it's there, skip it: */ /* 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 */ /* Process hex bytes optionally separated by whitespace */
for (;;) { for (;;) {
uint8_t val, c; uint8_t val, c;
int badchar = 0;
nibble1: nibble1:
p = skip_whitespace(p); if (opt & OPT_p)
p = skip_whitespace(p);
c = *p++; c = *p++;
if (isdigit(c)) if (isdigit(c))
val = c - '0'; val = c - '0';
else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
val = (c|0x20) - ('a' - 10); val = (c|0x20) - ('a' - 10);
else { else {
/* xxd V1.10 is inconsistent here. /* xxd V1.10 allows one non-hexnum char:
* echo -e "31 !3 0a 0a" | xxd -r -p * echo -e "31 !3 0a 0a" | xxd -r -p
* is "10<a0>" (no <cr>) - "!" is ignored, * is "10<a0>" (no <cr>) - "!" is ignored,
* but * but stops for more than one:
* echo -e "31 !!343434\n30 0a" | xxd -r -p * echo -e "31 !!343434\n30 0a" | xxd -r -p
* is "10<cr>" - "!!" drops rest of the line. * 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') if (c == '\0' || badchar)
goto nibble1; break;
break; badchar++;
goto nibble1;
} }
val <<= 4; 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: 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++; c = *p++;
if (isdigit(c)) if (isdigit(c))
@ -132,7 +143,16 @@ static void reverse(unsigned opt, const char *filename)
val |= (c|0x20) - ('a' - 10); val |= (c|0x20) - ('a' - 10);
else { else {
if (c != '\0') { 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; goto nibble1;
} }
/* Nibbles can join even through newline: /* Nibbles can join even through newline:
@ -143,10 +163,12 @@ static void reverse(unsigned opt, const char *filename)
p = buf = xmalloc_fgetline(fp); p = buf = xmalloc_fgetline(fp);
if (!buf) if (!buf)
break; break;
if (!(opt & OPT_p)) /* -p and !-p: different behavior */
goto skip_address;
goto nibble2; goto nibble2;
} }
putchar(val); putchar(val);
} } /* for(;;) */
free(buf); free(buf);
} }
//fclose(fp); //fclose(fp);