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