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