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 36static int verbose; /* Verbose mode. */ 37static int decimal; /* Print addresses in decimal. */ 38static int assume_hex; /* Assume input is hexencoded. */ 39static int advanced; /* Advanced format output. */ 40 41static void 42print_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 68static int 69print_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. */ 91static inline int 92whitespace_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 101static void 102logit (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 */ 114static unsigned char databuffer[16]; 115static int databufferlen; 116/* The number of bytes in databuffer which should be skipped at a flush. */ 117static int skipdatabufferlen; 118/* The number of raw bytes printed on the last line. */ 119static int nbytesprinted; 120/* The file offset of the current data buffer . */ 121static unsigned long databufferoffset; 122 123 124 125static int 126my_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. */ 164static void 165flushdatabuffer (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. */ 214static void 215addrawdata (int c) 216{ 217 if ( databufferlen >= sizeof databuffer ) 218 flushdatabuffer (); 219 databuffer[databufferlen++] = c; 220} 221 222 223static void 224printcursor (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 253static void 254printerr (const char *text) 255{ 256 printcursor (1); 257 printf ("\n Error: %s\n", text); 258} 259 260static void 261printctl (const char *text) 262{ 263 if (verbose && !advanced) 264 { 265 printcursor (0); 266 printf ("%s\n", text); 267 } 268} 269 270static void 271printchr (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 */ 290size_t 291gcry_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. */ 371static void 372init_data (void) 373{ 374 375} 376 377/* Push C on the current data chunk. */ 378static void 379push_data (int c) 380{ 381 (void)c; 382} 383 384/* Flush and thus print the current data chunk. */ 385static void 386flush_data (void) 387{ 388 389} 390 391 392/* Returns 0 on success. */ 393static int 394parse_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 case IN_DATA: 550 if (datalen) 551 { 552 push_data (c); 553 datalen--; 554 } 555 if (!datalen) 556 { 557 state = INIT_STATE; 558 printctl ("enddata"); 559 flush_data (); 560 } 561 break; 562 563 case IN_STRING: 564 if (c == '\"') 565 { 566 printctl ("endstring"); 567 flush_data (); 568 state = INIT_STATE; 569 } 570 else if (c == '\\') 571 state = IN_ESCAPE; 572 else 573 push_data (c); 574 break; 575 576 case IN_ESCAPE: 577 switch (c) 578 { 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; 588 589 case '0': case '1': case '2': case '3': case '4': 590 case '5': case '6': case '7': 591 state = IN_OCT_ESC; 592 quote_idx = 0; 593 quote_buf[quote_idx++] = c; 594 break; 595 596 case 'x': 597 state = IN_HEX_ESC; 598 quote_idx = 0; 599 break; 600 601 case '\r': 602 state = CR_ESC; 603 break; 604 605 case '\n': 606 state = LF_ESC; 607 break; 608 609 default: 610 printerr ("invalid escape sequence"); 611 state = IN_STRING; 612 break; 613 } 614 break; 615 616 case IN_OCT_ESC: 617 if (quote_idx < 3 && strchr ("01234567", c)) 618 { 619 quote_buf[quote_idx++] = c; 620 if (quote_idx == 3) 621 { 622 push_data ((unsigned int)quote_buf[0] * 8 * 8 623 + (unsigned int)quote_buf[1] * 8 624 + (unsigned int)quote_buf[2]); 625 state = IN_STRING; 626 } 627 } 628 else 629 state = IN_STRING; 630 break; 631 case IN_HEX_ESC: 632 if (quote_idx < 2 && strchr ("0123456789abcdefABCDEF", c)) 633 { 634 quote_buf[quote_idx++] = c; 635 if (quote_idx == 2) 636 { 637 push_data (xtoi_1 (quote_buf[0]) * 16 638 + xtoi_1 (quote_buf[1])); 639 state = IN_STRING; 640 } 641 } 642 else 643 state = IN_STRING; 644 break; 645 case CR_ESC: 646 state = IN_STRING; 647 break; 648 case LF_ESC: 649 state = IN_STRING; 650 break; 651 652 case IN_HEXFMT: 653 if (hexdigit_p (c)) 654 { 655 push_data (c); 656 hexcount++; 657 } 658 else if (c == '#') 659 { 660 if ((hexcount & 1)) 661 printerr ("odd number of hex digits"); 662 printctl ("endhex"); 663 flush_data (); 664 state = INIT_STATE; 665 } 666 else if (!whitespace_p (c)) 667 printerr ("bad hex character"); 668 break; 669 670 case IN_BASE64: 671 if (c == '|') 672 { 673 printctl ("endbase64"); 674 flush_data (); 675 state = INIT_STATE; 676 } 677 else 678 push_data (c); 679 break; 680 681 default: 682 logit ("invalid state %d detected", state); 683 exit (1); 684 } 685 } 686 flushdatabuffer (); 687 if (ferror (fp)) 688 { 689 logit ("error reading input: %s\n", strerror (errno)); 690 return -1; 691 } 692 return 0; 693} 694 695 696 697int 698main (int argc, char **argv) 699{ 700 int rc; 701 702 if (argc) 703 { 704 argc--; argv++; 705 } 706 while (argc && **argv == '-' && (*argv)[1] == '-') 707 { 708 if (!(*argv)[2]) 709 { 710 argc--; argv++; 711 break; 712 } 713 else if (!strcmp (*argv, "--version")) 714 print_version (0); 715 else if (!strcmp (*argv, "--help")) 716 print_version (1); 717 else if (!strcmp (*argv, "--verbose")) 718 { 719 argc--; argv++; 720 verbose = 1; 721 } 722 else if (!strcmp (*argv, "--decimal")) 723 { 724 argc--; argv++; 725 decimal = 1; 726 } 727 else if (!strcmp (*argv, "--assume-hex")) 728 { 729 argc--; argv++; 730 assume_hex = 1; 731 } 732 else if (!strcmp (*argv, "--advanced")) 733 { 734 argc--; argv++; 735 advanced = 1; 736 } 737 else 738 print_usage (); 739 } 740 741 if (!argc) 742 { 743 rc = parse_and_print (stdin); 744 } 745 else 746 { 747 rc = 0; 748 for (; argc; argv++, argc--) 749 { 750 FILE *fp = fopen (*argv, "rb"); 751 if (!fp) 752 { 753 logit ("can't open `%s': %s\n", *argv, strerror (errno)); 754 rc = 1; 755 } 756 else 757 { 758 if (parse_and_print (fp)) 759 rc = 1; 760 fclose (fp); 761 } 762 } 763 } 764 765 return !!rc; 766} 767