1 /* chew
2 Copyright (C) 1990-2016 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
init_string_with_size(buffer,size)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
init_string(buffer)135 init_string (buffer)
136 string_type *buffer;
137 {
138 init_string_with_size (buffer, DEF_SIZE);
139 }
140
141 static int
find(str,what)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
write_buffer(buffer,f)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
delete_string(buffer)170 delete_string (buffer)
171 string_type *buffer;
172 {
173 free (buffer->ptr);
174 }
175
176 static char *
addr(buffer,idx)177 addr (buffer, idx)
178 string_type *buffer;
179 unsigned int idx;
180 {
181 return buffer->ptr + idx;
182 }
183
184 static char
at(buffer,pos)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
catchar(buffer,ch)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
overwrite_string(dst,src)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
catbuf(buffer,buf,len)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
cattext(buffer,string)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
catstr(dst,src)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
skip_white_and_stars(src,idx)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
skip_past_newline_1(ptr,idx)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
die(msg)310 die (msg)
311 char *msg;
312 {
313 fprintf (stderr, "%s\n", msg);
314 exit (1);
315 }
316
317 static void
check_range()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
icheck_range()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
exec(word)372 exec (word)
373 dict_type *word;
374 {
375 pc = word->code;
376 while (*pc)
377 (*pc) ();
378 }
379
380 static void
call()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
remchar()391 remchar ()
392 {
393 if (tos->write_idx)
394 tos->write_idx--;
395 pc++;
396 }
397
398 static void
strip_trailing_newlines()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
push_number()409 push_number ()
410 {
411 isp++;
412 icheck_range ();
413 pc++;
414 *isp = (long) (*pc);
415 pc++;
416 }
417
418 static void
push_text()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
remove_noncomments(src,dst)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
print_stack_level()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
paramstuff()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
translatecomments()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
outputdots()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
courierize()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
bulletize()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
do_fancy_stuff()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
iscommand(ptr,idx)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
copy_past_newline(ptr,idx,dst)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
icopy_past_newline()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
kill_bogus_lines()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
indent()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
get_stuff_in_command()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
swap()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
other_dup()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
drop()1087 drop ()
1088 {
1089 tos--;
1090 check_range ();
1091 pc++;
1092 }
1093
1094 static void
idrop()1095 idrop ()
1096 {
1097 isp--;
1098 icheck_range ();
1099 pc++;
1100 }
1101
1102 static void
icatstr()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
skip_past_newline()1113 skip_past_newline ()
1114 {
1115 idx = skip_past_newline_1 (ptr, idx);
1116 pc++;
1117 }
1118
1119 static void
internalmode()1120 internalmode ()
1121 {
1122 internal_mode = *(isp);
1123 isp--;
1124 icheck_range ();
1125 pc++;
1126 }
1127
1128 static void
maybecatstr()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 *
nextword(string,word)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 *
lookup_word(word)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
perform()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 *
newentry(word)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
add_to_definition(entry,word)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
add_intrinsic(name,func)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
add_var(name)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
compile(string)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
bang()1398 bang ()
1399 {
1400 *(long *) ((isp[0])) = isp[-1];
1401 isp -= 2;
1402 icheck_range ();
1403 pc++;
1404 }
1405
1406 static void
atsign()1407 atsign ()
1408 {
1409 isp[0] = *(long *) (isp[0]);
1410 pc++;
1411 }
1412
1413 static void
hello()1414 hello ()
1415 {
1416 printf ("hello\n");
1417 pc++;
1418 }
1419
1420 static void
stdout_()1421 stdout_ ()
1422 {
1423 isp++;
1424 icheck_range ();
1425 *isp = 1;
1426 pc++;
1427 }
1428
1429 static void
stderr_()1430 stderr_ ()
1431 {
1432 isp++;
1433 icheck_range ();
1434 *isp = 2;
1435 pc++;
1436 }
1437
1438 static void
print()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
read_in(str,file)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
usage()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
chew_exit()1485 chew_exit ()
1486 {
1487 exit (0);
1488 }
1489
1490 int
main(ac,av)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