• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
9 
10 config 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 
GLOBALS(long s;long g;long l;long c;)30 GLOBALS(
31   long s;
32   long g;
33   long l;
34   long c;
35 )
36 
37 static 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 
dehex(char ch)73 static 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 
do_xxd_reverse(int fd,char * name)81 static 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 
xxd_main(void)132 void 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