2 Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 1998
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.
99 /* Here is a string type ... */
101 typedef struct buffer
104 unsigned long write_idx
;
110 static void init_string_with_size (string_type
*, unsigned int);
111 static void init_string (string_type
*);
112 static int find (string_type
*, char *);
113 static void write_buffer (string_type
*, FILE *);
114 static void delete_string (string_type
*);
115 static char *addr (string_type
*, unsigned int);
116 static char at (string_type
*, unsigned int);
117 static void catchar (string_type
*, int);
118 static void overwrite_string (string_type
*, string_type
*);
119 static void catbuf (string_type
*, char *, unsigned int);
120 static void cattext (string_type
*, char *);
121 static void catstr (string_type
*, string_type
*);
125 static void DEFUN(init_string_with_size
,(buffer
, size
),
126 string_type
*buffer AND
129 buffer
->write_idx
= 0;
131 buffer
->ptr
= malloc(size
);
134 static void DEFUN(init_string
,(buffer
),
137 init_string_with_size(buffer
, DEF_SIZE
);
141 static int DEFUN(find
, (str
, what
),
148 for (i
= 0; i
< str
->write_idx
&& *p
; i
++)
150 if (*p
== str
->ptr
[i
])
159 static void DEFUN(write_buffer
,(buffer
, f
),
160 string_type
*buffer AND
163 fwrite(buffer
->ptr
, buffer
->write_idx
, 1, f
);
167 static void DEFUN(delete_string
,(buffer
),
174 static char *DEFUN(addr
, (buffer
, idx
),
175 string_type
*buffer AND
178 return buffer
->ptr
+ idx
;
181 static char DEFUN(at
,(buffer
, pos
),
182 string_type
*buffer AND
185 if (pos
>= buffer
->write_idx
)
187 return buffer
->ptr
[pos
];
190 static void DEFUN(catchar
,(buffer
, ch
),
191 string_type
*buffer AND
194 if (buffer
->write_idx
== buffer
->size
)
197 buffer
->ptr
= realloc(buffer
->ptr
, buffer
->size
);
200 buffer
->ptr
[buffer
->write_idx
++ ] = ch
;
204 static void DEFUN(overwrite_string
,(dst
, src
),
209 dst
->size
= src
->size
;
210 dst
->write_idx
= src
->write_idx
;
214 static void DEFUN(catbuf
,(buffer
, buf
, len
),
215 string_type
*buffer AND
219 if (buffer
->write_idx
+ len
>= buffer
->size
)
221 while (buffer
->write_idx
+ len
>= buffer
->size
)
223 buffer
->ptr
= realloc (buffer
->ptr
, buffer
->size
);
225 memcpy (buffer
->ptr
+ buffer
->write_idx
, buf
, len
);
226 buffer
->write_idx
+= len
;
229 static void DEFUN(cattext
,(buffer
, string
),
230 string_type
*buffer AND
233 catbuf (buffer
, string
, (unsigned int) strlen (string
));
236 static void DEFUN(catstr
,(dst
, src
),
240 catbuf (dst
, src
->ptr
, src
->write_idx
);
245 DEFUN(skip_white_and_stars
,(src
, idx
),
250 while ((c
= at(src
,idx
)),
251 isspace ((unsigned char) c
)
253 /* Don't skip past end-of-comment or star as first
254 character on its line. */
255 && at(src
,idx
+1) != '/'
256 && at(src
,idx
-1) != '\n'))
261 /***********************************************************************/
264 string_type stack
[STACK
];
267 unsigned int idx
= 0; /* Pos in input buffer */
268 string_type
*ptr
; /* and the buffer */
269 typedef void (*stinst_type
)();
271 stinst_type sstack
[STACK
];
272 stinst_type
*ssp
= &sstack
[0];
274 long *isp
= &istack
[0];
276 typedef int *word_type
;
283 struct dict_struct
*next
;
290 typedef struct dict_struct dict_type
;
291 #define WORD(x) static void x()
297 fprintf (stderr
, "%s\n", msg
);
305 die ("underflow in string stack");
306 if (tos
>= stack
+ STACK
)
307 die ("overflow in string stack");
314 die ("underflow in integer stack");
315 if (isp
>= istack
+ STACK
)
316 die ("overflow in integer stack");
320 static void exec (dict_type
*);
321 static void call (void);
322 static void remchar (void), strip_trailing_newlines (void), push_number (void);
323 static void push_text (void);
324 static void remove_noncomments (string_type
*, string_type
*);
325 static void print_stack_level (void);
326 static void paramstuff (void), translatecomments (void);
327 static void outputdots (void), courierize (void), bulletize (void);
328 static void do_fancy_stuff (void);
329 static int iscommand (string_type
*, unsigned int);
330 static int copy_past_newline (string_type
*, unsigned int, string_type
*);
331 static void icopy_past_newline (void), kill_bogus_lines (void), indent (void);
332 static void get_stuff_in_command (void), swap (void), other_dup (void);
333 static void drop (void), idrop (void);
334 static void icatstr (void), skip_past_newline (void), internalmode (void);
335 static void maybecatstr (void);
336 static char *nextword (char *, char **);
337 dict_type
*lookup_word (char *);
338 static void perform (void);
339 dict_type
*newentry (char *);
340 unsigned int add_to_definition (dict_type
*, stinst_type
);
341 void add_intrinsic (char *, void (*)());
342 void add_var (char *);
343 void compile (char *);
344 static void bang (void);
345 static void atsign (void);
346 static void hello (void);
347 static void stdout_ (void);
348 static void stderr_ (void);
349 static void print (void);
350 static void read_in (string_type
*, FILE *);
351 static void usage (void);
352 static void chew_exit (void);
355 static void DEFUN(exec
,(word
),
364 stinst_type
*oldpc
= pc
;
366 e
= (dict_type
*)(pc
[1]);
380 strip_trailing_newlines ()
382 while ((isspace ((unsigned char) at (tos
, tos
->write_idx
- 1))
383 || at (tos
, tos
->write_idx
- 1) == '\n')
384 && tos
->write_idx
> 0)
404 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 DEFUN(remove_noncomments
,(src
,dst
),
420 unsigned int idx
= 0;
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
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
));
470 fprintf (stderr
, "current string stack depth = %d, ", tos
- stack
);
471 fprintf (stderr
, "current integer stack depth = %d\n", isp
- istack
);
479 name PARAMS ((stuff));
484 DEFUN_VOID(paramstuff
)
493 /* make sure that it's not already param'd or proto'd */
494 if(find(tos
,"PARAMS") || find(tos
,"PROTO") || !find(tos
,"(")) {
499 /* Find the open paren */
500 for (openp
= 0; at(tos
, openp
) != '(' && at(tos
,openp
); openp
++)
504 /* Step back to the fname */
506 while (fname
&& isspace((unsigned char) at(tos
, fname
)))
509 && !isspace((unsigned char) at(tos
,fname
))
510 && at(tos
,fname
) != '*')
515 for (idx
= 0; idx
< fname
; idx
++) /* Output type */
517 catchar(&out
, at(tos
,idx
));
520 cattext(&out
, "\n"); /* Insert a newline between type and fnname */
522 for (idx
= fname
; idx
< openp
; idx
++) /* Output fnname */
524 catchar(&out
, at(tos
,idx
));
527 cattext(&out
," PARAMS (");
529 while (at(tos
,idx
) && at(tos
,idx
) !=';')
531 catchar(&out
, at(tos
, idx
));
534 cattext(&out
,");\n\n");
536 overwrite_string(tos
, &out
);
544 and *} into comments */
546 WORD(translatecomments
)
548 unsigned int idx
= 0;
554 if (at(tos
,idx
) == '{' && at(tos
,idx
+1) =='*')
559 else if (at(tos
,idx
) == '*' && at(tos
,idx
+1) =='}')
566 catchar(&out
, at(tos
, idx
));
572 overwrite_string(tos
, &out
);
580 /* This is not currently used. */
582 /* turn everything not starting with a . into a comment */
586 unsigned int idx
= 0;
592 if (at(tos
,idx
) == '\n' && at(tos
,idx
+1) =='*')
597 else if (at(tos
,idx
) == '*' && at(tos
,idx
+1) =='}')
604 catchar(&out
, at(tos
, idx
));
610 overwrite_string(tos
, &out
);
618 /* Mod tos so that only lines with leading dots remain */
620 DEFUN_VOID(outputdots
)
622 unsigned int idx
= 0;
628 if (at(tos
, idx
) == '\n' && at(tos
, idx
+1) == '.')
633 while ((c
= at(tos
, idx
)) && c
!= '\n')
635 if (c
== '{' && at(tos
,idx
+1) =='*')
640 else if (c
== '*' && at(tos
,idx
+1) =='}')
659 overwrite_string(tos
, &out
);
664 /* Find lines starting with . and | and put example around them on tos */
668 unsigned int idx
= 0;
675 if (at(tos
, idx
) == '\n'
676 && (at(tos
, idx
+1 ) == '.'
677 || at(tos
,idx
+1) == '|'))
679 cattext(&out
,"\n@example\n");
684 while (at(tos
, idx
) && at(tos
, idx
)!='\n')
686 if (at(tos
,idx
)=='{' && at(tos
,idx
+1) =='*')
691 else if (at(tos
,idx
)=='*' && at(tos
,idx
+1) =='}')
696 else if (at(tos
,idx
) == '{' && !command
)
701 else if (at(tos
,idx
) == '}' && !command
)
708 if (at(tos
,idx
) == '@')
710 else if (isspace((unsigned char) at(tos
,idx
))
711 || at(tos
,idx
) == '}')
713 catchar(&out
, at(tos
, idx
));
720 while (at(tos
, idx
) == '\n'
721 && ((at(tos
, idx
+1) == '.')
722 || (at(tos
,idx
+1) == '|')))
724 cattext(&out
,"@end example");
728 catchar(&out
, at(tos
, idx
));
733 overwrite_string(tos
, &out
);
739 /* Finds any lines starting with "o ", if there are any, then turns
740 on @itemize @bullet, and @items each of them. Then ends with @end
741 itemize, inplace at TOS*/
746 unsigned int idx
= 0;
751 while (at(tos
, idx
)) {
752 if (at(tos
, idx
) == '@' &&
753 at(tos
, idx
+1) == '*')
760 if (at(tos
, idx
) == '\n' &&
761 at(tos
, idx
+1) == 'o' &&
762 isspace((unsigned char) at(tos
, idx
+2)))
766 cattext(&out
,"\n@itemize @bullet\n");
770 cattext(&out
,"\n@item\n");
775 catchar(&out
, at(tos
, idx
));
776 if (on
&& at(tos
, idx
) == '\n' &&
777 at(tos
, idx
+1) == '\n' &&
778 at(tos
, idx
+2) != 'o')
780 cattext(&out
, "@end itemize");
789 cattext(&out
,"@end itemize\n");
798 /* Turn <<foo>> into @code{foo} in place at TOS*/
803 unsigned int idx
= 0;
808 if (at(tos
, idx
) == '<'
809 && at(tos
, idx
+1) == '<'
810 && !isspace((unsigned char) at(tos
,idx
+ 2)))
812 /* This qualifies as a << startup */
814 cattext(&out
,"@code{");
818 catchar(&out
, at(tos
, idx
));
827 catchar(&out
, at(tos
, idx
));
836 /* A command is all upper case,and alone on a line */
838 DEFUN( iscommand
,(ptr
, idx
),
842 unsigned int len
= 0;
843 while (at(ptr
,idx
)) {
844 if (isupper((unsigned char) at(ptr
,idx
)) || at(ptr
,idx
) == ' ' ||
850 else if(at(ptr
,idx
) == '\n')
852 if (len
> 3) return 1;
863 DEFUN(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
));
894 WORD(icopy_past_newline
)
899 idx
= copy_past_newline(ptr
, idx
, tos
);
904 Take the string at the top of the stack, do some prettying */
907 WORD(kill_bogus_lines
)
917 /* Drop leading nl */
918 while (at(tos
,idx
) == '\n')
924 /* If the first char is a '.' prepend a newline so that it is
925 recognized properly later. */
926 if (at (tos
, idx
) == '.')
927 catchar (&out
, '\n');
929 /* Find the last char */
935 /* find the last non white before the nl */
938 while (idx
&& isspace((unsigned char) at(tos
,idx
)))
942 /* Copy buffer upto last char, but blank lines before and after
948 if (at(tos
,c
) == '\n'
949 && at(tos
,c
+1) == '\n'
950 && at(tos
,c
+2) == '.')
952 /* Ignore two newlines before a dot*/
955 else if (at(tos
,c
) == '.' && sl
)
957 /* remember that this line started with a dot */
960 else if (at(tos
,c
) == '\n'
961 && at(tos
,c
+1) == '\n'
965 /* Ignore two newlines when last line was dot */
968 catchar(&out
, at(tos
,c
));
969 if (at(tos
,c
) == '\n')
973 if (dot
== 2)dot
=1;else dot
= 0;
998 while (at(tos
,idx
)) {
1004 if (tab
&& at(tos
,idx
))
1026 catchar(&out
,at(tos
,idx
));
1041 WORD(get_stuff_in_command
)
1047 while (at(ptr
, idx
)) {
1048 if (iscommand(ptr
, idx
)) break;
1049 idx
= copy_past_newline(ptr
, idx
, tos
);
1093 delete_string(tos
+1);
1097 WORD(skip_past_newline
)
1100 && at(ptr
,idx
) != '\n')
1109 internal_mode
= *(isp
);
1117 if (internal_wanted
== internal_mode
)
1128 DEFUN(nextword
,(string
, word
),
1139 while (isspace((unsigned char) *string
) || *string
== '-') {
1142 while (*string
&& *string
!= '\n')
1150 if (!*string
) return 0;
1152 word_start
= string
;
1159 if (*string
== '\\')
1165 while (*string
!= '"');
1169 while (!isspace((unsigned char) *string
))
1177 *word
= malloc(length
+ 1);
1183 for (idx
= 0; idx
< length
; idx
++)
1185 if (src
[idx
] == '\\')
1194 *dst
++ = src
[idx
+1];
1218 DEFUN(lookup_word
,(word
),
1221 dict_type
*ptr
= root
;
1223 if (strcmp(ptr
->word
, word
) == 0) return ptr
;
1228 fprintf(stderr
,"Can't find %s\n",word
);
1234 static void DEFUN_VOID(perform
)
1238 while (at(ptr
, idx
)) {
1239 /* It's worth looking through the command list */
1240 if (iscommand(ptr
, idx
))
1245 (void) nextword(addr(ptr
, idx
), &next
);
1248 word
= lookup_word(next
);
1260 fprintf(stderr
,"warning, %s is not recognised\n", next
);
1261 skip_past_newline();
1265 else skip_past_newline();
1271 DEFUN(newentry
,(word
),
1274 dict_type
*new = (dict_type
*)malloc(sizeof(dict_type
));
1278 new->code
= (stinst_type
*)malloc(sizeof(stinst_type
));
1279 new->code_length
= 1;
1287 DEFUN(add_to_definition
,(entry
, word
),
1288 dict_type
*entry AND
1291 if (entry
->code_end
== entry
->code_length
)
1293 entry
->code_length
+= 2;
1295 (stinst_type
*) realloc((char *)(entry
->code
),
1296 entry
->code_length
*sizeof(word_type
));
1298 entry
->code
[entry
->code_end
] = word
;
1300 return entry
->code_end
++;
1310 DEFUN(add_intrinsic
,(name
, func
),
1314 dict_type
*new = newentry(name
);
1315 add_to_definition(new, func
);
1316 add_to_definition(new, 0);
1320 DEFUN(add_var
,(name
),
1323 dict_type
*new = newentry(name
);
1324 add_to_definition(new, push_number
);
1325 add_to_definition(new, (stinst_type
)(&(new->var
)));
1326 add_to_definition(new,0);
1331 DEFUN(compile
, (string
),
1334 /* add words to the dictionary */
1336 string
= nextword(string
, &word
);
1337 while (string
&& *string
&& word
[0])
1339 if (strcmp(word
,"var")==0)
1341 string
=nextword(string
, &word
);
1344 string
=nextword(string
, &word
);
1351 /* Compile a word and add to dictionary */
1352 string
= nextword(string
, &word
);
1354 ptr
= newentry(word
);
1355 string
= nextword(string
, &word
);
1356 while (word
[0] != ';' )
1361 /* got a string, embed magic push string
1363 add_to_definition(ptr
, push_text
);
1364 add_to_definition(ptr
, (stinst_type
)(word
+1));
1376 /* Got a number, embedd the magic push number
1378 add_to_definition(ptr
, push_number
);
1379 add_to_definition(ptr
, (stinst_type
)atol(word
));
1382 add_to_definition(ptr
, call
);
1383 add_to_definition(ptr
, (stinst_type
)lookup_word(word
));
1386 string
= nextword(string
, &word
);
1388 add_to_definition(ptr
,0);
1389 string
= nextword(string
, &word
);
1393 fprintf(stderr
,"syntax error at %s\n",string
-1);
1400 static void DEFUN_VOID(bang
)
1402 *(long *)((isp
[0])) = isp
[-1];
1410 isp
[0] = *(long *)(isp
[0]);
1439 write_buffer (tos
, stdout
);
1441 write_buffer (tos
, stderr
);
1443 fprintf (stderr
, "print: illegal print destination `%ld'\n", *isp
);
1452 static void DEFUN(read_in
, (str
, file
),
1453 string_type
*str AND
1460 r
= fread(buff
, 1, sizeof(buff
), file
);
1461 catbuf(str
, buff
, r
);
1466 catbuf(str
, buff
,1);
1470 static void DEFUN_VOID(usage
)
1472 fprintf(stderr
,"usage: -[d|i|g] <file >file\n");
1476 /* There is no reliable way to declare exit. Sometimes it returns
1477 int, and sometimes it returns void. Sometimes it changes between
1478 OS releases. Trying to get it declared correctly in the hosts file
1479 is a pointless waste of time. */
1487 int DEFUN(main
,(ac
,av
),
1495 init_string(&buffer
);
1497 init_string(stack
+0);
1501 add_intrinsic("push_text", push_text
);
1502 add_intrinsic("!", bang
);
1503 add_intrinsic("@", atsign
);
1504 add_intrinsic("hello",hello
);
1505 add_intrinsic("stdout",stdout_
);
1506 add_intrinsic("stderr",stderr_
);
1507 add_intrinsic("print",print
);
1508 add_intrinsic("skip_past_newline", skip_past_newline
);
1509 add_intrinsic("catstr", icatstr
);
1510 add_intrinsic("copy_past_newline", icopy_past_newline
);
1511 add_intrinsic("dup", other_dup
);
1512 add_intrinsic("drop", drop
);
1513 add_intrinsic("idrop", idrop
);
1514 add_intrinsic("remchar", remchar
);
1515 add_intrinsic("get_stuff_in_command", get_stuff_in_command
);
1516 add_intrinsic("do_fancy_stuff", do_fancy_stuff
);
1517 add_intrinsic("bulletize", bulletize
);
1518 add_intrinsic("courierize", courierize
);
1519 /* If the following line gives an error, exit() is not declared in the
1520 ../hosts/foo.h file for this host. Fix it there, not here! */
1521 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1522 add_intrinsic("exit", chew_exit
);
1523 add_intrinsic("swap", swap
);
1524 add_intrinsic("outputdots", outputdots
);
1525 add_intrinsic("paramstuff", paramstuff
);
1526 add_intrinsic("maybecatstr", maybecatstr
);
1527 add_intrinsic("translatecomments", translatecomments
);
1528 add_intrinsic("kill_bogus_lines", kill_bogus_lines
);
1529 add_intrinsic("indent", indent
);
1530 add_intrinsic("internalmode", internalmode
);
1531 add_intrinsic("print_stack_level", print_stack_level
);
1532 add_intrinsic("strip_trailing_newlines", strip_trailing_newlines
);
1534 /* Put a nl at the start */
1535 catchar(&buffer
,'\n');
1537 read_in(&buffer
, stdin
);
1538 remove_noncomments(&buffer
, ptr
);
1539 for (i
= 1; i
< (unsigned int) ac
; i
++)
1541 if (av
[i
][0] == '-')
1543 if (av
[i
][1] == 'f')
1549 f
= fopen(av
[i
+1],"r");
1552 fprintf(stderr
,"Can't open the input file %s\n",av
[i
+1]);
1560 else if (av
[i
][1] == 'i')
1562 internal_wanted
= 1;
1564 else if (av
[i
][1] == 'w')
1572 write_buffer(stack
+0, stdout
);
1575 fprintf (stderr
, "finishing with current stack level %d\n", tos
- stack
);