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