[git commit] xxd: fix printing of trailing spaces

Denys Vlasenko vda.linux at googlemail.com
Tue Oct 20 16:57:02 UTC 2020


commit: https://git.busybox.net/busybox/commit/?id=dac5b8314236338963877903cba3d7edfbfc9c58
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at googlemail.com>
---
 include/dump.h           | 39 ++------------------
 libbb/dump.c             | 93 ++++++++++++++++++++++++++++++++++++------------
 testsuite/xxd.tests      | 34 ++++++++++++++++++
 util-linux/hexdump_xxd.c |  1 +
 4 files changed, 108 insertions(+), 59 deletions(-)

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";
-
-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";
-
+#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 */
+
+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 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 at 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 <vda.linux at 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
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);


More information about the busybox-cvs mailing list