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