summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/dump.h39
-rw-r--r--libbb/dump.c93
-rwxr-xr-xtestsuite/xxd.tests34
-rw-r--r--util-linux/hexdump_xxd.c1
4 files changed, 108 insertions, 59 deletions
diff --git a/include/dump.h b/include/dump.h
index 4c237ef..f4759c1 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 8029cca..920f003 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@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 0000000..2e80be5
--- /dev/null
+++ b/testsuite/xxd.tests
@@ -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
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index 6cf6d02..f2d1ecb 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);