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