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