1 /* chew
2 Copyright (C) 1990-2022 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 paramstuff - convert full declaration into "PARAMS" form if not already
69 maybecatstr - do catstr if internal_mode == internal_wanted, discard
70 value in any case
71 translatecomments - turn {* and *} into comment delimiters
72 kill_bogus_lines - get rid of extra newlines
73 indent
74 internalmode - pop from integer stack, set `internalmode' to that value
75 print_stack_level - print current stack depth to stderr
76 strip_trailing_newlines - go ahead, guess...
77 [quoted string] - push string onto string stack
78 [word starting with digit] - push atol(str) onto integer stack
79
80 A command must be all upper-case, and alone on a line.
81
82 Foo. */
83
84 #include <assert.h>
85 #include <stdio.h>
86 #include <ctype.h>
87 #include <stdlib.h>
88 #include <string.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 long 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 int 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 long istack[STACK];
133 long *isp = &istack[0];
134
135 dict_type *root;
136
137 pcu *pc;
138
139 static void
die(char * msg)140 die (char *msg)
141 {
142 fprintf (stderr, "%s\n", msg);
143 exit (1);
144 }
145
146 void *
xmalloc(size_t size)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 *
xrealloc(void * oldmem,size_t size)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 *
xstrdup(const char * s)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
init_string_with_size(string_type * buffer,unsigned int size)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
init_string(string_type * buffer)194 init_string (string_type *buffer)
195 {
196 init_string_with_size (buffer, DEF_SIZE);
197 }
198
199 static int
find(string_type * str,char * what)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
write_buffer(string_type * buffer,FILE * f)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
delete_string(string_type * buffer)224 delete_string (string_type *buffer)
225 {
226 free (buffer->ptr);
227 buffer->ptr = NULL;
228 }
229
230 static char *
addr(string_type * buffer,unsigned int idx)231 addr (string_type *buffer, unsigned int idx)
232 {
233 return buffer->ptr + idx;
234 }
235
236 static char
at(string_type * buffer,unsigned int pos)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
catchar(string_type * buffer,int ch)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
overwrite_string(string_type * dst,string_type * src)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
catbuf(string_type * buffer,char * buf,unsigned int len)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
cattext(string_type * buffer,char * string)279 cattext (string_type *buffer, char *string)
280 {
281 catbuf (buffer, string, (unsigned int) strlen (string));
282 }
283
284 static void
catstr(string_type * dst,string_type * src)285 catstr (string_type *dst, string_type *src)
286 {
287 catbuf (dst, src->ptr, src->write_idx);
288 }
289
290 static unsigned int
skip_white_and_stars(string_type * src,unsigned int idx)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
skip_past_newline_1(string_type * ptr,unsigned int idx)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
check_range(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
icheck_range(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
exec(dict_type * word)335 exec (dict_type *word)
336 {
337 pc = word->code;
338 while (pc->f)
339 pc->f ();
340 }
341
342 static void
call(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
remchar(void)352 remchar (void)
353 {
354 if (tos->write_idx)
355 tos->write_idx--;
356 pc++;
357 }
358
359 static void
strip_trailing_newlines(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
push_number(void)370 push_number (void)
371 {
372 isp++;
373 icheck_range ();
374 pc++;
375 *isp = pc->l;
376 pc++;
377 }
378
379 static void
push_text(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
remove_noncomments(string_type * src,string_type * dst)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
print_stack_level(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 foobar name(stuff);
458 into:
459 foobar
460 name PARAMS ((stuff));
461 and a blank line.
462 */
463
464 static void
paramstuff(void)465 paramstuff (void)
466 {
467 unsigned int openp;
468 unsigned int fname;
469 unsigned int idx;
470 unsigned int len;
471 string_type out;
472 init_string (&out);
473
474 #define NO_PARAMS 1
475
476 /* Make sure that it's not already param'd or proto'd. */
477 if (NO_PARAMS
478 || find (tos, "PARAMS") || find (tos, "PROTO") || !find (tos, "("))
479 {
480 catstr (&out, tos);
481 }
482 else
483 {
484 /* Find the open paren. */
485 for (openp = 0; at (tos, openp) != '(' && at (tos, openp); openp++)
486 ;
487
488 fname = openp;
489 /* Step back to the fname. */
490 fname--;
491 while (fname && isspace ((unsigned char) at (tos, fname)))
492 fname--;
493 while (fname
494 && !isspace ((unsigned char) at (tos,fname))
495 && at (tos,fname) != '*')
496 fname--;
497
498 fname++;
499
500 /* Output type, omitting trailing whitespace character(s), if
501 any. */
502 for (len = fname; 0 < len; len--)
503 {
504 if (!isspace ((unsigned char) at (tos, len - 1)))
505 break;
506 }
507 for (idx = 0; idx < len; idx++)
508 catchar (&out, at (tos, idx));
509
510 cattext (&out, "\n"); /* Insert a newline between type and fnname */
511
512 /* Output function name, omitting trailing whitespace
513 character(s), if any. */
514 for (len = openp; 0 < len; len--)
515 {
516 if (!isspace ((unsigned char) at (tos, len - 1)))
517 break;
518 }
519 for (idx = fname; idx < len; idx++)
520 catchar (&out, at (tos, idx));
521
522 cattext (&out, " PARAMS (");
523
524 for (idx = openp; at (tos, idx) && at (tos, idx) != ';'; idx++)
525 catchar (&out, at (tos, idx));
526
527 cattext (&out, ");\n\n");
528 }
529 overwrite_string (tos, &out);
530 pc++;
531
532 }
533
534 /* turn {*
535 and *} into comments */
536
537 static void
translatecomments(void)538 translatecomments (void)
539 {
540 unsigned int idx = 0;
541 string_type out;
542 init_string (&out);
543
544 while (at (tos, idx))
545 {
546 if (at (tos, idx) == '{' && at (tos, idx + 1) == '*')
547 {
548 cattext (&out, "/*");
549 idx += 2;
550 }
551 else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}')
552 {
553 cattext (&out, "*/");
554 idx += 2;
555 }
556 else
557 {
558 catchar (&out, at (tos, idx));
559 idx++;
560 }
561 }
562
563 overwrite_string (tos, &out);
564
565 pc++;
566 }
567
568 /* Mod tos so that only lines with leading dots remain */
569 static void
outputdots(void)570 outputdots (void)
571 {
572 unsigned int idx = 0;
573 string_type out;
574 init_string (&out);
575
576 while (at (tos, idx))
577 {
578 /* Every iteration begins at the start of a line. */
579 if (at (tos, idx) == '.')
580 {
581 char c;
582
583 idx++;
584
585 while ((c = at (tos, idx)) && c != '\n')
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 /* indent
863 Take the string at the top of the stack, do some prettying. */
864
865 static void
kill_bogus_lines(void)866 kill_bogus_lines (void)
867 {
868 int sl;
869
870 int idx = 0;
871 int c;
872 int dot = 0;
873
874 string_type out;
875 init_string (&out);
876 /* Drop leading nl. */
877 while (at (tos, idx) == '\n')
878 {
879 idx++;
880 }
881 c = idx;
882
883 /* If the first char is a '.' prepend a newline so that it is
884 recognized properly later. */
885 if (at (tos, idx) == '.')
886 catchar (&out, '\n');
887
888 /* Find the last char. */
889 while (at (tos, idx))
890 {
891 idx++;
892 }
893
894 /* Find the last non white before the nl. */
895 idx--;
896
897 while (idx && isspace ((unsigned char) at (tos, idx)))
898 idx--;
899 idx++;
900
901 /* Copy buffer upto last char, but blank lines before and after
902 dots don't count. */
903 sl = 1;
904
905 while (c < idx)
906 {
907 if (at (tos, c) == '\n'
908 && at (tos, c + 1) == '\n'
909 && at (tos, c + 2) == '.')
910 {
911 /* Ignore two newlines before a dot. */
912 c++;
913 }
914 else if (at (tos, c) == '.' && sl)
915 {
916 /* remember that this line started with a dot. */
917 dot = 2;
918 }
919 else if (at (tos, c) == '\n'
920 && at (tos, c + 1) == '\n'
921 && dot)
922 {
923 c++;
924 /* Ignore two newlines when last line was dot. */
925 }
926
927 catchar (&out, at (tos, c));
928 if (at (tos, c) == '\n')
929 {
930 sl = 1;
931
932 if (dot == 2)
933 dot = 1;
934 else
935 dot = 0;
936 }
937 else
938 sl = 0;
939
940 c++;
941
942 }
943
944 /* Append nl. */
945 catchar (&out, '\n');
946 pc++;
947 delete_string (tos);
948 *tos = out;
949
950 }
951
952 static void
indent(void)953 indent (void)
954 {
955 string_type out;
956 int tab = 0;
957 int idx = 0;
958 int ol = 0;
959 init_string (&out);
960 while (at (tos, idx))
961 {
962 switch (at (tos, idx))
963 {
964 case '\n':
965 cattext (&out, "\n");
966 idx++;
967 if (tab && at (tos, idx))
968 {
969 cattext (&out, " ");
970 }
971 ol = 0;
972 break;
973 case '(':
974 tab++;
975 if (ol == 0)
976 cattext (&out, " ");
977 idx++;
978 cattext (&out, "(");
979 ol = 1;
980 break;
981 case ')':
982 tab--;
983 cattext (&out, ")");
984 idx++;
985 ol = 1;
986
987 break;
988 default:
989 catchar (&out, at (tos, idx));
990 ol = 1;
991
992 idx++;
993 break;
994 }
995 }
996
997 pc++;
998 delete_string (tos);
999 *tos = out;
1000
1001 }
1002
1003 static void
get_stuff_in_command(void)1004 get_stuff_in_command (void)
1005 {
1006 tos++;
1007 check_range ();
1008 init_string (tos);
1009
1010 while (at (ptr, idx))
1011 {
1012 if (iscommand (ptr, idx))
1013 break;
1014 idx = copy_past_newline (ptr, idx, tos);
1015 }
1016 pc++;
1017 }
1018
1019 static void
swap(void)1020 swap (void)
1021 {
1022 string_type t;
1023
1024 t = tos[0];
1025 tos[0] = tos[-1];
1026 tos[-1] = t;
1027 pc++;
1028 }
1029
1030 static void
other_dup(void)1031 other_dup (void)
1032 {
1033 tos++;
1034 check_range ();
1035 init_string (tos);
1036 catstr (tos, tos - 1);
1037 pc++;
1038 }
1039
1040 static void
drop(void)1041 drop (void)
1042 {
1043 tos--;
1044 check_range ();
1045 delete_string (tos + 1);
1046 pc++;
1047 }
1048
1049 static void
idrop(void)1050 idrop (void)
1051 {
1052 isp--;
1053 icheck_range ();
1054 pc++;
1055 }
1056
1057 static void
icatstr(void)1058 icatstr (void)
1059 {
1060 tos--;
1061 check_range ();
1062 catstr (tos, tos + 1);
1063 delete_string (tos + 1);
1064 pc++;
1065 }
1066
1067 static void
skip_past_newline(void)1068 skip_past_newline (void)
1069 {
1070 idx = skip_past_newline_1 (ptr, idx);
1071 pc++;
1072 }
1073
1074 static void
internalmode(void)1075 internalmode (void)
1076 {
1077 internal_mode = *(isp);
1078 isp--;
1079 icheck_range ();
1080 pc++;
1081 }
1082
1083 static void
maybecatstr(void)1084 maybecatstr (void)
1085 {
1086 if (internal_wanted == internal_mode)
1087 {
1088 catstr (tos - 1, tos);
1089 }
1090 delete_string (tos);
1091 tos--;
1092 check_range ();
1093 pc++;
1094 }
1095
1096 char *
nextword(char * string,char ** word)1097 nextword (char *string, char **word)
1098 {
1099 char *word_start;
1100 int idx;
1101 char *dst;
1102 char *src;
1103
1104 int length = 0;
1105
1106 while (isspace ((unsigned char) *string) || *string == '-')
1107 {
1108 if (*string == '-')
1109 {
1110 while (*string && *string != '\n')
1111 string++;
1112
1113 }
1114 else
1115 {
1116 string++;
1117 }
1118 }
1119 if (!*string)
1120 {
1121 *word = NULL;
1122 return NULL;
1123 }
1124
1125 word_start = string;
1126 if (*string == '"')
1127 {
1128 do
1129 {
1130 string++;
1131 length++;
1132 if (*string == '\\')
1133 {
1134 string += 2;
1135 length += 2;
1136 }
1137 }
1138 while (*string != '"');
1139 }
1140 else
1141 {
1142 while (!isspace ((unsigned char) *string))
1143 {
1144 string++;
1145 length++;
1146
1147 }
1148 }
1149
1150 *word = xmalloc (length + 1);
1151
1152 dst = *word;
1153 src = word_start;
1154
1155 for (idx = 0; idx < length; idx++)
1156 {
1157 if (src[idx] == '\\')
1158 switch (src[idx + 1])
1159 {
1160 case 'n':
1161 *dst++ = '\n';
1162 idx++;
1163 break;
1164 case '"':
1165 case '\\':
1166 *dst++ = src[idx + 1];
1167 idx++;
1168 break;
1169 default:
1170 *dst++ = '\\';
1171 break;
1172 }
1173 else
1174 *dst++ = src[idx];
1175 }
1176 *dst++ = 0;
1177
1178 if (*string)
1179 return string + 1;
1180 else
1181 return NULL;
1182 }
1183
1184 dict_type *
lookup_word(char * word)1185 lookup_word (char *word)
1186 {
1187 dict_type *ptr = root;
1188 while (ptr)
1189 {
1190 if (strcmp (ptr->word, word) == 0)
1191 return ptr;
1192 ptr = ptr->next;
1193 }
1194 if (warning)
1195 fprintf (stderr, "Can't find %s\n", word);
1196 return NULL;
1197 }
1198
1199 static void
free_words(void)1200 free_words (void)
1201 {
1202 dict_type *ptr = root;
1203
1204 while (ptr)
1205 {
1206 dict_type *next;
1207
1208 free (ptr->word);
1209 if (ptr->code)
1210 {
1211 int i;
1212 for (i = 0; i < ptr->code_end - 1; i ++)
1213 if (ptr->code[i].f == push_text
1214 && ptr->code[i + 1].s)
1215 {
1216 free (ptr->code[i + 1].s - 1);
1217 ++i;
1218 }
1219 free (ptr->code);
1220 }
1221 next = ptr->next;
1222 free (ptr);
1223 ptr = next;
1224 }
1225 }
1226
1227 static void
perform(void)1228 perform (void)
1229 {
1230 tos = stack;
1231
1232 while (at (ptr, idx))
1233 {
1234 /* It's worth looking through the command list. */
1235 if (iscommand (ptr, idx))
1236 {
1237 char *next;
1238 dict_type *word;
1239
1240 (void) nextword (addr (ptr, idx), &next);
1241
1242 word = lookup_word (next);
1243
1244 if (word)
1245 {
1246 exec (word);
1247 }
1248 else
1249 {
1250 if (warning)
1251 fprintf (stderr, "warning, %s is not recognised\n", next);
1252 idx = skip_past_newline_1 (ptr, idx);
1253 }
1254 free (next);
1255 }
1256 else
1257 idx = skip_past_newline_1 (ptr, idx);
1258 }
1259 }
1260
1261 dict_type *
newentry(char * word)1262 newentry (char *word)
1263 {
1264 dict_type *new_d = xmalloc (sizeof (*new_d));
1265 new_d->word = word;
1266 new_d->next = root;
1267 root = new_d;
1268 new_d->code = xmalloc (sizeof (*new_d->code));
1269 new_d->code_length = 1;
1270 new_d->code_end = 0;
1271 return new_d;
1272 }
1273
1274 unsigned int
add_to_definition(dict_type * entry,pcu word)1275 add_to_definition (dict_type *entry, pcu word)
1276 {
1277 if (entry->code_end == entry->code_length)
1278 {
1279 entry->code_length += 2;
1280 entry->code = xrealloc (entry->code,
1281 entry->code_length * sizeof (*entry->code));
1282 }
1283 entry->code[entry->code_end] = word;
1284
1285 return entry->code_end++;
1286 }
1287
1288 void
add_intrinsic(char * name,void (* func)(void))1289 add_intrinsic (char *name, void (*func) (void))
1290 {
1291 dict_type *new_d = newentry (xstrdup (name));
1292 pcu p = { func };
1293 add_to_definition (new_d, p);
1294 p.f = 0;
1295 add_to_definition (new_d, p);
1296 }
1297
1298 void
compile(char * string)1299 compile (char *string)
1300 {
1301 /* Add words to the dictionary. */
1302 char *word;
1303
1304 string = nextword (string, &word);
1305 while (string && *string && word[0])
1306 {
1307 if (word[0] == ':')
1308 {
1309 dict_type *ptr;
1310 pcu p;
1311
1312 /* Compile a word and add to dictionary. */
1313 free (word);
1314 string = nextword (string, &word);
1315 if (!string)
1316 continue;
1317 ptr = newentry (word);
1318 string = nextword (string, &word);
1319 if (!string)
1320 {
1321 free (ptr->code);
1322 free (ptr);
1323 continue;
1324 }
1325
1326 while (word[0] != ';')
1327 {
1328 switch (word[0])
1329 {
1330 case '"':
1331 /* got a string, embed magic push string
1332 function */
1333 p.f = push_text;
1334 add_to_definition (ptr, p);
1335 p.s = word + 1;
1336 add_to_definition (ptr, p);
1337 break;
1338 case '0':
1339 case '1':
1340 case '2':
1341 case '3':
1342 case '4':
1343 case '5':
1344 case '6':
1345 case '7':
1346 case '8':
1347 case '9':
1348 /* Got a number, embedd the magic push number
1349 function */
1350 p.f = push_number;
1351 add_to_definition (ptr, p);
1352 p.l = atol (word);
1353 add_to_definition (ptr, p);
1354 free (word);
1355 break;
1356 default:
1357 p.f = call;
1358 add_to_definition (ptr, p);
1359 p.e = lookup_word (word);
1360 add_to_definition (ptr, p);
1361 free (word);
1362 }
1363
1364 string = nextword (string, &word);
1365 }
1366 p.f = 0;
1367 add_to_definition (ptr, p);
1368 free (word);
1369 string = nextword (string, &word);
1370 }
1371 else
1372 {
1373 fprintf (stderr, "syntax error at %s\n", string - 1);
1374 }
1375 }
1376 free (word);
1377 }
1378
1379 static void
bang(void)1380 bang (void)
1381 {
1382 *(long *) ((isp[0])) = isp[-1];
1383 isp -= 2;
1384 icheck_range ();
1385 pc++;
1386 }
1387
1388 static void
atsign(void)1389 atsign (void)
1390 {
1391 isp[0] = *(long *) (isp[0]);
1392 pc++;
1393 }
1394
1395 static void
hello(void)1396 hello (void)
1397 {
1398 printf ("hello\n");
1399 pc++;
1400 }
1401
1402 static void
stdout_(void)1403 stdout_ (void)
1404 {
1405 isp++;
1406 icheck_range ();
1407 *isp = 1;
1408 pc++;
1409 }
1410
1411 static void
stderr_(void)1412 stderr_ (void)
1413 {
1414 isp++;
1415 icheck_range ();
1416 *isp = 2;
1417 pc++;
1418 }
1419
1420 static void
print(void)1421 print (void)
1422 {
1423 if (*isp == 1)
1424 write_buffer (tos, stdout);
1425 else if (*isp == 2)
1426 write_buffer (tos, stderr);
1427 else
1428 fprintf (stderr, "print: illegal print destination `%ld'\n", *isp);
1429 isp--;
1430 tos--;
1431 icheck_range ();
1432 check_range ();
1433 pc++;
1434 }
1435
1436 static void
read_in(string_type * str,FILE * file)1437 read_in (string_type *str, FILE *file)
1438 {
1439 char buff[10000];
1440 unsigned int r;
1441 do
1442 {
1443 r = fread (buff, 1, sizeof (buff), file);
1444 catbuf (str, buff, r);
1445 }
1446 while (r);
1447 buff[0] = 0;
1448
1449 catbuf (str, buff, 1);
1450 }
1451
1452 static void
usage(void)1453 usage (void)
1454 {
1455 fprintf (stderr, "usage: -[d|i|g] <file >file\n");
1456 exit (33);
1457 }
1458
1459 /* There is no reliable way to declare exit. Sometimes it returns
1460 int, and sometimes it returns void. Sometimes it changes between
1461 OS releases. Trying to get it declared correctly in the hosts file
1462 is a pointless waste of time. */
1463
1464 static void
chew_exit(void)1465 chew_exit (void)
1466 {
1467 exit (0);
1468 }
1469
1470 int
main(int ac,char * av[])1471 main (int ac, char *av[])
1472 {
1473 unsigned int i;
1474 string_type buffer;
1475 string_type pptr;
1476
1477 init_string (&buffer);
1478 init_string (&pptr);
1479 init_string (stack + 0);
1480 tos = stack + 1;
1481 ptr = &pptr;
1482
1483 add_intrinsic ("push_text", push_text);
1484 add_intrinsic ("!", bang);
1485 add_intrinsic ("@", atsign);
1486 add_intrinsic ("hello", hello);
1487 add_intrinsic ("stdout", stdout_);
1488 add_intrinsic ("stderr", stderr_);
1489 add_intrinsic ("print", print);
1490 add_intrinsic ("skip_past_newline", skip_past_newline);
1491 add_intrinsic ("catstr", icatstr);
1492 add_intrinsic ("copy_past_newline", icopy_past_newline);
1493 add_intrinsic ("dup", other_dup);
1494 add_intrinsic ("drop", drop);
1495 add_intrinsic ("idrop", idrop);
1496 add_intrinsic ("remchar", remchar);
1497 add_intrinsic ("get_stuff_in_command", get_stuff_in_command);
1498 add_intrinsic ("do_fancy_stuff", do_fancy_stuff);
1499 add_intrinsic ("bulletize", bulletize);
1500 add_intrinsic ("courierize", courierize);
1501 /* If the following line gives an error, exit() is not declared in the
1502 ../hosts/foo.h file for this host. Fix it there, not here! */
1503 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1504 add_intrinsic ("exit", chew_exit);
1505 add_intrinsic ("swap", swap);
1506 add_intrinsic ("outputdots", outputdots);
1507 add_intrinsic ("paramstuff", paramstuff);
1508 add_intrinsic ("maybecatstr", maybecatstr);
1509 add_intrinsic ("translatecomments", translatecomments);
1510 add_intrinsic ("kill_bogus_lines", kill_bogus_lines);
1511 add_intrinsic ("indent", indent);
1512 add_intrinsic ("internalmode", internalmode);
1513 add_intrinsic ("print_stack_level", print_stack_level);
1514 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines);
1515
1516 /* Put a nl at the start. */
1517 catchar (&buffer, '\n');
1518
1519 read_in (&buffer, stdin);
1520 remove_noncomments (&buffer, ptr);
1521 for (i = 1; i < (unsigned int) ac; i++)
1522 {
1523 if (av[i][0] == '-')
1524 {
1525 if (av[i][1] == 'f')
1526 {
1527 string_type b;
1528 FILE *f;
1529 init_string (&b);
1530
1531 f = fopen (av[i + 1], "r");
1532 if (!f)
1533 {
1534 fprintf (stderr, "Can't open the input file %s\n",
1535 av[i + 1]);
1536 return 33;
1537 }
1538
1539 read_in (&b, f);
1540 compile (b.ptr);
1541 perform ();
1542 delete_string (&b);
1543 }
1544 else if (av[i][1] == 'i')
1545 {
1546 internal_wanted = 1;
1547 }
1548 else if (av[i][1] == 'w')
1549 {
1550 warning = 1;
1551 }
1552 else
1553 usage ();
1554 }
1555 }
1556 write_buffer (stack + 0, stdout);
1557 free_words ();
1558 delete_string (&pptr);
1559 delete_string (&buffer);
1560 if (tos != stack)
1561 {
1562 fprintf (stderr, "finishing with current stack level %ld\n",
1563 (long) (tos - stack));
1564 return 1;
1565 }
1566 return 0;
1567 }
1568