From 215a6af5f83e7866751f24fc61f709b444e61c1d Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Sun, 24 Dec 2017 00:42:38 -0800 Subject: 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 --- toys/other/xxd.c | 122 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 41 deletions(-) (limited to 'toys/other/xxd.c') 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)); } -- cgit v1.2.3