2 Copyright (C) 1990-1991 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 /* Yet another way of extracting documentation from source.
22 No, I haven't finished it yet, but I hope you people like it better
27 Basically, this is a sort of string forth, maybe we should call it
30 You define new words thus:
31 : <newword> <oldwords> ;
35 /* Primitives provided by the program:
37 Two stacks are provided, a string stack and an integer stack.
39 Internal state variables:
40 internal_wanted - indicates whether `-i' was passed
41 internal_mode - user-settable
45 ! - pop top of integer stack for address, pop next for value; store
46 @ - treat value on integer stack as the address of an integer; push
47 that integer on the integer stack after popping the "address"
48 hello - print "hello\n" to stdout
49 stdout - put stdout marker on TOS
50 stderr - put stderr marker on TOS
51 print - print TOS-1 on TOS (eg: "hello\n" stdout print)
54 copy_past_newline - append input, up to and including newline into TOS
58 remchar - delete last character from TOS
60 do_fancy_stuff - translate <<foo>> to @code{foo} in TOS
61 bulletize - if "o" lines found, prepend @itemize @bullet to TOS
62 and @item to each "o" line; append @end itemize
63 courierize - put @example around . and | lines, translate {* *} { }
66 outputdots - strip out lines without leading dots
67 paramstuff - convert full declaration into "PARAMS" form if not already
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.
98 /* Here is a string type ... */
100 typedef struct buffer
103 unsigned long write_idx;
109 static void init_string_with_size (string_type *, unsigned int);
110 static void init_string (string_type *);
111 static int find (string_type *, char *);
112 static void write_buffer (string_type *, FILE *);
113 static void delete_string (string_type *);
114 static char *addr (string_type *, unsigned int);
115 static char at (string_type *, unsigned int);
116 static void catchar (string_type *, int);
117 static void overwrite_string (string_type *, string_type *);
118 static void catbuf (string_type *, char *, unsigned int);
119 static void cattext (string_type *, char *);
120 static void catstr (string_type *, string_type *);
121 static unsigned int skip_white_and_starts (string_type *, unsigned int);
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)),
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), manglecomments (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 (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(at(tos, fname)))
508 while (fname && !isspace(at(tos,fname)) && at(tos,fname) != '*')
513 for (idx = 0; idx < fname; idx++) /* Output type */
515 catchar(&out, at(tos,idx));
518 cattext(&out, "\n"); /* Insert a newline between type and fnname */
520 for (idx = fname; idx < openp; idx++) /* Output fnname */
522 catchar(&out, at(tos,idx));
525 cattext(&out," PARAMS (");
527 while (at(tos,idx) && at(tos,idx) !=';')
529 catchar(&out, at(tos, idx));
532 cattext(&out,");\n\n");
534 overwrite_string(tos, &out);
542 and *} into comments */
544 WORD(translatecomments)
546 unsigned int idx = 0;
552 if (at(tos,idx) == '{' && at(tos,idx+1) =='*')
557 else if (at(tos,idx) == '*' && at(tos,idx+1) =='}')
564 catchar(&out, at(tos, idx));
570 overwrite_string(tos, &out);
576 /* turn everything not starting with a . into a comment */
580 unsigned int idx = 0;
586 if (at(tos,idx) == '\n' && at(tos,idx+1) =='*')
591 else if (at(tos,idx) == '*' && at(tos,idx+1) =='}')
598 catchar(&out, at(tos, idx));
604 overwrite_string(tos, &out);
610 /* Mod tos so that only lines with leading dots remain */
612 DEFUN_VOID(outputdots)
614 unsigned int idx = 0;
620 if (at(tos, idx) == '\n' && at(tos, idx+1) == '.')
625 while ((c = at(tos, idx)) && c != '\n')
627 if (c == '{' && at(tos,idx+1) =='*')
632 else if (c == '*' && at(tos,idx+1) =='}')
651 overwrite_string(tos, &out);
656 /* Find lines starting with . and | and put example around them on tos */
660 unsigned int idx = 0;
667 if (at(tos, idx) == '\n'
668 && (at(tos, idx +1 ) == '.'
669 || at(tos,idx+1) == '|'))
671 cattext(&out,"\n@example\n");
676 while (at(tos, idx) && at(tos, idx)!='\n')
678 if (at(tos,idx)=='{' && at(tos,idx+1) =='*')
683 else if (at(tos,idx)=='*' && at(tos,idx+1) =='}')
688 else if (at(tos,idx) == '{' && !command)
693 else if (at(tos,idx) == '}' && !command)
700 if (at(tos,idx) == '@')
702 else if (isspace(at(tos,idx)) || at(tos,idx) == '}')
704 catchar(&out, at(tos, idx));
711 while (at(tos, idx) == '\n'
712 && (at(tos, idx+1) == '.')
713 || (at(tos,idx+1) == '|'));
714 cattext(&out,"@end example");
718 catchar(&out, at(tos, idx));
723 overwrite_string(tos, &out);
729 /* Finds any lines starting with "o ", if there are any, then turns
730 on @itemize @bullet, and @items each of them. Then ends with @end
731 itemize, inplace at TOS*/
736 unsigned int idx = 0;
741 while (at(tos, idx)) {
742 if (at(tos, idx) == '@' &&
743 at(tos, idx+1) == '*')
750 if (at(tos, idx) == '\n' &&
751 at(tos, idx+1) == 'o' &&
752 isspace(at(tos, idx +2)))
756 cattext(&out,"\n@itemize @bullet\n");
760 cattext(&out,"\n@item\n");
765 catchar(&out, at(tos, idx));
766 if (on && at(tos, idx) == '\n' &&
767 at(tos, idx+1) == '\n' &&
768 at(tos, idx+2) != 'o')
770 cattext(&out, "@end itemize");
779 cattext(&out,"@end itemize\n");
788 /* Turn <<foo>> into @code{foo} in place at TOS*/
793 unsigned int idx = 0;
798 if (at(tos, idx) == '<'
799 && at(tos, idx+1) == '<'
800 && !isspace(at(tos,idx + 2)))
802 /* This qualifies as a << startup */
804 cattext(&out,"@code{");
808 catchar(&out, at(tos, idx));
817 catchar(&out, at(tos, idx));
826 /* A command is all upper case,and alone on a line */
828 DEFUN( iscommand,(ptr, idx),
832 unsigned int len = 0;
833 while (at(ptr,idx)) {
834 if (isupper(at(ptr,idx)) || at(ptr,idx) == ' ' ||
840 else if(at(ptr,idx) == '\n')
842 if (len > 3) return 1;
852 DEFUN(copy_past_newline,(ptr, idx, dst),
857 while (at(ptr, idx) && at(ptr, idx) != '\n')
859 catchar(dst, at(ptr, idx));
863 catchar(dst, at(ptr, idx));
869 WORD(icopy_past_newline)
874 idx = copy_past_newline(ptr, idx, tos);
879 Take the string at the top of the stack, do some prettying */
882 WORD(kill_bogus_lines)
893 /* Drop leading nl */
894 while (at(tos,idx) == '\n')
900 /* Find the last char */
906 /* find the last non white before the nl */
909 while (idx && isspace(at(tos,idx)))
913 /* Copy buffer upto last char, but blank lines before and after
919 if (at(tos,c) == '\n'
920 && at(tos,c+1) == '\n'
921 && at(tos,c+2) == '.')
923 /* Ignore two newlines before a dot*/
926 else if (at(tos,c) == '.' && sl)
928 /* remember that this line started with a dot */
931 else if (at(tos,c) == '\n'
932 && at(tos,c+1) == '\n'
936 /* Ignore two newlines when last line was dot */
939 catchar(&out, at(tos,c));
940 if (at(tos,c) == '\n')
944 if (dot == 2)dot=1;else dot = 0;
967 while (at(tos,idx)) {
995 catchar(&out,at(tos,idx));
1010 WORD(get_stuff_in_command)
1016 while (at(ptr, idx)) {
1017 if (iscommand(ptr, idx)) break;
1018 idx = copy_past_newline(ptr, idx, tos);
1062 delete_string(tos+1);
1066 WORD(skip_past_newline)
1069 && at(ptr,idx) != '\n')
1078 internal_mode = *(isp);
1086 if (internal_wanted == internal_mode)
1097 DEFUN(nextword,(string, word),
1108 while (isspace(*string) || *string == '-') {
1111 while (*string && *string != '\n')
1119 if (!*string) return 0;
1121 word_start = string;
1128 if (*string == '\\')
1134 while (*string != '"');
1138 while (!isspace(*string))
1146 *word = malloc(length + 1);
1152 for (idx= 0; idx < length; idx++)
1154 if (src[idx] == '\\')
1163 *dst++ = src[idx+1];
1187 DEFUN(lookup_word,(word),
1190 dict_type *ptr = root;
1192 if (strcmp(ptr->word, word) == 0) return ptr;
1197 fprintf(stderr,"Can't find %s\n",word);
1203 static void DEFUN_VOID(perform)
1207 while (at(ptr, idx)) {
1208 /* It's worth looking through the command list */
1209 if (iscommand(ptr, idx))
1217 (void) nextword(addr(ptr, idx), &next);
1220 word = lookup_word(next);
1232 fprintf(stderr,"warning, %s is not recognised\n", next);
1233 skip_past_newline();
1237 else skip_past_newline();
1243 DEFUN(newentry,(word),
1246 dict_type *new = (dict_type *)malloc(sizeof(dict_type));
1250 new->code = (stinst_type *)malloc(sizeof(stinst_type ));
1251 new->code_length = 1;
1259 DEFUN(add_to_definition,(entry, word),
1260 dict_type *entry AND
1263 if (entry->code_end == entry->code_length)
1265 entry->code_length += 2;
1267 (stinst_type *) realloc((char *)(entry->code),
1268 entry->code_length *sizeof(word_type));
1270 entry->code[entry->code_end] = word;
1272 return entry->code_end++;
1282 DEFUN(add_intrinsic,(name, func),
1286 dict_type *new = newentry(name);
1287 add_to_definition(new, func);
1288 add_to_definition(new, 0);
1292 DEFUN(add_var,(name),
1295 dict_type *new = newentry(name);
1296 add_to_definition(new, push_number);
1297 add_to_definition(new, (stinst_type)(&(new->var)));
1298 add_to_definition(new,0);
1303 DEFUN(compile, (string),
1308 /* add words to the dictionary */
1310 string = nextword(string, &word);
1311 while (string && *string && word[0])
1313 if (strcmp(word,"var")==0)
1315 string=nextword(string, &word);
1318 string=nextword(string, &word);
1325 /* Compile a word and add to dictionary */
1326 string = nextword(string, &word);
1328 ptr = newentry(word);
1329 string = nextword(string, &word);
1330 while (word[0] != ';' )
1335 /* got a string, embed magic push string
1337 add_to_definition(ptr, push_text);
1338 add_to_definition(ptr, (stinst_type)(word+1));
1350 /* Got a number, embedd the magic push number
1352 add_to_definition(ptr, push_number);
1353 add_to_definition(ptr, (stinst_type)atol(word));
1356 add_to_definition(ptr, call);
1357 add_to_definition(ptr, (stinst_type)lookup_word(word));
1360 string = nextword(string, &word);
1362 add_to_definition(ptr,0);
1363 string = nextword(string, &word);
1367 fprintf(stderr,"syntax error at %s\n",string-1);
1374 static void DEFUN_VOID(bang)
1376 *(long *)((isp[0])) = isp[-1];
1384 isp[0] = *(long *)(isp[0]);
1413 write_buffer (tos, stdout);
1415 write_buffer (tos, stderr);
1417 fprintf (stderr, "print: illegal print destination `%d'\n", *isp);
1426 static void DEFUN(read_in, (str, file),
1427 string_type *str AND
1434 r = fread(buff, 1, sizeof(buff), file);
1435 catbuf(str, buff, r);
1440 catbuf(str, buff,1);
1444 static void DEFUN_VOID(usage)
1446 fprintf(stderr,"usage: -[d|i|g] <file >file\n");
1450 /* There is no reliable way to declare exit. Sometimes it returns
1451 int, and sometimes it returns void. Sometimes it changes between
1452 OS releases. Trying to get it declared correctly in the hosts file
1453 is a pointless waste of time. */
1461 int DEFUN(main,(ac,av),
1469 init_string(&buffer);
1471 init_string(stack+0);
1475 add_intrinsic("push_text", push_text);
1476 add_intrinsic("!", bang);
1477 add_intrinsic("@", atsign);
1478 add_intrinsic("hello",hello);
1479 add_intrinsic("stdout",stdout_);
1480 add_intrinsic("stderr",stderr_);
1481 add_intrinsic("print",print);
1482 add_intrinsic("skip_past_newline", skip_past_newline );
1483 add_intrinsic("catstr", icatstr );
1484 add_intrinsic("copy_past_newline", icopy_past_newline );
1485 add_intrinsic("dup", other_dup );
1486 add_intrinsic("drop", drop);
1487 add_intrinsic("idrop", idrop);
1488 add_intrinsic("remchar", remchar );
1489 add_intrinsic("get_stuff_in_command", get_stuff_in_command );
1490 add_intrinsic("do_fancy_stuff", do_fancy_stuff );
1491 add_intrinsic("bulletize", bulletize );
1492 add_intrinsic("courierize", courierize );
1493 /* If the following line gives an error, exit() is not declared in the
1494 ../hosts/foo.h file for this host. Fix it there, not here! */
1495 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1496 add_intrinsic("exit", chew_exit );
1497 add_intrinsic("swap", swap );
1498 add_intrinsic("outputdots", outputdots );
1499 add_intrinsic("paramstuff", paramstuff );
1500 add_intrinsic("maybecatstr", maybecatstr );
1501 add_intrinsic("translatecomments", translatecomments );
1502 add_intrinsic("kill_bogus_lines", kill_bogus_lines);
1503 add_intrinsic("indent", indent);
1504 add_intrinsic("internalmode", internalmode);
1505 add_intrinsic("print_stack_level", print_stack_level);
1506 add_intrinsic("strip_trailing_newlines", strip_trailing_newlines);
1508 /* Put a nl at the start */
1509 catchar(&buffer,'\n');
1511 read_in(&buffer, stdin);
1512 remove_noncomments(&buffer, ptr);
1513 for (i= 1; i < ac; i++)
1515 if (av[i][0] == '-')
1517 if (av[i][1] == 'f')
1523 f = fopen(av[i+1],"r");
1526 fprintf(stderr,"Can't open the input file %s\n",av[i+1]);
1534 else if (av[i][1] == 'i')
1536 internal_wanted = 1;
1538 else if (av[i][1] == 'w')
1544 write_buffer(stack+0, stdout);
1547 fprintf (stderr, "finishing with current stack level %d\n", tos - stack);