Line data Source code
1 : /* dumpsexp.c - Dump S-expressions.
2 : * Copyright (C) 2007, 2010 Free Software Foundation, Inc.
3 : *
4 : * This program is free software; you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published
6 : * by the Free Software Foundation; either version 3 of the License,
7 : * or (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful, but
10 : * WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include <config.h>
19 : #include <stdio.h>
20 : #include <string.h>
21 : #include <stdlib.h>
22 : #include <assert.h>
23 : #include <stdarg.h>
24 : #include <errno.h>
25 : /* For a native WindowsCE binary we need to include gpg-error.h to
26 : provide a replacement for strerror. */
27 : #ifdef __MINGW32CE__
28 : # include <gpg-error.h>
29 : #endif
30 :
31 : #define PGM "dumpsexp"
32 : #define MYVERSION_LINE PGM " (Libgcrypt) " VERSION
33 : #define BUGREPORT_LINE "\nReport bugs to <bug-libgcrypt@gnupg.org>.\n"
34 :
35 :
36 : static int verbose; /* Verbose mode. */
37 : static int decimal; /* Print addresses in decimal. */
38 : static int assume_hex; /* Assume input is hexencoded. */
39 : static int advanced; /* Advanced format output. */
40 :
41 : static void
42 0 : print_version (int with_help)
43 : {
44 0 : fputs (MYVERSION_LINE "\n"
45 : "Copyright (C) 2010 Free Software Foundation, Inc.\n"
46 : "License GPLv3+: GNU GPL version 3 or later "
47 : "<http://gnu.org/licenses/gpl.html>\n"
48 : "This is free software: you are free to change and redistribute it.\n"
49 : "There is NO WARRANTY, to the extent permitted by law.\n",
50 : stdout);
51 :
52 0 : if (with_help)
53 0 : fputs ("\n"
54 : "Usage: " PGM " [OPTIONS] [file]\n"
55 : "Debug tool for S-expressions\n"
56 : "\n"
57 : " --decimal Print offsets using decimal notation\n"
58 : " --assume-hex Assume input is a hex dump\n"
59 : " --advanced Print file in advanced format\n"
60 : " --verbose Show what we are doing\n"
61 : " --version Print version of the program and exit\n"
62 : " --help Display this help and exit\n"
63 : BUGREPORT_LINE, stdout );
64 :
65 0 : exit (0);
66 : }
67 :
68 : static int
69 0 : print_usage (void)
70 : {
71 0 : fputs ("usage: " PGM " [OPTIONS] NBYTES\n", stderr);
72 0 : fputs (" (use --help to display options)\n", stderr);
73 0 : exit (1);
74 : }
75 :
76 :
77 : #define space_p(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
78 : #define digit_p(a) ((a) >= '0' && (a) <= '9')
79 : #define octdigit_p(a) ((a) >= '0' && (a) <= '7')
80 : #define alpha_p(a) ( ((a) >= 'A' && (a) <= 'Z') \
81 : || ((a) >= 'a' && (a) <= 'z'))
82 : #define hexdigit_p(a) (digit_p (a) \
83 : || ((a) >= 'A' && (a) <= 'F') \
84 : || ((a) >= 'a' && (a) <= 'f'))
85 : #define xtoi_1(a) ((a) <= '9'? ((a)- '0'): \
86 : (a) <= 'F'? ((a)-'A'+10):((a)-'a'+10))
87 :
88 :
89 : /* Return true if P points to a byte containing a whitespace according
90 : to the S-expressions definition. */
91 : static inline int
92 0 : whitespace_p (int c)
93 : {
94 0 : switch (c)
95 : {
96 0 : case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1;
97 0 : default: return 0;
98 : }
99 : }
100 :
101 : static void
102 0 : logit (const char *format, ...)
103 : {
104 : va_list arg_ptr;
105 :
106 0 : va_start (arg_ptr, format) ;
107 0 : fputs (PGM ": ", stderr);
108 0 : vfprintf (stderr, format, arg_ptr);
109 0 : putc ('\n', stderr);
110 0 : va_end (arg_ptr);
111 0 : }
112 :
113 : /* The raw data buffer and its current length */
114 : static unsigned char databuffer[16];
115 : static int databufferlen;
116 : /* The number of bytes in databuffer which should be skipped at a flush. */
117 : static int skipdatabufferlen;
118 : /* The number of raw bytes printed on the last line. */
119 : static int nbytesprinted;
120 : /* The file offset of the current data buffer . */
121 : static unsigned long databufferoffset;
122 :
123 :
124 :
125 : static int
126 0 : my_getc (FILE *fp)
127 : {
128 : int c1, c2;
129 :
130 0 : if (!assume_hex)
131 0 : return getc (fp);
132 :
133 0 : while ( (c1=getc (fp)) != EOF && space_p (c1) )
134 : ;
135 0 : if (c1 == EOF)
136 0 : return EOF;
137 :
138 0 : if (!hexdigit_p (c1))
139 : {
140 0 : logit ("non hex-digit encountered\n");
141 0 : return EOF;
142 : }
143 :
144 0 : while ( (c2=getc (fp)) != EOF && space_p (c2) )
145 : ;
146 0 : if (c2 == EOF)
147 : {
148 0 : logit ("error reading second hex nibble\n");
149 0 : return EOF;
150 : }
151 0 : if (!hexdigit_p (c2))
152 : {
153 0 : logit ("second hex nibble is not a hex-digit\n");
154 0 : return EOF;
155 : }
156 0 : return xtoi_1 (c1) * 16 + xtoi_1 (c2);
157 : }
158 :
159 :
160 :
161 :
162 :
163 : /* Flush the raw data buffer. */
164 : static void
165 0 : flushdatabuffer (void)
166 : {
167 : int i;
168 :
169 0 : if (!databufferlen)
170 0 : return;
171 0 : nbytesprinted = 0;
172 0 : if (decimal)
173 0 : printf ("%08lu ", databufferoffset);
174 : else
175 0 : printf ("%08lx ", databufferoffset);
176 0 : for (i=0; i < databufferlen; i++)
177 : {
178 0 : if (i == 8)
179 0 : putchar (' ');
180 0 : if (i < skipdatabufferlen)
181 0 : fputs (" ", stdout);
182 : else
183 : {
184 0 : printf (" %02x", databuffer[i]);
185 0 : databufferoffset++;
186 : }
187 0 : nbytesprinted++;
188 : }
189 0 : for (; i < sizeof (databuffer); i++)
190 : {
191 0 : if (i == 8)
192 0 : putchar (' ');
193 0 : fputs (" ", stdout);
194 : }
195 0 : fputs (" |", stdout);
196 0 : for (i=0; i < databufferlen; i++)
197 : {
198 0 : if (i < skipdatabufferlen)
199 0 : putchar (' ');
200 0 : else if (databuffer[i] >= ' ' && databuffer[i] <= '~'
201 0 : && databuffer[i] != '|')
202 0 : putchar (databuffer[i]);
203 : else
204 0 : putchar ('.');
205 : }
206 0 : putchar ('|');
207 0 : putchar ('\n');
208 0 : databufferlen = 0;
209 0 : skipdatabufferlen = 0;
210 : }
211 :
212 :
213 : /* Add C to the raw data buffer and flush as needed. */
214 : static void
215 0 : addrawdata (int c)
216 : {
217 0 : if ( databufferlen >= sizeof databuffer )
218 0 : flushdatabuffer ();
219 0 : databuffer[databufferlen++] = c;
220 0 : }
221 :
222 :
223 : static void
224 0 : printcursor (int both)
225 : {
226 : int i;
227 :
228 0 : flushdatabuffer ();
229 0 : printf ("%8s ", "");
230 0 : for (i=0; i < sizeof (databuffer); i++)
231 : {
232 0 : if (i == 8)
233 0 : putchar (' ');
234 0 : if (i+1 == nbytesprinted)
235 : {
236 0 : fputs (" ^ ", stdout);
237 0 : if (!both)
238 0 : break;
239 : }
240 : else
241 0 : fputs (" ", stdout);
242 : }
243 0 : if (both)
244 : {
245 0 : fputs (" ", stdout);
246 0 : for (i=0; i < nbytesprinted-1; i++)
247 0 : putchar (' ');
248 0 : putchar ('^');
249 : }
250 0 : databufferlen = skipdatabufferlen = nbytesprinted;
251 0 : }
252 :
253 : static void
254 0 : printerr (const char *text)
255 : {
256 0 : printcursor (1);
257 0 : printf ("\n Error: %s\n", text);
258 0 : }
259 :
260 : static void
261 0 : printctl (const char *text)
262 : {
263 0 : if (verbose && !advanced)
264 : {
265 0 : printcursor (0);
266 0 : printf ("%s\n", text);
267 : }
268 0 : }
269 :
270 : static void
271 0 : printchr (int c)
272 : {
273 0 : putchar (c);
274 0 : }
275 :
276 : /* static void */
277 : /* printhex (int c) */
278 : /* { */
279 : /* printf ("\\x%02x", c); */
280 : /* } */
281 :
282 :
283 : #if 0
284 : /****************
285 : * Print SEXP to buffer using the MODE. Returns the length of the
286 : * SEXP in buffer or 0 if the buffer is too short (We have at least an
287 : * empty list consisting of 2 bytes). If a buffer of NULL is provided,
288 : * the required length is returned.
289 : */
290 : size_t
291 : gcry_sexp_sprint (const gcry_sexp_t list,
292 : void *buffer, size_t maxlength )
293 : {
294 : static unsigned char empty[3] = { ST_OPEN, ST_CLOSE, ST_STOP };
295 : const unsigned char *s;
296 : char *d;
297 : DATALEN n;
298 : char numbuf[20];
299 : int i, indent = 0;
300 :
301 : s = list? list->d : empty;
302 : d = buffer;
303 : while ( *s != ST_STOP )
304 : {
305 : switch ( *s )
306 : {
307 : case ST_OPEN:
308 : s++;
309 : if (indent)
310 : putchar ('\n');
311 : for (i=0; i < indent; i++)
312 : putchar (' ');
313 : putchar ('(');
314 : indent++;
315 : break;
316 : case ST_CLOSE:
317 : s++;
318 : putchar (')');
319 : indent--;
320 : if (*s != ST_OPEN && *s != ST_STOP)
321 : {
322 : putchar ('\n');
323 : for (i=0; i < indent; i++)
324 : putchar (' ');
325 : }
326 : break;
327 : case ST_DATA:
328 : s++;
329 : memcpy (&n, s, sizeof n);
330 : s += sizeof n;
331 : {
332 : int type;
333 : size_t nn;
334 :
335 : switch ( (type=suitable_encoding (s, n)))
336 : {
337 : case 1: nn = convert_to_string (s, n, NULL); break;
338 : case 2: nn = convert_to_token (s, n, NULL); break;
339 : default: nn = convert_to_hex (s, n, NULL); break;
340 : }
341 : switch (type)
342 : {
343 : case 1: convert_to_string (s, n, d); break;
344 : case 2: convert_to_token (s, n, d); break;
345 : default: convert_to_hex (s, n, d); break;
346 : }
347 : d += nn;
348 : if (s[n] != ST_CLOSE)
349 : putchar (' ');
350 : }
351 : else
352 : {
353 : snprintf (numbuf, sizeof numbuf, "%u:", (unsigned int)n );
354 : d = stpcpy (d, numbuf);
355 : memcpy (d, s, n);
356 : d += n;
357 : }
358 : s += n;
359 : break;
360 : default:
361 : BUG ();
362 : }
363 : }
364 : putchar ('\n');
365 : return len;
366 : }
367 : #endif
368 :
369 :
370 : /* Prepare for saving a chunk of data. */
371 : static void
372 0 : init_data (void)
373 : {
374 :
375 0 : }
376 :
377 : /* Push C on the current data chunk. */
378 : static void
379 0 : push_data (int c)
380 : {
381 : (void)c;
382 0 : }
383 :
384 : /* Flush and thus print the current data chunk. */
385 : static void
386 0 : flush_data (void)
387 : {
388 :
389 0 : }
390 :
391 :
392 : /* Returns 0 on success. */
393 : static int
394 0 : parse_and_print (FILE *fp)
395 : {
396 : static const char tokenchars[] =
397 : "abcdefghijklmnopqrstuvwxyz"
398 : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
399 : "0123456789-./_:*+=";
400 : int c;
401 0 : int level = 0;
402 0 : int tokenc = 0;
403 0 : int hexcount = 0;
404 0 : int disphint = 0;
405 0 : unsigned long datalen = 0;
406 : char quote_buf[10];
407 0 : int quote_idx = 0;
408 : enum
409 : {
410 : INIT_STATE = 0, IN_NUMBER, PRE_DATA, IN_DATA, IN_STRING,
411 : IN_ESCAPE, IN_OCT_ESC, IN_HEX_ESC,
412 : CR_ESC, LF_ESC, IN_HEXFMT, IN_BASE64
413 : }
414 0 : state = INIT_STATE;
415 :
416 :
417 0 : while ((c = my_getc (fp)) != EOF )
418 : {
419 0 : addrawdata (c);
420 0 : switch (state)
421 : {
422 : case INIT_STATE:
423 0 : if (tokenc)
424 : {
425 0 : if (strchr (tokenchars, c))
426 : {
427 0 : printchr (c);
428 0 : continue;
429 : }
430 0 : tokenc = 0;
431 : }
432 : parse_init_state:
433 0 : if (c == '(')
434 : {
435 0 : if (disphint)
436 : {
437 0 : printerr ("unmatched display hint");
438 0 : disphint = 0;
439 : }
440 0 : printctl ("open");
441 0 : level++;
442 : }
443 0 : else if (c == ')')
444 : {
445 0 : if (disphint)
446 : {
447 0 : printerr ("unmatched display hint");
448 0 : disphint = 0;
449 : }
450 0 : printctl ("close");
451 0 : level--;
452 : }
453 0 : else if (c == '\"')
454 : {
455 0 : state = IN_STRING;
456 0 : printctl ("beginstring");
457 0 : init_data ();
458 : }
459 0 : else if (c == '#')
460 : {
461 0 : state = IN_HEXFMT;
462 0 : hexcount = 0;
463 0 : printctl ("beginhex");
464 0 : init_data ();
465 : }
466 0 : else if (c == '|')
467 : {
468 0 : state = IN_BASE64;
469 0 : printctl ("beginbase64");
470 0 : init_data ();
471 : }
472 0 : else if (c == '[')
473 : {
474 0 : if (disphint)
475 0 : printerr ("nested display hint");
476 0 : disphint = c;
477 : }
478 0 : else if (c == ']')
479 : {
480 0 : if (!disphint)
481 0 : printerr ("no open display hint");
482 0 : disphint = 0;
483 : }
484 0 : else if (c >= '0' && c <= '9')
485 : {
486 0 : if (c == '0')
487 0 : printerr ("zero prefixed length");
488 0 : state = IN_NUMBER;
489 0 : datalen = (c - '0');
490 : }
491 0 : else if (strchr (tokenchars, c))
492 : {
493 0 : printchr (c);
494 0 : tokenc = c;
495 : }
496 0 : else if (whitespace_p (c))
497 : ;
498 0 : else if (c == '{')
499 : {
500 0 : printerr ("rescanning is not supported");
501 : }
502 0 : else if (c == '&' || c == '\\')
503 : {
504 0 : printerr ("reserved punctuation detected");
505 : }
506 : else
507 : {
508 0 : printerr ("bad character detected");
509 : }
510 0 : break;
511 :
512 : case IN_NUMBER:
513 0 : if (digit_p (c))
514 0 : {
515 0 : unsigned long tmp = datalen * 10 + (c - '0');
516 0 : if (tmp < datalen)
517 : {
518 0 : printerr ("overflow in data length");
519 0 : state = INIT_STATE;
520 0 : datalen = 0;
521 : }
522 : else
523 0 : datalen = tmp;
524 : }
525 0 : else if (c == ':')
526 : {
527 0 : if (!datalen)
528 : {
529 0 : printerr ("no data length");
530 0 : state = INIT_STATE;
531 : }
532 : else
533 0 : state = PRE_DATA;
534 : }
535 0 : else if (c == '\"' || c == '#' || c == '|' )
536 : {
537 : /* We ignore the optional length and divert to the init
538 : state parser code. */
539 : goto parse_init_state;
540 : }
541 : else
542 0 : printerr ("invalid length specification");
543 0 : break;
544 :
545 : case PRE_DATA:
546 0 : state = IN_DATA;
547 0 : printctl ("begindata");
548 0 : init_data ();
549 : case IN_DATA:
550 0 : if (datalen)
551 : {
552 0 : push_data (c);
553 0 : datalen--;
554 : }
555 0 : if (!datalen)
556 : {
557 0 : state = INIT_STATE;
558 0 : printctl ("enddata");
559 0 : flush_data ();
560 : }
561 0 : break;
562 :
563 : case IN_STRING:
564 0 : if (c == '\"')
565 : {
566 0 : printctl ("endstring");
567 0 : flush_data ();
568 0 : state = INIT_STATE;
569 : }
570 0 : else if (c == '\\')
571 0 : state = IN_ESCAPE;
572 : else
573 0 : push_data (c);
574 0 : break;
575 :
576 : case IN_ESCAPE:
577 0 : switch (c)
578 : {
579 0 : case 'b': push_data ('\b'); state = IN_STRING; break;
580 0 : case 't': push_data ('\t'); state = IN_STRING; break;
581 0 : case 'v': push_data ('\v'); state = IN_STRING; break;
582 0 : case 'n': push_data ('\n'); state = IN_STRING; break;
583 0 : case 'f': push_data ('\f'); state = IN_STRING; break;
584 0 : case 'r': push_data ('\r'); state = IN_STRING; break;
585 0 : case '"': push_data ('"'); state = IN_STRING; break;
586 0 : case '\'': push_data ('\''); state = IN_STRING; break;
587 0 : case '\\': push_data ('\\'); state = IN_STRING; break;
588 :
589 : case '0': case '1': case '2': case '3': case '4':
590 : case '5': case '6': case '7':
591 0 : state = IN_OCT_ESC;
592 0 : quote_idx = 0;
593 0 : quote_buf[quote_idx++] = c;
594 0 : break;
595 :
596 : case 'x':
597 0 : state = IN_HEX_ESC;
598 0 : quote_idx = 0;
599 0 : break;
600 :
601 : case '\r':
602 0 : state = CR_ESC;
603 0 : break;
604 :
605 : case '\n':
606 0 : state = LF_ESC;
607 0 : break;
608 :
609 : default:
610 0 : printerr ("invalid escape sequence");
611 0 : state = IN_STRING;
612 0 : break;
613 : }
614 0 : break;
615 :
616 : case IN_OCT_ESC:
617 0 : if (quote_idx < 3 && strchr ("01234567", c))
618 : {
619 0 : quote_buf[quote_idx++] = c;
620 0 : if (quote_idx == 3)
621 : {
622 0 : push_data ((unsigned int)quote_buf[0] * 8 * 8
623 0 : + (unsigned int)quote_buf[1] * 8
624 0 : + (unsigned int)quote_buf[2]);
625 0 : state = IN_STRING;
626 : }
627 : }
628 : else
629 0 : state = IN_STRING;
630 0 : break;
631 : case IN_HEX_ESC:
632 0 : if (quote_idx < 2 && strchr ("0123456789abcdefABCDEF", c))
633 : {
634 0 : quote_buf[quote_idx++] = c;
635 0 : if (quote_idx == 2)
636 : {
637 0 : push_data (xtoi_1 (quote_buf[0]) * 16
638 0 : + xtoi_1 (quote_buf[1]));
639 0 : state = IN_STRING;
640 : }
641 : }
642 : else
643 0 : state = IN_STRING;
644 0 : break;
645 : case CR_ESC:
646 0 : state = IN_STRING;
647 0 : break;
648 : case LF_ESC:
649 0 : state = IN_STRING;
650 0 : break;
651 :
652 : case IN_HEXFMT:
653 0 : if (hexdigit_p (c))
654 : {
655 0 : push_data (c);
656 0 : hexcount++;
657 : }
658 0 : else if (c == '#')
659 : {
660 0 : if ((hexcount & 1))
661 0 : printerr ("odd number of hex digits");
662 0 : printctl ("endhex");
663 0 : flush_data ();
664 0 : state = INIT_STATE;
665 : }
666 0 : else if (!whitespace_p (c))
667 0 : printerr ("bad hex character");
668 0 : break;
669 :
670 : case IN_BASE64:
671 0 : if (c == '|')
672 : {
673 0 : printctl ("endbase64");
674 0 : flush_data ();
675 0 : state = INIT_STATE;
676 : }
677 : else
678 0 : push_data (c);
679 0 : break;
680 :
681 : default:
682 0 : logit ("invalid state %d detected", state);
683 0 : exit (1);
684 : }
685 : }
686 0 : flushdatabuffer ();
687 0 : if (ferror (fp))
688 : {
689 0 : logit ("error reading input: %s\n", strerror (errno));
690 0 : return -1;
691 : }
692 0 : return 0;
693 : }
694 :
695 :
696 :
697 : int
698 0 : main (int argc, char **argv)
699 : {
700 : int rc;
701 :
702 0 : if (argc)
703 : {
704 0 : argc--; argv++;
705 : }
706 0 : while (argc && **argv == '-' && (*argv)[1] == '-')
707 : {
708 0 : if (!(*argv)[2])
709 : {
710 0 : argc--; argv++;
711 0 : break;
712 : }
713 0 : else if (!strcmp (*argv, "--version"))
714 0 : print_version (0);
715 0 : else if (!strcmp (*argv, "--help"))
716 0 : print_version (1);
717 0 : else if (!strcmp (*argv, "--verbose"))
718 : {
719 0 : argc--; argv++;
720 0 : verbose = 1;
721 : }
722 0 : else if (!strcmp (*argv, "--decimal"))
723 : {
724 0 : argc--; argv++;
725 0 : decimal = 1;
726 : }
727 0 : else if (!strcmp (*argv, "--assume-hex"))
728 : {
729 0 : argc--; argv++;
730 0 : assume_hex = 1;
731 : }
732 0 : else if (!strcmp (*argv, "--advanced"))
733 : {
734 0 : argc--; argv++;
735 0 : advanced = 1;
736 : }
737 : else
738 0 : print_usage ();
739 : }
740 :
741 0 : if (!argc)
742 : {
743 0 : rc = parse_and_print (stdin);
744 : }
745 : else
746 : {
747 0 : rc = 0;
748 0 : for (; argc; argv++, argc--)
749 : {
750 0 : FILE *fp = fopen (*argv, "rb");
751 0 : if (!fp)
752 : {
753 0 : logit ("can't open `%s': %s\n", *argv, strerror (errno));
754 0 : rc = 1;
755 : }
756 : else
757 : {
758 0 : if (parse_and_print (fp))
759 0 : rc = 1;
760 0 : fclose (fp);
761 : }
762 : }
763 : }
764 :
765 0 : return !!rc;
766 : }
|