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