search sequence command
[hexdump.git] / hexdump.c
1 /* depend: */
2 /* cflags: */
3 /* linker: debug.o */
4
5 #include <assert.h>
6 #include <malloc.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "debug.h"
12
13 /* macros */
14
15 #define CEIL(x, y) (((x) + (y) - 1) / (y))
16 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
17 #define MAX(x, y) (((x) > (y)) ? (x) : (y))
18
19 //#define BUFFERSIZE 4096
20 #define BUFFERSIZE 256
21 #define NBCOLS 8
22 #define NBDIGITS 6
23
24 /* gobal variables */
25
26 int nbcols = NBCOLS;
27 int nbdigits = NBDIGITS;
28 int offset = 1;
29
30 char buffer[BUFFERSIZE] = {0};
31 FILE *fin = NULL;
32 int addrfile = 0;
33 FILE *fout = NULL;
34 char *progname = NULL;
35
36 /* help function */
37
38 int usage (int ret)
39 {
40 FILE *fd = ret ? stderr : stdout;
41 fprintf (fd, "usage: %s [-i file] [-h] [-n nbcols] [-o file] [-v]\n", progname);
42 fprintf (fd, " -i: input file\n");
43 fprintf (fd, " -h: help message\n");
44 fprintf (fd, " -n: number of columns\n");
45 fprintf (fd, " -e: commands\n");
46 fprintf (fd, " -o: output file\n");
47 fprintf (fd, " -v: verbose level (%d)\n", verbose);
48 fprintf (fd, "\n");
49 fprintf (fd, "commands: [/hstr/|0xaddr] [a hstr] [d nb|-] [i hstr] [p nb|-] [s/h1/h2/[g]]\n");
50 fprintf (fd, " 0x: move to address addr\n");
51 fprintf (fd, " //: move to hexa stringi hstr\n");
52 fprintf (fd, " a : append hexa string hstr to current address\n");
53 fprintf (fd, " d : delete nb bytes (- until end file)\n");
54 fprintf (fd, " i : insert hexa string hstr to current address\n");
55 fprintf (fd, " p : print nb bytes (- until end file)\n");
56 fprintf (fd, " s : substitute h1 by h2 (g for globally)\n");
57
58 return ret;
59 }
60
61 /* get number of digits */
62
63 int getnbdigits (long int l) {
64 int n = 0;
65 while (l) {
66 n += 2;
67 l /= 256;
68 }
69 return n;
70 }
71
72 /* print a line */
73
74 void printline (char *buffer, int nb, int addr) {
75 int i;
76
77 printf ("0x%0*x:", nbdigits, addr);
78 for (i = 0; i < nb; i++) {
79 printf (" %02x", buffer[i]);
80 }
81 for (i = nb; i < nbcols; i++) {
82 printf (" ");
83 }
84 printf (" ");
85 for (i = 0; i < nb; i++) {
86 char c = buffer[i];
87 printf ("%c", (c > 31) && (c < 127) ? c : '.');
88 }
89 printf ("\n");
90 }
91
92 /* write file function */
93
94 int writefile (char *pt, int nb) {
95 if (fout) {
96 fwrite (pt, 1, nb, fout);
97 }
98 return 1;
99 }
100
101 /* search sequence function */
102
103 int searchseq (char *seq) {
104 char *pt = buffer;
105 int nb = 0;
106 int i, j;
107 int valid = 0;
108 int len = strlen (seq);
109
110 VERBOSE (DEBUG, printf ("search sequence: %s\n", seq));
111
112 while (!feof (fin)) {
113 int nbread = fread (pt, 1, BUFFERSIZE - (pt - buffer), fin);
114 nb += nbread;
115 pt = buffer;
116 for (i = 0; i < nb - len; i++) {
117 valid = 1;
118 for (j = 0; (j < len) && (valid); j++) {
119 if (pt[i + j] != seq[j]) {
120 valid = 0;
121 }
122 }
123 if (valid) {
124 break;
125 }
126 }
127
128 if (!valid) {
129 writefile (buffer, nb - len);
130 offset = 0;
131 addrfile += nb - len;
132 for (i = 0; i < len; i++) {
133 buffer[i] = buffer[nb - len + i];
134 }
135 pt = buffer + len;
136 nb = len;
137 } else {
138 writefile (buffer, i);
139 offset = len;
140 addrfile += i;
141 fseek (fin, i - nb, SEEK_CUR);
142 VERBOSE (DEBUG, printf ("found sequence (%d)\n", i - nb));
143 return 0;
144 }
145 }
146
147 if (!valid) {
148 writefile (buffer, nb);
149 addrfile += len;
150 }
151
152 return 1;
153 }
154
155 /* hexadecimal dump function */
156
157 int hexdump (int len) {
158 char buffer[BUFFERSIZE] = {0};
159 int i;
160
161 char *pt = buffer;
162
163 int nb = 0;
164 while (!feof (fin)) {
165 int nbtoread = BUFFERSIZE - (pt - buffer);
166 if ((len > 0) && (nbtoread > len)) {
167 nbtoread = len;
168 }
169 int nbread = fread (pt, 1, nbtoread, fin);
170 if (len > 0) {
171 len -= nbread;
172 }
173 nb += nbread;
174 pt = buffer;
175
176 /* print line */
177 while ((nb - (int)(pt - buffer)) / nbcols > 0) {
178 printline (pt, nbcols, addrfile);
179 writefile (pt, nbcols);
180 addrfile += nbcols;
181 pt += nbcols;
182 }
183
184 /* copy end buffer */
185 nb -= pt - buffer;
186 for (i = 0; i < nb; i++) {
187 buffer[i] = pt[i];
188 }
189 pt = buffer + nb;
190
191 /* end partial reading */
192 if (len == 0) {
193 break;
194 }
195 }
196
197 /* last line */
198 if (nb > 0) {
199 printline (buffer, nb, addrfile);
200 writefile (buffer, nb);
201 addrfile += nb;
202 }
203
204 return 0;
205 }
206
207 /* special character function */
208
209 char *specialchar (char *s) {
210 int i = 0, j = 0;
211 while (s[i] != 0) {
212 if (s[i] != '\\') {
213 s[j++] = s[i++];
214 continue;
215 }
216
217 int l = 0;
218 switch (s[i + 1]) {
219 case 'a': l = 0x07; i += 2; break;
220 case 'b': l = 0x08; i += 2; break;
221 case 'e': l = 0x1b; i += 2; break;
222 case 'f': l = 0x0c; i += 2; break;
223 case 'n': l = 0x0a; i += 2; break;
224 case 'r': l = 0x0d; i += 2; break;
225 case 't': l = 0x09; i += 2; break;
226 case 'v': l = 0x0b; i += 2; break;
227 case '\\': l = '\\'; i += 2; break;
228 case '\'': l = '\''; i += 2; break;
229 case '"': l = '"'; i += 2; break;
230 case '0':
231 case '1':
232 case '2':
233 case '3':
234 if ((s[i + 2] >= '0') && (s[i + 2] <= '9') &&
235 (s[i + 3] >= '0') && (s[i + 3] <= '9')) {
236 l = (s[i + 1] - '0') * 8 * 8 + (s[i + 2] - '0') * 8 + s[i + 3] - '0';
237 i += 4;
238 }
239 break;
240 case 'x':
241 if ((((s[i + 2] >= '0') && (s[i + 2] <= '9')) ||
242 ((s[i + 2] >= 'A') && (s[i + 2] <= 'F')) ||
243 ((s[i + 2] >= 'a') && (s[i + 2] <= 'f'))) &&
244 (((s[i + 3] >= '0') && (s[i + 3] <= '9')) ||
245 ((s[i + 3] >= 'A') && (s[i + 3] <= 'F')) ||
246 ((s[i + 3] >= 'a') && (s[i + 3] <= 'f')))) {
247 l = s[i + 2] - '0';
248 if (s[i + 2] >= 'a') {
249 l += '0' + 10 - 'a';
250 } else if (s[i + 2] >= 'A') {
251 l += '0' + 10 - 'A';
252 }
253 l = l * 16 + s[i + 3] - '0';
254 if (s[i + 3] >= 'a') {
255 l += '0' + 10 - 'a';
256 } else if (s[i + 3] >= 'A') {
257 l += '0' + 10 - 'A';
258 }
259 i += 4;
260 }
261 break;
262 default:
263 }
264 VERBOSE (DEBUG, printf("l: 0x%02x '%c'\n", l, l));
265 s[j++] = (l != 0) ? l : s[i++];
266 }
267 s[j] = '\0';
268
269 return s;
270 }
271
272 /* main function */
273
274 int main (int argc, char *argv[])
275 {
276 int rc = 0;
277 char *input = NULL;
278 char *output = NULL;
279 char *commands = NULL;
280 int printlen = -1;
281 char *seq = NULL;
282 char *addr = NULL;
283
284 /* get basename */
285 char *pt = progname = argv[0];
286 while (*pt) {
287 if ((*pt == '/') || (*pt == '\\')) {
288 progname = pt + 1;
289 }
290 pt++;
291 }
292
293 while (argc-- > 1) {
294 char *arg = *(++argv);
295 if (arg[0] != '-') {
296 VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- %s\n", progname, arg));
297 return usage (1);
298 }
299 char c = arg[1];
300 switch (c) {
301 case 'e':
302 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
303 if (arg) {
304 arg = specialchar (arg);
305 if (commands == NULL) {
306 commands = arg;
307 } else {
308 strcat (commands, " ");
309 strcat (commands, arg);
310 }
311 }
312 break;
313 case 'i':
314 input = (arg[2]) ? arg + 2 : (--argc > 0 ) ? *(++argv) : NULL;
315 break;
316 case 'n':
317 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
318 if (arg == NULL) {
319 VERBOSE (ERROR, fprintf (stderr, "%s: missing number of columns\n", progname));
320 return usage (1);
321 }
322 nbcols = atoi (arg);
323 break;
324 case 'o':
325 output = (arg[2]) ? arg + 2 : (--argc > 0 ) ? *(++argv) : NULL;
326 break;
327 case 'v':
328 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
329 if (arg == NULL) {
330 VERBOSE (ERROR, fprintf (stderr, "%s: missing verbose level\n", progname));
331 return usage (1);
332 }
333 verbose = atoi (arg);
334 break;
335 case 'h':
336 default:
337 return usage (c != 'h');
338 }
339 }
340
341 /* check input */
342 if (input) {
343 fin = fopen (input, "rb");
344 if (!fin) {
345 VERBOSE (ERROR, fprintf (stderr, "error: can't open file '%s'\n", input));
346 return 1;
347 }
348 } else {
349 fin = stdin;
350 }
351
352 /* check output */
353 if (output) {
354 fout = fopen (output, "wb");
355 if (!fout) {
356 VERBOSE (ERROR, fprintf (stderr, "error: can't open file '%s'\n", output));
357 fclose (fin);
358 return 1;
359 }
360 } else {
361 //fout = stdout;
362 }
363
364 /* get file size */
365 if (fin != stdin) {
366 fseek (fin, 0 , SEEK_END);
367 long int filesize = ftell (fin);
368 fseek (fin, 0 , SEEK_SET);
369 nbdigits = getnbdigits (filesize);
370 }
371
372 if (commands == NULL) {
373 hexdump (-1);
374 } else {
375 VERBOSE (DEBUG, printf ("commands: %s\n", commands));
376 while ((*commands != '\0') && (rc == 0)) {
377 switch (*commands++) {
378 case ' ':
379 case '\t':
380 break;
381
382 case '/': /* read patern */
383 seq = commands;
384 while (*commands) {
385 if (*commands == '/') {
386 *commands++ = 0;
387 break;
388 }
389 commands++;
390 }
391 if (*seq != 0) {
392 rc = searchseq (seq);
393 } else {
394 VERBOSE (ERROR, fprintf (stderr, "no sequence definied\n"));
395 rc = 1;
396 }
397 break;
398
399 case '0': /* read address */
400 break;
401
402 case 'a': /* append mode */
403 break;
404
405 case 'd': /* delete mode */
406 break;
407
408 case 'i': /* insert mode */
409 break;
410
411 case 'p': /* print mode */
412 printlen = -1;
413 while (*commands != '\0') {
414 if ((*commands == ' ') || (*commands == '\t')) {
415 commands++;
416 } else if ((*commands >= '0') && (*commands <= '9')) {
417 printlen = strtol (commands, &commands, 10);
418 break;
419 } else if (*commands == '-') {
420 printlen = -1;
421 commands++;
422 break;
423 } else {
424 VERBOSE (ERROR, fprintf (stderr, "unkown print lenght (%s)\n", commands));
425 rc = 1;
426 break;
427 }
428 }
429 if (rc == 0) {
430 hexdump (printlen);
431 }
432 break;
433
434 case 's': /* substitute mode */
435 break;
436
437 default:
438 VERBOSE (ERROR, fprintf (stderr, "unknown command (%c)\n", commands[-1]));
439 rc = 1;
440 }
441 }
442 }
443
444 /* end of file */
445 if ((rc == 0) && (fout != NULL)) {
446 while (!feof (fin)) {
447 int nbread = fread (buffer, 1, BUFFERSIZE, fin);
448 if (nbread) {
449 fwrite (buffer, 1, nbread, fout);
450 }
451 }
452 }
453
454 /* close all */
455 if (fin) fclose (fin);
456 if (fout) fclose (fout);
457
458 return rc;
459 }
460
461 // test: hexdump.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
462 // test: hexdump.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
463 // test: hexdump.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
464 // test: hexdump.exe -i hexdump.c | grep -q '0x[0-9a-f]*: '
465 // test: hexdump.exe -i hexdump.c -n 3 | head -2 | tail -1 | grep -q '0x0003: 64 65 70 dep'
466 // test: hexdump.exe -i hexdump.c -o test.c -e 'p 200' | tail -1 | grep -q '0x00c0:'
467 // test: cmp hexdump.c test.c
468 // test: rm test.c
469 // test: hexdump.exe -i hexdump.c -e ' /cflags/ p 16 /debug/ p 8' | grep -q '0x0019: 2a 2f 0a 2f 2a 20 6c 69 \*/\./\* li'
470 // test: hexdump.exe -i hexdump.c -o test.c -e ' /cflags/ p 16 /debug/ p 8' | grep -q '0x0027: 64 65 62 75 67 2e 6f 20 debug.o'
471 // test: cmp hexdump.c test.c
472 // test: rm test.c
473
474 /* vim: set ts=4 sw=4 et: */