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