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 546 /* Mod tos so that only lines with leading dots remain */ 547 static void 548 outputdots (void) 549 { 550 unsigned int idx = 0; 551 string_type out; 552 init_string (&out); 553 554 while (at (tos, idx)) 555 { 556 /* Every iteration begins at the start of a line. */ 557 if (at (tos, idx) == '.') 558 { 559 char c; 560 int spaces; 561 562 idx++; 563 spaces = 0; 564 while ((c = at (tos, idx)) && c != '\n') 565 { 566 if (spaces >= 0) 567 { 568 if (c == ' ') 569 { 570 spaces++; 571 idx++; 572 continue; 573 } 574 else 575 { 576 while (spaces >= 8) 577 { 578 catchar (&out, '\t'); 579 spaces -= 8; 580 } 581 while (spaces-- > 0) 582 catchar (&out, ' '); 583 } 584 } 585 if (c == '{' && at (tos, idx + 1) == '*') 586 { 587 cattext (&out, "/*"); 588 idx += 2; 589 } 590 else if (c == '*' && at (tos, idx + 1) == '}') 591 { 592 cattext (&out, "*/"); 593 idx += 2; 594 } 595 else 596 { 597 catchar (&out, c); 598 idx++; 599 } 600 } 601 if (c == '\n') 602 idx++; 603 catchar (&out, '\n'); 604 } 605 else 606 { 607 idx = skip_past_newline_1 (tos, idx); 608 } 609 } 610 611 overwrite_string (tos, &out); 612 pc++; 613 } 614 615 /* Find lines starting with . and | and put example around them on tos */ 616 static void 617 courierize (void) 618 { 619 string_type out; 620 unsigned int idx = 0; 621 int command = 0; 622 623 init_string (&out); 624 625 while (at (tos, idx)) 626 { 627 if (at (tos, idx) == '\n' 628 && (at (tos, idx +1 ) == '.' 629 || at (tos, idx + 1) == '|')) 630 { 631 cattext (&out, "\n@example\n"); 632 do 633 { 634 idx += 2; 635 636 while (at (tos, idx) && at (tos, idx) != '\n') 637 { 638 if (command > 1) 639 { 640 /* We are inside {} parameters of some command; 641 Just pass through until matching brace. */ 642 if (at (tos, idx) == '{') 643 ++command; 644 else if (at (tos, idx) == '}') 645 --command; 646 } 647 else if (command != 0) 648 { 649 if (at (tos, idx) == '{') 650 ++command; 651 else if (!islower ((unsigned char) at (tos, idx))) 652 --command; 653 } 654 else if (at (tos, idx) == '@' 655 && islower ((unsigned char) at (tos, idx + 1))) 656 { 657 ++command; 658 } 659 else if (at (tos, idx) == '{' && at (tos, idx + 1) == '*') 660 { 661 cattext (&out, "/*"); 662 idx += 2; 663 continue; 664 } 665 else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}') 666 { 667 cattext (&out, "*/"); 668 idx += 2; 669 continue; 670 } 671 else if (at (tos, idx) == '{' 672 || at (tos, idx) == '}') 673 { 674 catchar (&out, '@'); 675 } 676 677 catchar (&out, at (tos, idx)); 678 idx++; 679 } 680 catchar (&out, '\n'); 681 } 682 while (at (tos, idx) == '\n' 683 && ((at (tos, idx + 1) == '.') 684 || (at (tos, idx + 1) == '|'))) 685 ; 686 cattext (&out, "@end example"); 687 } 688 else 689 { 690 catchar (&out, at (tos, idx)); 691 idx++; 692 } 693 } 694 695 overwrite_string (tos, &out); 696 pc++; 697 } 698 699 /* Finds any lines starting with "o ", if there are any, then turns 700 on @itemize @bullet, and @items each of them. Then ends with @end 701 itemize, inplace at TOS*/ 702 703 static void 704 bulletize (void) 705 { 706 unsigned int idx = 0; 707 int on = 0; 708 string_type out; 709 init_string (&out); 710 711 while (at (tos, idx)) 712 { 713 if (at (tos, idx) == '@' 714 && at (tos, idx + 1) == '*') 715 { 716 cattext (&out, "*"); 717 idx += 2; 718 } 719 else if (at (tos, idx) == '\n' 720 && at (tos, idx + 1) == 'o' 721 && isspace ((unsigned char) at (tos, idx + 2))) 722 { 723 if (!on) 724 { 725 cattext (&out, "\n@itemize @bullet\n"); 726 on = 1; 727 728 } 729 cattext (&out, "\n@item\n"); 730 idx += 3; 731 } 732 else 733 { 734 catchar (&out, at (tos, idx)); 735 if (on && at (tos, idx) == '\n' 736 && at (tos, idx + 1) == '\n' 737 && at (tos, idx + 2) != 'o') 738 { 739 cattext (&out, "@end itemize"); 740 on = 0; 741 } 742 idx++; 743 744 } 745 } 746 if (on) 747 { 748 cattext (&out, "@end itemize\n"); 749 } 750 751 delete_string (tos); 752 *tos = out; 753 pc++; 754 } 755 756 /* Turn <<foo>> into @code{foo} in place at TOS*/ 757 758 static void 759 do_fancy_stuff (void) 760 { 761 unsigned int idx = 0; 762 string_type out; 763 init_string (&out); 764 while (at (tos, idx)) 765 { 766 if (at (tos, idx) == '<' 767 && at (tos, idx + 1) == '<' 768 && !isspace ((unsigned char) at (tos, idx + 2))) 769 { 770 /* This qualifies as a << startup. */ 771 idx += 2; 772 cattext (&out, "@code{"); 773 while (at (tos, idx) 774 && at (tos, idx) != '>' ) 775 { 776 catchar (&out, at (tos, idx)); 777 idx++; 778 779 } 780 cattext (&out, "}"); 781 idx += 2; 782 } 783 else 784 { 785 catchar (&out, at (tos, idx)); 786 idx++; 787 } 788 } 789 delete_string (tos); 790 *tos = out; 791 pc++; 792 793 } 794 795 /* A command is all upper case,and alone on a line. */ 796 797 static int 798 iscommand (string_type *ptr, unsigned int idx) 799 { 800 unsigned int len = 0; 801 while (at (ptr, idx)) 802 { 803 if (isupper ((unsigned char) at (ptr, idx)) 804 || at (ptr, idx) == ' ' || at (ptr, idx) == '_') 805 { 806 len++; 807 idx++; 808 } 809 else if (at (ptr, idx) == '\n') 810 { 811 if (len > 3) 812 return 1; 813 return 0; 814 } 815 else 816 return 0; 817 } 818 return 0; 819 } 820 821 static int 822 copy_past_newline (string_type *ptr, unsigned int idx, string_type *dst) 823 { 824 int column = 0; 825 826 while (at (ptr, idx) && at (ptr, idx) != '\n') 827 { 828 if (at (ptr, idx) == '\t') 829 { 830 /* Expand tabs. Neither makeinfo nor TeX can cope well with 831 them. */ 832 do 833 catchar (dst, ' '); 834 while (++column & 7); 835 } 836 else 837 { 838 catchar (dst, at (ptr, idx)); 839 column++; 840 } 841 idx++; 842 843 } 844 catchar (dst, at (ptr, idx)); 845 idx++; 846 return idx; 847 848 } 849 850 static void 851 icopy_past_newline (void) 852 { 853 tos++; 854 check_range (); 855 init_string (tos); 856 idx = copy_past_newline (ptr, idx, tos); 857 pc++; 858 } 859 860 static void 861 kill_bogus_lines (void) 862 { 863 int sl; 864 865 int idx = 0; 866 int c; 867 int dot = 0; 868 869 string_type out; 870 init_string (&out); 871 /* Drop leading nl. */ 872 while (at (tos, idx) == '\n') 873 { 874 idx++; 875 } 876 c = idx; 877 878 /* If the first char is a '.' prepend a newline so that it is 879 recognized properly later. */ 880 if (at (tos, idx) == '.') 881 catchar (&out, '\n'); 882 883 /* Find the last char. */ 884 while (at (tos, idx)) 885 { 886 idx++; 887 } 888 889 /* Find the last non white before the nl. */ 890 idx--; 891 892 while (idx && isspace ((unsigned char) at (tos, idx))) 893 idx--; 894 idx++; 895 896 /* Copy buffer upto last char, but blank lines before and after 897 dots don't count. */ 898 sl = 1; 899 900 while (c < idx) 901 { 902 if (at (tos, c) == '\n' 903 && at (tos, c + 1) == '\n' 904 && at (tos, c + 2) == '.') 905 { 906 /* Ignore two newlines before a dot. */ 907 c++; 908 } 909 else if (at (tos, c) == '.' && sl) 910 { 911 /* remember that this line started with a dot. */ 912 dot = 2; 913 } 914 else if (at (tos, c) == '\n' 915 && at (tos, c + 1) == '\n' 916 && dot) 917 { 918 c++; 919 /* Ignore two newlines when last line was dot. */ 920 } 921 922 catchar (&out, at (tos, c)); 923 if (at (tos, c) == '\n') 924 { 925 sl = 1; 926 927 if (dot == 2) 928 dot = 1; 929 else 930 dot = 0; 931 } 932 else 933 sl = 0; 934 935 c++; 936 937 } 938 939 /* Append nl. */ 940 catchar (&out, '\n'); 941 pc++; 942 delete_string (tos); 943 *tos = out; 944 945 } 946 947 static void 948 collapse_whitespace (void) 949 { 950 int last_was_ws = 0; 951 int idx; 952 953 string_type out; 954 init_string (&out); 955 956 for (idx = 0; at (tos, idx) != 0; ++idx) 957 { 958 char c = at (tos, idx); 959 if (isspace (c)) 960 { 961 if (!last_was_ws) 962 { 963 catchar (&out, ' '); 964 last_was_ws = 1; 965 } 966 } 967 else 968 { 969 catchar (&out, c); 970 last_was_ws = 0; 971 } 972 } 973 974 pc++; 975 delete_string (tos); 976 *tos = out; 977 } 978 979 /* indent 980 Take the string at the top of the stack, do some prettying. */ 981 982 static void 983 indent (void) 984 { 985 string_type out; 986 int tab = 0; 987 int idx = 0; 988 int ol = 0; 989 init_string (&out); 990 while (at (tos, idx)) 991 { 992 switch (at (tos, idx)) 993 { 994 case '\n': 995 catchar (&out, '\n'); 996 idx++; 997 if (tab && at (tos, idx)) 998 { 999 int i; 1000 for (i = 0; i < tab - 1; i += 2) 1001 catchar (&out, '\t'); 1002 if (i < tab) 1003 cattext (&out, " "); 1004 } 1005 ol = 0; 1006 break; 1007 case '(': 1008 if (ol == 0) 1009 { 1010 int i; 1011 for (i = 1; i < tab - 1; i += 2) 1012 catchar (&out, '\t'); 1013 if (i < tab) 1014 cattext (&out, " "); 1015 cattext (&out, " "); 1016 } 1017 tab++; 1018 idx++; 1019 catchar (&out, '('); 1020 ol = 1; 1021 break; 1022 case ')': 1023 tab--; 1024 catchar (&out, ')'); 1025 idx++; 1026 ol = 1; 1027 break; 1028 default: 1029 catchar (&out, at (tos, idx)); 1030 ol = 1; 1031 idx++; 1032 break; 1033 } 1034 } 1035 1036 pc++; 1037 delete_string (tos); 1038 *tos = out; 1039 1040 } 1041 1042 static void 1043 get_stuff_in_command (void) 1044 { 1045 tos++; 1046 check_range (); 1047 init_string (tos); 1048 1049 while (at (ptr, idx)) 1050 { 1051 if (iscommand (ptr, idx)) 1052 break; 1053 idx = copy_past_newline (ptr, idx, tos); 1054 } 1055 pc++; 1056 } 1057 1058 static void 1059 swap (void) 1060 { 1061 string_type t; 1062 1063 t = tos[0]; 1064 tos[0] = tos[-1]; 1065 tos[-1] = t; 1066 pc++; 1067 } 1068 1069 static void 1070 other_dup (void) 1071 { 1072 tos++; 1073 check_range (); 1074 init_string (tos); 1075 catstr (tos, tos - 1); 1076 pc++; 1077 } 1078 1079 static void 1080 icatstr (void) 1081 { 1082 tos--; 1083 check_range (); 1084 catstr (tos, tos + 1); 1085 delete_string (tos + 1); 1086 pc++; 1087 } 1088 1089 static void 1090 skip_past_newline (void) 1091 { 1092 idx = skip_past_newline_1 (ptr, idx); 1093 pc++; 1094 } 1095 1096 static void 1097 maybecatstr (void) 1098 { 1099 if (internal_wanted == *internal_mode) 1100 { 1101 catstr (tos - 1, tos); 1102 } 1103 delete_string (tos); 1104 tos--; 1105 check_range (); 1106 pc++; 1107 } 1108 1109 static void 1110 catstrif (void) 1111 { 1112 int cond = isp[0]; 1113 isp--; 1114 icheck_range (); 1115 if (cond) 1116 catstr (tos - 1, tos); 1117 delete_string (tos); 1118 tos--; 1119 check_range (); 1120 pc++; 1121 } 1122 1123 char * 1124 nextword (char *string, char **word) 1125 { 1126 char *word_start; 1127 int idx; 1128 char *dst; 1129 char *src; 1130 1131 int length = 0; 1132 1133 while (isspace ((unsigned char) *string) || *string == '-') 1134 { 1135 if (*string == '-') 1136 { 1137 while (*string && *string != '\n') 1138 string++; 1139 1140 } 1141 else 1142 { 1143 string++; 1144 } 1145 } 1146 if (!*string) 1147 { 1148 *word = NULL; 1149 return NULL; 1150 } 1151 1152 word_start = string; 1153 if (*string == '"') 1154 { 1155 do 1156 { 1157 string++; 1158 length++; 1159 if (*string == '\\') 1160 { 1161 string += 2; 1162 length += 2; 1163 } 1164 } 1165 while (*string != '"'); 1166 } 1167 else 1168 { 1169 while (!isspace ((unsigned char) *string)) 1170 { 1171 string++; 1172 length++; 1173 1174 } 1175 } 1176 1177 *word = xmalloc (length + 1); 1178 1179 dst = *word; 1180 src = word_start; 1181 1182 for (idx = 0; idx < length; idx++) 1183 { 1184 if (src[idx] == '\\') 1185 switch (src[idx + 1]) 1186 { 1187 case 'n': 1188 *dst++ = '\n'; 1189 idx++; 1190 break; 1191 case '"': 1192 case '\\': 1193 *dst++ = src[idx + 1]; 1194 idx++; 1195 break; 1196 default: 1197 *dst++ = '\\'; 1198 break; 1199 } 1200 else 1201 *dst++ = src[idx]; 1202 } 1203 *dst++ = 0; 1204 1205 if (*string) 1206 return string + 1; 1207 else 1208 return NULL; 1209 } 1210 1211 dict_type * 1212 lookup_word (char *word) 1213 { 1214 dict_type *ptr = root; 1215 while (ptr) 1216 { 1217 if (strcmp (ptr->word, word) == 0) 1218 return ptr; 1219 ptr = ptr->next; 1220 } 1221 if (warning) 1222 fprintf (stderr, "Can't find %s\n", word); 1223 return NULL; 1224 } 1225 1226 static void 1227 free_words (void) 1228 { 1229 dict_type *ptr = root; 1230 1231 while (ptr) 1232 { 1233 dict_type *next; 1234 1235 free (ptr->word); 1236 if (ptr->code) 1237 { 1238 int i; 1239 for (i = 0; i < ptr->code_end - 1; i ++) 1240 if (ptr->code[i].f == push_text 1241 && ptr->code[i + 1].s) 1242 { 1243 free (ptr->code[i + 1].s - 1); 1244 ++i; 1245 } 1246 else if (ptr->code[i].f == push_variable) 1247 { 1248 free ((void *) ptr->code[i + 1].l); 1249 ++i; 1250 } 1251 free (ptr->code); 1252 } 1253 next = ptr->next; 1254 free (ptr); 1255 ptr = next; 1256 } 1257 } 1258 1259 static void 1260 perform (void) 1261 { 1262 tos = stack; 1263 1264 while (at (ptr, idx)) 1265 { 1266 /* It's worth looking through the command list. */ 1267 if (iscommand (ptr, idx)) 1268 { 1269 char *next; 1270 dict_type *word; 1271 1272 (void) nextword (addr (ptr, idx), &next); 1273 1274 word = lookup_word (next); 1275 1276 if (word) 1277 { 1278 exec (word); 1279 } 1280 else 1281 { 1282 if (warning) 1283 fprintf (stderr, "warning, %s is not recognised\n", next); 1284 idx = skip_past_newline_1 (ptr, idx); 1285 } 1286 free (next); 1287 } 1288 else 1289 idx = skip_past_newline_1 (ptr, idx); 1290 } 1291 } 1292 1293 dict_type * 1294 newentry (char *word) 1295 { 1296 dict_type *new_d = xmalloc (sizeof (*new_d)); 1297 new_d->word = word; 1298 new_d->next = root; 1299 root = new_d; 1300 new_d->code = xmalloc (sizeof (*new_d->code)); 1301 new_d->code_length = 1; 1302 new_d->code_end = 0; 1303 return new_d; 1304 } 1305 1306 unsigned int 1307 add_to_definition (dict_type *entry, pcu word) 1308 { 1309 if (entry->code_end == entry->code_length) 1310 { 1311 entry->code_length += 2; 1312 entry->code = xrealloc (entry->code, 1313 entry->code_length * sizeof (*entry->code)); 1314 } 1315 entry->code[entry->code_end] = word; 1316 1317 return entry->code_end++; 1318 } 1319 1320 void 1321 add_intrinsic (char *name, void (*func) (void)) 1322 { 1323 dict_type *new_d = newentry (xstrdup (name)); 1324 pcu p = { func }; 1325 add_to_definition (new_d, p); 1326 p.f = 0; 1327 add_to_definition (new_d, p); 1328 } 1329 1330 static void 1331 add_variable (char *name, intptr_t *loc) 1332 { 1333 dict_type *new_d = newentry (name); 1334 pcu p = { push_variable }; 1335 add_to_definition (new_d, p); 1336 p.l = (intptr_t) loc; 1337 add_to_definition (new_d, p); 1338 p.f = 0; 1339 add_to_definition (new_d, p); 1340 } 1341 1342 static void 1343 add_intrinsic_variable (const char *name, intptr_t *loc) 1344 { 1345 add_variable (xstrdup (name), loc); 1346 } 1347 1348 void 1349 compile (char *string) 1350 { 1351 /* Add words to the dictionary. */ 1352 char *word; 1353 1354 string = nextword (string, &word); 1355 while (string && *string && word[0]) 1356 { 1357 if (word[0] == ':') 1358 { 1359 dict_type *ptr; 1360 pcu p; 1361 1362 /* Compile a word and add to dictionary. */ 1363 free (word); 1364 string = nextword (string, &word); 1365 if (!string) 1366 continue; 1367 ptr = newentry (word); 1368 string = nextword (string, &word); 1369 if (!string) 1370 { 1371 free (ptr->code); 1372 free (ptr); 1373 continue; 1374 } 1375 1376 while (word[0] != ';') 1377 { 1378 switch (word[0]) 1379 { 1380 case '"': 1381 /* got a string, embed magic push string 1382 function */ 1383 p.f = push_text; 1384 add_to_definition (ptr, p); 1385 p.s = word + 1; 1386 add_to_definition (ptr, p); 1387 break; 1388 case '0': 1389 case '1': 1390 case '2': 1391 case '3': 1392 case '4': 1393 case '5': 1394 case '6': 1395 case '7': 1396 case '8': 1397 case '9': 1398 /* Got a number, embedd the magic push number 1399 function */ 1400 p.f = push_number; 1401 add_to_definition (ptr, p); 1402 p.l = atol (word); 1403 add_to_definition (ptr, p); 1404 free (word); 1405 break; 1406 default: 1407 p.f = call; 1408 add_to_definition (ptr, p); 1409 p.e = lookup_word (word); 1410 add_to_definition (ptr, p); 1411 free (word); 1412 } 1413 1414 string = nextword (string, &word); 1415 } 1416 p.f = 0; 1417 add_to_definition (ptr, p); 1418 free (word); 1419 string = nextword (string, &word); 1420 } 1421 else if (strcmp (word, "variable") == 0) 1422 { 1423 free (word); 1424 string = nextword (string, &word); 1425 if (!string) 1426 continue; 1427 intptr_t *loc = xmalloc (sizeof (intptr_t)); 1428 *loc = 0; 1429 add_variable (word, loc); 1430 string = nextword (string, &word); 1431 } 1432 else 1433 { 1434 fprintf (stderr, "syntax error at %s\n", string - 1); 1435 } 1436 } 1437 free (word); 1438 } 1439 1440 static void 1441 bang (void) 1442 { 1443 *(intptr_t *) ((isp[0])) = isp[-1]; 1444 isp -= 2; 1445 icheck_range (); 1446 pc++; 1447 } 1448 1449 static void 1450 atsign (void) 1451 { 1452 isp[0] = *(intptr_t *) (isp[0]); 1453 pc++; 1454 } 1455 1456 static void 1457 hello (void) 1458 { 1459 printf ("hello\n"); 1460 pc++; 1461 } 1462 1463 static void 1464 stdout_ (void) 1465 { 1466 isp++; 1467 icheck_range (); 1468 *isp = 1; 1469 pc++; 1470 } 1471 1472 static void 1473 stderr_ (void) 1474 { 1475 isp++; 1476 icheck_range (); 1477 *isp = 2; 1478 pc++; 1479 } 1480 1481 static void 1482 print (void) 1483 { 1484 if (*isp == 1) 1485 write_buffer (tos, stdout); 1486 else if (*isp == 2) 1487 write_buffer (tos, stderr); 1488 else 1489 fprintf (stderr, "print: illegal print destination `%" PRIdPTR "'\n", *isp); 1490 isp--; 1491 tos--; 1492 icheck_range (); 1493 check_range (); 1494 pc++; 1495 } 1496 1497 static void 1498 read_in (string_type *str, FILE *file) 1499 { 1500 char buff[10000]; 1501 unsigned int r; 1502 do 1503 { 1504 r = fread (buff, 1, sizeof (buff), file); 1505 catbuf (str, buff, r); 1506 } 1507 while (r); 1508 buff[0] = 0; 1509 1510 catbuf (str, buff, 1); 1511 } 1512 1513 static void 1514 usage (void) 1515 { 1516 fprintf (stderr, "usage: -[d|i|g] <file >file\n"); 1517 exit (33); 1518 } 1519 1520 /* There is no reliable way to declare exit. Sometimes it returns 1521 int, and sometimes it returns void. Sometimes it changes between 1522 OS releases. Trying to get it declared correctly in the hosts file 1523 is a pointless waste of time. */ 1524 1525 static void 1526 chew_exit (void) 1527 { 1528 exit (0); 1529 } 1530 1531 int 1532 main (int ac, char *av[]) 1533 { 1534 unsigned int i; 1535 string_type buffer; 1536 string_type pptr; 1537 1538 init_string (&buffer); 1539 init_string (&pptr); 1540 init_string (stack + 0); 1541 tos = stack + 1; 1542 ptr = &pptr; 1543 1544 add_intrinsic ("push_text", push_text); 1545 add_intrinsic ("!", bang); 1546 add_intrinsic ("@", atsign); 1547 add_intrinsic ("hello", hello); 1548 add_intrinsic ("stdout", stdout_); 1549 add_intrinsic ("stderr", stderr_); 1550 add_intrinsic ("print", print); 1551 add_intrinsic ("skip_past_newline", skip_past_newline); 1552 add_intrinsic ("catstr", icatstr); 1553 add_intrinsic ("copy_past_newline", icopy_past_newline); 1554 add_intrinsic ("dup", other_dup); 1555 add_intrinsic ("drop", drop); 1556 add_intrinsic ("idrop", idrop); 1557 add_intrinsic ("remchar", remchar); 1558 add_intrinsic ("get_stuff_in_command", get_stuff_in_command); 1559 add_intrinsic ("do_fancy_stuff", do_fancy_stuff); 1560 add_intrinsic ("bulletize", bulletize); 1561 add_intrinsic ("courierize", courierize); 1562 /* If the following line gives an error, exit() is not declared in the 1563 ../hosts/foo.h file for this host. Fix it there, not here! */ 1564 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */ 1565 add_intrinsic ("exit", chew_exit); 1566 add_intrinsic ("swap", swap); 1567 add_intrinsic ("outputdots", outputdots); 1568 add_intrinsic ("maybecatstr", maybecatstr); 1569 add_intrinsic ("catstrif", catstrif); 1570 add_intrinsic ("translatecomments", translatecomments); 1571 add_intrinsic ("wrap_comment", wrap_comment); 1572 add_intrinsic ("kill_bogus_lines", kill_bogus_lines); 1573 add_intrinsic ("indent", indent); 1574 add_intrinsic ("print_stack_level", print_stack_level); 1575 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines); 1576 add_intrinsic ("collapse_whitespace", collapse_whitespace); 1577 1578 internal_mode = xmalloc (sizeof (intptr_t)); 1579 *internal_mode = 0; 1580 add_intrinsic_variable ("internalmode", internal_mode); 1581 1582 /* Put a nl at the start. */ 1583 catchar (&buffer, '\n'); 1584 1585 read_in (&buffer, stdin); 1586 remove_noncomments (&buffer, ptr); 1587 for (i = 1; i < (unsigned int) ac; i++) 1588 { 1589 if (av[i][0] == '-') 1590 { 1591 if (av[i][1] == 'f') 1592 { 1593 string_type b; 1594 FILE *f; 1595 init_string (&b); 1596 1597 f = fopen (av[i + 1], "r"); 1598 if (!f) 1599 { 1600 fprintf (stderr, "Can't open the input file %s\n", 1601 av[i + 1]); 1602 return 33; 1603 } 1604 1605 read_in (&b, f); 1606 compile (b.ptr); 1607 perform (); 1608 delete_string (&b); 1609 } 1610 else if (av[i][1] == 'i') 1611 { 1612 internal_wanted = 1; 1613 } 1614 else if (av[i][1] == 'w') 1615 { 1616 warning = 1; 1617 } 1618 else 1619 usage (); 1620 } 1621 } 1622 write_buffer (stack + 0, stdout); 1623 free_words (); 1624 delete_string (&pptr); 1625 delete_string (&buffer); 1626 if (tos != stack) 1627 { 1628 fprintf (stderr, "finishing with current stack level %ld\n", 1629 (long) (tos - stack)); 1630 return 1; 1631 } 1632 return 0; 1633 } 1634