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