2 Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2000, 2001,
4 Free Software Foundation, Inc.
5 Contributed by steve chamberlain @cygnus
7 This file is part of BFD, the Binary File Descriptor library.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 /* Yet another way of extracting documentation from source.
24 No, I haven't finished it yet, but I hope you people like it better
29 Basically, this is a sort of string forth, maybe we should call it
32 You define new words thus:
33 : <newword> <oldwords> ;
37 /* Primitives provided by the program:
39 Two stacks are provided, a string stack and an integer stack.
41 Internal state variables:
42 internal_wanted - indicates whether `-i' was passed
43 internal_mode - user-settable
47 ! - pop top of integer stack for address, pop next for value; store
48 @ - treat value on integer stack as the address of an integer; push
49 that integer on the integer stack after popping the "address"
50 hello - print "hello\n" to stdout
51 stdout - put stdout marker on TOS
52 stderr - put stderr marker on TOS
53 print - print TOS-1 on TOS (eg: "hello\n" stdout print)
56 copy_past_newline - append input, up to and including newline into TOS
60 remchar - delete last character from TOS
62 do_fancy_stuff - translate <<foo>> to @code{foo} in TOS
63 bulletize - if "o" lines found, prepend @itemize @bullet to TOS
64 and @item to each "o" line; append @end itemize
65 courierize - put @example around . and | lines, translate {* *} { }
68 outputdots - strip out lines without leading dots
69 paramstuff - convert full declaration into "PARAMS" form if not already
70 maybecatstr - do catstr if internal_mode == internal_wanted, discard
72 translatecomments - turn {* and *} into comment delimiters
73 kill_bogus_lines - get rid of extra newlines
75 internalmode - pop from integer stack, set `internalmode' to that value
76 print_stack_level - print current stack depth to stderr
77 strip_trailing_newlines - go ahead, guess...
78 [quoted string] - push string onto string stack
79 [word starting with digit] - push atol(str) onto integer stack
81 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
;
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
*);
124 init_string_with_size (buffer
, size
)
128 buffer
->write_idx
= 0;
130 buffer
->ptr
= malloc (size
);
137 init_string_with_size (buffer
, DEF_SIZE
);
148 for (i
= 0; i
< str
->write_idx
&& *p
; i
++)
150 if (*p
== str
->ptr
[i
])
159 write_buffer (buffer
, f
)
163 fwrite (buffer
->ptr
, buffer
->write_idx
, 1, f
);
167 delete_string (buffer
)
178 return buffer
->ptr
+ idx
;
186 if (pos
>= buffer
->write_idx
)
188 return buffer
->ptr
[pos
];
196 if (buffer
->write_idx
== buffer
->size
)
199 buffer
->ptr
= realloc (buffer
->ptr
, buffer
->size
);
202 buffer
->ptr
[buffer
->write_idx
++] = ch
;
206 overwrite_string (dst
, src
)
211 dst
->size
= src
->size
;
212 dst
->write_idx
= src
->write_idx
;
217 catbuf (buffer
, buf
, len
)
222 if (buffer
->write_idx
+ len
>= buffer
->size
)
224 while (buffer
->write_idx
+ len
>= buffer
->size
)
226 buffer
->ptr
= realloc (buffer
->ptr
, buffer
->size
);
228 memcpy (buffer
->ptr
+ buffer
->write_idx
, buf
, len
);
229 buffer
->write_idx
+= len
;
233 cattext (buffer
, string
)
237 catbuf (buffer
, string
, (unsigned int) strlen (string
));
245 catbuf (dst
, src
->ptr
, src
->write_idx
);
249 skip_white_and_stars (src
, idx
)
254 while ((c
= at (src
, idx
)),
255 isspace ((unsigned char) c
)
257 /* Don't skip past end-of-comment or star as first
258 character on its line. */
259 && at (src
, idx
+1) != '/'
260 && at (src
, idx
-1) != '\n'))
265 /***********************************************************************/
267 string_type stack
[STACK
];
270 unsigned int idx
= 0; /* Pos in input buffer */
271 string_type
*ptr
; /* and the buffer */
272 typedef void (*stinst_type
)();
274 stinst_type sstack
[STACK
];
275 stinst_type
*ssp
= &sstack
[0];
277 long *isp
= &istack
[0];
279 typedef int *word_type
;
284 struct dict_struct
*next
;
291 typedef struct dict_struct dict_type
;
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);
367 stinst_type
*oldpc
= pc
;
369 e
= (dict_type
*) (pc
[1]);
383 strip_trailing_newlines ()
385 while ((isspace ((unsigned char) at (tos
, tos
->write_idx
- 1))
386 || at (tos
, tos
->write_idx
- 1) == '\n')
387 && tos
->write_idx
> 0)
409 cattext (tos
, *((char **) pc
));
413 /* This function removes everything not inside comments starting on
414 the first char of the line from the string, also when copying
415 comments, removes blank space and leading *'s.
416 Blank lines are turned into one blank line. */
419 remove_noncomments (src
, dst
)
423 unsigned int idx
= 0;
425 while (at (src
, idx
))
427 /* Now see if we have a comment at the start of the line. */
428 if (at (src
, idx
) == '\n'
429 && at (src
, idx
+ 1) == '/'
430 && at (src
, idx
+ 2) == '*')
434 idx
= skip_white_and_stars (src
, idx
);
436 /* Remove leading dot */
437 if (at (src
, idx
) == '.')
440 /* Copy to the end of the line, or till the end of the
442 while (at (src
, idx
))
444 if (at (src
, idx
) == '\n')
446 /* end of line, echo and scrape of leading blanks */
447 if (at (src
, idx
+ 1) == '\n')
451 idx
= skip_white_and_stars (src
, idx
);
453 else if (at (src
, idx
) == '*' && at (src
, idx
+ 1) == '/')
456 cattext (dst
, "\nENDDD\n");
461 catchar (dst
, at (src
, idx
));
474 fprintf (stderr
, "current string stack depth = %d, ", tos
- stack
);
475 fprintf (stderr
, "current integer stack depth = %d\n", isp
- istack
);
483 name PARAMS ((stuff));
499 /* Make sure that it's not already param'd or proto'd. */
501 || find (tos
, "PARAMS") || find (tos
, "PROTO") || !find (tos
, "("))
507 /* Find the open paren. */
508 for (openp
= 0; at (tos
, openp
) != '(' && at (tos
, openp
); openp
++)
512 /* Step back to the fname. */
514 while (fname
&& isspace ((unsigned char) at (tos
, fname
)))
517 && !isspace ((unsigned char) at (tos
,fname
))
518 && at (tos
,fname
) != '*')
523 /* Output type, omitting trailing whitespace character(s), if
525 for (len
= fname
; 0 < len
; len
--)
527 if (!isspace ((unsigned char) at (tos
, len
- 1)))
530 for (idx
= 0; idx
< len
; idx
++)
531 catchar (&out
, at (tos
, idx
));
533 cattext (&out
, "\n"); /* Insert a newline between type and fnname */
535 /* Output function name, omitting trailing whitespace
536 character(s), if any. */
537 for (len
= openp
; 0 < len
; len
--)
539 if (!isspace ((unsigned char) at (tos
, len
- 1)))
542 for (idx
= fname
; idx
< len
; idx
++)
543 catchar (&out
, at (tos
, idx
));
545 cattext (&out
, " PARAMS (");
547 for (idx
= openp
; at (tos
, idx
) && at (tos
, idx
) != ';'; idx
++)
548 catchar (&out
, at (tos
, idx
));
550 cattext (&out
, ");\n\n");
552 overwrite_string (tos
, &out
);
558 and *} into comments */
563 unsigned int idx
= 0;
567 while (at (tos
, idx
))
569 if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
571 cattext (&out
, "/*");
574 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
576 cattext (&out
, "*/");
581 catchar (&out
, at (tos
, idx
));
586 overwrite_string (tos
, &out
);
593 /* This is not currently used. */
595 /* turn everything not starting with a . into a comment */
600 unsigned int idx
= 0;
604 while (at (tos
, idx
))
606 if (at (tos
, idx
) == '\n' && at (tos
, idx
+ 1) == '*')
608 cattext (&out
, " /*");
611 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
613 cattext (&out
, "*/");
618 catchar (&out
, at (tos
, idx
));
623 overwrite_string (tos
, &out
);
630 /* Mod tos so that only lines with leading dots remain */
634 unsigned int idx
= 0;
638 while (at (tos
, idx
))
640 if (at (tos
, idx
) == '\n' && at (tos
, idx
+ 1) == '.')
645 while ((c
= at (tos
, idx
)) && c
!= '\n')
647 if (c
== '{' && at (tos
, idx
+ 1) == '*')
649 cattext (&out
, "/*");
652 else if (c
== '*' && at (tos
, idx
+ 1) == '}')
654 cattext (&out
, "*/");
663 catchar (&out
, '\n');
671 overwrite_string (tos
, &out
);
675 /* Find lines starting with . and | and put example around them on tos */
680 unsigned int idx
= 0;
685 while (at (tos
, idx
))
687 if (at (tos
, idx
) == '\n'
688 && (at (tos
, idx
+1 ) == '.'
689 || at (tos
, idx
+ 1) == '|'))
691 cattext (&out
, "\n@example\n");
696 while (at (tos
, idx
) && at (tos
, idx
) != '\n')
700 /* We are inside {} parameters of some command;
701 Just pass through until matching brace. */
702 if (at (tos
, idx
) == '{')
704 else if (at (tos
, idx
) == '}')
707 else if (command
!= 0)
709 if (at (tos
, idx
) == '{')
711 else if (!islower ((unsigned char) at (tos
, idx
)))
714 else if (at (tos
, idx
) == '@'
715 && islower ((unsigned char) at (tos
, idx
+ 1)))
719 else if (at (tos
, idx
) == '{' && at (tos
, idx
+ 1) == '*')
721 cattext (&out
, "/*");
725 else if (at (tos
, idx
) == '*' && at (tos
, idx
+ 1) == '}')
727 cattext (&out
, "*/");
731 else if (at (tos
, idx
) == '{'
732 || at (tos
, idx
) == '}')
737 catchar (&out
, at (tos
, idx
));
740 catchar (&out
, '\n');
742 while (at (tos
, idx
) == '\n'
743 && ((at (tos
, idx
+ 1) == '.')
744 || (at (tos
, idx
+ 1) == '|')))
746 cattext (&out
, "@end example");
750 catchar (&out
, at (tos
, idx
));
755 overwrite_string (tos
, &out
);
759 /* Finds any lines starting with "o ", if there are any, then turns
760 on @itemize @bullet, and @items each of them. Then ends with @end
761 itemize, inplace at TOS*/
766 unsigned int idx
= 0;
771 while (at (tos
, idx
))
773 if (at (tos
, idx
) == '@'
774 && at (tos
, idx
+ 1) == '*')
779 else if (at (tos
, idx
) == '\n'
780 && at (tos
, idx
+ 1) == 'o'
781 && isspace ((unsigned char) at (tos
, idx
+ 2)))
785 cattext (&out
, "\n@itemize @bullet\n");
789 cattext (&out
, "\n@item\n");
794 catchar (&out
, at (tos
, idx
));
795 if (on
&& at (tos
, idx
) == '\n'
796 && at (tos
, idx
+ 1) == '\n'
797 && at (tos
, idx
+ 2) != 'o')
799 cattext (&out
, "@end itemize");
808 cattext (&out
, "@end itemize\n");
816 /* Turn <<foo>> into @code{foo} in place at TOS*/
821 unsigned int idx
= 0;
824 while (at (tos
, idx
))
826 if (at (tos
, idx
) == '<'
827 && at (tos
, idx
+ 1) == '<'
828 && !isspace ((unsigned char) at (tos
, idx
+ 2)))
830 /* This qualifies as a << startup. */
832 cattext (&out
, "@code{");
834 && at (tos
, idx
) != '>' )
836 catchar (&out
, at (tos
, idx
));
845 catchar (&out
, at (tos
, idx
));
855 /* A command is all upper case,and alone on a line. */
862 unsigned int len
= 0;
863 while (at (ptr
, idx
))
865 if (isupper ((unsigned char) at (ptr
, idx
))
866 || at (ptr
, idx
) == ' ' || at (ptr
, idx
) == '_')
871 else if (at (ptr
, idx
) == '\n')
884 copy_past_newline (ptr
, idx
, dst
)
891 while (at (ptr
, idx
) && at (ptr
, idx
) != '\n')
893 if (at (ptr
, idx
) == '\t')
895 /* Expand tabs. Neither makeinfo nor TeX can cope well with
899 while (++column
& 7);
903 catchar (dst
, at (ptr
, idx
));
909 catchar (dst
, at (ptr
, idx
));
916 icopy_past_newline ()
921 idx
= copy_past_newline (ptr
, idx
, tos
);
926 Take the string at the top of the stack, do some prettying. */
939 /* Drop leading nl. */
940 while (at (tos
, idx
) == '\n')
946 /* If the first char is a '.' prepend a newline so that it is
947 recognized properly later. */
948 if (at (tos
, idx
) == '.')
949 catchar (&out
, '\n');
951 /* Find the last char. */
952 while (at (tos
, idx
))
957 /* Find the last non white before the nl. */
960 while (idx
&& isspace ((unsigned char) at (tos
, idx
)))
964 /* Copy buffer upto last char, but blank lines before and after
970 if (at (tos
, c
) == '\n'
971 && at (tos
, c
+ 1) == '\n'
972 && at (tos
, c
+ 2) == '.')
974 /* Ignore two newlines before a dot. */
977 else if (at (tos
, c
) == '.' && sl
)
979 /* remember that this line started with a dot. */
982 else if (at (tos
, c
) == '\n'
983 && at (tos
, c
+ 1) == '\n'
987 /* Ignore two newlines when last line was dot. */
990 catchar (&out
, at (tos
, c
));
991 if (at (tos
, c
) == '\n')
1008 catchar (&out
, '\n');
1010 delete_string (tos
);
1023 while (at (tos
, idx
))
1025 switch (at (tos
, idx
))
1028 cattext (&out
, "\n");
1030 if (tab
&& at (tos
, idx
))
1032 cattext (&out
, " ");
1039 cattext (&out
, " ");
1041 cattext (&out
, "(");
1046 cattext (&out
, ")");
1052 catchar (&out
, at (tos
, idx
));
1061 delete_string (tos
);
1067 get_stuff_in_command ()
1073 while (at (ptr
, idx
))
1075 if (iscommand (ptr
, idx
))
1077 idx
= copy_past_newline (ptr
, idx
, tos
);
1099 catstr (tos
, tos
- 1);
1124 catstr (tos
, tos
+ 1);
1125 delete_string (tos
+ 1);
1130 skip_past_newline ()
1132 while (at (ptr
, idx
)
1133 && at (ptr
, idx
) != '\n')
1142 internal_mode
= *(isp
);
1151 if (internal_wanted
== internal_mode
)
1153 catstr (tos
- 1, tos
);
1155 delete_string (tos
);
1162 nextword (string
, word
)
1173 while (isspace ((unsigned char) *string
) || *string
== '-')
1177 while (*string
&& *string
!= '\n')
1189 word_start
= string
;
1196 if (*string
== '\\')
1202 while (*string
!= '"');
1206 while (!isspace ((unsigned char) *string
))
1214 *word
= malloc (length
+ 1);
1219 for (idx
= 0; idx
< length
; idx
++)
1221 if (src
[idx
] == '\\')
1222 switch (src
[idx
+ 1])
1230 *dst
++ = src
[idx
+ 1];
1254 dict_type
*ptr
= root
;
1257 if (strcmp (ptr
->word
, word
) == 0)
1262 fprintf (stderr
, "Can't find %s\n", word
);
1271 while (at (ptr
, idx
))
1273 /* It's worth looking through the command list. */
1274 if (iscommand (ptr
, idx
))
1279 (void) nextword (addr (ptr
, idx
), &next
);
1281 word
= lookup_word (next
);
1290 fprintf (stderr
, "warning, %s is not recognised\n", next
);
1291 skip_past_newline ();
1296 skip_past_newline ();
1304 dict_type
*new = (dict_type
*) malloc (sizeof (dict_type
));
1308 new->code
= (stinst_type
*) malloc (sizeof (stinst_type
));
1309 new->code_length
= 1;
1315 add_to_definition (entry
, word
)
1319 if (entry
->code_end
== entry
->code_length
)
1321 entry
->code_length
+= 2;
1323 (stinst_type
*) realloc ((char *) (entry
->code
),
1324 entry
->code_length
* sizeof (word_type
));
1326 entry
->code
[entry
->code_end
] = word
;
1328 return entry
->code_end
++;
1332 add_intrinsic (name
, func
)
1336 dict_type
*new = newentry (name
);
1337 add_to_definition (new, func
);
1338 add_to_definition (new, 0);
1345 dict_type
*new = newentry (name
);
1346 add_to_definition (new, push_number
);
1347 add_to_definition (new, (stinst_type
) (&(new->var
)));
1348 add_to_definition (new, 0);
1355 /* Add words to the dictionary. */
1357 string
= nextword (string
, &word
);
1358 while (string
&& *string
&& word
[0])
1360 if (strcmp (word
, "var") == 0)
1362 string
= nextword (string
, &word
);
1365 string
= nextword (string
, &word
);
1367 else if (word
[0] == ':')
1370 /* Compile a word and add to dictionary. */
1371 string
= nextword (string
, &word
);
1373 ptr
= newentry (word
);
1374 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
));
1401 add_to_definition (ptr
, call
);
1402 add_to_definition (ptr
, (stinst_type
) lookup_word (word
));
1405 string
= nextword (string
, &word
);
1407 add_to_definition (ptr
, 0);
1408 string
= nextword (string
, &word
);
1412 fprintf (stderr
, "syntax error at %s\n", string
- 1);
1420 *(long *) ((isp
[0])) = isp
[-1];
1429 isp
[0] = *(long *) (isp
[0]);
1462 write_buffer (tos
, stdout
);
1464 write_buffer (tos
, stderr
);
1466 fprintf (stderr
, "print: illegal print destination `%ld'\n", *isp
);
1483 r
= fread (buff
, 1, sizeof (buff
), file
);
1484 catbuf (str
, buff
, r
);
1489 catbuf (str
, buff
, 1);
1495 fprintf (stderr
, "usage: -[d|i|g] <file >file\n");
1499 /* There is no reliable way to declare exit. Sometimes it returns
1500 int, and sometimes it returns void. Sometimes it changes between
1501 OS releases. Trying to get it declared correctly in the hosts file
1502 is a pointless waste of time. */
1519 init_string (&buffer
);
1520 init_string (&pptr
);
1521 init_string (stack
+ 0);
1525 add_intrinsic ("push_text", push_text
);
1526 add_intrinsic ("!", bang
);
1527 add_intrinsic ("@", atsign
);
1528 add_intrinsic ("hello", hello
);
1529 add_intrinsic ("stdout", stdout_
);
1530 add_intrinsic ("stderr", stderr_
);
1531 add_intrinsic ("print", print
);
1532 add_intrinsic ("skip_past_newline", skip_past_newline
);
1533 add_intrinsic ("catstr", icatstr
);
1534 add_intrinsic ("copy_past_newline", icopy_past_newline
);
1535 add_intrinsic ("dup", other_dup
);
1536 add_intrinsic ("drop", drop
);
1537 add_intrinsic ("idrop", idrop
);
1538 add_intrinsic ("remchar", remchar
);
1539 add_intrinsic ("get_stuff_in_command", get_stuff_in_command
);
1540 add_intrinsic ("do_fancy_stuff", do_fancy_stuff
);
1541 add_intrinsic ("bulletize", bulletize
);
1542 add_intrinsic ("courierize", courierize
);
1543 /* If the following line gives an error, exit() is not declared in the
1544 ../hosts/foo.h file for this host. Fix it there, not here! */
1545 /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
1546 add_intrinsic ("exit", chew_exit
);
1547 add_intrinsic ("swap", swap
);
1548 add_intrinsic ("outputdots", outputdots
);
1549 add_intrinsic ("paramstuff", paramstuff
);
1550 add_intrinsic ("maybecatstr", maybecatstr
);
1551 add_intrinsic ("translatecomments", translatecomments
);
1552 add_intrinsic ("kill_bogus_lines", kill_bogus_lines
);
1553 add_intrinsic ("indent", indent
);
1554 add_intrinsic ("internalmode", internalmode
);
1555 add_intrinsic ("print_stack_level", print_stack_level
);
1556 add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines
);
1558 /* Put a nl at the start. */
1559 catchar (&buffer
, '\n');
1561 read_in (&buffer
, stdin
);
1562 remove_noncomments (&buffer
, ptr
);
1563 for (i
= 1; i
< (unsigned int) ac
; i
++)
1565 if (av
[i
][0] == '-')
1567 if (av
[i
][1] == 'f')
1573 f
= fopen (av
[i
+ 1], "r");
1576 fprintf (stderr
, "Can't open the input file %s\n",
1585 else if (av
[i
][1] == 'i')
1587 internal_wanted
= 1;
1589 else if (av
[i
][1] == 'w')
1597 write_buffer (stack
+ 0, stdout
);
1600 fprintf (stderr
, "finishing with current stack level %d\n",