Back to index

cell-binutils  2.17cvs20070401
chew.c
Go to the documentation of this file.
00001 /* chew
00002    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2000, 2001,
00003    2002, 2003, 2005
00004    Free Software Foundation, Inc.
00005    Contributed by steve chamberlain @cygnus
00006 
00007 This file is part of BFD, the Binary File Descriptor library.
00008 
00009 This program is free software; you can redistribute it and/or modify
00010 it under the terms of the GNU General Public License as published by
00011 the Free Software Foundation; either version 2 of the License, or
00012 (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
00022 
00023 /* Yet another way of extracting documentation from source.
00024    No, I haven't finished it yet, but I hope you people like it better
00025    than the old way
00026 
00027    sac
00028 
00029    Basically, this is a sort of string forth, maybe we should call it
00030    struth?
00031 
00032    You define new words thus:
00033    : <newword> <oldwords> ;
00034 
00035 */
00036 
00037 /* Primitives provided by the program:
00038 
00039    Two stacks are provided, a string stack and an integer stack.
00040 
00041    Internal state variables:
00042        internal_wanted - indicates whether `-i' was passed
00043        internal_mode - user-settable
00044 
00045    Commands:
00046        push_text
00047        ! - pop top of integer stack for address, pop next for value; store
00048        @ - treat value on integer stack as the address of an integer; push
00049               that integer on the integer stack after popping the "address"
00050        hello - print "hello\n" to stdout
00051        stdout - put stdout marker on TOS
00052        stderr - put stderr marker on TOS
00053        print - print TOS-1 on TOS (eg: "hello\n" stdout print)
00054        skip_past_newline
00055        catstr - fn icatstr
00056        copy_past_newline - append input, up to and including newline into TOS
00057        dup - fn other_dup
00058        drop - discard TOS
00059        idrop - ditto
00060        remchar - delete last character from TOS
00061        get_stuff_in_command
00062        do_fancy_stuff - translate <<foo>> to @code{foo} in TOS
00063        bulletize - if "o" lines found, prepend @itemize @bullet to TOS
00064               and @item to each "o" line; append @end itemize
00065        courierize - put @example around . and | lines, translate {* *} { }
00066        exit - fn chew_exit
00067        swap
00068        outputdots - strip out lines without leading dots
00069        paramstuff - convert full declaration into "PARAMS" form if not already
00070        maybecatstr - do catstr if internal_mode == internal_wanted, discard
00071               value in any case
00072        translatecomments - turn {* and *} into comment delimiters
00073        kill_bogus_lines - get rid of extra newlines
00074        indent
00075        internalmode - pop from integer stack, set `internalmode' to that value
00076        print_stack_level - print current stack depth to stderr
00077        strip_trailing_newlines - go ahead, guess...
00078        [quoted string] - push string onto string stack
00079        [word starting with digit] - push atol(str) onto integer stack
00080 
00081    A command must be all upper-case, and alone on a line.
00082 
00083    Foo.  */
00084 
00085 #include "ansidecl.h"
00086 #include <assert.h>
00087 #include <stdio.h>
00088 #include <ctype.h>
00089 #include <stdlib.h>
00090 #include <string.h>
00091 
00092 #define DEF_SIZE 5000
00093 #define STACK 50
00094 
00095 int internal_wanted;
00096 int internal_mode;
00097 
00098 int warning;
00099 
00100 /* Here is a string type ...  */
00101 
00102 typedef struct buffer
00103 {
00104   char *ptr;
00105   unsigned long write_idx;
00106   unsigned long size;
00107 } string_type;
00108 
00109 #ifdef __STDC__
00110 static void init_string_with_size (string_type *, unsigned int);
00111 static void init_string (string_type *);
00112 static int find (string_type *, char *);
00113 static void write_buffer (string_type *, FILE *);
00114 static void delete_string (string_type *);
00115 static char *addr (string_type *, unsigned int);
00116 static char at (string_type *, unsigned int);
00117 static void catchar (string_type *, int);
00118 static void overwrite_string (string_type *, string_type *);
00119 static void catbuf (string_type *, char *, unsigned int);
00120 static void cattext (string_type *, char *);
00121 static void catstr (string_type *, string_type *);
00122 #endif
00123 
00124 static void
00125 init_string_with_size (buffer, size)
00126      string_type *buffer;
00127      unsigned int size;
00128 {
00129   buffer->write_idx = 0;
00130   buffer->size = size;
00131   buffer->ptr = malloc (size);
00132 }
00133 
00134 static void
00135 init_string (buffer)
00136      string_type *buffer;
00137 {
00138   init_string_with_size (buffer, DEF_SIZE);
00139 }
00140 
00141 static int
00142 find (str, what)
00143      string_type *str;
00144      char *what;
00145 {
00146   unsigned int i;
00147   char *p;
00148   p = what;
00149   for (i = 0; i < str->write_idx && *p; i++)
00150     {
00151       if (*p == str->ptr[i])
00152        p++;
00153       else
00154        p = what;
00155     }
00156   return (*p == 0);
00157 }
00158 
00159 static void
00160 write_buffer (buffer, f)
00161      string_type *buffer;
00162      FILE *f;
00163 {
00164   fwrite (buffer->ptr, buffer->write_idx, 1, f);
00165 }
00166 
00167 static void
00168 delete_string (buffer)
00169      string_type *buffer;
00170 {
00171   free (buffer->ptr);
00172 }
00173 
00174 static char *
00175 addr (buffer, idx)
00176      string_type *buffer;
00177      unsigned int idx;
00178 {
00179   return buffer->ptr + idx;
00180 }
00181 
00182 static char
00183 at (buffer, pos)
00184      string_type *buffer;
00185      unsigned int pos;
00186 {
00187   if (pos >= buffer->write_idx)
00188     return 0;
00189   return buffer->ptr[pos];
00190 }
00191 
00192 static void
00193 catchar (buffer, ch)
00194      string_type *buffer;
00195      int ch;
00196 {
00197   if (buffer->write_idx == buffer->size)
00198     {
00199       buffer->size *= 2;
00200       buffer->ptr = realloc (buffer->ptr, buffer->size);
00201     }
00202 
00203   buffer->ptr[buffer->write_idx++] = ch;
00204 }
00205 
00206 static void
00207 overwrite_string (dst, src)
00208      string_type *dst;
00209      string_type *src;
00210 {
00211   free (dst->ptr);
00212   dst->size = src->size;
00213   dst->write_idx = src->write_idx;
00214   dst->ptr = src->ptr;
00215 }
00216 
00217 static void
00218 catbuf (buffer, buf, len)
00219      string_type *buffer;
00220      char *buf;
00221      unsigned int len;
00222 {
00223   if (buffer->write_idx + len >= buffer->size)
00224     {
00225       while (buffer->write_idx + len >= buffer->size)
00226        buffer->size *= 2;
00227       buffer->ptr = realloc (buffer->ptr, buffer->size);
00228     }
00229   memcpy (buffer->ptr + buffer->write_idx, buf, len);
00230   buffer->write_idx += len;
00231 }
00232 
00233 static void
00234 cattext (buffer, string)
00235      string_type *buffer;
00236      char *string;
00237 {
00238   catbuf (buffer, string, (unsigned int) strlen (string));
00239 }
00240 
00241 static void
00242 catstr (dst, src)
00243      string_type *dst;
00244      string_type *src;
00245 {
00246   catbuf (dst, src->ptr, src->write_idx);
00247 }
00248 
00249 static unsigned int
00250 skip_white_and_stars (src, idx)
00251      string_type *src;
00252      unsigned int idx;
00253 {
00254   char c;
00255   while ((c = at (src, idx)),
00256         isspace ((unsigned char) c)
00257         || (c == '*'
00258             /* Don't skip past end-of-comment or star as first
00259               character on its line.  */
00260             && at (src, idx +1) != '/'
00261             && at (src, idx -1) != '\n'))
00262     idx++;
00263   return idx;
00264 }
00265 
00266 /***********************************************************************/
00267 
00268 string_type stack[STACK];
00269 string_type *tos;
00270 
00271 unsigned int idx = 0; /* Pos in input buffer */
00272 string_type *ptr; /* and the buffer */
00273 typedef void (*stinst_type)();
00274 stinst_type *pc;
00275 stinst_type sstack[STACK];
00276 stinst_type *ssp = &sstack[0];
00277 long istack[STACK];
00278 long *isp = &istack[0];
00279 
00280 typedef int *word_type;
00281 
00282 struct dict_struct
00283 {
00284   char *word;
00285   struct dict_struct *next;
00286   stinst_type *code;
00287   int code_length;
00288   int code_end;
00289   int var;
00290 };
00291 
00292 typedef struct dict_struct dict_type;
00293 
00294 static void
00295 die (msg)
00296      char *msg;
00297 {
00298   fprintf (stderr, "%s\n", msg);
00299   exit (1);
00300 }
00301 
00302 static void
00303 check_range ()
00304 {
00305   if (tos < stack)
00306     die ("underflow in string stack");
00307   if (tos >= stack + STACK)
00308     die ("overflow in string stack");
00309 }
00310 
00311 static void
00312 icheck_range ()
00313 {
00314   if (isp < istack)
00315     die ("underflow in integer stack");
00316   if (isp >= istack + STACK)
00317     die ("overflow in integer stack");
00318 }
00319 
00320 #ifdef __STDC__
00321 static void exec (dict_type *);
00322 static void call (void);
00323 static void remchar (void), strip_trailing_newlines (void), push_number (void);
00324 static void push_text (void);
00325 static void remove_noncomments (string_type *, string_type *);
00326 static void print_stack_level (void);
00327 static void paramstuff (void), translatecomments (void);
00328 static void outputdots (void), courierize (void), bulletize (void);
00329 static void do_fancy_stuff (void);
00330 static int iscommand (string_type *, unsigned int);
00331 static int copy_past_newline (string_type *, unsigned int, string_type *);
00332 static void icopy_past_newline (void), kill_bogus_lines (void), indent (void);
00333 static void get_stuff_in_command (void), swap (void), other_dup (void);
00334 static void drop (void), idrop (void);
00335 static void icatstr (void), skip_past_newline (void), internalmode (void);
00336 static void maybecatstr (void);
00337 static char *nextword (char *, char **);
00338 dict_type *lookup_word (char *);
00339 static void perform (void);
00340 dict_type *newentry (char *);
00341 unsigned int add_to_definition (dict_type *, stinst_type);
00342 void add_intrinsic (char *, void (*)());
00343 void add_var (char *);
00344 void compile (char *);
00345 static void bang (void);
00346 static void atsign (void);
00347 static void hello (void);
00348 static void stdout_ (void);
00349 static void stderr_ (void);
00350 static void print (void);
00351 static void read_in (string_type *, FILE *);
00352 static void usage (void);
00353 static void chew_exit (void);
00354 #endif
00355 
00356 static void
00357 exec (word)
00358      dict_type *word;
00359 {
00360   pc = word->code;
00361   while (*pc)
00362     (*pc) ();
00363 }
00364 
00365 static void
00366 call ()
00367 {
00368   stinst_type *oldpc = pc;
00369   dict_type *e;
00370   e = (dict_type *) (pc[1]);
00371   exec (e);
00372   pc = oldpc + 2;
00373 }
00374 
00375 static void
00376 remchar ()
00377 {
00378   if (tos->write_idx)
00379     tos->write_idx--;
00380   pc++;
00381 }
00382 
00383 static void
00384 strip_trailing_newlines ()
00385 {
00386   while ((isspace ((unsigned char) at (tos, tos->write_idx - 1))
00387          || at (tos, tos->write_idx - 1) == '\n')
00388         && tos->write_idx > 0)
00389     tos->write_idx--;
00390   pc++;
00391 }
00392 
00393 static void
00394 push_number ()
00395 {
00396   isp++;
00397   icheck_range ();
00398   pc++;
00399   *isp = (long) (*pc);
00400   pc++;
00401 }
00402 
00403 static void
00404 push_text ()
00405 {
00406   tos++;
00407   check_range ();
00408   init_string (tos);
00409   pc++;
00410   cattext (tos, *((char **) pc));
00411   pc++;
00412 }
00413 
00414 /* This function removes everything not inside comments starting on
00415    the first char of the line from the  string, also when copying
00416    comments, removes blank space and leading *'s.
00417    Blank lines are turned into one blank line.  */
00418 
00419 static void
00420 remove_noncomments (src, dst)
00421      string_type *src;
00422      string_type *dst;
00423 {
00424   unsigned int idx = 0;
00425 
00426   while (at (src, idx))
00427     {
00428       /* Now see if we have a comment at the start of the line.  */
00429       if (at (src, idx) == '\n'
00430          && at (src, idx + 1) == '/'
00431          && at (src, idx + 2) == '*')
00432        {
00433          idx += 3;
00434 
00435          idx = skip_white_and_stars (src, idx);
00436 
00437          /* Remove leading dot */
00438          if (at (src, idx) == '.')
00439            idx++;
00440 
00441          /* Copy to the end of the line, or till the end of the
00442             comment.  */
00443          while (at (src, idx))
00444            {
00445              if (at (src, idx) == '\n')
00446               {
00447                 /* end of line, echo and scrape of leading blanks  */
00448                 if (at (src, idx + 1) == '\n')
00449                   catchar (dst, '\n');
00450                 catchar (dst, '\n');
00451                 idx++;
00452                 idx = skip_white_and_stars (src, idx);
00453               }
00454              else if (at (src, idx) == '*' && at (src, idx + 1) == '/')
00455               {
00456                 idx += 2;
00457                 cattext (dst, "\nENDDD\n");
00458                 break;
00459               }
00460              else
00461               {
00462                 catchar (dst, at (src, idx));
00463                 idx++;
00464               }
00465            }
00466        }
00467       else
00468        idx++;
00469     }
00470 }
00471 
00472 static void
00473 print_stack_level ()
00474 {
00475   fprintf (stderr, "current string stack depth = %d, ", tos - stack);
00476   fprintf (stderr, "current integer stack depth = %d\n", isp - istack);
00477   pc++;
00478 }
00479 
00480 /* turn:
00481      foobar name(stuff);
00482    into:
00483      foobar
00484      name PARAMS ((stuff));
00485    and a blank line.
00486  */
00487 
00488 static void
00489 paramstuff ()
00490 {
00491   unsigned int openp;
00492   unsigned int fname;
00493   unsigned int idx;
00494   unsigned int len;
00495   string_type out;
00496   init_string (&out);
00497 
00498 #define NO_PARAMS 1
00499 
00500   /* Make sure that it's not already param'd or proto'd.  */
00501   if (NO_PARAMS
00502       || find (tos, "PARAMS") || find (tos, "PROTO") || !find (tos, "("))
00503     {
00504       catstr (&out, tos);
00505     }
00506   else
00507     {
00508       /* Find the open paren.  */
00509       for (openp = 0; at (tos, openp) != '(' && at (tos, openp); openp++)
00510        ;
00511 
00512       fname = openp;
00513       /* Step back to the fname.  */
00514       fname--;
00515       while (fname && isspace ((unsigned char) at (tos, fname)))
00516        fname--;
00517       while (fname
00518             && !isspace ((unsigned char) at (tos,fname))
00519             && at (tos,fname) != '*')
00520        fname--;
00521 
00522       fname++;
00523 
00524       /* Output type, omitting trailing whitespace character(s), if
00525          any.  */
00526       for (len = fname; 0 < len; len--)
00527        {
00528          if (!isspace ((unsigned char) at (tos, len - 1)))
00529            break;
00530        }
00531       for (idx = 0; idx < len; idx++)
00532        catchar (&out, at (tos, idx));
00533 
00534       cattext (&out, "\n"); /* Insert a newline between type and fnname */
00535 
00536       /* Output function name, omitting trailing whitespace
00537          character(s), if any.  */
00538       for (len = openp; 0 < len; len--)
00539        {
00540          if (!isspace ((unsigned char) at (tos, len - 1)))
00541            break;
00542        }
00543       for (idx = fname; idx < len; idx++)
00544        catchar (&out, at (tos, idx));
00545 
00546       cattext (&out, " PARAMS (");
00547 
00548       for (idx = openp; at (tos, idx) && at (tos, idx) != ';'; idx++)
00549        catchar (&out, at (tos, idx));
00550 
00551       cattext (&out, ");\n\n");
00552     }
00553   overwrite_string (tos, &out);
00554   pc++;
00555 
00556 }
00557 
00558 /* turn {*
00559    and *} into comments */
00560 
00561 static void
00562 translatecomments ()
00563 {
00564   unsigned int idx = 0;
00565   string_type out;
00566   init_string (&out);
00567 
00568   while (at (tos, idx))
00569     {
00570       if (at (tos, idx) == '{' && at (tos, idx + 1) == '*')
00571        {
00572          cattext (&out, "/*");
00573          idx += 2;
00574        }
00575       else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}')
00576        {
00577          cattext (&out, "*/");
00578          idx += 2;
00579        }
00580       else
00581        {
00582          catchar (&out, at (tos, idx));
00583          idx++;
00584        }
00585     }
00586 
00587   overwrite_string (tos, &out);
00588 
00589   pc++;
00590 }
00591 
00592 /* Mod tos so that only lines with leading dots remain */
00593 static void
00594 outputdots ()
00595 {
00596   unsigned int idx = 0;
00597   string_type out;
00598   init_string (&out);
00599 
00600   while (at (tos, idx))
00601     {
00602       if (at (tos, idx) == '\n' && at (tos, idx + 1) == '.')
00603        {
00604          char c;
00605          idx += 2;
00606 
00607          while ((c = at (tos, idx)) && c != '\n')
00608            {
00609              if (c == '{' && at (tos, idx + 1) == '*')
00610               {
00611                 cattext (&out, "/*");
00612                 idx += 2;
00613               }
00614              else if (c == '*' && at (tos, idx + 1) == '}')
00615               {
00616                 cattext (&out, "*/");
00617                 idx += 2;
00618               }
00619              else
00620               {
00621                 catchar (&out, c);
00622                 idx++;
00623               }
00624            }
00625          catchar (&out, '\n');
00626        }
00627       else
00628        {
00629          idx++;
00630        }
00631     }
00632 
00633   overwrite_string (tos, &out);
00634   pc++;
00635 }
00636 
00637 /* Find lines starting with . and | and put example around them on tos */
00638 static void
00639 courierize ()
00640 {
00641   string_type out;
00642   unsigned int idx = 0;
00643   int command = 0;
00644 
00645   init_string (&out);
00646 
00647   while (at (tos, idx))
00648     {
00649       if (at (tos, idx) == '\n'
00650          && (at (tos, idx +1 ) == '.'
00651              || at (tos, idx + 1) == '|'))
00652        {
00653          cattext (&out, "\n@example\n");
00654          do
00655            {
00656              idx += 2;
00657 
00658              while (at (tos, idx) && at (tos, idx) != '\n')
00659               {
00660                 if (command > 1)
00661                   {
00662                     /* We are inside {} parameters of some command;
00663                       Just pass through until matching brace.  */
00664                     if (at (tos, idx) == '{')
00665                      ++command;
00666                     else if (at (tos, idx) == '}')
00667                      --command;
00668                   }
00669                 else if (command != 0)
00670                   {
00671                     if (at (tos, idx) == '{')
00672                      ++command;
00673                     else if (!islower ((unsigned char) at (tos, idx)))
00674                      --command;
00675                   }
00676                 else if (at (tos, idx) == '@'
00677                         && islower ((unsigned char) at (tos, idx + 1)))
00678                   {
00679                     ++command;
00680                   }
00681                 else if (at (tos, idx) == '{' && at (tos, idx + 1) == '*')
00682                   {
00683                     cattext (&out, "/*");
00684                     idx += 2;
00685                     continue;
00686                   }
00687                 else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}')
00688                   {
00689                     cattext (&out, "*/");
00690                     idx += 2;
00691                     continue;
00692                   }
00693                 else if (at (tos, idx) == '{'
00694                         || at (tos, idx) == '}')
00695                   {
00696                     catchar (&out, '@');
00697                   }
00698 
00699                 catchar (&out, at (tos, idx));
00700                 idx++;
00701               }
00702              catchar (&out, '\n');
00703            }
00704          while (at (tos, idx) == '\n'
00705                && ((at (tos, idx + 1) == '.')
00706                    || (at (tos, idx + 1) == '|')))
00707            ;
00708          cattext (&out, "@end example");
00709        }
00710       else
00711        {
00712          catchar (&out, at (tos, idx));
00713          idx++;
00714        }
00715     }
00716 
00717   overwrite_string (tos, &out);
00718   pc++;
00719 }
00720 
00721 /* Finds any lines starting with "o ", if there are any, then turns
00722    on @itemize @bullet, and @items each of them. Then ends with @end
00723    itemize, inplace at TOS*/
00724 
00725 static void
00726 bulletize ()
00727 {
00728   unsigned int idx = 0;
00729   int on = 0;
00730   string_type out;
00731   init_string (&out);
00732 
00733   while (at (tos, idx))
00734     {
00735       if (at (tos, idx) == '@'
00736          && at (tos, idx + 1) == '*')
00737        {
00738          cattext (&out, "*");
00739          idx += 2;
00740        }
00741       else if (at (tos, idx) == '\n'
00742               && at (tos, idx + 1) == 'o'
00743               && isspace ((unsigned char) at (tos, idx + 2)))
00744        {
00745          if (!on)
00746            {
00747              cattext (&out, "\n@itemize @bullet\n");
00748              on = 1;
00749 
00750            }
00751          cattext (&out, "\n@item\n");
00752          idx += 3;
00753        }
00754       else
00755        {
00756          catchar (&out, at (tos, idx));
00757          if (on && at (tos, idx) == '\n'
00758              && at (tos, idx + 1) == '\n'
00759              && at (tos, idx + 2) != 'o')
00760            {
00761              cattext (&out, "@end itemize");
00762              on = 0;
00763            }
00764          idx++;
00765 
00766        }
00767     }
00768   if (on)
00769     {
00770       cattext (&out, "@end itemize\n");
00771     }
00772 
00773   delete_string (tos);
00774   *tos = out;
00775   pc++;
00776 }
00777 
00778 /* Turn <<foo>> into @code{foo} in place at TOS*/
00779 
00780 static void
00781 do_fancy_stuff ()
00782 {
00783   unsigned int idx = 0;
00784   string_type out;
00785   init_string (&out);
00786   while (at (tos, idx))
00787     {
00788       if (at (tos, idx) == '<'
00789          && at (tos, idx + 1) == '<'
00790          && !isspace ((unsigned char) at (tos, idx + 2)))
00791        {
00792          /* This qualifies as a << startup.  */
00793          idx += 2;
00794          cattext (&out, "@code{");
00795          while (at (tos, idx)
00796                && at (tos, idx) != '>' )
00797            {
00798              catchar (&out, at (tos, idx));
00799              idx++;
00800 
00801            }
00802          cattext (&out, "}");
00803          idx += 2;
00804        }
00805       else
00806        {
00807          catchar (&out, at (tos, idx));
00808          idx++;
00809        }
00810     }
00811   delete_string (tos);
00812   *tos = out;
00813   pc++;
00814 
00815 }
00816 
00817 /* A command is all upper case,and alone on a line.  */
00818 
00819 static int
00820 iscommand (ptr, idx)
00821      string_type *ptr;
00822      unsigned int idx;
00823 {
00824   unsigned int len = 0;
00825   while (at (ptr, idx))
00826     {
00827       if (isupper ((unsigned char) at (ptr, idx))
00828          || at (ptr, idx) == ' ' || at (ptr, idx) == '_')
00829        {
00830          len++;
00831          idx++;
00832        }
00833       else if (at (ptr, idx) == '\n')
00834        {
00835          if (len > 3)
00836            return 1;
00837          return 0;
00838        }
00839       else
00840        return 0;
00841     }
00842   return 0;
00843 }
00844 
00845 static int
00846 copy_past_newline (ptr, idx, dst)
00847      string_type *ptr;
00848      unsigned int idx;
00849      string_type *dst;
00850 {
00851   int column = 0;
00852 
00853   while (at (ptr, idx) && at (ptr, idx) != '\n')
00854     {
00855       if (at (ptr, idx) == '\t')
00856        {
00857          /* Expand tabs.  Neither makeinfo nor TeX can cope well with
00858             them.  */
00859          do
00860            catchar (dst, ' ');
00861          while (++column & 7);
00862        }
00863       else
00864        {
00865          catchar (dst, at (ptr, idx));
00866          column++;
00867        }
00868       idx++;
00869 
00870     }
00871   catchar (dst, at (ptr, idx));
00872   idx++;
00873   return idx;
00874 
00875 }
00876 
00877 static void
00878 icopy_past_newline ()
00879 {
00880   tos++;
00881   check_range ();
00882   init_string (tos);
00883   idx = copy_past_newline (ptr, idx, tos);
00884   pc++;
00885 }
00886 
00887 /* indent
00888    Take the string at the top of the stack, do some prettying.  */
00889 
00890 static void
00891 kill_bogus_lines ()
00892 {
00893   int sl;
00894 
00895   int idx = 0;
00896   int c;
00897   int dot = 0;
00898 
00899   string_type out;
00900   init_string (&out);
00901   /* Drop leading nl.  */
00902   while (at (tos, idx) == '\n')
00903     {
00904       idx++;
00905     }
00906   c = idx;
00907 
00908   /* If the first char is a '.' prepend a newline so that it is
00909      recognized properly later.  */
00910   if (at (tos, idx) == '.')
00911     catchar (&out, '\n');
00912 
00913   /* Find the last char.  */
00914   while (at (tos, idx))
00915     {
00916       idx++;
00917     }
00918 
00919   /* Find the last non white before the nl.  */
00920   idx--;
00921 
00922   while (idx && isspace ((unsigned char) at (tos, idx)))
00923     idx--;
00924   idx++;
00925 
00926   /* Copy buffer upto last char, but blank lines before and after
00927      dots don't count.  */
00928   sl = 1;
00929 
00930   while (c < idx)
00931     {
00932       if (at (tos, c) == '\n'
00933          && at (tos, c + 1) == '\n'
00934          && at (tos, c + 2) == '.')
00935        {
00936          /* Ignore two newlines before a dot.  */
00937          c++;
00938        }
00939       else if (at (tos, c) == '.' && sl)
00940        {
00941          /* remember that this line started with a dot.  */
00942          dot = 2;
00943        }
00944       else if (at (tos, c) == '\n'
00945               && at (tos, c + 1) == '\n'
00946               && dot)
00947        {
00948          c++;
00949          /* Ignore two newlines when last line was dot.  */
00950        }
00951 
00952       catchar (&out, at (tos, c));
00953       if (at (tos, c) == '\n')
00954        {
00955          sl = 1;
00956 
00957          if (dot == 2)
00958            dot = 1;
00959          else
00960            dot = 0;
00961        }
00962       else
00963        sl = 0;
00964 
00965       c++;
00966 
00967     }
00968 
00969   /* Append nl.  */
00970   catchar (&out, '\n');
00971   pc++;
00972   delete_string (tos);
00973   *tos = out;
00974 
00975 }
00976 
00977 static void
00978 indent ()
00979 {
00980   string_type out;
00981   int tab = 0;
00982   int idx = 0;
00983   int ol = 0;
00984   init_string (&out);
00985   while (at (tos, idx))
00986     {
00987       switch (at (tos, idx))
00988        {
00989        case '\n':
00990          cattext (&out, "\n");
00991          idx++;
00992          if (tab && at (tos, idx))
00993            {
00994              cattext (&out, "    ");
00995            }
00996          ol = 0;
00997          break;
00998        case '(':
00999          tab++;
01000          if (ol == 0)
01001            cattext (&out, "   ");
01002          idx++;
01003          cattext (&out, "(");
01004          ol = 1;
01005          break;
01006        case ')':
01007          tab--;
01008          cattext (&out, ")");
01009          idx++;
01010          ol = 1;
01011 
01012          break;
01013        default:
01014          catchar (&out, at (tos, idx));
01015          ol = 1;
01016 
01017          idx++;
01018          break;
01019        }
01020     }
01021 
01022   pc++;
01023   delete_string (tos);
01024   *tos = out;
01025 
01026 }
01027 
01028 static void
01029 get_stuff_in_command ()
01030 {
01031   tos++;
01032   check_range ();
01033   init_string (tos);
01034 
01035   while (at (ptr, idx))
01036     {
01037       if (iscommand (ptr, idx))
01038        break;
01039       idx = copy_past_newline (ptr, idx, tos);
01040     }
01041   pc++;
01042 }
01043 
01044 static void
01045 swap ()
01046 {
01047   string_type t;
01048 
01049   t = tos[0];
01050   tos[0] = tos[-1];
01051   tos[-1] = t;
01052   pc++;
01053 }
01054 
01055 static void
01056 other_dup ()
01057 {
01058   tos++;
01059   check_range ();
01060   init_string (tos);
01061   catstr (tos, tos - 1);
01062   pc++;
01063 }
01064 
01065 static void
01066 drop ()
01067 {
01068   tos--;
01069   check_range ();
01070   pc++;
01071 }
01072 
01073 static void
01074 idrop ()
01075 {
01076   isp--;
01077   icheck_range ();
01078   pc++;
01079 }
01080 
01081 static void
01082 icatstr ()
01083 {
01084   tos--;
01085   check_range ();
01086   catstr (tos, tos + 1);
01087   delete_string (tos + 1);
01088   pc++;
01089 }
01090 
01091 static void
01092 skip_past_newline ()
01093 {
01094   while (at (ptr, idx)
01095         && at (ptr, idx) != '\n')
01096     idx++;
01097   idx++;
01098   pc++;
01099 }
01100 
01101 static void
01102 internalmode ()
01103 {
01104   internal_mode = *(isp);
01105   isp--;
01106   icheck_range ();
01107   pc++;
01108 }
01109 
01110 static void
01111 maybecatstr ()
01112 {
01113   if (internal_wanted == internal_mode)
01114     {
01115       catstr (tos - 1, tos);
01116     }
01117   delete_string (tos);
01118   tos--;
01119   check_range ();
01120   pc++;
01121 }
01122 
01123 char *
01124 nextword (string, word)
01125      char *string;
01126      char **word;
01127 {
01128   char *word_start;
01129   int idx;
01130   char *dst;
01131   char *src;
01132 
01133   int length = 0;
01134 
01135   while (isspace ((unsigned char) *string) || *string == '-')
01136     {
01137       if (*string == '-')
01138        {
01139          while (*string && *string != '\n')
01140            string++;
01141 
01142        }
01143       else
01144        {
01145          string++;
01146        }
01147     }
01148   if (!*string)
01149     return 0;
01150 
01151   word_start = string;
01152   if (*string == '"')
01153     {
01154       do
01155        {
01156          string++;
01157          length++;
01158          if (*string == '\\')
01159            {
01160              string += 2;
01161              length += 2;
01162            }
01163        }
01164       while (*string != '"');
01165     }
01166   else
01167     {
01168       while (!isspace ((unsigned char) *string))
01169        {
01170          string++;
01171          length++;
01172 
01173        }
01174     }
01175 
01176   *word = malloc (length + 1);
01177 
01178   dst = *word;
01179   src = word_start;
01180 
01181   for (idx = 0; idx < length; idx++)
01182     {
01183       if (src[idx] == '\\')
01184        switch (src[idx + 1])
01185          {
01186          case 'n':
01187            *dst++ = '\n';
01188            idx++;
01189            break;
01190          case '"':
01191          case '\\':
01192            *dst++ = src[idx + 1];
01193            idx++;
01194            break;
01195          default:
01196            *dst++ = '\\';
01197            break;
01198          }
01199       else
01200        *dst++ = src[idx];
01201     }
01202   *dst++ = 0;
01203 
01204   if (*string)
01205     return string + 1;
01206   else
01207     return 0;
01208 }
01209 
01210 dict_type *root;
01211 
01212 dict_type *
01213 lookup_word (word)
01214      char *word;
01215 {
01216   dict_type *ptr = root;
01217   while (ptr)
01218     {
01219       if (strcmp (ptr->word, word) == 0)
01220        return ptr;
01221       ptr = ptr->next;
01222     }
01223   if (warning)
01224     fprintf (stderr, "Can't find %s\n", word);
01225   return 0;
01226 }
01227 
01228 static void
01229 perform ()
01230 {
01231   tos = stack;
01232 
01233   while (at (ptr, idx))
01234     {
01235       /* It's worth looking through the command list.  */
01236       if (iscommand (ptr, idx))
01237        {
01238          char *next;
01239          dict_type *word;
01240 
01241          (void) nextword (addr (ptr, idx), &next);
01242 
01243          word = lookup_word (next);
01244 
01245          if (word)
01246            {
01247              exec (word);
01248            }
01249          else
01250            {
01251              if (warning)
01252               fprintf (stderr, "warning, %s is not recognised\n", next);
01253              skip_past_newline ();
01254            }
01255 
01256        }
01257       else
01258        skip_past_newline ();
01259     }
01260 }
01261 
01262 dict_type *
01263 newentry (word)
01264      char *word;
01265 {
01266   dict_type *new = (dict_type *) malloc (sizeof (dict_type));
01267   new->word = word;
01268   new->next = root;
01269   root = new;
01270   new->code = (stinst_type *) malloc (sizeof (stinst_type));
01271   new->code_length = 1;
01272   new->code_end = 0;
01273   return new;
01274 }
01275 
01276 unsigned int
01277 add_to_definition (entry, word)
01278      dict_type *entry;
01279      stinst_type word;
01280 {
01281   if (entry->code_end == entry->code_length)
01282     {
01283       entry->code_length += 2;
01284       entry->code =
01285        (stinst_type *) realloc ((char *) (entry->code),
01286                              entry->code_length * sizeof (word_type));
01287     }
01288   entry->code[entry->code_end] = word;
01289 
01290   return entry->code_end++;
01291 }
01292 
01293 void
01294 add_intrinsic (name, func)
01295      char *name;
01296      void (*func) ();
01297 {
01298   dict_type *new = newentry (name);
01299   add_to_definition (new, func);
01300   add_to_definition (new, 0);
01301 }
01302 
01303 void
01304 add_var (name)
01305      char *name;
01306 {
01307   dict_type *new = newentry (name);
01308   add_to_definition (new, push_number);
01309   add_to_definition (new, (stinst_type) (&(new->var)));
01310   add_to_definition (new, 0);
01311 }
01312 
01313 void
01314 compile (string)
01315      char *string;
01316 {
01317   /* Add words to the dictionary.  */
01318   char *word;
01319   string = nextword (string, &word);
01320   while (string && *string && word[0])
01321     {
01322       if (strcmp (word, "var") == 0)
01323        {
01324          string = nextword (string, &word);
01325 
01326          add_var (word);
01327          string = nextword (string, &word);
01328        }
01329       else if (word[0] == ':')
01330        {
01331          dict_type *ptr;
01332          /* Compile a word and add to dictionary.  */
01333          string = nextword (string, &word);
01334 
01335          ptr = newentry (word);
01336          string = nextword (string, &word);
01337          while (word[0] != ';')
01338            {
01339              switch (word[0])
01340               {
01341               case '"':
01342                 /* got a string, embed magic push string
01343                    function */
01344                 add_to_definition (ptr, push_text);
01345                 add_to_definition (ptr, (stinst_type) (word + 1));
01346                 break;
01347               case '0':
01348               case '1':
01349               case '2':
01350               case '3':
01351               case '4':
01352               case '5':
01353               case '6':
01354               case '7':
01355               case '8':
01356               case '9':
01357                 /* Got a number, embedd the magic push number
01358                    function */
01359                 add_to_definition (ptr, push_number);
01360                 add_to_definition (ptr, (stinst_type) atol (word));
01361                 break;
01362               default:
01363                 add_to_definition (ptr, call);
01364                 add_to_definition (ptr, (stinst_type) lookup_word (word));
01365               }
01366 
01367              string = nextword (string, &word);
01368            }
01369          add_to_definition (ptr, 0);
01370          string = nextword (string, &word);
01371        }
01372       else
01373        {
01374          fprintf (stderr, "syntax error at %s\n", string - 1);
01375        }
01376     }
01377 }
01378 
01379 static void
01380 bang ()
01381 {
01382   *(long *) ((isp[0])) = isp[-1];
01383   isp -= 2;
01384   icheck_range ();
01385   pc++;
01386 }
01387 
01388 static void
01389 atsign ()
01390 {
01391   isp[0] = *(long *) (isp[0]);
01392   pc++;
01393 }
01394 
01395 static void
01396 hello ()
01397 {
01398   printf ("hello\n");
01399   pc++;
01400 }
01401 
01402 static void
01403 stdout_ ()
01404 {
01405   isp++;
01406   icheck_range ();
01407   *isp = 1;
01408   pc++;
01409 }
01410 
01411 static void
01412 stderr_ ()
01413 {
01414   isp++;
01415   icheck_range ();
01416   *isp = 2;
01417   pc++;
01418 }
01419 
01420 static void
01421 print ()
01422 {
01423   if (*isp == 1)
01424     write_buffer (tos, stdout);
01425   else if (*isp == 2)
01426     write_buffer (tos, stderr);
01427   else
01428     fprintf (stderr, "print: illegal print destination `%ld'\n", *isp);
01429   isp--;
01430   tos--;
01431   icheck_range ();
01432   check_range ();
01433   pc++;
01434 }
01435 
01436 static void
01437 read_in (str, file)
01438      string_type *str;
01439      FILE *file;
01440 {
01441   char buff[10000];
01442   unsigned int r;
01443   do
01444     {
01445       r = fread (buff, 1, sizeof (buff), file);
01446       catbuf (str, buff, r);
01447     }
01448   while (r);
01449   buff[0] = 0;
01450 
01451   catbuf (str, buff, 1);
01452 }
01453 
01454 static void
01455 usage ()
01456 {
01457   fprintf (stderr, "usage: -[d|i|g] <file >file\n");
01458   exit (33);
01459 }
01460 
01461 /* There is no reliable way to declare exit.  Sometimes it returns
01462    int, and sometimes it returns void.  Sometimes it changes between
01463    OS releases.  Trying to get it declared correctly in the hosts file
01464    is a pointless waste of time.  */
01465 
01466 static void
01467 chew_exit ()
01468 {
01469   exit (0);
01470 }
01471 
01472 int
01473 main (ac, av)
01474      int ac;
01475      char *av[];
01476 {
01477   unsigned int i;
01478   string_type buffer;
01479   string_type pptr;
01480 
01481   init_string (&buffer);
01482   init_string (&pptr);
01483   init_string (stack + 0);
01484   tos = stack + 1;
01485   ptr = &pptr;
01486 
01487   add_intrinsic ("push_text", push_text);
01488   add_intrinsic ("!", bang);
01489   add_intrinsic ("@", atsign);
01490   add_intrinsic ("hello", hello);
01491   add_intrinsic ("stdout", stdout_);
01492   add_intrinsic ("stderr", stderr_);
01493   add_intrinsic ("print", print);
01494   add_intrinsic ("skip_past_newline", skip_past_newline);
01495   add_intrinsic ("catstr", icatstr);
01496   add_intrinsic ("copy_past_newline", icopy_past_newline);
01497   add_intrinsic ("dup", other_dup);
01498   add_intrinsic ("drop", drop);
01499   add_intrinsic ("idrop", idrop);
01500   add_intrinsic ("remchar", remchar);
01501   add_intrinsic ("get_stuff_in_command", get_stuff_in_command);
01502   add_intrinsic ("do_fancy_stuff", do_fancy_stuff);
01503   add_intrinsic ("bulletize", bulletize);
01504   add_intrinsic ("courierize", courierize);
01505   /* If the following line gives an error, exit() is not declared in the
01506      ../hosts/foo.h file for this host.  Fix it there, not here!  */
01507   /* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor.  */
01508   add_intrinsic ("exit", chew_exit);
01509   add_intrinsic ("swap", swap);
01510   add_intrinsic ("outputdots", outputdots);
01511   add_intrinsic ("paramstuff", paramstuff);
01512   add_intrinsic ("maybecatstr", maybecatstr);
01513   add_intrinsic ("translatecomments", translatecomments);
01514   add_intrinsic ("kill_bogus_lines", kill_bogus_lines);
01515   add_intrinsic ("indent", indent);
01516   add_intrinsic ("internalmode", internalmode);
01517   add_intrinsic ("print_stack_level", print_stack_level);
01518   add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines);
01519 
01520   /* Put a nl at the start.  */
01521   catchar (&buffer, '\n');
01522 
01523   read_in (&buffer, stdin);
01524   remove_noncomments (&buffer, ptr);
01525   for (i = 1; i < (unsigned int) ac; i++)
01526     {
01527       if (av[i][0] == '-')
01528        {
01529          if (av[i][1] == 'f')
01530            {
01531              string_type b;
01532              FILE *f;
01533              init_string (&b);
01534 
01535              f = fopen (av[i + 1], "r");
01536              if (!f)
01537               {
01538                 fprintf (stderr, "Can't open the input file %s\n",
01539                         av[i + 1]);
01540                 return 33;
01541               }
01542 
01543              read_in (&b, f);
01544              compile (b.ptr);
01545              perform ();
01546            }
01547          else if (av[i][1] == 'i')
01548            {
01549              internal_wanted = 1;
01550            }
01551          else if (av[i][1] == 'w')
01552            {
01553              warning = 1;
01554            }
01555          else
01556            usage ();
01557        }
01558     }
01559   write_buffer (stack + 0, stdout);
01560   if (tos != stack)
01561     {
01562       fprintf (stderr, "finishing with current stack level %d\n",
01563               tos - stack);
01564       return 1;
01565     }
01566   return 0;
01567 }