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