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