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