2 Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2000, 2001,
4 Free Software Foundation, Inc.
5 Contributed by steve chamberlain @cygnus
7 This file is part of BFD, the Binary File Descriptor library.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
22 MA 02110-1301, USA. */
24 /* Yet another way of extracting documentation from source.
25 No, I haven't finished it yet, but I hope you people like it better
30 Basically, this is a sort of string forth, maybe we should call it
33 You define new words thus:
34 : <newword> <oldwords> ;
38 /* Primitives provided by the program:
40 Two stacks are provided, a string stack and an integer stack.
42 Internal state variables:
43 internal_wanted - indicates whether `-i' was passed
44 internal_mode - user-settable
48 ! - pop top of integer stack for address, pop next for value; store
49 @ - treat value on integer stack as the address of an integer; push
50 that integer on the integer stack after popping the "address"
51 hello - print "hello\n" to stdout
52 stdout - put stdout marker on TOS
53 stderr - put stderr marker on TOS
54 print - print TOS-1 on TOS (eg: "hello\n" stdout print)
57 copy_past_newline - append input, up to and including newline into TOS
61 remchar - delete last character from TOS
63 do_fancy_stuff - translate <<foo>> to @code{foo} in TOS
64 bulletize - if "o" lines found, prepend @itemize @bullet to TOS
65 and @item to each "o" line; append @end itemize
66 courierize - put @example around . and | lines, translate {* *} { }
69 outputdots - strip out lines without leading dots
70 paramstuff - convert full declaration into "PARAMS" form if not already
71 maybecatstr - do catstr if internal_mode == internal_wanted, discard
73 translatecomments - turn {* and *} into comment delimiters
74 kill_bogus_lines - get rid of extra newlines
76 internalmode - pop from integer stack, set `internalmode' to that value
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
82 A command must be all upper-case, and alone on a line.
101 /* Here is a string type ... */
103 typedef struct buffer
106 unsigned long write_idx
;
111 static void init_string_with_size (string_type
*, unsigned int);
112 static void init_string (string_type
*);
113 static int find (string_type
*, char *);
114 static void write_buffer (string_type
*, FILE *);
115 static void delete_string (string_type
*);
116 static char *addr (string_type
*, unsigned int);
117 static char at (string_type
*, unsigned int);
118 static void catchar (string_type
*, int);
119 static void overwrite_string (string_type
*, string_type
*);
120 static void catbuf (string_type
*, char *, unsigned int);
121 static void cattext (string_type
*, char *);
122 static void catstr (string_type
*, string_type
*);
126 init_string_with_size (buffer
, size
)
130 buffer
->write_idx
= 0;
132 buffer
->ptr
= malloc (size
);
139 init_string_with_size (buffer
, DEF_SIZE
);
150 for (i
= 0; i
< str
->write_idx
&& *p
; i
++)
152 if (*p
== str
->ptr
[i
])
161 write_buffer (buffer
, f
)
165 fwrite (buffer
->ptr
, buffer
->write_idx
, 1, f
);
169 delete_string (buffer
)
180 return buffer
->ptr
+ idx
;
188 if (pos
>= buffer
->write_idx
)
190 return buffer
->ptr
[pos
];
198 if (buffer
->write_idx
== buffer
->size
)
201 buffer
->ptr
= realloc (buffer
->ptr
, buffer
->size
);
204 buffer
->ptr
[buffer
->write_idx
++] = ch
;
208 overwrite_string (dst
, src
)
213 dst
->size
= src
->size
;
214 dst
->write_idx
= src
->write_idx
;
219 catbuf (buffer
, buf
, len
)
224 if (buffer
->write_idx
+ len
>= buffer
->size
)
226 while (buffer
->write_idx
+ len
>= buffer
->size
)
228 buffer
->ptr
= realloc (buffer
->ptr
, buffer
->size
);
230 memcpy (buffer
->ptr
+ buffer
->write_idx
, buf
, len
);
231 buffer
->write_idx
+= len
;
235 cattext (buffer
, string
)
239 catbuf (buffer
, string
, (unsigned int) strlen (string
));
247 catbuf (dst
, src
->ptr
, src
->write_idx
);
251 skip_white_and_stars (src
, idx
)
256 while ((c
= at (src
, idx
)),
257 isspace ((unsigned char) c
)
259 /* Don't skip past end-of-comment or star as first
260 character on its line. */
261 && at (src
, idx
+1) != '/'
262 && at (src
, idx
-1) != '\n'))
267 /***********************************************************************/
269 string_type stack
[STACK
];
272 unsigned int idx
= 0; /* Pos in input buffer */
273 string_type
*ptr
; /* and the buffer */
274 typedef void (*stinst_type
)();
276 stinst_type sstack
[STACK
];
277 stinst_type
*ssp
= &sstack
[0];
279 long *isp
= &istack
[0];
281 typedef int *word_type
;
286 struct dict_struct
*next
;
293 typedef struct dict_struct dict_type
;
299 fprintf (stderr
, "%s\n", msg
);
307 die ("underflow in string stack");
308 if (tos
>= stack
+ STACK
)
309 die ("overflow in string stack");
316 die ("underflow in integer stack");
317 if (isp
>= istack
+ STACK
)
318 die ("overflow in integer stack");
322 static void exec (dict_type
*);
323 static void call (void);
324 static void remchar (void), strip_trailing_newlines (void), push_number (void);
325 static void push_text (void);
326 static void remove_noncomments (string_type
*, string_type
*);
327 static void print_stack_level (void);
328 static void paramstuff (void), translatecomments (void);
329 static void outputdots (void), courierize (void), bulletize (void);
330 static void do_fancy_stuff (void);
331 static int iscommand (string_type
*, unsigned int);
332 static int copy_past_newline (string_type
*, unsigned int, string_type
*);
333 static void icopy_past_newline (void), kill_bogus_lines (void), indent (void);
334 static void get_stuff_in_command (void), swap (void), other_dup (void);
335 static void drop (void), idrop (void);
336 static void icatstr (void), skip_past_newline (void), internalmode (void);
337 static void maybecatstr (void);
338 static char *nextword (char *, char **);
339 dict_type
*lookup_word (char *);
340 static void perform (void);
341 dict_type
*newentry (char *);
342 unsigned int add_to_definition (dict_type
*, stinst_type
);
343 void add_intrinsic (char *, void (*)());
344 void add_var (char *);
345 void compile (char *);
346 static void bang (void);
347 static void atsign (void);
348 static void hello (void);
349 static void stdout_ (void);
350 static void stderr_ (void);
351 static void print (void);
352 static void read_in (string_type
*, FILE *);
353 static void usage (void);
354 static void chew_exit (void);
369 stinst_type
*oldpc
= pc
;
371 e
= (dict_type
*) (pc
[1]);
385 strip_trailing_newlines ()
387 while ((isspace ((unsigned char) at (tos
, tos
->write_idx
- 1))
388 || at (tos
, tos
->write_idx
- 1) == '\n')
389 && tos
->write_idx
> 0)
411 cattext (tos
, *((char **) pc
));
415 /* This function removes everything not inside comments starting on
416 the first char of the line from the string, also when copying
417 comments, removes blank space and leading *'s.
418 Blank lines are turned into one blank line. */
421 remove_noncomments (src
, dst
)
425 unsigned int idx
= 0;
427 while (at (src
, idx
))
429 /* Now see if we have a comment at the start of the line. */
430 if (at (src
, idx
) == '\n'
431 && at (src
, idx
+ 1) == '/'
432 && at (src
, idx
+ 2) == '*')
436 idx
= skip_white_and_stars (src
, idx
);
438 /* Remove leading dot */
439 if (at (src
, idx
) == '.')
442 /* Copy to the end of the line, or till the end of the
444 while (at (src
, idx
))
446 if (at (src
, idx
) == '\n')
448 /* end of line, echo and scrape of leading blanks */
449 if (at (src
, idx
+ 1) == '\n')
453 idx
= skip_white_and_stars (src
, idx
);
455 else if (at (src
, idx
) == '*' && at (src
, idx
+ 1) == '/')
458 cattext (dst
, "\nENDDD\n");
463 catchar (dst
, at (src
, idx
));
476 fprintf (stderr
, "current string stack depth = %d, ", tos
- stack
);
477 fprintf (stderr
, "current integer stack depth = %d\n", isp
- istack
);
485 name PARAMS ((stuff));
501 /* Make sure that it's not already param'd or proto'd. */
503 || find (tos
, "PARAMS") || find (tos
, "PROTO") || !find (tos
, "("))
509 /* Find the open paren. */
510 for (openp
= 0; at (tos
, openp
) != '(' && at (tos
, openp
); openp
++)
514 /* Step back to the fname. */
516 while (fname
&& isspace ((unsigned char) at (tos
, fname
)))
519 && !isspace ((unsigned char) at (tos
,fname
))
520 && at (tos
,fname
) != '*')
525 /* Output type, omitting trailing whitespace character(s), if
527 for (len
= fname
; 0 < len
; len
--)
529 if (!isspace ((unsigned char) at (tos
, len
- 1)))
532 for (idx
= 0; idx
< len
; idx
++)
533 catchar (&out
, at (tos
, idx
));
535 cattext (&out
, "\n"); /* Insert a newline between type and fnname */
537 /* Output function name, omitting trailing whitespace
538 character(s), if any. */
539 for (len
= openp
; 0 < len
; len
--)
541 if (!isspace ((unsigned char) at (tos
, len
- 1)))
544 for (idx
= fname
; idx
< len
; idx
++)
545 catchar (&out
, at (tos
, idx
));
547 cattext (&out
, " PARAMS (");
549 for (idx
= openp
; at (tos
, idx
) && at (tos
, idx
) != ';'; idx
++)
550 catchar (&out
, at (tos
, idx
));
552 cattext (&out
, ");\n\n");
554 overwrite_string (tos
, &out
);
560 and *} into comments */
565 unsigned int idx
= 0;
569 while (at (tos
, idx
))
571 if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
573 cattext (&out
, "/*");
576 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
578 cattext (&out
, "*/");
583 catchar (&out
, at (tos
, idx
));
588 overwrite_string (tos
, &out
);
593 /* Mod tos so that only lines with leading dots remain */
597 unsigned int idx
= 0;
601 while (at (tos
, idx
))
603 if (at (tos
, idx
) == '\n' && at (tos
, idx
+ 1) == '.')
608 while ((c
= at (tos
, idx
)) && c
!= '\n')
610 if (c
== '{' && at (tos
, idx
+ 1) == '*')
612 cattext (&out
, "/*");
615 else if (c
== '*' && at (tos
, idx
+ 1) == '}')
617 cattext (&out
, "*/");
626 catchar (&out
, '\n');
634 overwrite_string (tos
, &out
);
638 /* Find lines starting with . and | and put example around them on tos */
643 unsigned int idx
= 0;
648 while (at (tos
, idx
))
650 if (at (tos
, idx
) == '\n'
651 && (at (tos
, idx
+1 ) == '.'
652 || at (tos
, idx
+ 1) == '|'))
654 cattext (&out
, "\n@example\n");
659 while (at (tos
, idx
) && at (tos
, idx
) != '\n')
663 /* We are inside {} parameters of some command;
664 Just pass through until matching brace. */
665 if (at (tos
, idx
) == '{')
667 else if (at (tos
, idx
) == '}')
670 else if (command
!= 0)
672 if (at (tos
, idx
) == '{')
674 else if (!islower ((unsigned char) at (tos
, idx
)))
677 else if (at (tos
, idx
) == '@'
678 && islower ((unsigned char) at (tos
, idx
+ 1)))
682 else if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
684 cattext (&out
, "/*");
688 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
690 cattext (&out
, "*/");
694 else if (at (tos
, idx
) == '{'
695 || at (tos
, idx
) == '}')
700 catchar (&out
, at (tos
, idx
));
703 catchar (&out
, '\n');
705 while (at (tos
, idx
) == '\n'
706 && ((at (tos
, idx
+ 1) == '.')
707 || (at (tos
, idx
+ 1) == '|')))
709 cattext (&out
, "@end example");
713 catchar (&out
, at (tos
, idx
));
718 overwrite_string (tos
, &out
);
722 /* Finds any lines starting with "o ", if there are any, then turns
723 on @itemize @bullet, and @items each of them. Then ends with @end
724 itemize, inplace at TOS*/
729 unsigned int idx
= 0;
734 while (at (tos
, idx
))
736 if (at (tos
, idx
) == '@'
737 && at (tos
, idx
+ 1) == '*')
742 else if (at (tos
, idx
) == '\n'
743 && at (tos
, idx
+ 1) == 'o'
744 && isspace ((unsigned char) at (tos
, idx
+ 2)))
748 cattext (&out
, "\n@itemize @bullet\n");
752 cattext (&out
, "\n@item\n");
757 catchar (&out
, at (tos
, idx
));
758 if (on
&& at (tos
, idx
) == '\n'
759 && at (tos
, idx
+ 1) == '\n'
760 && at (tos
, idx
+ 2) != 'o')
762 cattext (&out
, "@end itemize");
771 cattext (&out
, "@end itemize\n");
779 /* Turn <<foo>> into @code{foo} in place at TOS*/
784 unsigned int idx
= 0;
787 while (at (tos
, idx
))
789 if (at (tos
, idx
) == '<'
790 && at (tos
, idx
+ 1) == '<'
791 && !isspace ((unsigned char) at (tos
, idx
+ 2)))
793 /* This qualifies as a << startup. */
795 cattext (&out
, "@code{");
797 && at (tos
, idx
) != '>' )
799 catchar (&out
, at (tos
, idx
));
808 catchar (&out
, at (tos
, idx
));
818 /* A command is all upper case,and alone on a line. */
825 unsigned int len
= 0;
826 while (at (ptr
, idx
))
828 if (isupper ((unsigned char) at (ptr
, idx
))
829 || at (ptr
, idx
) == ' ' || at (ptr
, idx
) == '_')
834 else if (at (ptr
, idx
) == '\n')
847 copy_past_newline (ptr
, idx
, dst
)
854 while (at (ptr
, idx
) && at (ptr
, idx
) != '\n')
856 if (at (ptr
, idx
) == '\t')
858 /* Expand tabs. Neither makeinfo nor TeX can cope well with
862 while (++column
& 7);
866 catchar (dst
, at (ptr
, idx
));
872 catchar (dst
, at (ptr
, idx
));
879 icopy_past_newline ()
884 idx
= copy_past_newline (ptr
, idx
, tos
);
889 Take the string at the top of the stack, do some prettying. */
902 /* Drop leading nl. */
903 while (at (tos
, idx
) == '\n')
909 /* If the first char is a '.' prepend a newline so that it is
910 recognized properly later. */
911 if (at (tos
, idx
) == '.')
912 catchar (&out
, '\n');
914 /* Find the last char. */
915 while (at (tos
, idx
))
920 /* Find the last non white before the nl. */
923 while (idx
&& isspace ((unsigned char) at (tos
, idx
)))
927 /* Copy buffer upto last char, but blank lines before and after
933 if (at (tos
, c
) == '\n'
934 && at (tos
, c
+ 1) == '\n'
935 && at (tos
, c
+ 2) == '.')
937 /* Ignore two newlines before a dot. */
940 else if (at (tos
, c
) == '.' && sl
)
942 /* remember that this line started with a dot. */
945 else if (at (tos
, c
) == '\n'
946 && at (tos
, c
+ 1) == '\n'
950 /* Ignore two newlines when last line was dot. */
953 catchar (&out
, at (tos
, c
));
954 if (at (tos
, c
) == '\n')
971 catchar (&out
, '\n');
986 while (at (tos
, idx
))
988 switch (at (tos
, idx
))
991 cattext (&out
, "\n");
993 if (tab
&& at (tos
, idx
))
1002 cattext (&out
, " ");
1004 cattext (&out
, "(");
1009 cattext (&out
, ")");
1015 catchar (&out
, at (tos
, idx
));
1024 delete_string (tos
);
1030 get_stuff_in_command ()
1036 while (at (ptr
, idx
))
1038 if (iscommand (ptr
, idx
))
1040 idx
= copy_past_newline (ptr
, idx
, tos
);
1062 catstr (tos
, tos
- 1);
1087 catstr (tos
, tos
+ 1);
1088 delete_string (tos
+ 1);
1093 skip_past_newline ()
1095 while (at (ptr
, idx
)
1096 && at (ptr
, idx
) != '\n')
1105 internal_mode
= *(isp
);
1114 if (internal_wanted
== internal_mode
)
1116 catstr (tos
- 1, tos
);
1118 delete_string (tos
);
1125 nextword (string
, word
)
1136 while (isspace ((unsigned char) *string
) || *string
== '-')
1140 while (*string
&& *string
!= '\n')
1152 word_start
= string
;
1159 if (*string
== '\\')
1165 while (*string
!= '"');
1169 while (!isspace ((unsigned char) *string
))
1177 *word
= malloc (length
+ 1);
1182 for (idx
= 0; idx
< length
; idx
++)
1184 if (src
[idx
] == '\\')
1185 switch (src
[idx
+ 1])
1193 *dst
++ = src
[idx
+ 1];
1217 dict_type
*ptr
= root
;
1220 if (strcmp (ptr
->word
, word
) == 0)
1225 fprintf (stderr
, "Can't find %s\n", word
);
1234 while (at (ptr
, idx
))
1236 /* It's worth looking through the command list. */
1237 if (iscommand (ptr
, idx
))
1242 (void) nextword (addr (ptr
, idx
), &next
);
1244 word
= lookup_word (next
);
1253 fprintf (stderr
, "warning, %s is not recognised\n", next
);
1254 skip_past_newline ();
1259 skip_past_newline ();
1267 dict_type
*new = (dict_type
*) malloc (sizeof (dict_type
));
1271 new->code
= (stinst_type
*) malloc (sizeof (stinst_type
));
1272 new->code_length
= 1;
1278 add_to_definition (entry
, word
)
1282 if (entry
->code_end
== entry
->code_length
)
1284 entry
->code_length
+= 2;
1286 (stinst_type
*) realloc ((char *) (entry
->code
),
1287 entry
->code_length
* sizeof (word_type
));
1289 entry
->code
[entry
->code_end
] = word
;
1291 return entry
->code_end
++;
1295 add_intrinsic (name
, func
)
1299 dict_type
*new = newentry (name
);
1300 add_to_definition (new, func
);
1301 add_to_definition (new, 0);
1308 dict_type
*new = newentry (name
);
1309 add_to_definition (new, push_number
);
1310 add_to_definition (new, (stinst_type
) (&(new->var
)));
1311 add_to_definition (new, 0);
1318 /* Add words to the dictionary. */
1320 string
= nextword (string
, &word
);
1321 while (string
&& *string
&& word
[0])
1323 if (strcmp (word
, "var") == 0)
1325 string
= nextword (string
, &word
);
1328 string
= nextword (string
, &word
);
1330 else if (word
[0] == ':')
1333 /* Compile a word and add to dictionary. */
1334 string
= nextword (string
, &word
);
1336 ptr
= newentry (word
);
1337 string
= nextword (string
, &word
);
1338 while (word
[0] != ';')
1343 /* got a string, embed magic push string
1345 add_to_definition (ptr
, push_text
);
1346 add_to_definition (ptr
, (stinst_type
) (word
+ 1));
1358 /* Got a number, embedd the magic push number
1360 add_to_definition (ptr
, push_number
);
1361 add_to_definition (ptr
, (stinst_type
) atol (word
));
1364 add_to_definition (ptr
, call
);
1365 add_to_definition (ptr
, (stinst_type
) lookup_word (word
));
1368 string
= nextword (string
, &word
);
1370 add_to_definition (ptr
, 0);
1371 string
= nextword (string
, &word
);
1375 fprintf (stderr
, "syntax error at %s\n", string
- 1);
1383 *(long *) ((isp
[0])) = isp
[-1];
1392 isp
[0] = *(long *) (isp
[0]);
1425 write_buffer (tos
, stdout
);
1427 write_buffer (tos
, stderr
);
1429 fprintf (stderr
, "print: illegal print destination `%ld'\n", *isp
);
1446 r
= fread (buff
, 1, sizeof (buff
), file
);
1447 catbuf (str
, buff
, r
);
1452 catbuf (str
, buff
, 1);
1458 fprintf (stderr
, "usage: -[d|i|g] <file >file\n");
1462 /* There is no reliable way to declare exit. Sometimes it returns
1463 int, and sometimes it returns void. Sometimes it changes between
1464 OS releases. Trying to get it declared correctly in the hosts file
1465 is a pointless waste of time. */
1482 init_string (&buffer
);
1483 init_string (&pptr
);
1484 init_string (stack
+ 0);
1488 add_intrinsic ("push_text", push_text
);
1489 add_intrinsic ("!", bang
);
1490 add_intrinsic ("@", atsign
);
1491 add_intrinsic ("hello", hello
);
1492 add_intrinsic ("stdout", stdout_
);
1493 add_intrinsic ("stderr", stderr_
);
1494 add_intrinsic ("print", print
);
1495 add_intrinsic ("skip_past_newline", skip_past_newline
);
1496 add_intrinsic ("catstr", icatstr
);
1497 add_intrinsic ("copy_past_newline", icopy_past_newline
);
1498 add_intrinsic ("dup", other_dup
);
1499 add_intrinsic ("drop", drop
);
1500 add_intrinsic ("idrop", idrop
);
1501 add_intrinsic ("remchar", remchar
);
1502 add_intrinsic ("get_stuff_in_command", get_stuff_in_command
);
1503 add_intrinsic ("do_fancy_stuff", do_fancy_stuff
);
1504 add_intrinsic ("bulletize", bulletize
);
1505 add_intrinsic ("courierize", courierize
);
1506 /* If the following line gives an error, exit() is not declared in the
1507 ../hosts/foo.h file for this host. Fix it there, not here! */
1508 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1509 add_intrinsic ("exit", chew_exit
);
1510 add_intrinsic ("swap", swap
);
1511 add_intrinsic ("outputdots", outputdots
);
1512 add_intrinsic ("paramstuff", paramstuff
);
1513 add_intrinsic ("maybecatstr", maybecatstr
);
1514 add_intrinsic ("translatecomments", translatecomments
);
1515 add_intrinsic ("kill_bogus_lines", kill_bogus_lines
);
1516 add_intrinsic ("indent", indent
);
1517 add_intrinsic ("internalmode", internalmode
);
1518 add_intrinsic ("print_stack_level", print_stack_level
);
1519 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines
);
1521 /* Put a nl at the start. */
1522 catchar (&buffer
, '\n');
1524 read_in (&buffer
, stdin
);
1525 remove_noncomments (&buffer
, ptr
);
1526 for (i
= 1; i
< (unsigned int) ac
; i
++)
1528 if (av
[i
][0] == '-')
1530 if (av
[i
][1] == 'f')
1536 f
= fopen (av
[i
+ 1], "r");
1539 fprintf (stderr
, "Can't open the input file %s\n",
1548 else if (av
[i
][1] == 'i')
1550 internal_wanted
= 1;
1552 else if (av
[i
][1] == 'w')
1560 write_buffer (stack
+ 0, stdout
);
1563 fprintf (stderr
, "finishing with current stack level %d\n",