AES-NI/OCB: Perform checksumming inline with encryption
[libgcrypt.git] / src / dumpsexp.c
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 print_version (int with_help)
43 {
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",
50          stdout);
51
52   if (with_help)
53     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   exit (0);
66 }
67
68 static int
69 print_usage (void)
70 {
71   fputs ("usage: " PGM " [OPTIONS] NBYTES\n", stderr);
72   fputs ("       (use --help to display options)\n", stderr);
73   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 whitespace_p (int c)
93 {
94   switch (c)
95     {
96     case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1;
97     default: return 0;
98     }
99 }
100
101 static void
102 logit (const char *format, ...)
103 {
104   va_list arg_ptr;
105
106   va_start (arg_ptr, format) ;
107   fputs (PGM ": ", stderr);
108   vfprintf (stderr, format, arg_ptr);
109   putc ('\n', stderr);
110   va_end (arg_ptr);
111 }
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 my_getc (FILE *fp)
127 {
128   int c1, c2;
129
130   if (!assume_hex)
131     return getc (fp);
132
133   while ( (c1=getc (fp)) != EOF && space_p (c1) )
134     ;
135   if (c1 == EOF)
136     return EOF;
137
138   if (!hexdigit_p (c1))
139     {
140       logit ("non hex-digit encountered\n");
141       return EOF;
142     }
143
144   while ( (c2=getc (fp)) != EOF && space_p (c2) )
145     ;
146   if (c2 == EOF)
147     {
148       logit ("error reading second hex nibble\n");
149       return EOF;
150     }
151   if (!hexdigit_p (c2))
152     {
153       logit ("second hex nibble is not a hex-digit\n");
154       return EOF;
155     }
156   return xtoi_1 (c1) * 16 + xtoi_1 (c2);
157 }
158
159
160
161
162
163 /* Flush the raw data buffer.  */
164 static void
165 flushdatabuffer (void)
166 {
167   int i;
168
169   if (!databufferlen)
170     return;
171   nbytesprinted = 0;
172   if (decimal)
173     printf ("%08lu ", databufferoffset);
174   else
175     printf ("%08lx ", databufferoffset);
176   for (i=0; i < databufferlen; i++)
177     {
178       if (i == 8)
179         putchar (' ');
180       if (i < skipdatabufferlen)
181         fputs ("   ", stdout);
182       else
183         {
184           printf (" %02x", databuffer[i]);
185           databufferoffset++;
186         }
187       nbytesprinted++;
188     }
189   for (; i < sizeof (databuffer); i++)
190     {
191       if (i == 8)
192         putchar (' ');
193       fputs ("   ", stdout);
194     }
195   fputs ("  |", stdout);
196   for (i=0; i < databufferlen; i++)
197     {
198       if (i < skipdatabufferlen)
199         putchar (' ');
200       else if (databuffer[i] >= ' ' && databuffer[i] <= '~'
201                && databuffer[i] != '|')
202         putchar (databuffer[i]);
203       else
204         putchar ('.');
205     }
206   putchar ('|');
207   putchar ('\n');
208   databufferlen = 0;
209   skipdatabufferlen = 0;
210 }
211
212
213 /* Add C to the raw data buffer and flush as needed.  */
214 static void
215 addrawdata (int c)
216 {
217   if ( databufferlen >= sizeof databuffer )
218     flushdatabuffer ();
219   databuffer[databufferlen++] = c;
220 }
221
222
223 static void
224 printcursor (int both)
225 {
226   int i;
227
228   flushdatabuffer ();
229   printf ("%8s ", "");
230   for (i=0; i < sizeof (databuffer); i++)
231     {
232       if (i == 8)
233         putchar (' ');
234       if (i+1 == nbytesprinted)
235         {
236           fputs (" ^ ", stdout);
237           if (!both)
238             break;
239         }
240       else
241         fputs ("   ", stdout);
242     }
243   if (both)
244     {
245       fputs ("   ", stdout);
246       for (i=0; i < nbytesprinted-1; i++)
247         putchar (' ');
248       putchar ('^');
249     }
250   databufferlen = skipdatabufferlen = nbytesprinted;
251 }
252
253 static void
254 printerr (const char *text)
255 {
256   printcursor (1);
257   printf ("\n          Error: %s\n", text);
258 }
259
260 static void
261 printctl (const char *text)
262 {
263   if (verbose && !advanced)
264     {
265       printcursor (0);
266       printf ("%s\n", text);
267     }
268 }
269
270 static void
271 printchr (int c)
272 {
273   putchar (c);
274 }
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 init_data (void)
373 {
374
375 }
376
377 /* Push C on the current data chunk.  */
378 static void
379 push_data (int c)
380 {
381   (void)c;
382 }
383
384 /* Flush and thus print the current data chunk.  */
385 static void
386 flush_data (void)
387 {
388
389 }
390
391
392 /* Returns 0 on success.  */
393 static int
394 parse_and_print (FILE *fp)
395 {
396   static const char tokenchars[] =
397     "abcdefghijklmnopqrstuvwxyz"
398     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
399     "0123456789-./_:*+=";
400   int c;
401   int level = 0;
402   int tokenc = 0;
403   int hexcount = 0;
404   int disphint = 0;
405   unsigned long datalen = 0;
406   char quote_buf[10];
407   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   state = INIT_STATE;
415
416
417   while ((c = my_getc (fp)) != EOF )
418     {
419       addrawdata (c);
420       switch (state)
421         {
422         case INIT_STATE:
423           if (tokenc)
424             {
425               if (strchr (tokenchars, c))
426                 {
427                   printchr (c);
428                   continue;
429                 }
430               tokenc = 0;
431             }
432         parse_init_state:
433           if (c == '(')
434             {
435               if (disphint)
436                 {
437                   printerr ("unmatched display hint");
438                   disphint = 0;
439                 }
440               printctl ("open");
441               level++;
442             }
443           else if (c == ')')
444             {
445               if (disphint)
446                 {
447                   printerr ("unmatched display hint");
448                   disphint = 0;
449                 }
450               printctl ("close");
451               level--;
452             }
453           else if (c == '\"')
454             {
455               state = IN_STRING;
456               printctl ("beginstring");
457               init_data ();
458             }
459           else if (c == '#')
460             {
461               state = IN_HEXFMT;
462               hexcount = 0;
463               printctl ("beginhex");
464               init_data ();
465             }
466           else if (c == '|')
467             {
468               state = IN_BASE64;
469               printctl ("beginbase64");
470               init_data ();
471             }
472           else if (c == '[')
473             {
474               if (disphint)
475                 printerr ("nested display hint");
476               disphint = c;
477             }
478           else if (c == ']')
479             {
480               if (!disphint)
481                 printerr ("no open display hint");
482               disphint = 0;
483             }
484           else if (c >= '0' && c <= '9')
485             {
486               if (c == '0')
487                 printerr ("zero prefixed length");
488               state = IN_NUMBER;
489               datalen = (c - '0');
490             }
491           else if (strchr (tokenchars, c))
492             {
493               printchr (c);
494               tokenc = c;
495             }
496           else if (whitespace_p (c))
497             ;
498           else if (c == '{')
499             {
500               printerr ("rescanning is not supported");
501             }
502           else if (c == '&' || c == '\\')
503             {
504               printerr ("reserved punctuation detected");
505             }
506           else
507             {
508               printerr ("bad character detected");
509             }
510           break;
511
512         case IN_NUMBER:
513           if (digit_p (c))
514             {
515               unsigned long tmp = datalen * 10 + (c - '0');
516               if (tmp < datalen)
517                 {
518                   printerr ("overflow in data length");
519                   state = INIT_STATE;
520                   datalen = 0;
521                 }
522               else
523                 datalen = tmp;
524             }
525           else if (c == ':')
526             {
527               if (!datalen)
528                 {
529                   printerr ("no data length");
530                   state = INIT_STATE;
531                 }
532               else
533                 state = PRE_DATA;
534             }
535           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             printerr ("invalid length specification");
543           break;
544
545         case PRE_DATA:
546           state = IN_DATA;
547           printctl ("begindata");
548           init_data ();
549           /* fall through */
550         case IN_DATA:
551           if (datalen)
552             {
553               push_data (c);
554               datalen--;
555             }
556           if (!datalen)
557             {
558               state = INIT_STATE;
559               printctl ("enddata");
560               flush_data ();
561             }
562           break;
563
564         case IN_STRING:
565           if (c == '\"')
566             {
567               printctl ("endstring");
568               flush_data ();
569               state = INIT_STATE;
570             }
571           else if (c == '\\')
572             state = IN_ESCAPE;
573           else
574             push_data (c);
575           break;
576
577         case IN_ESCAPE:
578           switch (c)
579             {
580             case 'b':  push_data ('\b'); state = IN_STRING; break;
581             case 't':  push_data ('\t'); state = IN_STRING; break;
582             case 'v':  push_data ('\v'); state = IN_STRING; break;
583             case 'n':  push_data ('\n'); state = IN_STRING; break;
584             case 'f':  push_data ('\f'); state = IN_STRING; break;
585             case 'r':  push_data ('\r'); state = IN_STRING; break;
586             case '"':  push_data ('"');  state = IN_STRING; break;
587             case '\'': push_data ('\''); state = IN_STRING; break;
588             case '\\': push_data ('\\'); state = IN_STRING; break;
589
590             case '0': case '1': case '2': case '3': case '4':
591             case '5': case '6': case '7':
592               state = IN_OCT_ESC;
593               quote_idx = 0;
594               quote_buf[quote_idx++] = c;
595               break;
596
597             case 'x':
598               state = IN_HEX_ESC;
599               quote_idx = 0;
600               break;
601
602             case '\r':
603               state = CR_ESC;
604               break;
605
606             case '\n':
607               state = LF_ESC;
608               break;
609
610             default:
611               printerr ("invalid escape sequence");
612               state = IN_STRING;
613               break;
614             }
615           break;
616
617         case IN_OCT_ESC:
618           if (quote_idx < 3 && strchr ("01234567", c))
619             {
620               quote_buf[quote_idx++] = c;
621               if (quote_idx == 3)
622                 {
623                   push_data ((unsigned int)quote_buf[0] * 8 * 8
624                              + (unsigned int)quote_buf[1] * 8
625                              + (unsigned int)quote_buf[2]);
626                   state = IN_STRING;
627                 }
628             }
629           else
630             state = IN_STRING;
631           break;
632         case IN_HEX_ESC:
633           if (quote_idx < 2 && strchr ("0123456789abcdefABCDEF", c))
634             {
635               quote_buf[quote_idx++] = c;
636               if (quote_idx == 2)
637                 {
638                   push_data (xtoi_1 (quote_buf[0]) * 16
639                              + xtoi_1 (quote_buf[1]));
640                   state = IN_STRING;
641                 }
642             }
643           else
644             state = IN_STRING;
645           break;
646         case CR_ESC:
647           state = IN_STRING;
648           break;
649         case LF_ESC:
650           state = IN_STRING;
651           break;
652
653         case IN_HEXFMT:
654           if (hexdigit_p (c))
655             {
656               push_data (c);
657               hexcount++;
658             }
659           else if (c == '#')
660             {
661               if ((hexcount & 1))
662                 printerr ("odd number of hex digits");
663               printctl ("endhex");
664               flush_data ();
665               state = INIT_STATE;
666             }
667           else if (!whitespace_p (c))
668             printerr ("bad hex character");
669           break;
670
671         case IN_BASE64:
672           if (c == '|')
673             {
674               printctl ("endbase64");
675               flush_data ();
676               state = INIT_STATE;
677             }
678           else
679             push_data (c);
680           break;
681
682         default:
683           logit ("invalid state %d detected", state);
684           exit (1);
685         }
686     }
687   flushdatabuffer ();
688   if (ferror (fp))
689     {
690       logit ("error reading input: %s\n", strerror (errno));
691       return -1;
692     }
693   return 0;
694 }
695
696
697
698 int
699 main (int argc, char **argv)
700 {
701   int rc;
702
703   if (argc)
704     {
705       argc--; argv++;
706     }
707   while (argc && **argv == '-' && (*argv)[1] == '-')
708     {
709       if (!(*argv)[2])
710         {
711           argc--; argv++;
712           break;
713         }
714       else if (!strcmp (*argv, "--version"))
715         print_version (0);
716       else if (!strcmp (*argv, "--help"))
717         print_version (1);
718       else if (!strcmp (*argv, "--verbose"))
719         {
720           argc--; argv++;
721           verbose = 1;
722         }
723       else if (!strcmp (*argv, "--decimal"))
724         {
725           argc--; argv++;
726           decimal = 1;
727         }
728       else if (!strcmp (*argv, "--assume-hex"))
729         {
730           argc--; argv++;
731           assume_hex = 1;
732         }
733       else if (!strcmp (*argv, "--advanced"))
734         {
735           argc--; argv++;
736           advanced = 1;
737         }
738       else
739         print_usage ();
740     }
741
742   if (!argc)
743     {
744       rc = parse_and_print (stdin);
745     }
746   else
747     {
748       rc = 0;
749       for (; argc; argv++, argc--)
750         {
751           FILE *fp = fopen (*argv, "rb");
752           if (!fp)
753             {
754               logit ("can't open `%s': %s\n", *argv, strerror (errno));
755               rc = 1;
756             }
757           else
758             {
759               if (parse_and_print (fp))
760                 rc = 1;
761               fclose (fp);
762             }
763         }
764     }
765
766   return !!rc;
767 }