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> ;
34 Variables are defined using:
39 /* Primitives provided by the program:
41 Two stacks are provided, a string stack and an integer stack.
43 Internal state variables:
44 internal_wanted - indicates whether `-i' was passed
45 internal_mode - user-settable
49 ! - pop top of integer stack for address, pop next for value; store
50 @ - treat value on integer stack as the address of an integer; push
51 that integer on the integer stack after popping the "address"
52 hello - print "hello\n" to stdout
53 stdout - put stdout marker on TOS
54 stderr - put stderr marker on TOS
55 print - print TOS-1 on TOS (eg: "hello\n" stdout print)
58 copy_past_newline - append input, up to and including newline into TOS
62 remchar - delete last character from TOS
64 do_fancy_stuff - translate <<foo>> to @code{foo} in TOS
65 bulletize - if "o" lines found, prepend @itemize @bullet to TOS
66 and @item to each "o" line; append @end itemize
67 courierize - put @example around . and | lines, translate {* *} { }
70 outputdots - strip out lines without leading dots
71 maybecatstr - do catstr if internal_mode == internal_wanted, discard
73 catstrif - do catstr if top of integer stack is nonzero
74 translatecomments - turn {* and *} into comment delimiters
75 kill_bogus_lines - get rid of extra newlines
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 internalmode - the internalmode variable (evaluates to address)
84 A command must be all upper-case, and alone on a line.
99 /* Here is a string type ... */
101 typedef struct buffer
104 unsigned long write_idx
;
108 /* Compiled programs consist of arrays of these. */
113 struct dict_struct
*e
;
118 typedef struct dict_struct
121 struct dict_struct
*next
;
128 intptr_t *internal_mode
;
132 string_type stack
[STACK
];
135 unsigned int idx
= 0; /* Pos in input buffer */
136 string_type
*ptr
; /* and the buffer */
138 intptr_t istack
[STACK
];
139 intptr_t *isp
= &istack
[0];
148 fprintf (stderr
, "%s\n", msg
);
153 xmalloc (size_t size
)
159 newmem
= malloc (size
);
161 die ("out of memory");
167 xrealloc (void *oldmem
, size_t size
)
174 newmem
= malloc (size
);
176 newmem
= realloc (oldmem
, size
);
178 die ("out of memory");
184 xstrdup (const char *s
)
186 size_t len
= strlen (s
) + 1;
187 char *ret
= xmalloc (len
);
188 return memcpy (ret
, s
, len
);
192 init_string_with_size (string_type
*buffer
, unsigned int size
)
194 buffer
->write_idx
= 0;
196 buffer
->ptr
= xmalloc (size
);
200 init_string (string_type
*buffer
)
202 init_string_with_size (buffer
, DEF_SIZE
);
206 find (string_type
*str
, char *what
)
211 for (i
= 0; i
< str
->write_idx
&& *p
; i
++)
213 if (*p
== str
->ptr
[i
])
222 write_buffer (string_type
*buffer
, FILE *f
)
224 if (buffer
->write_idx
!= 0
225 && fwrite (buffer
->ptr
, buffer
->write_idx
, 1, f
) != 1)
226 die ("cannot write output");
230 delete_string (string_type
*buffer
)
237 addr (string_type
*buffer
, unsigned int idx
)
239 return buffer
->ptr
+ idx
;
243 at (string_type
*buffer
, unsigned int pos
)
245 if (pos
>= buffer
->write_idx
)
247 return buffer
->ptr
[pos
];
251 catchar (string_type
*buffer
, int ch
)
253 if (buffer
->write_idx
== buffer
->size
)
256 buffer
->ptr
= xrealloc (buffer
->ptr
, buffer
->size
);
259 buffer
->ptr
[buffer
->write_idx
++] = ch
;
263 overwrite_string (string_type
*dst
, string_type
*src
)
266 dst
->size
= src
->size
;
267 dst
->write_idx
= src
->write_idx
;
272 catbuf (string_type
*buffer
, char *buf
, unsigned int len
)
274 if (buffer
->write_idx
+ len
>= buffer
->size
)
276 while (buffer
->write_idx
+ len
>= buffer
->size
)
278 buffer
->ptr
= xrealloc (buffer
->ptr
, buffer
->size
);
280 memcpy (buffer
->ptr
+ buffer
->write_idx
, buf
, len
);
281 buffer
->write_idx
+= len
;
285 cattext (string_type
*buffer
, char *string
)
287 catbuf (buffer
, string
, (unsigned int) strlen (string
));
291 catstr (string_type
*dst
, string_type
*src
)
293 catbuf (dst
, src
->ptr
, src
->write_idx
);
297 skip_white_and_stars (string_type
*src
, unsigned int idx
)
300 while ((c
= at (src
, idx
)),
301 isspace ((unsigned char) c
)
303 /* Don't skip past end-of-comment or star as first
304 character on its line. */
305 && at (src
, idx
+1) != '/'
306 && at (src
, idx
-1) != '\n'))
312 skip_past_newline_1 (string_type
*ptr
, unsigned int idx
)
315 && at (ptr
, idx
) != '\n')
317 if (at (ptr
, idx
) == '\n')
326 die ("underflow in string stack");
327 if (tos
>= stack
+ STACK
)
328 die ("overflow in string stack");
335 die ("underflow in integer stack");
336 if (isp
>= istack
+ STACK
)
337 die ("overflow in integer stack");
341 exec (dict_type
*word
)
352 dict_type
*e
= pc
[1].e
;
366 strip_trailing_newlines (void)
368 while ((isspace ((unsigned char) at (tos
, tos
->write_idx
- 1))
369 || at (tos
, tos
->write_idx
- 1) == '\n')
370 && tos
->write_idx
> 0)
385 /* This is a wrapper for push_number just so we can correctly free the
386 variable at the end. */
400 cattext (tos
, pc
->s
);
404 /* This function removes everything not inside comments starting on
405 the first char of the line from the string, also when copying
406 comments, removes blank space and leading *'s.
407 Blank lines are turned into one blank line. */
410 remove_noncomments (string_type
*src
, string_type
*dst
)
412 unsigned int idx
= 0;
414 while (at (src
, idx
))
416 /* Now see if we have a comment at the start of the line. */
417 if (at (src
, idx
) == '\n'
418 && at (src
, idx
+ 1) == '/'
419 && at (src
, idx
+ 2) == '*')
423 idx
= skip_white_and_stars (src
, idx
);
425 /* Remove leading dot */
426 if (at (src
, idx
) == '.')
429 /* Copy to the end of the line, or till the end of the
431 while (at (src
, idx
))
433 if (at (src
, idx
) == '\n')
435 /* end of line, echo and scrape of leading blanks */
436 if (at (src
, idx
+ 1) == '\n')
440 idx
= skip_white_and_stars (src
, idx
);
442 else if (at (src
, idx
) == '*' && at (src
, idx
+ 1) == '/')
445 cattext (dst
, "\nENDDD\n");
450 catchar (dst
, at (src
, idx
));
461 print_stack_level (void)
463 fprintf (stderr
, "current string stack depth = %ld, ",
464 (long) (tos
- stack
));
465 fprintf (stderr
, "current integer stack depth = %ld\n",
466 (long) (isp
- istack
));
471 and *} into comments */
474 translatecomments (void)
476 unsigned int idx
= 0;
480 while (at (tos
, idx
))
482 if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
484 cattext (&out
, "/*");
487 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
489 cattext (&out
, "*/");
494 catchar (&out
, at (tos
, idx
));
499 overwrite_string (tos
, &out
);
504 /* Mod tos so that only lines with leading dots remain */
508 unsigned int idx
= 0;
512 while (at (tos
, idx
))
514 /* Every iteration begins at the start of a line. */
515 if (at (tos
, idx
) == '.')
522 while ((c
= at (tos
, idx
)) && c
!= '\n')
536 catchar (&out
, '\t');
543 if (c
== '{' && at (tos
, idx
+ 1) == '*')
545 cattext (&out
, "/*");
548 else if (c
== '*' && at (tos
, idx
+ 1) == '}')
550 cattext (&out
, "*/");
561 catchar (&out
, '\n');
565 idx
= skip_past_newline_1 (tos
, idx
);
569 overwrite_string (tos
, &out
);
573 /* Find lines starting with . and | and put example around them on tos */
578 unsigned int idx
= 0;
583 while (at (tos
, idx
))
585 if (at (tos
, idx
) == '\n'
586 && (at (tos
, idx
+1 ) == '.'
587 || at (tos
, idx
+ 1) == '|'))
589 cattext (&out
, "\n@example\n");
594 while (at (tos
, idx
) && at (tos
, idx
) != '\n')
598 /* We are inside {} parameters of some command;
599 Just pass through until matching brace. */
600 if (at (tos
, idx
) == '{')
602 else if (at (tos
, idx
) == '}')
605 else if (command
!= 0)
607 if (at (tos
, idx
) == '{')
609 else if (!islower ((unsigned char) at (tos
, idx
)))
612 else if (at (tos
, idx
) == '@'
613 && islower ((unsigned char) at (tos
, idx
+ 1)))
617 else if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
619 cattext (&out
, "/*");
623 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
625 cattext (&out
, "*/");
629 else if (at (tos
, idx
) == '{'
630 || at (tos
, idx
) == '}')
635 catchar (&out
, at (tos
, idx
));
638 catchar (&out
, '\n');
640 while (at (tos
, idx
) == '\n'
641 && ((at (tos
, idx
+ 1) == '.')
642 || (at (tos
, idx
+ 1) == '|')))
644 cattext (&out
, "@end example");
648 catchar (&out
, at (tos
, idx
));
653 overwrite_string (tos
, &out
);
657 /* Finds any lines starting with "o ", if there are any, then turns
658 on @itemize @bullet, and @items each of them. Then ends with @end
659 itemize, inplace at TOS*/
664 unsigned int idx
= 0;
669 while (at (tos
, idx
))
671 if (at (tos
, idx
) == '@'
672 && at (tos
, idx
+ 1) == '*')
677 else if (at (tos
, idx
) == '\n'
678 && at (tos
, idx
+ 1) == 'o'
679 && isspace ((unsigned char) at (tos
, idx
+ 2)))
683 cattext (&out
, "\n@itemize @bullet\n");
687 cattext (&out
, "\n@item\n");
692 catchar (&out
, at (tos
, idx
));
693 if (on
&& at (tos
, idx
) == '\n'
694 && at (tos
, idx
+ 1) == '\n'
695 && at (tos
, idx
+ 2) != 'o')
697 cattext (&out
, "@end itemize");
706 cattext (&out
, "@end itemize\n");
714 /* Turn <<foo>> into @code{foo} in place at TOS*/
717 do_fancy_stuff (void)
719 unsigned int idx
= 0;
722 while (at (tos
, idx
))
724 if (at (tos
, idx
) == '<'
725 && at (tos
, idx
+ 1) == '<'
726 && !isspace ((unsigned char) at (tos
, idx
+ 2)))
728 /* This qualifies as a << startup. */
730 cattext (&out
, "@code{");
732 && at (tos
, idx
) != '>' )
734 catchar (&out
, at (tos
, idx
));
743 catchar (&out
, at (tos
, idx
));
753 /* A command is all upper case,and alone on a line. */
756 iscommand (string_type
*ptr
, unsigned int idx
)
758 unsigned int len
= 0;
759 while (at (ptr
, idx
))
761 if (isupper ((unsigned char) at (ptr
, idx
))
762 || at (ptr
, idx
) == ' ' || at (ptr
, idx
) == '_')
767 else if (at (ptr
, idx
) == '\n')
780 copy_past_newline (string_type
*ptr
, unsigned int idx
, string_type
*dst
)
784 while (at (ptr
, idx
) && at (ptr
, idx
) != '\n')
786 if (at (ptr
, idx
) == '\t')
788 /* Expand tabs. Neither makeinfo nor TeX can cope well with
792 while (++column
& 7);
796 catchar (dst
, at (ptr
, idx
));
802 catchar (dst
, at (ptr
, idx
));
809 icopy_past_newline (void)
814 idx
= copy_past_newline (ptr
, idx
, tos
);
819 kill_bogus_lines (void)
829 /* Drop leading nl. */
830 while (at (tos
, idx
) == '\n')
836 /* If the first char is a '.' prepend a newline so that it is
837 recognized properly later. */
838 if (at (tos
, idx
) == '.')
839 catchar (&out
, '\n');
841 /* Find the last char. */
842 while (at (tos
, idx
))
847 /* Find the last non white before the nl. */
850 while (idx
&& isspace ((unsigned char) at (tos
, idx
)))
854 /* Copy buffer upto last char, but blank lines before and after
860 if (at (tos
, c
) == '\n'
861 && at (tos
, c
+ 1) == '\n'
862 && at (tos
, c
+ 2) == '.')
864 /* Ignore two newlines before a dot. */
867 else if (at (tos
, c
) == '.' && sl
)
869 /* remember that this line started with a dot. */
872 else if (at (tos
, c
) == '\n'
873 && at (tos
, c
+ 1) == '\n'
877 /* Ignore two newlines when last line was dot. */
880 catchar (&out
, at (tos
, c
));
881 if (at (tos
, c
) == '\n')
898 catchar (&out
, '\n');
906 collapse_whitespace (void)
914 for (idx
= 0; at (tos
, idx
) != 0; ++idx
)
916 char c
= at (tos
, idx
);
938 Take the string at the top of the stack, do some prettying. */
948 while (at (tos
, idx
))
950 switch (at (tos
, idx
))
953 catchar (&out
, '\n');
955 if (tab
&& at (tos
, idx
))
958 for (i
= 0; i
< tab
- 1; i
+= 2)
959 catchar (&out
, '\t');
969 for (i
= 1; i
< tab
- 1; i
+= 2)
970 catchar (&out
, '\t');
987 catchar (&out
, at (tos
, idx
));
1001 get_stuff_in_command (void)
1007 while (at (ptr
, idx
))
1009 if (iscommand (ptr
, idx
))
1011 idx
= copy_past_newline (ptr
, idx
, tos
);
1033 catstr (tos
, tos
- 1);
1042 delete_string (tos
+ 1);
1059 catstr (tos
, tos
+ 1);
1060 delete_string (tos
+ 1);
1065 skip_past_newline (void)
1067 idx
= skip_past_newline_1 (ptr
, idx
);
1074 if (internal_wanted
== *internal_mode
)
1076 catstr (tos
- 1, tos
);
1078 delete_string (tos
);
1091 catstr (tos
- 1, tos
);
1092 delete_string (tos
);
1099 nextword (char *string
, char **word
)
1108 while (isspace ((unsigned char) *string
) || *string
== '-')
1112 while (*string
&& *string
!= '\n')
1127 word_start
= string
;
1134 if (*string
== '\\')
1140 while (*string
!= '"');
1144 while (!isspace ((unsigned char) *string
))
1152 *word
= xmalloc (length
+ 1);
1157 for (idx
= 0; idx
< length
; idx
++)
1159 if (src
[idx
] == '\\')
1160 switch (src
[idx
+ 1])
1168 *dst
++ = src
[idx
+ 1];
1187 lookup_word (char *word
)
1189 dict_type
*ptr
= root
;
1192 if (strcmp (ptr
->word
, word
) == 0)
1197 fprintf (stderr
, "Can't find %s\n", word
);
1204 dict_type
*ptr
= root
;
1214 for (i
= 0; i
< ptr
->code_end
- 1; i
++)
1215 if (ptr
->code
[i
].f
== push_text
1216 && ptr
->code
[i
+ 1].s
)
1218 free (ptr
->code
[i
+ 1].s
- 1);
1221 else if (ptr
->code
[i
].f
== push_variable
)
1223 free ((void *) ptr
->code
[i
+ 1].l
);
1239 while (at (ptr
, idx
))
1241 /* It's worth looking through the command list. */
1242 if (iscommand (ptr
, idx
))
1247 (void) nextword (addr (ptr
, idx
), &next
);
1249 word
= lookup_word (next
);
1258 fprintf (stderr
, "warning, %s is not recognised\n", next
);
1259 idx
= skip_past_newline_1 (ptr
, idx
);
1264 idx
= skip_past_newline_1 (ptr
, idx
);
1269 newentry (char *word
)
1271 dict_type
*new_d
= xmalloc (sizeof (*new_d
));
1275 new_d
->code
= xmalloc (sizeof (*new_d
->code
));
1276 new_d
->code_length
= 1;
1277 new_d
->code_end
= 0;
1282 add_to_definition (dict_type
*entry
, pcu word
)
1284 if (entry
->code_end
== entry
->code_length
)
1286 entry
->code_length
+= 2;
1287 entry
->code
= xrealloc (entry
->code
,
1288 entry
->code_length
* sizeof (*entry
->code
));
1290 entry
->code
[entry
->code_end
] = word
;
1292 return entry
->code_end
++;
1296 add_intrinsic (char *name
, void (*func
) (void))
1298 dict_type
*new_d
= newentry (xstrdup (name
));
1300 add_to_definition (new_d
, p
);
1302 add_to_definition (new_d
, p
);
1306 add_variable (char *name
, intptr_t *loc
)
1308 dict_type
*new_d
= newentry (name
);
1309 pcu p
= { push_variable
};
1310 add_to_definition (new_d
, p
);
1311 p
.l
= (intptr_t) loc
;
1312 add_to_definition (new_d
, p
);
1314 add_to_definition (new_d
, p
);
1318 add_intrinsic_variable (const char *name
, intptr_t *loc
)
1320 add_variable (xstrdup (name
), loc
);
1324 compile (char *string
)
1326 /* Add words to the dictionary. */
1329 string
= nextword (string
, &word
);
1330 while (string
&& *string
&& word
[0])
1337 /* Compile a word and add to dictionary. */
1339 string
= nextword (string
, &word
);
1342 ptr
= newentry (word
);
1343 string
= nextword (string
, &word
);
1351 while (word
[0] != ';')
1356 /* got a string, embed magic push string
1359 add_to_definition (ptr
, p
);
1361 add_to_definition (ptr
, p
);
1373 /* Got a number, embedd the magic push number
1376 add_to_definition (ptr
, p
);
1378 add_to_definition (ptr
, p
);
1383 add_to_definition (ptr
, p
);
1384 p
.e
= lookup_word (word
);
1385 add_to_definition (ptr
, p
);
1389 string
= nextword (string
, &word
);
1392 add_to_definition (ptr
, p
);
1394 string
= nextword (string
, &word
);
1396 else if (strcmp (word
, "variable") == 0)
1399 string
= nextword (string
, &word
);
1402 intptr_t *loc
= xmalloc (sizeof (intptr_t));
1404 add_variable (word
, loc
);
1405 string
= nextword (string
, &word
);
1409 fprintf (stderr
, "syntax error at %s\n", string
- 1);
1418 *(intptr_t *) ((isp
[0])) = isp
[-1];
1427 isp
[0] = *(intptr_t *) (isp
[0]);
1460 write_buffer (tos
, stdout
);
1462 write_buffer (tos
, stderr
);
1464 fprintf (stderr
, "print: illegal print destination `%" PRIdPTR
"'\n", *isp
);
1473 read_in (string_type
*str
, FILE *file
)
1479 r
= fread (buff
, 1, sizeof (buff
), file
);
1480 catbuf (str
, buff
, r
);
1485 catbuf (str
, buff
, 1);
1491 fprintf (stderr
, "usage: -[d|i|g] <file >file\n");
1495 /* There is no reliable way to declare exit. Sometimes it returns
1496 int, and sometimes it returns void. Sometimes it changes between
1497 OS releases. Trying to get it declared correctly in the hosts file
1498 is a pointless waste of time. */
1507 main (int ac
, char *av
[])
1513 init_string (&buffer
);
1514 init_string (&pptr
);
1515 init_string (stack
+ 0);
1519 add_intrinsic ("push_text", push_text
);
1520 add_intrinsic ("!", bang
);
1521 add_intrinsic ("@", atsign
);
1522 add_intrinsic ("hello", hello
);
1523 add_intrinsic ("stdout", stdout_
);
1524 add_intrinsic ("stderr", stderr_
);
1525 add_intrinsic ("print", print
);
1526 add_intrinsic ("skip_past_newline", skip_past_newline
);
1527 add_intrinsic ("catstr", icatstr
);
1528 add_intrinsic ("copy_past_newline", icopy_past_newline
);
1529 add_intrinsic ("dup", other_dup
);
1530 add_intrinsic ("drop", drop
);
1531 add_intrinsic ("idrop", idrop
);
1532 add_intrinsic ("remchar", remchar
);
1533 add_intrinsic ("get_stuff_in_command", get_stuff_in_command
);
1534 add_intrinsic ("do_fancy_stuff", do_fancy_stuff
);
1535 add_intrinsic ("bulletize", bulletize
);
1536 add_intrinsic ("courierize", courierize
);
1537 /* If the following line gives an error, exit() is not declared in the
1538 ../hosts/foo.h file for this host. Fix it there, not here! */
1539 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1540 add_intrinsic ("exit", chew_exit
);
1541 add_intrinsic ("swap", swap
);
1542 add_intrinsic ("outputdots", outputdots
);
1543 add_intrinsic ("maybecatstr", maybecatstr
);
1544 add_intrinsic ("catstrif", catstrif
);
1545 add_intrinsic ("translatecomments", translatecomments
);
1546 add_intrinsic ("kill_bogus_lines", kill_bogus_lines
);
1547 add_intrinsic ("indent", indent
);
1548 add_intrinsic ("print_stack_level", print_stack_level
);
1549 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines
);
1550 add_intrinsic ("collapse_whitespace", collapse_whitespace
);
1552 internal_mode
= xmalloc (sizeof (intptr_t));
1554 add_intrinsic_variable ("internalmode", internal_mode
);
1556 /* Put a nl at the start. */
1557 catchar (&buffer
, '\n');
1559 read_in (&buffer
, stdin
);
1560 remove_noncomments (&buffer
, ptr
);
1561 for (i
= 1; i
< (unsigned int) ac
; i
++)
1563 if (av
[i
][0] == '-')
1565 if (av
[i
][1] == 'f')
1571 f
= fopen (av
[i
+ 1], "r");
1574 fprintf (stderr
, "Can't open the input file %s\n",
1584 else if (av
[i
][1] == 'i')
1586 internal_wanted
= 1;
1588 else if (av
[i
][1] == 'w')
1596 write_buffer (stack
+ 0, stdout
);
1598 delete_string (&pptr
);
1599 delete_string (&buffer
);
1602 fprintf (stderr
, "finishing with current stack level %ld\n",
1603 (long) (tos
- stack
));