From dac5b8314236338963877903cba3d7edfbfc9c58 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 20 Oct 2020 18:54:36 +0200 Subject: [PATCH] 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 --- include/dump.h | 39 +----------------- libbb/dump.c | 87 +++++++++++++++++++++++++++++++--------- testsuite/xxd.tests | 34 ++++++++++++++++ util-linux/hexdump_xxd.c | 1 + 4 files changed, 105 insertions(+), 56 deletions(-) create mode 100755 testsuite/xxd.tests diff --git a/include/dump.h b/include/dump.h index 4c237ef05..f4759c193 100644 --- a/include/dump.h +++ b/include/dump.h @@ -2,50 +2,15 @@ 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 */ -typedef struct PR { - 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 FS FS; typedef struct dumper_t { off_t dump_skip; /* bytes to skip */ int dump_length; /* max bytes to read */ smallint dump_vflag; /*enum dump_vflag_t*/ + const char *eofstring; FS *fshead; } dumper_t; diff --git a/libbb/dump.c b/libbb/dump.c index 8029cca0e..920f003ef 100644 --- a/libbb/dump.c +++ b/libbb/dump.c @@ -13,13 +13,43 @@ #include "libbb.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 = -"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; +typedef struct PR { + 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 { dumper_t pub; @@ -39,6 +69,13 @@ typedef struct priv_dumper_t { unsigned char *get__savp; } 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) { priv_dumper_t *dumper = xzalloc(sizeof(*dumper)); @@ -48,7 +85,6 @@ dumper_t* FAST_FUNC alloc_dumper(void) return &dumper->pub; } - static NOINLINE int bb_dump_size(FS *fs) { FU *fu; @@ -284,7 +320,9 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) * repeat it as necessary. * * 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) { if (!fu->nextfu @@ -453,7 +491,7 @@ static void bpad(PR *pr) for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1) if (pr->nospace) pr->nospace--; - while ((*p2++ = *p1++) != 0) + while ((*p2++ = *p1++) != '\0') continue; } @@ -520,36 +558,43 @@ static void conv_u(PR *pr, unsigned char *p) static void display(priv_dumper_t* dumper) { - FS *fs; - FU *fu; - PR *pr; - int cnt; - unsigned char *bp, *savebp; - off_t saveaddress; + unsigned char *bp; unsigned char savech = '\0'; while ((bp = get(dumper)) != NULL) { + FS *fs; + unsigned char *savebp; + off_t saveaddress; + fs = dumper->pub.fshead; savebp = bp; saveaddress = dumper->address; for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) { + FU *fu; for (fu = fs->nextfu; fu; fu = fu->nextfu) { + int cnt; if (fu->flags & F_IGNORE) { break; } for (cnt = fu->reps; cnt; --cnt) { + PR *pr; for (pr = fu->nextpr; pr; dumper->address += pr->bcnt, bp += pr->bcnt, pr = pr->nextpr) { - if (dumper->eaddress && dumper->address >= dumper->eaddress - && !(pr->flags & (F_TEXT | F_BPAD)) + if (dumper->eaddress + && 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) { savech = *pr->nospace; *pr->nospace = '\0'; } -/* PRINT; */ switch (pr->flags) { case F_ADDRESS: printf(pr->fmt, (unsigned) dumper->address); @@ -638,7 +683,9 @@ static void display(priv_dumper_t* dumper) } } } + if (dumper->endfu) { + PR *pr; /* * if eaddress not set, error or file size was multiple * 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; FS *tfs; - FU *tfu, **nextfupp; - const char *savep; + FU **nextfupp; /* start new linked list of format units */ 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 */ p = fmt; for (;;) { + FU *tfu; + const char *savep; + p = skip_whitespace(p); if (*p == '\0') { break; diff --git a/testsuite/xxd.tests b/testsuite/xxd.tests new file mode 100755 index 000000000..2e80be5fe --- /dev/null +++ b/testsuite/xxd.tests @@ -0,0 +1,34 @@ +#!/bin/sh + +# Copyright 2020 by Denys Vlasenko +# 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 diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c index 6cf6d0297..f2d1ecb2c 100644 --- a/util-linux/hexdump_xxd.c +++ b/util-linux/hexdump_xxd.c @@ -141,6 +141,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv) bb_dump_add(dumper, buf); } else { bb_dump_add(dumper, "\"\n\""); + dumper->eofstring = "\n"; } return bb_dump_dump(dumper, argv);