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