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