From 0f4364775fbc0b716024d5211325045c40b431c9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 25 Jan 2017 01:58:00 +0100 Subject: [PATCH] xxd: new applet Yet Another Hexdumper function old new delta xxd_main - 364 +364 packed_usage 31046 31116 +70 applet_names 2560 2564 +4 applet_main 1476 1480 +4 rewrite 1022 1013 -9 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 3/1 up/down: 442/-9) Total: 433 bytes Signed-off-by: Denys Vlasenko --- libbb/dump.c | 12 ++-- util-linux/hexdump.c | 21 ++++--- util-linux/hexdump_xxd.c | 132 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 16 deletions(-) create mode 100644 util-linux/hexdump_xxd.c diff --git a/libbb/dump.c b/libbb/dump.c index 154be5d80..e9ac0c673 100644 --- a/libbb/dump.c +++ b/libbb/dump.c @@ -165,16 +165,14 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) byte_count_str = "\001"; DO_BYTE_COUNT: if (fu->bcnt) { - do { - if (fu->bcnt == *byte_count_str) { + for (;;) { + if (fu->bcnt == *byte_count_str) break; - } - } while (*++byte_count_str); + if (*++byte_count_str == 0) + bb_error_msg_and_die("bad byte count for conversion character %s", p1); + } } /* Unlike the original, output the remainder of the format string. */ - if (!*byte_count_str) { - bb_error_msg_and_die("bad byte count for conversion character %s", p1); - } pr->bcnt = *byte_count_str; } else if (*p1 == 'l') { ++p2; diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c index 4a7f641db..4b536e1c2 100644 --- a/util-linux/hexdump.c +++ b/util-linux/hexdump.c @@ -47,6 +47,7 @@ //usage: "\n -d Two-byte decimal display" //usage: "\n -e FORMAT_STRING" //usage: "\n -f FORMAT_FILE" +// exactly the same help text lines in hexdump and xxd: //usage: "\n -n LENGTH Interpret only LENGTH bytes of input" //usage: "\n -o Two-byte octal display" //usage: "\n -s OFFSET Skip OFFSET bytes" @@ -83,11 +84,11 @@ static void bb_dump_addfile(dumper_t *dumper, char *name) } static const char *const add_strings[] = { - "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"", /* b */ - "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"", /* c */ - "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"", /* d */ - "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"", /* o */ - "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"", /* x */ + "\"%07.7_ax \"16/1 \"%03o \"\"\n\"", /* b */ + "\"%07.7_ax \"16/1 \"%3_c \"\"\n\"", /* c */ + "\"%07.7_ax \"8/2 \" %05u \"\"\n\"", /* d */ + "\"%07.7_ax \"8/2 \" %06o \"\"\n\"", /* o */ + "\"%07.7_ax \"8/2 \" %04x \"\"\n\"", /* x */ }; static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\""; @@ -125,9 +126,11 @@ int hexdump_main(int argc, char **argv) /* Save a little bit of space below by omitting the 'else's. */ if (ch == 'C') { hd_applet: - bb_dump_add(dumper, "\"%08.8_Ax\n\""); - bb_dump_add(dumper, "\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); - bb_dump_add(dumper, "\" |\" 16/1 \"%_p\" \"|\\n\""); + bb_dump_add(dumper, "\"%08.8_Ax\n\""); // final address line after dump + //------------------- "address " 8 * "xx " " " 8 * "xx " + bb_dump_add(dumper, "\"%08.8_ax \"8/1 \"%02x \" \" \"8/1 \"%02x \""); + //------------------- " |ASCII...........|\n" + bb_dump_add(dumper, "\" |\"16/1 \"%_p\"\"|\n\""); } if (ch == 'e') { bb_dump_add(dumper, optarg); @@ -158,7 +161,7 @@ int hexdump_main(int argc, char **argv) if (!dumper->fshead) { bb_dump_add(dumper, add_first); - bb_dump_add(dumper, "\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); + bb_dump_add(dumper, "\"%07.7_ax \"8/2 \"%04x \"\"\n\""); } argv += optind; diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c new file mode 100644 index 000000000..3a7e64bb9 --- /dev/null +++ b/util-linux/hexdump_xxd.c @@ -0,0 +1,132 @@ +/* vi: set sw=4 ts=4: */ +/* + * xxd implementation for busybox + * + * Copyright (c) 2017 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config XXD +//config: bool "xxd" +//config: default y +//config: help +//config: The xxd utility is used to display binary data in a readable +//config: way that is comparable to the output from most hex editors. + +//applet:IF_XXD(APPLET_NOEXEC(xxd, xxd, BB_DIR_USR_BIN, BB_SUID_DROP, xxd)) + +//kbuild:lib-$(CONFIG_XXD) += hexdump_xxd.o + +// $ xxd --version +// xxd V1.10 27oct98 by Juergen Weigert +// $ xxd --help +// Usage: +// xxd [options] [infile [outfile]] +// or +// xxd -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]] +// Options: +// -a toggle autoskip: A single '*' replaces nul-lines. Default off. +// -b binary digit dump (incompatible with -ps,-i,-r). Default hex. +// -c cols format octets per line. Default 16 (-i: 12, -ps: 30). +// -E show characters in EBCDIC. Default ASCII. +// -e little-endian dump (incompatible with -ps,-i,-r). +// -g number of octets per group in normal output. Default 2 (-e: 4). +// -i output in C include file style. +// -l len stop after octets. +// -o off add to the displayed file position. +// -ps output in postscript plain hexdump style. +// -r reverse operation: convert (or patch) hexdump into binary. +// -r -s off revert with added to file positions found in hexdump. +// -s [+][-]seek start at bytes abs. (or +: rel.) infile offset. +// -u use upper case hex letters. + +//usage:#define xxd_trivial_usage +//usage: "[OPTIONS] [FILE]" +//usage:#define xxd_full_usage "\n\n" +//usage: "Hex dump FILE (or stdin)\n" +//usage: "\n -g N Bytes per group" +//usage: "\n -c N Bytes per line" +// exactly the same help text lines in hexdump and xxd: +//usage: "\n -l LENGTH Interpret only LENGTH bytes of input" +//usage: "\n -s OFFSET Skip OFFSET bytes" + +#include "libbb.h" +#include "dump.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int xxd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int xxd_main(int argc UNUSED_PARAM, char **argv) +{ + char buf[80]; + dumper_t *dumper; + char *opt_l, *opt_s; + unsigned bytes = 2; + unsigned cols = 0; + unsigned opt; + + dumper = alloc_dumper(); + +#define OPT_l (1 << 0) +#define OPT_s (1 << 1) +#define OPT_a (1 << 2) + opt_complementary = "?1"; /* 1 argument max */ + opt = getopt32(argv, "l:s:ag:+c:+", &opt_l, &opt_s, &bytes, &cols); + argv += optind; + +// dumper->dump_vflag = ALL; // default +// if (opt & OPT_a) +// dumper->dump_vflag = SKIPNUL; ..does not exist + if (opt & OPT_l) { + dumper->dump_length = xstrtou_range( + opt_l, + /*base:*/ 0, + /*lo:*/ 0, /*hi:*/ INT_MAX + ); + } + if (opt & OPT_s) { + dumper->dump_skip = xstrtoull_range( + opt_s, + /*base:*/ 0, + /*lo:*/ 0, /*hi:*/ OFF_T_MAX + ); + //BUGGY for /proc/version (unseekable?) + } + + bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: " + if (cols == 0) + cols = 16; + if (bytes < 1 || bytes >= cols) { + sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "xx" + bb_dump_add(dumper, buf); + } + else if (bytes == 1) { + sprintf(buf, "%u/1 \"%%02x \"", cols); // cols * "xx " + bb_dump_add(dumper, buf); + } + else { +/* Format "print byte" with and without trailing space */ +#define BS "/1 \"%02x \"" +#define B "/1 \"%02x\"" + unsigned i; + char *bigbuf = xmalloc(1 + cols * (sizeof(BS)-1)); + char *p = bigbuf; + for (i = 1; i <= cols; i++) { + if (i == cols || i % bytes) + p = stpcpy(p, B); + else + p = stpcpy(p, BS); + } + // for -g3, this results in B B BS B B BS... B = "xxxxxx xxxxxx .....xx" + // todo: can be more clever and use + // one "cols-1/B" format instead of many "B B B..." formats + //bb_error_msg("ADDED:'%s'", bigbuf); + bb_dump_add(dumper, bigbuf); + free(bigbuf); + } + + sprintf(buf, "\" \" %u/1 \"%%_p\" \"\n\"", cols); // " ASCII\n" + bb_dump_add(dumper, buf); + + return bb_dump_dump(dumper, argv); +}