xxd: fix printing of trailing spaces

function                                             old     new   delta
bb_dump_dump                                        1497    1523     +26
xxd_main                                             459     466      +7
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 33/0)               Total: 33 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2020-10-20 18:54:36 +02:00
parent 085f19cdff
commit dac5b83142
4 changed files with 105 additions and 56 deletions

View File

@ -2,50 +2,15 @@
PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
#define F_IGNORE 0x01 /* %_A */
#define F_SETREP 0x02 /* rep count set, not default */
#define F_ADDRESS 0x001 /* print offset */
#define F_BPAD 0x002 /* blank pad */
#define F_C 0x004 /* %_c */
#define F_CHAR 0x008 /* %c */
#define F_DBL 0x010 /* %[EefGf] */
#define F_INT 0x020 /* %[di] */
#define F_P 0x040 /* %_p */
#define F_STR 0x080 /* %s */
#define F_U 0x100 /* %_u */
#define F_UINT 0x200 /* %[ouXx] */
#define F_TEXT 0x400 /* no conversions */
enum dump_vflag_t { ALL, DUP, FIRST, WAIT }; /* -v values */ enum dump_vflag_t { ALL, DUP, FIRST, WAIT }; /* -v values */
typedef struct PR { typedef struct FS FS;
struct PR *nextpr; /* next print unit */
unsigned flags; /* flag values */
int bcnt; /* byte count */
char *cchar; /* conversion character */
char *fmt; /* printf format */
char *nospace; /* no whitespace version */
} PR;
typedef struct FU {
struct FU *nextfu; /* next format unit */
struct PR *nextpr; /* next print unit */
unsigned flags; /* flag values */
int reps; /* repetition count */
int bcnt; /* byte count */
char *fmt; /* format string */
} FU;
typedef struct FS { /* format strings */
struct FS *nextfs; /* linked list of format strings */
struct FU *nextfu; /* linked list of format units */
int bcnt;
} FS;
typedef struct dumper_t { typedef struct dumper_t {
off_t dump_skip; /* bytes to skip */ off_t dump_skip; /* bytes to skip */
int dump_length; /* max bytes to read */ int dump_length; /* max bytes to read */
smallint dump_vflag; /*enum dump_vflag_t*/ smallint dump_vflag; /*enum dump_vflag_t*/
const char *eofstring;
FS *fshead; FS *fshead;
} dumper_t; } dumper_t;

View File

@ -13,13 +13,43 @@
#include "libbb.h" #include "libbb.h"
#include "dump.h" #include "dump.h"
static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789"; #define F_IGNORE 0x01 /* %_A */
#define F_SETREP 0x02 /* rep count set, not default */
#define F_ADDRESS 0x001 /* print offset */
#define F_BPAD 0x002 /* blank pad */
#define F_C 0x004 /* %_c */
#define F_CHAR 0x008 /* %c */
#define F_DBL 0x010 /* %[EefGf] */
#define F_INT 0x020 /* %[di] */
#define F_P 0x040 /* %_p */
#define F_STR 0x080 /* %s */
#define F_U 0x100 /* %_u */
#define F_UINT 0x200 /* %[ouXx] */
#define F_TEXT 0x400 /* no conversions */
static const char size_conv_str[] ALIGN1 = typedef struct PR {
"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; struct PR *nextpr; /* next print unit */
unsigned flags; /* flag values */
int bcnt; /* byte count */
char *cchar; /* conversion character */
char *fmt; /* printf format */
char *nospace; /* no whitespace version */
} PR;
static const char int_convs[] ALIGN1 = "diouxX"; typedef struct FU {
struct FU *nextfu; /* next format unit */
struct PR *nextpr; /* next print unit */
unsigned flags; /* flag values */
int reps; /* repetition count */
int bcnt; /* byte count */
char *fmt; /* format string */
} FU;
typedef struct FS { /* format strings */
struct FS *nextfs; /* linked list of format strings */
struct FU *nextfu; /* linked list of format units */
int bcnt;
} FS;
typedef struct priv_dumper_t { typedef struct priv_dumper_t {
dumper_t pub; dumper_t pub;
@ -39,6 +69,13 @@ typedef struct priv_dumper_t {
unsigned char *get__savp; unsigned char *get__savp;
} priv_dumper_t; } priv_dumper_t;
static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789";
static const char size_conv_str[] ALIGN1 =
"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
static const char int_convs[] ALIGN1 = "diouxX";
dumper_t* FAST_FUNC alloc_dumper(void) dumper_t* FAST_FUNC alloc_dumper(void)
{ {
priv_dumper_t *dumper = xzalloc(sizeof(*dumper)); priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
@ -48,7 +85,6 @@ dumper_t* FAST_FUNC alloc_dumper(void)
return &dumper->pub; return &dumper->pub;
} }
static NOINLINE int bb_dump_size(FS *fs) static NOINLINE int bb_dump_size(FS *fs)
{ {
FU *fu; FU *fu;
@ -284,7 +320,9 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
* repeat it as necessary. * repeat it as necessary.
* *
* if rep count is greater than 1, no trailing whitespace * if rep count is greater than 1, no trailing whitespace
* gets output from the last iteration of the format unit. * gets output from the last iteration of the format unit:
* 2/1 "%02x " prints "XX XX", not "XX XX "
* 2/1 "%02x\n" prints "XX\nXX", not "XX\nXX\n"
*/ */
for (fu = fs->nextfu; fu; fu = fu->nextfu) { for (fu = fs->nextfu; fu; fu = fu->nextfu) {
if (!fu->nextfu if (!fu->nextfu
@ -453,7 +491,7 @@ static void bpad(PR *pr)
for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
if (pr->nospace) if (pr->nospace)
pr->nospace--; pr->nospace--;
while ((*p2++ = *p1++) != 0) while ((*p2++ = *p1++) != '\0')
continue; continue;
} }
@ -520,36 +558,43 @@ static void conv_u(PR *pr, unsigned char *p)
static void display(priv_dumper_t* dumper) static void display(priv_dumper_t* dumper)
{ {
FS *fs; unsigned char *bp;
FU *fu;
PR *pr;
int cnt;
unsigned char *bp, *savebp;
off_t saveaddress;
unsigned char savech = '\0'; unsigned char savech = '\0';
while ((bp = get(dumper)) != NULL) { while ((bp = get(dumper)) != NULL) {
FS *fs;
unsigned char *savebp;
off_t saveaddress;
fs = dumper->pub.fshead; fs = dumper->pub.fshead;
savebp = bp; savebp = bp;
saveaddress = dumper->address; saveaddress = dumper->address;
for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) { for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
FU *fu;
for (fu = fs->nextfu; fu; fu = fu->nextfu) { for (fu = fs->nextfu; fu; fu = fu->nextfu) {
int cnt;
if (fu->flags & F_IGNORE) { if (fu->flags & F_IGNORE) {
break; break;
} }
for (cnt = fu->reps; cnt; --cnt) { for (cnt = fu->reps; cnt; --cnt) {
PR *pr;
for (pr = fu->nextpr; pr; dumper->address += pr->bcnt, for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
bp += pr->bcnt, pr = pr->nextpr) { bp += pr->bcnt, pr = pr->nextpr) {
if (dumper->eaddress && dumper->address >= dumper->eaddress if (dumper->eaddress
&& !(pr->flags & (F_TEXT | F_BPAD)) && dumper->address >= dumper->eaddress
) { ) {
bpad(pr); if (dumper->pub.eofstring) {
/* xxd support: requested to not pad incomplete blocks */
fputs(dumper->pub.eofstring, stdout);
return;
}
if (!(pr->flags & (F_TEXT | F_BPAD)))
bpad(pr);
} }
if (cnt == 1 && pr->nospace) { if (cnt == 1 && pr->nospace) {
savech = *pr->nospace; savech = *pr->nospace;
*pr->nospace = '\0'; *pr->nospace = '\0';
} }
/* PRINT; */
switch (pr->flags) { switch (pr->flags) {
case F_ADDRESS: case F_ADDRESS:
printf(pr->fmt, (unsigned) dumper->address); printf(pr->fmt, (unsigned) dumper->address);
@ -638,7 +683,9 @@ static void display(priv_dumper_t* dumper)
} }
} }
} }
if (dumper->endfu) { if (dumper->endfu) {
PR *pr;
/* /*
* if eaddress not set, error or file size was multiple * if eaddress not set, error or file size was multiple
* of blocksize, and no partial block ever found. * of blocksize, and no partial block ever found.
@ -695,8 +742,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
{ {
const char *p; const char *p;
FS *tfs; FS *tfs;
FU *tfu, **nextfupp; FU **nextfupp;
const char *savep;
/* start new linked list of format units */ /* start new linked list of format units */
tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */ tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
@ -713,6 +759,9 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
/* take the format string and break it up into format units */ /* take the format string and break it up into format units */
p = fmt; p = fmt;
for (;;) { for (;;) {
FU *tfu;
const char *savep;
p = skip_whitespace(p); p = skip_whitespace(p);
if (*p == '\0') { if (*p == '\0') {
break; break;

34
testsuite/xxd.tests Executable file
View File

@ -0,0 +1,34 @@
#!/bin/sh
# Copyright 2020 by Denys Vlasenko <vda.linux@googlemail.com>
# Licensed under GPLv2, see file LICENSE in this source tree.
. ./testing.sh
# testing "description" "command" "result" "infile" "stdin"
testing 'xxd -p with one NUL' \
'xxd -p' \
"\
00
" \
'' \
'\0'
testing 'xxd -p with 30 NULs' \
'xxd -p' \
"\
000000000000000000000000000000000000000000000000000000000000
" \
'' \
'\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 with 31 NULs' \
'xxd -p' \
"\
000000000000000000000000000000000000000000000000000000000000
00
" \
'' \
'\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'
exit $FAILCOUNT

View File

@ -141,6 +141,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
bb_dump_add(dumper, buf); bb_dump_add(dumper, buf);
} else { } else {
bb_dump_add(dumper, "\"\n\""); bb_dump_add(dumper, "\"\n\"");
dumper->eofstring = "\n";
} }
return bb_dump_dump(dumper, argv); return bb_dump_dump(dumper, argv);