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