1 /*
2  *  Copyright (C) 2003, 2004, 2005  James Antill
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  email: james@and.org
19  */
20 /* hexdump in "readable" format ... note this is a bit more fleshed out than
21  * some of the other examples mainly because I actually use it */
22 
23 /* this is roughly equiv. to the Linux hexdump command...
24 % rpm -qf /usr/bin/hexdump
25 util-linux-2.11r-10
26 % hexdump -e '"%08_ax:"
27             " " 2/1 "%02X"
28             " " 2/1 "%02X"
29             " " 2/1 "%02X"
30             " " 2/1 "%02X"
31             " " 2/1 "%02X"
32             " " 2/1 "%02X"
33             " " 2/1 "%02X"
34             " " 2/1 "%02X"'
35         -e '"  " 16 "%_p" "\n"'
36 
37 
38  * ...except that it prints the address in big hex digits, and it doesn't take
39  * you 30 minutes to remember how to type it out.
40  *  It also acts differently in that seperate files aren't merged
41  * into one output line (Ie. in this version each file starts on a new line,
42  * however the addresses are continuious).
43 
44  * It's also similar to "xxd" in vim, and "od -tx1z -Ax".
45  */
46 #define EX_UTILS_NO_FUNCS 1
47 #include "ex_utils.h"
48 
49 #include "hexdump.h"
50 
51 /* number of characters we output per line (assumes 80 char width screen)... */
52 #define CHRS_PER_LINE 16
53 
54 #ifndef CONF_USE_FAST_NUM_PRINT
55 #define CONF_USE_FAST_NUM_PRINT 1
56 #endif
57 
58 #define APOS() (apos + ((s1)->len - orig_len))
59 
60 #if !CONF_USE_FAST_NUM_PRINT
61 /* simple print of a number */
62 
63 /* print the address */
64 # define EX_HEXDUMP_X8(s1, num) \
65     vstr_add_fmt(s1, APOS(), "0x%08X:", (num))
66 /* print a set of two bytes */
67 # define EX_HEXDUMP_X2X2(s1, num1, num2) \
68     vstr_add_fmt(s1, APOS(), " %02X%02X", (num1), (num2))
69 /* print a byte and spaces for the missing byte */
70 # define EX_HEXDUMP_X2__(s1, num1) \
71     vstr_add_fmt(s1, APOS(), " %02X  ",   (num1))
72 #else
73 /* fast print of a number */
74 static const char *hexnums = "0123456789ABCDEF";
75 
76 # define EX_HEXDUMP_BYTE(buf, b) do {           \
77       (buf)[1] = hexnums[((b) >> 0) & 0xf];     \
78       (buf)[0] = hexnums[((b) >> 4) & 0xf];     \
79     } while (FALSE)
80 
81 # define EX_HEXDUMP_UINT(buf, i) do {           \
82       EX_HEXDUMP_BYTE((buf) + 6, (i) >>  0);    \
83       EX_HEXDUMP_BYTE((buf) + 4, (i) >>  8);    \
84       EX_HEXDUMP_BYTE((buf) + 2, (i) >> 16);    \
85       EX_HEXDUMP_BYTE((buf) + 0, (i) >> 24);    \
86     } while (FALSE)
87 
88 /* print the address */
89 # define EX_HEXDUMP_X8(s1, num) do {                                    \
90       unsigned char xbuf[9];                                            \
91                                                                         \
92       xbuf[8] = ':';                                                    \
93       EX_HEXDUMP_UINT(xbuf, num);                                       \
94       vstr_add_buf(s1, APOS(), xbuf, sizeof(xbuf));                     \
95     } while (FALSE)
96 /* print a set of two bytes */
97 # define EX_HEXDUMP_X2X2(s1, num1, num2) do {                        \
98       unsigned char xbuf[5];                                         \
99                                                                      \
100       xbuf[0] = ' ';                                                 \
101       EX_HEXDUMP_BYTE(xbuf + 3, num2);                               \
102       EX_HEXDUMP_BYTE(xbuf + 1, num1);                               \
103       vstr_add_buf(s1, APOS(), xbuf, sizeof(xbuf));                  \
104     } while (FALSE)
105 /* print a byte and spaces for the missing byte */
106 # define EX_HEXDUMP_X2__(s1, num1) do {                                 \
107       unsigned char xbuf[5];                                            \
108                                                                         \
109       xbuf[4] = ' ';                                                    \
110       xbuf[3] = ' ';                                                    \
111       EX_HEXDUMP_BYTE(xbuf + 1, num1);                                  \
112       xbuf[0] = ' ';                                                    \
113       vstr_add_buf(s1, APOS(), xbuf, sizeof(xbuf));                     \
114     } while (FALSE)
115 #endif
116 
117 static unsigned int addr = 0;
118 
ex_hexdump_reset(void)119 void ex_hexdump_reset(void)
120 {
121   addr = 0;
122 }
123 
ex_hexdump_process(Vstr_base * s1,size_t apos,Vstr_base * s2,size_t fpos,size_t flen,unsigned int prnt_type,size_t max_sz,int del,int last)124 int ex_hexdump_process(Vstr_base *s1, size_t apos,
125                        Vstr_base *s2, size_t fpos, size_t flen,
126                        unsigned int prnt_type, size_t max_sz, int del, int last)
127 {
128   size_t orig_len = s1->len;
129   /* normal ASCII chars, just allow COMMA and DOT flags */
130   unsigned int flags = VSTR_FLAG02(CONV_UNPRINTABLE_ALLOW, COMMA, DOT);
131   /* allow spaces, allow COMMA, DOT, underbar _, and space */
132   unsigned int flags_sp = VSTR_FLAG04(CONV_UNPRINTABLE_ALLOW,
133                                       COMMA, DOT, _, SP);
134   /* high ascii too, allow
135    * COMMA, DOT, underbar _, space, high space and other high characters */
136   unsigned int flags_hsp = VSTR_FLAG06(CONV_UNPRINTABLE_ALLOW,
137                                        COMMA, DOT, _, SP, HSP, HIGH);
138   unsigned char buf[CHRS_PER_LINE];
139 
140   switch (prnt_type)
141   {
142     case PRNT_HIGH: flags = flags_hsp; break;
143     case PRNT_SPAC: flags = flags_sp;  break;
144     case PRNT_NONE:                    break;
145     default: ASSERT(FALSE);            break;
146   }
147 
148   /* we don't want to create more data, if we are over our limit */
149   if (s1->len > max_sz)
150     return (FALSE);
151 
152   /* while we have a hexdump line ... */
153   while (flen >= CHRS_PER_LINE)
154   {
155     unsigned int count = 0;
156     size_t tmp = 0;
157 
158     /* get a hexdump line from the vstr */
159     vstr_export_buf(s2, fpos, CHRS_PER_LINE, buf, sizeof(buf));
160 
161     /* write out a hexdump line address */
162     EX_HEXDUMP_X8(s1, addr);
163 
164     /* write out hex values */
165     while (count < CHRS_PER_LINE)
166     {
167       EX_HEXDUMP_X2X2(s1, buf[count], buf[count + 1]);
168       count += 2;
169     }
170 
171     vstr_add_rep_chr(s1, APOS(), ' ', 2);
172 
173     /* write out characters, converting reference and pointer nodes to
174      * _BUF nodes */
175     tmp = APOS();
176     if (vstr_add_vstr(s1, tmp, s2, fpos, CHRS_PER_LINE,
177                       VSTR_TYPE_ADD_ALL_BUF))
178       /* convert unprintable characters to the '.' character */
179       vstr_conv_unprintable_chr(s1, tmp + 1, CHRS_PER_LINE, flags, '.');
180 
181     vstr_add_rep_chr(s1, APOS(), '\n', 1);
182 
183     addr += CHRS_PER_LINE;
184 
185     flen -= CHRS_PER_LINE;
186     if (del) /* delete the set of characters just processed */
187       vstr_del(s2, fpos, CHRS_PER_LINE);
188     else
189       fpos += CHRS_PER_LINE;
190 
191     /* note that we don't want to create data indefinitely, so stop
192      * according to in core configuration */
193     if (s1->len > max_sz)
194       return (TRUE);
195   }
196 
197   if (last && flen)
198   { /* do the same as above, but print the partial line for
199      * the end of a file */
200     size_t got = flen;
201     size_t missing = CHRS_PER_LINE - flen;
202     const unsigned char *ptr = buf;
203     size_t tmp = 0;
204 
205     missing -= (missing % 2);
206     vstr_export_buf(s2, fpos, flen, buf, sizeof(buf));
207 
208     EX_HEXDUMP_X8(s1, addr);
209 
210     while (got >= 2)
211     {
212       EX_HEXDUMP_X2X2(s1, ptr[0], ptr[1]);
213       got -= 2;
214       ptr += 2;
215     }
216     if (got)
217     {
218       EX_HEXDUMP_X2__(s1, ptr[0]);
219       got -= 2;
220     }
221 
222     /* Add spaces until the point where the characters should start */
223     vstr_add_rep_chr(s1, APOS(), ' ', (missing * 2) + (missing / 2) + 2);
224 
225     tmp = APOS();
226     if (vstr_add_vstr(s1, tmp, s2, fpos, flen, VSTR_TYPE_ADD_ALL_BUF))
227       vstr_conv_unprintable_chr(s1, tmp + 1, flen, flags, '.');
228 
229     vstr_add_cstr_buf(s1, APOS(), "\n");
230 
231     addr += flen;
232     if (del)
233       vstr_del(s2, fpos, flen);
234 
235     return (TRUE);
236   }
237 
238   return (FALSE);
239 }
240