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