2 Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 1998, 2000
3 Free Software Foundation, Inc.
4 Contributed by steve chamberlain @cygnus
6 This file is part of BFD, the Binary File Descriptor library.
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.
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.
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. */
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
28 Basically, this is a sort of string forth, maybe we should call it
31 You define new words thus:
32 : <newword> <oldwords> ;
36 /* Primitives provided by the program:
38 Two stacks are provided, a string stack and an integer stack.
40 Internal state variables:
41 internal_wanted - indicates whether `-i' was passed
42 internal_mode - user-settable
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)
55 copy_past_newline - append input, up to and including newline into TOS
59 remchar - delete last character from TOS
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 {* *} { }
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
71 translatecomments - turn {* and *} into comment delimiters
72 kill_bogus_lines - get rid of extra newlines
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
80 A command must be all upper-case, and alone on a line.
98 /* Here is a string type ... */
100 typedef struct buffer
103 unsigned long write_idx
;
108 static void init_string_with_size (string_type
*, unsigned int);
109 static void init_string (string_type
*);
110 static int find (string_type
*, char *);
111 static void write_buffer (string_type
*, FILE *);
112 static void delete_string (string_type
*);
113 static char *addr (string_type
*, unsigned int);
114 static char at (string_type
*, unsigned int);
115 static void catchar (string_type
*, int);
116 static void overwrite_string (string_type
*, string_type
*);
117 static void catbuf (string_type
*, char *, unsigned int);
118 static void cattext (string_type
*, char *);
119 static void catstr (string_type
*, string_type
*);
123 init_string_with_size (buffer
, size
)
127 buffer
->write_idx
= 0;
129 buffer
->ptr
= malloc (size
);
136 init_string_with_size (buffer
, DEF_SIZE
);
147 for (i
= 0; i
< str
->write_idx
&& *p
; i
++)
149 if (*p
== str
->ptr
[i
])
158 write_buffer (buffer
, f
)
162 fwrite (buffer
->ptr
, buffer
->write_idx
, 1, f
);
166 delete_string (buffer
)
177 return buffer
->ptr
+ idx
;
185 if (pos
>= buffer
->write_idx
)
187 return buffer
->ptr
[pos
];
195 if (buffer
->write_idx
== buffer
->size
)
198 buffer
->ptr
= realloc (buffer
->ptr
, buffer
->size
);
201 buffer
->ptr
[buffer
->write_idx
++] = ch
;
205 overwrite_string (dst
, src
)
210 dst
->size
= src
->size
;
211 dst
->write_idx
= src
->write_idx
;
216 catbuf (buffer
, buf
, len
)
221 if (buffer
->write_idx
+ len
>= buffer
->size
)
223 while (buffer
->write_idx
+ len
>= buffer
->size
)
225 buffer
->ptr
= realloc (buffer
->ptr
, buffer
->size
);
227 memcpy (buffer
->ptr
+ buffer
->write_idx
, buf
, len
);
228 buffer
->write_idx
+= len
;
232 cattext (buffer
, string
)
236 catbuf (buffer
, string
, (unsigned int) strlen (string
));
244 catbuf (dst
, src
->ptr
, src
->write_idx
);
248 skip_white_and_stars (src
, idx
)
253 while ((c
= at (src
, idx
)),
254 isspace ((unsigned char) c
)
256 /* Don't skip past end-of-comment or star as first
257 character on its line. */
258 && at (src
, idx
+1) != '/'
259 && at (src
, idx
-1) != '\n'))
264 /***********************************************************************/
266 string_type stack
[STACK
];
269 unsigned int idx
= 0; /* Pos in input buffer */
270 string_type
*ptr
; /* and the buffer */
271 typedef void (*stinst_type
)();
273 stinst_type sstack
[STACK
];
274 stinst_type
*ssp
= &sstack
[0];
276 long *isp
= &istack
[0];
278 typedef int *word_type
;
283 struct dict_struct
*next
;
290 typedef struct dict_struct dict_type
;
292 #define WORD(x) static void x()
298 fprintf (stderr
, "%s\n", msg
);
306 die ("underflow in string stack");
307 if (tos
>= stack
+ STACK
)
308 die ("overflow in string stack");
315 die ("underflow in integer stack");
316 if (isp
>= istack
+ STACK
)
317 die ("overflow in integer stack");
321 static void exec (dict_type
*);
322 static void call (void);
323 static void remchar (void), strip_trailing_newlines (void), push_number (void);
324 static void push_text (void);
325 static void remove_noncomments (string_type
*, string_type
*);
326 static void print_stack_level (void);
327 static void paramstuff (void), translatecomments (void);
328 static void outputdots (void), courierize (void), bulletize (void);
329 static void do_fancy_stuff (void);
330 static int iscommand (string_type
*, unsigned int);
331 static int copy_past_newline (string_type
*, unsigned int, string_type
*);
332 static void icopy_past_newline (void), kill_bogus_lines (void), indent (void);
333 static void get_stuff_in_command (void), swap (void), other_dup (void);
334 static void drop (void), idrop (void);
335 static void icatstr (void), skip_past_newline (void), internalmode (void);
336 static void maybecatstr (void);
337 static char *nextword (char *, char **);
338 dict_type
*lookup_word (char *);
339 static void perform (void);
340 dict_type
*newentry (char *);
341 unsigned int add_to_definition (dict_type
*, stinst_type
);
342 void add_intrinsic (char *, void (*)());
343 void add_var (char *);
344 void compile (char *);
345 static void bang (void);
346 static void atsign (void);
347 static void hello (void);
348 static void stdout_ (void);
349 static void stderr_ (void);
350 static void print (void);
351 static void read_in (string_type
*, FILE *);
352 static void usage (void);
353 static void chew_exit (void);
367 stinst_type
*oldpc
= pc
;
369 e
= (dict_type
*) (pc
[1]);
382 strip_trailing_newlines ()
384 while ((isspace ((unsigned char) at (tos
, tos
->write_idx
- 1))
385 || at (tos
, tos
->write_idx
- 1) == '\n')
386 && tos
->write_idx
> 0)
406 cattext (tos
, *((char **) pc
));
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. */
416 remove_noncomments (src
, dst
)
420 unsigned int idx
= 0;
422 while (at (src
, idx
))
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) == '*')
431 idx
= skip_white_and_stars (src
, idx
);
433 /* Remove leading dot */
434 if (at (src
, idx
) == '.')
437 /* Copy to the end of the line, or till the end of the
439 while (at (src
, idx
))
441 if (at (src
, idx
) == '\n')
443 /* end of line, echo and scrape of leading blanks */
444 if (at (src
, idx
+ 1) == '\n')
448 idx
= skip_white_and_stars (src
, idx
);
450 else if (at (src
, idx
) == '*' && at (src
, idx
+ 1) == '/')
453 cattext (dst
, "\nENDDD\n");
458 catchar (dst
, at (src
, idx
));
471 fprintf (stderr
, "current string stack depth = %d, ", tos
- stack
);
472 fprintf (stderr
, "current integer stack depth = %d\n", isp
- istack
);
480 name PARAMS ((stuff));
493 /* Make sure that it's not already param'd or proto'd. */
494 if (find (tos
, "PARAMS") || find (tos
, "PROTO") || !find (tos
, "("))
500 /* Find the open paren. */
501 for (openp
= 0; at (tos
, openp
) != '(' && at (tos
, openp
); openp
++)
505 /* Step back to the fname. */
507 while (fname
&& isspace ((unsigned char) at (tos
, fname
)))
510 && !isspace ((unsigned char) at (tos
,fname
))
511 && at (tos
,fname
) != '*')
516 for (idx
= 0; idx
< fname
; idx
++) /* Output type */
518 catchar (&out
, at (tos
, idx
));
521 cattext (&out
, "\n"); /* Insert a newline between type and fnname */
523 for (idx
= fname
; idx
< openp
; idx
++) /* Output fnname */
525 catchar (&out
, at (tos
, idx
));
528 cattext (&out
, " PARAMS (");
530 while (at (tos
, idx
) && at (tos
, idx
) != ';')
532 catchar (&out
, at (tos
, idx
));
535 cattext (&out
, ");\n\n");
537 overwrite_string (tos
, &out
);
543 and *} into comments */
545 WORD (translatecomments
)
547 unsigned int idx
= 0;
551 while (at (tos
, idx
))
553 if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
555 cattext (&out
, "/*");
558 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
560 cattext (&out
, "*/");
565 catchar (&out
, at (tos
, idx
));
570 overwrite_string (tos
, &out
);
577 /* This is not currently used. */
579 /* turn everything not starting with a . into a comment */
581 WORD (manglecomments
)
583 unsigned int idx
= 0;
587 while (at (tos
, idx
))
589 if (at (tos
, idx
) == '\n' && at (tos
, idx
+ 1) == '*')
591 cattext (&out
, " /*");
594 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
596 cattext (&out
, "*/");
601 catchar (&out
, at (tos
, idx
));
606 overwrite_string (tos
, &out
);
613 /* Mod tos so that only lines with leading dots remain */
617 unsigned int idx
= 0;
621 while (at (tos
, idx
))
623 if (at (tos
, idx
) == '\n' && at (tos
, idx
+ 1) == '.')
628 while ((c
= at (tos
, idx
)) && c
!= '\n')
630 if (c
== '{' && at (tos
, idx
+ 1) == '*')
632 cattext (&out
, "/*");
635 else if (c
== '*' && at (tos
, idx
+ 1) == '}')
637 cattext (&out
, "*/");
646 catchar (&out
, '\n');
654 overwrite_string (tos
, &out
);
658 /* Find lines starting with . and | and put example around them on tos */
662 unsigned int idx
= 0;
667 while (at (tos
, idx
))
669 if (at (tos
, idx
) == '\n'
670 && (at (tos
, idx
+1 ) == '.'
671 || at (tos
, idx
+ 1) == '|'))
673 cattext (&out
, "\n@example\n");
678 while (at (tos
, idx
) && at (tos
, idx
) != '\n')
680 if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
682 cattext (&out
, "/*");
685 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
687 cattext (&out
, "*/");
690 else if (at (tos
, idx
) == '{' && !command
)
692 cattext (&out
, "@{");
695 else if (at (tos
, idx
) == '}' && !command
)
697 cattext (&out
, "@}");
702 if (at (tos
, idx
) == '@')
704 else if (isspace ((unsigned char) at (tos
, idx
))
705 || at (tos
, idx
) == '}')
707 catchar (&out
, at (tos
, idx
));
712 catchar (&out
, '\n');
714 while (at (tos
, idx
) == '\n'
715 && ((at (tos
, idx
+ 1) == '.')
716 || (at (tos
, idx
+ 1) == '|')))
718 cattext (&out
, "@end example");
722 catchar (&out
, at (tos
, idx
));
727 overwrite_string (tos
, &out
);
731 /* Finds any lines starting with "o ", if there are any, then turns
732 on @itemize @bullet, and @items each of them. Then ends with @end
733 itemize, inplace at TOS*/
737 unsigned int idx
= 0;
742 while (at (tos
, idx
))
744 if (at (tos
, idx
) == '@'
745 && at (tos
, idx
+ 1) == '*')
750 else if (at (tos
, idx
) == '\n'
751 && at (tos
, idx
+ 1) == 'o'
752 && isspace ((unsigned char) at (tos
, idx
+ 2)))
756 cattext (&out
, "\n@itemize @bullet\n");
760 cattext (&out
, "\n@item\n");
765 catchar (&out
, at (tos
, idx
));
766 if (on
&& at (tos
, idx
) == '\n'
767 && at (tos
, idx
+ 1) == '\n'
768 && at (tos
, idx
+ 2) != 'o')
770 cattext (&out
, "@end itemize");
779 cattext (&out
, "@end itemize\n");
787 /* Turn <<foo>> into @code{foo} in place at TOS*/
789 WORD (do_fancy_stuff
)
791 unsigned int idx
= 0;
794 while (at (tos
, idx
))
796 if (at (tos
, idx
) == '<'
797 && at (tos
, idx
+ 1) == '<'
798 && !isspace ((unsigned char) at (tos
, idx
+ 2)))
800 /* This qualifies as a << startup. */
802 cattext (&out
, "@code{");
804 && at (tos
, idx
) != '>' )
806 catchar (&out
, at (tos
, idx
));
815 catchar (&out
, at (tos
, idx
));
825 /* A command is all upper case,and alone on a line. */
832 unsigned int len
= 0;
833 while (at (ptr
, idx
))
835 if (isupper ((unsigned char) at (ptr
, idx
))
836 || at (ptr
, idx
) == ' ' || at (ptr
, idx
) == '_')
841 else if (at (ptr
, idx
) == '\n')
854 copy_past_newline (ptr
, idx
, dst
)
861 while (at (ptr
, idx
) && at (ptr
, idx
) != '\n')
863 if (at (ptr
, idx
) == '\t')
865 /* Expand tabs. Neither makeinfo nor TeX can cope well with
869 while (++column
& 7);
873 catchar (dst
, at (ptr
, idx
));
879 catchar (dst
, at (ptr
, idx
));
885 WORD (icopy_past_newline
)
890 idx
= copy_past_newline (ptr
, idx
, tos
);
895 Take the string at the top of the stack, do some prettying. */
897 WORD (kill_bogus_lines
)
907 /* Drop leading nl. */
908 while (at (tos
, idx
) == '\n')
914 /* If the first char is a '.' prepend a newline so that it is
915 recognized properly later. */
916 if (at (tos
, idx
) == '.')
917 catchar (&out
, '\n');
919 /* Find the last char. */
920 while (at (tos
, idx
))
925 /* Find the last non white before the nl. */
928 while (idx
&& isspace ((unsigned char) at (tos
, idx
)))
932 /* Copy buffer upto last char, but blank lines before and after
938 if (at (tos
, c
) == '\n'
939 && at (tos
, c
+ 1) == '\n'
940 && at (tos
, c
+ 2) == '.')
942 /* Ignore two newlines before a dot. */
945 else if (at (tos
, c
) == '.' && sl
)
947 /* remember that this line started with a dot. */
950 else if (at (tos
, c
) == '\n'
951 && at (tos
, c
+ 1) == '\n'
955 /* Ignore two newlines when last line was dot. */
958 catchar (&out
, at (tos
, c
));
959 if (at (tos
, c
) == '\n')
976 catchar (&out
, '\n');
990 while (at (tos
, idx
))
992 switch (at (tos
, idx
))
995 cattext (&out
, "\n");
997 if (tab
&& at (tos
, idx
))
1006 cattext (&out
, " ");
1008 cattext (&out
, "(");
1013 cattext (&out
, ")");
1019 catchar (&out
, at (tos
, idx
));
1028 delete_string (tos
);
1033 WORD (get_stuff_in_command
)
1039 while (at (ptr
, idx
))
1041 if (iscommand (ptr
, idx
))
1043 idx
= copy_past_newline (ptr
, idx
, tos
);
1063 catstr (tos
, tos
- 1);
1085 catstr (tos
, tos
+ 1);
1086 delete_string (tos
+ 1);
1090 WORD (skip_past_newline
)
1092 while (at (ptr
, idx
)
1093 && at (ptr
, idx
) != '\n')
1101 internal_mode
= *(isp
);
1109 if (internal_wanted
== internal_mode
)
1111 catstr (tos
- 1, tos
);
1113 delete_string (tos
);
1120 nextword (string
, word
)
1131 while (isspace ((unsigned char) *string
) || *string
== '-')
1135 while (*string
&& *string
!= '\n')
1147 word_start
= string
;
1154 if (*string
== '\\')
1160 while (*string
!= '"');
1164 while (!isspace ((unsigned char) *string
))
1172 *word
= malloc (length
+ 1);
1177 for (idx
= 0; idx
< length
; idx
++)
1179 if (src
[idx
] == '\\')
1180 switch (src
[idx
+ 1])
1188 *dst
++ = src
[idx
+ 1];
1212 dict_type
*ptr
= root
;
1215 if (strcmp (ptr
->word
, word
) == 0)
1220 fprintf (stderr
, "Can't find %s\n", word
);
1229 while (at (ptr
, idx
))
1231 /* It's worth looking through the command list. */
1232 if (iscommand (ptr
, idx
))
1237 (void) nextword (addr (ptr
, idx
), &next
);
1239 word
= lookup_word (next
);
1248 fprintf (stderr
, "warning, %s is not recognised\n", next
);
1249 skip_past_newline ();
1254 skip_past_newline ();
1262 dict_type
*new = (dict_type
*) malloc (sizeof (dict_type
));
1266 new->code
= (stinst_type
*) malloc (sizeof (stinst_type
));
1267 new->code_length
= 1;
1273 add_to_definition (entry
, word
)
1277 if (entry
->code_end
== entry
->code_length
)
1279 entry
->code_length
+= 2;
1281 (stinst_type
*) realloc ((char *) (entry
->code
),
1282 entry
->code_length
* sizeof (word_type
));
1284 entry
->code
[entry
->code_end
] = word
;
1286 return entry
->code_end
++;
1290 add_intrinsic (name
, func
)
1294 dict_type
*new = newentry (name
);
1295 add_to_definition (new, func
);
1296 add_to_definition (new, 0);
1303 dict_type
*new = newentry (name
);
1304 add_to_definition (new, push_number
);
1305 add_to_definition (new, (stinst_type
) (&(new->var
)));
1306 add_to_definition (new, 0);
1313 /* Add words to the dictionary. */
1315 string
= nextword (string
, &word
);
1316 while (string
&& *string
&& word
[0])
1318 if (strcmp (word
, "var") == 0)
1320 string
= nextword (string
, &word
);
1323 string
= nextword (string
, &word
);
1325 else if (word
[0] == ':')
1328 /* Compile a word and add to dictionary. */
1329 string
= nextword (string
, &word
);
1331 ptr
= newentry (word
);
1332 string
= nextword (string
, &word
);
1333 while (word
[0] != ';')
1338 /* got a string, embed magic push string
1340 add_to_definition (ptr
, push_text
);
1341 add_to_definition (ptr
, (stinst_type
) (word
+ 1));
1353 /* Got a number, embedd the magic push number
1355 add_to_definition (ptr
, push_number
);
1356 add_to_definition (ptr
, (stinst_type
) atol (word
));
1359 add_to_definition (ptr
, call
);
1360 add_to_definition (ptr
, (stinst_type
) lookup_word (word
));
1363 string
= nextword (string
, &word
);
1365 add_to_definition (ptr
, 0);
1366 string
= nextword (string
, &word
);
1370 fprintf (stderr
, "syntax error at %s\n", string
- 1);
1378 *(long *) ((isp
[0])) = isp
[-1];
1386 isp
[0] = *(long *) (isp
[0]);
1415 write_buffer (tos
, stdout
);
1417 write_buffer (tos
, stderr
);
1419 fprintf (stderr
, "print: illegal print destination `%ld'\n", *isp
);
1436 r
= fread (buff
, 1, sizeof (buff
), file
);
1437 catbuf (str
, buff
, r
);
1442 catbuf (str
, buff
, 1);
1448 fprintf (stderr
, "usage: -[d|i|g] <file >file\n");
1452 /* There is no reliable way to declare exit. Sometimes it returns
1453 int, and sometimes it returns void. Sometimes it changes between
1454 OS releases. Trying to get it declared correctly in the hosts file
1455 is a pointless waste of time. */
1472 init_string (&buffer
);
1473 init_string (&pptr
);
1474 init_string (stack
+ 0);
1478 add_intrinsic ("push_text", push_text
);
1479 add_intrinsic ("!", bang
);
1480 add_intrinsic ("@", atsign
);
1481 add_intrinsic ("hello", hello
);
1482 add_intrinsic ("stdout", stdout_
);
1483 add_intrinsic ("stderr", stderr_
);
1484 add_intrinsic ("print", print
);
1485 add_intrinsic ("skip_past_newline", skip_past_newline
);
1486 add_intrinsic ("catstr", icatstr
);
1487 add_intrinsic ("copy_past_newline", icopy_past_newline
);
1488 add_intrinsic ("dup", other_dup
);
1489 add_intrinsic ("drop", drop
);
1490 add_intrinsic ("idrop", idrop
);
1491 add_intrinsic ("remchar", remchar
);
1492 add_intrinsic ("get_stuff_in_command", get_stuff_in_command
);
1493 add_intrinsic ("do_fancy_stuff", do_fancy_stuff
);
1494 add_intrinsic ("bulletize", bulletize
);
1495 add_intrinsic ("courierize", courierize
);
1496 /* If the following line gives an error, exit() is not declared in the
1497 ../hosts/foo.h file for this host. Fix it there, not here! */
1498 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1499 add_intrinsic ("exit", chew_exit
);
1500 add_intrinsic ("swap", swap
);
1501 add_intrinsic ("outputdots", outputdots
);
1502 add_intrinsic ("paramstuff", paramstuff
);
1503 add_intrinsic ("maybecatstr", maybecatstr
);
1504 add_intrinsic ("translatecomments", translatecomments
);
1505 add_intrinsic ("kill_bogus_lines", kill_bogus_lines
);
1506 add_intrinsic ("indent", indent
);
1507 add_intrinsic ("internalmode", internalmode
);
1508 add_intrinsic ("print_stack_level", print_stack_level
);
1509 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines
);
1511 /* Put a nl at the start. */
1512 catchar (&buffer
, '\n');
1514 read_in (&buffer
, stdin
);
1515 remove_noncomments (&buffer
, ptr
);
1516 for (i
= 1; i
< (unsigned int) ac
; i
++)
1518 if (av
[i
][0] == '-')
1520 if (av
[i
][1] == 'f')
1526 f
= fopen (av
[i
+ 1], "r");
1529 fprintf (stderr
, "Can't open the input file %s\n",
1538 else if (av
[i
][1] == 'i')
1540 internal_wanted
= 1;
1542 else if (av
[i
][1] == 'w')
1550 write_buffer (stack
+ 0, stdout
);
1553 fprintf (stderr
, "finishing with current stack level %d\n",