1 /* chew 2 Copyright (C) 1990-2024 Free Software Foundation, Inc. 3 Contributed by steve chamberlain @cygnus 4 5 This file is part of BFD, the Binary File Descriptor library. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 20 MA 02110-1301, USA. */ 21 22 /* Yet another way of extracting documentation from source. 23 No, I haven't finished it yet, but I hope you people like it better 24 than the old way 25 26 sac 27 28 Basically, this is a sort of string forth, maybe we should call it 29 struth? 30 31 You define new words thus: 32 : <newword> <oldwords> ; 33 34 Variables are defined using: 35 variable NAME 36 37 */ 38 39 /* Primitives provided by the program: 40 41 Two stacks are provided, a string stack and an integer stack. 42 43 Internal state variables: 44 internal_wanted - indicates whether `-i' was passed 45 internal_mode - user-settable 46 47 Commands: 48 push_text 49 ! - pop top of integer stack for address, pop next for value; store 50 @ - treat value on integer stack as the address of an integer; push 51 that integer on the integer stack after popping the "address" 52 hello - print "hello\n" to stdout 53 stdout - put stdout marker on TOS 54 stderr - put stderr marker on TOS 55 print - print TOS-1 on TOS (eg: "hello\n" stdout print) 56 skip_past_newline 57 catstr - fn icatstr 58 copy_past_newline - append input, up to and including newline into TOS 59 dup - fn other_dup 60 drop - discard TOS 61 idrop - ditto 62 remchar - delete last character from TOS 63 get_stuff_in_command 64 do_fancy_stuff - translate <<foo>> to @code{foo} in TOS 65 bulletize - if "o" lines found, prepend @itemize @bullet to TOS 66 and @item to each "o" line; append @end itemize 67 courierize - put @example around . and | lines, translate {* *} { } 68 exit - fn chew_exit 69 swap 70 outputdots - strip out lines without leading dots 71 maybecatstr - do catstr if internal_mode == internal_wanted, discard 72 value in any case 73 catstrif - do catstr if top of integer stack is nonzero 74 translatecomments - turn {* and *} into comment delimiters 75 kill_bogus_lines - get rid of extra newlines 76 indent 77 print_stack_level - print current stack depth to stderr 78 strip_trailing_newlines - go ahead, guess... 79 [quoted string] - push string onto string stack 80 [word starting with digit] - push atol(str) onto integer stack 81 82 internalmode - the internalmode variable (evaluates to address) 83 84 A command must be all upper-case, and alone on a line. 85 86 Foo. */ 87 88 #include <assert.h> 89 #include <stdio.h> 90 #include <ctype.h> 91 #include <stdlib.h> 92 #include <string.h> 93 #include <stdint.h> 94 #include <inttypes.h> 95 96 #define DEF_SIZE 5000 97 #define STACK 50 98 99 /* Here is a string type ... */ 100 101 typedef struct buffer 102 { 103 char *ptr; 104 unsigned long write_idx; 105 unsigned long size; 106 } string_type; 107 108 /* Compiled programs consist of arrays of these. */ 109 110 typedef union 111 { 112 void (*f) (void); 113 struct dict_struct *e; 114 char *s; 115 intptr_t l; 116 } pcu; 117 118 typedef struct dict_struct 119 { 120 char *word; 121 struct dict_struct *next; 122 pcu *code; 123 int code_length; 124 int code_end; 125 } dict_type; 126 127 int internal_wanted; 128 intptr_t *internal_mode; 129 130 int warning; 131 132 string_type stack[STACK]; 133 string_type *tos; 134 135 unsigned int idx = 0; /* Pos in input buffer */ 136 string_type *ptr; /* and the buffer */ 137 138 intptr_t istack[STACK]; 139 intptr_t *isp = &istack[0]; 140 141 dict_type *root; 142 143 pcu *pc; 144 145 static void 146 die (char *msg) 147 { 148 fprintf (stderr, "%s\n", msg); 149 exit (1); 150 } 151 152 void * 153 xmalloc (size_t size) 154 { 155 void *newmem; 156 157 if (size == 0) 158 size = 1; 159 newmem = malloc (size); 160 if (!newmem) 161 die ("out of memory"); 162 163 return newmem; 164 } 165 166 void * 167 xrealloc (void *oldmem, size_t size) 168 { 169 void *newmem; 170 171 if (size == 0) 172 size = 1; 173 if (!oldmem) 174 newmem = malloc (size); 175 else 176 newmem = realloc (oldmem, size); 177 if (!newmem) 178 die ("out of memory"); 179 180 return newmem; 181 } 182 183 char * 184 xstrdup (const char *s) 185 { 186 size_t len = strlen (s) + 1; 187 char *ret = xmalloc (len); 188 return memcpy (ret, s, len); 189 } 190 191 static void 192 init_string_with_size (string_type *buffer, unsigned int size) 193 { 194 buffer->write_idx = 0; 195 buffer->size = size; 196 buffer->ptr = xmalloc (size); 197 } 198 199 static void 200 init_string (string_type *buffer) 201 { 202 init_string_with_size (buffer, DEF_SIZE); 203 } 204 205 static int 206 find (string_type *str, char *what) 207 { 208 unsigned int i; 209 char *p; 210 p = what; 211 for (i = 0; i < str->write_idx && *p; i++) 212 { 213 if (*p == str->ptr[i]) 214 p++; 215 else 216 p = what; 217 } 218 return (*p == 0); 219 } 220 221 static void 222 write_buffer (string_type *buffer, FILE *f) 223 { 224 if (buffer->write_idx != 0 225 && fwrite (buffer->ptr, buffer->write_idx, 1, f) != 1) 226 die ("cannot write output"); 227 } 228 229 static void 230 delete_string (string_type *buffer) 231 { 232 free (buffer->ptr); 233 buffer->ptr = NULL; 234 } 235 236 static char * 237 addr (string_type *buffer, unsigned int idx) 238 { 239 return buffer->ptr + idx; 240 } 241 242 static char 243 at (string_type *buffer, unsigned int pos) 244 { 245 if (pos >= buffer->write_idx) 246 return 0; 247 return buffer->ptr[pos]; 248 } 249 250 static void 251 catchar (string_type *buffer, int ch) 252 { 253 if (buffer->write_idx == buffer->size) 254 { 255 buffer->size *= 2; 256 buffer->ptr = xrealloc (buffer->ptr, buffer->size); 257 } 258 259 buffer->ptr[buffer->write_idx++] = ch; 260 } 261 262 static void 263 overwrite_string (string_type *dst, string_type *src) 264 { 265 free (dst->ptr); 266 dst->size = src->size; 267 dst->write_idx = src->write_idx; 268 dst->ptr = src->ptr; 269 } 270 271 static void 272 catbuf (string_type *buffer, char *buf, unsigned int len) 273 { 274 if (buffer->write_idx + len >= buffer->size) 275 { 276 while (buffer->write_idx + len >= buffer->size) 277 buffer->size *= 2; 278 buffer->ptr = xrealloc (buffer->ptr, buffer->size); 279 } 280 memcpy (buffer->ptr + buffer->write_idx, buf, len); 281 buffer->write_idx += len; 282 } 283 284 static void 285 cattext (string_type *buffer, char *string) 286 { 287 catbuf (buffer, string, (unsigned int) strlen (string)); 288 } 289 290 static void 291 catstr (string_type *dst, string_type *src) 292 { 293 catbuf (dst, src->ptr, src->write_idx); 294 } 295 296 static unsigned int 297 skip_white_and_stars (string_type *src, unsigned int idx) 298 { 299 char c; 300 while ((c = at (src, idx)), 301 isspace ((unsigned char) c) 302 || (c == '*' 303 /* Don't skip past end-of-comment or star as first 304 character on its line. */ 305 && at (src, idx +1) != '/' 306 && at (src, idx -1) != '\n')) 307 idx++; 308 return idx; 309 } 310 311 static unsigned int 312 skip_past_newline_1 (string_type *ptr, unsigned int idx) 313 { 314 while (at (ptr, idx) 315 && at (ptr, idx) != '\n') 316 idx++; 317 if (at (ptr, idx) == '\n') 318 return idx + 1; 319 return idx; 320 } 321 322 static void 323 check_range (void) 324 { 325 if (tos < stack) 326 die ("underflow in string stack"); 327 if (tos >= stack + STACK) 328 die ("overflow in string stack"); 329 } 330 331 static void 332 icheck_range (void) 333 { 334 if (isp < istack) 335 die ("underflow in integer stack"); 336 if (isp >= istack + STACK) 337 die ("overflow in integer stack"); 338 } 339 340 static void 341 drop (void) 342 { 343 tos--; 344 check_range (); 345 delete_string (tos + 1); 346 pc++; 347 } 348 349 static void 350 idrop (void) 351 { 352 isp--; 353 icheck_range (); 354 pc++; 355 } 356 357 static void 358 exec (dict_type *word) 359 { 360 pc = word->code; 361 while (pc->f) 362 pc->f (); 363 } 364 365 static void 366 call (void) 367 { 368 pcu *oldpc = pc; 369 dict_type *e = pc[1].e; 370 exec (e); 371 pc = oldpc + 2; 372 } 373 374 static void 375 remchar (void) 376 { 377 if (tos->write_idx) 378 tos->write_idx--; 379 pc++; 380 } 381 382 static void 383 strip_trailing_newlines (void) 384 { 385 while (tos->write_idx > 0 386 && (isspace ((unsigned char) at (tos, tos->write_idx - 1)) 387 || at (tos, tos->write_idx - 1) == '\n')) 388 tos->write_idx--; 389 pc++; 390 } 391 392 static void 393 push_number (void) 394 { 395 isp++; 396 icheck_range (); 397 pc++; 398 *isp = pc->l; 399 pc++; 400 } 401 402 /* This is a wrapper for push_number just so we can correctly free the 403 variable at the end. */ 404 static void 405 push_variable (void) 406 { 407 push_number (); 408 } 409 410 static void 411 push_text (void) 412 { 413 tos++; 414 check_range (); 415 init_string (tos); 416 pc++; 417 cattext (tos, pc->s); 418 pc++; 419 } 420 421 /* This function removes everything not inside comments starting on 422 the first char of the line from the string, also when copying 423 comments, removes blank space and leading *'s. 424 Blank lines are turned into one blank line. */ 425 426 static void 427 remove_noncomments (string_type *src, string_type *dst) 428 { 429 unsigned int idx = 0; 430 431 while (at (src, idx)) 432 { 433 /* Now see if we have a comment at the start of the line. */ 434 if (at (src, idx) == '\n' 435 && at (src, idx + 1) == '/' 436 && at (src, idx + 2) == '*') 437 { 438 idx += 3; 439 440 idx = skip_white_and_stars (src, idx); 441 442 /* Remove leading dot */ 443 if (at (src, idx) == '.') 444 idx++; 445 446 /* Copy to the end of the line, or till the end of the 447 comment. */ 448 while (at (src, idx)) 449 { 450 if (at (src, idx) == '\n') 451 { 452 /* end of line, echo and scrape of leading blanks */ 453 if (at (src, idx + 1) == '\n') 454 catchar (dst, '\n'); 455 catchar (dst, '\n'); 456 idx++; 457 idx = skip_white_and_stars (src, idx); 458 } 459 else if (at (src, idx) == '*' && at (src, idx + 1) == '/') 460 { 461 idx += 2; 462 cattext (dst, "\nENDDD\n"); 463 break; 464 } 465 else 466 { 467 catchar (dst, at (src, idx)); 468 idx++; 469 } 470 } 471 } 472 else 473 idx++; 474 } 475 } 476 477 static void 478 print_stack_level (void) 479 { 480 fprintf (stderr, "current string stack depth = %ld, ", 481 (long) (tos - stack)); 482 fprintf (stderr, "current integer stack depth = %ld\n", 483 (long) (isp - istack)); 484 pc++; 485 } 486 487 /* turn {* 488 and *} into comments */ 489 490 static void 491 translatecomments (void) 492 { 493 unsigned int idx = 0; 494 string_type out; 495 init_string (&out); 496 497 while (at (tos, idx)) 498 { 499 if (at (tos, idx) == '{' && at (tos, idx + 1) == '*') 500 { 501 cattext (&out, "/*"); 502 idx += 2; 503 } 504 else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}') 505 { 506 cattext (&out, "*/"); 507 idx += 2; 508 } 509 else 510 { 511 catchar (&out, at (tos, idx)); 512 idx++; 513 } 514 } 515 516 overwrite_string (tos, &out); 517 518 pc++; 519 } 520 521 /* Wrap tos-1 as a C comment, indenting by tos. */ 522 523 static void 524 wrap_comment (void) 525 { 526 string_type out; 527 init_string (&out); 528 529 catstr (&out, tos); 530 cattext (&out, "/* "); 531 for (unsigned int idx = 0; at (tos - 1, idx); idx++) 532 { 533 catchar (&out, at (tos - 1, idx)); 534 if (at (tos - 1, idx) == '\n' && at (tos - 1, idx + 1) != '\n') 535 { 536 catstr (&out, tos); 537 cattext (&out, " "); 538 } 539 } 540 cattext (&out, " */"); 541 542 overwrite_string (tos - 1, &out); 543 drop (); 544 545 pc++; 546 } 547 548 /* Mod tos so that only lines with leading dots remain */ 549 static void 550 outputdots (void) 551 { 552 unsigned int idx = 0; 553 string_type out; 554 init_string (&out); 555 556 while (at (tos, idx)) 557 { 558 /* Every iteration begins at the start of a line. */ 559 if (at (tos, idx) == '.') 560 { 561 char c; 562 int spaces; 563 564 idx++; 565 spaces = 0; 566 while ((c = at (tos, idx)) && c != '\n') 567 { 568 if (spaces >= 0) 569 { 570 if (c == ' ') 571 { 572 spaces++; 573 idx++; 574 continue; 575 } 576 else 577 { 578 while (spaces >= 8) 579 { 580 catchar (&out, '\t'); 581 spaces -= 8; 582 } 583 while (spaces-- > 0) 584 catchar (&out, ' '); 585 } 586 } 587 if (c == '{' && at (tos, idx + 1) == '*') 588 { 589 cattext (&out, "/*"); 590 idx += 2; 591 } 592 else if (c == '*' && at (tos, idx + 1) == '}') 593 { 594 cattext (&out, "*/"); 595 idx += 2; 596 } 597 else 598 { 599 catchar (&out, c); 600 idx++; 601 } 602 } 603 if (c == '\n') 604 idx++; 605 catchar (&out, '\n'); 606 } 607 else 608 { 609 idx = skip_past_newline_1 (tos, idx); 610 } 611 } 612 613 overwrite_string (tos, &out); 614 pc++; 615 } 616 617 /* Find lines starting with . and | and put example around them on tos */ 618 static void 619 courierize (void) 620 { 621 string_type out; 622 unsigned int idx = 0; 623 int command = 0; 624 625 init_string (&out); 626 627 while (at (tos, idx)) 628 { 629 if (at (tos, idx) == '\n' 630 && (at (tos, idx +1 ) == '.' 631 || at (tos, idx + 1) == '|')) 632 { 633 cattext (&out, "\n@example\n"); 634 do 635 { 636 idx += 2; 637 638 while (at (tos, idx) && at (tos, idx) != '\n') 639 { 640 if (command > 1) 641 { 642 /* We are inside {} parameters of some command; 643 Just pass through until matching brace. */ 644 if (at (tos, idx) == '{') 645 ++command; 646 else if (at (tos, idx) == '}') 647 --command; 648 } 649 else if (command != 0) 650 { 651 if (at (tos, idx) == '{') 652 ++command; 653 else if (!islower ((unsigned char) at (tos, idx))) 654 --command; 655 } 656 else if (at (tos, idx) == '@' 657 && islower ((unsigned char) at (tos, idx + 1))) 658 { 659 ++command; 660 } 661 else if (at (tos, idx) == '{' && at (tos, idx + 1) == '*') 662 { 663 cattext (&out, "/*"); 664 idx += 2; 665 continue; 666 } 667 else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}') 668 { 669 cattext (&out, "*/"); 670 idx += 2; 671 continue; 672 } 673 else if (at (tos, idx) == '{' 674 || at (tos, idx) == '}') 675 { 676 catchar (&out, '@'); 677 } 678 679 catchar (&out, at (tos, idx)); 680 idx++; 681 } 682 catchar (&out, '\n'); 683 } 684 while (at (tos, idx) == '\n' 685 && ((at (tos, idx + 1) == '.') 686 || (at (tos, idx + 1) == '|'))) 687 ; 688 cattext (&out, "@end example"); 689 } 690 else 691 { 692 catchar (&out, at (tos, idx)); 693 idx++; 694 } 695 } 696 697 overwrite_string (tos, &out); 698 pc++; 699 } 700 701 /* Finds any lines starting with "o ", if there are any, then turns 702 on @itemize @bullet, and @items each of them. Then ends with @end 703 itemize, inplace at TOS*/ 704 705 static void 706 bulletize (void) 707 { 708 unsigned int idx = 0; 709 int on = 0; 710 string_type out; 711 init_string (&out); 712 713 while (at (tos, idx)) 714 { 715 if (at (tos, idx) == '@' 716 && at (tos, idx + 1) == '*') 717 { 718 cattext (&out, "*"); 719 idx += 2; 720 } 721 else if (at (tos, idx) == '\n' 722 && at (tos, idx + 1) == 'o' 723 && isspace ((unsigned char) at (tos, idx + 2))) 724 { 725 if (!on) 726 { 727 cattext (&out, "\n@itemize @bullet\n"); 728 on = 1; 729 730 } 731 cattext (&out, "\n@item\n"); 732 idx += 3; 733 } 734 else 735 { 736 catchar (&out, at (tos, idx)); 737 if (on && at (tos, idx) == '\n' 738 && at (tos, idx + 1) == '\n' 739 && at (tos, idx + 2) != 'o') 740 { 741 cattext (&out, "@end itemize"); 742 on = 0; 743 } 744 idx++; 745 746 } 747 } 748 if (on) 749 { 750 cattext (&out, "@end itemize\n"); 751 } 752 753 delete_string (tos); 754 *tos = out; 755 pc++; 756 } 757 758 /* Turn <<foo>> into @code{foo} in place at TOS*/ 759 760 static void 761 do_fancy_stuff (void) 762 { 763 unsigned int idx = 0; 764 string_type out; 765 init_string (&out); 766 while (at (tos, idx)) 767 { 768 if (at (tos, idx) == '<' 769 && at (tos, idx + 1) == '<' 770 && !isspace ((unsigned char) at (tos, idx + 2))) 771 { 772 /* This qualifies as a << startup. */ 773 idx += 2; 774 cattext (&out, "@code{"); 775 while (at (tos, idx) 776 && at (tos, idx) != '>' ) 777 { 778 catchar (&out, at (tos, idx)); 779 idx++; 780 781 } 782 cattext (&out, "}"); 783 idx += 2; 784 } 785 else 786 { 787 catchar (&out, at (tos, idx)); 788 idx++; 789 } 790 } 791 delete_string (tos); 792 *tos = out; 793 pc++; 794 795 } 796 797 /* A command is all upper case,and alone on a line. */ 798 799 static int 800 iscommand (string_type *ptr, unsigned int idx) 801 { 802 unsigned int len = 0; 803 while (at (ptr, idx)) 804 { 805 if (isupper ((unsigned char) at (ptr, idx)) 806 || at (ptr, idx) == ' ' || at (ptr, idx) == '_') 807 { 808 len++; 809 idx++; 810 } 811 else if (at (ptr, idx) == '\n') 812 { 813 if (len > 3) 814 return 1; 815 return 0; 816 } 817 else 818 return 0; 819 } 820 return 0; 821 } 822 823 static int 824 copy_past_newline (string_type *ptr, unsigned int idx, string_type *dst) 825 { 826 int column = 0; 827 828 while (at (ptr, idx) && at (ptr, idx) != '\n') 829 { 830 if (at (ptr, idx) == '\t') 831 { 832 /* Expand tabs. Neither makeinfo nor TeX can cope well with 833 them. */ 834 do 835 catchar (dst, ' '); 836 while (++column & 7); 837 } 838 else 839 { 840 catchar (dst, at (ptr, idx)); 841 column++; 842 } 843 idx++; 844 845 } 846 catchar (dst, at (ptr, idx)); 847 idx++; 848 return idx; 849 850 } 851 852 static void 853 icopy_past_newline (void) 854 { 855 tos++; 856 check_range (); 857 init_string (tos); 858 idx = copy_past_newline (ptr, idx, tos); 859 pc++; 860 } 861 862 static void 863 kill_bogus_lines (void) 864 { 865 int sl; 866 867 int idx = 0; 868 int c; 869 int dot = 0; 870 871 string_type out; 872 init_string (&out); 873 /* Drop leading nl. */ 874 while (at (tos, idx) == '\n') 875 { 876 idx++; 877 } 878 c = idx; 879 880 /* If the first char is a '.' prepend a newline so that it is 881 recognized properly later. */ 882 if (at (tos, idx) == '.') 883 catchar (&out, '\n'); 884 885 /* Find the last char. */ 886 while (at (tos, idx)) 887 { 888 idx++; 889 } 890 891 /* Find the last non white before the nl. */ 892 idx--; 893 894 while (idx && isspace ((unsigned char) at (tos, idx))) 895 idx--; 896 idx++; 897 898 /* Copy buffer upto last char, but blank lines before and after 899 dots don't count. */ 900 sl = 1; 901 902 while (c < idx) 903 { 904 if (at (tos, c) == '\n' 905 && at (tos, c + 1) == '\n' 906 && at (tos, c + 2) == '.') 907 { 908 /* Ignore two newlines before a dot. */ 909 c++; 910 } 911 else if (at (tos, c) == '.' && sl) 912 { 913 /* remember that this line started with a dot. */ 914 dot = 2; 915 } 916 else if (at (tos, c) == '\n' 917 && at (tos, c + 1) == '\n' 918 && dot) 919 { 920 c++; 921 /* Ignore two newlines when last line was dot. */ 922 } 923 924 catchar (&out, at (tos, c)); 925 if (at (tos, c) == '\n') 926 { 927 sl = 1; 928 929 if (dot == 2) 930 dot = 1; 931 else 932 dot = 0; 933 } 934 else 935 sl = 0; 936 937 c++; 938 939 } 940 941 /* Append nl. */ 942 catchar (&out, '\n'); 943 pc++; 944 delete_string (tos); 945 *tos = out; 946 947 } 948 949 static void 950 collapse_whitespace (void) 951 { 952 int last_was_ws = 0; 953 int idx; 954 955 string_type out; 956 init_string (&out); 957 958 for (idx = 0; at (tos, idx) != 0; ++idx) 959 { 960 char c = at (tos, idx); 961 if (isspace (c)) 962 { 963 if (!last_was_ws) 964 { 965 catchar (&out, ' '); 966 last_was_ws = 1; 967 } 968 } 969 else 970 { 971 catchar (&out, c); 972 last_was_ws = 0; 973 } 974 } 975 976 pc++; 977 delete_string (tos); 978 *tos = out; 979 } 980 981 /* indent 982 Take the string at the top of the stack, do some prettying. */ 983 984 static void 985 indent (void) 986 { 987 string_type out; 988 int tab = 0; 989 int idx = 0; 990 int ol = 0; 991 init_string (&out); 992 while (at (tos, idx)) 993 { 994 switch (at (tos, idx)) 995 { 996 case '\n': 997 catchar (&out, '\n'); 998 idx++; 999 if (tab && at (tos, idx)) 1000 { 1001 int i; 1002 for (i = 0; i < tab - 1; i += 2) 1003 catchar (&out, '\t'); 1004 if (i < tab) 1005 cattext (&out, " "); 1006 } 1007 ol = 0; 1008 break; 1009 case '(': 1010 if (ol == 0) 1011 { 1012 int i; 1013 for (i = 1; i < tab - 1; i += 2) 1014 catchar (&out, '\t'); 1015 if (i < tab) 1016 cattext (&out, " "); 1017 cattext (&out, " "); 1018 } 1019 tab++; 1020 idx++; 1021 catchar (&out, '('); 1022 ol = 1; 1023 break; 1024 case ')': 1025 tab--; 1026 catchar (&out, ')'); 1027 idx++; 1028 ol = 1; 1029 break; 1030 default: 1031 catchar (&out, at (tos, idx)); 1032 ol = 1; 1033 idx++; 1034 break; 1035 } 1036 } 1037 1038 pc++; 1039 delete_string (tos); 1040 *tos = out; 1041 1042 } 1043 1044 static void 1045 get_stuff_in_command (void) 1046 { 1047 tos++; 1048 check_range (); 1049 init_string (tos); 1050 1051 while (at (ptr, idx)) 1052 { 1053 if (iscommand (ptr, idx)) 1054 break; 1055 idx = copy_past_newline (ptr, idx, tos); 1056 } 1057 pc++; 1058 } 1059 1060 static void 1061 swap (void) 1062 { 1063 string_type t; 1064 1065 t = tos[0]; 1066 tos[0] = tos[-1]; 1067 tos[-1] = t; 1068 pc++; 1069 } 1070 1071 static void 1072 other_dup (void) 1073 { 1074 tos++; 1075 check_range (); 1076 init_string (tos); 1077 catstr (tos, tos - 1); 1078 pc++; 1079 } 1080 1081 static void 1082 icatstr (void) 1083 { 1084 tos--; 1085 check_range (); 1086 catstr (tos, tos + 1); 1087 delete_string (tos + 1); 1088 pc++; 1089 } 1090 1091 static void 1092 skip_past_newline (void) 1093 { 1094 idx = skip_past_newline_1 (ptr, idx); 1095 pc++; 1096 } 1097 1098 static void 1099 maybecatstr (void) 1100 { 1101 if (internal_wanted == *internal_mode) 1102 { 1103 catstr (tos - 1, tos); 1104 } 1105 delete_string (tos); 1106 tos--; 1107 check_range (); 1108 pc++; 1109 } 1110 1111 static void 1112 catstrif (void) 1113 { 1114 int cond = isp[0]; 1115 isp--; 1116 icheck_range (); 1117 if (cond) 1118 catstr (tos - 1, tos); 1119 delete_string (tos); 1120 tos--; 1121 check_range (); 1122 pc++; 1123 } 1124 1125 char * 1126 nextword (char *string, char **word) 1127 { 1128 char *word_start; 1129 int idx; 1130 char *dst; 1131 char *src; 1132 1133 int length = 0; 1134 1135 while (isspace ((unsigned char) *string) || *string == '-') 1136 { 1137 if (*string == '-') 1138 { 1139 while (*string && *string != '\n') 1140 string++; 1141 1142 } 1143 else 1144 { 1145 string++; 1146 } 1147 } 1148 if (!*string) 1149 { 1150 *word = NULL; 1151 return NULL; 1152 } 1153 1154 word_start = string; 1155 if (*string == '"') 1156 { 1157 do 1158 { 1159 string++; 1160 length++; 1161 if (*string == '\\') 1162 { 1163 string += 2; 1164 length += 2; 1165 } 1166 } 1167 while (*string != '"'); 1168 } 1169 else 1170 { 1171 while (!isspace ((unsigned char) *string)) 1172 { 1173 string++; 1174 length++; 1175 1176 } 1177 } 1178 1179 *word = xmalloc (length + 1); 1180 1181 dst = *word; 1182 src = word_start; 1183 1184 for (idx = 0; idx < length; idx++) 1185 { 1186 if (src[idx] == '\\') 1187 switch (src[idx + 1]) 1188 { 1189 case 'n': 1190 *dst++ = '\n'; 1191 idx++; 1192 break; 1193 case '"': 1194 case '\\': 1195 *dst++ = src[idx + 1]; 1196 idx++; 1197 break; 1198 default: 1199 *dst++ = '\\'; 1200 break; 1201 } 1202 else 1203 *dst++ = src[idx]; 1204 } 1205 *dst++ = 0; 1206 1207 if (*string) 1208 return string + 1; 1209 else 1210 return NULL; 1211 } 1212 1213 dict_type * 1214 lookup_word (char *word) 1215 { 1216 dict_type *ptr = root; 1217 while (ptr) 1218 { 1219 if (strcmp (ptr->word, word) == 0) 1220 return ptr; 1221 ptr = ptr->next; 1222 } 1223 if (warning) 1224 fprintf (stderr, "Can't find %s\n", word); 1225 return NULL; 1226 } 1227 1228 static void 1229 free_words (void) 1230 { 1231 dict_type *ptr = root; 1232 1233 while (ptr) 1234 { 1235 dict_type *next; 1236 1237 free (ptr->word); 1238 if (ptr->code) 1239 { 1240 int i; 1241 for (i = 0; i < ptr->code_end - 1; i ++) 1242 if (ptr->code[i].f == push_text 1243 && ptr->code[i + 1].s) 1244 { 1245 free (ptr->code[i + 1].s - 1); 1246 ++i; 1247 } 1248 else if (ptr->code[i].f == push_variable) 1249 { 1250 free ((void *) ptr->code[i + 1].l); 1251 ++i; 1252 } 1253 free (ptr->code); 1254 } 1255 next = ptr->next; 1256 free (ptr); 1257 ptr = next; 1258 } 1259 } 1260 1261 static void 1262 perform (void) 1263 { 1264 tos = stack; 1265 1266 while (at (ptr, idx)) 1267 { 1268 /* It's worth looking through the command list. */ 1269 if (iscommand (ptr, idx)) 1270 { 1271 char *next; 1272 dict_type *word; 1273 1274 (void) nextword (addr (ptr, idx), &next); 1275 1276 word = lookup_word (next); 1277 1278 if (word) 1279 { 1280 exec (word); 1281 } 1282 else 1283 { 1284 if (warning) 1285 fprintf (stderr, "warning, %s is not recognised\n", next); 1286 idx = skip_past_newline_1 (ptr, idx); 1287 } 1288 free (next); 1289 } 1290 else 1291 idx = skip_past_newline_1 (ptr, idx); 1292 } 1293 } 1294 1295 dict_type * 1296 newentry (char *word) 1297 { 1298 dict_type *new_d = xmalloc (sizeof (*new_d)); 1299 new_d->word = word; 1300 new_d->next = root; 1301 root = new_d; 1302 new_d->code = xmalloc (sizeof (*new_d->code)); 1303 new_d->code_length = 1; 1304 new_d->code_end = 0; 1305 return new_d; 1306 } 1307 1308 unsigned int 1309 add_to_definition (dict_type *entry, pcu word) 1310 { 1311 if (entry->code_end == entry->code_length) 1312 { 1313 entry->code_length += 2; 1314 entry->code = xrealloc (entry->code, 1315 entry->code_length * sizeof (*entry->code)); 1316 } 1317 entry->code[entry->code_end] = word; 1318 1319 return entry->code_end++; 1320 } 1321 1322 void 1323 add_intrinsic (char *name, void (*func) (void)) 1324 { 1325 dict_type *new_d = newentry (xstrdup (name)); 1326 pcu p = { func }; 1327 add_to_definition (new_d, p); 1328 p.f = 0; 1329 add_to_definition (new_d, p); 1330 } 1331 1332 static void 1333 add_variable (char *name, intptr_t *loc) 1334 { 1335 dict_type *new_d = newentry (name); 1336 pcu p = { push_variable }; 1337 add_to_definition (new_d, p); 1338 p.l = (intptr_t) loc; 1339 add_to_definition (new_d, p); 1340 p.f = 0; 1341 add_to_definition (new_d, p); 1342 } 1343 1344 static void 1345 add_intrinsic_variable (const char *name, intptr_t *loc) 1346 { 1347 add_variable (xstrdup (name), loc); 1348 } 1349 1350 void 1351 compile (char *string) 1352 { 1353 /* Add words to the dictionary. */ 1354 char *word; 1355 1356 string = nextword (string, &word); 1357 while (string && *string && word[0]) 1358 { 1359 if (word[0] == ':') 1360 { 1361 dict_type *ptr; 1362 pcu p; 1363 1364 /* Compile a word and add to dictionary. */ 1365 free (word); 1366 string = nextword (string, &word); 1367 if (!string) 1368 continue; 1369 ptr = newentry (word); 1370 string = nextword (string, &word); 1371 if (!string) 1372 { 1373 free (ptr->code); 1374 free (ptr); 1375 continue; 1376 } 1377 1378 while (word[0] != ';') 1379 { 1380 switch (word[0]) 1381 { 1382 case '"': 1383 /* got a string, embed magic push string 1384 function */ 1385 p.f = push_text; 1386 add_to_definition (ptr, p); 1387 p.s = word + 1; 1388 add_to_definition (ptr, p); 1389 break; 1390 case '0': 1391 case '1': 1392 case '2': 1393 case '3': 1394 case '4': 1395 case '5': 1396 case '6': 1397 case '7': 1398 case '8': 1399 case '9': 1400 /* Got a number, embedd the magic push number 1401 function */ 1402 p.f = push_number; 1403 add_to_definition (ptr, p); 1404 p.l = atol (word); 1405 add_to_definition (ptr, p); 1406 free (word); 1407 break; 1408 default: 1409 p.f = call; 1410 add_to_definition (ptr, p); 1411 p.e = lookup_word (word); 1412 add_to_definition (ptr, p); 1413 free (word); 1414 } 1415 1416 string = nextword (string, &word); 1417 } 1418 p.f = 0; 1419 add_to_definition (ptr, p); 1420 free (word); 1421 string = nextword (string, &word); 1422 } 1423 else if (strcmp (word, "variable") == 0) 1424 { 1425 free (word); 1426 string = nextword (string, &word); 1427 if (!string) 1428 continue; 1429 intptr_t *loc = xmalloc (sizeof (intptr_t)); 1430 *loc = 0; 1431 add_variable (word, loc); 1432 string = nextword (string, &word); 1433 } 1434 else 1435 { 1436 fprintf (stderr, "syntax error at %s\n", string - 1); 1437 } 1438 } 1439 free (word); 1440 } 1441 1442 static void 1443 bang (void) 1444 { 1445 *(intptr_t *) ((isp[0])) = isp[-1]; 1446 isp -= 2; 1447 icheck_range (); 1448 pc++; 1449 } 1450 1451 static void 1452 atsign (void) 1453 { 1454 isp[0] = *(intptr_t *) (isp[0]); 1455 pc++; 1456 } 1457 1458 static void 1459 hello (void) 1460 { 1461 printf ("hello\n"); 1462 pc++; 1463 } 1464 1465 static void 1466 stdout_ (void) 1467 { 1468 isp++; 1469 icheck_range (); 1470 *isp = 1; 1471 pc++; 1472 } 1473 1474 static void 1475 stderr_ (void) 1476 { 1477 isp++; 1478 icheck_range (); 1479 *isp = 2; 1480 pc++; 1481 } 1482 1483 static void 1484 print (void) 1485 { 1486 if (*isp == 1) 1487 write_buffer (tos, stdout); 1488 else if (*isp == 2) 1489 write_buffer (tos, stderr); 1490 else 1491 fprintf (stderr, "print: illegal print destination `%" PRIdPTR "'\n", *isp); 1492 isp--; 1493 tos--; 1494 icheck_range (); 1495 check_range (); 1496 pc++; 1497 } 1498 1499 static void 1500 read_in (string_type *str, FILE *file) 1501 { 1502 char buff[10000]; 1503 unsigned int r; 1504 do 1505 { 1506 r = fread (buff, 1, sizeof (buff), file); 1507 catbuf (str, buff, r); 1508 } 1509 while (r); 1510 buff[0] = 0; 1511 1512 catbuf (str, buff, 1); 1513 } 1514 1515 static void 1516 usage (void) 1517 { 1518 fprintf (stderr, "usage: -[d|i|g] <file >file\n"); 1519 exit (33); 1520 } 1521 1522 /* There is no reliable way to declare exit. Sometimes it returns 1523 int, and sometimes it returns void. Sometimes it changes between 1524 OS releases. Trying to get it declared correctly in the hosts file 1525 is a pointless waste of time. */ 1526 1527 static void 1528 chew_exit (void) 1529 { 1530 exit (0); 1531 } 1532 1533 int 1534 main (int ac, char *av[]) 1535 { 1536 unsigned int i; 1537 string_type buffer; 1538 string_type pptr; 1539 1540 init_string (&buffer); 1541 init_string (&pptr); 1542 init_string (stack + 0); 1543 tos = stack + 1; 1544 ptr = &pptr; 1545 1546 add_intrinsic ("push_text", push_text); 1547 add_intrinsic ("!", bang); 1548 add_intrinsic ("@", atsign); 1549 add_intrinsic ("hello", hello); 1550 add_intrinsic ("stdout", stdout_); 1551 add_intrinsic ("stderr", stderr_); 1552 add_intrinsic ("print", print); 1553 add_intrinsic ("skip_past_newline", skip_past_newline); 1554 add_intrinsic ("catstr", icatstr); 1555 add_intrinsic ("copy_past_newline", icopy_past_newline); 1556 add_intrinsic ("dup", other_dup); 1557 add_intrinsic ("drop", drop); 1558 add_intrinsic ("idrop", idrop); 1559 add_intrinsic ("remchar", remchar); 1560 add_intrinsic ("get_stuff_in_command", get_stuff_in_command); 1561 add_intrinsic ("do_fancy_stuff", do_fancy_stuff); 1562 add_intrinsic ("bulletize", bulletize); 1563 add_intrinsic ("courierize", courierize); 1564 /* If the following line gives an error, exit() is not declared in the 1565 ../hosts/foo.h file for this host. Fix it there, not here! */ 1566 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */ 1567 add_intrinsic ("exit", chew_exit); 1568 add_intrinsic ("swap", swap); 1569 add_intrinsic ("outputdots", outputdots); 1570 add_intrinsic ("maybecatstr", maybecatstr); 1571 add_intrinsic ("catstrif", catstrif); 1572 add_intrinsic ("translatecomments", translatecomments); 1573 add_intrinsic ("wrap_comment", wrap_comment); 1574 add_intrinsic ("kill_bogus_lines", kill_bogus_lines); 1575 add_intrinsic ("indent", indent); 1576 add_intrinsic ("print_stack_level", print_stack_level); 1577 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines); 1578 add_intrinsic ("collapse_whitespace", collapse_whitespace); 1579 1580 internal_mode = xmalloc (sizeof (intptr_t)); 1581 *internal_mode = 0; 1582 add_intrinsic_variable ("internalmode", internal_mode); 1583 1584 /* Put a nl at the start. */ 1585 catchar (&buffer, '\n'); 1586 1587 read_in (&buffer, stdin); 1588 remove_noncomments (&buffer, ptr); 1589 for (i = 1; i < (unsigned int) ac; i++) 1590 { 1591 if (av[i][0] == '-') 1592 { 1593 if (av[i][1] == 'f') 1594 { 1595 string_type b; 1596 FILE *f; 1597 init_string (&b); 1598 1599 f = fopen (av[i + 1], "r"); 1600 if (!f) 1601 { 1602 fprintf (stderr, "Can't open the input file %s\n", 1603 av[i + 1]); 1604 return 33; 1605 } 1606 1607 read_in (&b, f); 1608 compile (b.ptr); 1609 perform (); 1610 delete_string (&b); 1611 } 1612 else if (av[i][1] == 'i') 1613 { 1614 internal_wanted = 1; 1615 } 1616 else if (av[i][1] == 'w') 1617 { 1618 warning = 1; 1619 } 1620 else 1621 usage (); 1622 } 1623 } 1624 write_buffer (stack + 0, stdout); 1625 free_words (); 1626 delete_string (&pptr); 1627 delete_string (&buffer); 1628 if (tos != stack) 1629 { 1630 fprintf (stderr, "finishing with current stack level %ld\n", 1631 (long) (tos - stack)); 1632 return 1; 1633 } 1634 return 0; 1635 } 1636