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