Use extra counter to check random pool filling.
[libgcrypt.git] / src / dumpsexp.c
1 /* dumpsexp.c - Dump S-expressions.
2  * Copyright (C) 2007 Free Software Foundation, Inc.
3  *
4  * Getrandom 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 2 of the License,
7  * or (at your option) any later version.
8  *
9  * Getrandom 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, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <errno.h>
27
28 #define PGM "dumpsexp"
29 #define MYVERSION_LINE PGM " (Libgcrypt) " VERSION
30 #define BUGREPORT_LINE "\nReport bugs to <bug-libgcrypt@gnupg.org>.\n"
31
32
33 static int verbose;  /* Verbose mode.  */
34 static int decimal;  /* Print addresses in decimal.  */
35 static int assume_hex;  /* Assume input is hexencoded.  */
36
37 static void
38 print_version (int with_help)
39 {
40   fputs (MYVERSION_LINE "\n"
41          "Copyright (C) 2007 Free Software Foundation, Inc.\n"
42          "License GPLv2+: GNU GPL version 2 or later "
43          "<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>\n"
44          "This is free software: you are free to change and redistribute it.\n"
45          "There is NO WARRANTY, to the extent permitted by law.\n",
46          stdout);
47         
48   if (with_help)
49     fputs ("\n"
50            "Usage: " PGM " [OPTIONS] [file]\n"
51            "Debug tool for S-expressions\n"
52            "\n"
53            "  --decimal     Print offsetc using decimal notation\n"
54            "  --assume-hex  Assume input is a hex dump\n"
55            "  --verbose     Show what we are doing\n"
56            "  --version     Print version of the program and exit\n"
57            "  --help        Display this help and exit\n"
58            BUGREPORT_LINE, stdout );
59   
60   exit (0);
61 }
62
63 static int
64 print_usage (void)
65 {
66   fputs ("usage: " PGM " [OPTIONS] NBYTES\n", stderr);
67   fputs ("       (use --help to display options)\n", stderr);
68   exit (1);
69 }
70
71
72 #define space_p(a)    ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
73 #define digit_p(a)    ((a) >= '0' && (a) <= '9')
74 #define octdigit_p(a) ((a) >= '0' && (a) <= '7')
75 #define alpha_p(a)    (   ((a) >= 'A' && (a) <= 'Z')  \
76                        || ((a) >= 'a' && (a) <= 'z'))
77 #define hexdigit_p(a) (digit_p (a)                     \
78                        || ((a) >= 'A' && (a) <= 'F')  \
79                        || ((a) >= 'a' && (a) <= 'f'))
80 #define xtoi_1(a)     ((a) <= '9'? ((a)- '0'): \
81                        (a) <= 'F'? ((a)-'A'+10):((a)-'a'+10))
82
83
84 /* Return true if P points to a byte containing a whitespace according
85    to the S-expressions definition. */
86 static inline int
87 whitespace_p (int c)
88
89   switch (c)
90     {
91     case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1;
92     default: return 0;
93     }
94 }
95
96 static void
97 logit (const char *format, ...)
98 {
99   va_list arg_ptr;
100
101   va_start (arg_ptr, format) ;
102   fputs (PGM ": ", stderr);
103   vfprintf (stderr, format, arg_ptr);
104   putc ('\n', stderr);
105   va_end (arg_ptr);
106 }
107
108 /* The raw data buffer and its current length */
109 static unsigned char databuffer[16];
110 static int databufferlen;
111 /* The number of bytes in databuffer which should be skipped at a flush.  */
112 static int skipdatabufferlen;
113 /* The number of raw bytes printed on the last line.  */
114 static int nbytesprinted;
115 /* The file offset of the current data buffer .  */
116 static unsigned long databufferoffset;
117
118
119
120 static int
121 my_getc (FILE *fp)
122 {
123   int c1, c2;
124
125   if (!assume_hex)
126     return getc (fp);
127
128   while ( (c1=getc (fp)) != EOF && space_p (c1) )
129     ;
130   if (c1 == EOF)
131     return EOF;
132
133   if (!hexdigit_p (c1))
134     {
135       logit ("non hex-digit encountered\n");
136       return EOF;
137     }
138
139   while ( (c2=getc (fp)) != EOF && space_p (c2) )
140     ;
141   if (c2 == EOF)
142     {
143       logit ("error reading second hex nibble\n");
144       return EOF;
145     }
146   if (!hexdigit_p (c2))
147     {
148       logit ("second hex nibble is not a hex-digit\n");
149       return EOF;
150     }
151   return xtoi_1 (c1) * 16 + xtoi_1 (c2);
152 }
153
154
155
156
157
158 /* Flush the raw data buffer.  */
159 static void
160 flushdatabuffer (void)
161 {
162   int i;
163
164   if (!databufferlen)
165     return;
166   nbytesprinted = 0;
167   if (decimal)
168     printf ("%08lu ", databufferoffset);
169   else
170     printf ("%08lx ", databufferoffset);
171   for (i=0; i < databufferlen; i++)
172     {
173       if (i == 8)
174         putchar (' ');
175       if (i < skipdatabufferlen)
176         fputs ("   ", stdout);
177       else
178         {
179           printf (" %02x", databuffer[i]);
180           databufferoffset++;
181         }
182       nbytesprinted++;
183     }
184   for (; i < sizeof (databuffer); i++)
185     {
186       if (i == 8)
187         putchar (' ');
188       fputs ("   ", stdout);
189     }
190   fputs ("  |", stdout);
191   for (i=0; i < databufferlen; i++)
192     {
193       if (i < skipdatabufferlen)
194         putchar (' ');
195       else if (databuffer[i] >= ' ' && databuffer[i] <= '~'
196                && databuffer[i] != '|')
197         putchar (databuffer[i]);
198       else
199         putchar ('.');
200     }
201   putchar ('|');
202   putchar ('\n');
203   databufferlen = 0;
204   skipdatabufferlen = 0;
205 }
206
207
208 /* Add C to the raw data buffer and flush as needed.  */
209 static void
210 addrawdata (int c)
211 {
212   if ( databufferlen >= sizeof databuffer )
213     flushdatabuffer ();
214   databuffer[databufferlen++] = c;
215 }
216
217
218 static void 
219 printcursor (int both)
220 {
221   int i;
222  
223   flushdatabuffer ();
224   printf ("%8s ", "");
225   for (i=0; i < sizeof (databuffer); i++)
226     {
227       if (i == 8)
228         putchar (' ');
229       if (i+1 == nbytesprinted)
230         {
231           fputs (" ^ ", stdout);
232           if (!both)
233             break;
234         }
235       else
236         fputs ("   ", stdout);
237     }
238   if (both)
239     {
240       fputs ("   ", stdout);
241       for (i=0; i < nbytesprinted-1; i++)
242         putchar (' ');
243       putchar ('^');
244     }
245   databufferlen = skipdatabufferlen = nbytesprinted;
246 }
247
248 static void 
249 printerr (const char *text)
250 {
251   printcursor (1);
252   printf ("\n          Error: %s\n", text);
253 }
254
255 static void 
256 printctl (const char *text)
257 {
258   if (verbose)
259     {
260       printcursor (0);
261       printf ("%s\n", text);
262     }
263 }
264
265 static void 
266 printchr (int c)
267 {
268 }
269
270 static void
271 printhex (int c)
272 {
273 }
274
275
276
277
278
279
280 static int
281 parse_and_print (FILE *fp)
282 {
283   static const char tokenchars[] =
284     "abcdefghijklmnopqrstuvwxyz"
285     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
286     "0123456789-./_:*+=";
287   int c;
288   int level = 0;
289   int tokenc = 0;
290   int hexcount = 0;
291   int disphint = 0;
292   unsigned long datalen = 0;
293   char quote_buf[10];
294   int quote_idx = 0;
295   enum 
296     {
297       INIT_STATE = 0, IN_NUMBER, PRE_DATA, IN_DATA, IN_STRING,
298       IN_ESCAPE, IN_OCT_ESC, IN_HEX_ESC,
299       CR_ESC, LF_ESC, IN_HEXFMT, IN_BASE64
300     }
301   state = INIT_STATE;
302   
303
304   while ((c = my_getc (fp)) != EOF )
305     {
306       addrawdata (c);
307       switch (state)
308         {
309         case INIT_STATE:
310           if (tokenc)
311             {
312               if (strchr (tokenchars, c))
313                 {
314                   printchr (c);
315                   continue;
316                 }
317               tokenc = 0;
318             }
319         parse_init_state:
320           if (c == '(')
321             {
322               if (disphint)
323                 {
324                   printerr ("unmatched display hint");
325                   disphint = 0;
326                 }
327               printctl ("open");
328               level++;
329             }
330           else if (c == ')')
331             {
332               if (disphint)
333                 {
334                   printerr ("unmatched display hint");
335                   disphint = 0;
336                 }
337               printctl ("close");
338               level--;
339             }
340           else if (c == '\"')
341             {
342               state = IN_STRING;
343               printctl ("beginstring");
344             }
345           else if (c == '#')
346             {
347               state = IN_HEXFMT;
348               hexcount = 0;
349               printctl ("beginhex");
350             }
351           else if (c == '|')
352             {
353               state = IN_BASE64;
354               printctl ("beginbase64");
355             }
356           else if (c == '[')
357             {
358               if (disphint)
359                 printerr ("nested display hint");
360               disphint = c;
361             }
362           else if (c == ']')
363             {
364               if (!disphint)
365                 printerr ("no open display hint");
366               disphint = 0;
367             }
368           else if (c >= '0' && c <= '9')
369             {
370               if (c == '0')
371                 printerr ("zero prefixed length");
372               state = IN_NUMBER;
373               datalen = (c - '0');
374             }
375           else if (strchr (tokenchars, c))
376             {
377               printchr (c);
378               tokenc = c;
379             }
380           else if (whitespace_p (c))
381             ;
382           else if (c == '{')
383             {
384               printerr ("rescanning is not supported");
385             }
386           else if (c == '&' || c == '\\')
387             {
388               printerr ("reserved punctuation detected");
389             }
390           else
391             {
392               printerr ("bad character detected");
393             }
394           break;
395
396         case IN_NUMBER:
397           if (digit_p (c))
398             {
399               unsigned long tmp = datalen * 10 + (c - '0');
400               if (tmp < datalen)
401                 {
402                   printerr ("overflow in data length");
403                   state = INIT_STATE;
404                   datalen = 0;
405                 }
406               else
407                 datalen = tmp;
408             }
409           else if (c == ':')
410             {
411               if (!datalen)
412                 {
413                   printerr ("no data length");
414                   state = INIT_STATE;
415                 }
416               else
417                 state = PRE_DATA;
418             }
419           else if (c == '\"' || c == '#' || c == '|' )
420             {
421               /* We ignore the optional length and divert to the init
422                  state parser code. */
423               goto parse_init_state;
424             }
425           else
426             printerr ("invalid length specification");
427           break;
428
429         case PRE_DATA:
430           state = IN_DATA;
431           printctl ("begindata");
432         case IN_DATA:
433           if (datalen)
434             {
435               printhex (c);
436               datalen--;
437             }
438           if (!datalen)
439             {
440               state = INIT_STATE;
441               printctl ("enddata");
442             }
443           break;
444
445         case IN_STRING:
446           if (c == '\"')
447             {
448               printctl ("endstring");
449               state = INIT_STATE;
450             } 
451           else if (c == '\\')
452             state = IN_ESCAPE;
453           else
454             printchr (c);
455           break;
456
457         case IN_ESCAPE:
458           switch (c)
459             {
460             case 'b': case 't': case 'v': case 'n': case 'f':
461             case 'r': case '"': case '\'': case '\\':
462               printhex (c);
463               state = IN_STRING;
464               break;
465               
466             case '0': case '1': case '2': case '3': case '4':
467             case '5': case '6': case '7':
468               state = IN_OCT_ESC;
469               quote_idx = 0;
470               quote_buf[quote_idx++] = c; 
471               break;
472               
473             case 'x':
474               state = IN_HEX_ESC;
475               quote_idx = 0;
476               break;
477               
478             case '\r':
479               state = CR_ESC;
480               break;
481               
482             case '\n':
483               state = LF_ESC;
484               break;
485
486             default:
487               printerr ("invalid escape sequence");
488               state = IN_STRING;
489               break;
490             }
491           
492         case IN_OCT_ESC: 
493           state = IN_STRING;
494           break;
495         case IN_HEX_ESC: 
496           state = IN_STRING;
497           break;
498         case CR_ESC:
499           state = IN_STRING;
500           break;
501         case LF_ESC:
502           state = IN_STRING;
503           break;
504
505         case IN_HEXFMT:
506           if (hexdigit_p (c))
507             {
508               printchr (c);
509               hexcount++;
510             }
511           else if (c == '#')
512             {
513               if ((hexcount & 1))
514                 printerr ("odd number of hex digits");
515               printctl ("endhex");
516               state = INIT_STATE;
517             }
518           else if (!whitespace_p (c))
519             printerr ("bad hex character");
520           break;
521
522         case IN_BASE64:
523           if (c == '|')
524             {
525               printctl ("endbase64");
526               state = INIT_STATE;
527             }
528           else
529             printchr (c);
530           break;
531
532         default:
533           logit ("invalid state %d detected", state);
534           exit (1);
535         }
536     }
537   flushdatabuffer ();
538   if (ferror (fp))
539     {
540       logit ("error reading input: %s\n", strerror (errno));
541       return -1;
542     }
543   return 0;
544 }
545
546
547
548 int 
549 main (int argc, char **argv)
550 {
551   int rc;
552
553   if (argc)
554     {
555       argc--; argv++;
556     }
557   while (argc && **argv == '-' && (*argv)[1] == '-')
558     {
559       if (!(*argv)[2])
560         {
561           argc--; argv++;
562           break;
563         }
564       else if (!strcmp (*argv, "--version"))
565         print_version (0);
566       else if (!strcmp (*argv, "--help"))
567         print_version (1);
568       else if (!strcmp (*argv, "--verbose"))
569         {
570           argc--; argv++;
571           verbose = 1;
572         }
573       else if (!strcmp (*argv, "--decimal"))
574         {
575           argc--; argv++;
576           decimal = 1;
577         }
578       else if (!strcmp (*argv, "--assume-hex"))
579         {
580           argc--; argv++;
581           assume_hex = 1;
582         }
583       else
584         print_usage ();
585     }          
586
587   if (!argc)
588     {
589       rc = parse_and_print (stdin);
590     }
591   else
592     {
593       for (; argc; argc--)
594         {
595           FILE *fp = fopen (*argv, "rb");
596           if (!fp)
597             {
598               logit ("can't open `%s': %s\n", *argv, strerror (errno));
599               rc = 1;
600             }
601           else 
602             {
603               if ( parse_and_print (fp) )
604                 rc = 1;
605               fclose (fp);
606             }
607         }
608     }
609   
610
611   return !rc;
612 }
613