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