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
95typedef 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
104typedef union
105{
106  void (*f) (void);
107  struct dict_struct *e;
108  char *s;
109  long l;
110} pcu;
111
112typedef 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
121int internal_wanted;
122int internal_mode;
123
124int warning;
125
126string_type stack[STACK];
127string_type *tos;
128
129unsigned int idx = 0; /* Pos in input buffer */
130string_type *ptr; /* and the buffer */
131
132long istack[STACK];
133long *isp = &istack[0];
134
135dict_type *root;
136
137pcu *pc;
138
139static void
140die (char *msg)
141{
142  fprintf (stderr, "%s\n", msg);
143  exit (1);
144}
145
146void *
147xmalloc (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
160void *
161xrealloc (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
177char *
178xstrdup (const char *s)
179{
180  size_t len = strlen (s) + 1;
181  char *ret = xmalloc (len);
182  return memcpy (ret, s, len);
183}
184
185static void
186init_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
193static void
194init_string (string_type *buffer)
195{
196  init_string_with_size (buffer, DEF_SIZE);
197}
198
199static int
200find (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
215static void
216write_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
223static void
224delete_string (string_type *buffer)
225{
226  free (buffer->ptr);
227  buffer->ptr = NULL;
228}
229
230static char *
231addr (string_type *buffer, unsigned int idx)
232{
233  return buffer->ptr + idx;
234}
235
236static char
237at (string_type *buffer, unsigned int pos)
238{
239  if (pos >= buffer->write_idx)
240    return 0;
241  return buffer->ptr[pos];
242}
243
244static void
245catchar (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
256static void
257overwrite_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
265static void
266catbuf (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
278static void
279cattext (string_type *buffer, char *string)
280{
281  catbuf (buffer, string, (unsigned int) strlen (string));
282}
283
284static void
285catstr (string_type *dst, string_type *src)
286{
287  catbuf (dst, src->ptr, src->write_idx);
288}
289
290static unsigned int
291skip_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
305static unsigned int
306skip_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
316static void
317check_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
325static void
326icheck_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
334static void
335exec (dict_type *word)
336{
337  pc = word->code;
338  while (pc->f)
339    pc->f ();
340}
341
342static void
343call (void)
344{
345  pcu *oldpc = pc;
346  dict_type *e = pc[1].e;
347  exec (e);
348  pc = oldpc + 2;
349}
350
351static void
352remchar (void)
353{
354  if (tos->write_idx)
355    tos->write_idx--;
356  pc++;
357}
358
359static void
360strip_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
369static void
370push_number (void)
371{
372  isp++;
373  icheck_range ();
374  pc++;
375  *isp = pc->l;
376  pc++;
377}
378
379static void
380push_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
395static void
396remove_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
446static void
447print_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
464static void
465paramstuff (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
537static void
538translatecomments (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 */
569static void
570outputdots (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 */
618static void
619courierize (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
705static void
706bulletize (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
760static void
761do_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
799static int
800iscommand (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
823static int
824copy_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
852static void
853icopy_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
865static void
866kill_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
952static void
953indent (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
1003static void
1004get_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
1019static void
1020swap (void)
1021{
1022  string_type t;
1023
1024  t = tos[0];
1025  tos[0] = tos[-1];
1026  tos[-1] = t;
1027  pc++;
1028}
1029
1030static void
1031other_dup (void)
1032{
1033  tos++;
1034  check_range ();
1035  init_string (tos);
1036  catstr (tos, tos - 1);
1037  pc++;
1038}
1039
1040static void
1041drop (void)
1042{
1043  tos--;
1044  check_range ();
1045  delete_string (tos + 1);
1046  pc++;
1047}
1048
1049static void
1050idrop (void)
1051{
1052  isp--;
1053  icheck_range ();
1054  pc++;
1055}
1056
1057static void
1058icatstr (void)
1059{
1060  tos--;
1061  check_range ();
1062  catstr (tos, tos + 1);
1063  delete_string (tos + 1);
1064  pc++;
1065}
1066
1067static void
1068skip_past_newline (void)
1069{
1070  idx = skip_past_newline_1 (ptr, idx);
1071  pc++;
1072}
1073
1074static void
1075internalmode (void)
1076{
1077  internal_mode = *(isp);
1078  isp--;
1079  icheck_range ();
1080  pc++;
1081}
1082
1083static void
1084maybecatstr (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
1096char *
1097nextword (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
1184dict_type *
1185lookup_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
1199static void
1200free_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
1227static void
1228perform (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
1261dict_type *
1262newentry (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
1274unsigned int
1275add_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
1288void
1289add_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
1298void
1299compile (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
1379static void
1380bang (void)
1381{
1382  *(long *) ((isp[0])) = isp[-1];
1383  isp -= 2;
1384  icheck_range ();
1385  pc++;
1386}
1387
1388static void
1389atsign (void)
1390{
1391  isp[0] = *(long *) (isp[0]);
1392  pc++;
1393}
1394
1395static void
1396hello (void)
1397{
1398  printf ("hello\n");
1399  pc++;
1400}
1401
1402static void
1403stdout_ (void)
1404{
1405  isp++;
1406  icheck_range ();
1407  *isp = 1;
1408  pc++;
1409}
1410
1411static void
1412stderr_ (void)
1413{
1414  isp++;
1415  icheck_range ();
1416  *isp = 2;
1417  pc++;
1418}
1419
1420static void
1421print (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
1436static void
1437read_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
1452static void
1453usage (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
1464static void
1465chew_exit (void)
1466{
1467  exit (0);
1468}
1469
1470int
1471main (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