2 Copyright (C) 1990-2022 Free Software Foundation, Inc.
3 Contributed by steve chamberlain @cygnus
5 This file is part of BFD, the Binary File Descriptor library.
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.
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.
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. */
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.
94 /* Here is a string type ... */
99 unsigned long write_idx
;
103 typedef void (*stinst_type
)();
105 typedef struct dict_struct
108 struct dict_struct
*next
;
119 string_type stack
[STACK
];
122 unsigned int idx
= 0; /* Pos in input buffer */
123 string_type
*ptr
; /* and the buffer */
126 long *isp
= &istack
[0];
133 static void init_string_with_size (string_type
*, unsigned int);
134 static void init_string (string_type
*);
135 static int find (string_type
*, char *);
136 static void write_buffer (string_type
*, FILE *);
137 static void delete_string (string_type
*);
138 static char *addr (string_type
*, unsigned int);
139 static char at (string_type
*, unsigned int);
140 static void catchar (string_type
*, int);
141 static void overwrite_string (string_type
*, string_type
*);
142 static void catbuf (string_type
*, char *, unsigned int);
143 static void cattext (string_type
*, char *);
144 static void catstr (string_type
*, string_type
*);
145 static void die (char *);
152 fprintf (stderr
, "%s\n", msg
);
157 init_string_with_size (buffer
, size
)
161 buffer
->write_idx
= 0;
163 buffer
->ptr
= (char *) malloc (size
);
170 init_string_with_size (buffer
, DEF_SIZE
);
181 for (i
= 0; i
< str
->write_idx
&& *p
; i
++)
183 if (*p
== str
->ptr
[i
])
192 write_buffer (buffer
, f
)
196 if (buffer
->write_idx
!= 0
197 && fwrite (buffer
->ptr
, buffer
->write_idx
, 1, f
) != 1)
198 die ("cannot write output");
202 delete_string (buffer
)
214 return buffer
->ptr
+ idx
;
222 if (pos
>= buffer
->write_idx
)
224 return buffer
->ptr
[pos
];
232 if (buffer
->write_idx
== buffer
->size
)
235 buffer
->ptr
= (char *) realloc (buffer
->ptr
, buffer
->size
);
238 buffer
->ptr
[buffer
->write_idx
++] = ch
;
242 overwrite_string (dst
, src
)
247 dst
->size
= src
->size
;
248 dst
->write_idx
= src
->write_idx
;
253 catbuf (buffer
, buf
, len
)
258 if (buffer
->write_idx
+ len
>= buffer
->size
)
260 while (buffer
->write_idx
+ len
>= buffer
->size
)
262 buffer
->ptr
= (char *) realloc (buffer
->ptr
, buffer
->size
);
264 memcpy (buffer
->ptr
+ buffer
->write_idx
, buf
, len
);
265 buffer
->write_idx
+= len
;
269 cattext (buffer
, string
)
273 catbuf (buffer
, string
, (unsigned int) strlen (string
));
281 catbuf (dst
, src
->ptr
, src
->write_idx
);
285 skip_white_and_stars (src
, idx
)
290 while ((c
= at (src
, idx
)),
291 isspace ((unsigned char) c
)
293 /* Don't skip past end-of-comment or star as first
294 character on its line. */
295 && at (src
, idx
+1) != '/'
296 && at (src
, idx
-1) != '\n'))
302 skip_past_newline_1 (ptr
, idx
)
307 && at (ptr
, idx
) != '\n')
309 if (at (ptr
, idx
) == '\n')
318 die ("underflow in string stack");
319 if (tos
>= stack
+ STACK
)
320 die ("overflow in string stack");
327 die ("underflow in integer stack");
328 if (isp
>= istack
+ STACK
)
329 die ("overflow in integer stack");
333 static void exec (dict_type
*);
334 static void call (void);
335 static void remchar (void), strip_trailing_newlines (void), push_number (void);
336 static void push_text (void);
337 static void remove_noncomments (string_type
*, string_type
*);
338 static void print_stack_level (void);
339 static void paramstuff (void), translatecomments (void);
340 static void outputdots (void), courierize (void), bulletize (void);
341 static void do_fancy_stuff (void);
342 static int iscommand (string_type
*, unsigned int);
343 static int copy_past_newline (string_type
*, unsigned int, string_type
*);
344 static void icopy_past_newline (void), kill_bogus_lines (void), indent (void);
345 static void get_stuff_in_command (void), swap (void), other_dup (void);
346 static void drop (void), idrop (void);
347 static void icatstr (void), skip_past_newline (void), internalmode (void);
348 static void maybecatstr (void);
349 static char *nextword (char *, char **);
350 dict_type
*lookup_word (char *);
351 static void perform (void);
352 dict_type
*newentry (char *);
353 unsigned int add_to_definition (dict_type
*, stinst_type
);
354 void add_intrinsic (char *, void (*)());
355 void compile (char *);
356 static void bang (void);
357 static void atsign (void);
358 static void hello (void);
359 static void stdout_ (void);
360 static void stderr_ (void);
361 static void print (void);
362 static void read_in (string_type
*, FILE *);
363 static void usage (void);
364 static void chew_exit (void);
379 stinst_type
*oldpc
= pc
;
381 e
= (dict_type
*) (pc
[1]);
395 strip_trailing_newlines ()
397 while ((isspace ((unsigned char) at (tos
, tos
->write_idx
- 1))
398 || at (tos
, tos
->write_idx
- 1) == '\n')
399 && tos
->write_idx
> 0)
421 cattext (tos
, *((char **) pc
));
425 /* This function removes everything not inside comments starting on
426 the first char of the line from the string, also when copying
427 comments, removes blank space and leading *'s.
428 Blank lines are turned into one blank line. */
431 remove_noncomments (src
, dst
)
435 unsigned int idx
= 0;
437 while (at (src
, idx
))
439 /* Now see if we have a comment at the start of the line. */
440 if (at (src
, idx
) == '\n'
441 && at (src
, idx
+ 1) == '/'
442 && at (src
, idx
+ 2) == '*')
446 idx
= skip_white_and_stars (src
, idx
);
448 /* Remove leading dot */
449 if (at (src
, idx
) == '.')
452 /* Copy to the end of the line, or till the end of the
454 while (at (src
, idx
))
456 if (at (src
, idx
) == '\n')
458 /* end of line, echo and scrape of leading blanks */
459 if (at (src
, idx
+ 1) == '\n')
463 idx
= skip_white_and_stars (src
, idx
);
465 else if (at (src
, idx
) == '*' && at (src
, idx
+ 1) == '/')
468 cattext (dst
, "\nENDDD\n");
473 catchar (dst
, at (src
, idx
));
486 fprintf (stderr
, "current string stack depth = %ld, ",
487 (long) (tos
- stack
));
488 fprintf (stderr
, "current integer stack depth = %ld\n",
489 (long) (isp
- istack
));
497 name PARAMS ((stuff));
513 /* Make sure that it's not already param'd or proto'd. */
515 || find (tos
, "PARAMS") || find (tos
, "PROTO") || !find (tos
, "("))
521 /* Find the open paren. */
522 for (openp
= 0; at (tos
, openp
) != '(' && at (tos
, openp
); openp
++)
526 /* Step back to the fname. */
528 while (fname
&& isspace ((unsigned char) at (tos
, fname
)))
531 && !isspace ((unsigned char) at (tos
,fname
))
532 && at (tos
,fname
) != '*')
537 /* Output type, omitting trailing whitespace character(s), if
539 for (len
= fname
; 0 < len
; len
--)
541 if (!isspace ((unsigned char) at (tos
, len
- 1)))
544 for (idx
= 0; idx
< len
; idx
++)
545 catchar (&out
, at (tos
, idx
));
547 cattext (&out
, "\n"); /* Insert a newline between type and fnname */
549 /* Output function name, omitting trailing whitespace
550 character(s), if any. */
551 for (len
= openp
; 0 < len
; len
--)
553 if (!isspace ((unsigned char) at (tos
, len
- 1)))
556 for (idx
= fname
; idx
< len
; idx
++)
557 catchar (&out
, at (tos
, idx
));
559 cattext (&out
, " PARAMS (");
561 for (idx
= openp
; at (tos
, idx
) && at (tos
, idx
) != ';'; idx
++)
562 catchar (&out
, at (tos
, idx
));
564 cattext (&out
, ");\n\n");
566 overwrite_string (tos
, &out
);
572 and *} into comments */
577 unsigned int idx
= 0;
581 while (at (tos
, idx
))
583 if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
585 cattext (&out
, "/*");
588 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
590 cattext (&out
, "*/");
595 catchar (&out
, at (tos
, idx
));
600 overwrite_string (tos
, &out
);
605 /* Mod tos so that only lines with leading dots remain */
609 unsigned int idx
= 0;
613 while (at (tos
, idx
))
615 /* Every iteration begins at the start of a line. */
616 if (at (tos
, idx
) == '.')
622 while ((c
= at (tos
, idx
)) && c
!= '\n')
624 if (c
== '{' && at (tos
, idx
+ 1) == '*')
626 cattext (&out
, "/*");
629 else if (c
== '*' && at (tos
, idx
+ 1) == '}')
631 cattext (&out
, "*/");
642 catchar (&out
, '\n');
646 idx
= skip_past_newline_1 (tos
, idx
);
650 overwrite_string (tos
, &out
);
654 /* Find lines starting with . and | and put example around them on tos */
659 unsigned int idx
= 0;
664 while (at (tos
, idx
))
666 if (at (tos
, idx
) == '\n'
667 && (at (tos
, idx
+1 ) == '.'
668 || at (tos
, idx
+ 1) == '|'))
670 cattext (&out
, "\n@example\n");
675 while (at (tos
, idx
) && at (tos
, idx
) != '\n')
679 /* We are inside {} parameters of some command;
680 Just pass through until matching brace. */
681 if (at (tos
, idx
) == '{')
683 else if (at (tos
, idx
) == '}')
686 else if (command
!= 0)
688 if (at (tos
, idx
) == '{')
690 else if (!islower ((unsigned char) at (tos
, idx
)))
693 else if (at (tos
, idx
) == '@'
694 && islower ((unsigned char) at (tos
, idx
+ 1)))
698 else if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
700 cattext (&out
, "/*");
704 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
706 cattext (&out
, "*/");
710 else if (at (tos
, idx
) == '{'
711 || at (tos
, idx
) == '}')
716 catchar (&out
, at (tos
, idx
));
719 catchar (&out
, '\n');
721 while (at (tos
, idx
) == '\n'
722 && ((at (tos
, idx
+ 1) == '.')
723 || (at (tos
, idx
+ 1) == '|')))
725 cattext (&out
, "@end example");
729 catchar (&out
, at (tos
, idx
));
734 overwrite_string (tos
, &out
);
738 /* Finds any lines starting with "o ", if there are any, then turns
739 on @itemize @bullet, and @items each of them. Then ends with @end
740 itemize, inplace at TOS*/
745 unsigned int idx
= 0;
750 while (at (tos
, idx
))
752 if (at (tos
, idx
) == '@'
753 && at (tos
, idx
+ 1) == '*')
758 else if (at (tos
, idx
) == '\n'
759 && at (tos
, idx
+ 1) == 'o'
760 && isspace ((unsigned char) at (tos
, idx
+ 2)))
764 cattext (&out
, "\n@itemize @bullet\n");
768 cattext (&out
, "\n@item\n");
773 catchar (&out
, at (tos
, idx
));
774 if (on
&& at (tos
, idx
) == '\n'
775 && at (tos
, idx
+ 1) == '\n'
776 && at (tos
, idx
+ 2) != 'o')
778 cattext (&out
, "@end itemize");
787 cattext (&out
, "@end itemize\n");
795 /* Turn <<foo>> into @code{foo} in place at TOS*/
800 unsigned int idx
= 0;
803 while (at (tos
, idx
))
805 if (at (tos
, idx
) == '<'
806 && at (tos
, idx
+ 1) == '<'
807 && !isspace ((unsigned char) at (tos
, idx
+ 2)))
809 /* This qualifies as a << startup. */
811 cattext (&out
, "@code{");
813 && at (tos
, idx
) != '>' )
815 catchar (&out
, at (tos
, idx
));
824 catchar (&out
, at (tos
, idx
));
834 /* A command is all upper case,and alone on a line. */
841 unsigned int len
= 0;
842 while (at (ptr
, idx
))
844 if (isupper ((unsigned char) at (ptr
, idx
))
845 || at (ptr
, idx
) == ' ' || at (ptr
, idx
) == '_')
850 else if (at (ptr
, idx
) == '\n')
863 copy_past_newline (ptr
, idx
, dst
)
870 while (at (ptr
, idx
) && at (ptr
, idx
) != '\n')
872 if (at (ptr
, idx
) == '\t')
874 /* Expand tabs. Neither makeinfo nor TeX can cope well with
878 while (++column
& 7);
882 catchar (dst
, at (ptr
, idx
));
888 catchar (dst
, at (ptr
, idx
));
895 icopy_past_newline ()
900 idx
= copy_past_newline (ptr
, idx
, tos
);
905 Take the string at the top of the stack, do some prettying. */
918 /* Drop leading nl. */
919 while (at (tos
, idx
) == '\n')
925 /* If the first char is a '.' prepend a newline so that it is
926 recognized properly later. */
927 if (at (tos
, idx
) == '.')
928 catchar (&out
, '\n');
930 /* Find the last char. */
931 while (at (tos
, idx
))
936 /* Find the last non white before the nl. */
939 while (idx
&& isspace ((unsigned char) at (tos
, idx
)))
943 /* Copy buffer upto last char, but blank lines before and after
949 if (at (tos
, c
) == '\n'
950 && at (tos
, c
+ 1) == '\n'
951 && at (tos
, c
+ 2) == '.')
953 /* Ignore two newlines before a dot. */
956 else if (at (tos
, c
) == '.' && sl
)
958 /* remember that this line started with a dot. */
961 else if (at (tos
, c
) == '\n'
962 && at (tos
, c
+ 1) == '\n'
966 /* Ignore two newlines when last line was dot. */
969 catchar (&out
, at (tos
, c
));
970 if (at (tos
, c
) == '\n')
987 catchar (&out
, '\n');
1002 while (at (tos
, idx
))
1004 switch (at (tos
, idx
))
1007 cattext (&out
, "\n");
1009 if (tab
&& at (tos
, idx
))
1011 cattext (&out
, " ");
1018 cattext (&out
, " ");
1020 cattext (&out
, "(");
1025 cattext (&out
, ")");
1031 catchar (&out
, at (tos
, idx
));
1040 delete_string (tos
);
1046 get_stuff_in_command ()
1052 while (at (ptr
, idx
))
1054 if (iscommand (ptr
, idx
))
1056 idx
= copy_past_newline (ptr
, idx
, tos
);
1078 catstr (tos
, tos
- 1);
1087 delete_string (tos
+ 1);
1104 catstr (tos
, tos
+ 1);
1105 delete_string (tos
+ 1);
1110 skip_past_newline ()
1112 idx
= skip_past_newline_1 (ptr
, idx
);
1119 internal_mode
= *(isp
);
1128 if (internal_wanted
== internal_mode
)
1130 catstr (tos
- 1, tos
);
1132 delete_string (tos
);
1139 nextword (string
, word
)
1150 while (isspace ((unsigned char) *string
) || *string
== '-')
1154 while (*string
&& *string
!= '\n')
1169 word_start
= string
;
1176 if (*string
== '\\')
1182 while (*string
!= '"');
1186 while (!isspace ((unsigned char) *string
))
1194 *word
= (char *) malloc (length
+ 1);
1199 for (idx
= 0; idx
< length
; idx
++)
1201 if (src
[idx
] == '\\')
1202 switch (src
[idx
+ 1])
1210 *dst
++ = src
[idx
+ 1];
1232 dict_type
*ptr
= root
;
1235 if (strcmp (ptr
->word
, word
) == 0)
1240 fprintf (stderr
, "Can't find %s\n", word
);
1247 dict_type
*ptr
= root
;
1257 for (i
= 0; i
< ptr
->code_end
- 1; i
++)
1258 if (ptr
->code
[i
] == push_text
1259 && ptr
->code
[i
+ 1])
1261 free ((char *) ptr
->code
[i
+ 1] - 1);
1277 while (at (ptr
, idx
))
1279 /* It's worth looking through the command list. */
1280 if (iscommand (ptr
, idx
))
1285 (void) nextword (addr (ptr
, idx
), &next
);
1287 word
= lookup_word (next
);
1296 fprintf (stderr
, "warning, %s is not recognised\n", next
);
1297 idx
= skip_past_newline_1 (ptr
, idx
);
1302 idx
= skip_past_newline_1 (ptr
, idx
);
1310 dict_type
*new_d
= (dict_type
*) malloc (sizeof (dict_type
));
1314 new_d
->code
= (stinst_type
*) malloc (sizeof (stinst_type
));
1315 new_d
->code_length
= 1;
1316 new_d
->code_end
= 0;
1321 add_to_definition (entry
, word
)
1325 if (entry
->code_end
== entry
->code_length
)
1327 entry
->code_length
+= 2;
1329 (stinst_type
*) realloc ((char *) (entry
->code
),
1330 entry
->code_length
* sizeof (stinst_type
));
1332 entry
->code
[entry
->code_end
] = word
;
1334 return entry
->code_end
++;
1338 add_intrinsic (name
, func
)
1342 dict_type
*new_d
= newentry (strdup (name
));
1343 add_to_definition (new_d
, func
);
1344 add_to_definition (new_d
, 0);
1351 /* Add words to the dictionary. */
1354 string
= nextword (string
, &word
);
1355 while (string
&& *string
&& word
[0])
1361 /* Compile a word and add to dictionary. */
1363 string
= nextword (string
, &word
);
1366 ptr
= newentry (word
);
1367 string
= nextword (string
, &word
);
1375 while (word
[0] != ';')
1380 /* got a string, embed magic push string
1382 add_to_definition (ptr
, push_text
);
1383 add_to_definition (ptr
, (stinst_type
) (word
+ 1));
1395 /* Got a number, embedd the magic push number
1397 add_to_definition (ptr
, push_number
);
1398 add_to_definition (ptr
, (stinst_type
) atol (word
));
1402 add_to_definition (ptr
, call
);
1403 add_to_definition (ptr
, (stinst_type
) lookup_word (word
));
1407 string
= nextword (string
, &word
);
1409 add_to_definition (ptr
, 0);
1411 string
= nextword (string
, &word
);
1415 fprintf (stderr
, "syntax error at %s\n", string
- 1);
1424 *(long *) ((isp
[0])) = isp
[-1];
1433 isp
[0] = *(long *) (isp
[0]);
1466 write_buffer (tos
, stdout
);
1468 write_buffer (tos
, stderr
);
1470 fprintf (stderr
, "print: illegal print destination `%ld'\n", *isp
);
1487 r
= fread (buff
, 1, sizeof (buff
), file
);
1488 catbuf (str
, buff
, r
);
1493 catbuf (str
, buff
, 1);
1499 fprintf (stderr
, "usage: -[d|i|g] <file >file\n");
1503 /* There is no reliable way to declare exit. Sometimes it returns
1504 int, and sometimes it returns void. Sometimes it changes between
1505 OS releases. Trying to get it declared correctly in the hosts file
1506 is a pointless waste of time. */
1523 init_string (&buffer
);
1524 init_string (&pptr
);
1525 init_string (stack
+ 0);
1529 add_intrinsic ("push_text", push_text
);
1530 add_intrinsic ("!", bang
);
1531 add_intrinsic ("@", atsign
);
1532 add_intrinsic ("hello", hello
);
1533 add_intrinsic ("stdout", stdout_
);
1534 add_intrinsic ("stderr", stderr_
);
1535 add_intrinsic ("print", print
);
1536 add_intrinsic ("skip_past_newline", skip_past_newline
);
1537 add_intrinsic ("catstr", icatstr
);
1538 add_intrinsic ("copy_past_newline", icopy_past_newline
);
1539 add_intrinsic ("dup", other_dup
);
1540 add_intrinsic ("drop", drop
);
1541 add_intrinsic ("idrop", idrop
);
1542 add_intrinsic ("remchar", remchar
);
1543 add_intrinsic ("get_stuff_in_command", get_stuff_in_command
);
1544 add_intrinsic ("do_fancy_stuff", do_fancy_stuff
);
1545 add_intrinsic ("bulletize", bulletize
);
1546 add_intrinsic ("courierize", courierize
);
1547 /* If the following line gives an error, exit() is not declared in the
1548 ../hosts/foo.h file for this host. Fix it there, not here! */
1549 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1550 add_intrinsic ("exit", chew_exit
);
1551 add_intrinsic ("swap", swap
);
1552 add_intrinsic ("outputdots", outputdots
);
1553 add_intrinsic ("paramstuff", paramstuff
);
1554 add_intrinsic ("maybecatstr", maybecatstr
);
1555 add_intrinsic ("translatecomments", translatecomments
);
1556 add_intrinsic ("kill_bogus_lines", kill_bogus_lines
);
1557 add_intrinsic ("indent", indent
);
1558 add_intrinsic ("internalmode", internalmode
);
1559 add_intrinsic ("print_stack_level", print_stack_level
);
1560 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines
);
1562 /* Put a nl at the start. */
1563 catchar (&buffer
, '\n');
1565 read_in (&buffer
, stdin
);
1566 remove_noncomments (&buffer
, ptr
);
1567 for (i
= 1; i
< (unsigned int) ac
; i
++)
1569 if (av
[i
][0] == '-')
1571 if (av
[i
][1] == 'f')
1577 f
= fopen (av
[i
+ 1], "r");
1580 fprintf (stderr
, "Can't open the input file %s\n",
1590 else if (av
[i
][1] == 'i')
1592 internal_wanted
= 1;
1594 else if (av
[i
][1] == 'w')
1602 write_buffer (stack
+ 0, stdout
);
1604 delete_string (&pptr
);
1605 delete_string (&buffer
);
1608 fprintf (stderr
, "finishing with current stack level %ld\n",
1609 (long) (tos
- stack
));