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