Home | History | Annotate | Line # | Download | only in doc
      1 /* chew
      2    Copyright (C) 1990-2026 Free Software Foundation, Inc.
      3    Contributed by steve chamberlain @cygnus
      4 
      5    This file is part of BFD, the Binary File Descriptor library.
      6 
      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.
     11 
     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.
     16 
     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.  */
     21 
     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
     24    than the old way
     25 
     26    sac
     27 
     28    Basically, this is a sort of string forth, maybe we should call it
     29    struth?
     30 
     31    You define new words thus:
     32    : <newword> <oldwords> ;
     33 
     34    Variables are defined using:
     35    variable NAME
     36 
     37 */
     38 
     39 /* Primitives provided by the program:
     40 
     41    Two stacks are provided, a string stack and an integer stack.
     42 
     43    Internal state variables:
     44 	internal_wanted - indicates whether `-i' was passed
     45 	internal_mode - user-settable
     46 
     47    Commands:
     48 	push_text
     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)
     56 	skip_past_newline
     57 	catstr - fn icatstr
     58 	copy_past_newline - append input, up to and including newline into TOS
     59 	dup - fn other_dup
     60 	drop - discard TOS
     61 	idrop - ditto
     62 	remchar - delete last character from TOS
     63 	get_stuff_in_command
     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 {* *} { }
     68 	exit - fn chew_exit
     69 	swap
     70 	outputdots - strip out lines without leading dots
     71 	maybecatstr - do catstr if internal_mode == internal_wanted, discard
     72 		value in any case
     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
     76 	indent
     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
     81 
     82 	internalmode - the internalmode variable (evaluates to address)
     83 
     84    A command must be all upper-case, and alone on a line.
     85 
     86    Foo.  */
     87 
     88 #include <assert.h>
     89 #include <stdio.h>
     90 #include <ctype.h>
     91 #include <stdlib.h>
     92 #include <string.h>
     93 #include <stdint.h>
     94 #include <inttypes.h>
     95 
     96 #define DEF_SIZE 5000
     97 #define STACK 50
     98 
     99 /* Here is a string type ...  */
    100 
    101 typedef struct buffer
    102 {
    103   char *ptr;
    104   unsigned long write_idx;
    105   unsigned long size;
    106 } string_type;
    107 
    108 /* Compiled programs consist of arrays of these.  */
    109 
    110 typedef union
    111 {
    112   void (*f) (void);
    113   struct dict_struct *e;
    114   char *s;
    115   intptr_t l;
    116 } pcu;
    117 
    118 typedef struct dict_struct
    119 {
    120   char *word;
    121   struct dict_struct *next;
    122   pcu *code;
    123   int code_length;
    124   int code_end;
    125 } dict_type;
    126 
    127 int internal_wanted;
    128 intptr_t *internal_mode;
    129 
    130 int warning;
    131 
    132 string_type stack[STACK];
    133 string_type *tos;
    134 
    135 unsigned int pos_idx = 0; /* Pos in input buffer */
    136 string_type *buf_ptr; /* and the buffer */
    137 
    138 intptr_t istack[STACK];
    139 intptr_t *isp = &istack[0];
    140 
    141 dict_type *root;
    142 
    143 pcu *pc;
    144 
    145 static void
    146 die (char *msg)
    147 {
    148   fprintf (stderr, "%s\n", msg);
    149   exit (1);
    150 }
    151 
    152 static void *
    153 xmalloc (size_t size)
    154 {
    155   void *newmem;
    156 
    157   if (size == 0)
    158     size = 1;
    159   newmem = malloc (size);
    160   if (!newmem)
    161     die ("out of memory");
    162 
    163   return newmem;
    164 }
    165 
    166 static void *
    167 xrealloc (void *oldmem, size_t size)
    168 {
    169   void *newmem;
    170 
    171   if (size == 0)
    172     size = 1;
    173   if (!oldmem)
    174     newmem = malloc (size);
    175   else
    176     newmem = realloc (oldmem, size);
    177   if (!newmem)
    178     die ("out of memory");
    179 
    180   return newmem;
    181 }
    182 
    183 static char *
    184 xstrdup (const char *s)
    185 {
    186   size_t len = strlen (s) + 1;
    187   char *ret = xmalloc (len);
    188   return memcpy (ret, s, len);
    189 }
    190 
    191 static void
    192 init_string_with_size (string_type *buffer, unsigned int size)
    193 {
    194   buffer->write_idx = 0;
    195   buffer->size = size;
    196   buffer->ptr = xmalloc (size);
    197 }
    198 
    199 static void
    200 init_string (string_type *buffer)
    201 {
    202   init_string_with_size (buffer, DEF_SIZE);
    203 }
    204 
    205 static void
    206 write_buffer (string_type *buffer, FILE *f)
    207 {
    208   if (buffer->write_idx != 0
    209       && fwrite (buffer->ptr, buffer->write_idx, 1, f) != 1)
    210     die ("cannot write output");
    211 }
    212 
    213 static void
    214 delete_string (string_type *buffer)
    215 {
    216   free (buffer->ptr);
    217   buffer->ptr = NULL;
    218 }
    219 
    220 static char *
    221 addr (string_type *buffer, unsigned int idx)
    222 {
    223   return buffer->ptr + idx;
    224 }
    225 
    226 static char
    227 at (string_type *buffer, unsigned int pos)
    228 {
    229   if (pos >= buffer->write_idx)
    230     return 0;
    231   return buffer->ptr[pos];
    232 }
    233 
    234 static void
    235 catchar (string_type *buffer, int ch)
    236 {
    237   if (buffer->write_idx == buffer->size)
    238     {
    239       buffer->size *= 2;
    240       buffer->ptr = xrealloc (buffer->ptr, buffer->size);
    241     }
    242 
    243   buffer->ptr[buffer->write_idx++] = ch;
    244 }
    245 
    246 static void
    247 overwrite_string (string_type *dst, string_type *src)
    248 {
    249   free (dst->ptr);
    250   dst->size = src->size;
    251   dst->write_idx = src->write_idx;
    252   dst->ptr = src->ptr;
    253 }
    254 
    255 static void
    256 catbuf (string_type *buffer, char *buf, unsigned int len)
    257 {
    258   if (buffer->write_idx + len >= buffer->size)
    259     {
    260       while (buffer->write_idx + len >= buffer->size)
    261 	buffer->size *= 2;
    262       buffer->ptr = xrealloc (buffer->ptr, buffer->size);
    263     }
    264   memcpy (buffer->ptr + buffer->write_idx, buf, len);
    265   buffer->write_idx += len;
    266 }
    267 
    268 static void
    269 cattext (string_type *buffer, char *string)
    270 {
    271   catbuf (buffer, string, (unsigned int) strlen (string));
    272 }
    273 
    274 static void
    275 catstr (string_type *dst, string_type *src)
    276 {
    277   catbuf (dst, src->ptr, src->write_idx);
    278 }
    279 
    280 static unsigned int
    281 skip_white_and_stars (string_type *src, unsigned int idx)
    282 {
    283   char c;
    284   while ((c = at (src, idx)),
    285 	 isspace ((unsigned char) c)
    286 	 || (c == '*'
    287 	     /* Don't skip past end-of-comment or star as first
    288 		character on its line.  */
    289 	     && at (src, idx +1) != '/'
    290 	     && at (src, idx -1) != '\n'))
    291     idx++;
    292   return idx;
    293 }
    294 
    295 static unsigned int
    296 skip_past_newline_1 (string_type *ptr, unsigned int idx)
    297 {
    298   while (at (ptr, idx)
    299 	 && at (ptr, idx) != '\n')
    300     idx++;
    301   if (at (ptr, idx) == '\n')
    302     return idx + 1;
    303   return idx;
    304 }
    305 
    306 static void
    307 check_range (void)
    308 {
    309   if (tos < stack)
    310     die ("underflow in string stack");
    311   if (tos >= stack + STACK)
    312     die ("overflow in string stack");
    313 }
    314 
    315 static void
    316 icheck_range (void)
    317 {
    318   if (isp < istack)
    319     die ("underflow in integer stack");
    320   if (isp >= istack + STACK)
    321     die ("overflow in integer stack");
    322 }
    323 
    324 static void
    325 drop (void)
    326 {
    327   tos--;
    328   check_range ();
    329   delete_string (tos + 1);
    330   pc++;
    331 }
    332 
    333 static void
    334 idrop (void)
    335 {
    336   isp--;
    337   icheck_range ();
    338   pc++;
    339 }
    340 
    341 static void
    342 exec (dict_type *word)
    343 {
    344   pc = word->code;
    345   while (pc->f)
    346     pc->f ();
    347 }
    348 
    349 static void
    350 call (void)
    351 {
    352   pcu *oldpc = pc;
    353   dict_type *e = pc[1].e;
    354   exec (e);
    355   pc = oldpc + 2;
    356 }
    357 
    358 static void
    359 remchar (void)
    360 {
    361   if (tos->write_idx)
    362     tos->write_idx--;
    363   pc++;
    364 }
    365 
    366 static void
    367 strip_trailing_newlines (void)
    368 {
    369   while (tos->write_idx > 0
    370 	 && (isspace ((unsigned char) at (tos, tos->write_idx - 1))
    371 	     || at (tos, tos->write_idx - 1) == '\n'))
    372     tos->write_idx--;
    373   pc++;
    374 }
    375 
    376 static void
    377 push_number (void)
    378 {
    379   isp++;
    380   icheck_range ();
    381   pc++;
    382   *isp = pc->l;
    383   pc++;
    384 }
    385 
    386 /* This is a wrapper for push_number just so we can correctly free the
    387    variable at the end.  */
    388 static void
    389 push_variable (void)
    390 {
    391   push_number ();
    392 }
    393 
    394 static void
    395 push_text (void)
    396 {
    397   tos++;
    398   check_range ();
    399   init_string (tos);
    400   pc++;
    401   cattext (tos, pc->s);
    402   pc++;
    403 }
    404 
    405 /* This function removes everything not inside comments starting on
    406    the first char of the line from the  string, also when copying
    407    comments, removes blank space and leading *'s.
    408    Blank lines are turned into one blank line.  */
    409 
    410 static void
    411 remove_noncomments (string_type *src, string_type *dst)
    412 {
    413   unsigned int idx = 0;
    414 
    415   while (at (src, idx))
    416     {
    417       /* Now see if we have a comment at the start of the line.  */
    418       if (at (src, idx) == '\n'
    419 	  && at (src, idx + 1) == '/'
    420 	  && at (src, idx + 2) == '*')
    421 	{
    422 	  idx += 3;
    423 
    424 	  idx = skip_white_and_stars (src, idx);
    425 
    426 	  /* Remove leading dot */
    427 	  if (at (src, idx) == '.')
    428 	    idx++;
    429 
    430 	  /* Copy to the end of the line, or till the end of the
    431 	     comment.  */
    432 	  while (at (src, idx))
    433 	    {
    434 	      if (at (src, idx) == '\n')
    435 		{
    436 		  /* end of line, echo and scrape of leading blanks  */
    437 		  if (at (src, idx + 1) == '\n')
    438 		    catchar (dst, '\n');
    439 		  catchar (dst, '\n');
    440 		  idx++;
    441 		  idx = skip_white_and_stars (src, idx);
    442 		}
    443 	      else if (at (src, idx) == '*' && at (src, idx + 1) == '/')
    444 		{
    445 		  idx += 2;
    446 		  cattext (dst, "\nENDDD\n");
    447 		  break;
    448 		}
    449 	      else
    450 		{
    451 		  catchar (dst, at (src, idx));
    452 		  idx++;
    453 		}
    454 	    }
    455 	}
    456       else
    457 	idx++;
    458     }
    459 }
    460 
    461 static void
    462 print_stack_level (void)
    463 {
    464   fprintf (stderr, "current string stack depth = %ld, ",
    465 	   (long) (tos - stack));
    466   fprintf (stderr, "current integer stack depth = %ld\n",
    467 	   (long) (isp - istack));
    468   pc++;
    469 }
    470 
    471 /* turn {*
    472    and *} into comments */
    473 
    474 static void
    475 translatecomments (void)
    476 {
    477   unsigned int idx = 0;
    478   string_type out;
    479   init_string (&out);
    480 
    481   while (at (tos, idx))
    482     {
    483       if (at (tos, idx) == '{' && at (tos, idx + 1) == '*')
    484 	{
    485 	  cattext (&out, "/*");
    486 	  idx += 2;
    487 	}
    488       else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}')
    489 	{
    490 	  cattext (&out, "*/");
    491 	  idx += 2;
    492 	}
    493       else
    494 	{
    495 	  catchar (&out, at (tos, idx));
    496 	  idx++;
    497 	}
    498     }
    499 
    500   overwrite_string (tos, &out);
    501 
    502   pc++;
    503 }
    504 
    505 /* Wrap tos-1 as a C comment, indenting by tos.  */
    506 
    507 static void
    508 wrap_comment (void)
    509 {
    510   string_type out;
    511   init_string (&out);
    512 
    513   catstr (&out, tos);
    514   cattext (&out, "/* ");
    515   for (unsigned int idx = 0; at (tos - 1, idx); idx++)
    516     {
    517       catchar (&out, at (tos - 1, idx));
    518       if (at (tos - 1, idx) == '\n' && at (tos - 1, idx + 1) != '\n')
    519 	{
    520 	  catstr (&out, tos);
    521 	  cattext (&out, "   ");
    522 	}
    523     }
    524   cattext (&out, "  */");
    525 
    526   overwrite_string (tos - 1, &out);
    527   drop ();
    528 }
    529 
    530 /* Mod tos so that only lines with leading dots remain */
    531 static void
    532 outputdots (void)
    533 {
    534   unsigned int idx = 0;
    535   string_type out;
    536   init_string (&out);
    537 
    538   while (at (tos, idx))
    539     {
    540       /* Every iteration begins at the start of a line.  */
    541       if (at (tos, idx) == '.')
    542 	{
    543 	  char c;
    544 	  int spaces;
    545 
    546 	  idx++;
    547 	  spaces = 0;
    548 	  while ((c = at (tos, idx)) && c != '\n')
    549 	    {
    550 	      if (spaces >= 0)
    551 		{
    552 		  if (c == ' ')
    553 		    {
    554 		      spaces++;
    555 		      idx++;
    556 		      continue;
    557 		    }
    558 		  else
    559 		    {
    560 		      while (spaces >= 8)
    561 			{
    562 			  catchar (&out, '\t');
    563 			  spaces -= 8;
    564 			}
    565 		      while (spaces-- > 0)
    566 			catchar (&out, ' ');
    567 		    }
    568 		}
    569 	      if (c == '{' && at (tos, idx + 1) == '*')
    570 		{
    571 		  cattext (&out, "/*");
    572 		  idx += 2;
    573 		}
    574 	      else if (c == '*' && at (tos, idx + 1) == '}')
    575 		{
    576 		  cattext (&out, "*/");
    577 		  idx += 2;
    578 		}
    579 	      else
    580 		{
    581 		  catchar (&out, c);
    582 		  idx++;
    583 		}
    584 	    }
    585 	  if (c == '\n')
    586 	    idx++;
    587 	  catchar (&out, '\n');
    588 	}
    589       else
    590 	{
    591 	  idx = skip_past_newline_1 (tos, idx);
    592 	}
    593     }
    594 
    595   overwrite_string (tos, &out);
    596   pc++;
    597 }
    598 
    599 /* Find lines starting with . and | and put example around them on tos */
    600 static void
    601 courierize (void)
    602 {
    603   string_type out;
    604   unsigned int idx = 0;
    605   int command = 0;
    606 
    607   init_string (&out);
    608 
    609   while (at (tos, idx))
    610     {
    611       if (at (tos, idx) == '\n'
    612 	  && (at (tos, idx +1 ) == '.'
    613 	      || at (tos, idx + 1) == '|'))
    614 	{
    615 	  cattext (&out, "\n@example\n");
    616 	  do
    617 	    {
    618 	      idx += 2;
    619 
    620 	      while (at (tos, idx) && at (tos, idx) != '\n')
    621 		{
    622 		  if (command > 1)
    623 		    {
    624 		      /* We are inside {} parameters of some command;
    625 			 Just pass through until matching brace.  */
    626 		      if (at (tos, idx) == '{')
    627 			++command;
    628 		      else if (at (tos, idx) == '}')
    629 			--command;
    630 		    }
    631 		  else if (command != 0)
    632 		    {
    633 		      if (at (tos, idx) == '{')
    634 			++command;
    635 		      else if (!islower ((unsigned char) at (tos, idx)))
    636 			--command;
    637 		    }
    638 		  else if (at (tos, idx) == '@'
    639 			   && islower ((unsigned char) at (tos, idx + 1)))
    640 		    {
    641 		      ++command;
    642 		    }
    643 		  else if (at (tos, idx) == '{' && at (tos, idx + 1) == '*')
    644 		    {
    645 		      cattext (&out, "/*");
    646 		      idx += 2;
    647 		      continue;
    648 		    }
    649 		  else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}')
    650 		    {
    651 		      cattext (&out, "*/");
    652 		      idx += 2;
    653 		      continue;
    654 		    }
    655 		  else if (at (tos, idx) == '{'
    656 			   || at (tos, idx) == '}')
    657 		    {
    658 		      catchar (&out, '@');
    659 		    }
    660 
    661 		  catchar (&out, at (tos, idx));
    662 		  idx++;
    663 		}
    664 	      catchar (&out, '\n');
    665 	    }
    666 	  while (at (tos, idx) == '\n'
    667 		 && ((at (tos, idx + 1) == '.')
    668 		     || (at (tos, idx + 1) == '|')))
    669 	    ;
    670 	  cattext (&out, "@end example");
    671 	}
    672       else
    673 	{
    674 	  catchar (&out, at (tos, idx));
    675 	  idx++;
    676 	}
    677     }
    678 
    679   overwrite_string (tos, &out);
    680   pc++;
    681 }
    682 
    683 /* Finds any lines starting with "o ", if there are any, then turns
    684    on @itemize @bullet, and @items each of them. Then ends with @end
    685    itemize, inplace at TOS*/
    686 
    687 static void
    688 bulletize (void)
    689 {
    690   unsigned int idx = 0;
    691   int on = 0;
    692   string_type out;
    693   init_string (&out);
    694 
    695   while (at (tos, idx))
    696     {
    697       if (at (tos, idx) == '@'
    698 	  && at (tos, idx + 1) == '*')
    699 	{
    700 	  cattext (&out, "*");
    701 	  idx += 2;
    702 	}
    703       else if (at (tos, idx) == '\n'
    704 	       && at (tos, idx + 1) == 'o'
    705 	       && isspace ((unsigned char) at (tos, idx + 2)))
    706 	{
    707 	  if (!on)
    708 	    {
    709 	      cattext (&out, "\n@itemize @bullet\n");
    710 	      on = 1;
    711 
    712 	    }
    713 	  cattext (&out, "\n@item\n");
    714 	  idx += 3;
    715 	}
    716       else
    717 	{
    718 	  catchar (&out, at (tos, idx));
    719 	  if (on && at (tos, idx) == '\n'
    720 	      && at (tos, idx + 1) == '\n'
    721 	      && at (tos, idx + 2) != 'o')
    722 	    {
    723 	      cattext (&out, "@end itemize");
    724 	      on = 0;
    725 	    }
    726 	  idx++;
    727 
    728 	}
    729     }
    730   if (on)
    731     {
    732       cattext (&out, "@end itemize\n");
    733     }
    734 
    735   delete_string (tos);
    736   *tos = out;
    737   pc++;
    738 }
    739 
    740 /* Turn <<foo>> into @code{foo} in place at TOS*/
    741 
    742 static void
    743 do_fancy_stuff (void)
    744 {
    745   unsigned int idx = 0;
    746   string_type out;
    747   init_string (&out);
    748   while (at (tos, idx))
    749     {
    750       if (at (tos, idx) == '<'
    751 	  && at (tos, idx + 1) == '<'
    752 	  && !isspace ((unsigned char) at (tos, idx + 2)))
    753 	{
    754 	  /* This qualifies as a << startup.  */
    755 	  idx += 2;
    756 	  cattext (&out, "@code{");
    757 	  while (at (tos, idx)
    758 		 && at (tos, idx) != '>' )
    759 	    {
    760 	      catchar (&out, at (tos, idx));
    761 	      idx++;
    762 
    763 	    }
    764 	  cattext (&out, "}");
    765 	  idx += 2;
    766 	}
    767       else
    768 	{
    769 	  catchar (&out, at (tos, idx));
    770 	  idx++;
    771 	}
    772     }
    773   delete_string (tos);
    774   *tos = out;
    775   pc++;
    776 
    777 }
    778 
    779 /* A command is all upper case,and alone on a line.  */
    780 
    781 static int
    782 iscommand (string_type *ptr, unsigned int idx)
    783 {
    784   unsigned int len = 0;
    785   while (at (ptr, idx))
    786     {
    787       if (isupper ((unsigned char) at (ptr, idx))
    788 	  || at (ptr, idx) == ' ' || at (ptr, idx) == '_')
    789 	{
    790 	  len++;
    791 	  idx++;
    792 	}
    793       else if (at (ptr, idx) == '\n')
    794 	{
    795 	  if (len > 3)
    796 	    return 1;
    797 	  return 0;
    798 	}
    799       else
    800 	return 0;
    801     }
    802   return 0;
    803 }
    804 
    805 static int
    806 copy_past_newline (string_type *ptr, unsigned int idx, string_type *dst)
    807 {
    808   int column = 0;
    809 
    810   while (at (ptr, idx) && at (ptr, idx) != '\n')
    811     {
    812       if (at (ptr, idx) == '\t')
    813 	{
    814 	  /* Expand tabs.  Neither makeinfo nor TeX can cope well with
    815 	     them.  */
    816 	  do
    817 	    catchar (dst, ' ');
    818 	  while (++column & 7);
    819 	}
    820       else
    821 	{
    822 	  catchar (dst, at (ptr, idx));
    823 	  column++;
    824 	}
    825       idx++;
    826 
    827     }
    828   catchar (dst, at (ptr, idx));
    829   idx++;
    830   return idx;
    831 
    832 }
    833 
    834 static void
    835 icopy_past_newline (void)
    836 {
    837   tos++;
    838   check_range ();
    839   init_string (tos);
    840   pos_idx = copy_past_newline (buf_ptr, pos_idx, tos);
    841   pc++;
    842 }
    843 
    844 static void
    845 kill_bogus_lines (void)
    846 {
    847   int sl;
    848 
    849   int idx = 0;
    850   int c;
    851   int dot = 0;
    852 
    853   string_type out;
    854   init_string (&out);
    855   /* Drop leading nl.  */
    856   while (at (tos, idx) == '\n')
    857     {
    858       idx++;
    859     }
    860   c = idx;
    861 
    862   /* If the first char is a '.' prepend a newline so that it is
    863      recognized properly later.  */
    864   if (at (tos, idx) == '.')
    865     catchar (&out, '\n');
    866 
    867   /* Find the last char.  */
    868   while (at (tos, idx))
    869     {
    870       idx++;
    871     }
    872 
    873   /* Find the last non white before the nl.  */
    874   idx--;
    875 
    876   while (idx && isspace ((unsigned char) at (tos, idx)))
    877     idx--;
    878   idx++;
    879 
    880   /* Copy buffer upto last char, but blank lines before and after
    881      dots don't count.  */
    882   sl = 1;
    883 
    884   while (c < idx)
    885     {
    886       if (at (tos, c) == '\n'
    887 	  && at (tos, c + 1) == '\n'
    888 	  && at (tos, c + 2) == '.')
    889 	{
    890 	  /* Ignore two newlines before a dot.  */
    891 	  c++;
    892 	}
    893       else if (at (tos, c) == '.' && sl)
    894 	{
    895 	  /* remember that this line started with a dot.  */
    896 	  dot = 2;
    897 	}
    898       else if (at (tos, c) == '\n'
    899 	       && at (tos, c + 1) == '\n'
    900 	       && dot)
    901 	{
    902 	  c++;
    903 	  /* Ignore two newlines when last line was dot.  */
    904 	}
    905 
    906       catchar (&out, at (tos, c));
    907       if (at (tos, c) == '\n')
    908 	{
    909 	  sl = 1;
    910 
    911 	  if (dot == 2)
    912 	    dot = 1;
    913 	  else
    914 	    dot = 0;
    915 	}
    916       else
    917 	sl = 0;
    918 
    919       c++;
    920 
    921     }
    922 
    923   /* Append nl.  */
    924   catchar (&out, '\n');
    925   pc++;
    926   delete_string (tos);
    927   *tos = out;
    928 
    929 }
    930 
    931 static void
    932 collapse_whitespace (void)
    933 {
    934   int last_was_ws = 0;
    935   int idx;
    936 
    937   string_type out;
    938   init_string (&out);
    939 
    940   for (idx = 0; at (tos, idx) != 0; ++idx)
    941     {
    942       char c = at (tos, idx);
    943       if (isspace (c))
    944 	{
    945 	  if (!last_was_ws)
    946 	    {
    947 	      catchar (&out, ' ');
    948 	      last_was_ws = 1;
    949 	    }
    950 	}
    951       else
    952 	{
    953 	  catchar (&out, c);
    954 	  last_was_ws = 0;
    955 	}
    956     }
    957 
    958   pc++;
    959   delete_string (tos);
    960   *tos = out;
    961 }
    962 
    963 /* indent
    964    Take the string at the top of the stack, do some prettying.  */
    965 
    966 static void
    967 indent (void)
    968 {
    969   string_type out;
    970   int tab = 0;
    971   int idx = 0;
    972   int ol = 0;
    973   init_string (&out);
    974   while (at (tos, idx))
    975     {
    976       switch (at (tos, idx))
    977 	{
    978 	case '\n':
    979 	  catchar (&out, '\n');
    980 	  idx++;
    981 	  if (tab && at (tos, idx))
    982 	    {
    983 	      int i;
    984 	      for (i = 0; i < tab - 1; i += 2)
    985 		catchar (&out, '\t');
    986 	      if (i < tab)
    987 		cattext (&out, "    ");
    988 	    }
    989 	  ol = 0;
    990 	  break;
    991 	case '(':
    992 	  if (ol == 0)
    993 	    {
    994 	      int i;
    995 	      for (i = 1; i < tab - 1; i += 2)
    996 		catchar (&out, '\t');
    997 	      if (i < tab)
    998 		cattext (&out, "    ");
    999 	      cattext (&out, "   ");
   1000 	    }
   1001 	  tab++;
   1002 	  idx++;
   1003 	  catchar (&out, '(');
   1004 	  ol = 1;
   1005 	  break;
   1006 	case ')':
   1007 	  tab--;
   1008 	  catchar (&out, ')');
   1009 	  idx++;
   1010 	  ol = 1;
   1011 	  break;
   1012 	default:
   1013 	  catchar (&out, at (tos, idx));
   1014 	  ol = 1;
   1015 	  idx++;
   1016 	  break;
   1017 	}
   1018     }
   1019 
   1020   pc++;
   1021   delete_string (tos);
   1022   *tos = out;
   1023 
   1024 }
   1025 
   1026 static void
   1027 get_stuff_in_command (void)
   1028 {
   1029   tos++;
   1030   check_range ();
   1031   init_string (tos);
   1032 
   1033   while (at (buf_ptr, pos_idx))
   1034     {
   1035       if (iscommand (buf_ptr, pos_idx))
   1036 	break;
   1037       pos_idx = copy_past_newline (buf_ptr, pos_idx, tos);
   1038     }
   1039   pc++;
   1040 }
   1041 
   1042 static void
   1043 swap (void)
   1044 {
   1045   string_type t;
   1046 
   1047   t = tos[0];
   1048   tos[0] = tos[-1];
   1049   tos[-1] = t;
   1050   pc++;
   1051 }
   1052 
   1053 static void
   1054 other_dup (void)
   1055 {
   1056   tos++;
   1057   check_range ();
   1058   init_string (tos);
   1059   catstr (tos, tos - 1);
   1060   pc++;
   1061 }
   1062 
   1063 static void
   1064 icatstr (void)
   1065 {
   1066   tos--;
   1067   check_range ();
   1068   catstr (tos, tos + 1);
   1069   delete_string (tos + 1);
   1070   pc++;
   1071 }
   1072 
   1073 static void
   1074 skip_past_newline (void)
   1075 {
   1076   pos_idx = skip_past_newline_1 (buf_ptr, pos_idx);
   1077   pc++;
   1078 }
   1079 
   1080 static void
   1081 maybecatstr (void)
   1082 {
   1083   if (internal_wanted == *internal_mode)
   1084     {
   1085       catstr (tos - 1, tos);
   1086     }
   1087   delete_string (tos);
   1088   tos--;
   1089   check_range ();
   1090   pc++;
   1091 }
   1092 
   1093 static void
   1094 catstrif (void)
   1095 {
   1096   int cond = isp[0];
   1097   isp--;
   1098   icheck_range ();
   1099   if (cond)
   1100     catstr (tos - 1, tos);
   1101   delete_string (tos);
   1102   tos--;
   1103   check_range ();
   1104   pc++;
   1105 }
   1106 
   1107 static char *
   1108 nextword (char *string, char **word)
   1109 {
   1110   char *word_start;
   1111   int idx;
   1112   char *dst;
   1113   char *src;
   1114 
   1115   int length = 0;
   1116 
   1117   while (isspace ((unsigned char) *string) || *string == '-')
   1118     {
   1119       if (*string == '-')
   1120 	{
   1121 	  while (*string && *string != '\n')
   1122 	    string++;
   1123 
   1124 	}
   1125       else
   1126 	{
   1127 	  string++;
   1128 	}
   1129     }
   1130   if (!*string)
   1131     {
   1132       *word = NULL;
   1133       return NULL;
   1134     }
   1135 
   1136   word_start = string;
   1137   if (*string == '"')
   1138     {
   1139       do
   1140 	{
   1141 	  string++;
   1142 	  length++;
   1143 	  if (*string == '\\')
   1144 	    {
   1145 	      string += 2;
   1146 	      length += 2;
   1147 	    }
   1148 	}
   1149       while (*string != '"');
   1150     }
   1151   else
   1152     {
   1153       while (!isspace ((unsigned char) *string))
   1154 	{
   1155 	  string++;
   1156 	  length++;
   1157 
   1158 	}
   1159     }
   1160 
   1161   *word = xmalloc (length + 1);
   1162 
   1163   dst = *word;
   1164   src = word_start;
   1165 
   1166   for (idx = 0; idx < length; idx++)
   1167     {
   1168       if (src[idx] == '\\')
   1169 	switch (src[idx + 1])
   1170 	  {
   1171 	  case 'n':
   1172 	    *dst++ = '\n';
   1173 	    idx++;
   1174 	    break;
   1175 	  case '"':
   1176 	  case '\\':
   1177 	    *dst++ = src[idx + 1];
   1178 	    idx++;
   1179 	    break;
   1180 	  default:
   1181 	    *dst++ = '\\';
   1182 	    break;
   1183 	  }
   1184       else
   1185 	*dst++ = src[idx];
   1186     }
   1187   *dst++ = 0;
   1188 
   1189   if (*string)
   1190     return string + 1;
   1191   else
   1192     return NULL;
   1193 }
   1194 
   1195 static dict_type *
   1196 lookup_word (char *word)
   1197 {
   1198   dict_type *ptr = root;
   1199   while (ptr)
   1200     {
   1201       if (strcmp (ptr->word, word) == 0)
   1202 	return ptr;
   1203       ptr = ptr->next;
   1204     }
   1205   if (warning)
   1206     fprintf (stderr, "Can't find %s\n", word);
   1207   return NULL;
   1208 }
   1209 
   1210 static void
   1211 free_words (void)
   1212 {
   1213   dict_type *ptr = root;
   1214 
   1215   while (ptr)
   1216     {
   1217       dict_type *next;
   1218 
   1219       free (ptr->word);
   1220       if (ptr->code)
   1221 	{
   1222 	  int i;
   1223 	  for (i = 0; i < ptr->code_end - 1; i ++)
   1224 	    if (ptr->code[i].f == push_text
   1225 		&& ptr->code[i + 1].s)
   1226 	      {
   1227 		free (ptr->code[i + 1].s - 1);
   1228 		++i;
   1229 	      }
   1230 	    else if (ptr->code[i].f == push_variable)
   1231 	      {
   1232 		free ((void *) ptr->code[i + 1].l);
   1233 		++i;
   1234 	      }
   1235 	  free (ptr->code);
   1236 	}
   1237       next = ptr->next;
   1238       free (ptr);
   1239       ptr = next;
   1240     }
   1241 }
   1242 
   1243 static void
   1244 perform (void)
   1245 {
   1246   tos = stack;
   1247 
   1248   while (at (buf_ptr, pos_idx))
   1249     {
   1250       /* It's worth looking through the command list.  */
   1251       if (iscommand (buf_ptr, pos_idx))
   1252 	{
   1253 	  char *next;
   1254 	  dict_type *word;
   1255 
   1256 	  (void) nextword (addr (buf_ptr, pos_idx), &next);
   1257 
   1258 	  word = lookup_word (next);
   1259 
   1260 	  if (word)
   1261 	    {
   1262 	      exec (word);
   1263 	    }
   1264 	  else
   1265 	    {
   1266 	      if (warning)
   1267 		fprintf (stderr, "warning, %s is not recognised\n", next);
   1268 	      pos_idx = skip_past_newline_1 (buf_ptr, pos_idx);
   1269 	    }
   1270 	  free (next);
   1271 	}
   1272       else
   1273 	pos_idx = skip_past_newline_1 (buf_ptr, pos_idx);
   1274     }
   1275 }
   1276 
   1277 static dict_type *
   1278 newentry (char *word)
   1279 {
   1280   dict_type *new_d = xmalloc (sizeof (*new_d));
   1281   new_d->word = word;
   1282   new_d->next = root;
   1283   root = new_d;
   1284   new_d->code = xmalloc (sizeof (*new_d->code));
   1285   new_d->code_length = 1;
   1286   new_d->code_end = 0;
   1287   return new_d;
   1288 }
   1289 
   1290 static unsigned int
   1291 add_to_definition (dict_type *entry, pcu word)
   1292 {
   1293   if (entry->code_end == entry->code_length)
   1294     {
   1295       entry->code_length += 2;
   1296       entry->code = xrealloc (entry->code,
   1297 			      entry->code_length * sizeof (*entry->code));
   1298     }
   1299   entry->code[entry->code_end] = word;
   1300 
   1301   return entry->code_end++;
   1302 }
   1303 
   1304 static void
   1305 add_intrinsic (char *name, void (*func) (void))
   1306 {
   1307   dict_type *new_d = newentry (xstrdup (name));
   1308   pcu p = { func };
   1309   add_to_definition (new_d, p);
   1310   p.f = 0;
   1311   add_to_definition (new_d, p);
   1312 }
   1313 
   1314 static void
   1315 add_variable (char *name, intptr_t *loc)
   1316 {
   1317   dict_type *new_d = newentry (name);
   1318   pcu p = { push_variable };
   1319   add_to_definition (new_d, p);
   1320   p.l = (intptr_t) loc;
   1321   add_to_definition (new_d, p);
   1322   p.f = 0;
   1323   add_to_definition (new_d, p);
   1324 }
   1325 
   1326 static void
   1327 add_intrinsic_variable (const char *name, intptr_t *loc)
   1328 {
   1329   add_variable (xstrdup (name), loc);
   1330 }
   1331 
   1332 static void
   1333 compile (char *string)
   1334 {
   1335   /* Add words to the dictionary.  */
   1336   char *word;
   1337 
   1338   string = nextword (string, &word);
   1339   while (string && *string && word[0])
   1340     {
   1341       if (word[0] == ':')
   1342 	{
   1343 	  dict_type *ptr;
   1344 	  pcu p;
   1345 
   1346 	  /* Compile a word and add to dictionary.  */
   1347 	  free (word);
   1348 	  string = nextword (string, &word);
   1349 	  if (!string)
   1350 	    continue;
   1351 	  ptr = newentry (word);
   1352 	  string = nextword (string, &word);
   1353 	  if (!string)
   1354 	    {
   1355 	      free (ptr->code);
   1356 	      free (ptr);
   1357 	      continue;
   1358 	    }
   1359 
   1360 	  while (word[0] != ';')
   1361 	    {
   1362 	      switch (word[0])
   1363 		{
   1364 		case '"':
   1365 		  /* got a string, embed magic push string
   1366 		     function */
   1367 		  p.f = push_text;
   1368 		  add_to_definition (ptr, p);
   1369 		  p.s = word + 1;
   1370 		  add_to_definition (ptr, p);
   1371 		  break;
   1372 		case '0':
   1373 		case '1':
   1374 		case '2':
   1375 		case '3':
   1376 		case '4':
   1377 		case '5':
   1378 		case '6':
   1379 		case '7':
   1380 		case '8':
   1381 		case '9':
   1382 		  /* Got a number, embedd the magic push number
   1383 		     function */
   1384 		  p.f = push_number;
   1385 		  add_to_definition (ptr, p);
   1386 		  p.l = atol (word);
   1387 		  add_to_definition (ptr, p);
   1388 		  free (word);
   1389 		  break;
   1390 		default:
   1391 		  p.f = call;
   1392 		  add_to_definition (ptr, p);
   1393 		  p.e = lookup_word (word);
   1394 		  add_to_definition (ptr, p);
   1395 		  free (word);
   1396 		}
   1397 
   1398 	      string = nextword (string, &word);
   1399 	    }
   1400 	  p.f = 0;
   1401 	  add_to_definition (ptr, p);
   1402 	  free (word);
   1403 	  string = nextword (string, &word);
   1404 	}
   1405       else if (strcmp (word, "variable") == 0)
   1406 	{
   1407 	  free (word);
   1408 	  string = nextword (string, &word);
   1409 	  if (!string)
   1410 	    continue;
   1411 	  intptr_t *loc = xmalloc (sizeof (intptr_t));
   1412 	  *loc = 0;
   1413 	  add_variable (word, loc);
   1414 	  string = nextword (string, &word);
   1415 	}
   1416       else
   1417 	{
   1418 	  fprintf (stderr, "syntax error at %s\n", string - 1);
   1419 	}
   1420     }
   1421   free (word);
   1422 }
   1423 
   1424 static void
   1425 bang (void)
   1426 {
   1427   *(intptr_t *) ((isp[0])) = isp[-1];
   1428   isp -= 2;
   1429   icheck_range ();
   1430   pc++;
   1431 }
   1432 
   1433 static void
   1434 atsign (void)
   1435 {
   1436   isp[0] = *(intptr_t *) (isp[0]);
   1437   pc++;
   1438 }
   1439 
   1440 static void
   1441 hello (void)
   1442 {
   1443   printf ("hello\n");
   1444   pc++;
   1445 }
   1446 
   1447 static void
   1448 stdout_ (void)
   1449 {
   1450   isp++;
   1451   icheck_range ();
   1452   *isp = 1;
   1453   pc++;
   1454 }
   1455 
   1456 static void
   1457 stderr_ (void)
   1458 {
   1459   isp++;
   1460   icheck_range ();
   1461   *isp = 2;
   1462   pc++;
   1463 }
   1464 
   1465 static void
   1466 print (void)
   1467 {
   1468   if (*isp == 1)
   1469     write_buffer (tos, stdout);
   1470   else if (*isp == 2)
   1471     write_buffer (tos, stderr);
   1472   else
   1473     fprintf (stderr, "print: illegal print destination `%" PRIdPTR "'\n", *isp);
   1474   isp--;
   1475   tos--;
   1476   icheck_range ();
   1477   check_range ();
   1478   pc++;
   1479 }
   1480 
   1481 static void
   1482 read_in (string_type *str, FILE *file)
   1483 {
   1484   char buff[10000];
   1485   unsigned int r;
   1486   do
   1487     {
   1488       r = fread (buff, 1, sizeof (buff), file);
   1489       catbuf (str, buff, r);
   1490     }
   1491   while (r);
   1492   buff[0] = 0;
   1493 
   1494   catbuf (str, buff, 1);
   1495 }
   1496 
   1497 static void
   1498 usage (void)
   1499 {
   1500   fprintf (stderr, "usage: -[d|i|g] <file >file\n");
   1501   exit (33);
   1502 }
   1503 
   1504 /* There is no reliable way to declare exit.  Sometimes it returns
   1505    int, and sometimes it returns void.  Sometimes it changes between
   1506    OS releases.  Trying to get it declared correctly in the hosts file
   1507    is a pointless waste of time.  */
   1508 
   1509 static void
   1510 chew_exit (void)
   1511 {
   1512   exit (0);
   1513 }
   1514 
   1515 int
   1516 main (int ac, char *av[])
   1517 {
   1518   unsigned int i;
   1519   string_type buffer;
   1520   string_type pptr;
   1521 
   1522   init_string (&buffer);
   1523   init_string (&pptr);
   1524   init_string (stack + 0);
   1525   tos = stack + 1;
   1526   buf_ptr = &pptr;
   1527 
   1528   add_intrinsic ("push_text", push_text);
   1529   add_intrinsic ("!", bang);
   1530   add_intrinsic ("@", atsign);
   1531   add_intrinsic ("hello", hello);
   1532   add_intrinsic ("stdout", stdout_);
   1533   add_intrinsic ("stderr", stderr_);
   1534   add_intrinsic ("print", print);
   1535   add_intrinsic ("skip_past_newline", skip_past_newline);
   1536   add_intrinsic ("catstr", icatstr);
   1537   add_intrinsic ("copy_past_newline", icopy_past_newline);
   1538   add_intrinsic ("dup", other_dup);
   1539   add_intrinsic ("drop", drop);
   1540   add_intrinsic ("idrop", idrop);
   1541   add_intrinsic ("remchar", remchar);
   1542   add_intrinsic ("get_stuff_in_command", get_stuff_in_command);
   1543   add_intrinsic ("do_fancy_stuff", do_fancy_stuff);
   1544   add_intrinsic ("bulletize", bulletize);
   1545   add_intrinsic ("courierize", courierize);
   1546   /* If the following line gives an error, exit() is not declared in the
   1547      ../hosts/foo.h file for this host.  Fix it there, not here!  */
   1548   /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor.  */
   1549   add_intrinsic ("exit", chew_exit);
   1550   add_intrinsic ("swap", swap);
   1551   add_intrinsic ("outputdots", outputdots);
   1552   add_intrinsic ("maybecatstr", maybecatstr);
   1553   add_intrinsic ("catstrif", catstrif);
   1554   add_intrinsic ("translatecomments", translatecomments);
   1555   add_intrinsic ("wrap_comment", wrap_comment);
   1556   add_intrinsic ("kill_bogus_lines", kill_bogus_lines);
   1557   add_intrinsic ("indent", indent);
   1558   add_intrinsic ("print_stack_level", print_stack_level);
   1559   add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines);
   1560   add_intrinsic ("collapse_whitespace", collapse_whitespace);
   1561 
   1562   internal_mode = xmalloc (sizeof (intptr_t));
   1563   *internal_mode = 0;
   1564   add_intrinsic_variable ("internalmode", internal_mode);
   1565 
   1566   /* Put a nl at the start.  */
   1567   catchar (&buffer, '\n');
   1568 
   1569   read_in (&buffer, stdin);
   1570   remove_noncomments (&buffer, buf_ptr);
   1571   for (i = 1; i < (unsigned int) ac; i++)
   1572     {
   1573       if (av[i][0] == '-')
   1574 	{
   1575 	  if (av[i][1] == 'f')
   1576 	    {
   1577 	      string_type b;
   1578 	      FILE *f;
   1579 	      init_string (&b);
   1580 
   1581 	      f = fopen (av[i + 1], "r");
   1582 	      if (!f)
   1583 		{
   1584 		  fprintf (stderr, "Can't open the input file %s\n",
   1585 			   av[i + 1]);
   1586 		  return 33;
   1587 		}
   1588 
   1589 	      read_in (&b, f);
   1590 	      compile (b.ptr);
   1591 	      perform ();
   1592 	      delete_string (&b);
   1593 	    }
   1594 	  else if (av[i][1] == 'i')
   1595 	    {
   1596 	      internal_wanted = 1;
   1597 	    }
   1598 	  else if (av[i][1] == 'w')
   1599 	    {
   1600 	      warning = 1;
   1601 	    }
   1602 	  else
   1603 	    usage ();
   1604 	}
   1605     }
   1606   write_buffer (stack + 0, stdout);
   1607   free_words ();
   1608   delete_string (&pptr);
   1609   delete_string (&buffer);
   1610   if (tos != stack)
   1611     {
   1612       fprintf (stderr, "finishing with current stack level %ld\n",
   1613 	       (long) (tos - stack));
   1614       return 1;
   1615     }
   1616   return 0;
   1617 }
   1618