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