1/* xxd.c - hexdump. 2 * 3 * Copyright 2015 The Android Open Source Project 4 * 5 * No obvious standard. 6 * Regular output: 7 * "00000000: 4c69 6e75 7820 7665 7273 696f 6e20 342e Linux version 4." 8 * xxd -i "include" or "initializer" output: 9 * " 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f," 10 * xxd -p "plain" output: 11 * "4c696e75782076657273696f6e20342e392e302d342d616d643634202864" 12 13USE_XXD(NEWTOY(xxd, ">1c#l#o#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN)) 14 15config XXD 16 bool "xxd" 17 default y 18 help 19 usage: xxd [-c n] [-g n] [-i] [-l n] [-o n] [-p] [-r] [-s n] [file] 20 21 Hexdump a file to stdout. If no file is listed, copy from stdin. 22 Filename "-" is a synonym for stdin. 23 24 -c n Show n bytes per line (default 16) 25 -g n Group bytes by adding a ' ' every n bytes (default 2) 26 -i Include file output format (comma-separated hex byte literals) 27 -l n Limit of n bytes before stopping (default is no limit) 28 -o n Add n to display offset 29 -p Plain hexdump (30 bytes/line, no grouping) 30 -r Reverse operation: turn a hexdump into a binary file 31 -s n Skip to offset n 32*/ 33 34#define FOR_xxd 35#include "toys.h" 36 37GLOBALS( 38 long s, g, o, l, c; 39) 40 41static void do_xxd(int fd, char *name) 42{ 43 long long pos = 0; 44 long long limit = TT.l; 45 int i, len, space; 46 47 if (toys.optflags&FLAG_s) { 48 xlseek(fd, TT.s, SEEK_SET); 49 pos = TT.s; 50 if (limit) limit += TT.s; 51 } 52 53 while (0<(len = readall(fd, toybuf, 54 (limit && limit-pos<TT.c)?limit-pos:TT.c))) { 55 if (!(toys.optflags&FLAG_p)) printf("%08llx: ", TT.o + pos); 56 pos += len; 57 space = 2*TT.c+TT.c/TT.g+1; 58 59 for (i=0; i<len;) { 60 space -= printf("%02x", toybuf[i]); 61 if (!(++i%TT.g)) { 62 putchar(' '); 63 space--; 64 } 65 } 66 67 if (!(toys.optflags&FLAG_p)) { 68 printf("%*s", space, ""); 69 for (i=0; i<len; i++) 70 putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.'); 71 } 72 putchar('\n'); 73 } 74 if (len<0) perror_exit("read"); 75} 76 77static void do_xxd_include(int fd, char *name) 78{ 79 long long total = 0; 80 int c = 1, i, len; 81 82 // The original xxd outputs a header/footer if given a filename (not stdin). 83 // We don't, which means that unlike the original we can implement -ri. 84 while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) { 85 total += len; 86 for (i = 0; i < len; ++i) { 87 printf("%s%#.02x", c > 1 ? ", " : " ", toybuf[i]); 88 if (c++ == TT.c) { 89 xprintf(",\n"); 90 c = 1; 91 } 92 } 93 } 94 if (len < 0) perror_msg_raw(name); 95 if (c > 1) xputc('\n'); 96} 97 98static int dehex(char ch) 99{ 100 if (ch >= '0' && ch <= '9') return ch - '0'; 101 if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; 102 if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; 103 return (ch == '\n') ? -2 : -1; 104} 105 106static void do_xxd_reverse(int fd, char *name) 107{ 108 FILE *fp = xfdopen(fd, "r"); 109 int tmp; 110 111 if (toys.optflags&FLAG_i) { 112 // -ri is a very easy special case. 113 while (fscanf(fp, " 0x%02x,", &tmp) == 1) { 114 fputc(tmp & 0xff, stdout); 115 } 116 } else { 117 while (!feof(fp)) { 118 int col = 0; 119 120 // Each line of a regular hexdump starts with an offset/address. 121 // Each line of a plain hexdump just goes straight into the bytes. 122 if (!(toys.optflags&FLAG_p)) { 123 long long pos; 124 125 if (fscanf(fp, "%llx: ", &pos) == 1) { 126 if (fseek(stdout, pos, SEEK_SET) != 0) { 127 // TODO: just write out zeros if non-seekable? 128 perror_exit("%s: seek failed", name); 129 } 130 } 131 } 132 133 // A plain hexdump can have as many bytes per line as you like, 134 // but a non-plain hexdump assumes garbage after it's seen the 135 // specified number of bytes. 136 while (toys.optflags&FLAG_p || col < TT.c) { 137 int n1, n2; 138 139 // If we're at EOF or EOL or we read some non-hex... 140 if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) { 141 // If we're at EOL, start on that line. 142 if (n1 == -2 || n2 == -2) continue; 143 // Otherwise, skip to the next line. 144 break; 145 } 146 147 fputc((n1 << 4) | (n2 & 0xf), stdout); 148 col++; 149 150 // Is there any grouping going on? Ignore a single space. 151 tmp = fgetc(fp); 152 if (tmp != ' ') ungetc(tmp, fp); 153 } 154 155 // Skip anything else on this line (such as the ASCII dump). 156 while ((tmp = fgetc(fp)) != EOF && tmp != '\n') 157 ; 158 } 159 } 160 161 if (ferror(fp)) perror_msg_raw(name); 162 fclose(fp); 163} 164 165void xxd_main(void) 166{ 167 if (TT.c < 0 || TT.c > 256) error_exit("invalid -c: %ld", TT.c); 168 if (TT.c == 0) TT.c = (toys.optflags&FLAG_i)?12:16; 169 170 // Plain style is 30 bytes/line, no grouping. 171 if (toys.optflags&FLAG_p) TT.c = TT.g = 30; 172 173 loopfiles(toys.optargs, 174 toys.optflags&FLAG_r ? do_xxd_reverse 175 : (toys.optflags&FLAG_i ? do_xxd_include : do_xxd)); 176} 177