2 Copyright (C) 1990-2023 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 maybecatstr - do catstr if internal_mode == internal_wanted, discard
70 translatecomments - turn {* and *} into comment delimiters
71 kill_bogus_lines - get rid of extra newlines
73 internalmode - pop from integer stack, set `internalmode' to that value
74 print_stack_level - print current stack depth to stderr
75 strip_trailing_newlines - go ahead, guess...
76 [quoted string] - push string onto string stack
77 [word starting with digit] - push atol(str) onto integer stack
79 A command must be all upper-case, and alone on a line.
93 /* Here is a string type ... */
98 unsigned long write_idx
;
102 /* Compiled programs consist of arrays of these. */
107 struct dict_struct
*e
;
112 typedef struct dict_struct
115 struct dict_struct
*next
;
122 intptr_t internal_mode
;
126 string_type stack
[STACK
];
129 unsigned int idx
= 0; /* Pos in input buffer */
130 string_type
*ptr
; /* and the buffer */
132 intptr_t istack
[STACK
];
133 intptr_t *isp
= &istack
[0];
142 fprintf (stderr
, "%s\n", msg
);
147 xmalloc (size_t size
)
153 newmem
= malloc (size
);
155 die ("out of memory");
161 xrealloc (void *oldmem
, size_t size
)
168 newmem
= malloc (size
);
170 newmem
= realloc (oldmem
, size
);
172 die ("out of memory");
178 xstrdup (const char *s
)
180 size_t len
= strlen (s
) + 1;
181 char *ret
= xmalloc (len
);
182 return memcpy (ret
, s
, len
);
186 init_string_with_size (string_type
*buffer
, unsigned int size
)
188 buffer
->write_idx
= 0;
190 buffer
->ptr
= xmalloc (size
);
194 init_string (string_type
*buffer
)
196 init_string_with_size (buffer
, DEF_SIZE
);
200 find (string_type
*str
, char *what
)
205 for (i
= 0; i
< str
->write_idx
&& *p
; i
++)
207 if (*p
== str
->ptr
[i
])
216 write_buffer (string_type
*buffer
, FILE *f
)
218 if (buffer
->write_idx
!= 0
219 && fwrite (buffer
->ptr
, buffer
->write_idx
, 1, f
) != 1)
220 die ("cannot write output");
224 delete_string (string_type
*buffer
)
231 addr (string_type
*buffer
, unsigned int idx
)
233 return buffer
->ptr
+ idx
;
237 at (string_type
*buffer
, unsigned int pos
)
239 if (pos
>= buffer
->write_idx
)
241 return buffer
->ptr
[pos
];
245 catchar (string_type
*buffer
, int ch
)
247 if (buffer
->write_idx
== buffer
->size
)
250 buffer
->ptr
= xrealloc (buffer
->ptr
, buffer
->size
);
253 buffer
->ptr
[buffer
->write_idx
++] = ch
;
257 overwrite_string (string_type
*dst
, string_type
*src
)
260 dst
->size
= src
->size
;
261 dst
->write_idx
= src
->write_idx
;
266 catbuf (string_type
*buffer
, char *buf
, unsigned int len
)
268 if (buffer
->write_idx
+ len
>= buffer
->size
)
270 while (buffer
->write_idx
+ len
>= buffer
->size
)
272 buffer
->ptr
= xrealloc (buffer
->ptr
, buffer
->size
);
274 memcpy (buffer
->ptr
+ buffer
->write_idx
, buf
, len
);
275 buffer
->write_idx
+= len
;
279 cattext (string_type
*buffer
, char *string
)
281 catbuf (buffer
, string
, (unsigned int) strlen (string
));
285 catstr (string_type
*dst
, string_type
*src
)
287 catbuf (dst
, src
->ptr
, src
->write_idx
);
291 skip_white_and_stars (string_type
*src
, unsigned int idx
)
294 while ((c
= at (src
, idx
)),
295 isspace ((unsigned char) c
)
297 /* Don't skip past end-of-comment or star as first
298 character on its line. */
299 && at (src
, idx
+1) != '/'
300 && at (src
, idx
-1) != '\n'))
306 skip_past_newline_1 (string_type
*ptr
, unsigned int idx
)
309 && at (ptr
, idx
) != '\n')
311 if (at (ptr
, idx
) == '\n')
320 die ("underflow in string stack");
321 if (tos
>= stack
+ STACK
)
322 die ("overflow in string stack");
329 die ("underflow in integer stack");
330 if (isp
>= istack
+ STACK
)
331 die ("overflow in integer stack");
335 exec (dict_type
*word
)
346 dict_type
*e
= pc
[1].e
;
360 strip_trailing_newlines (void)
362 while ((isspace ((unsigned char) at (tos
, tos
->write_idx
- 1))
363 || at (tos
, tos
->write_idx
- 1) == '\n')
364 && tos
->write_idx
> 0)
386 cattext (tos
, pc
->s
);
390 /* This function removes everything not inside comments starting on
391 the first char of the line from the string, also when copying
392 comments, removes blank space and leading *'s.
393 Blank lines are turned into one blank line. */
396 remove_noncomments (string_type
*src
, string_type
*dst
)
398 unsigned int idx
= 0;
400 while (at (src
, idx
))
402 /* Now see if we have a comment at the start of the line. */
403 if (at (src
, idx
) == '\n'
404 && at (src
, idx
+ 1) == '/'
405 && at (src
, idx
+ 2) == '*')
409 idx
= skip_white_and_stars (src
, idx
);
411 /* Remove leading dot */
412 if (at (src
, idx
) == '.')
415 /* Copy to the end of the line, or till the end of the
417 while (at (src
, idx
))
419 if (at (src
, idx
) == '\n')
421 /* end of line, echo and scrape of leading blanks */
422 if (at (src
, idx
+ 1) == '\n')
426 idx
= skip_white_and_stars (src
, idx
);
428 else if (at (src
, idx
) == '*' && at (src
, idx
+ 1) == '/')
431 cattext (dst
, "\nENDDD\n");
436 catchar (dst
, at (src
, idx
));
447 print_stack_level (void)
449 fprintf (stderr
, "current string stack depth = %ld, ",
450 (long) (tos
- stack
));
451 fprintf (stderr
, "current integer stack depth = %ld\n",
452 (long) (isp
- istack
));
457 and *} into comments */
460 translatecomments (void)
462 unsigned int idx
= 0;
466 while (at (tos
, idx
))
468 if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
470 cattext (&out
, "/*");
473 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
475 cattext (&out
, "*/");
480 catchar (&out
, at (tos
, idx
));
485 overwrite_string (tos
, &out
);
490 /* Mod tos so that only lines with leading dots remain */
494 unsigned int idx
= 0;
498 while (at (tos
, idx
))
500 /* Every iteration begins at the start of a line. */
501 if (at (tos
, idx
) == '.')
507 while ((c
= at (tos
, idx
)) && c
!= '\n')
509 if (c
== '{' && at (tos
, idx
+ 1) == '*')
511 cattext (&out
, "/*");
514 else if (c
== '*' && at (tos
, idx
+ 1) == '}')
516 cattext (&out
, "*/");
527 catchar (&out
, '\n');
531 idx
= skip_past_newline_1 (tos
, idx
);
535 overwrite_string (tos
, &out
);
539 /* Find lines starting with . and | and put example around them on tos */
544 unsigned int idx
= 0;
549 while (at (tos
, idx
))
551 if (at (tos
, idx
) == '\n'
552 && (at (tos
, idx
+1 ) == '.'
553 || at (tos
, idx
+ 1) == '|'))
555 cattext (&out
, "\n@example\n");
560 while (at (tos
, idx
) && at (tos
, idx
) != '\n')
564 /* We are inside {} parameters of some command;
565 Just pass through until matching brace. */
566 if (at (tos
, idx
) == '{')
568 else if (at (tos
, idx
) == '}')
571 else if (command
!= 0)
573 if (at (tos
, idx
) == '{')
575 else if (!islower ((unsigned char) at (tos
, idx
)))
578 else if (at (tos
, idx
) == '@'
579 && islower ((unsigned char) at (tos
, idx
+ 1)))
583 else if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
585 cattext (&out
, "/*");
589 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
591 cattext (&out
, "*/");
595 else if (at (tos
, idx
) == '{'
596 || at (tos
, idx
) == '}')
601 catchar (&out
, at (tos
, idx
));
604 catchar (&out
, '\n');
606 while (at (tos
, idx
) == '\n'
607 && ((at (tos
, idx
+ 1) == '.')
608 || (at (tos
, idx
+ 1) == '|')))
610 cattext (&out
, "@end example");
614 catchar (&out
, at (tos
, idx
));
619 overwrite_string (tos
, &out
);
623 /* Finds any lines starting with "o ", if there are any, then turns
624 on @itemize @bullet, and @items each of them. Then ends with @end
625 itemize, inplace at TOS*/
630 unsigned int idx
= 0;
635 while (at (tos
, idx
))
637 if (at (tos
, idx
) == '@'
638 && at (tos
, idx
+ 1) == '*')
643 else if (at (tos
, idx
) == '\n'
644 && at (tos
, idx
+ 1) == 'o'
645 && isspace ((unsigned char) at (tos
, idx
+ 2)))
649 cattext (&out
, "\n@itemize @bullet\n");
653 cattext (&out
, "\n@item\n");
658 catchar (&out
, at (tos
, idx
));
659 if (on
&& at (tos
, idx
) == '\n'
660 && at (tos
, idx
+ 1) == '\n'
661 && at (tos
, idx
+ 2) != 'o')
663 cattext (&out
, "@end itemize");
672 cattext (&out
, "@end itemize\n");
680 /* Turn <<foo>> into @code{foo} in place at TOS*/
683 do_fancy_stuff (void)
685 unsigned int idx
= 0;
688 while (at (tos
, idx
))
690 if (at (tos
, idx
) == '<'
691 && at (tos
, idx
+ 1) == '<'
692 && !isspace ((unsigned char) at (tos
, idx
+ 2)))
694 /* This qualifies as a << startup. */
696 cattext (&out
, "@code{");
698 && at (tos
, idx
) != '>' )
700 catchar (&out
, at (tos
, idx
));
709 catchar (&out
, at (tos
, idx
));
719 /* A command is all upper case,and alone on a line. */
722 iscommand (string_type
*ptr
, unsigned int idx
)
724 unsigned int len
= 0;
725 while (at (ptr
, idx
))
727 if (isupper ((unsigned char) at (ptr
, idx
))
728 || at (ptr
, idx
) == ' ' || at (ptr
, idx
) == '_')
733 else if (at (ptr
, idx
) == '\n')
746 copy_past_newline (string_type
*ptr
, unsigned int idx
, string_type
*dst
)
750 while (at (ptr
, idx
) && at (ptr
, idx
) != '\n')
752 if (at (ptr
, idx
) == '\t')
754 /* Expand tabs. Neither makeinfo nor TeX can cope well with
758 while (++column
& 7);
762 catchar (dst
, at (ptr
, idx
));
768 catchar (dst
, at (ptr
, idx
));
775 icopy_past_newline (void)
780 idx
= copy_past_newline (ptr
, idx
, tos
);
785 Take the string at the top of the stack, do some prettying. */
788 kill_bogus_lines (void)
798 /* Drop leading nl. */
799 while (at (tos
, idx
) == '\n')
805 /* If the first char is a '.' prepend a newline so that it is
806 recognized properly later. */
807 if (at (tos
, idx
) == '.')
808 catchar (&out
, '\n');
810 /* Find the last char. */
811 while (at (tos
, idx
))
816 /* Find the last non white before the nl. */
819 while (idx
&& isspace ((unsigned char) at (tos
, idx
)))
823 /* Copy buffer upto last char, but blank lines before and after
829 if (at (tos
, c
) == '\n'
830 && at (tos
, c
+ 1) == '\n'
831 && at (tos
, c
+ 2) == '.')
833 /* Ignore two newlines before a dot. */
836 else if (at (tos
, c
) == '.' && sl
)
838 /* remember that this line started with a dot. */
841 else if (at (tos
, c
) == '\n'
842 && at (tos
, c
+ 1) == '\n'
846 /* Ignore two newlines when last line was dot. */
849 catchar (&out
, at (tos
, c
));
850 if (at (tos
, c
) == '\n')
867 catchar (&out
, '\n');
882 while (at (tos
, idx
))
884 switch (at (tos
, idx
))
887 cattext (&out
, "\n");
889 if (tab
&& at (tos
, idx
))
911 catchar (&out
, at (tos
, idx
));
926 get_stuff_in_command (void)
932 while (at (ptr
, idx
))
934 if (iscommand (ptr
, idx
))
936 idx
= copy_past_newline (ptr
, idx
, tos
);
958 catstr (tos
, tos
- 1);
967 delete_string (tos
+ 1);
984 catstr (tos
, tos
+ 1);
985 delete_string (tos
+ 1);
990 skip_past_newline (void)
992 idx
= skip_past_newline_1 (ptr
, idx
);
999 internal_mode
= *(isp
);
1008 if (internal_wanted
== internal_mode
)
1010 catstr (tos
- 1, tos
);
1012 delete_string (tos
);
1019 nextword (char *string
, char **word
)
1028 while (isspace ((unsigned char) *string
) || *string
== '-')
1032 while (*string
&& *string
!= '\n')
1047 word_start
= string
;
1054 if (*string
== '\\')
1060 while (*string
!= '"');
1064 while (!isspace ((unsigned char) *string
))
1072 *word
= xmalloc (length
+ 1);
1077 for (idx
= 0; idx
< length
; idx
++)
1079 if (src
[idx
] == '\\')
1080 switch (src
[idx
+ 1])
1088 *dst
++ = src
[idx
+ 1];
1107 lookup_word (char *word
)
1109 dict_type
*ptr
= root
;
1112 if (strcmp (ptr
->word
, word
) == 0)
1117 fprintf (stderr
, "Can't find %s\n", word
);
1124 dict_type
*ptr
= root
;
1134 for (i
= 0; i
< ptr
->code_end
- 1; i
++)
1135 if (ptr
->code
[i
].f
== push_text
1136 && ptr
->code
[i
+ 1].s
)
1138 free (ptr
->code
[i
+ 1].s
- 1);
1154 while (at (ptr
, idx
))
1156 /* It's worth looking through the command list. */
1157 if (iscommand (ptr
, idx
))
1162 (void) nextword (addr (ptr
, idx
), &next
);
1164 word
= lookup_word (next
);
1173 fprintf (stderr
, "warning, %s is not recognised\n", next
);
1174 idx
= skip_past_newline_1 (ptr
, idx
);
1179 idx
= skip_past_newline_1 (ptr
, idx
);
1184 newentry (char *word
)
1186 dict_type
*new_d
= xmalloc (sizeof (*new_d
));
1190 new_d
->code
= xmalloc (sizeof (*new_d
->code
));
1191 new_d
->code_length
= 1;
1192 new_d
->code_end
= 0;
1197 add_to_definition (dict_type
*entry
, pcu word
)
1199 if (entry
->code_end
== entry
->code_length
)
1201 entry
->code_length
+= 2;
1202 entry
->code
= xrealloc (entry
->code
,
1203 entry
->code_length
* sizeof (*entry
->code
));
1205 entry
->code
[entry
->code_end
] = word
;
1207 return entry
->code_end
++;
1211 add_intrinsic (char *name
, void (*func
) (void))
1213 dict_type
*new_d
= newentry (xstrdup (name
));
1215 add_to_definition (new_d
, p
);
1217 add_to_definition (new_d
, p
);
1221 compile (char *string
)
1223 /* Add words to the dictionary. */
1226 string
= nextword (string
, &word
);
1227 while (string
&& *string
&& word
[0])
1234 /* Compile a word and add to dictionary. */
1236 string
= nextword (string
, &word
);
1239 ptr
= newentry (word
);
1240 string
= nextword (string
, &word
);
1248 while (word
[0] != ';')
1253 /* got a string, embed magic push string
1256 add_to_definition (ptr
, p
);
1258 add_to_definition (ptr
, p
);
1270 /* Got a number, embedd the magic push number
1273 add_to_definition (ptr
, p
);
1275 add_to_definition (ptr
, p
);
1280 add_to_definition (ptr
, p
);
1281 p
.e
= lookup_word (word
);
1282 add_to_definition (ptr
, p
);
1286 string
= nextword (string
, &word
);
1289 add_to_definition (ptr
, p
);
1291 string
= nextword (string
, &word
);
1295 fprintf (stderr
, "syntax error at %s\n", string
- 1);
1304 *(intptr_t *) ((isp
[0])) = isp
[-1];
1313 isp
[0] = *(intptr_t *) (isp
[0]);
1346 write_buffer (tos
, stdout
);
1348 write_buffer (tos
, stderr
);
1350 fprintf (stderr
, "print: illegal print destination `%ld'\n", *isp
);
1359 read_in (string_type
*str
, FILE *file
)
1365 r
= fread (buff
, 1, sizeof (buff
), file
);
1366 catbuf (str
, buff
, r
);
1371 catbuf (str
, buff
, 1);
1377 fprintf (stderr
, "usage: -[d|i|g] <file >file\n");
1381 /* There is no reliable way to declare exit. Sometimes it returns
1382 int, and sometimes it returns void. Sometimes it changes between
1383 OS releases. Trying to get it declared correctly in the hosts file
1384 is a pointless waste of time. */
1393 main (int ac
, char *av
[])
1399 init_string (&buffer
);
1400 init_string (&pptr
);
1401 init_string (stack
+ 0);
1405 add_intrinsic ("push_text", push_text
);
1406 add_intrinsic ("!", bang
);
1407 add_intrinsic ("@", atsign
);
1408 add_intrinsic ("hello", hello
);
1409 add_intrinsic ("stdout", stdout_
);
1410 add_intrinsic ("stderr", stderr_
);
1411 add_intrinsic ("print", print
);
1412 add_intrinsic ("skip_past_newline", skip_past_newline
);
1413 add_intrinsic ("catstr", icatstr
);
1414 add_intrinsic ("copy_past_newline", icopy_past_newline
);
1415 add_intrinsic ("dup", other_dup
);
1416 add_intrinsic ("drop", drop
);
1417 add_intrinsic ("idrop", idrop
);
1418 add_intrinsic ("remchar", remchar
);
1419 add_intrinsic ("get_stuff_in_command", get_stuff_in_command
);
1420 add_intrinsic ("do_fancy_stuff", do_fancy_stuff
);
1421 add_intrinsic ("bulletize", bulletize
);
1422 add_intrinsic ("courierize", courierize
);
1423 /* If the following line gives an error, exit() is not declared in the
1424 ../hosts/foo.h file for this host. Fix it there, not here! */
1425 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1426 add_intrinsic ("exit", chew_exit
);
1427 add_intrinsic ("swap", swap
);
1428 add_intrinsic ("outputdots", outputdots
);
1429 add_intrinsic ("maybecatstr", maybecatstr
);
1430 add_intrinsic ("translatecomments", translatecomments
);
1431 add_intrinsic ("kill_bogus_lines", kill_bogus_lines
);
1432 add_intrinsic ("indent", indent
);
1433 add_intrinsic ("internalmode", internalmode
);
1434 add_intrinsic ("print_stack_level", print_stack_level
);
1435 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines
);
1437 /* Put a nl at the start. */
1438 catchar (&buffer
, '\n');
1440 read_in (&buffer
, stdin
);
1441 remove_noncomments (&buffer
, ptr
);
1442 for (i
= 1; i
< (unsigned int) ac
; i
++)
1444 if (av
[i
][0] == '-')
1446 if (av
[i
][1] == 'f')
1452 f
= fopen (av
[i
+ 1], "r");
1455 fprintf (stderr
, "Can't open the input file %s\n",
1465 else if (av
[i
][1] == 'i')
1467 internal_wanted
= 1;
1469 else if (av
[i
][1] == 'w')
1477 write_buffer (stack
+ 0, stdout
);
1479 delete_string (&pptr
);
1480 delete_string (&buffer
);
1483 fprintf (stderr
, "finishing with current stack level %ld\n",
1484 (long) (tos
- stack
));