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