xref: /netbsd-src/external/gpl3/binutils/dist/bfd/doc/chew.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
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
die(char * msg)146 die (char *msg)
147 {
148   fprintf (stderr, "%s\n", msg);
149   exit (1);
150 }
151 
152 void *
xmalloc(size_t size)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 *
xrealloc(void * oldmem,size_t size)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 *
xstrdup(const char * s)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
init_string_with_size(string_type * buffer,unsigned int size)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
init_string(string_type * buffer)200 init_string (string_type *buffer)
201 {
202   init_string_with_size (buffer, DEF_SIZE);
203 }
204 
205 static int
find(string_type * str,char * what)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
write_buffer(string_type * buffer,FILE * f)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
delete_string(string_type * buffer)230 delete_string (string_type *buffer)
231 {
232   free (buffer->ptr);
233   buffer->ptr = NULL;
234 }
235 
236 static char *
addr(string_type * buffer,unsigned int idx)237 addr (string_type *buffer, unsigned int idx)
238 {
239   return buffer->ptr + idx;
240 }
241 
242 static char
at(string_type * buffer,unsigned int pos)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
catchar(string_type * buffer,int ch)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
overwrite_string(string_type * dst,string_type * src)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
catbuf(string_type * buffer,char * buf,unsigned int len)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
cattext(string_type * buffer,char * string)285 cattext (string_type *buffer, char *string)
286 {
287   catbuf (buffer, string, (unsigned int) strlen (string));
288 }
289 
290 static void
catstr(string_type * dst,string_type * src)291 catstr (string_type *dst, string_type *src)
292 {
293   catbuf (dst, src->ptr, src->write_idx);
294 }
295 
296 static unsigned int
skip_white_and_stars(string_type * src,unsigned int idx)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
skip_past_newline_1(string_type * ptr,unsigned int idx)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
check_range(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
icheck_range(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
drop(void)341 drop (void)
342 {
343   tos--;
344   check_range ();
345   delete_string (tos + 1);
346   pc++;
347 }
348 
349 static void
idrop(void)350 idrop (void)
351 {
352   isp--;
353   icheck_range ();
354   pc++;
355 }
356 
357 static void
exec(dict_type * word)358 exec (dict_type *word)
359 {
360   pc = word->code;
361   while (pc->f)
362     pc->f ();
363 }
364 
365 static void
call(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
remchar(void)375 remchar (void)
376 {
377   if (tos->write_idx)
378     tos->write_idx--;
379   pc++;
380 }
381 
382 static void
strip_trailing_newlines(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
push_number(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
push_variable(void)405 push_variable (void)
406 {
407   push_number ();
408 }
409 
410 static void
push_text(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
remove_noncomments(string_type * src,string_type * dst)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
print_stack_level(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
translatecomments(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
wrap_comment(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   pc++;
546 }
547 
548 /* Mod tos so that only lines with leading dots remain */
549 static void
outputdots(void)550 outputdots (void)
551 {
552   unsigned int idx = 0;
553   string_type out;
554   init_string (&out);
555 
556   while (at (tos, idx))
557     {
558       /* Every iteration begins at the start of a line.  */
559       if (at (tos, idx) == '.')
560 	{
561 	  char c;
562 	  int spaces;
563 
564 	  idx++;
565 	  spaces = 0;
566 	  while ((c = at (tos, idx)) && c != '\n')
567 	    {
568 	      if (spaces >= 0)
569 		{
570 		  if (c == ' ')
571 		    {
572 		      spaces++;
573 		      idx++;
574 		      continue;
575 		    }
576 		  else
577 		    {
578 		      while (spaces >= 8)
579 			{
580 			  catchar (&out, '\t');
581 			  spaces -= 8;
582 			}
583 		      while (spaces-- > 0)
584 			catchar (&out, ' ');
585 		    }
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 static void
kill_bogus_lines(void)863 kill_bogus_lines (void)
864 {
865   int sl;
866 
867   int idx = 0;
868   int c;
869   int dot = 0;
870 
871   string_type out;
872   init_string (&out);
873   /* Drop leading nl.  */
874   while (at (tos, idx) == '\n')
875     {
876       idx++;
877     }
878   c = idx;
879 
880   /* If the first char is a '.' prepend a newline so that it is
881      recognized properly later.  */
882   if (at (tos, idx) == '.')
883     catchar (&out, '\n');
884 
885   /* Find the last char.  */
886   while (at (tos, idx))
887     {
888       idx++;
889     }
890 
891   /* Find the last non white before the nl.  */
892   idx--;
893 
894   while (idx && isspace ((unsigned char) at (tos, idx)))
895     idx--;
896   idx++;
897 
898   /* Copy buffer upto last char, but blank lines before and after
899      dots don't count.  */
900   sl = 1;
901 
902   while (c < idx)
903     {
904       if (at (tos, c) == '\n'
905 	  && at (tos, c + 1) == '\n'
906 	  && at (tos, c + 2) == '.')
907 	{
908 	  /* Ignore two newlines before a dot.  */
909 	  c++;
910 	}
911       else if (at (tos, c) == '.' && sl)
912 	{
913 	  /* remember that this line started with a dot.  */
914 	  dot = 2;
915 	}
916       else if (at (tos, c) == '\n'
917 	       && at (tos, c + 1) == '\n'
918 	       && dot)
919 	{
920 	  c++;
921 	  /* Ignore two newlines when last line was dot.  */
922 	}
923 
924       catchar (&out, at (tos, c));
925       if (at (tos, c) == '\n')
926 	{
927 	  sl = 1;
928 
929 	  if (dot == 2)
930 	    dot = 1;
931 	  else
932 	    dot = 0;
933 	}
934       else
935 	sl = 0;
936 
937       c++;
938 
939     }
940 
941   /* Append nl.  */
942   catchar (&out, '\n');
943   pc++;
944   delete_string (tos);
945   *tos = out;
946 
947 }
948 
949 static void
collapse_whitespace(void)950 collapse_whitespace (void)
951 {
952   int last_was_ws = 0;
953   int idx;
954 
955   string_type out;
956   init_string (&out);
957 
958   for (idx = 0; at (tos, idx) != 0; ++idx)
959     {
960       char c = at (tos, idx);
961       if (isspace (c))
962 	{
963 	  if (!last_was_ws)
964 	    {
965 	      catchar (&out, ' ');
966 	      last_was_ws = 1;
967 	    }
968 	}
969       else
970 	{
971 	  catchar (&out, c);
972 	  last_was_ws = 0;
973 	}
974     }
975 
976   pc++;
977   delete_string (tos);
978   *tos = out;
979 }
980 
981 /* indent
982    Take the string at the top of the stack, do some prettying.  */
983 
984 static void
indent(void)985 indent (void)
986 {
987   string_type out;
988   int tab = 0;
989   int idx = 0;
990   int ol = 0;
991   init_string (&out);
992   while (at (tos, idx))
993     {
994       switch (at (tos, idx))
995 	{
996 	case '\n':
997 	  catchar (&out, '\n');
998 	  idx++;
999 	  if (tab && at (tos, idx))
1000 	    {
1001 	      int i;
1002 	      for (i = 0; i < tab - 1; i += 2)
1003 		catchar (&out, '\t');
1004 	      if (i < tab)
1005 		cattext (&out, "    ");
1006 	    }
1007 	  ol = 0;
1008 	  break;
1009 	case '(':
1010 	  if (ol == 0)
1011 	    {
1012 	      int i;
1013 	      for (i = 1; i < tab - 1; i += 2)
1014 		catchar (&out, '\t');
1015 	      if (i < tab)
1016 		cattext (&out, "    ");
1017 	      cattext (&out, "   ");
1018 	    }
1019 	  tab++;
1020 	  idx++;
1021 	  catchar (&out, '(');
1022 	  ol = 1;
1023 	  break;
1024 	case ')':
1025 	  tab--;
1026 	  catchar (&out, ')');
1027 	  idx++;
1028 	  ol = 1;
1029 	  break;
1030 	default:
1031 	  catchar (&out, at (tos, idx));
1032 	  ol = 1;
1033 	  idx++;
1034 	  break;
1035 	}
1036     }
1037 
1038   pc++;
1039   delete_string (tos);
1040   *tos = out;
1041 
1042 }
1043 
1044 static void
get_stuff_in_command(void)1045 get_stuff_in_command (void)
1046 {
1047   tos++;
1048   check_range ();
1049   init_string (tos);
1050 
1051   while (at (ptr, idx))
1052     {
1053       if (iscommand (ptr, idx))
1054 	break;
1055       idx = copy_past_newline (ptr, idx, tos);
1056     }
1057   pc++;
1058 }
1059 
1060 static void
swap(void)1061 swap (void)
1062 {
1063   string_type t;
1064 
1065   t = tos[0];
1066   tos[0] = tos[-1];
1067   tos[-1] = t;
1068   pc++;
1069 }
1070 
1071 static void
other_dup(void)1072 other_dup (void)
1073 {
1074   tos++;
1075   check_range ();
1076   init_string (tos);
1077   catstr (tos, tos - 1);
1078   pc++;
1079 }
1080 
1081 static void
icatstr(void)1082 icatstr (void)
1083 {
1084   tos--;
1085   check_range ();
1086   catstr (tos, tos + 1);
1087   delete_string (tos + 1);
1088   pc++;
1089 }
1090 
1091 static void
skip_past_newline(void)1092 skip_past_newline (void)
1093 {
1094   idx = skip_past_newline_1 (ptr, idx);
1095   pc++;
1096 }
1097 
1098 static void
maybecatstr(void)1099 maybecatstr (void)
1100 {
1101   if (internal_wanted == *internal_mode)
1102     {
1103       catstr (tos - 1, tos);
1104     }
1105   delete_string (tos);
1106   tos--;
1107   check_range ();
1108   pc++;
1109 }
1110 
1111 static void
catstrif(void)1112 catstrif (void)
1113 {
1114   int cond = isp[0];
1115   isp--;
1116   icheck_range ();
1117   if (cond)
1118     catstr (tos - 1, tos);
1119   delete_string (tos);
1120   tos--;
1121   check_range ();
1122   pc++;
1123 }
1124 
1125 char *
nextword(char * string,char ** word)1126 nextword (char *string, char **word)
1127 {
1128   char *word_start;
1129   int idx;
1130   char *dst;
1131   char *src;
1132 
1133   int length = 0;
1134 
1135   while (isspace ((unsigned char) *string) || *string == '-')
1136     {
1137       if (*string == '-')
1138 	{
1139 	  while (*string && *string != '\n')
1140 	    string++;
1141 
1142 	}
1143       else
1144 	{
1145 	  string++;
1146 	}
1147     }
1148   if (!*string)
1149     {
1150       *word = NULL;
1151       return NULL;
1152     }
1153 
1154   word_start = string;
1155   if (*string == '"')
1156     {
1157       do
1158 	{
1159 	  string++;
1160 	  length++;
1161 	  if (*string == '\\')
1162 	    {
1163 	      string += 2;
1164 	      length += 2;
1165 	    }
1166 	}
1167       while (*string != '"');
1168     }
1169   else
1170     {
1171       while (!isspace ((unsigned char) *string))
1172 	{
1173 	  string++;
1174 	  length++;
1175 
1176 	}
1177     }
1178 
1179   *word = xmalloc (length + 1);
1180 
1181   dst = *word;
1182   src = word_start;
1183 
1184   for (idx = 0; idx < length; idx++)
1185     {
1186       if (src[idx] == '\\')
1187 	switch (src[idx + 1])
1188 	  {
1189 	  case 'n':
1190 	    *dst++ = '\n';
1191 	    idx++;
1192 	    break;
1193 	  case '"':
1194 	  case '\\':
1195 	    *dst++ = src[idx + 1];
1196 	    idx++;
1197 	    break;
1198 	  default:
1199 	    *dst++ = '\\';
1200 	    break;
1201 	  }
1202       else
1203 	*dst++ = src[idx];
1204     }
1205   *dst++ = 0;
1206 
1207   if (*string)
1208     return string + 1;
1209   else
1210     return NULL;
1211 }
1212 
1213 dict_type *
lookup_word(char * word)1214 lookup_word (char *word)
1215 {
1216   dict_type *ptr = root;
1217   while (ptr)
1218     {
1219       if (strcmp (ptr->word, word) == 0)
1220 	return ptr;
1221       ptr = ptr->next;
1222     }
1223   if (warning)
1224     fprintf (stderr, "Can't find %s\n", word);
1225   return NULL;
1226 }
1227 
1228 static void
free_words(void)1229 free_words (void)
1230 {
1231   dict_type *ptr = root;
1232 
1233   while (ptr)
1234     {
1235       dict_type *next;
1236 
1237       free (ptr->word);
1238       if (ptr->code)
1239 	{
1240 	  int i;
1241 	  for (i = 0; i < ptr->code_end - 1; i ++)
1242 	    if (ptr->code[i].f == push_text
1243 		&& ptr->code[i + 1].s)
1244 	      {
1245 		free (ptr->code[i + 1].s - 1);
1246 		++i;
1247 	      }
1248 	    else if (ptr->code[i].f == push_variable)
1249 	      {
1250 		free ((void *) ptr->code[i + 1].l);
1251 		++i;
1252 	      }
1253 	  free (ptr->code);
1254 	}
1255       next = ptr->next;
1256       free (ptr);
1257       ptr = next;
1258     }
1259 }
1260 
1261 static void
perform(void)1262 perform (void)
1263 {
1264   tos = stack;
1265 
1266   while (at (ptr, idx))
1267     {
1268       /* It's worth looking through the command list.  */
1269       if (iscommand (ptr, idx))
1270 	{
1271 	  char *next;
1272 	  dict_type *word;
1273 
1274 	  (void) nextword (addr (ptr, idx), &next);
1275 
1276 	  word = lookup_word (next);
1277 
1278 	  if (word)
1279 	    {
1280 	      exec (word);
1281 	    }
1282 	  else
1283 	    {
1284 	      if (warning)
1285 		fprintf (stderr, "warning, %s is not recognised\n", next);
1286 	      idx = skip_past_newline_1 (ptr, idx);
1287 	    }
1288 	  free (next);
1289 	}
1290       else
1291 	idx = skip_past_newline_1 (ptr, idx);
1292     }
1293 }
1294 
1295 dict_type *
newentry(char * word)1296 newentry (char *word)
1297 {
1298   dict_type *new_d = xmalloc (sizeof (*new_d));
1299   new_d->word = word;
1300   new_d->next = root;
1301   root = new_d;
1302   new_d->code = xmalloc (sizeof (*new_d->code));
1303   new_d->code_length = 1;
1304   new_d->code_end = 0;
1305   return new_d;
1306 }
1307 
1308 unsigned int
add_to_definition(dict_type * entry,pcu word)1309 add_to_definition (dict_type *entry, pcu word)
1310 {
1311   if (entry->code_end == entry->code_length)
1312     {
1313       entry->code_length += 2;
1314       entry->code = xrealloc (entry->code,
1315 			      entry->code_length * sizeof (*entry->code));
1316     }
1317   entry->code[entry->code_end] = word;
1318 
1319   return entry->code_end++;
1320 }
1321 
1322 void
add_intrinsic(char * name,void (* func)(void))1323 add_intrinsic (char *name, void (*func) (void))
1324 {
1325   dict_type *new_d = newentry (xstrdup (name));
1326   pcu p = { func };
1327   add_to_definition (new_d, p);
1328   p.f = 0;
1329   add_to_definition (new_d, p);
1330 }
1331 
1332 static void
add_variable(char * name,intptr_t * loc)1333 add_variable (char *name, intptr_t *loc)
1334 {
1335   dict_type *new_d = newentry (name);
1336   pcu p = { push_variable };
1337   add_to_definition (new_d, p);
1338   p.l = (intptr_t) loc;
1339   add_to_definition (new_d, p);
1340   p.f = 0;
1341   add_to_definition (new_d, p);
1342 }
1343 
1344 static void
add_intrinsic_variable(const char * name,intptr_t * loc)1345 add_intrinsic_variable (const char *name, intptr_t *loc)
1346 {
1347   add_variable (xstrdup (name), loc);
1348 }
1349 
1350 void
compile(char * string)1351 compile (char *string)
1352 {
1353   /* Add words to the dictionary.  */
1354   char *word;
1355 
1356   string = nextword (string, &word);
1357   while (string && *string && word[0])
1358     {
1359       if (word[0] == ':')
1360 	{
1361 	  dict_type *ptr;
1362 	  pcu p;
1363 
1364 	  /* Compile a word and add to dictionary.  */
1365 	  free (word);
1366 	  string = nextword (string, &word);
1367 	  if (!string)
1368 	    continue;
1369 	  ptr = newentry (word);
1370 	  string = nextword (string, &word);
1371 	  if (!string)
1372 	    {
1373 	      free (ptr->code);
1374 	      free (ptr);
1375 	      continue;
1376 	    }
1377 
1378 	  while (word[0] != ';')
1379 	    {
1380 	      switch (word[0])
1381 		{
1382 		case '"':
1383 		  /* got a string, embed magic push string
1384 		     function */
1385 		  p.f = push_text;
1386 		  add_to_definition (ptr, p);
1387 		  p.s = word + 1;
1388 		  add_to_definition (ptr, p);
1389 		  break;
1390 		case '0':
1391 		case '1':
1392 		case '2':
1393 		case '3':
1394 		case '4':
1395 		case '5':
1396 		case '6':
1397 		case '7':
1398 		case '8':
1399 		case '9':
1400 		  /* Got a number, embedd the magic push number
1401 		     function */
1402 		  p.f = push_number;
1403 		  add_to_definition (ptr, p);
1404 		  p.l = atol (word);
1405 		  add_to_definition (ptr, p);
1406 		  free (word);
1407 		  break;
1408 		default:
1409 		  p.f = call;
1410 		  add_to_definition (ptr, p);
1411 		  p.e = lookup_word (word);
1412 		  add_to_definition (ptr, p);
1413 		  free (word);
1414 		}
1415 
1416 	      string = nextword (string, &word);
1417 	    }
1418 	  p.f = 0;
1419 	  add_to_definition (ptr, p);
1420 	  free (word);
1421 	  string = nextword (string, &word);
1422 	}
1423       else if (strcmp (word, "variable") == 0)
1424 	{
1425 	  free (word);
1426 	  string = nextword (string, &word);
1427 	  if (!string)
1428 	    continue;
1429 	  intptr_t *loc = xmalloc (sizeof (intptr_t));
1430 	  *loc = 0;
1431 	  add_variable (word, loc);
1432 	  string = nextword (string, &word);
1433 	}
1434       else
1435 	{
1436 	  fprintf (stderr, "syntax error at %s\n", string - 1);
1437 	}
1438     }
1439   free (word);
1440 }
1441 
1442 static void
bang(void)1443 bang (void)
1444 {
1445   *(intptr_t *) ((isp[0])) = isp[-1];
1446   isp -= 2;
1447   icheck_range ();
1448   pc++;
1449 }
1450 
1451 static void
atsign(void)1452 atsign (void)
1453 {
1454   isp[0] = *(intptr_t *) (isp[0]);
1455   pc++;
1456 }
1457 
1458 static void
hello(void)1459 hello (void)
1460 {
1461   printf ("hello\n");
1462   pc++;
1463 }
1464 
1465 static void
stdout_(void)1466 stdout_ (void)
1467 {
1468   isp++;
1469   icheck_range ();
1470   *isp = 1;
1471   pc++;
1472 }
1473 
1474 static void
stderr_(void)1475 stderr_ (void)
1476 {
1477   isp++;
1478   icheck_range ();
1479   *isp = 2;
1480   pc++;
1481 }
1482 
1483 static void
print(void)1484 print (void)
1485 {
1486   if (*isp == 1)
1487     write_buffer (tos, stdout);
1488   else if (*isp == 2)
1489     write_buffer (tos, stderr);
1490   else
1491     fprintf (stderr, "print: illegal print destination `%" PRIdPTR "'\n", *isp);
1492   isp--;
1493   tos--;
1494   icheck_range ();
1495   check_range ();
1496   pc++;
1497 }
1498 
1499 static void
read_in(string_type * str,FILE * file)1500 read_in (string_type *str, FILE *file)
1501 {
1502   char buff[10000];
1503   unsigned int r;
1504   do
1505     {
1506       r = fread (buff, 1, sizeof (buff), file);
1507       catbuf (str, buff, r);
1508     }
1509   while (r);
1510   buff[0] = 0;
1511 
1512   catbuf (str, buff, 1);
1513 }
1514 
1515 static void
usage(void)1516 usage (void)
1517 {
1518   fprintf (stderr, "usage: -[d|i|g] <file >file\n");
1519   exit (33);
1520 }
1521 
1522 /* There is no reliable way to declare exit.  Sometimes it returns
1523    int, and sometimes it returns void.  Sometimes it changes between
1524    OS releases.  Trying to get it declared correctly in the hosts file
1525    is a pointless waste of time.  */
1526 
1527 static void
chew_exit(void)1528 chew_exit (void)
1529 {
1530   exit (0);
1531 }
1532 
1533 int
main(int ac,char * av[])1534 main (int ac, char *av[])
1535 {
1536   unsigned int i;
1537   string_type buffer;
1538   string_type pptr;
1539 
1540   init_string (&buffer);
1541   init_string (&pptr);
1542   init_string (stack + 0);
1543   tos = stack + 1;
1544   ptr = &pptr;
1545 
1546   add_intrinsic ("push_text", push_text);
1547   add_intrinsic ("!", bang);
1548   add_intrinsic ("@", atsign);
1549   add_intrinsic ("hello", hello);
1550   add_intrinsic ("stdout", stdout_);
1551   add_intrinsic ("stderr", stderr_);
1552   add_intrinsic ("print", print);
1553   add_intrinsic ("skip_past_newline", skip_past_newline);
1554   add_intrinsic ("catstr", icatstr);
1555   add_intrinsic ("copy_past_newline", icopy_past_newline);
1556   add_intrinsic ("dup", other_dup);
1557   add_intrinsic ("drop", drop);
1558   add_intrinsic ("idrop", idrop);
1559   add_intrinsic ("remchar", remchar);
1560   add_intrinsic ("get_stuff_in_command", get_stuff_in_command);
1561   add_intrinsic ("do_fancy_stuff", do_fancy_stuff);
1562   add_intrinsic ("bulletize", bulletize);
1563   add_intrinsic ("courierize", courierize);
1564   /* If the following line gives an error, exit() is not declared in the
1565      ../hosts/foo.h file for this host.  Fix it there, not here!  */
1566   /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor.  */
1567   add_intrinsic ("exit", chew_exit);
1568   add_intrinsic ("swap", swap);
1569   add_intrinsic ("outputdots", outputdots);
1570   add_intrinsic ("maybecatstr", maybecatstr);
1571   add_intrinsic ("catstrif", catstrif);
1572   add_intrinsic ("translatecomments", translatecomments);
1573   add_intrinsic ("wrap_comment", wrap_comment);
1574   add_intrinsic ("kill_bogus_lines", kill_bogus_lines);
1575   add_intrinsic ("indent", indent);
1576   add_intrinsic ("print_stack_level", print_stack_level);
1577   add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines);
1578   add_intrinsic ("collapse_whitespace", collapse_whitespace);
1579 
1580   internal_mode = xmalloc (sizeof (intptr_t));
1581   *internal_mode = 0;
1582   add_intrinsic_variable ("internalmode", internal_mode);
1583 
1584   /* Put a nl at the start.  */
1585   catchar (&buffer, '\n');
1586 
1587   read_in (&buffer, stdin);
1588   remove_noncomments (&buffer, ptr);
1589   for (i = 1; i < (unsigned int) ac; i++)
1590     {
1591       if (av[i][0] == '-')
1592 	{
1593 	  if (av[i][1] == 'f')
1594 	    {
1595 	      string_type b;
1596 	      FILE *f;
1597 	      init_string (&b);
1598 
1599 	      f = fopen (av[i + 1], "r");
1600 	      if (!f)
1601 		{
1602 		  fprintf (stderr, "Can't open the input file %s\n",
1603 			   av[i + 1]);
1604 		  return 33;
1605 		}
1606 
1607 	      read_in (&b, f);
1608 	      compile (b.ptr);
1609 	      perform ();
1610 	      delete_string (&b);
1611 	    }
1612 	  else if (av[i][1] == 'i')
1613 	    {
1614 	      internal_wanted = 1;
1615 	    }
1616 	  else if (av[i][1] == 'w')
1617 	    {
1618 	      warning = 1;
1619 	    }
1620 	  else
1621 	    usage ();
1622 	}
1623     }
1624   write_buffer (stack + 0, stdout);
1625   free_words ();
1626   delete_string (&pptr);
1627   delete_string (&buffer);
1628   if (tos != stack)
1629     {
1630       fprintf (stderr, "finishing with current stack level %ld\n",
1631 	       (long) (tos - stack));
1632       return 1;
1633     }
1634   return 0;
1635 }
1636