aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2017-12-24 00:42:38 -0800
committerRob Landley <rob@landley.net>2017-12-26 22:28:10 -0600
commit215a6af5f83e7866751f24fc61f709b444e61c1d (patch)
treec1365d13267256e6fc1877ab3f238b6f5ac0ea32
parent70a6c55499eabf1229dfb0b49f08b366cbaa7896 (diff)
downloadtoybox-215a6af5f83e7866751f24fc61f709b444e61c1d.tar.gz
Add xxd -i (and xxd -ri).
The original xxd doesn't support -r with -i. The original also outputs "unsigned char name[] = { ... };" for input other than stdin, but that actually makes it less useful --- many languages support array initializers, but far fewer support that exact declaration syntax. Also fix the -c range checking and defaulting: ><= in the argument string only works for floating point arguments. Bug: http://b/64711851
-rw-r--r--tests/xxd.test7
-rw-r--r--toys/other/xxd.c122
2 files changed, 86 insertions, 43 deletions
diff --git a/tests/xxd.test b/tests/xxd.test
index 5f43b8b5..97634e86 100644
--- a/tests/xxd.test
+++ b/tests/xxd.test
@@ -25,12 +25,15 @@ testing "-c 8 -g 4 file1" "xxd -c 8 -g 4 file1" \
testing "-c 8 -g 3 file1" "xxd -c 8 -g 3 file1" \
"00000000: 746869 732069 7320 this is \n00000008: 736f6d 652074 6578 some tex\n00000010: 740a t.\n" "" ""
+testing "-i" "cat file1 | xxd -i -" " 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65,\n 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a\n" "" ""
+
testing "-p" "xxd -p file1" "7468697320697320736f6d6520746578740a\n" "" ""
testing "-s" "xxd -s 13 file1" "0000000d: 7465 7874 0a text.\n" "" ""
-testing "-r" "xxd file1 | xxd -r" "this is some text\n" "" ""
-testing "-r -p" "xxd -p file1 | xxd -r -p" "this is some text\n" "" ""
+testing "-r" "echo -e ' 00000000: 7468 6973 2069 7320 736f 6d65 2074 6578 this is some tex\n00000010: 740a t.' | xxd -r" "this is some text\n" "" ""
+SKIP_HOST=1 testing "-r -i" "echo -e '0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65,\n 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a' | xxd -ri" "this is some text\n" "" ""
+testing "-r -p" "echo 7468697320697320736f6d6520746578740a | xxd -r -p" "this is some text\n" "" ""
testing "-r garbage" "echo '0000: 68 65 6c6c 6fxxxx' | xxd -r -" "hello" "" ""
diff --git a/toys/other/xxd.c b/toys/other/xxd.c
index 0ff5947f..edc5772c 100644
--- a/toys/other/xxd.c
+++ b/toys/other/xxd.c
@@ -2,22 +2,28 @@
*
* Copyright 2015 The Android Open Source Project
*
- * No obvious standard, output looks like:
- * 0000000: 4c69 6e75 7820 7665 7273 696f 6e20 332e Linux version 3.
+ * No obvious standard.
+ * Regular output:
+ * "00000000: 4c69 6e75 7820 7665 7273 696f 6e20 342e Linux version 4."
+ * xxd -i "include" or "initializer" output:
+ * " 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,"
+ * xxd -p "plain" output:
+ * "4c696e75782076657273696f6e20342e392e302d342d616d643634202864"
-USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XXD(NEWTOY(xxd, ">1c#l#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
config XXD
bool "xxd"
default y
help
- usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [-s n] [file]
+ usage: xxd [-c n] [-g n] [-i] [-l n] [-p] [-r] [-s n] [file]
Hexdump a file to stdout. If no file is listed, copy from stdin.
Filename "-" is a synonym for stdin.
-c n Show n bytes per line (default 16)
-g n Group bytes by adding a ' ' every n bytes (default 2)
+ -i Include file output format (comma-separated hex byte literals)
-l n Limit of n bytes before stopping (default is no limit)
-p Plain hexdump (30 bytes/line, no grouping)
-r Reverse operation: turn a hexdump into a binary file
@@ -70,6 +76,27 @@ static void do_xxd(int fd, char *name)
if (len<0) perror_exit("read");
}
+static void do_xxd_include(int fd, char *name)
+{
+ long long total = 0;
+ int c = 1, i, len;
+
+ // The original xxd outputs a header/footer if given a filename (not stdin).
+ // We don't, which means that unlike the original we can implement -ri.
+ while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
+ total += len;
+ for (i = 0; i < len; ++i) {
+ printf("%s%#.02x", c > 1 ? ", " : " ", toybuf[i]);
+ if (c++ == TT.c) {
+ xprintf(",\n");
+ c = 1;
+ }
+ }
+ }
+ if (len < 0) perror_msg_raw(name);
+ if (c > 1) xputc('\n');
+}
+
static int dehex(char ch)
{
if (ch >= '0' && ch <= '9') return ch - '0';
@@ -81,58 +108,71 @@ static int dehex(char ch)
static void do_xxd_reverse(int fd, char *name)
{
FILE *fp = xfdopen(fd, "r");
+ int tmp;
- while (!feof(fp)) {
- int col = 0;
- int tmp;
-
- // Each line of a non-plain hexdump starts with an offset/address.
- if (!(toys.optflags&FLAG_p)) {
- long long pos;
-
- if (fscanf(fp, "%llx: ", &pos) == 1) {
- if (fseek(stdout, pos, SEEK_SET) != 0) {
- // TODO: just write out zeros if non-seekable?
- perror_exit("%s: seek failed", name);
+ if (toys.optflags&FLAG_i) {
+ // -ri is a very easy special case.
+ while (fscanf(fp, " 0x%02x,", &tmp) == 1) {
+ fputc(tmp & 0xff, stdout);
+ }
+ } else {
+ while (!feof(fp)) {
+ int col = 0;
+
+ // Each line of a regular hexdump starts with an offset/address.
+ // Each line of a plain hexdump just goes straight into the bytes.
+ if (!(toys.optflags&FLAG_p)) {
+ long long pos;
+
+ if (fscanf(fp, "%llx: ", &pos) == 1) {
+ if (fseek(stdout, pos, SEEK_SET) != 0) {
+ // TODO: just write out zeros if non-seekable?
+ perror_exit("%s: seek failed", name);
+ }
}
}
- }
- // A plain hexdump can have as many bytes per line as you like,
- // but a non-plain hexdump assumes garbage after it's seen the
- // specified number of bytes.
- while (toys.optflags&FLAG_p || col < TT.c) {
- int n1, n2;
-
- // If we're at EOF or EOL or we read some non-hex...
- if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
- // If we're at EOL, start on that line.
- if (n1 == -2 || n2 == -2) continue;
- // Otherwise, skip to the next line.
- break;
- }
+ // A plain hexdump can have as many bytes per line as you like,
+ // but a non-plain hexdump assumes garbage after it's seen the
+ // specified number of bytes.
+ while (toys.optflags&FLAG_p || col < TT.c) {
+ int n1, n2;
+
+ // If we're at EOF or EOL or we read some non-hex...
+ if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
+ // If we're at EOL, start on that line.
+ if (n1 == -2 || n2 == -2) continue;
+ // Otherwise, skip to the next line.
+ break;
+ }
- fputc((n1 << 4) | (n2 & 0xf), stdout);
- col++;
+ fputc((n1 << 4) | (n2 & 0xf), stdout);
+ col++;
- // Is there any grouping going on? Ignore a single space.
- tmp = fgetc(fp);
- if (tmp != ' ') ungetc(tmp, fp);
- }
+ // Is there any grouping going on? Ignore a single space.
+ tmp = fgetc(fp);
+ if (tmp != ' ') ungetc(tmp, fp);
+ }
- // Skip anything else on this line (such as the ASCII dump).
- while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
- ;
+ // Skip anything else on this line (such as the ASCII dump).
+ while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
+ ;
+ }
}
- if (ferror(fp)) perror_msg_raw(name);
+ if (ferror(fp)) perror_msg_raw(name);
fclose(fp);
}
void xxd_main(void)
{
+ if (TT.c < 0 || TT.c > 256) error_exit("invalid -c: %ld", TT.c);
+ if (TT.c == 0) TT.c = (toys.optflags&FLAG_i)?12:16;
+
// Plain style is 30 bytes/line, no grouping.
if (toys.optflags&FLAG_p) TT.c = TT.g = 30;
- loopfiles(toys.optargs, toys.optflags&FLAG_r ? do_xxd_reverse : do_xxd);
+ loopfiles(toys.optargs,
+ toys.optflags&FLAG_r ? do_xxd_reverse
+ : (toys.optflags&FLAG_i ? do_xxd_include : do_xxd));
}