1/* xxd.c - hexdump. 2 * 3 * Copyright 2015 The Android Open Source Project 4 * 5 * No obvious standard, output looks like: 6 * 0000000: 4c69 6e75 7820 7665 7273 696f 6e20 332e Linux version 3. 7 8USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN)) 9 10config XXD 11 bool "xxd" 12 default y 13 help 14 usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [-s n] [file] 15 16 Hexdump a file to stdout. If no file is listed, copy from stdin. 17 Filename "-" is a synonym for stdin. 18 19 -c n Show n bytes per line (default 16). 20 -g n Group bytes by adding a ' ' every n bytes (default 2). 21 -l n Limit of n bytes before stopping (default is no limit). 22 -p Plain hexdump (30 bytes/line, no grouping). 23 -r Reverse operation: turn a hexdump into a binary file. 24 -s n Skip to offset n. 25*/ 26 27#define FOR_xxd 28#include "toys.h" 29 30GLOBALS( 31 long s; 32 long g; 33 long l; 34 long c; 35) 36 37static void do_xxd(int fd, char *name) 38{ 39 long long pos = 0; 40 long long limit = TT.l; 41 int i, len, space; 42 43 if (toys.optflags&FLAG_s) { 44 xlseek(fd, TT.s, SEEK_SET); 45 pos = TT.s; 46 if (limit) limit += TT.s; 47 } 48 49 while (0<(len = readall(fd, toybuf, 50 (limit && limit-pos<TT.c)?limit-pos:TT.c))) { 51 if (!(toys.optflags&FLAG_p)) printf("%08llx: ", pos); 52 pos += len; 53 space = 2*TT.c+TT.c/TT.g+1; 54 55 for (i=0; i<len;) { 56 space -= printf("%02x", toybuf[i]); 57 if (!(++i%TT.g)) { 58 putchar(' '); 59 space--; 60 } 61 } 62 63 if (!(toys.optflags&FLAG_p)) { 64 printf("%*s", space, ""); 65 for (i=0; i<len; i++) 66 putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.'); 67 } 68 putchar('\n'); 69 } 70 if (len<0) perror_exit("read"); 71} 72 73static int dehex(char ch) 74{ 75 if (ch >= '0' && ch <= '9') return ch - '0'; 76 if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; 77 if (ch >= 'A' && ch <= 'F') return ch - 'a' + 10; 78 return (ch == '\n') ? -2 : -1; 79} 80 81static void do_xxd_reverse(int fd, char *name) 82{ 83 FILE *fp = xfdopen(fd, "r"); 84 85 while (!feof(fp)) { 86 int col = 0; 87 int tmp; 88 89 // Each line of a non-plain hexdump starts with an offset/address. 90 if (!(toys.optflags&FLAG_p)) { 91 long long pos; 92 93 if (fscanf(fp, "%llx: ", &pos) == 1) { 94 if (fseek(stdout, pos, SEEK_SET) != 0) { 95 // TODO: just write out zeros if non-seekable? 96 perror_exit("%s: seek failed", name); 97 } 98 } 99 } 100 101 // A plain hexdump can have as many bytes per line as you like, 102 // but a non-plain hexdump assumes garbage after it's seen the 103 // specified number of bytes. 104 while (toys.optflags&FLAG_p || col < TT.c) { 105 int n1, n2; 106 107 // If we're at EOF or EOL or we read some non-hex... 108 if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) { 109 // If we're at EOL, start on that line. 110 if (n1 == -2 || n2 == -2) continue; 111 // Otherwise, skip to the next line. 112 break; 113 } 114 115 fputc((n1 << 4) | (n2 & 0xf), stdout); 116 col++; 117 118 // Is there any grouping going on? Ignore a single space. 119 tmp = fgetc(fp); 120 if (tmp != ' ') ungetc(tmp, fp); 121 } 122 123 // Skip anything else on this line (such as the ASCII dump). 124 while ((tmp = fgetc(fp)) != EOF && tmp != '\n') 125 ; 126 } 127 if (ferror(fp)) perror_msg_raw(name); 128 129 fclose(fp); 130} 131 132void xxd_main(void) 133{ 134 // Plain style is 30 bytes/line, no grouping. 135 if (toys.optflags&FLAG_p) TT.c = TT.g = 30; 136 137 loopfiles(toys.optargs, toys.optflags&FLAG_r ? do_xxd_reverse : do_xxd); 138} 139