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