1/* chew 2 Copyright (C) 1990-2022 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*/ 35 36/* Primitives provided by the program: 37 38 Two stacks are provided, a string stack and an integer stack. 39 40 Internal state variables: 41 internal_wanted - indicates whether `-i' was passed 42 internal_mode - user-settable 43 44 Commands: 45 push_text 46 ! - pop top of integer stack for address, pop next for value; store 47 @ - treat value on integer stack as the address of an integer; push 48 that integer on the integer stack after popping the "address" 49 hello - print "hello\n" to stdout 50 stdout - put stdout marker on TOS 51 stderr - put stderr marker on TOS 52 print - print TOS-1 on TOS (eg: "hello\n" stdout print) 53 skip_past_newline 54 catstr - fn icatstr 55 copy_past_newline - append input, up to and including newline into TOS 56 dup - fn other_dup 57 drop - discard TOS 58 idrop - ditto 59 remchar - delete last character from TOS 60 get_stuff_in_command 61 do_fancy_stuff - translate <<foo>> to @code{foo} in TOS 62 bulletize - if "o" lines found, prepend @itemize @bullet to TOS 63 and @item to each "o" line; append @end itemize 64 courierize - put @example around . and | lines, translate {* *} { } 65 exit - fn chew_exit 66 swap 67 outputdots - strip out lines without leading dots 68 paramstuff - convert full declaration into "PARAMS" form if not already 69 maybecatstr - do catstr if internal_mode == internal_wanted, discard 70 value in any case 71 translatecomments - turn {* and *} into comment delimiters 72 kill_bogus_lines - get rid of extra newlines 73 indent 74 internalmode - pop from integer stack, set `internalmode' to that value 75 print_stack_level - print current stack depth to stderr 76 strip_trailing_newlines - go ahead, guess... 77 [quoted string] - push string onto string stack 78 [word starting with digit] - push atol(str) onto integer stack 79 80 A command must be all upper-case, and alone on a line. 81 82 Foo. */ 83 84#include <assert.h> 85#include <stdio.h> 86#include <ctype.h> 87#include <stdlib.h> 88#include <string.h> 89 90#define DEF_SIZE 5000 91#define STACK 50 92 93/* Here is a string type ... */ 94 95typedef struct buffer 96{ 97 char *ptr; 98 unsigned long write_idx; 99 unsigned long size; 100} string_type; 101 102/* Compiled programs consist of arrays of these. */ 103 104typedef union 105{ 106 void (*f) (void); 107 struct dict_struct *e; 108 char *s; 109 long l; 110} pcu; 111 112typedef struct dict_struct 113{ 114 char *word; 115 struct dict_struct *next; 116 pcu *code; 117 int code_length; 118 int code_end; 119} dict_type; 120 121int internal_wanted; 122int internal_mode; 123 124int warning; 125 126string_type stack[STACK]; 127string_type *tos; 128 129unsigned int idx = 0; /* Pos in input buffer */ 130string_type *ptr; /* and the buffer */ 131 132long istack[STACK]; 133long *isp = &istack[0]; 134 135dict_type *root; 136 137pcu *pc; 138 139static void 140die (char *msg) 141{ 142 fprintf (stderr, "%s\n", msg); 143 exit (1); 144} 145 146void * 147xmalloc (size_t size) 148{ 149 void *newmem; 150 151 if (size == 0) 152 size = 1; 153 newmem = malloc (size); 154 if (!newmem) 155 die ("out of memory"); 156 157 return newmem; 158} 159 160void * 161xrealloc (void *oldmem, size_t size) 162{ 163 void *newmem; 164 165 if (size == 0) 166 size = 1; 167 if (!oldmem) 168 newmem = malloc (size); 169 else 170 newmem = realloc (oldmem, size); 171 if (!newmem) 172 die ("out of memory"); 173 174 return newmem; 175} 176 177char * 178xstrdup (const char *s) 179{ 180 size_t len = strlen (s) + 1; 181 char *ret = xmalloc (len); 182 return memcpy (ret, s, len); 183} 184 185static void 186init_string_with_size (string_type *buffer, unsigned int size) 187{ 188 buffer->write_idx = 0; 189 buffer->size = size; 190 buffer->ptr = xmalloc (size); 191} 192 193static void 194init_string (string_type *buffer) 195{ 196 init_string_with_size (buffer, DEF_SIZE); 197} 198 199static int 200find (string_type *str, char *what) 201{ 202 unsigned int i; 203 char *p; 204 p = what; 205 for (i = 0; i < str->write_idx && *p; i++) 206 { 207 if (*p == str->ptr[i]) 208 p++; 209 else 210 p = what; 211 } 212 return (*p == 0); 213} 214 215static void 216write_buffer (string_type *buffer, FILE *f) 217{ 218 if (buffer->write_idx != 0 219 && fwrite (buffer->ptr, buffer->write_idx, 1, f) != 1) 220 die ("cannot write output"); 221} 222 223static void 224delete_string (string_type *buffer) 225{ 226 free (buffer->ptr); 227 buffer->ptr = NULL; 228} 229 230static char * 231addr (string_type *buffer, unsigned int idx) 232{ 233 return buffer->ptr + idx; 234} 235 236static char 237at (string_type *buffer, unsigned int pos) 238{ 239 if (pos >= buffer->write_idx) 240 return 0; 241 return buffer->ptr[pos]; 242} 243 244static void 245catchar (string_type *buffer, int ch) 246{ 247 if (buffer->write_idx == buffer->size) 248 { 249 buffer->size *= 2; 250 buffer->ptr = xrealloc (buffer->ptr, buffer->size); 251 } 252 253 buffer->ptr[buffer->write_idx++] = ch; 254} 255 256static void 257overwrite_string (string_type *dst, string_type *src) 258{ 259 free (dst->ptr); 260 dst->size = src->size; 261 dst->write_idx = src->write_idx; 262 dst->ptr = src->ptr; 263} 264 265static void 266catbuf (string_type *buffer, char *buf, unsigned int len) 267{ 268 if (buffer->write_idx + len >= buffer->size) 269 { 270 while (buffer->write_idx + len >= buffer->size) 271 buffer->size *= 2; 272 buffer->ptr = xrealloc (buffer->ptr, buffer->size); 273 } 274 memcpy (buffer->ptr + buffer->write_idx, buf, len); 275 buffer->write_idx += len; 276} 277 278static void 279cattext (string_type *buffer, char *string) 280{ 281 catbuf (buffer, string, (unsigned int) strlen (string)); 282} 283 284static void 285catstr (string_type *dst, string_type *src) 286{ 287 catbuf (dst, src->ptr, src->write_idx); 288} 289 290static unsigned int 291skip_white_and_stars (string_type *src, unsigned int idx) 292{ 293 char c; 294 while ((c = at (src, idx)), 295 isspace ((unsigned char) c) 296 || (c == '*' 297 /* Don't skip past end-of-comment or star as first 298 character on its line. */ 299 && at (src, idx +1) != '/' 300 && at (src, idx -1) != '\n')) 301 idx++; 302 return idx; 303} 304 305static unsigned int 306skip_past_newline_1 (string_type *ptr, unsigned int idx) 307{ 308 while (at (ptr, idx) 309 && at (ptr, idx) != '\n') 310 idx++; 311 if (at (ptr, idx) == '\n') 312 return idx + 1; 313 return idx; 314} 315 316static void 317check_range (void) 318{ 319 if (tos < stack) 320 die ("underflow in string stack"); 321 if (tos >= stack + STACK) 322 die ("overflow in string stack"); 323} 324 325static void 326icheck_range (void) 327{ 328 if (isp < istack) 329 die ("underflow in integer stack"); 330 if (isp >= istack + STACK) 331 die ("overflow in integer stack"); 332} 333 334static void 335exec (dict_type *word) 336{ 337 pc = word->code; 338 while (pc->f) 339 pc->f (); 340} 341 342static void 343call (void) 344{ 345 pcu *oldpc = pc; 346 dict_type *e = pc[1].e; 347 exec (e); 348 pc = oldpc + 2; 349} 350 351static void 352remchar (void) 353{ 354 if (tos->write_idx) 355 tos->write_idx--; 356 pc++; 357} 358 359static void 360strip_trailing_newlines (void) 361{ 362 while ((isspace ((unsigned char) at (tos, tos->write_idx - 1)) 363 || at (tos, tos->write_idx - 1) == '\n') 364 && tos->write_idx > 0) 365 tos->write_idx--; 366 pc++; 367} 368 369static void 370push_number (void) 371{ 372 isp++; 373 icheck_range (); 374 pc++; 375 *isp = pc->l; 376 pc++; 377} 378 379static void 380push_text (void) 381{ 382 tos++; 383 check_range (); 384 init_string (tos); 385 pc++; 386 cattext (tos, pc->s); 387 pc++; 388} 389 390/* This function removes everything not inside comments starting on 391 the first char of the line from the string, also when copying 392 comments, removes blank space and leading *'s. 393 Blank lines are turned into one blank line. */ 394 395static void 396remove_noncomments (string_type *src, string_type *dst) 397{ 398 unsigned int idx = 0; 399 400 while (at (src, idx)) 401 { 402 /* Now see if we have a comment at the start of the line. */ 403 if (at (src, idx) == '\n' 404 && at (src, idx + 1) == '/' 405 && at (src, idx + 2) == '*') 406 { 407 idx += 3; 408 409 idx = skip_white_and_stars (src, idx); 410 411 /* Remove leading dot */ 412 if (at (src, idx) == '.') 413 idx++; 414 415 /* Copy to the end of the line, or till the end of the 416 comment. */ 417 while (at (src, idx)) 418 { 419 if (at (src, idx) == '\n') 420 { 421 /* end of line, echo and scrape of leading blanks */ 422 if (at (src, idx + 1) == '\n') 423 catchar (dst, '\n'); 424 catchar (dst, '\n'); 425 idx++; 426 idx = skip_white_and_stars (src, idx); 427 } 428 else if (at (src, idx) == '*' && at (src, idx + 1) == '/') 429 { 430 idx += 2; 431 cattext (dst, "\nENDDD\n"); 432 break; 433 } 434 else 435 { 436 catchar (dst, at (src, idx)); 437 idx++; 438 } 439 } 440 } 441 else 442 idx++; 443 } 444} 445 446static void 447print_stack_level (void) 448{ 449 fprintf (stderr, "current string stack depth = %ld, ", 450 (long) (tos - stack)); 451 fprintf (stderr, "current integer stack depth = %ld\n", 452 (long) (isp - istack)); 453 pc++; 454} 455 456/* turn: 457 foobar name(stuff); 458 into: 459 foobar 460 name PARAMS ((stuff)); 461 and a blank line. 462 */ 463 464static void 465paramstuff (void) 466{ 467 unsigned int openp; 468 unsigned int fname; 469 unsigned int idx; 470 unsigned int len; 471 string_type out; 472 init_string (&out); 473 474#define NO_PARAMS 1 475 476 /* Make sure that it's not already param'd or proto'd. */ 477 if (NO_PARAMS 478 || find (tos, "PARAMS") || find (tos, "PROTO") || !find (tos, "(")) 479 { 480 catstr (&out, tos); 481 } 482 else 483 { 484 /* Find the open paren. */ 485 for (openp = 0; at (tos, openp) != '(' && at (tos, openp); openp++) 486 ; 487 488 fname = openp; 489 /* Step back to the fname. */ 490 fname--; 491 while (fname && isspace ((unsigned char) at (tos, fname))) 492 fname--; 493 while (fname 494 && !isspace ((unsigned char) at (tos,fname)) 495 && at (tos,fname) != '*') 496 fname--; 497 498 fname++; 499 500 /* Output type, omitting trailing whitespace character(s), if 501 any. */ 502 for (len = fname; 0 < len; len--) 503 { 504 if (!isspace ((unsigned char) at (tos, len - 1))) 505 break; 506 } 507 for (idx = 0; idx < len; idx++) 508 catchar (&out, at (tos, idx)); 509 510 cattext (&out, "\n"); /* Insert a newline between type and fnname */ 511 512 /* Output function name, omitting trailing whitespace 513 character(s), if any. */ 514 for (len = openp; 0 < len; len--) 515 { 516 if (!isspace ((unsigned char) at (tos, len - 1))) 517 break; 518 } 519 for (idx = fname; idx < len; idx++) 520 catchar (&out, at (tos, idx)); 521 522 cattext (&out, " PARAMS ("); 523 524 for (idx = openp; at (tos, idx) && at (tos, idx) != ';'; idx++) 525 catchar (&out, at (tos, idx)); 526 527 cattext (&out, ");\n\n"); 528 } 529 overwrite_string (tos, &out); 530 pc++; 531 532} 533 534/* turn {* 535 and *} into comments */ 536 537static void 538translatecomments (void) 539{ 540 unsigned int idx = 0; 541 string_type out; 542 init_string (&out); 543 544 while (at (tos, idx)) 545 { 546 if (at (tos, idx) == '{' && at (tos, idx + 1) == '*') 547 { 548 cattext (&out, "/*"); 549 idx += 2; 550 } 551 else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}') 552 { 553 cattext (&out, "*/"); 554 idx += 2; 555 } 556 else 557 { 558 catchar (&out, at (tos, idx)); 559 idx++; 560 } 561 } 562 563 overwrite_string (tos, &out); 564 565 pc++; 566} 567 568/* Mod tos so that only lines with leading dots remain */ 569static void 570outputdots (void) 571{ 572 unsigned int idx = 0; 573 string_type out; 574 init_string (&out); 575 576 while (at (tos, idx)) 577 { 578 /* Every iteration begins at the start of a line. */ 579 if (at (tos, idx) == '.') 580 { 581 char c; 582 583 idx++; 584 585 while ((c = at (tos, idx)) && c != '\n') 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 */ 618static void 619courierize (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 705static void 706bulletize (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 760static void 761do_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 799static int 800iscommand (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 823static int 824copy_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 852static void 853icopy_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/* indent 863 Take the string at the top of the stack, do some prettying. */ 864 865static void 866kill_bogus_lines (void) 867{ 868 int sl; 869 870 int idx = 0; 871 int c; 872 int dot = 0; 873 874 string_type out; 875 init_string (&out); 876 /* Drop leading nl. */ 877 while (at (tos, idx) == '\n') 878 { 879 idx++; 880 } 881 c = idx; 882 883 /* If the first char is a '.' prepend a newline so that it is 884 recognized properly later. */ 885 if (at (tos, idx) == '.') 886 catchar (&out, '\n'); 887 888 /* Find the last char. */ 889 while (at (tos, idx)) 890 { 891 idx++; 892 } 893 894 /* Find the last non white before the nl. */ 895 idx--; 896 897 while (idx && isspace ((unsigned char) at (tos, idx))) 898 idx--; 899 idx++; 900 901 /* Copy buffer upto last char, but blank lines before and after 902 dots don't count. */ 903 sl = 1; 904 905 while (c < idx) 906 { 907 if (at (tos, c) == '\n' 908 && at (tos, c + 1) == '\n' 909 && at (tos, c + 2) == '.') 910 { 911 /* Ignore two newlines before a dot. */ 912 c++; 913 } 914 else if (at (tos, c) == '.' && sl) 915 { 916 /* remember that this line started with a dot. */ 917 dot = 2; 918 } 919 else if (at (tos, c) == '\n' 920 && at (tos, c + 1) == '\n' 921 && dot) 922 { 923 c++; 924 /* Ignore two newlines when last line was dot. */ 925 } 926 927 catchar (&out, at (tos, c)); 928 if (at (tos, c) == '\n') 929 { 930 sl = 1; 931 932 if (dot == 2) 933 dot = 1; 934 else 935 dot = 0; 936 } 937 else 938 sl = 0; 939 940 c++; 941 942 } 943 944 /* Append nl. */ 945 catchar (&out, '\n'); 946 pc++; 947 delete_string (tos); 948 *tos = out; 949 950} 951 952static void 953indent (void) 954{ 955 string_type out; 956 int tab = 0; 957 int idx = 0; 958 int ol = 0; 959 init_string (&out); 960 while (at (tos, idx)) 961 { 962 switch (at (tos, idx)) 963 { 964 case '\n': 965 cattext (&out, "\n"); 966 idx++; 967 if (tab && at (tos, idx)) 968 { 969 cattext (&out, " "); 970 } 971 ol = 0; 972 break; 973 case '(': 974 tab++; 975 if (ol == 0) 976 cattext (&out, " "); 977 idx++; 978 cattext (&out, "("); 979 ol = 1; 980 break; 981 case ')': 982 tab--; 983 cattext (&out, ")"); 984 idx++; 985 ol = 1; 986 987 break; 988 default: 989 catchar (&out, at (tos, idx)); 990 ol = 1; 991 992 idx++; 993 break; 994 } 995 } 996 997 pc++; 998 delete_string (tos); 999 *tos = out; 1000 1001} 1002 1003static void 1004get_stuff_in_command (void) 1005{ 1006 tos++; 1007 check_range (); 1008 init_string (tos); 1009 1010 while (at (ptr, idx)) 1011 { 1012 if (iscommand (ptr, idx)) 1013 break; 1014 idx = copy_past_newline (ptr, idx, tos); 1015 } 1016 pc++; 1017} 1018 1019static void 1020swap (void) 1021{ 1022 string_type t; 1023 1024 t = tos[0]; 1025 tos[0] = tos[-1]; 1026 tos[-1] = t; 1027 pc++; 1028} 1029 1030static void 1031other_dup (void) 1032{ 1033 tos++; 1034 check_range (); 1035 init_string (tos); 1036 catstr (tos, tos - 1); 1037 pc++; 1038} 1039 1040static void 1041drop (void) 1042{ 1043 tos--; 1044 check_range (); 1045 delete_string (tos + 1); 1046 pc++; 1047} 1048 1049static void 1050idrop (void) 1051{ 1052 isp--; 1053 icheck_range (); 1054 pc++; 1055} 1056 1057static void 1058icatstr (void) 1059{ 1060 tos--; 1061 check_range (); 1062 catstr (tos, tos + 1); 1063 delete_string (tos + 1); 1064 pc++; 1065} 1066 1067static void 1068skip_past_newline (void) 1069{ 1070 idx = skip_past_newline_1 (ptr, idx); 1071 pc++; 1072} 1073 1074static void 1075internalmode (void) 1076{ 1077 internal_mode = *(isp); 1078 isp--; 1079 icheck_range (); 1080 pc++; 1081} 1082 1083static void 1084maybecatstr (void) 1085{ 1086 if (internal_wanted == internal_mode) 1087 { 1088 catstr (tos - 1, tos); 1089 } 1090 delete_string (tos); 1091 tos--; 1092 check_range (); 1093 pc++; 1094} 1095 1096char * 1097nextword (char *string, char **word) 1098{ 1099 char *word_start; 1100 int idx; 1101 char *dst; 1102 char *src; 1103 1104 int length = 0; 1105 1106 while (isspace ((unsigned char) *string) || *string == '-') 1107 { 1108 if (*string == '-') 1109 { 1110 while (*string && *string != '\n') 1111 string++; 1112 1113 } 1114 else 1115 { 1116 string++; 1117 } 1118 } 1119 if (!*string) 1120 { 1121 *word = NULL; 1122 return NULL; 1123 } 1124 1125 word_start = string; 1126 if (*string == '"') 1127 { 1128 do 1129 { 1130 string++; 1131 length++; 1132 if (*string == '\\') 1133 { 1134 string += 2; 1135 length += 2; 1136 } 1137 } 1138 while (*string != '"'); 1139 } 1140 else 1141 { 1142 while (!isspace ((unsigned char) *string)) 1143 { 1144 string++; 1145 length++; 1146 1147 } 1148 } 1149 1150 *word = xmalloc (length + 1); 1151 1152 dst = *word; 1153 src = word_start; 1154 1155 for (idx = 0; idx < length; idx++) 1156 { 1157 if (src[idx] == '\\') 1158 switch (src[idx + 1]) 1159 { 1160 case 'n': 1161 *dst++ = '\n'; 1162 idx++; 1163 break; 1164 case '"': 1165 case '\\': 1166 *dst++ = src[idx + 1]; 1167 idx++; 1168 break; 1169 default: 1170 *dst++ = '\\'; 1171 break; 1172 } 1173 else 1174 *dst++ = src[idx]; 1175 } 1176 *dst++ = 0; 1177 1178 if (*string) 1179 return string + 1; 1180 else 1181 return NULL; 1182} 1183 1184dict_type * 1185lookup_word (char *word) 1186{ 1187 dict_type *ptr = root; 1188 while (ptr) 1189 { 1190 if (strcmp (ptr->word, word) == 0) 1191 return ptr; 1192 ptr = ptr->next; 1193 } 1194 if (warning) 1195 fprintf (stderr, "Can't find %s\n", word); 1196 return NULL; 1197} 1198 1199static void 1200free_words (void) 1201{ 1202 dict_type *ptr = root; 1203 1204 while (ptr) 1205 { 1206 dict_type *next; 1207 1208 free (ptr->word); 1209 if (ptr->code) 1210 { 1211 int i; 1212 for (i = 0; i < ptr->code_end - 1; i ++) 1213 if (ptr->code[i].f == push_text 1214 && ptr->code[i + 1].s) 1215 { 1216 free (ptr->code[i + 1].s - 1); 1217 ++i; 1218 } 1219 free (ptr->code); 1220 } 1221 next = ptr->next; 1222 free (ptr); 1223 ptr = next; 1224 } 1225} 1226 1227static void 1228perform (void) 1229{ 1230 tos = stack; 1231 1232 while (at (ptr, idx)) 1233 { 1234 /* It's worth looking through the command list. */ 1235 if (iscommand (ptr, idx)) 1236 { 1237 char *next; 1238 dict_type *word; 1239 1240 (void) nextword (addr (ptr, idx), &next); 1241 1242 word = lookup_word (next); 1243 1244 if (word) 1245 { 1246 exec (word); 1247 } 1248 else 1249 { 1250 if (warning) 1251 fprintf (stderr, "warning, %s is not recognised\n", next); 1252 idx = skip_past_newline_1 (ptr, idx); 1253 } 1254 free (next); 1255 } 1256 else 1257 idx = skip_past_newline_1 (ptr, idx); 1258 } 1259} 1260 1261dict_type * 1262newentry (char *word) 1263{ 1264 dict_type *new_d = xmalloc (sizeof (*new_d)); 1265 new_d->word = word; 1266 new_d->next = root; 1267 root = new_d; 1268 new_d->code = xmalloc (sizeof (*new_d->code)); 1269 new_d->code_length = 1; 1270 new_d->code_end = 0; 1271 return new_d; 1272} 1273 1274unsigned int 1275add_to_definition (dict_type *entry, pcu word) 1276{ 1277 if (entry->code_end == entry->code_length) 1278 { 1279 entry->code_length += 2; 1280 entry->code = xrealloc (entry->code, 1281 entry->code_length * sizeof (*entry->code)); 1282 } 1283 entry->code[entry->code_end] = word; 1284 1285 return entry->code_end++; 1286} 1287 1288void 1289add_intrinsic (char *name, void (*func) (void)) 1290{ 1291 dict_type *new_d = newentry (xstrdup (name)); 1292 pcu p = { func }; 1293 add_to_definition (new_d, p); 1294 p.f = 0; 1295 add_to_definition (new_d, p); 1296} 1297 1298void 1299compile (char *string) 1300{ 1301 /* Add words to the dictionary. */ 1302 char *word; 1303 1304 string = nextword (string, &word); 1305 while (string && *string && word[0]) 1306 { 1307 if (word[0] == ':') 1308 { 1309 dict_type *ptr; 1310 pcu p; 1311 1312 /* Compile a word and add to dictionary. */ 1313 free (word); 1314 string = nextword (string, &word); 1315 if (!string) 1316 continue; 1317 ptr = newentry (word); 1318 string = nextword (string, &word); 1319 if (!string) 1320 { 1321 free (ptr->code); 1322 free (ptr); 1323 continue; 1324 } 1325 1326 while (word[0] != ';') 1327 { 1328 switch (word[0]) 1329 { 1330 case '"': 1331 /* got a string, embed magic push string 1332 function */ 1333 p.f = push_text; 1334 add_to_definition (ptr, p); 1335 p.s = word + 1; 1336 add_to_definition (ptr, p); 1337 break; 1338 case '0': 1339 case '1': 1340 case '2': 1341 case '3': 1342 case '4': 1343 case '5': 1344 case '6': 1345 case '7': 1346 case '8': 1347 case '9': 1348 /* Got a number, embedd the magic push number 1349 function */ 1350 p.f = push_number; 1351 add_to_definition (ptr, p); 1352 p.l = atol (word); 1353 add_to_definition (ptr, p); 1354 free (word); 1355 break; 1356 default: 1357 p.f = call; 1358 add_to_definition (ptr, p); 1359 p.e = lookup_word (word); 1360 add_to_definition (ptr, p); 1361 free (word); 1362 } 1363 1364 string = nextword (string, &word); 1365 } 1366 p.f = 0; 1367 add_to_definition (ptr, p); 1368 free (word); 1369 string = nextword (string, &word); 1370 } 1371 else 1372 { 1373 fprintf (stderr, "syntax error at %s\n", string - 1); 1374 } 1375 } 1376 free (word); 1377} 1378 1379static void 1380bang (void) 1381{ 1382 *(long *) ((isp[0])) = isp[-1]; 1383 isp -= 2; 1384 icheck_range (); 1385 pc++; 1386} 1387 1388static void 1389atsign (void) 1390{ 1391 isp[0] = *(long *) (isp[0]); 1392 pc++; 1393} 1394 1395static void 1396hello (void) 1397{ 1398 printf ("hello\n"); 1399 pc++; 1400} 1401 1402static void 1403stdout_ (void) 1404{ 1405 isp++; 1406 icheck_range (); 1407 *isp = 1; 1408 pc++; 1409} 1410 1411static void 1412stderr_ (void) 1413{ 1414 isp++; 1415 icheck_range (); 1416 *isp = 2; 1417 pc++; 1418} 1419 1420static void 1421print (void) 1422{ 1423 if (*isp == 1) 1424 write_buffer (tos, stdout); 1425 else if (*isp == 2) 1426 write_buffer (tos, stderr); 1427 else 1428 fprintf (stderr, "print: illegal print destination `%ld'\n", *isp); 1429 isp--; 1430 tos--; 1431 icheck_range (); 1432 check_range (); 1433 pc++; 1434} 1435 1436static void 1437read_in (string_type *str, FILE *file) 1438{ 1439 char buff[10000]; 1440 unsigned int r; 1441 do 1442 { 1443 r = fread (buff, 1, sizeof (buff), file); 1444 catbuf (str, buff, r); 1445 } 1446 while (r); 1447 buff[0] = 0; 1448 1449 catbuf (str, buff, 1); 1450} 1451 1452static void 1453usage (void) 1454{ 1455 fprintf (stderr, "usage: -[d|i|g] <file >file\n"); 1456 exit (33); 1457} 1458 1459/* There is no reliable way to declare exit. Sometimes it returns 1460 int, and sometimes it returns void. Sometimes it changes between 1461 OS releases. Trying to get it declared correctly in the hosts file 1462 is a pointless waste of time. */ 1463 1464static void 1465chew_exit (void) 1466{ 1467 exit (0); 1468} 1469 1470int 1471main (int ac, char *av[]) 1472{ 1473 unsigned int i; 1474 string_type buffer; 1475 string_type pptr; 1476 1477 init_string (&buffer); 1478 init_string (&pptr); 1479 init_string (stack + 0); 1480 tos = stack + 1; 1481 ptr = &pptr; 1482 1483 add_intrinsic ("push_text", push_text); 1484 add_intrinsic ("!", bang); 1485 add_intrinsic ("@", atsign); 1486 add_intrinsic ("hello", hello); 1487 add_intrinsic ("stdout", stdout_); 1488 add_intrinsic ("stderr", stderr_); 1489 add_intrinsic ("print", print); 1490 add_intrinsic ("skip_past_newline", skip_past_newline); 1491 add_intrinsic ("catstr", icatstr); 1492 add_intrinsic ("copy_past_newline", icopy_past_newline); 1493 add_intrinsic ("dup", other_dup); 1494 add_intrinsic ("drop", drop); 1495 add_intrinsic ("idrop", idrop); 1496 add_intrinsic ("remchar", remchar); 1497 add_intrinsic ("get_stuff_in_command", get_stuff_in_command); 1498 add_intrinsic ("do_fancy_stuff", do_fancy_stuff); 1499 add_intrinsic ("bulletize", bulletize); 1500 add_intrinsic ("courierize", courierize); 1501 /* If the following line gives an error, exit() is not declared in the 1502 ../hosts/foo.h file for this host. Fix it there, not here! */ 1503 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */ 1504 add_intrinsic ("exit", chew_exit); 1505 add_intrinsic ("swap", swap); 1506 add_intrinsic ("outputdots", outputdots); 1507 add_intrinsic ("paramstuff", paramstuff); 1508 add_intrinsic ("maybecatstr", maybecatstr); 1509 add_intrinsic ("translatecomments", translatecomments); 1510 add_intrinsic ("kill_bogus_lines", kill_bogus_lines); 1511 add_intrinsic ("indent", indent); 1512 add_intrinsic ("internalmode", internalmode); 1513 add_intrinsic ("print_stack_level", print_stack_level); 1514 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines); 1515 1516 /* Put a nl at the start. */ 1517 catchar (&buffer, '\n'); 1518 1519 read_in (&buffer, stdin); 1520 remove_noncomments (&buffer, ptr); 1521 for (i = 1; i < (unsigned int) ac; i++) 1522 { 1523 if (av[i][0] == '-') 1524 { 1525 if (av[i][1] == 'f') 1526 { 1527 string_type b; 1528 FILE *f; 1529 init_string (&b); 1530 1531 f = fopen (av[i + 1], "r"); 1532 if (!f) 1533 { 1534 fprintf (stderr, "Can't open the input file %s\n", 1535 av[i + 1]); 1536 return 33; 1537 } 1538 1539 read_in (&b, f); 1540 compile (b.ptr); 1541 perform (); 1542 delete_string (&b); 1543 } 1544 else if (av[i][1] == 'i') 1545 { 1546 internal_wanted = 1; 1547 } 1548 else if (av[i][1] == 'w') 1549 { 1550 warning = 1; 1551 } 1552 else 1553 usage (); 1554 } 1555 } 1556 write_buffer (stack + 0, stdout); 1557 free_words (); 1558 delete_string (&pptr); 1559 delete_string (&buffer); 1560 if (tos != stack) 1561 { 1562 fprintf (stderr, "finishing with current stack level %ld\n", 1563 (long) (tos - stack)); 1564 return 1; 1565 } 1566 return 0; 1567} 1568