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