1 /* dumpsexp.c - Dump S-expressions.
2 * Copyright (C) 2007, 2010 Free Software Foundation, Inc.
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.
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.
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/>.
25 /* For a native WindowsCE binary we need to include gpg-error.h to
26 provide a replacement for strerror. */
28 # include <gpg-error.h>
31 #define PGM "dumpsexp"
32 #define MYVERSION_LINE PGM " (Libgcrypt) " VERSION
33 #define BUGREPORT_LINE "\nReport bugs to <bug-libgcrypt@gnupg.org>.\n"
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. */
42 print_version (int with_help)
44 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",
54 "Usage: " PGM " [OPTIONS] [file]\n"
55 "Debug tool for S-expressions\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 );
71 fputs ("usage: " PGM " [OPTIONS] NBYTES\n", stderr);
72 fputs (" (use --help to display options)\n", stderr);
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))
89 /* Return true if P points to a byte containing a whitespace according
90 to the S-expressions definition. */
96 case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1;
102 logit (const char *format, ...)
106 va_start (arg_ptr, format) ;
107 fputs (PGM ": ", stderr);
108 vfprintf (stderr, format, arg_ptr);
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;
133 while ( (c1=getc (fp)) != EOF && space_p (c1) )
138 if (!hexdigit_p (c1))
140 logit ("non hex-digit encountered\n");
144 while ( (c2=getc (fp)) != EOF && space_p (c2) )
148 logit ("error reading second hex nibble\n");
151 if (!hexdigit_p (c2))
153 logit ("second hex nibble is not a hex-digit\n");
156 return xtoi_1 (c1) * 16 + xtoi_1 (c2);
163 /* Flush the raw data buffer. */
165 flushdatabuffer (void)
173 printf ("%08lu ", databufferoffset);
175 printf ("%08lx ", databufferoffset);
176 for (i=0; i < databufferlen; i++)
180 if (i < skipdatabufferlen)
184 printf (" %02x", databuffer[i]);
189 for (; i < sizeof (databuffer); i++)
195 fputs (" |", stdout);
196 for (i=0; i < databufferlen; i++)
198 if (i < skipdatabufferlen)
200 else if (databuffer[i] >= ' ' && databuffer[i] <= '~'
201 && databuffer[i] != '|')
202 putchar (databuffer[i]);
209 skipdatabufferlen = 0;
213 /* Add C to the raw data buffer and flush as needed. */
217 if ( databufferlen >= sizeof databuffer )
219 databuffer[databufferlen++] = c;
224 printcursor (int both)
230 for (i=0; i < sizeof (databuffer); i++)
234 if (i+1 == nbytesprinted)
236 fputs (" ^ ", stdout);
246 for (i=0; i < nbytesprinted-1; i++)
250 databufferlen = skipdatabufferlen = nbytesprinted;
254 printerr (const char *text)
257 printf ("\n Error: %s\n", text);
261 printctl (const char *text)
263 if (verbose && !advanced)
266 printf ("%s\n", text);
277 /* printhex (int c) */
279 /* printf ("\\x%02x", c); */
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.
291 gcry_sexp_sprint (const gcry_sexp_t list,
292 void *buffer, size_t maxlength )
294 static unsigned char empty[3] = { ST_OPEN, ST_CLOSE, ST_STOP };
295 const unsigned char *s;
301 s = list? list->d : empty;
303 while ( *s != ST_STOP )
311 for (i=0; i < indent; i++)
320 if (*s != ST_OPEN && *s != ST_STOP)
323 for (i=0; i < indent; i++)
329 memcpy (&n, s, sizeof n);
335 switch ( (type=suitable_encoding (s, n)))
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;
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;
348 if (s[n] != ST_CLOSE)
353 snprintf (numbuf, sizeof numbuf, "%u:", (unsigned int)n );
354 d = stpcpy (d, numbuf);
370 /* Prepare for saving a chunk of data. */
377 /* Push C on the current data chunk. */
384 /* Flush and thus print the current data chunk. */
392 /* Returns 0 on success. */
394 parse_and_print (FILE *fp)
396 static const char tokenchars[] =
397 "abcdefghijklmnopqrstuvwxyz"
398 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
399 "0123456789-./_:*+=";
405 unsigned long datalen = 0;
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
417 while ((c = my_getc (fp)) != EOF )
425 if (strchr (tokenchars, c))
437 printerr ("unmatched display hint");
447 printerr ("unmatched display hint");
456 printctl ("beginstring");
463 printctl ("beginhex");
469 printctl ("beginbase64");
475 printerr ("nested display hint");
481 printerr ("no open display hint");
484 else if (c >= '0' && c <= '9')
487 printerr ("zero prefixed length");
491 else if (strchr (tokenchars, c))
496 else if (whitespace_p (c))
500 printerr ("rescanning is not supported");
502 else if (c == '&' || c == '\\')
504 printerr ("reserved punctuation detected");
508 printerr ("bad character detected");
515 unsigned long tmp = datalen * 10 + (c - '0');
518 printerr ("overflow in data length");
529 printerr ("no data length");
535 else if (c == '\"' || c == '#' || c == '|' )
537 /* We ignore the optional length and divert to the init
538 state parser code. */
539 goto parse_init_state;
542 printerr ("invalid length specification");
547 printctl ("begindata");
558 printctl ("enddata");
566 printctl ("endstring");
579 case 'b': push_data ('\b'); state = IN_STRING; break;
580 case 't': push_data ('\t'); state = IN_STRING; break;
581 case 'v': push_data ('\v'); state = IN_STRING; break;
582 case 'n': push_data ('\n'); state = IN_STRING; break;
583 case 'f': push_data ('\f'); state = IN_STRING; break;
584 case 'r': push_data ('\r'); state = IN_STRING; break;
585 case '"': push_data ('"'); state = IN_STRING; break;
586 case '\'': push_data ('\''); state = IN_STRING; break;
587 case '\\': push_data ('\\'); state = IN_STRING; break;
589 case '0': case '1': case '2': case '3': case '4':
590 case '5': case '6': case '7':
593 quote_buf[quote_idx++] = c;
610 printerr ("invalid escape sequence");
617 if (quote_idx < 3 && strchr ("01234567", c))
619 quote_buf[quote_idx++] = c;
622 push_data ((unsigned int)quote_buf[0] * 8 * 8
623 + (unsigned int)quote_buf[1] * 8
624 + (unsigned int)quote_buf[2]);
632 if (quote_idx < 2 && strchr ("0123456789abcdefABCDEF", c))
634 quote_buf[quote_idx++] = c;
637 push_data (xtoi_1 (quote_buf[0]) * 16
638 + xtoi_1 (quote_buf[1]));
661 printerr ("odd number of hex digits");
666 else if (!whitespace_p (c))
667 printerr ("bad hex character");
673 printctl ("endbase64");
682 logit ("invalid state %d detected", state);
689 logit ("error reading input: %s\n", strerror (errno));
698 main (int argc, char **argv)
706 while (argc && **argv == '-' && (*argv)[1] == '-')
713 else if (!strcmp (*argv, "--version"))
715 else if (!strcmp (*argv, "--help"))
717 else if (!strcmp (*argv, "--verbose"))
722 else if (!strcmp (*argv, "--decimal"))
727 else if (!strcmp (*argv, "--assume-hex"))
732 else if (!strcmp (*argv, "--advanced"))
743 rc = parse_and_print (stdin);
748 for (; argc; argv++, argc--)
750 FILE *fp = fopen (*argv, "rb");
753 logit ("can't open `%s': %s\n", *argv, strerror (errno));
758 if (parse_and_print (fp))