OSDN Git Service

* tradcpp.c: New file.
authorzack <zack@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 6 Jul 2000 22:59:34 +0000 (22:59 +0000)
committerzack <zack@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 6 Jul 2000 22:59:34 +0000 (22:59 +0000)
* tradcif.y: New file.
* tradcif.c: New generated file.

* Makefile.in: Add rules to build tradcpp.o, tradcif.o,
$(srcdir)/tradcif.c.  Add tradcpp to STAGESTUFF and
dependencies of C.  Install tradcpp from install-common, in
$(libsubdir).

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@34893 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/Makefile.in
gcc/tradcif.c [new file with mode: 0644]
gcc/tradcif.y [new file with mode: 0644]
gcc/tradcpp.c [new file with mode: 0644]

index d7e95ee..3f32a1d 100644 (file)
@@ -1,5 +1,16 @@
 2000-07-06  Zack Weinberg  <zack@wolery.cumb.org>
 
+       * tradcpp.c: New file.
+       * tradcif.y: New file.
+       * tradcif.c: New generated file.
+
+       * Makefile.in: Add rules to build tradcpp.o, tradcif.o,
+       $(srcdir)/tradcif.c.  Add tradcpp to STAGESTUFF and
+       dependencies of C.  Install tradcpp from install-common, in
+       $(libsubdir).
+
+2000-07-06  Zack Weinberg  <zack@wolery.cumb.org>
+
        * cppinit.c: Include cppdefault.h.  Refer to
        cpp_GCC_INCLUDE_DIR and cpp_GCC_INCLUDE_DIR_len, not directly
        to GCC_INCLUDE_DIR and its length.
index 3391132..4412afd 100644 (file)
@@ -716,7 +716,7 @@ STAGESTUFF = *$(objext) insn-flags.h insn-config.h insn-codes.h \
  xgcc$(exeext) xcpp$(exeext) cc1$(exeext) cpp$(exeext) $(EXTRA_PASSES) \
  $(EXTRA_PARTS) $(EXTRA_PROGRAMS) gcc-cross$(exeext) cc1obj$(exeext) \
  enquire$(exeext) protoize$(exeext) unprotoize$(exeext) \
- specs collect2$(exeext) $(USE_COLLECT2) underscore.c \
+ specs collect2$(exeext) $(USE_COLLECT2) underscore.c tradcpp$(exeext) \
  gcov$(exeext) *.[0-9][0-9].* *.[si] libcpp.a libgcc libgcc.mk \
  $(LANG_STAGESTUFF)
 
@@ -869,7 +869,7 @@ native: config.status auto-host.h cpp$(exeext) intl.all $(LANGUAGES) \
        $(EXTRA_PASSES) $(EXTRA_PROGRAMS) $(USE_COLLECT2)
 
 # Define the names for selecting languages in LANGUAGES.
-C c: cc1$(exeext)
+C c: cc1$(exeext) tradcpp$(exeext)
 PROTO: proto
 
 # Tell GNU make these are phony targets.
@@ -1814,6 +1814,18 @@ cppdefault.o: cppdefault.c $(CONFIG_H) system.h cppdefault.h Makefile
 
 mkdeps.o: mkdeps.c $(CONFIG_H) system.h mkdeps.h
 
+# The traditional mode preprocessor, a separate program for ease of
+# maintenance.  Some code is shared with the ISO-C cpp.
+tradcpp$(exeext): tradcpp.o tradcif.o cppdefault.o $(LIBDEPS)
+       $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o tradcpp$(exeext) \
+       tradcpp.o tradcif.o cppdefault.o version.o intl.o $(LIBS)
+
+tradcpp.o: tradcpp.c $(CONFIG_H) system.h version.h cppdefault.h
+tradcif.o: tradcif.c $(CONFIG_H) system.h
+
+$(srcdir)/tradcif.c: $(srcdir)/tradcif.y
+       cd $(srcdir); $(BISON) $(BISONFLAGS) -o tradcif.c tradcif.y
+
 # Note for the stamp targets, we run the program `true' instead of
 # having an empty command (nothing following the semicolon).
 
@@ -2339,6 +2351,8 @@ install-common: native installdirs $(EXTRA_PARTS) lang.install-common
        fi
        -rm -f $(libsubdir)/cpp$(exeext)
        $(INSTALL_PROGRAM) cpp$(exeext) $(libsubdir)/cpp$(exeext)
+       -rm -f $(libsubdir)/tradcpp$(exeext)
+       $(INSTALL_PROGRAM) tradcpp$(exeext) $(libsubdir)/tradcpp$(exeext)
 # Install gcov if it was compiled.
        -if [ -f gcov$(exeext) ]; \
        then \
diff --git a/gcc/tradcif.c b/gcc/tradcif.c
new file mode 100644 (file)
index 0000000..36d2fce
--- /dev/null
@@ -0,0 +1,1543 @@
+
+/*  A Bison parser, made from tradcif.y
+    by GNU Bison version 1.28  */
+
+#define YYBISON 1  /* Identify Bison output.  */
+
+#define        INT     257
+#define        CHAR    258
+#define        NAME    259
+#define        ERROR   260
+#define        OR      261
+#define        AND     262
+#define        EQUAL   263
+#define        NOTEQUAL        264
+#define        LEQ     265
+#define        GEQ     266
+#define        LSH     267
+#define        RSH     268
+#define        UNARY   269
+
+#line 26 "tradcif.y"
+
+#include "config.h"
+#include "system.h"
+#include <setjmp.h>
+
+  int yylex PARAMS ((void));
+  void yyerror PARAMS ((const char *msgid));
+  extern void error   PARAMS ((const char *msgid, ...));
+  extern void warning PARAMS ((const char *msgid, ...));
+  extern struct hashnode *lookup PARAMS ((const unsigned char *, int, int));
+
+  int parse_number PARAMS ((int));
+  int parse_escape PARAMS ((char **));
+  int parse_c_expression PARAMS ((char *));
+
+  int expression_value;
+  static jmp_buf parse_return_error;
+
+  /* some external tables of character types */
+  extern unsigned char is_idstart[], is_idchar[];
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+
+#line 52 "tradcif.y"
+typedef union {
+  struct constant {long value; int unsignedp;} integer;
+  int voidval;
+  char *sval;
+} YYSTYPE;
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define        YYFINAL         61
+#define        YYFLAG          -32768
+#define        YYNTBASE        33
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 269 ? yytranslate[x] : 36)
+
+static const char yytranslate[] = {     0,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,    29,     2,     2,     2,    27,    14,     2,    31,
+    32,    25,    23,     9,    24,     2,    26,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     8,     2,    17,
+     2,    18,     7,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,    13,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,    12,     2,    30,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     1,     3,     4,     5,     6,
+    10,    11,    15,    16,    19,    20,    21,    22,    28
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = {     0,
+     0,     2,     4,     8,    11,    14,    17,    20,    24,    28,
+    32,    36,    40,    44,    48,    52,    56,    60,    64,    68,
+    72,    76,    80,    84,    88,    92,    96,   102,   104,   106
+};
+
+static const short yyrhs[] = {    34,
+     0,    35,     0,    34,     9,    35,     0,    24,    35,     0,
+    29,    35,     0,    23,    35,     0,    30,    35,     0,    31,
+    34,    32,     0,    35,    25,    35,     0,    35,    26,    35,
+     0,    35,    27,    35,     0,    35,    23,    35,     0,    35,
+    24,    35,     0,    35,    21,    35,     0,    35,    22,    35,
+     0,    35,    15,    35,     0,    35,    16,    35,     0,    35,
+    19,    35,     0,    35,    20,    35,     0,    35,    17,    35,
+     0,    35,    18,    35,     0,    35,    14,    35,     0,    35,
+    13,    35,     0,    35,    12,    35,     0,    35,    11,    35,
+     0,    35,    10,    35,     0,    35,     7,    35,     8,    35,
+     0,     3,     0,     4,     0,     5,     0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+    81,    86,    87,    92,    95,    98,   100,   103,   108,   114,
+   125,   136,   139,   142,   148,   154,   157,   160,   167,   174,
+   181,   188,   191,   194,   197,   200,   203,   206,   208,   210
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = {   "$","error","$undefined.","INT","CHAR",
+"NAME","ERROR","'?'","':'","','","OR","AND","'|'","'^'","'&'","EQUAL","NOTEQUAL",
+"'<'","'>'","LEQ","GEQ","LSH","RSH","'+'","'-'","'*'","'/'","'%'","UNARY","'!'",
+"'~'","'('","')'","start","exp1","exp", NULL
+};
+#endif
+
+static const short yyr1[] = {     0,
+    33,    34,    34,    35,    35,    35,    35,    35,    35,    35,
+    35,    35,    35,    35,    35,    35,    35,    35,    35,    35,
+    35,    35,    35,    35,    35,    35,    35,    35,    35,    35
+};
+
+static const short yyr2[] = {     0,
+     1,     1,     3,     2,     2,     2,     2,     3,     3,     3,
+     3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
+     3,     3,     3,     3,     3,     3,     5,     1,     1,     1
+};
+
+static const short yydefact[] = {     0,
+    28,    29,    30,     0,     0,     0,     0,     0,     1,     2,
+     6,     4,     5,     7,     0,     0,     0,     0,     0,     0,
+     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     0,     0,     0,     0,     0,     8,     3,     0,    26,    25,
+    24,    23,    22,    16,    17,    20,    21,    18,    19,    14,
+    15,    12,    13,     9,    10,    11,     0,    27,     0,     0,
+     0
+};
+
+static const short yydefgoto[] = {    59,
+     9,    10
+};
+
+static const short yypact[] = {    31,
+-32768,-32768,-32768,    31,    31,    31,    31,    31,     1,    77,
+-32768,-32768,-32768,-32768,     0,    31,    31,    31,    31,    31,
+    31,    31,    31,    31,    31,    31,    31,    31,    31,    31,
+    31,    31,    31,    31,    31,-32768,    77,    56,    94,    25,
+   109,   123,   136,   147,   147,   154,   154,   154,   154,   -19,
+   -19,    32,    32,-32768,-32768,-32768,    31,    77,    11,    33,
+-32768
+};
+
+static const short yypgoto[] = {-32768,
+    48,    -4
+};
+
+
+#define        YYLAST          181
+
+
+static const short yytable[] = {    11,
+    12,    13,    14,    31,    32,    33,    34,    35,    16,    16,
+    60,    37,    38,    39,    40,    41,    42,    43,    44,    45,
+    46,    47,    48,    49,    50,    51,    52,    53,    54,    55,
+    56,    36,    61,     1,     2,     3,    20,    21,    22,    23,
+    24,    25,    26,    27,    28,    29,    30,    31,    32,    33,
+    34,    35,    58,     4,     5,    15,    33,    34,    35,     6,
+     7,     8,    17,    57,     0,    18,    19,    20,    21,    22,
+    23,    24,    25,    26,    27,    28,    29,    30,    31,    32,
+    33,    34,    35,    17,     0,     0,    18,    19,    20,    21,
+    22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
+    32,    33,    34,    35,    19,    20,    21,    22,    23,    24,
+    25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+    35,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+    30,    31,    32,    33,    34,    35,    22,    23,    24,    25,
+    26,    27,    28,    29,    30,    31,    32,    33,    34,    35,
+    23,    24,    25,    26,    27,    28,    29,    30,    31,    32,
+    33,    34,    35,    25,    26,    27,    28,    29,    30,    31,
+    32,    33,    34,    35,    29,    30,    31,    32,    33,    34,
+    35
+};
+
+static const short yycheck[] = {     4,
+     5,     6,     7,    23,    24,    25,    26,    27,     9,     9,
+     0,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+    25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+    35,    32,     0,     3,     4,     5,    12,    13,    14,    15,
+    16,    17,    18,    19,    20,    21,    22,    23,    24,    25,
+    26,    27,    57,    23,    24,     8,    25,    26,    27,    29,
+    30,    31,     7,     8,    -1,    10,    11,    12,    13,    14,
+    15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+    25,    26,    27,     7,    -1,    -1,    10,    11,    12,    13,
+    14,    15,    16,    17,    18,    19,    20,    21,    22,    23,
+    24,    25,    26,    27,    11,    12,    13,    14,    15,    16,
+    17,    18,    19,    20,    21,    22,    23,    24,    25,    26,
+    27,    13,    14,    15,    16,    17,    18,    19,    20,    21,
+    22,    23,    24,    25,    26,    27,    14,    15,    16,    17,
+    18,    19,    20,    21,    22,    23,    24,    25,    26,    27,
+    15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+    25,    26,    27,    17,    18,    19,    20,    21,    22,    23,
+    24,    25,    26,    27,    21,    22,    23,    24,    25,    26,
+    27
+};
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+#line 3 "/usr/share/misc/bison.simple"
+/* This file comes from bison-1.28.  */
+
+/* Skeleton output parser for bison,
+   Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* This is the parser code that is written into each bison parser
+  when the %semantic_parser declaration is not specified in the grammar.
+  It was written by Richard Stallman by simplifying the hairy parser
+  used when %semantic_parser is specified.  */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C.  */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C.  */
+/* This used to test MSDOS, but that is a bad idea
+   since that symbol is in the user namespace.  */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+        instead, just don't use alloca.  */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+   So I turned it off.   rms, 2 May 1997.  */
+/* #include <malloc.h>  */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up,
+                and on HPUX 10.  Eventually we can turn this on.  */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+   It is replaced by the list of actions, each action
+   as one case of the switch.  */
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                -2
+#define YYEOF          0
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT        goto yyabortlab
+#define YYERROR                goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+   This remains here temporarily to ease the
+   transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL         goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    { yychar = (token), yylval = (value);                      \
+      yychar1 = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK;                                              \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    { yyerror ("syntax error: cannot back up"); YYERROR; }     \
+while (0)
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+#ifndef YYPURE
+#define YYLEX          yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX          yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX          yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX          yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX          yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int    yychar;                 /*  the lookahead symbol                */
+YYSTYPE        yylval;                 /*  the semantic value of the           */
+                               /*  lookahead symbol                    */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc;                        /*  location data for the lookahead     */
+                               /*  symbol                              */
+#endif
+
+int yynerrs;                   /*  number of parse errors so far       */
+#endif  /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug;                   /*  nonzero means print parse trace     */
+/* Since this is uninitialized, it does not stop multiple parsers
+   from coexisting.  */
+#endif
+
+/*  YYINITDEPTH indicates the initial size of the parser's stacks      */
+
+#ifndef        YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/*  YYMAXDEPTH is the maximum size the stacks can grow to
+    (effective only if the built-in stack extension method is used).  */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+\f
+/* Define __yy_memcpy.  Note that the size argument
+   should be passed with type unsigned int, because that is what the non-GCC
+   definitions require.  With GCC, __builtin_memcpy takes an arg
+   of type size_t, but it can handle unsigned int.  */
+
+#if __GNUC__ > 1               /* GNU C and GNU C++ define this.  */
+#define __yy_memcpy(TO,FROM,COUNT)     __builtin_memcpy(TO,FROM,COUNT)
+#else                          /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (to, from, count)
+     char *to;
+     char *from;
+     unsigned int count;
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+  register char *t = to;
+  register char *f = from;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#endif
+#endif
+\f
+#line 217 "/usr/share/misc/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+     YYPARSE_PARAM_DECL
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register YYSTYPE *yyvsp;
+  int yyerrstatus;     /*  number of tokens to shift before error messages enabled */
+  int yychar1 = 0;             /*  lookahead token as an internal (translated) token number */
+
+  short        yyssa[YYINITDEPTH];     /*  the state stack                     */
+  YYSTYPE yyvsa[YYINITDEPTH];  /*  the semantic value stack            */
+
+  short *yyss = yyssa;         /*  refer to the stacks thru separate pointers */
+  YYSTYPE *yyvs = yyvsa;       /*  to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylsa[YYINITDEPTH];  /*  the location stack                  */
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+
+#define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  int yystacksize = YYINITDEPTH;
+  int yyfree_stacks = 0;
+
+#ifdef YYPURE
+  int yychar;
+  YYSTYPE yylval;
+  int yynerrs;
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylloc;
+#endif
+#endif
+
+  YYSTYPE yyval;               /*  the variable used to return         */
+                               /*  semantic values from the action     */
+                               /*  routines                            */
+
+  int yylen;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Starting parse\n");
+#endif
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss - 1;
+  yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in  yystate  .  */
+/* In all cases, when you get here, the value and location stacks
+   have just been pushed. so pushing a state here evens the stacks.  */
+yynewstate:
+
+  *++yyssp = yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Give user a chance to reallocate the stack */
+      /* Use copies of these so that the &'s don't force the real ones into memory. */
+      YYSTYPE *yyvs1 = yyvs;
+      short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+      YYLTYPE *yyls1 = yyls;
+#endif
+
+      /* Get the current used size of the three stacks, in elements.  */
+      int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      /* Each stack pointer address is followed by the size of
+        the data in use in that stack, in bytes.  */
+#ifdef YYLSP_NEEDED
+      /* This used to be a conditional around just the two extra args,
+        but that might be undefined if yyoverflow is a macro.  */
+      yyoverflow("parser stack overflow",
+                &yyss1, size * sizeof (*yyssp),
+                &yyvs1, size * sizeof (*yyvsp),
+                &yyls1, size * sizeof (*yylsp),
+                &yystacksize);
+#else
+      yyoverflow("parser stack overflow",
+                &yyss1, size * sizeof (*yyssp),
+                &yyvs1, size * sizeof (*yyvsp),
+                &yystacksize);
+#endif
+
+      yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+      yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+       {
+         yyerror("parser stack overflow");
+         if (yyfree_stacks)
+           {
+             free (yyss);
+             free (yyvs);
+#ifdef YYLSP_NEEDED
+             free (yyls);
+#endif
+           }
+         return 2;
+       }
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+       yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+      yyfree_stacks = 1;
+#endif
+      yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+      __yy_memcpy ((char *)yyss, (char *)yyss1,
+                  size * (unsigned int) sizeof (*yyssp));
+      yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+      __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+                  size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+      yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+      __yy_memcpy ((char *)yyls, (char *)yyls1,
+                  size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + size - 1;
+      yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+      yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+      if (yyssp >= yyss + yystacksize - 1)
+       YYABORT;
+    }
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+  goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Reading a token: ");
+#endif
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)             /* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;          /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Now at end of input.\n");
+#endif
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+      if (yydebug)
+       {
+         fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+         /* Give the individual parser a way to print the precise meaning
+            of a token, for further debugging info.  */
+#ifdef YYPRINT
+         YYPRINT (stderr, yychar, yylval);
+#endif
+         fprintf (stderr, ")\n");
+       }
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+       goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* count tokens shifted since error; after three, turn off error status.  */
+  if (yyerrstatus) yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+/* Do the default action for the current state.  */
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+/* Do a reduction.  yyn is the number of a rule to reduce with.  */
+yyreduce:
+  yylen = yyr2[yyn];
+  if (yylen > 0)
+    yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      int i;
+
+      fprintf (stderr, "Reducing via rule %d (line %d), ",
+              yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+       fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+      fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+
+  switch (yyn) {
+
+case 1:
+#line 82 "tradcif.y"
+{ expression_value = yyvsp[0].integer.value; ;
+    break;}
+case 3:
+#line 88 "tradcif.y"
+{ yyval.integer = yyvsp[0].integer; ;
+    break;}
+case 4:
+#line 93 "tradcif.y"
+{ yyval.integer.value = - yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[0].integer.unsignedp; ;
+    break;}
+case 5:
+#line 96 "tradcif.y"
+{ yyval.integer.value = ! yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = 0; ;
+    break;}
+case 6:
+#line 99 "tradcif.y"
+{ yyval.integer = yyvsp[0].integer; ;
+    break;}
+case 7:
+#line 101 "tradcif.y"
+{ yyval.integer.value = ~ yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[0].integer.unsignedp; ;
+    break;}
+case 8:
+#line 104 "tradcif.y"
+{ yyval.integer = yyvsp[-1].integer; ;
+    break;}
+case 9:
+#line 109 "tradcif.y"
+{ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+                         if (yyval.integer.unsignedp)
+                           yyval.integer.value = (unsigned) yyvsp[-2].integer.value * yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value * yyvsp[0].integer.value; ;
+    break;}
+case 10:
+#line 115 "tradcif.y"
+{ if (yyvsp[0].integer.value == 0)
+                           {
+                             error ("division by zero in #if");
+                             yyvsp[0].integer.value = 1;
+                           }
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+                         if (yyval.integer.unsignedp)
+                           yyval.integer.value = (unsigned) yyvsp[-2].integer.value / yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value / yyvsp[0].integer.value; ;
+    break;}
+case 11:
+#line 126 "tradcif.y"
+{ if (yyvsp[0].integer.value == 0)
+                           {
+                             error ("division by zero in #if");
+                             yyvsp[0].integer.value = 1;
+                           }
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+                         if (yyval.integer.unsignedp)
+                           yyval.integer.value = (unsigned) yyvsp[-2].integer.value % yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value % yyvsp[0].integer.value; ;
+    break;}
+case 12:
+#line 137 "tradcif.y"
+{ yyval.integer.value = yyvsp[-2].integer.value + yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+    break;}
+case 13:
+#line 140 "tradcif.y"
+{ yyval.integer.value = yyvsp[-2].integer.value - yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+    break;}
+case 14:
+#line 143 "tradcif.y"
+{ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp;
+                         if (yyval.integer.unsignedp)
+                           yyval.integer.value = (unsigned) yyvsp[-2].integer.value << yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value << yyvsp[0].integer.value; ;
+    break;}
+case 15:
+#line 149 "tradcif.y"
+{ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp;
+                         if (yyval.integer.unsignedp)
+                           yyval.integer.value = (unsigned) yyvsp[-2].integer.value >> yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value >> yyvsp[0].integer.value; ;
+    break;}
+case 16:
+#line 155 "tradcif.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value == yyvsp[0].integer.value);
+                         yyval.integer.unsignedp = 0; ;
+    break;}
+case 17:
+#line 158 "tradcif.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value != yyvsp[0].integer.value);
+                         yyval.integer.unsignedp = 0; ;
+    break;}
+case 18:
+#line 161 "tradcif.y"
+{ yyval.integer.unsignedp = 0;
+                         if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+                           yyval.integer.value =
+                             (unsigned) yyvsp[-2].integer.value <= (unsigned) yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value <= yyvsp[0].integer.value; ;
+    break;}
+case 19:
+#line 168 "tradcif.y"
+{ yyval.integer.unsignedp = 0;
+                         if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+                           yyval.integer.value =
+                             (unsigned) yyvsp[-2].integer.value >= (unsigned) yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value >= yyvsp[0].integer.value; ;
+    break;}
+case 20:
+#line 175 "tradcif.y"
+{ yyval.integer.unsignedp = 0;
+                         if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+                           yyval.integer.value =
+                             (unsigned) yyvsp[-2].integer.value < (unsigned) yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value < yyvsp[0].integer.value; ;
+    break;}
+case 21:
+#line 182 "tradcif.y"
+{ yyval.integer.unsignedp = 0;
+                         if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+                           yyval.integer.value =
+                             (unsigned) yyvsp[-2].integer.value > (unsigned) yyvsp[0].integer.value;
+                         else
+                           yyval.integer.value = yyvsp[-2].integer.value > yyvsp[0].integer.value; ;
+    break;}
+case 22:
+#line 189 "tradcif.y"
+{ yyval.integer.value = yyvsp[-2].integer.value & yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+    break;}
+case 23:
+#line 192 "tradcif.y"
+{ yyval.integer.value = yyvsp[-2].integer.value ^ yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+    break;}
+case 24:
+#line 195 "tradcif.y"
+{ yyval.integer.value = yyvsp[-2].integer.value | yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+    break;}
+case 25:
+#line 198 "tradcif.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value && yyvsp[0].integer.value);
+                         yyval.integer.unsignedp = 0; ;
+    break;}
+case 26:
+#line 201 "tradcif.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value || yyvsp[0].integer.value);
+                         yyval.integer.unsignedp = 0; ;
+    break;}
+case 27:
+#line 204 "tradcif.y"
+{ yyval.integer.value = yyvsp[-4].integer.value ? yyvsp[-2].integer.value : yyvsp[0].integer.value;
+                         yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+    break;}
+case 28:
+#line 207 "tradcif.y"
+{ yyval.integer = yylval.integer; ;
+    break;}
+case 29:
+#line 209 "tradcif.y"
+{ yyval.integer = yylval.integer; ;
+    break;}
+case 30:
+#line 211 "tradcif.y"
+{ yyval.integer.value = 0;
+                         yyval.integer.unsignedp = 0; ;
+    break;}
+}
+   /* the action file gets copied in in place of this dollarsign */
+#line 543 "/usr/share/misc/bison.simple"
+\f
+  yyvsp -= yylen;
+  yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "state stack now");
+      while (ssp1 != yyssp)
+       fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+  yylsp++;
+  if (yylen == 0)
+    {
+      yylsp->first_line = yylloc.first_line;
+      yylsp->first_column = yylloc.first_column;
+      yylsp->last_line = (yylsp-1)->last_line;
+      yylsp->last_column = (yylsp-1)->last_column;
+      yylsp->text = 0;
+    }
+  else
+    {
+      yylsp->last_line = (yylsp+yylen-1)->last_line;
+      yylsp->last_column = (yylsp+yylen-1)->last_column;
+    }
+#endif
+
+  /* Now "shift" the result of the reduction.
+     Determine what state that goes to,
+     based on the state we popped back to
+     and the rule number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yyerrlab:   /* here on detecting error */
+
+  if (! yyerrstatus)
+    /* If not already recovering from an error, report this error.  */
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+       {
+         int size = 0;
+         char *msg;
+         int x, count;
+
+         count = 0;
+         /* Start X at -yyn if nec to avoid negative indexes in yycheck.  */
+         for (x = (yyn < 0 ? -yyn : 0);
+              x < (sizeof(yytname) / sizeof(char *)); x++)
+           if (yycheck[x + yyn] == x)
+             size += strlen(yytname[x]) + 15, count++;
+         msg = (char *) malloc(size + 15);
+         if (msg != 0)
+           {
+             strcpy(msg, "parse error");
+
+             if (count < 5)
+               {
+                 count = 0;
+                 for (x = (yyn < 0 ? -yyn : 0);
+                      x < (sizeof(yytname) / sizeof(char *)); x++)
+                   if (yycheck[x + yyn] == x)
+                     {
+                       strcat(msg, count == 0 ? ", expecting `" : " or `");
+                       strcat(msg, yytname[x]);
+                       strcat(msg, "'");
+                       count++;
+                     }
+               }
+             yyerror(msg);
+             free(msg);
+           }
+         else
+           yyerror ("parse error; also virtual memory exceeded");
+       }
+      else
+#endif /* YYERROR_VERBOSE */
+       yyerror("parse error");
+    }
+
+  goto yyerrlab1;
+yyerrlab1:   /* here on error raised explicitly by an action */
+
+  if (yyerrstatus == 3)
+    {
+      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+       YYABORT;
+
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token
+     after shifting the error token.  */
+
+  yyerrstatus = 3;             /* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+yyerrdefault:  /* current state does not do anything special for the error token. */
+
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
+  if (yyn) goto yydefault;
+#endif
+
+yyerrpop:   /* pop the current state because it cannot handle the error token */
+
+  if (yyssp == yyss) YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "Error: state stack now");
+      while (ssp1 != yyssp)
+       fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+yyerrhandle:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+       goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting error token, ");
+#endif
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+
+ yyacceptlab:
+  /* YYACCEPT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 0;
+
+ yyabortlab:
+  /* YYABORT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 1;
+}
+#line 214 "tradcif.y"
+
+\f
+/* During parsing of a C expression, the pointer to the next character
+   is in this variable.  */
+
+static char *lexptr;
+
+/* Take care of parsing a number (anything that starts with a digit).
+   Set yylval and return the token type; update lexptr.
+   LEN is the number of characters in it.  */
+
+/* maybe needs to actually deal with floating point numbers */
+
+int
+parse_number (olen)
+     int olen;
+{
+  register char *p = lexptr;
+  register long n = 0;
+  register int c;
+  register int base = 10;
+  register int len = olen;
+
+  for (c = 0; c < len; c++)
+    if (p[c] == '.') {
+      /* It's a float since it contains a point.  */
+      yyerror ("floating point numbers not allowed in #if expressions");
+      return ERROR;
+    }
+
+  yylval.integer.unsignedp = 0;
+
+  if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
+    p += 2;
+    base = 16;
+    len -= 2;
+  }
+  else if (*p == '0')
+    base = 8;
+
+  while (len > 0) {
+    c = *p++;
+    len--;
+    if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
+
+    if (c >= '0' && c <= '9') {
+      n *= base;
+      n += c - '0';
+    } else if (base == 16 && c >= 'a' && c <= 'f') {
+      n *= base;
+      n += c - 'a' + 10;
+    } else {
+      /* `l' means long, and `u' means unsigned.  */
+      while (1) {
+       if (c == 'l' || c == 'L')
+         ;
+       else if (c == 'u' || c == 'U')
+         yylval.integer.unsignedp = 1;
+       else
+         break;
+
+       if (len == 0)
+         break;
+       c = *p++;
+       len--;
+      }
+      /* Don't look for any more digits after the suffixes.  */
+      break;
+    }
+  }
+
+  if (len != 0) {
+    yyerror ("Invalid number in #if expression");
+    return ERROR;
+  }
+
+  /* If too big to be signed, consider it unsigned.  */
+  if (n < 0)
+    yylval.integer.unsignedp = 1;
+
+  lexptr = p;
+  yylval.integer.value = n;
+  return INT;
+}
+
+struct token {
+  const char *operator;
+  int token;
+};
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+static struct token tokentab2[] = {
+  {"&&", AND},
+  {"||", OR},
+  {"<<", LSH},
+  {">>", RSH},
+  {"==", EQUAL},
+  {"!=", NOTEQUAL},
+  {"<=", LEQ},
+  {">=", GEQ},
+  {NULL, ERROR}
+};
+
+/* Read one token, getting characters through lexptr.  */
+
+int
+yylex ()
+{
+  register int c;
+  register int namelen;
+  register char *tokstart;
+  register struct token *toktab;
+
+ retry:
+
+  tokstart = lexptr;
+  c = *tokstart;
+  /* See if it is a special token of length 2.  */
+  for (toktab = tokentab2; toktab->operator != NULL; toktab++)
+    if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) {
+      lexptr += 2;
+      return toktab->token;
+    }
+
+  switch (c) {
+  case 0:
+    return 0;
+    
+  case ' ':
+  case '\t':
+  case '\r':
+  case '\n':
+    lexptr++;
+    goto retry;
+    
+  case '\'':
+    lexptr++;
+    c = *lexptr++;
+    if (c == '\\')
+      c = parse_escape (&lexptr);
+
+    /* Sign-extend the constant if chars are signed on target machine.  */
+    {
+      if (lookup ((const unsigned char *)"__CHAR_UNSIGNED__",
+                  sizeof ("__CHAR_UNSIGNED__")-1, -1)
+         || ((c >> (CHAR_TYPE_SIZE - 1)) & 1) == 0)
+       yylval.integer.value = c & ((1 << CHAR_TYPE_SIZE) - 1);
+      else
+       yylval.integer.value = c | ~((1 << CHAR_TYPE_SIZE) - 1);
+    }
+
+    yylval.integer.unsignedp = 0;
+    c = *lexptr++;
+    if (c != '\'') {
+      yyerror ("Invalid character constant in #if");
+      return ERROR;
+    }
+    
+    return CHAR;
+
+    /* some of these chars are invalid in constant expressions;
+       maybe do something about them later */
+  case '/':
+  case '+':
+  case '-':
+  case '*':
+  case '%':
+  case '|':
+  case '&':
+  case '^':
+  case '~':
+  case '!':
+  case '@':
+  case '<':
+  case '>':
+  case '(':
+  case ')':
+  case '[':
+  case ']':
+  case '.':
+  case '?':
+  case ':':
+  case '=':
+  case '{':
+  case '}':
+  case ',':
+    lexptr++;
+    return c;
+    
+  case '"':
+    yyerror ("double quoted strings not allowed in #if expressions");
+    return ERROR;
+  }
+  if (c >= '0' && c <= '9') {
+    /* It's a number */
+    for (namelen = 0;
+        c = tokstart[namelen], is_idchar[c] || c == '.'; 
+        namelen++)
+      ;
+    return parse_number (namelen);
+  }
+  
+  if (!is_idstart[c]) {
+    yyerror ("Invalid token in expression");
+    return ERROR;
+  }
+  
+  /* It is a name.  See how long it is.  */
+  
+  for (namelen = 0;
+       is_idchar[(int)(unsigned char)tokstart[namelen]];
+       namelen++)
+    ;
+  
+  lexptr += namelen;
+  return NAME;
+}
+
+
+/* Parse a C escape sequence.  STRING_PTR points to a variable
+   containing a pointer to the string to parse.  That pointer
+   is updated past the characters we use.  The value of the
+   escape sequence is returned.
+
+   A negative value means the sequence \ newline was seen,
+   which is supposed to be equivalent to nothing at all.
+
+   If \ is followed by a null character, we return a negative
+   value and leave the string pointer pointing at the null character.
+
+   If \ is followed by 000, we return 0 and leave the string pointer
+   after the zeros.  A value of 0 does not mean end of string.  */
+
+int
+parse_escape (string_ptr)
+     char **string_ptr;
+{
+  register int c = *(*string_ptr)++;
+  switch (c)
+    {
+    case 'a':
+      return TARGET_BELL;
+    case 'b':
+      return TARGET_BS;
+    case 'e':
+      return 033;
+    case 'f':
+      return TARGET_FF;
+    case 'n':
+      return TARGET_NEWLINE;
+    case 'r':
+      return TARGET_CR;
+    case 't':
+      return TARGET_TAB;
+    case 'v':
+      return TARGET_VT;
+    case '\n':
+      return -2;
+    case 0:
+      (*string_ptr)--;
+      return 0;
+    case '^':
+      c = *(*string_ptr)++;
+      if (c == '\\')
+       c = parse_escape (string_ptr);
+      if (c == '?')
+       return 0177;
+      return (c & 0200) | (c & 037);
+      
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+      {
+       register int i = c - '0';
+       register int count = 0;
+       while (++count < 3)
+         {
+           c = *(*string_ptr)++;
+           if (c >= '0' && c <= '7')
+             i = (i << 3) + c - '0';
+           else
+             {
+               (*string_ptr)--;
+               break;
+             }
+         }
+       if ((i & ~((1 << CHAR_TYPE_SIZE) - 1)) != 0)
+         {
+           i &= (1 << CHAR_TYPE_SIZE) - 1;
+           warning ("octal character constant does not fit in a byte");
+         }
+       return i;
+      }
+    case 'x':
+      {
+       register int i = 0;
+       for (;;)
+         {
+           c = *(*string_ptr)++;
+           if (c >= '0' && c <= '9')
+             i = (i << 4) + c - '0';
+           else if (c >= 'a' && c <= 'f')
+             i = (i << 4) + c - 'a' + 10;
+           else if (c >= 'A' && c <= 'F')
+             i = (i << 4) + c - 'A' + 10;
+           else
+             {
+               (*string_ptr)--;
+               break;
+             }
+         }
+       if ((i & ~((1 << BITS_PER_UNIT) - 1)) != 0)
+         {
+           i &= (1 << BITS_PER_UNIT) - 1;
+           warning ("hex character constant does not fit in a byte");
+         }
+       return i;
+      }
+    default:
+      return c;
+    }
+}
+
+void
+yyerror (s)
+     const char *s;
+{
+  error (s);
+  longjmp (parse_return_error, 1);
+}
+\f
+/* This page contains the entry point to this file.  */
+
+/* Parse STRING as an expression, and complain if this fails
+   to use up all of the contents of STRING.  */
+/* We do not support C comments.  They should be removed before
+   this function is called.  */
+
+int
+parse_c_expression (string)
+     char *string;
+{
+  lexptr = string;
+  
+  if (lexptr == 0 || *lexptr == 0) {
+    error ("empty #if expression");
+    return 0;                  /* don't include the #if group */
+  }
+
+  /* if there is some sort of scanning error, just return 0 and assume
+     the parsing routine has printed an error message somewhere.
+     there is surely a better thing to do than this.     */
+  if (setjmp (parse_return_error))
+    return 0;
+
+  if (yyparse ())
+    return 0;                  /* actually this is never reached
+                                  the way things stand. */
+  if (*lexptr)
+    error ("Junk after end of expression.");
+
+  return expression_value;     /* set by yyparse () */
+}
diff --git a/gcc/tradcif.y b/gcc/tradcif.y
new file mode 100644 (file)
index 0000000..4a70bed
--- /dev/null
@@ -0,0 +1,584 @@
+/* Parse C expressions for CCCP.
+   Copyright (C) 1987 Free Software Foundation.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 1, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ In other words, you are welcome to use, share and improve this program.
+ You are forbidden to forbid anyone else to use, share and improve
+ what you give them.   Help stamp out software-hoarding!
+
+ Adapted from expread.y of GDB by Paul Rubin, July 1986.
+
+/* Parse a C expression from text in a string  */
+   
+%{
+#include "config.h"
+#include "system.h"
+#include <setjmp.h>
+
+  int yylex PARAMS ((void));
+  void yyerror PARAMS ((const char *msgid));
+  extern void error   PARAMS ((const char *msgid, ...));
+  extern void warning PARAMS ((const char *msgid, ...));
+  extern struct hashnode *lookup PARAMS ((const unsigned char *, int, int));
+
+  int parse_number PARAMS ((int));
+  int parse_escape PARAMS ((char **));
+  int parse_c_expression PARAMS ((char *));
+
+  int expression_value;
+  static jmp_buf parse_return_error;
+
+  /* some external tables of character types */
+  extern unsigned char is_idstart[], is_idchar[];
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+%}
+
+%union {
+  struct constant {long value; int unsignedp;} integer;
+  int voidval;
+  char *sval;
+}
+
+%type <integer> exp exp1 start
+%token <integer> INT CHAR
+%token <sval> NAME
+%token <integer> ERROR
+
+%right '?' ':'
+%left ','
+%left OR
+%left AND
+%left '|'
+%left '^'
+%left '&'
+%left EQUAL NOTEQUAL
+%left '<' '>' LEQ GEQ
+%left LSH RSH
+%left '+' '-'
+%left '*' '/' '%'
+%right UNARY
+
+/* %expect 40 */
+\f
+%%
+
+start   :      exp1
+               { expression_value = $1.value; }
+       ;
+
+/* Expressions, including the comma operator.  */
+exp1   :       exp
+       |       exp1 ',' exp
+                       { $$ = $3; }
+       ;
+
+/* Expressions, not including the comma operator.  */
+exp    :       '-' exp    %prec UNARY
+                       { $$.value = - $2.value;
+                         $$.unsignedp = $2.unsignedp; }
+       |       '!' exp    %prec UNARY
+                       { $$.value = ! $2.value;
+                         $$.unsignedp = 0; }
+       |       '+' exp    %prec UNARY
+                       { $$ = $2; }
+       |       '~' exp    %prec UNARY
+                       { $$.value = ~ $2.value;
+                         $$.unsignedp = $2.unsignedp; }
+       |       '(' exp1 ')'
+                       { $$ = $2; }
+       ;
+
+/* Binary operators in order of decreasing precedence.  */
+exp    :       exp '*' exp
+                       { $$.unsignedp = $1.unsignedp || $3.unsignedp;
+                         if ($$.unsignedp)
+                           $$.value = (unsigned) $1.value * $3.value;
+                         else
+                           $$.value = $1.value * $3.value; }
+       |       exp '/' exp
+                       { if ($3.value == 0)
+                           {
+                             error ("division by zero in #if");
+                             $3.value = 1;
+                           }
+                         $$.unsignedp = $1.unsignedp || $3.unsignedp;
+                         if ($$.unsignedp)
+                           $$.value = (unsigned) $1.value / $3.value;
+                         else
+                           $$.value = $1.value / $3.value; }
+       |       exp '%' exp
+                       { if ($3.value == 0)
+                           {
+                             error ("division by zero in #if");
+                             $3.value = 1;
+                           }
+                         $$.unsignedp = $1.unsignedp || $3.unsignedp;
+                         if ($$.unsignedp)
+                           $$.value = (unsigned) $1.value % $3.value;
+                         else
+                           $$.value = $1.value % $3.value; }
+       |       exp '+' exp
+                       { $$.value = $1.value + $3.value;
+                         $$.unsignedp = $1.unsignedp || $3.unsignedp; }
+       |       exp '-' exp
+                       { $$.value = $1.value - $3.value;
+                         $$.unsignedp = $1.unsignedp || $3.unsignedp; }
+       |       exp LSH exp
+                       { $$.unsignedp = $1.unsignedp;
+                         if ($$.unsignedp)
+                           $$.value = (unsigned) $1.value << $3.value;
+                         else
+                           $$.value = $1.value << $3.value; }
+       |       exp RSH exp
+                       { $$.unsignedp = $1.unsignedp;
+                         if ($$.unsignedp)
+                           $$.value = (unsigned) $1.value >> $3.value;
+                         else
+                           $$.value = $1.value >> $3.value; }
+       |       exp EQUAL exp
+                       { $$.value = ($1.value == $3.value);
+                         $$.unsignedp = 0; }
+       |       exp NOTEQUAL exp
+                       { $$.value = ($1.value != $3.value);
+                         $$.unsignedp = 0; }
+       |       exp LEQ exp
+                       { $$.unsignedp = 0;
+                         if ($1.unsignedp || $3.unsignedp)
+                           $$.value =
+                             (unsigned) $1.value <= (unsigned) $3.value;
+                         else
+                           $$.value = $1.value <= $3.value; }
+       |       exp GEQ exp
+                       { $$.unsignedp = 0;
+                         if ($1.unsignedp || $3.unsignedp)
+                           $$.value =
+                             (unsigned) $1.value >= (unsigned) $3.value;
+                         else
+                           $$.value = $1.value >= $3.value; }
+       |       exp '<' exp
+                       { $$.unsignedp = 0;
+                         if ($1.unsignedp || $3.unsignedp)
+                           $$.value =
+                             (unsigned) $1.value < (unsigned) $3.value;
+                         else
+                           $$.value = $1.value < $3.value; }
+       |       exp '>' exp
+                       { $$.unsignedp = 0;
+                         if ($1.unsignedp || $3.unsignedp)
+                           $$.value =
+                             (unsigned) $1.value > (unsigned) $3.value;
+                         else
+                           $$.value = $1.value > $3.value; }
+       |       exp '&' exp
+                       { $$.value = $1.value & $3.value;
+                         $$.unsignedp = $1.unsignedp || $3.unsignedp; }
+       |       exp '^' exp
+                       { $$.value = $1.value ^ $3.value;
+                         $$.unsignedp = $1.unsignedp || $3.unsignedp; }
+       |       exp '|' exp
+                       { $$.value = $1.value | $3.value;
+                         $$.unsignedp = $1.unsignedp || $3.unsignedp; }
+       |       exp AND exp
+                       { $$.value = ($1.value && $3.value);
+                         $$.unsignedp = 0; }
+       |       exp OR exp
+                       { $$.value = ($1.value || $3.value);
+                         $$.unsignedp = 0; }
+       |       exp '?' exp ':' exp
+                       { $$.value = $1.value ? $3.value : $5.value;
+                         $$.unsignedp = $3.unsignedp || $5.unsignedp; }
+       |       INT
+                       { $$ = yylval.integer; }
+       |       CHAR
+                       { $$ = yylval.integer; }
+       |       NAME
+                       { $$.value = 0;
+                         $$.unsignedp = 0; }
+       ;
+%%
+\f
+/* During parsing of a C expression, the pointer to the next character
+   is in this variable.  */
+
+static char *lexptr;
+
+/* Take care of parsing a number (anything that starts with a digit).
+   Set yylval and return the token type; update lexptr.
+   LEN is the number of characters in it.  */
+
+/* maybe needs to actually deal with floating point numbers */
+
+int
+parse_number (olen)
+     int olen;
+{
+  register char *p = lexptr;
+  register long n = 0;
+  register int c;
+  register int base = 10;
+  register int len = olen;
+
+  for (c = 0; c < len; c++)
+    if (p[c] == '.') {
+      /* It's a float since it contains a point.  */
+      yyerror ("floating point numbers not allowed in #if expressions");
+      return ERROR;
+    }
+
+  yylval.integer.unsignedp = 0;
+
+  if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
+    p += 2;
+    base = 16;
+    len -= 2;
+  }
+  else if (*p == '0')
+    base = 8;
+
+  while (len > 0) {
+    c = *p++;
+    len--;
+    if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
+
+    if (c >= '0' && c <= '9') {
+      n *= base;
+      n += c - '0';
+    } else if (base == 16 && c >= 'a' && c <= 'f') {
+      n *= base;
+      n += c - 'a' + 10;
+    } else {
+      /* `l' means long, and `u' means unsigned.  */
+      while (1) {
+       if (c == 'l' || c == 'L')
+         ;
+       else if (c == 'u' || c == 'U')
+         yylval.integer.unsignedp = 1;
+       else
+         break;
+
+       if (len == 0)
+         break;
+       c = *p++;
+       len--;
+      }
+      /* Don't look for any more digits after the suffixes.  */
+      break;
+    }
+  }
+
+  if (len != 0) {
+    yyerror ("Invalid number in #if expression");
+    return ERROR;
+  }
+
+  /* If too big to be signed, consider it unsigned.  */
+  if (n < 0)
+    yylval.integer.unsignedp = 1;
+
+  lexptr = p;
+  yylval.integer.value = n;
+  return INT;
+}
+
+struct token {
+  const char *operator;
+  int token;
+};
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+static struct token tokentab2[] = {
+  {"&&", AND},
+  {"||", OR},
+  {"<<", LSH},
+  {">>", RSH},
+  {"==", EQUAL},
+  {"!=", NOTEQUAL},
+  {"<=", LEQ},
+  {">=", GEQ},
+  {NULL, ERROR}
+};
+
+/* Read one token, getting characters through lexptr.  */
+
+int
+yylex ()
+{
+  register int c;
+  register int namelen;
+  register char *tokstart;
+  register struct token *toktab;
+
+ retry:
+
+  tokstart = lexptr;
+  c = *tokstart;
+  /* See if it is a special token of length 2.  */
+  for (toktab = tokentab2; toktab->operator != NULL; toktab++)
+    if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) {
+      lexptr += 2;
+      return toktab->token;
+    }
+
+  switch (c) {
+  case 0:
+    return 0;
+    
+  case ' ':
+  case '\t':
+  case '\r':
+  case '\n':
+    lexptr++;
+    goto retry;
+    
+  case '\'':
+    lexptr++;
+    c = *lexptr++;
+    if (c == '\\')
+      c = parse_escape (&lexptr);
+
+    /* Sign-extend the constant if chars are signed on target machine.  */
+    {
+      if (lookup ((const unsigned char *)"__CHAR_UNSIGNED__",
+                  sizeof ("__CHAR_UNSIGNED__")-1, -1)
+         || ((c >> (CHAR_TYPE_SIZE - 1)) & 1) == 0)
+       yylval.integer.value = c & ((1 << CHAR_TYPE_SIZE) - 1);
+      else
+       yylval.integer.value = c | ~((1 << CHAR_TYPE_SIZE) - 1);
+    }
+
+    yylval.integer.unsignedp = 0;
+    c = *lexptr++;
+    if (c != '\'') {
+      yyerror ("Invalid character constant in #if");
+      return ERROR;
+    }
+    
+    return CHAR;
+
+    /* some of these chars are invalid in constant expressions;
+       maybe do something about them later */
+  case '/':
+  case '+':
+  case '-':
+  case '*':
+  case '%':
+  case '|':
+  case '&':
+  case '^':
+  case '~':
+  case '!':
+  case '@':
+  case '<':
+  case '>':
+  case '(':
+  case ')':
+  case '[':
+  case ']':
+  case '.':
+  case '?':
+  case ':':
+  case '=':
+  case '{':
+  case '}':
+  case ',':
+    lexptr++;
+    return c;
+    
+  case '"':
+    yyerror ("double quoted strings not allowed in #if expressions");
+    return ERROR;
+  }
+  if (c >= '0' && c <= '9') {
+    /* It's a number */
+    for (namelen = 0;
+        c = tokstart[namelen], is_idchar[c] || c == '.'; 
+        namelen++)
+      ;
+    return parse_number (namelen);
+  }
+  
+  if (!is_idstart[c]) {
+    yyerror ("Invalid token in expression");
+    return ERROR;
+  }
+  
+  /* It is a name.  See how long it is.  */
+  
+  for (namelen = 0;
+       is_idchar[(int)(unsigned char)tokstart[namelen]];
+       namelen++)
+    ;
+  
+  lexptr += namelen;
+  return NAME;
+}
+
+
+/* Parse a C escape sequence.  STRING_PTR points to a variable
+   containing a pointer to the string to parse.  That pointer
+   is updated past the characters we use.  The value of the
+   escape sequence is returned.
+
+   A negative value means the sequence \ newline was seen,
+   which is supposed to be equivalent to nothing at all.
+
+   If \ is followed by a null character, we return a negative
+   value and leave the string pointer pointing at the null character.
+
+   If \ is followed by 000, we return 0 and leave the string pointer
+   after the zeros.  A value of 0 does not mean end of string.  */
+
+int
+parse_escape (string_ptr)
+     char **string_ptr;
+{
+  register int c = *(*string_ptr)++;
+  switch (c)
+    {
+    case 'a':
+      return TARGET_BELL;
+    case 'b':
+      return TARGET_BS;
+    case 'e':
+      return 033;
+    case 'f':
+      return TARGET_FF;
+    case 'n':
+      return TARGET_NEWLINE;
+    case 'r':
+      return TARGET_CR;
+    case 't':
+      return TARGET_TAB;
+    case 'v':
+      return TARGET_VT;
+    case '\n':
+      return -2;
+    case 0:
+      (*string_ptr)--;
+      return 0;
+    case '^':
+      c = *(*string_ptr)++;
+      if (c == '\\')
+       c = parse_escape (string_ptr);
+      if (c == '?')
+       return 0177;
+      return (c & 0200) | (c & 037);
+      
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+      {
+       register int i = c - '0';
+       register int count = 0;
+       while (++count < 3)
+         {
+           c = *(*string_ptr)++;
+           if (c >= '0' && c <= '7')
+             i = (i << 3) + c - '0';
+           else
+             {
+               (*string_ptr)--;
+               break;
+             }
+         }
+       if ((i & ~((1 << CHAR_TYPE_SIZE) - 1)) != 0)
+         {
+           i &= (1 << CHAR_TYPE_SIZE) - 1;
+           warning ("octal character constant does not fit in a byte");
+         }
+       return i;
+      }
+    case 'x':
+      {
+       register int i = 0;
+       for (;;)
+         {
+           c = *(*string_ptr)++;
+           if (c >= '0' && c <= '9')
+             i = (i << 4) + c - '0';
+           else if (c >= 'a' && c <= 'f')
+             i = (i << 4) + c - 'a' + 10;
+           else if (c >= 'A' && c <= 'F')
+             i = (i << 4) + c - 'A' + 10;
+           else
+             {
+               (*string_ptr)--;
+               break;
+             }
+         }
+       if ((i & ~((1 << BITS_PER_UNIT) - 1)) != 0)
+         {
+           i &= (1 << BITS_PER_UNIT) - 1;
+           warning ("hex character constant does not fit in a byte");
+         }
+       return i;
+      }
+    default:
+      return c;
+    }
+}
+
+void
+yyerror (s)
+     const char *s;
+{
+  error (s);
+  longjmp (parse_return_error, 1);
+}
+\f
+/* This page contains the entry point to this file.  */
+
+/* Parse STRING as an expression, and complain if this fails
+   to use up all of the contents of STRING.  */
+/* We do not support C comments.  They should be removed before
+   this function is called.  */
+
+int
+parse_c_expression (string)
+     char *string;
+{
+  lexptr = string;
+  
+  if (lexptr == 0 || *lexptr == 0) {
+    error ("empty #if expression");
+    return 0;                  /* don't include the #if group */
+  }
+
+  /* if there is some sort of scanning error, just return 0 and assume
+     the parsing routine has printed an error message somewhere.
+     there is surely a better thing to do than this.     */
+  if (setjmp (parse_return_error))
+    return 0;
+
+  if (yyparse ())
+    return 0;                  /* actually this is never reached
+                                  the way things stand. */
+  if (*lexptr)
+    error ("Junk after end of expression.");
+
+  return expression_value;     /* set by yyparse () */
+}
diff --git a/gcc/tradcpp.c b/gcc/tradcpp.c
new file mode 100644 (file)
index 0000000..ec42b63
--- /dev/null
@@ -0,0 +1,4831 @@
+/* C Compatible Compiler Preprocessor (CCCP)
+Copyright (C) 1986, 1987, 1989, 2000 Free Software Foundation, Inc.
+                    Written by Paul Rubin, June 1986
+                   Adapted to ANSI C, Richard Stallman, Jan 1987
+                   Dusted off, polished, and adapted for use as traditional
+                   preprocessor only, Zack Weinberg, Jul 2000
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 1, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ In other words, you are welcome to use, share and improve this program.
+ You are forbidden to forbid anyone else to use, share and improve
+ what you give them.   Help stamp out software-hoarding!  */
+
+#include "config.h"
+#include "system.h"
+#include "version.h"
+#include "cppdefault.h"
+
+#include <signal.h>
+
+typedef unsigned char U_CHAR;
+
+/* Name under which this program was invoked.  */
+
+char *progname;
+
+/* Current maximum length of directory names in the search path
+   for include files.  (Altered as we get more of them.)  */
+
+size_t max_include_len;
+
+/* Nonzero means copy comments into the output file.  */
+
+int put_out_comments = 0;
+
+/* Nonzero means print the names of included files rather than
+   the preprocessed output.  1 means just the #include "...",
+   2 means #include <...> as well.  */
+
+int print_deps = 0;
+
+/* Nonzero means don't output line number information.  */
+
+int no_line_commands;
+
+/* Nonzero means inhibit output of the preprocessed text
+   and instead output the definitions of all user-defined macros
+   in a form suitable for use as input to cccp.  */
+
+int dump_macros;
+
+/* Nonzero means don't print warning messages.  -w.  */
+
+int inhibit_warnings = 0;
+
+/* Nonzero means warn if slash-star appears in a comment.  */
+
+int warn_comments;
+
+/* Nonzero causes output not to be done,
+   but directives such as #define that have side effects
+   are still obeyed.  */
+
+int no_output;
+
+/* Value of __USER_LABEL_PREFIX__.  Target-dependent, also controlled
+   by -f(no-)leading-underscore.  */
+const char *user_label_prefix;
+
+/* I/O buffer structure.
+   The `fname' field is nonzero for source files and #include files
+   and for the dummy text used for -D and -U.
+   It is zero for rescanning results of macro expansion
+   and for expanding macro arguments.  */
+#define INPUT_STACK_MAX 200
+struct file_buf {
+  const char *fname;
+  int lineno;
+  int length;
+  U_CHAR *buf;
+  U_CHAR *bufp;
+  /* Macro that this level is the expansion of.
+     Included so that we can reenable the macro
+     at the end of this level.  */
+  struct hashnode *macro;
+  /* Value of if_stack at start of this file.
+     Used to prohibit unmatched #endif (etc) in an include file.  */
+  struct if_stack *if_stack;
+  /* Object to be freed at end of input at this level.  */
+  U_CHAR *free_ptr;
+} instack[INPUT_STACK_MAX];
+
+typedef struct file_buf FILE_BUF;
+
+/* Current nesting level of input sources.
+   `instack[indepth]' is the level currently being read.  */
+int indepth = -1;
+#define CHECK_DEPTH(code) \
+  if (indepth >= (INPUT_STACK_MAX - 1))                                        \
+    {                                                                  \
+      error_with_line (line_for_error (instack[indepth].lineno),       \
+                      "macro or #include recursion too deep");         \
+      code;                                                            \
+    }
+
+/* Current depth in #include directives that use <...>.  */
+int system_include_depth = 0;
+
+/* The output buffer.  Its LENGTH field is the amount of room allocated
+   for the buffer, not the number of chars actually present.  To get
+   that, subtract outbuf.buf from outbuf.bufp. */
+
+#define OUTBUF_SIZE 10 /* initial size of output buffer */
+FILE_BUF outbuf;
+
+/* Grow output buffer OBUF points at
+   so it can hold at least NEEDED more chars.  */
+
+#define check_expand(OBUF, NEEDED) do { \
+  if ((OBUF)->length - ((OBUF)->bufp - (OBUF)->buf) <= (NEEDED)) \
+    grow_outbuf ((OBUF), (NEEDED)); \
+ } while (0)
+
+struct file_name_list
+  {
+    struct file_name_list *next;
+    const char *fname;
+  };
+
+struct file_name_list *include = 0;    /* First dir to search */
+       /* First dir to search for <file> */
+struct file_name_list *first_bracket_include = 0;
+struct file_name_list *last_include = 0;       /* Last in chain */
+
+/* List of included files that contained #once.  */
+struct file_name_list *dont_repeat_files = 0;
+
+/* List of other included files.  */
+struct file_name_list *all_include_files = 0;
+\f
+/* Structure allocated for every #define.  For a simple replacement
+   such as
+       #define foo bar ,
+   nargs = -1, the `pattern' list is null, and the expansion is just
+   the replacement text.  Nargs = 0 means a functionlike macro with no args,
+   e.g.,
+       #define getchar() getc (stdin) .
+   When there are args, the expansion is the replacement text with the
+   args squashed out, and the reflist is a list describing how to
+   build the output from the input: e.g., "3 chars, then the 1st arg,
+   then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg".
+   The chars here come from the expansion.  Whatever is left of the
+   expansion after the last arg-occurrence is copied after that arg.
+   Note that the reflist can be arbitrarily long---
+   its length depends on the number of times the arguments appear in
+   the replacement text, not how many args there are.  Example:
+   #define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and
+   pattern list
+     { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
+   where (x, y) means (nchars, argno). */
+
+typedef struct definition DEFINITION;
+struct definition {
+  int nargs;
+  int length;                  /* length of expansion string */
+  U_CHAR *expansion;
+  struct reflist {
+    struct reflist *next;
+    char stringify;            /* nonzero if this arg was preceded by a
+                                  # operator. */
+    char raw_before;           /* Nonzero if a ## operator before arg. */
+    char raw_after;            /* Nonzero if a ## operator after arg. */
+    int nchars;                        /* Number of literal chars to copy before
+                                  this arg occurrence.  */
+    int argno;                 /* Number of arg to substitute (origin-0) */
+  } *pattern;
+  /* Names of macro args, concatenated in reverse order
+     with comma-space between them.
+     The only use of this is that we warn on redefinition
+     if this differs between the old and new definitions.  */
+  U_CHAR *argnames;
+};
+
+/* different kinds of things that can appear in the value field
+   of a hash node.  Actually, this may be useless now. */
+union hashval {
+  const char *cpval;
+  DEFINITION *defn;
+};
+
+
+/* The structure of a node in the hash table.  The hash table
+   has entries for all tokens defined by #define commands (type T_MACRO),
+   plus some special tokens like __LINE__ (these each have their own
+   type, and the appropriate code is run when that type of node is seen.
+   It does not contain control words like "#define", which are recognized
+   by a separate piece of code. */
+
+/* different flavors of hash nodes --- also used in keyword table */
+enum node_type {
+ T_DEFINE = 1, /* `#define' */
+ T_INCLUDE,    /* `#include' */
+ T_IFDEF,      /* `#ifdef' */
+ T_IFNDEF,     /* `#ifndef' */
+ T_IF,         /* `#if' */
+ T_ELSE,       /* `#else' */
+ T_ELIF,       /* `#elif' */
+ T_UNDEF,      /* `#undef' */
+ T_LINE,       /* `#line' */
+ T_ENDIF,      /* `#endif' */
+ T_SPECLINE,   /* special symbol `__LINE__' */
+ T_DATE,       /* `__DATE__' */
+ T_FILE,       /* `__FILE__' */
+ T_BASE_FILE,  /* `__BASE_FILE__' */
+ T_INCLUDE_LEVEL, /* `__INCLUDE_LEVEL__' */
+ T_VERSION,    /* `__VERSION__' */
+ T_TIME,       /* `__TIME__' */
+ T_CONST,      /* Constant value, used by `__STDC__' */
+ T_MACRO,      /* macro defined by `#define' */
+ T_SPEC_DEFINED, /* special `defined' macro for use in #if statements */
+ T_UNUSED      /* Used for something not defined.  */
+};
+
+struct hashnode {
+  struct hashnode *next;       /* double links for easy deletion */
+  struct hashnode *prev;
+  struct hashnode **bucket_hdr;        /* also, a back pointer to this node's hash
+                                  chain is kept, in case the node is the head
+                                  of the chain and gets deleted. */
+  enum node_type type;         /* type of special token */
+  int length;                  /* length of token, for quick comparison */
+  U_CHAR *name;                        /* the actual name */
+  union hashval value;         /* pointer to expansion, or whatever */
+};
+
+typedef struct hashnode HASHNODE;
+
+/* Some definitions for the hash table.  The hash function MUST be
+   computed as shown in hashf () below.  That is because the rescan
+   loop computes the hash value `on the fly' for most tokens,
+   in order to avoid the overhead of a lot of procedure calls to
+   the hashf () function.  Hashf () only exists for the sake of
+   politeness, for use when speed isn't so important. */
+
+#define HASHSIZE 1403
+HASHNODE *hashtab[HASHSIZE];
+#define HASHSTEP(old, c) ((old << 2) + c)
+#define MAKE_POS(v) (v & 0x7fffffff) /* make number positive */
+
+/* `struct directive' defines one #-directive, including how to handle it.  */
+
+struct directive {
+  int length;                  /* Length of name */
+  void (*func) PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+                               /* Function to handle directive */
+  const char *name;            /* Name of directive */
+  enum node_type type;         /* Code which describes which directive. */
+};
+
+/* Last arg to output_line_command.  */
+enum file_change_code {same_file, enter_file, leave_file};
+
+/* This structure represents one parsed argument in a macro call.
+   `raw' points to the argument text as written (`raw_length' is its length).
+   `expanded' points to the argument's macro-expansion
+   (its length is `expand_length').
+   `stringified_length' is the length the argument would have
+   if stringified.
+   `free1' and `free2', if nonzero, point to blocks to be freed
+   when the macro argument data is no longer needed.  */
+
+struct argdata {
+  U_CHAR *raw, *expanded;
+  int raw_length, expand_length;
+  int stringified_length;
+  U_CHAR *free1, *free2;
+  char newlines;
+  char comments;
+};
+
+/* The arglist structure is built by do_define to tell
+   collect_definition where the argument names begin.  That
+   is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist
+   would contain pointers to the strings x, y, and z.
+   Collect_definition would then build a DEFINITION node,
+   with reflist nodes pointing to the places x, y, and z had
+   appeared.  So the arglist is just convenience data passed
+   between these two routines.  It is not kept around after
+   the current #define has been processed and entered into the
+   hash table. */
+
+struct arglist {
+  struct arglist *next;
+  U_CHAR *name;
+  int length;
+  int argno;
+};
+
+/* Function prototypes.  */
+
+void do_define PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_line   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_include        PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_undef  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_if     PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_xifdef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_else   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_elif   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+void do_endif  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+
+struct hashnode *install PARAMS ((const U_CHAR *, int, enum node_type, int));
+struct hashnode *lookup  PARAMS ((const U_CHAR *, int, int));
+int hashf               PARAMS ((const U_CHAR *, int, int));
+int compare_defs        PARAMS ((DEFINITION *, DEFINITION *));
+int comp_def_part       PARAMS ((int, U_CHAR *, int, U_CHAR *, int, int));
+void delete_macro       PARAMS ((HASHNODE *));
+
+/* First arg to v_message.  */
+enum msgtype { WARNING = 0, ERROR, FATAL };
+void v_message          PARAMS ((enum msgtype mtype, int line,
+                                 const char *msgid, va_list ap));
+
+void warning            PARAMS ((const char *msgid, ...));
+void error              PARAMS ((const char *msgid, ...));
+void fatal              PARAMS ((const char *msgid, ...)) ATTRIBUTE_NORETURN;
+void error_with_line    PARAMS ((int, const char *msgid, ...));
+void error_from_errno   PARAMS ((const char *msgid));
+
+void perror_with_name   PARAMS ((const char *msgid));
+void pfatal_with_name   PARAMS ((const char *msgid)) ATTRIBUTE_NORETURN;
+void fancy_abort        PARAMS ((int, const char *)) ATTRIBUTE_NORETURN;
+
+int line_for_error      PARAMS ((int));
+
+/* We know perfectly well which file this is, so we don't need to
+   use __FILE__.  */
+#undef abort
+#if (GCC_VERSION >= 2007)
+#define abort()        fancy_abort(__LINE__, __FUNCTION__)
+#else
+#define abort() fancy_abort(__LINE__, 0);
+#endif
+
+void macroexpand               PARAMS ((HASHNODE *, FILE_BUF *));
+void special_symbol            PARAMS ((HASHNODE *, FILE_BUF *));
+void dump_all_macros           PARAMS ((void));
+void dump_defn_1               PARAMS ((U_CHAR *, int, int));
+void dump_arg_n                        PARAMS ((DEFINITION *, int));
+void conditional_skip          PARAMS ((FILE_BUF *, int, enum node_type));
+void skip_if_group             PARAMS ((FILE_BUF *, int));
+void output_line_command       PARAMS ((FILE_BUF *, FILE_BUF *,
+                                        int, enum file_change_code));
+
+int eval_if_expression         PARAMS ((U_CHAR *, int));
+int parse_c_expression         PARAMS ((char *));  /* in tradcif.y */
+
+void initialize_char_syntax    PARAMS ((void));
+void initialize_builtins       PARAMS ((void));
+void make_definition           PARAMS ((U_CHAR *));
+void make_undef                        PARAMS ((U_CHAR *));
+
+void grow_outbuf       PARAMS ((FILE_BUF *, int));
+int handle_directive   PARAMS ((FILE_BUF *, FILE_BUF *));
+void finclude          PARAMS ((int, const char *, FILE_BUF *));
+void deps_output       PARAMS ((const char *, int));
+void rescan            PARAMS ((FILE_BUF *, int));
+void newline_fix       PARAMS ((U_CHAR *));
+void name_newline_fix  PARAMS ((U_CHAR *));
+U_CHAR *macarg1        PARAMS ((U_CHAR *, U_CHAR *, int *, int *, int *));
+const char *macarg     PARAMS ((struct argdata *));
+int discard_comments   PARAMS ((U_CHAR *, int, int));
+int file_size_and_mode PARAMS ((int, int *, long *));
+
+U_CHAR *skip_to_end_of_comment PARAMS ((FILE_BUF *, int *));
+U_CHAR *skip_quoted_string     PARAMS ((U_CHAR *, U_CHAR *, int,
+                                       int *, int *, int *));
+
+void pipe_closed       PARAMS ((int));
+int main               PARAMS ((int, char **));
+
+/* Convenience.  Write U"string" to get an unsigned string constant.  */
+#define U (const unsigned char *)
+
+/* Here is the actual list of #-directives, most-often-used first.  */
+
+struct directive directive_table[] = {
+  {  6, do_define,  "define",  T_DEFINE  },
+  {  7, do_include, "include", T_INCLUDE },
+  {  5, do_endif,   "endif",   T_ENDIF   },
+  {  5, do_xifdef,  "ifdef",   T_IFDEF   },
+  {  2, do_if,      "if",      T_IF,     },
+  {  4, do_else,    "else",    T_ELSE    },
+  {  6, do_xifdef,  "ifndef",  T_IFNDEF  },
+  {  5, do_undef,   "undef",   T_UNDEF   },
+  {  4, do_line,    "line",    T_LINE    },
+  {  4, do_elif,    "elif",    T_ELIF    },
+  {  -1, 0, "", T_UNUSED},
+};
+
+/* table to tell if char can be part of a C identifier. */
+U_CHAR is_idchar[256];
+/* table to tell if char can be first char of a c identifier. */
+U_CHAR is_idstart[256];
+/* table to tell if c is horizontal space.  */
+U_CHAR is_hor_space[256];
+/* table to tell if c is horizontal or vertical space.  */
+U_CHAR is_space[256];
+
+#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0)
+#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*p]) p++; } while (0)
+  
+int errors = 0;                        /* Error counter for exit code */
+
+FILE_BUF expand_to_temp_buffer PARAMS ((U_CHAR *, U_CHAR *, int));
+DEFINITION *collect_expansion  PARAMS ((U_CHAR *, U_CHAR *, int,
+                                       struct arglist *));
+
+/* Stack of conditionals currently in progress
+   (including both successful and failing conditionals).  */
+
+struct if_stack {
+  struct if_stack *next;       /* for chaining to the next stack frame */
+  const char *fname;           /* copied from input when frame is made */
+  int lineno;                  /* similarly */
+  int if_succeeded;            /* true if a leg of this if-group
+                                   has been passed through rescan */
+  enum node_type type;         /* type of last directive seen in this group */
+};
+typedef struct if_stack IF_STACK_FRAME;
+IF_STACK_FRAME *if_stack = NULL;
+
+/* Buffer of -M output.  */
+
+char *deps_buffer;
+
+/* Number of bytes allocated in above.  */
+int deps_allocated_size;
+
+/* Number of bytes used.  */
+int deps_size;
+
+/* Number of bytes since the last newline.  */
+int deps_column;
+
+/* Nonzero means -I- has been seen,
+   so don't look for #include "foo" the source-file directory.  */
+int ignore_srcdir;
+\f
+/* Handler for SIGPIPE.  */
+
+void
+pipe_closed (dummy)
+     int dummy ATTRIBUTE_UNUSED;
+{
+  exit (FATAL_EXIT_CODE);
+}
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int st_mode;
+  long st_size;
+  const char *in_fname, *out_fname;
+  int f, i;
+  FILE_BUF *fp;
+  const char **pend_files = (const char **) xmalloc (argc * sizeof (char *));
+  const char **pend_defs = (const char **) xmalloc (argc * sizeof (char *));
+  const char **pend_undefs = (const char **) xmalloc (argc * sizeof (char *));
+  int no_standard_includes = 0;
+
+  /* Non-0 means don't output the preprocessed program.  */
+  int inhibit_output = 0;
+
+  /* Stream on which to print the dependency information.  */
+  FILE *deps_stream = 0;
+  /* Target-name to write with the dependency information.  */
+  char *deps_target = 0;
+
+#ifdef RLIMIT_STACK
+  /* Get rid of any avoidable limit on stack size.  */
+  {
+    struct rlimit rlim;
+
+    /* Set the stack limit huge so that alloca (particularly stringtab
+     * in dbxread.c) does not fail. */
+    getrlimit (RLIMIT_STACK, &rlim);
+    rlim.rlim_cur = rlim.rlim_max;
+    setrlimit (RLIMIT_STACK, &rlim);
+  }
+#endif /* RLIMIT_STACK defined */
+
+  progname = argv[0];
+
+  in_fname = NULL;
+  out_fname = NULL;
+
+  /* Initialize is_idchar to allow $.  */
+  initialize_char_syntax ();
+
+  no_line_commands = 0;
+  dump_macros = 0;
+  no_output = 0;
+
+  signal (SIGPIPE, pipe_closed);
+
+  max_include_len = cpp_GCC_INCLUDE_DIR_len + 7;  /* ??? */
+
+  memset (pend_files, 0, argc * sizeof (char *));
+  memset (pend_defs, 0, argc * sizeof (char *));
+  memset (pend_undefs, 0, argc * sizeof (char *));
+
+  /* Process switches and find input file name.  */
+
+  for (i = 1; i < argc; i++) {
+    if (argv[i][0] != '-') {
+      if (out_fname != NULL)
+       fatal ("Usage: %s [switches] input output", argv[0]);
+      else if (in_fname != NULL)
+       out_fname = argv[i];
+      else
+       in_fname = argv[i];
+    } else {
+      switch (argv[i][1]) {
+      case 'A':
+      case 'E':
+      case '$':
+      case 'g':
+       break;  /* Ignore for compatibility with ISO/extended cpp.  */
+
+      case 'l':
+       if (!strcmp (argv[i], "-lang-c++")
+           || !strcmp (argv[i], "-lang-objc++"))
+         fatal ("-traditional is not supported in C++");
+       else if (!strcmp (argv[i], "-lang-c89"))
+         fatal ("-traditional and -ansi are mutually exclusive");
+       else if (!strcmp (argv[i], "-lang-objc"))
+         pend_defs[i] = "__OBJC__";
+       else if (!strcmp (argv[i], "-lang-asm"))
+         pend_defs[i] = "__ASSEMBLER__";
+       else if (!strcmp (argv[i], "-lang-fortran"))
+         pend_defs[i] = "_LANGUAGE_FORTRAN";
+       /* All other possibilities ignored.  */
+       break;
+
+      case 'i':
+       if (!strcmp (argv[i], "-include"))
+         {
+           if (i + 1 == argc)
+             fatal ("Filename missing after -i option");
+           else
+             pend_files[i] = argv[i+1], i++;
+         }
+       else if (!strcmp (argv[i], "-iprefix"))
+         i++; /* Ignore for compatibility */
+       else if (!strcmp (argv[i], "-isystem")
+                || !strcmp (argv[i], "-iwithprefix")
+                || !strcmp (argv[i], "-iwithprefixbefore")
+                || !strcmp (argv[i], "-idirafter"))
+         goto include;  /* best we can do */
+         
+       break;
+
+      case 'o':
+       if (out_fname != NULL)
+         fatal ("Output filename specified twice");
+       if (i + 1 == argc)
+         fatal ("Filename missing after -o option");
+       out_fname = argv[++i];
+       if (!strcmp (out_fname, "-"))
+         out_fname = "";
+       break;
+
+      case 'w':
+       inhibit_warnings = 1;
+       break;
+
+      case 'W':
+       if (!strcmp (argv[i], "-Wcomments"))
+         warn_comments = 1;
+       else if (!strcmp (argv[i], "-Wcomment"))
+         warn_comments = 1;
+       else if (!strcmp (argv[i], "-Wall")) {
+         warn_comments = 1;
+       }
+       break;
+
+      case 'f':
+       if (!strcmp (argv[i], "-fleading-underscore"))
+         user_label_prefix = "_";
+       else if (!strcmp (argv[i], "-fno-leading-underscore"))
+         user_label_prefix = "";
+       break;
+
+      case 'M':
+       if (!strcmp (argv[i], "-M"))
+         print_deps = 2;
+       else if (!strcmp (argv[i], "-MM"))
+         print_deps = 1;
+       inhibit_output = 1;
+       break;
+
+      case 'd':
+       dump_macros = 1;
+       no_output = 1;
+       break;
+
+      case 'v':
+       fprintf (stderr, "GNU traditional CPP version %s\n", version_string);
+       break;
+
+      case 'D':
+       {
+         char *p, *p1;
+
+         if (argv[i][2] != 0)
+           p = argv[i] + 2;
+         else if (i + 1 == argc)
+           fatal ("Macro name missing after -D option");
+         else
+           p = argv[++i];
+
+         if ((p1 = (char *) strchr (p, '=')) != NULL)
+           *p1 = ' ';
+         pend_defs[i] = p;
+       }
+       break;
+
+      case 'U':                /* JF #undef something */
+       if (argv[i][2] != 0)
+         pend_undefs[i] = argv[i] + 2;
+       else if (i + 1 == argc)
+         fatal ("Macro name missing after -U option");
+       else
+         pend_undefs[i] = argv[i+1], i++;
+       break;
+
+      case 'C':
+       put_out_comments = 1;
+       break;
+
+      case 'p':
+       if (!strcmp (argv[i], "-pedantic"))
+         fatal ("-pedantic and -traditional are mutually exclusive");
+       break;
+
+      case 't':
+       if (!strcmp (argv[i], "-trigraphs"))
+         fatal ("-trigraphs and -traditional are mutually exclusive");
+       break;
+
+      case 'P':
+       no_line_commands = 1;
+       break;
+
+      case 'I':                        /* Add directory to path for includes.  */
+      include:
+       {
+         struct file_name_list *dirtmp;
+
+         if (! ignore_srcdir && !strcmp (argv[i] + 2, "-"))
+           ignore_srcdir = 1;
+         else {
+           dirtmp = (struct file_name_list *)
+             xmalloc (sizeof (struct file_name_list));
+           dirtmp->next = 0;           /* New one goes on the end */
+           if (include == 0)
+             include = dirtmp;
+           else
+             last_include->next = dirtmp;
+           last_include = dirtmp;      /* Tail follows the last one */
+           if (argv[i][1] == 'I' && argv[i][2] != 0)
+             dirtmp->fname = argv[i] + 2;
+           else if (i + 1 == argc)
+             fatal ("Directory name missing after -I option");
+           else
+             dirtmp->fname = argv[++i];
+           if (strlen (dirtmp->fname) > max_include_len)
+             max_include_len = strlen (dirtmp->fname);
+           if (ignore_srcdir && first_bracket_include == 0)
+             first_bracket_include = dirtmp;
+           }
+       }
+       break;
+
+      case 'n':
+       /* -nostdinc causes no default include directories.
+          You must specify all include-file directories with -I.  */
+       no_standard_includes = 1;
+       break;
+
+      case '\0': /* JF handle '-' as file name meaning stdin or stdout */
+       if (in_fname == NULL) {
+         in_fname = "";
+         break;
+       } else if (out_fname == NULL) {
+         out_fname = "";
+         break;
+       }       /* else fall through into error */
+
+      default:
+       fatal ("Invalid option `%s'", argv[i]);
+      }
+    }
+  }
+
+  if (user_label_prefix == 0)
+    user_label_prefix = USER_LABEL_PREFIX;
+
+  /* Initialize is_idchar.  */
+  initialize_char_syntax ();
+
+  /* Install __LINE__, etc.  Must follow initialize_char_syntax
+     and option processing.  */
+  initialize_builtins ();
+
+  /* Do defines specified with -D.  */
+  for (i = 1; i < argc; i++)
+    if (pend_defs[i])
+      make_definition ((U_CHAR *)pend_defs[i]);
+
+  /* Do undefines specified with -U.  */
+  for (i = 1; i < argc; i++)
+    if (pend_undefs[i])
+      make_undef ((U_CHAR *)pend_undefs[i]);
+
+  /* Unless -fnostdinc,
+     tack on the standard include file dirs to the specified list */
+  if (!no_standard_includes) {
+    const struct default_include *di;
+    struct file_name_list *old_last_include = last_include;
+    struct file_name_list *dirtmp;
+    for (di = cpp_include_defaults; di->fname; di++) {
+      if (di->cplusplus)
+       continue;
+      dirtmp = (struct file_name_list *)
+       xmalloc (sizeof (struct file_name_list));
+      dirtmp->next = 0;                /* New one goes on the end */
+      if (include == 0)
+       include = dirtmp;
+      else
+       last_include->next = dirtmp;
+      last_include = dirtmp;   /* Tail follows the last one */
+      dirtmp->fname = di->fname;
+    }
+
+    if (ignore_srcdir && first_bracket_include == 0)
+      first_bracket_include = old_last_include->next;
+  }
+
+  /* Initialize output buffer */
+
+  outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE);
+  outbuf.bufp = outbuf.buf;
+  outbuf.length = OUTBUF_SIZE;
+
+  /* Scan the -i files before the main input.
+     Much like #including them, but with no_output set
+     so that only their macro definitions matter.  */
+
+  no_output++;
+  for (i = 1; i < argc; i++)
+    if (pend_files[i]) {
+      int fd = open (pend_files[i], O_RDONLY, 0666);
+      if (fd < 0) {
+       perror_with_name (pend_files[i]);
+       return FATAL_EXIT_CODE;
+      }
+      finclude (fd, pend_files[i], &outbuf);
+    }
+  no_output--;
+
+  /* Create an input stack level for the main input file
+     and copy the entire contents of the file into it.  */
+
+  fp = &instack[++indepth];
+
+  /* JF check for stdin */
+  if (in_fname == NULL || *in_fname == 0) {
+    in_fname = "";
+    f = 0;
+  } else if ((f = open (in_fname, O_RDONLY, 0666)) < 0)
+    goto perror;
+
+  /* Either of two environment variables can specify output of deps.
+     Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
+     where OUTPUT_FILE is the file to write deps info to
+     and DEPS_TARGET is the target to mention in the deps.  */
+
+  if (print_deps == 0
+      && (getenv ("SUNPRO_DEPENDENCIES") != 0
+         || getenv ("DEPENDENCIES_OUTPUT") != 0))
+    {
+      char *spec = getenv ("DEPENDENCIES_OUTPUT");
+      char *s;
+      char *output_file;
+
+      if (spec == 0)
+       {
+         spec = getenv ("SUNPRO_DEPENDENCIES");
+         print_deps = 2;
+       }
+      else
+       print_deps = 1;
+
+      /* Find the space before the DEPS_TARGET, if there is one.  */
+      s = strchr (spec, ' ');
+      if (s)
+       {
+         deps_target = s + 1;
+         output_file = (char *) xmalloc (s - spec + 1);
+         memcpy (output_file, spec, s - spec);
+         output_file[s - spec] = 0;
+       }
+      else
+       {
+         deps_target = 0;
+         output_file = spec;
+       }
+      
+      deps_stream = fopen (output_file, "a");
+      if (deps_stream == 0)
+       pfatal_with_name (output_file);
+    }
+  /* If the -M option was used, output the deps to standard output.  */
+  else if (print_deps)
+    deps_stream = stdout;
+
+  /* For -M, print the expected object file name
+     as the target of this Make-rule.  */
+  if (print_deps) {
+    deps_allocated_size = 200;
+    deps_buffer = (char *) xmalloc (deps_allocated_size);
+    deps_buffer[0] = 0;
+    deps_size = 0;
+    deps_column = 0;
+
+    if (deps_target) {
+      deps_output (deps_target, 0);
+      deps_output (":", 0);
+    } else if (*in_fname == 0)
+      deps_output ("-: ", 0);
+    else {
+      int len;
+      const char *p = in_fname;
+      const char *p1 = p;
+      /* Discard all directory prefixes from P.  */
+      while (*p1) {
+       if (*p1 == '/')
+         p = p1 + 1;
+       p1++;
+      }
+      /* Output P, but remove known suffixes.  */
+      len = strlen (p);
+      if (p[len - 2] == '.'
+         && (p[len - 1] == 'c' || p[len - 1] == 'C' || p[len - 1] == 'S'))
+       deps_output (p, len - 2);
+      else if (p[len - 3] == '.'
+              && p[len - 2] == 'c'
+              && p[len - 1] == 'c')
+       deps_output (p, len - 3);
+      else
+       deps_output (p, 0);
+      /* Supply our own suffix.  */
+      deps_output (".o : ", 0);
+      deps_output (in_fname, 0);
+      deps_output (" ", 0);
+    }
+  }
+
+  if (file_size_and_mode (f, &st_mode, &st_size))
+    goto perror;
+  fp->fname = in_fname;
+  fp->lineno = 1;
+  /* JF all this is mine about reading pipes and ttys */
+  if (!S_ISREG (st_mode)) {
+    /* Read input from a file that is not a normal disk file.
+       We cannot preallocate a buffer with the correct size,
+       so we must read in the file a piece at the time and make it bigger.  */
+    int size;
+    int bsize;
+    int cnt;
+    U_CHAR *bufp;
+
+    bsize = 2000;
+    size = 0;
+    fp->buf = (U_CHAR *) xmalloc (bsize + 2);
+    bufp = fp->buf;
+    for (;;) {
+      cnt = read (f, bufp, bsize - size);
+      if (cnt < 0) goto perror;        /* error! */
+      if (cnt == 0) break;     /* End of file */
+      size += cnt;
+      bufp += cnt;
+      if (bsize == size) {     /* Buffer is full! */
+        bsize *= 2;
+        fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
+       bufp = fp->buf + size;  /* May have moved */
+      }
+    }
+    fp->length = size;
+  } else {
+    /* Read a file whose size we can determine in advance.
+       For the sake of VMS, st_size is just an upper bound.  */
+    long i;
+    fp->length = 0;
+    fp->buf = (U_CHAR *) xmalloc (st_size + 2);
+
+    while (st_size > 0) {
+      i = read (f, fp->buf + fp->length, st_size);
+      if (i <= 0) {
+        if (i == 0) break;
+       goto perror;
+      }
+      fp->length += i;
+      st_size -= i;
+    }
+  }
+  fp->bufp = fp->buf;
+  fp->if_stack = if_stack;
+
+  /* Make sure data ends with a newline.  And put a null after it.  */
+
+  if (fp->length > 0 && fp->buf[fp->length-1] != '\n')
+    fp->buf[fp->length++] = '\n';
+  fp->buf[fp->length] = '\0';
+  
+  /* Now that we know the input file is valid, open the output.  */
+
+  if (!out_fname || !strcmp (out_fname, ""))
+    out_fname = "stdout";
+  else if (! freopen (out_fname, "w", stdout))
+    pfatal_with_name (out_fname);
+
+  output_line_command (fp, &outbuf, 0, same_file);
+
+  /* Scan the input, processing macros and directives.  */
+
+  rescan (&outbuf, 0);
+
+  /* Now we have processed the entire input
+     Write whichever kind of output has been requested.  */
+
+
+  if (dump_macros)
+    dump_all_macros ();
+  else if (! inhibit_output && deps_stream != stdout) {
+    if (write (fileno (stdout), outbuf.buf, outbuf.bufp - outbuf.buf) < 0)
+      fatal ("I/O error on output");
+  }
+
+  if (print_deps) {
+    fputs (deps_buffer, deps_stream);
+    putc ('\n', deps_stream);
+    if (deps_stream != stdout) {
+      fclose (deps_stream);
+      if (ferror (deps_stream))
+       fatal ("I/O error on output");
+    }
+  }
+
+  if (ferror (stdout))
+    fatal ("I/O error on output");
+
+  if (errors)
+    exit (FATAL_EXIT_CODE);
+  exit (SUCCESS_EXIT_CODE);
+
+ perror:
+  pfatal_with_name (in_fname);
+}
+
+/* Move all backslash-newline pairs out of embarrassing places.
+   Exchange all such pairs following BP
+   with any potentially-embarrasing characters that follow them.
+   Potentially-embarrassing characters are / and *
+   (because a backslash-newline inside a comment delimiter
+   would cause it not to be recognized).  */
+void
+newline_fix (bp)
+     U_CHAR *bp;
+{
+  register U_CHAR *p = bp;
+  register int count = 0;
+
+  /* First count the backslash-newline pairs here.  */
+
+  while (*p++ == '\\' && *p++ == '\n')
+    count++;
+
+  p = bp + count * 2;
+
+  /* Exit if what follows the backslash-newlines is not embarrassing.  */
+
+  if (count == 0 || (*p != '/' && *p != '*'))
+    return;
+
+  /* Copy all potentially embarrassing characters
+     that follow the backslash-newline pairs
+     down to where the pairs originally started.  */
+
+  while (*p == '*' || *p == '/')
+    *bp++ = *p++;
+
+  /* Now write the same number of pairs after the embarrassing chars.  */
+  while (count-- > 0) {
+    *bp++ = '\\';
+    *bp++ = '\n';
+  }
+}
+
+/* Like newline_fix but for use within a directive-name.
+   Move any backslash-newlines up past any following symbol constituents.  */
+void
+name_newline_fix (bp)
+     U_CHAR *bp;
+{
+  register U_CHAR *p = bp;
+  register int count = 0;
+
+  /* First count the backslash-newline pairs here.  */
+
+  while (*p++ == '\\' && *p++ == '\n')
+    count++;
+
+  p = bp + count * 2;
+
+  /* What follows the backslash-newlines is not embarrassing.  */
+
+  if (count == 0 || !is_idchar[*p])
+    return;
+
+  /* Copy all potentially embarrassing characters
+     that follow the backslash-newline pairs
+     down to where the pairs originally started.  */
+
+  while (is_idchar[*p])
+    *bp++ = *p++;
+
+  /* Now write the same number of pairs after the embarrassing chars.  */
+  while (count-- > 0) {
+    *bp++ = '\\';
+    *bp++ = '\n';
+  }
+}
+\f
+/*
+ * The main loop of the program.
+ *
+ * Read characters from the input stack, transferring them to the
+ * output buffer OP.
+ *
+ * Macros are expanded and push levels on the input stack.
+ * At the end of such a level it is popped off and we keep reading.
+ * At the end of any other kind of level, we return.
+ * #-directives are handled, except within macros.
+ *
+ * If OUTPUT_MARKS is nonzero, keep Newline markers found in the input
+ * and insert them when appropriate.  This is set while scanning macro
+ * arguments before substitution.  It is zero when scanning for final output.
+ *   There are three types of Newline markers:
+ *   * Newline -  follows a macro name that was not expanded
+ *     because it appeared inside an expansion of the same macro.
+ *     This marker prevents future expansion of that identifier.
+ *     When the input is rescanned into the final output, these are deleted.
+ *     These are also deleted by ## concatenation.
+ *   * Newline Space (or Newline and any other whitespace character)
+ *     stands for a place that tokens must be separated or whitespace
+ *     is otherwise desirable, but where the ANSI standard specifies there
+ *     is no whitespace.  This marker turns into a Space (or whichever other
+ *     whitespace char appears in the marker) in the final output,
+ *     but it turns into nothing in an argument that is stringified with #.
+ *     Such stringified arguments are the only place where the ANSI standard
+ *     specifies with precision that whitespace may not appear.
+ *
+ * During this function, IP->bufp is kept cached in IBP for speed of access.
+ * Likewise, OP->bufp is kept in OBP.  Before calling a subroutine
+ * IBP, IP and OBP must be copied back to memory.  IP and IBP are
+ * copied back with the RECACHE macro.  OBP must be copied back from OP->bufp
+ * explicitly, and before RECACHE, since RECACHE uses OBP.
+ */
+
+void
+rescan (op, output_marks)
+     FILE_BUF *op;
+     int output_marks;
+{
+  /* Character being scanned in main loop.  */
+  register U_CHAR c;
+
+  /* Length of pending accumulated identifier.  */
+  register int ident_length = 0;
+
+  /* Hash code of pending accumulated identifier.  */
+  register int hash = 0;
+
+  /* Current input level (&instack[indepth]).  */
+  FILE_BUF *ip;
+
+  /* Pointer for scanning input.  */
+  register U_CHAR *ibp;
+
+  /* Pointer to end of input.  End of scan is controlled by LIMIT.  */
+  register U_CHAR *limit;
+
+  /* Pointer for storing output.  */
+  register U_CHAR *obp;
+
+  /* REDO_CHAR is nonzero if we are processing an identifier
+     after backing up over the terminating character.
+     Sometimes we process an identifier without backing up over
+     the terminating character, if the terminating character
+     is not special.  Backing up is done so that the terminating character
+     will be dispatched on again once the identifier is dealt with.  */
+  int redo_char = 0;
+
+  /* 1 if within an identifier inside of which a concatenation
+     marker (Newline -) has been seen.  */
+  int concatenated = 0;
+
+  /* While scanning a comment or a string constant,
+     this records the line it started on, for error messages.  */
+  int start_line;
+
+  /* Record position of last `real' newline.  */
+  U_CHAR *beg_of_line;
+
+/* Pop the innermost input stack level, assuming it is a macro expansion.  */
+
+#define POPMACRO \
+do { ip->macro->type = T_MACRO;                \
+     if (ip->free_ptr) free (ip->free_ptr);    \
+     --indepth; } while (0)
+
+/* Reload `rescan's local variables that describe the current
+   level of the input stack.  */
+
+#define RECACHE  \
+do { ip = &instack[indepth];           \
+     ibp = ip->bufp;                   \
+     limit = ip->buf + ip->length;     \
+     op->bufp = obp;                   \
+     check_expand (op, limit - ibp);   \
+     beg_of_line = 0;                  \
+     obp = op->bufp; } while (0)
+
+  if (no_output && instack[indepth].fname != 0)
+    skip_if_group (&instack[indepth], 1);
+
+  obp = op->bufp;
+  RECACHE;
+  beg_of_line = ibp;
+
+  /* Our caller must always put a null after the end of
+     the input at each input stack level.  */
+  if (*limit != 0)
+    abort ();
+
+  while (1) {
+    c = *ibp++;
+    *obp++ = c;
+
+    switch (c) {
+    case '\\':
+      if (ibp >= limit)
+       break;
+      if (*ibp == '\n') {
+       /* Always merge lines ending with backslash-newline,
+          even in middle of identifier.  */
+       ++ibp;
+       ++ip->lineno;
+       --obp;          /* remove backslash from obuf */
+       break;
+      }
+      /* Otherwise, backslash suppresses specialness of following char,
+        so copy it here to prevent the switch from seeing it.
+        But first get any pending identifier processed.  */
+      if (ident_length > 0)
+       goto specialchar;
+      *obp++ = *ibp++;
+      break;
+
+    case '#':
+      /* If this is expanding a macro definition, don't recognize
+        preprocessor directives.  */
+      if (ip->macro != 0)
+       goto randomchar;
+      if (ident_length)
+       goto specialchar;
+
+      /* # keyword: a # must be first nonblank char on the line */
+      if (beg_of_line == 0)
+       goto randomchar;
+      {
+       U_CHAR *bp;
+
+       /* Scan from start of line, skipping whitespace, comments
+          and backslash-newlines, and see if we reach this #.
+          If not, this # is not special.  */
+       bp = beg_of_line;
+       while (1) {
+         if (is_hor_space[*bp])
+           bp++;
+         else if (*bp == '\\' && bp[1] == '\n')
+           bp += 2;
+         else if (*bp == '/' && (newline_fix (bp + 1), bp[1]) == '*') {
+           bp += 2;
+           while (!(*bp == '*' && (newline_fix (bp + 1), bp[1]) == '/'))
+             bp++;
+           bp += 1;
+         }
+         else break;
+       }
+       if (bp + 1 != ibp)
+         goto randomchar;
+      }
+
+      /* This # can start a directive.  */
+
+      --obp;           /* Don't copy the '#' */
+
+      ip->bufp = ibp;
+      op->bufp = obp;
+      if (! handle_directive (ip, op)) {
+#ifdef USE_C_ALLOCA
+       alloca (0);
+#endif
+       /* Not a known directive: treat it as ordinary text.
+          IP, OP, IBP, etc. have not been changed.  */
+       if (no_output && instack[indepth].fname) {
+         /* If not generating expanded output,
+            what we do with ordinary text is skip it.
+            Discard everything until next # directive.  */
+         skip_if_group (&instack[indepth], 1);
+         RECACHE;
+         beg_of_line = ibp;
+         break;
+       }
+       ++obp;          /* Copy the '#' after all */
+       goto randomchar;
+      }
+#ifdef USE_C_ALLOCA
+      alloca (0);
+#endif
+      /* A # directive has been successfully processed.  */
+      /* If not generating expanded output, ignore everything until
+        next # directive.  */
+      if (no_output && instack[indepth].fname)
+       skip_if_group (&instack[indepth], 1);
+      obp = op->bufp;
+      RECACHE;
+      beg_of_line = ibp;
+      break;
+
+    case '\"':                 /* skip quoted string */
+    case '\'':
+      /* A single quoted string is treated like a double -- some
+        programs (e.g., troff) are perverse this way */
+
+      if (ident_length)
+       goto specialchar;
+
+      start_line = ip->lineno;
+
+      /* Skip ahead to a matching quote.  */
+
+      while (1) {
+       if (ibp >= limit) {
+         if (ip->macro != 0) {
+           /* try harder: this string crosses a macro expansion boundary */
+           POPMACRO;
+           RECACHE;
+           continue;
+         }
+         break;
+       }
+       *obp++ = *ibp;
+       switch (*ibp++) {
+       case '\n':
+         ++ip->lineno;
+         ++op->lineno;
+         /* Traditionally, end of line ends a string constant with no error.
+            So exit the loop and record the new line.  */
+         beg_of_line = ibp;
+         goto while2end;
+
+       case '\\':
+         if (ibp >= limit)
+           break;
+         if (*ibp == '\n') {
+           /* Backslash newline is replaced by nothing at all,
+              but keep the line counts correct.  */
+           --obp;
+           ++ibp;
+           ++ip->lineno;
+         } else {
+           /* ANSI stupidly requires that in \\ the second \
+              is *not* prevented from combining with a newline.  */
+           while (*ibp == '\\' && ibp[1] == '\n') {
+             ibp += 2;
+             ++ip->lineno;
+           }
+           *obp++ = *ibp++;
+         }
+         break;
+
+       case '\"':
+       case '\'':
+         if (ibp[-1] == c)
+           goto while2end;
+         break;
+       }
+      }
+    while2end:
+      break;
+
+    case '/':
+      if (*ibp == '\\' && ibp[1] == '\n')
+       newline_fix (ibp);
+      /* Don't look for comments inside a macro definition.  */
+      if (ip->macro != 0)
+       goto randomchar;
+      /* A comment constitutes white space, so it can terminate an identifier.
+        Process the identifier, if any.  */
+      if (ident_length)
+       goto specialchar;
+
+      if (*ibp != '*')
+       goto randomchar;
+
+      /* We have a comment.  Skip it, optionally copying it to output.  */
+
+      start_line = ip->lineno;
+
+      ++ibp;                   /* Skip the star. */
+
+      /* In K+R C, a comment is equivalent to nothing.  Note that we
+         already output the slash; we might not want it.  */
+      if (! put_out_comments)
+       obp--;
+      else
+       *obp++ = '*';
+
+      {
+       U_CHAR *before_bp = ibp;
+
+       while (ibp < limit) {
+         switch (*ibp++) {
+         case '/':
+           if (warn_comments && ibp < limit && *ibp == '*')
+             warning("`/*' within comment");
+           break;
+         case '*':
+           if (*ibp == '\\' && ibp[1] == '\n')
+             newline_fix (ibp);
+           if (ibp >= limit || *ibp == '/')
+             goto comment_end;
+           break;
+         case '\n':
+           ++ip->lineno;
+           /* Copy the newline into the output buffer, in order to
+              avoid the pain of a #line every time a multiline comment
+              is seen.  */
+           if (!put_out_comments)
+             *obp++ = '\n';
+           ++op->lineno;
+         }
+       }
+      comment_end:
+
+       if (ibp >= limit)
+         error_with_line (line_for_error (start_line),
+                          "unterminated comment");
+       else {
+         ibp++;
+         if (put_out_comments) {
+           memcpy (obp, before_bp, ibp - before_bp);
+           obp += ibp - before_bp;
+         }
+       }
+      }
+      break;
+
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+      /* If digit is not part of identifier, it starts a number,
+        which means that following letters are not an identifier.
+        "0x5" does not refer to an identifier "x5".
+        So copy all alphanumerics that follow without accumulating
+        as an identifier.  Periods also, for sake of "3.e7".  */
+
+      if (ident_length == 0) {
+       while (ibp < limit) {
+         while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
+           ++ip->lineno;
+           ibp += 2;
+         }
+         c = *ibp++;
+         if (!isalnum (c) && c != '.' && c != '_') {
+           --ibp;
+           break;
+         }
+         *obp++ = c;
+         /* A sign can be part of a preprocessing number
+            if it follows an e.  */
+         if (c == 'e' || c == 'E') {
+           while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
+             ++ip->lineno;
+             ibp += 2;
+           }
+           if (ibp < limit && (*ibp == '+' || *ibp == '-')) {
+             *obp++ = *ibp++;
+             /* Traditional C does not let the token go past the sign.  */
+             break;
+           }
+         }
+       }
+       break;
+      }
+      /* fall through */
+
+    case '_':
+    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+    case 'y': case 'z':
+    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+    case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+    case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+    case 'Y': case 'Z':
+      ident_length++;
+      /* Compute step of hash function, to avoid a proc call on every token */
+      hash = HASHSTEP (hash, c);
+      break;
+
+    case '\n':
+      /* If reprocessing a macro expansion, newline is a special marker.  */
+      if (ip->macro != 0) {
+       /* Newline White is a "funny space" to separate tokens that are
+          supposed to be separate but without space between.
+          Here White means any horizontal whitespace character.
+          Newline - marks a recursive macro use that is not
+          supposed to be expandable.  */
+
+       if (*ibp == '-') {
+         /* Newline - inhibits expansion of preceding token.
+            If expanding a macro arg, we keep the newline -.
+            In final output, it is deleted.  */
+         if (! concatenated) {
+           ident_length = 0;
+           hash = 0;
+         }
+         ibp++;
+         if (!output_marks) {
+           obp--;
+         } else {
+           /* If expanding a macro arg, keep the newline -.  */
+           *obp++ = '-';
+         }
+       } else if (is_space[*ibp]) {
+         /* Newline Space does not prevent expansion of preceding token
+            so expand the preceding token and then come back.  */
+         if (ident_length > 0)
+           goto specialchar;
+
+         /* If generating final output, newline space makes a space.  */
+         if (!output_marks) {
+           obp[-1] = *ibp++;
+           /* And Newline Newline makes a newline, so count it.  */
+           if (obp[-1] == '\n')
+             op->lineno++;
+         } else {
+           /* If expanding a macro arg, keep the newline space.
+              If the arg gets stringified, newline space makes nothing.  */
+           *obp++ = *ibp++;
+         }
+       } else abort ();        /* Newline followed by something random?  */
+       break;
+      }
+
+      /* If there is a pending identifier, handle it and come back here.  */
+      if (ident_length > 0)
+       goto specialchar;
+
+      beg_of_line = ibp;
+
+      /* Update the line counts and output a #line if necessary.  */
+      ++ip->lineno;
+      ++op->lineno;
+      if (ip->lineno != op->lineno) {
+       op->bufp = obp;
+       output_line_command (ip, op, 1, same_file);
+       check_expand (op, ip->length - (ip->bufp - ip->buf));
+       obp = op->bufp;
+      }
+      break;
+
+      /* Come here either after (1) a null character that is part of the input
+        or (2) at the end of the input, because there is a null there.  */
+    case 0:
+      if (ibp <= limit)
+       /* Our input really contains a null character.  */
+       goto randomchar;
+
+      /* At end of a macro-expansion level, pop it and read next level.  */
+      if (ip->macro != 0) {
+       obp--;
+       ibp--;
+       /* If we have an identifier that ends here, process it now, so
+          we get the right error for recursion.  */
+       if (ident_length && ! is_idchar[*instack[indepth - 1].bufp]) {
+         redo_char = 1;
+         goto randomchar;
+       }
+       POPMACRO;
+       RECACHE;
+       break;
+      }
+
+      /* If we don't have a pending identifier,
+        return at end of input.  */
+      if (ident_length == 0) {
+       obp--;
+       ibp--;
+       op->bufp = obp;
+       ip->bufp = ibp;
+       goto ending;
+      }
+
+      /* If we do have a pending identifier, just consider this null
+        a special character and arrange to dispatch on it again.
+        The second time, IDENT_LENGTH will be zero so we will return.  */
+
+      /* Fall through */
+
+specialchar:
+
+      /* Handle the case of a character such as /, ', " or null
+        seen following an identifier.  Back over it so that
+        after the identifier is processed the special char
+        will be dispatched on again.  */
+
+      ibp--;
+      obp--;
+      redo_char = 1;
+
+    default:
+
+randomchar:
+
+      if (ident_length > 0) {
+       register HASHNODE *hp;
+
+       /* We have just seen an identifier end.  If it's a macro, expand it.
+
+          IDENT_LENGTH is the length of the identifier
+          and HASH is its hash code.
+
+          The identifier has already been copied to the output,
+          so if it is a macro we must remove it.
+
+          If REDO_CHAR is 0, the char that terminated the identifier
+          has been skipped in the output and the input.
+          OBP-IDENT_LENGTH-1 points to the identifier.
+          If the identifier is a macro, we must back over the terminator.
+
+          If REDO_CHAR is 1, the terminating char has already been
+          backed over.  OBP-IDENT_LENGTH points to the identifier.  */
+
+       for (hp = hashtab[MAKE_POS (hash) % HASHSIZE]; hp != NULL;
+            hp = hp->next) {
+
+         if (hp->length == ident_length) {
+           U_CHAR *obufp_before_macroname;
+           int op_lineno_before_macroname;
+           register int i = ident_length;
+           register U_CHAR *p = hp->name;
+           register U_CHAR *q = obp - i;
+
+           if (! redo_char)
+             q--;
+
+           do {                /* All this to avoid a strncmp () */
+             if (*p++ != *q++)
+               goto hashcollision;
+           } while (--i);
+
+           /* We found a use of a macro name.
+              see if the context shows it is a macro call.  */
+
+           /* Back up over terminating character if not already done.  */
+           if (! redo_char) {
+             ibp--;
+             obp--;
+           }
+
+           obufp_before_macroname = obp - ident_length;
+           op_lineno_before_macroname = op->lineno;
+
+           /* If macro wants an arglist, verify that a '(' follows.
+              first skip all whitespace, copying it to the output
+              after the macro name.  Then, if there is no '(',
+              decide this is not a macro call and leave things that way.  */
+           if (hp->type == T_MACRO && hp->value.defn->nargs >= 0)
+             {
+               while (1) {
+                 /* Scan forward over whitespace, copying it to the output.  */
+                 if (ibp == limit && ip->macro != 0) {
+                   POPMACRO;
+                   RECACHE;
+                 }
+                 /* A comment: copy it unchanged or discard it.  */
+                 else if (*ibp == '/' && ibp+1 != limit && ibp[1] == '*') {
+                   if (put_out_comments) {
+                     *obp++ = '/';
+                     *obp++ = '*';
+                   }
+                   ibp += 2;
+                   while (ibp + 1 != limit
+                          && !(ibp[0] == '*' && ibp[1] == '/')) {
+                     /* We need not worry about newline-marks,
+                        since they are never found in comments.  */
+                     if (*ibp == '\n') {
+                       /* Newline in a file.  Count it.  */
+                       ++ip->lineno;
+                       ++op->lineno;
+                     }
+                     if (put_out_comments)
+                       *obp++ = *ibp++;
+                     else
+                       ibp++;
+                   }
+                   ibp += 2;
+                   if (put_out_comments) {
+                     *obp++ = '*';
+                     *obp++ = '/';
+                   }
+                 }
+                 else if (is_space[*ibp]) {
+                   *obp++ = *ibp++;
+                   if (ibp[-1] == '\n') {
+                     if (ip->macro == 0) {
+                       /* Newline in a file.  Count it.  */
+                       ++ip->lineno;
+                       ++op->lineno;
+                     } else if (!output_marks) {
+                       /* A newline mark, and we don't want marks
+                          in the output.  If it is newline-hyphen,
+                          discard it entirely.  Otherwise, it is
+                          newline-whitechar, so keep the whitechar.  */
+                       obp--;
+                       if (*ibp == '-')
+                         ibp++;
+                       else {
+                         if (*ibp == '\n')
+                           ++op->lineno;
+                         *obp++ = *ibp++;
+                       }
+                     } else {
+                       /* A newline mark; copy both chars to the output.  */
+                       *obp++ = *ibp++;
+                     }
+                   }
+                 }
+                 else break;
+               }
+               if (*ibp != '(')
+                 break;
+             }
+
+           /* This is now known to be a macro call.
+              Discard the macro name from the output,
+              along with any following whitespace just copied.  */
+           obp = obufp_before_macroname;
+           op->lineno = op_lineno_before_macroname;
+
+           /* Expand the macro, reading arguments as needed,
+              and push the expansion on the input stack.  */
+           ip->bufp = ibp;
+           op->bufp = obp;
+           macroexpand (hp, op);
+
+           /* Reexamine input stack, since macroexpand has pushed
+              a new level on it.  */
+           obp = op->bufp;
+           RECACHE;
+           break;
+         }
+hashcollision:
+              ;
+       }                       /* End hash-table-search loop */
+       ident_length = hash = 0; /* Stop collecting identifier */
+       redo_char = 0;
+       concatenated = 0;
+      }                                /* End if (ident_length > 0) */
+    }                          /* End switch */
+  }                            /* End per-char loop */
+
+  /* Come here to return -- but first give an error message
+     if there was an unterminated successful conditional.  */
+ ending:
+  if (if_stack != ip->if_stack) {
+    const char *str;
+    switch (if_stack->type) {
+    case T_IF:
+      str = "if";
+      break;
+    case T_IFDEF:
+      str = "ifdef";
+      break;
+    case T_IFNDEF:
+      str = "ifndef";
+      break;
+    case T_ELSE:
+      str = "else";
+      break;
+    case T_ELIF:
+      str = "elif";
+      break;
+    default:
+      abort ();
+    }
+    error_with_line (line_for_error (if_stack->lineno),
+                    "unterminated #%s conditional", str);
+  }
+  if_stack = ip->if_stack;
+}
+\f
+/*
+ * Rescan a string into a temporary buffer and return the result
+ * as a FILE_BUF.  Note this function returns a struct, not a pointer.
+ *
+ * OUTPUT_MARKS nonzero means keep Newline markers found in the input
+ * and insert such markers when appropriate.  See `rescan' for details.
+ * OUTPUT_MARKS is 1 for macroexpanding a macro argument separately
+ * before substitution; it is 0 for other uses.
+ */
+FILE_BUF
+expand_to_temp_buffer (buf, limit, output_marks)
+     U_CHAR *buf, *limit;
+     int output_marks;
+{
+  register FILE_BUF *ip;
+  FILE_BUF obuf;
+  int length = limit - buf;
+  U_CHAR *buf1;
+  int odepth = indepth;
+
+  if (length < 0)
+    abort ();
+
+  /* Set up the input on the input stack.  */
+
+  buf1 = (U_CHAR *) alloca (length + 1);
+  {
+    register U_CHAR *p1 = buf;
+    register U_CHAR *p2 = buf1;
+
+    while (p1 != limit)
+      *p2++ = *p1++;
+  }
+  buf1[length] = 0;
+
+  /* Set up to receive the output.  */
+
+  obuf.length = length * 2 + 100; /* Usually enough.  Why be stingy?  */
+  obuf.bufp = obuf.buf = (U_CHAR *) xmalloc (obuf.length);
+  obuf.fname = 0;
+  obuf.macro = 0;
+  obuf.free_ptr = 0;
+
+  CHECK_DEPTH ({return obuf;});
+
+  ++indepth;
+
+  ip = &instack[indepth];
+  ip->fname = 0;
+  ip->macro = 0;
+  ip->free_ptr = 0;
+  ip->length = length;
+  ip->buf = ip->bufp = buf1;
+  ip->if_stack = if_stack;
+
+  ip->lineno = obuf.lineno = 1;
+
+  /* Scan the input, create the output.  */
+
+  rescan (&obuf, output_marks);
+
+  /* Pop input stack to original state.  */
+  --indepth;
+
+  if (indepth != odepth)
+    abort ();
+
+  /* Record the output.  */
+  obuf.length = obuf.bufp - obuf.buf;
+
+  return obuf;
+}
+\f
+/*
+ * Process a # directive.  Expects IP->bufp to point to the '#', as in
+ * `#define foo bar'.  Passes to the command handler
+ * (do_define, do_include, etc.): the addresses of the 1st and
+ * last chars of the command (starting immediately after the #
+ * keyword), plus op and the keyword table pointer.  If the command
+ * contains comments it is copied into a temporary buffer sans comments
+ * and the temporary buffer is passed to the command handler instead.
+ * Likewise for backslash-newlines.
+ *
+ * Returns nonzero if this was a known # directive.
+ * Otherwise, returns zero, without advancing the input pointer.
+ */
+
+int
+handle_directive (ip, op)
+     FILE_BUF *ip, *op;
+{
+  register U_CHAR *bp, *cp;
+  register struct directive *kt;
+  register int ident_length;
+  U_CHAR *resume_p;
+
+  /* Nonzero means we must copy the entire command
+     to get rid of comments or backslash-newlines.  */
+  int copy_command = 0;
+
+  U_CHAR *ident, *after_ident;
+
+  bp = ip->bufp;
+  /* Skip whitespace and \-newline.  */
+  while (1) {
+    if (is_hor_space[*bp])
+      bp++;
+    else if (*bp == '/' && (newline_fix (bp + 1), bp[1]) == '*') {
+      ip->bufp = bp;
+      skip_to_end_of_comment (ip, &ip->lineno);
+      bp = ip->bufp;
+    } else if (*bp == '\\' && bp[1] == '\n') {
+      bp += 2; ip->lineno++;
+    } else break;
+  }
+
+  /* Now find end of directive name.
+     If we encounter a backslash-newline, exchange it with any following
+     symbol-constituents so that we end up with a contiguous name.  */
+
+  cp = bp;
+  while (1) {
+    if (is_idchar[*cp])
+      cp++;
+    else {
+      if (*cp == '\\' && cp[1] == '\n')
+       name_newline_fix (cp);
+      if (is_idchar[*cp])
+       cp++;
+      else break;
+    }
+  }
+  ident_length = cp - bp;
+  ident = bp;
+  after_ident = cp;
+
+  /* A line of just `#' becomes blank.  */
+
+  if (ident_length == 0 && *after_ident == '\n') {
+    ip->bufp = after_ident;
+    return 1;
+  }
+
+  /*
+   * Decode the keyword and call the appropriate expansion
+   * routine, after moving the input pointer up to the next line.
+   */
+  for (kt = directive_table; kt->length > 0; kt++) {
+    if (kt->length == ident_length
+       && !strncmp (kt->name, (char *)ident, ident_length)) {
+      register U_CHAR *buf;
+      register U_CHAR *limit = ip->buf + ip->length;
+      int unterminated = 0;
+
+      /* Nonzero means do not delete comments within the directive.
+        #define needs this to detect traditional token paste.  */
+      int keep_comments = kt->type == T_DEFINE;
+
+      /* Find the end of this command (first newline not backslashed
+        and not in a string or comment).
+        Set COPY_COMMAND if the command must be copied
+        (it contains a backslash-newline or a comment).  */
+
+      buf = bp = after_ident;
+      while (bp < limit) {
+       register U_CHAR c = *bp++;
+       switch (c) {
+       case '\\':
+         if (bp < limit) {
+           if (*bp == '\n') {
+             ip->lineno++;
+             copy_command = 1;
+           }
+           bp++;
+         }
+         break;
+
+       case '\'':
+       case '\"':
+         bp = skip_quoted_string (bp - 1, limit, ip->lineno, &ip->lineno, &copy_command, &unterminated);
+         if (unterminated) {
+           /* Traditional preprocessing permits unterminated strings.  */
+           ip->bufp = bp;
+           goto endloop1;
+         }
+         break;
+
+         /* <...> is special for #include.  */
+       case '<':
+         if (kt->type != T_INCLUDE)
+           break;
+         while (*bp && *bp != '>') bp++;
+         break;
+
+       case '/':
+         if (*bp == '\\' && bp[1] == '\n')
+           newline_fix (bp);
+         if (*bp == '*') {
+           U_CHAR *obp = bp - 1;
+           ip->bufp = bp + 1;
+           skip_to_end_of_comment (ip, &ip->lineno);
+           bp = ip->bufp;
+           /* No need to copy the command because of a comment at the end;
+              just don't include the comment in the directive.  */
+           if (bp == limit || *bp == '\n') {
+             bp = obp;
+             goto endloop1;
+           }
+           /* Don't remove the comments if this is #define.  */
+           if (! keep_comments)
+             copy_command++;
+         }
+         break;
+
+       case '\n':
+         --bp;         /* Point to the newline */
+         ip->bufp = bp;
+         goto endloop1;
+       }
+      }
+      ip->bufp = bp;
+
+    endloop1:
+      resume_p = ip->bufp;
+      /* BP is the end of the directive.
+        RESUME_P is the next interesting data after the directive.
+        A comment may come between.  */
+
+      if (copy_command) {
+       register U_CHAR *xp = buf;
+       /* Need to copy entire command into temp buffer before dispatching */
+
+       cp = (U_CHAR *) alloca (bp - buf + 5); /* room for cmd plus
+                                                 some slop */
+       buf = cp;
+
+       /* Copy to the new buffer, deleting comments
+          and backslash-newlines (and whitespace surrounding the latter).  */
+
+       while (xp < bp) {
+         register U_CHAR c = *xp++;
+         *cp++ = c;
+
+         switch (c) {
+         case '\n':
+           break;
+
+           /* <...> is special for #include.  */
+         case '<':
+           if (kt->type != T_INCLUDE)
+             break;
+           while (xp < bp && c != '>') {
+             c = *xp++;
+             if (c == '\\' && xp < bp && *xp == '\n')
+               xp++, ip->lineno++;
+             else
+               *cp++ = c;
+           }
+           break;
+
+         case '\\':
+           if (*xp == '\n') {
+             xp++;
+             cp--;
+             if (cp != buf && is_space[cp[-1]]) {
+               while (cp != buf && is_space[cp[-1]]) cp--;
+               cp++;
+               SKIP_WHITE_SPACE (xp);
+             } else if (is_space[*xp]) {
+               *cp++ = *xp++;
+               SKIP_WHITE_SPACE (xp);
+             }
+           } else {
+             *cp++ = *xp++;
+           }
+           break;
+
+         case '\'':
+         case '\"':
+           {
+             register U_CHAR *bp1
+               = skip_quoted_string (xp - 1, limit, ip->lineno, 0, 0, 0);
+             while (xp != bp1)
+               *cp++ = *xp++;
+           }
+           break;
+
+         case '/':
+           if (*xp == '*') {
+             ip->bufp = xp + 1;
+             skip_to_end_of_comment (ip, 0);
+             if (keep_comments)
+               while (xp != ip->bufp)
+                 *cp++ = *xp++;
+             /* Delete the slash.  */
+             else
+               cp--;
+             xp = ip->bufp;
+           }
+         }
+       }
+
+       /* Null-terminate the copy.  */
+
+       *cp = 0;
+      }
+      else
+       cp = bp;
+
+      ip->bufp = resume_p;
+
+      /* Call the appropriate command handler.  buf now points to
+        either the appropriate place in the input buffer, or to
+        the temp buffer if it was necessary to make one.  cp
+        points to the first char after the contents of the (possibly
+        copied) command, in either case. */
+      (*kt->func) (buf, cp, op, kt);
+      check_expand (op, ip->length - (ip->bufp - ip->buf));
+
+      return 1;
+    }
+  }
+
+  return 0;
+}
+\f
+static const char *const
+monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+/*
+ * expand things like __FILE__.  Place the expansion into the output
+ * buffer *without* rescanning.
+ */
+void
+special_symbol (hp, op)
+     HASHNODE *hp;
+     FILE_BUF *op;
+{
+  char *buf = 0;
+  time_t t;
+  int i, len;
+  int true_indepth;
+  FILE_BUF *ip = NULL;
+  static struct tm *timebuf = NULL;
+
+  int paren = 0;               /* For special `defined' keyword */
+
+  for (i = indepth; i >= 0; i--)
+    if (instack[i].fname != NULL) {
+      ip = &instack[i];
+      break;
+    }
+  if (ip == NULL)
+    fatal ("not in any file?!");
+
+  switch (hp->type) {
+  case T_FILE:
+  case T_BASE_FILE:
+    {
+      const char *string;
+      if (hp->type == T_FILE)
+       string = ip->fname;
+      else
+       string = instack[0].fname;
+
+      if (string)
+       {
+         buf = (char *) alloca (3 + strlen (string));
+         sprintf (buf, "\"%s\"", string);
+       }
+      else
+       strcpy (buf, "\"\"");
+
+      break;
+    }
+
+  case T_INCLUDE_LEVEL:
+    true_indepth = 0;
+    for (i = indepth; i >= 0; i--)
+      if (instack[i].fname != NULL)
+        true_indepth++;
+
+    buf = (char *) alloca (8); /* Eigth bytes ought to be more than enough */
+    sprintf (buf, "%d", true_indepth - 1);
+    break;
+
+  case T_VERSION:
+    buf = (char *) alloca (3 + strlen (version_string));
+    sprintf (buf, "\"%s\"", version_string);
+    break;
+
+  case T_CONST:
+    buf = (char *) hp->value.cpval;
+    break;
+
+  case T_SPECLINE:
+    buf = (char *) alloca (10);
+    sprintf (buf, "%d", ip->lineno);
+    break;
+
+  case T_DATE:
+  case T_TIME:
+    if (timebuf == NULL) {
+      t = time (0);
+      timebuf = localtime (&t);
+    }
+    buf = (char *) alloca (20);
+    if (hp->type == T_DATE)
+      sprintf (buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
+             timebuf->tm_mday, timebuf->tm_year + 1900);
+    else
+      sprintf (buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
+             timebuf->tm_sec);
+    break;
+
+  case T_SPEC_DEFINED:
+    buf = (char *) " 0 ";              /* Assume symbol is not defined */
+    ip = &instack[indepth];
+    SKIP_WHITE_SPACE (ip->bufp);
+    if (*ip->bufp == '(') {
+      paren++;
+      ip->bufp++;                      /* Skip over the paren */
+      SKIP_WHITE_SPACE (ip->bufp);
+    }
+
+    if (!is_idstart[*ip->bufp])
+      goto oops;
+    if (lookup (ip->bufp, -1, -1))
+      buf = (char *) " 1 ";
+    while (is_idchar[*ip->bufp])
+      ++ip->bufp;
+    SKIP_WHITE_SPACE (ip->bufp);
+    if (paren) {
+      if (*ip->bufp != ')')
+       goto oops;
+      ++ip->bufp;
+    }
+    break;
+
+oops:
+
+    error ("`defined' must be followed by ident or (ident)");
+    break;
+
+  default:
+    error ("cccp error: invalid special hash type"); /* time for gdb */
+    abort ();
+  }
+  len = strlen (buf);
+  check_expand (op, len);
+  memcpy (op->bufp, buf, len);
+  op->bufp += len;
+}
+
+\f
+/* Routines to handle #directives */
+
+/*
+ * Process include file by reading it in and calling rescan.
+ * Expects to see "fname" or <fname> on the input.
+ */
+void
+do_include (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  char *fname;         /* Dynamically allocated fname buffer */
+  U_CHAR *fbeg, *fend;         /* Beginning and end of fname */
+
+  struct file_name_list *stackp = include; /* Chain of dirs to search */
+  struct file_name_list dsp[1];        /* First in chain, if #include "..." */
+  int flen;
+
+  int f;                       /* file number */
+
+  int retried = 0;             /* Have already tried macro
+                                  expanding the include line*/
+  FILE_BUF trybuf;             /* It got expanded into here */
+  int system_header_p = 0;     /* 0 for "...", 1 for <...> */
+
+  f= -1;                       /* JF we iz paranoid! */
+
+get_filename:
+
+  fbeg = buf;
+  SKIP_WHITE_SPACE (fbeg);
+  /* Discard trailing whitespace so we can easily see
+     if we have parsed all the significant chars we were given.  */
+  while (limit != fbeg && is_hor_space[limit[-1]]) limit--;
+
+  switch (*fbeg++) {
+  case '\"':
+    fend = fbeg;
+    while (fend != limit && *fend != '\"')
+      fend++;
+    if (*fend == '\"' && fend + 1 == limit) {
+      FILE_BUF *fp;
+
+      /* We have "filename".  Figure out directory this source
+        file is coming from and put it on the front of the list. */
+
+      /* If -I- was specified, don't search current dir, only spec'd ones. */
+      if (ignore_srcdir) break;
+
+      for (fp = &instack[indepth]; fp >= instack; fp--)
+       {
+         size_t n;
+         const char *ep, *nam;
+
+         if ((nam = fp->fname) != NULL) {
+           /* Found a named file.  Figure out dir of the file,
+              and put it in front of the search list.  */
+           dsp[0].next = stackp;
+           stackp = dsp;
+           ep = strrchr (nam, '/');
+           if (ep != NULL) {
+             char *f; 
+             n = ep - nam;
+             f = (char *) alloca (n + 1);
+             strncpy (f, nam, n);
+             f[n] = '\0';
+             dsp[0].fname = f;
+             if (n > max_include_len) max_include_len = n;
+           } else {
+             dsp[0].fname = 0; /* Current directory */
+           }
+           break;
+         }
+       }
+      break;
+    }
+    goto fail;
+
+  case '<':
+    fend = fbeg;
+    while (fend != limit && *fend != '>') fend++;
+    if (*fend == '>' && fend + 1 == limit) {
+      system_header_p = 1;
+      /* If -I-, start with the first -I dir after the -I-.  */
+      if (first_bracket_include)
+       stackp = first_bracket_include;
+      break;
+    }
+    goto fail;
+
+  default:
+  fail:
+    if (retried) {
+      error ("#include expects \"fname\" or <fname>");
+      return;
+    } else {
+      trybuf = expand_to_temp_buffer (buf, limit, 0);
+      buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1);
+      memcpy (buf, trybuf.buf, trybuf.bufp - trybuf.buf);
+      limit = buf + (trybuf.bufp - trybuf.buf);
+      free (trybuf.buf);
+      retried++;
+      goto get_filename;
+    }
+  }
+
+  flen = fend - fbeg;
+  fname = (char *) alloca (max_include_len + flen + 2);
+  /* + 2 above for slash and terminating null.  */
+
+  /* If specified file name is absolute, just open it.  */
+
+  if (*fbeg == '/') {
+    strncpy (fname, (char *)fbeg, flen);
+    fname[flen] = 0;
+    f = open (fname, O_RDONLY, 0666);
+  } else {
+    /* Search directory path, trying to open the file.
+       Copy each filename tried into FNAME.  */
+
+    for (; stackp; stackp = stackp->next) {
+      if (stackp->fname) {
+       strcpy (fname, stackp->fname);
+       strcat (fname, "/");
+       fname[strlen (fname) + flen] = 0;
+      } else {
+       fname[0] = 0;
+      }
+      strncat (fname, (char *)fbeg, flen);
+      if ((f = open (fname, O_RDONLY, 0666)) >= 0)
+       break;
+    }
+  }
+
+  if (f < 0) {
+    strncpy (fname, (char *)fbeg, flen);
+    fname[flen] = 0;
+    error_from_errno (fname);
+
+    /* For -M, add this file to the dependencies.  */
+    if (print_deps > (system_header_p || (system_include_depth > 0))) {
+      if (system_header_p)
+       warning ("nonexistent file <%.*s> omitted from dependency output",
+                fend - fbeg, fbeg);
+      else
+       {
+         deps_output ((const char *)fbeg, fend - fbeg);
+         deps_output (" ", 0);
+       }
+    }
+  } else {
+
+    /* Check to see if this include file is a once-only include file.
+       If so, give up.  */
+
+    struct file_name_list* ptr;
+
+    for (ptr = dont_repeat_files; ptr; ptr = ptr->next) {
+      if (!strcmp (ptr->fname, fname)) {
+       close (f);
+        return;                                /* This file was once'd. */
+      }
+    }
+
+    for (ptr = all_include_files; ptr; ptr = ptr->next) {
+      if (!strcmp (ptr->fname, fname))
+        break;                         /* This file was included before. */
+    }
+
+    if (ptr == 0) {
+      /* This is the first time for this file.  */
+      /* Add it to list of files included.  */
+
+      ptr = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
+      ptr->next = all_include_files;
+      all_include_files = ptr;
+      ptr->fname = xstrdup (fname);
+
+      /* For -M, add this file to the dependencies.  */
+      if (print_deps > (system_header_p || (system_include_depth > 0))) {
+       deps_output (fname, strlen (fname));
+       deps_output (" ", 0);
+      }
+    }   
+
+    if (system_header_p)
+      system_include_depth++;
+
+    /* Actually process the file.  */
+    finclude (f, fname, op);
+
+    if (system_header_p)
+      system_include_depth--;
+
+    close (f);
+  }
+}
+
+/* Process the contents of include file FNAME, already open on descriptor F,
+   with output to OP.  */
+
+void
+finclude (f, fname, op)
+     int f;
+     const char *fname;
+     FILE_BUF *op;
+{
+  int st_mode;
+  long st_size;
+  long i;
+  FILE_BUF *fp;                        /* For input stack frame */
+
+  CHECK_DEPTH (return;);
+
+  if (file_size_and_mode (f, &st_mode, &st_size))
+    goto nope;
+
+  fp = &instack[indepth + 1];
+  memset (fp, 0, sizeof (FILE_BUF));
+  fp->fname = fname;
+  fp->length = 0;
+  fp->lineno = 1;
+  fp->if_stack = if_stack;
+
+  if (S_ISREG (st_mode)) {
+    fp->buf = (U_CHAR *) xmalloc (st_size + 2);
+    fp->bufp = fp->buf;
+
+    /* Read the file contents, knowing that st_size is an upper bound
+       on the number of bytes we can read.  */
+    while (st_size > 0) {
+      i = read (f, fp->buf + fp->length, st_size);
+      if (i <= 0) {
+       if (i == 0) break;
+       goto nope;
+      }
+      fp->length += i;
+      st_size -= i;
+    }
+  }
+  else {
+    /* Cannot count its file size before reading.  */
+
+    U_CHAR *bufp;
+    U_CHAR *basep;
+    int bsize = 2000;
+
+    st_size = 0;
+    basep = (U_CHAR *) xmalloc (bsize + 2);
+    bufp = basep;
+
+    for (;;) {
+      i = read (f, bufp, bsize - st_size);
+      if (i < 0)
+       goto nope;      /* error! */
+      if (i == 0)
+       break;  /* End of file */
+      st_size += i;
+      bufp += i;
+      if (bsize == st_size) {  /* Buffer is full! */
+         bsize *= 2;
+         basep = (U_CHAR *) xrealloc (basep, bsize + 2);
+         bufp = basep + st_size;       /* May have moved */
+       }
+    }
+    fp->buf = basep;
+    fp->bufp = fp->buf;
+    fp->length = st_size;
+  }
+  close (f);
+
+  /* Make sure data ends with a newline.  And put a null after it.  */
+
+  if (fp->length > 0 && fp->buf[fp->length-1] != '\n')
+    fp->buf[fp->length++] = '\n';
+  fp->buf[fp->length] = '\0';
+
+  indepth++;
+  output_line_command (fp, op, 0, enter_file);
+  rescan (op, 0);
+  indepth--;
+  output_line_command (&instack[indepth], op, 0, leave_file);
+  free (fp->buf);
+  return;
+
+nope:
+  perror_with_name (fname);
+  close (f);
+}
+\f
+
+/* Process a #define command.
+BUF points to the contents of the #define command, as a continguous string.
+LIMIT points to the first character past the end of the definition.
+KEYWORD is the keyword-table entry for #define.  */
+
+void
+do_define (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  U_CHAR *bp;                  /* temp ptr into input buffer */
+  U_CHAR *symname;             /* remember where symbol name starts */
+  int sym_length;              /* and how long it is */
+
+  DEFINITION *defn;
+  int arglengths = 0;          /* Accumulate lengths of arg names
+                                  plus number of args.  */
+  int hashcode;
+
+  bp = buf;
+
+  while (is_hor_space[*bp])
+    bp++;
+
+  symname = bp;                        /* remember where it starts */
+  while (is_idchar[*bp] && bp < limit) {
+    bp++;
+  }
+  sym_length = bp - symname;
+  if (sym_length == 0)
+    error ("invalid macro name");
+  else if (!is_idstart[*symname]) {
+    U_CHAR *msg;                       /* what pain... */
+    msg = (U_CHAR *) alloca (sym_length + 1);
+    memcpy (msg, symname, sym_length);
+    msg[sym_length] = 0;
+    error ("invalid macro name `%s'", msg);
+  } else {
+    if (! strncmp ((char *)symname, "defined", 7) && sym_length == 7)
+      error ("defining `defined' as a macro");
+  }
+
+  /* lossage will occur if identifiers or control keywords are broken
+     across lines using backslash.  This is not the right place to take
+     care of that. */
+
+  if (*bp == '(') {
+    struct arglist *arg_ptrs = NULL;
+    int argno = 0;
+
+    bp++;                      /* skip '(' */
+    SKIP_WHITE_SPACE (bp);
+
+    /* Loop over macro argument names.  */
+    while (*bp != ')') {
+      struct arglist *temp;
+
+      temp = (struct arglist *) alloca (sizeof (struct arglist));
+      temp->name = bp;
+      temp->next = arg_ptrs;
+      temp->argno = argno++;
+      arg_ptrs = temp;
+
+      if (!is_idstart[*bp])
+       warning ("parameter name starts with a digit in #define");
+
+      /* Find the end of the arg name.  */
+      while (is_idchar[*bp]) {
+       bp++;
+      }
+      temp->length = bp - temp->name;
+      arglengths += temp->length + 2;
+      SKIP_WHITE_SPACE (bp);
+      if (temp->length == 0 || (*bp != ',' && *bp != ')')) {
+       error ("badly punctuated parameter list in #define");
+       return;
+      }
+      if (*bp == ',') {
+       bp++;
+       SKIP_WHITE_SPACE (bp);
+      }
+      if (bp >= limit) {
+       error ("unterminated parameter list in #define");
+       return;
+      }
+    }
+
+    ++bp;                      /* skip paren */
+    /* Skip exactly one space or tab if any.  */
+    if (bp < limit && (*bp == ' ' || *bp == '\t')) ++bp;
+    /* now everything from bp before limit is the definition. */
+    defn = collect_expansion (bp, limit, argno, arg_ptrs);
+
+    /* Now set defn->argnames to the result of concatenating
+       the argument names in reverse order
+       with comma-space between them.  */
+    defn->argnames = (U_CHAR *) xmalloc (arglengths + 1);
+    {
+      struct arglist *temp;
+      int i = 0;
+      for (temp = arg_ptrs; temp; temp = temp->next) {
+       memcpy (&defn->argnames[i], temp->name, temp->length);
+       i += temp->length;
+       if (temp->next != 0) {
+         defn->argnames[i++] = ',';
+         defn->argnames[i++] = ' ';
+       }
+      }
+      defn->argnames[i] = 0;
+    }
+  } else {
+    /* simple expansion or empty definition; gobble it */
+    if (is_hor_space[*bp])
+      ++bp;            /* skip exactly one blank/tab char */
+    /* now everything from bp before limit is the definition. */
+    defn = collect_expansion (bp, limit, -1, 0);
+    defn->argnames = (U_CHAR *) "";
+  }
+
+  hashcode = hashf (symname, sym_length, HASHSIZE);
+
+  {
+    HASHNODE *hp;
+    if ((hp = lookup (symname, sym_length, hashcode)) == NULL)
+      hp = install (symname, sym_length, T_MACRO, hashcode);
+    else {
+      if (hp->type != T_MACRO || compare_defs (defn, hp->value.defn))
+       warning ("\"%.*s\" redefined", sym_length, symname);
+
+      /* Replace the old definition.  */
+      hp->type = T_MACRO;
+    }
+
+    hp->value.defn = defn;
+  }
+}
+
+/*
+ * return zero if two DEFINITIONs are isomorphic
+ */
+int
+compare_defs (d1, d2)
+     DEFINITION *d1, *d2;
+{
+  register struct reflist *a1, *a2;
+  register U_CHAR *p1 = d1->expansion;
+  register U_CHAR *p2 = d2->expansion;
+  int first = 1;
+
+  if (d1->nargs != d2->nargs)
+    return 1;
+  if (strcmp ((char *)d1->argnames, (char *)d2->argnames))
+    return 1;
+  for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
+       a1 = a1->next, a2 = a2->next) {
+    if (!((a1->nchars == a2->nchars
+          && ! strncmp ((char *)p1, (char *)p2, a1->nchars))
+         || ! comp_def_part (first, p1, a1->nchars, p2, a2->nchars, 0))
+       || a1->argno != a2->argno
+       || a1->stringify != a2->stringify
+       || a1->raw_before != a2->raw_before
+       || a1->raw_after != a2->raw_after)
+      return 1;
+    first = 0;
+    p1 += a1->nchars;
+    p2 += a2->nchars;
+  }
+  if (a1 != a2)
+    return 1;
+  if (comp_def_part (first, p1, d1->length - (p1 - d1->expansion),
+                    p2, d2->length - (p2 - d2->expansion), 1))
+    return 1;
+  return 0;
+}
+
+/* Return 1 if two parts of two macro definitions are effectively different.
+   One of the parts starts at BEG1 and has LEN1 chars;
+   the other has LEN2 chars at BEG2.
+   Any sequence of whitespace matches any other sequence of whitespace.
+   FIRST means these parts are the first of a macro definition;
+    so ignore leading whitespace entirely.
+   LAST means these parts are the last of a macro definition;
+    so ignore trailing whitespace entirely.  */
+int
+comp_def_part (first, beg1, len1, beg2, len2, last)
+     int first;
+     U_CHAR *beg1, *beg2;
+     int len1, len2;
+     int last;
+{
+  register U_CHAR *end1 = beg1 + len1;
+  register U_CHAR *end2 = beg2 + len2;
+  if (first) {
+    while (beg1 != end1 && is_space[*beg1]) beg1++;
+    while (beg2 != end2 && is_space[*beg2]) beg2++;
+  }
+  if (last) {
+    while (beg1 != end1 && is_space[end1[-1]]) end1--;
+    while (beg2 != end2 && is_space[end2[-1]]) end2--;
+  }
+  while (beg1 != end1 && beg2 != end2) {
+    if (is_space[*beg1] && is_space[*beg2]) {
+      while (beg1 != end1 && is_space[*beg1]) beg1++;
+      while (beg2 != end2 && is_space[*beg2]) beg2++;
+    } else if (*beg1 == *beg2) {
+      beg1++; beg2++;
+    } else break;
+  }
+  return (beg1 != end1) || (beg2 != end2);
+}
+
+/* Read a replacement list for a macro with parameters.
+   Build the DEFINITION structure.
+   Reads characters of text starting at BUF until LIMIT.
+   ARGLIST specifies the formal parameters to look for
+   in the text of the definition; NARGS is the number of args
+   in that list, or -1 for a macro name that wants no argument list.
+   MACRONAME is the macro name itself (so we can avoid recursive expansion)
+   and NAMELEN is its length in characters.
+   
+Note that comments and backslash-newlines have already been deleted
+from the argument.  */
+
+/* Leading and trailing Space, Tab, etc. are converted to markers
+   Newline Space, Newline Tab, etc.
+   Newline Space makes a space in the final output
+   but is discarded if stringified.  (Newline Tab is similar but
+   makes a Tab instead.)
+
+   If there is no trailing whitespace, a Newline Space is added at the end
+   to prevent concatenation that would be contrary to the standard.  */
+
+DEFINITION *
+collect_expansion (buf, end, nargs, arglist)
+     U_CHAR *buf, *end;
+     int nargs;
+     struct arglist *arglist;
+{
+  DEFINITION *defn;
+  register U_CHAR *p, *limit, *lastp, *exp_p;
+  struct reflist *endpat = NULL;
+  /* Pointer to first nonspace after last ## seen.  */
+  U_CHAR *concat = 0;
+  /* Pointer to first nonspace after last single-# seen.  */
+  U_CHAR *stringify = 0;
+  int maxsize;
+  int expected_delimiter = '\0';
+
+  /* Scan thru the replacement list, ignoring comments and quoted
+     strings, picking up on the macro calls.  It does a linear search
+     thru the arg list on every potential symbol.  Profiling might say
+     that something smarter should happen. */
+
+  if (end < buf)
+    abort ();
+
+  /* Find the beginning of the trailing whitespace.  */
+  /* Find end of leading whitespace.  */
+  limit = end;
+  p = buf;
+  while (p < limit && is_space[limit[-1]]) limit--;
+  while (p < limit && is_space[*p]) p++;
+
+  /* Allocate space for the text in the macro definition.
+     Leading and trailing whitespace chars need 2 bytes each.
+     Each other input char may or may not need 1 byte,
+     so this is an upper bound.
+     The extra 2 are for invented trailing newline-marker and final null.  */
+  maxsize = (sizeof (DEFINITION)
+            + 2 * (end - limit) + 2 * (p - buf)
+            + (limit - p) + 3);
+  defn = (DEFINITION *) xcalloc (1, maxsize);
+
+  defn->nargs = nargs;
+  exp_p = defn->expansion = (U_CHAR *) defn + sizeof (DEFINITION);
+  lastp = exp_p;
+
+  p = buf;
+
+  /* Convert leading whitespace to Newline-markers.  */
+  while (p < limit && is_space[*p]) {
+    *exp_p++ = '\n';
+    *exp_p++ = *p++;
+  }
+
+  /* Process the main body of the definition.  */
+  while (p < limit) {
+    int skipped_arg = 0;
+    register U_CHAR c = *p++;
+
+    *exp_p++ = c;
+
+    /* In -traditional mode, recognize arguments inside strings and
+       and character constants, and ignore special properties of #.
+       Arguments inside strings are considered "stringified", but no
+       extra quote marks are supplied.  */
+    switch (c) {
+    case '\'':
+    case '\"':
+      if (expected_delimiter != '\0') {
+       if (c == expected_delimiter)
+         expected_delimiter = '\0';
+      } else
+       expected_delimiter = c;
+      break;
+
+    case '\\':
+      /* Backslash quotes delimiters and itself, but not macro args.  */
+      if (expected_delimiter != 0 && p < limit
+         && (*p == expected_delimiter || *p == '\\')) {
+       *exp_p++ = *p++;
+       continue;
+      }
+      break;
+
+    case '/':
+      if (expected_delimiter != '\0') /* No comments inside strings.  */
+       break;
+      if (*p == '*') {
+       /* If we find a comment that wasn't removed by handle_directive,
+          this must be -traditional.  So replace the comment with
+          nothing at all.  */
+       exp_p--;
+       p += 1;
+       while (p < limit && !(p[-2] == '*' && p[-1] == '/'))
+         p++;
+      }
+      break;
+    }
+
+    if (is_idchar[c] && nargs > 0) {
+      U_CHAR *id_beg = p - 1;
+      int id_len;
+
+      --exp_p;
+      while (p != limit && is_idchar[*p]) p++;
+      id_len = p - id_beg;
+
+      if (is_idstart[c]) {
+       register struct arglist *arg;
+
+       for (arg = arglist; arg != NULL; arg = arg->next) {
+         struct reflist *tpat;
+
+         if (arg->name[0] == c
+             && arg->length == id_len
+             && strncmp ((char *)arg->name, (char *)id_beg, id_len) == 0) {
+           /* make a pat node for this arg and append it to the end of
+              the pat list */
+           tpat = (struct reflist *) xmalloc (sizeof (struct reflist));
+           tpat->next = NULL;
+           tpat->raw_before = concat == id_beg;
+           tpat->raw_after = 0;
+           tpat->stringify = expected_delimiter != '\0';
+
+           if (endpat == NULL)
+             defn->pattern = tpat;
+           else
+             endpat->next = tpat;
+           endpat = tpat;
+
+           tpat->argno = arg->argno;
+           tpat->nchars = exp_p - lastp;
+           {
+             register U_CHAR *p1 = p;
+             SKIP_WHITE_SPACE (p1);
+             if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#')
+               tpat->raw_after = 1;
+           }
+           lastp = exp_p;      /* place to start copying from next time */
+           skipped_arg = 1;
+           break;
+         }
+       }
+      }
+
+      /* If this was not a macro arg, copy it into the expansion.  */
+      if (! skipped_arg) {
+       register U_CHAR *lim1 = p;
+       p = id_beg;
+       while (p != lim1)
+         *exp_p++ = *p++;
+       if (stringify == id_beg)
+         error ("# operator should be followed by a macro argument name");
+      }
+    }
+  }
+
+  if (limit < end) {
+    /* Convert trailing whitespace to Newline-markers.  */
+    while (limit < end && is_space[*limit]) {
+      *exp_p++ = '\n';
+      *exp_p++ = *limit++;
+    }
+  }
+  *exp_p = '\0';
+
+  defn->length = exp_p - defn->expansion;
+
+  /* Crash now if we overrun the allocated size.  */
+  if (defn->length + 1 > maxsize)
+    abort ();
+
+  return defn;
+}
+\f
+/*
+ * interpret #line command.  Remembers previously seen fnames
+ * in its very own hash table.
+ */
+#define FNAME_HASHSIZE 37
+void
+do_line (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  register U_CHAR *bp;
+  FILE_BUF *ip = &instack[indepth];
+  FILE_BUF tem;
+  int new_lineno;
+  enum file_change_code file_change = same_file;
+
+  /* Expand any macros.  */
+  tem = expand_to_temp_buffer (buf, limit, 0);
+
+  /* Point to macroexpanded line, which is null-terminated now.  */
+  bp = tem.buf;
+  SKIP_WHITE_SPACE (bp);
+
+  if (!isdigit (*bp)) {
+    error ("invalid format #line command");
+    return;
+  }
+
+  /* The Newline at the end of this line remains to be processed.
+     To put the next line at the specified line number,
+     we must store a line number now that is one less.  */
+  new_lineno = atoi ((char *)bp) - 1;
+
+  /* skip over the line number.  */
+  while (isdigit (*bp))
+    bp++;
+
+#if 0 /* #line 10"foo.c" is supposed to be allowed.  */
+  if (*bp && !is_space[*bp]) {
+    error ("invalid format #line command");
+    return;
+  }
+#endif
+
+  SKIP_WHITE_SPACE (bp);
+
+  if (*bp == '\"') {
+    static HASHNODE *fname_table[FNAME_HASHSIZE];
+    HASHNODE *hp, **hash_bucket;
+    U_CHAR *fname;
+    int fname_length;
+
+    fname = ++bp;
+
+    while (*bp && *bp != '\"')
+      bp++;
+    if (*bp != '\"') {
+      error ("invalid format #line command");
+      return;
+    }
+
+    fname_length = bp - fname;
+
+    bp++;
+    SKIP_WHITE_SPACE (bp);
+    if (*bp) {
+      if (*bp == '1')
+       file_change = enter_file;
+      else if (*bp == '2')
+       file_change = leave_file;
+      else {
+       error ("invalid format #line command");
+       return;
+      }
+
+      bp++;
+      SKIP_WHITE_SPACE (bp);
+      if (*bp) {
+       error ("invalid format #line command");
+       return;
+      }
+    }
+
+    hash_bucket =
+      &fname_table[hashf (fname, fname_length, FNAME_HASHSIZE)];
+    for (hp = *hash_bucket; hp != NULL; hp = hp->next)
+      if (hp->length == fname_length &&
+         strncmp (hp->value.cpval, (char *)fname, fname_length) == 0) {
+       ip->fname = hp->value.cpval;
+       break;
+      }
+    if (hp == 0) {
+      char *q;
+      /* Didn't find it; cons up a new one.  */
+      hp = (HASHNODE *) xcalloc (1, sizeof (HASHNODE) + fname_length + 1);
+      hp->next = *hash_bucket;
+      *hash_bucket = hp;
+
+      hp->length = fname_length;
+      ip->fname = hp->value.cpval = q = ((char *) hp) + sizeof (HASHNODE);
+      memcpy (q, fname, fname_length);
+    }
+  } else if (*bp) {
+    error ("invalid format #line command");
+    return;
+  }
+
+  ip->lineno = new_lineno;
+  output_line_command (ip, op, 0, file_change);
+  check_expand (op, ip->length - (ip->bufp - ip->buf));
+}
+
+/*
+ * remove all definitions of symbol from symbol table.
+ * according to un*x /lib/cpp, it is not an error to undef
+ * something that has no definitions, so it isn't one here either.
+ */
+void
+do_undef (buf, limit, op, keyword)
+     U_CHAR *buf;
+     U_CHAR *limit ATTRIBUTE_UNUSED;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  HASHNODE *hp;
+
+  SKIP_WHITE_SPACE (buf);
+
+  if (! strncmp ((char *)buf, "defined", 7) && ! is_idchar[buf[7]])
+    warning ("undefining `defined'");
+
+  while ((hp = lookup (buf, -1, -1)) != NULL) {
+    if (hp->type != T_MACRO)
+      warning ("undefining `%s'", hp->name);
+    delete_macro (hp);
+  }
+}
+
+/*
+ * handle #if command by
+ *   1) inserting special `defined' keyword into the hash table
+ *     that gets turned into 0 or 1 by special_symbol (thus,
+ *     if the luser has a symbol called `defined' already, it won't
+ *      work inside the #if command)
+ *   2) rescan the input into a temporary output buffer
+ *   3) pass the output buffer to the yacc parser and collect a value
+ *   4) clean up the mess left from steps 1 and 2.
+ *   5) call conditional_skip to skip til the next #endif (etc.),
+ *      or not, depending on the value from step 3.
+ */
+void
+do_if (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  int value;
+  FILE_BUF *ip = &instack[indepth];
+
+  value = eval_if_expression (buf, limit - buf);
+  conditional_skip (ip, value == 0, T_IF);
+}
+
+/*
+ * handle a #elif directive by not changing  if_stack  either.
+ * see the comment above do_else.
+ */
+void
+do_elif (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  int value;
+  FILE_BUF *ip = &instack[indepth];
+
+  if (if_stack == instack[indepth].if_stack) {
+    error ("#elif not within a conditional");
+    return;
+  } else {
+    if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
+      error ("#elif after #else");
+      fprintf (stderr, " (matches line %d", if_stack->lineno);
+      if (if_stack->fname != NULL && ip->fname != NULL &&
+         strcmp (if_stack->fname, ip->fname) != 0)
+       fprintf (stderr, ", file %s", if_stack->fname);
+      fprintf (stderr, ")\n");
+    }
+    if_stack->type = T_ELIF;
+  }
+
+  if (if_stack->if_succeeded)
+    skip_if_group (ip, 0);
+  else {
+    value = eval_if_expression (buf, limit - buf);
+    if (value == 0)
+      skip_if_group (ip, 0);
+    else {
+      ++if_stack->if_succeeded;        /* continue processing input */
+      output_line_command (ip, op, 1, same_file);
+    }
+  }
+}
+
+/*
+ * evaluate a #if expression in BUF, of length LENGTH,
+ * then parse the result as a C expression and return the value as an int.
+ */
+int
+eval_if_expression (buf, length)
+     U_CHAR *buf;
+     int length;
+{
+  FILE_BUF temp_obuf;
+  HASHNODE *save_defined;
+  int value;
+
+  save_defined = install (U"defined", -1, T_SPEC_DEFINED, -1);
+  temp_obuf = expand_to_temp_buffer (buf, buf + length, 0);
+  delete_macro (save_defined); /* clean up special symbol */
+
+  value = parse_c_expression ((char *)temp_obuf.buf);
+
+  free (temp_obuf.buf);
+
+  return value;
+}
+
+/*
+ * routine to handle ifdef/ifndef.  Try to look up the symbol,
+ * then do or don't skip to the #endif/#else/#elif depending
+ * on what directive is actually being processed.
+ */
+void
+do_xifdef (buf, limit, op, keyword)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+     struct directive *keyword;
+{
+  int skip;
+  FILE_BUF *ip = &instack[indepth];
+  U_CHAR *end; 
+
+  /* Discard leading and trailing whitespace.  */
+  SKIP_WHITE_SPACE (buf);
+  while (limit != buf && is_hor_space[limit[-1]]) limit--;
+
+  /* Find the end of the identifier at the beginning.  */
+  for (end = buf; is_idchar[*end]; end++);
+
+  if (end == buf) {
+    skip = (keyword->type == T_IFDEF);
+  } else {
+    skip = (lookup (buf, end-buf, -1) == NULL) ^ (keyword->type == T_IFNDEF);
+  }
+
+  conditional_skip (ip, skip, T_IF);
+}
+
+/*
+ * push TYPE on stack; then, if SKIP is nonzero, skip ahead.
+ */
+void
+conditional_skip (ip, skip, type)
+     FILE_BUF *ip;
+     int skip;
+     enum node_type type;
+{
+  IF_STACK_FRAME *temp;
+
+  temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
+  temp->fname = ip->fname;
+  temp->lineno = ip->lineno;
+  temp->next = if_stack;
+  if_stack = temp;
+
+  if_stack->type = type;
+
+  if (skip != 0) {
+    skip_if_group (ip, 0);
+    return;
+  } else {
+    ++if_stack->if_succeeded;
+    output_line_command (ip, &outbuf, 1, same_file);
+  }
+}
+
+/*
+ * skip to #endif, #else, or #elif.  adjust line numbers, etc.
+ * leaves input ptr at the sharp sign found.
+ * If ANY is nonzero, return at next directive of any sort.
+ */
+void
+skip_if_group (ip, any)
+     FILE_BUF *ip;
+     int any;
+{
+  register U_CHAR *bp = ip->bufp, *cp;
+  register U_CHAR *endb = ip->buf + ip->length;
+  struct directive *kt;
+  IF_STACK_FRAME *save_if_stack = if_stack; /* don't pop past here */
+  U_CHAR *beg_of_line = bp;
+
+  while (bp < endb) {
+    switch (*bp++) {
+    case '/':                  /* possible comment */
+      if (*bp == '\\' && bp[1] == '\n')
+       newline_fix (bp);
+      if (*bp == '*') {
+       ip->bufp = ++bp;
+       bp = skip_to_end_of_comment (ip, &ip->lineno);
+      }
+      break;
+    case '\"':
+    case '\'':
+      bp = skip_quoted_string (bp - 1, endb, ip->lineno, &ip->lineno, 0, 0);
+      break;
+    case '\\':
+      /* Char after backslash loses its special meaning.  */
+      if (bp < endb) {
+       if (*bp == '\n')
+         ++ip->lineno;         /* But do update the line-count.  */
+       bp++;
+      }
+      break;
+    case '\n':
+      ++ip->lineno;
+      beg_of_line = bp;
+      break;
+    case '#':
+      ip->bufp = bp - 1;
+
+      /* # keyword: a # must be first nonblank char on the line */
+      if (beg_of_line == 0)
+       break;
+      /* Scan from start of line, skipping whitespace, comments
+        and backslash-newlines, and see if we reach this #.
+        If not, this # is not special.  */
+      bp = beg_of_line;
+      while (1) {
+       if (is_hor_space[*bp])
+         bp++;
+       else if (*bp == '\\' && bp[1] == '\n')
+         bp += 2;
+       else if (*bp == '/' && bp[1] == '*') {
+         bp += 2;
+         while (!(*bp == '*' && bp[1] == '/')) {
+           if (*bp == '\n')
+             ip->lineno++;
+           bp++;
+         }
+         bp += 2;
+       }
+       else break;
+      }
+      if (bp != ip->bufp) {
+       bp = ip->bufp + 1;      /* Reset bp to after the #.  */
+       break;
+      }
+
+      bp = ip->bufp + 1;       /* Point after '#'.  */
+
+      /* Skip whitespace and \-newline.  */
+      while (1) {
+       if (is_hor_space[*bp])
+         bp++;
+       else if (*bp == '\\' && bp[1] == '\n')
+         bp += 2;
+       else if (*bp == '/' && bp[1] == '*') {
+         bp += 2;
+         while (!(*bp == '*' && bp[1] == '/'))
+           bp++;
+         bp += 2;
+       }
+       else break;
+      }
+
+      cp = bp;
+
+      /* Now find end of directive name.
+        If we encounter a backslash-newline, exchange it with any following
+        symbol-constituents so that we end up with a contiguous name.  */
+
+      while (1) {
+       if (is_idchar[*bp])
+         bp++;
+       else {
+         if (*bp == '\\' && bp[1] == '\n')
+           name_newline_fix (bp);
+         if (is_idchar[*bp])
+           bp++;
+         else break;
+       }
+      }
+
+      for (kt = directive_table; kt->length >= 0; kt++) {
+       IF_STACK_FRAME *temp;
+       if (strncmp ((char *)cp, kt->name, kt->length) == 0
+           && !is_idchar[cp[kt->length]]) {
+
+         /* If we are asked to return on next directive,
+            do so now.  */
+         if (any)
+           return;
+
+         switch (kt->type) {
+         case T_IF:
+         case T_IFDEF:
+         case T_IFNDEF:
+           temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
+           temp->next = if_stack;
+           if_stack = temp;
+           temp->lineno = ip->lineno;
+           temp->fname = ip->fname;
+           temp->type = kt->type;
+           break;
+         case T_ELSE:
+         case T_ENDIF:
+         case T_ELIF:
+           if (if_stack == instack[indepth].if_stack) {
+             error ("#%s not within a conditional", kt->name);
+             break;
+           }
+           else if (if_stack == save_if_stack)
+             return;           /* found what we came for */
+
+           if (kt->type != T_ENDIF) {
+             if (if_stack->type == T_ELSE)
+               error ("#else or #elif after #else");
+             if_stack->type = kt->type;
+             break;
+           }
+
+           temp = if_stack;
+           if_stack = if_stack->next;
+           free (temp);
+           break;
+
+         default:
+           /* Anything else is ignored.  */
+           break;
+         }
+         break;
+       }
+      }
+    }
+  }
+  ip->bufp = bp;
+  /* after this returns, rescan will exit because ip->bufp
+     now points to the end of the buffer.
+     rescan is responsible for the error message also.  */
+}
+
+/*
+ * handle a #else directive.  Do this by just continuing processing
+ * without changing  if_stack ;  this is so that the error message
+ * for missing #endif's etc. will point to the original #if.  It
+ * is possible that something different would be better.
+ */
+void
+do_else (buf, limit, op, keyword)
+     U_CHAR *buf ATTRIBUTE_UNUSED;
+     U_CHAR *limit ATTRIBUTE_UNUSED;
+     FILE_BUF *op;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  FILE_BUF *ip = &instack[indepth];
+
+  if (if_stack == instack[indepth].if_stack) {
+    error ("#else not within a conditional");
+    return;
+  } else {
+    if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
+      error ("#else after #else");
+      fprintf (stderr, " (matches line %d", if_stack->lineno);
+      if (strcmp (if_stack->fname, ip->fname) != 0)
+       fprintf (stderr, ", file %s", if_stack->fname);
+      fprintf (stderr, ")\n");
+    }
+    if_stack->type = T_ELSE;
+  }
+
+  if (if_stack->if_succeeded)
+    skip_if_group (ip, 0);
+  else {
+    ++if_stack->if_succeeded;  /* continue processing input */
+    output_line_command (ip, op, 1, same_file);
+  }
+}
+
+/*
+ * unstack after #endif command
+ */
+void
+do_endif (buf, limit, op, keyword)
+     U_CHAR *buf ATTRIBUTE_UNUSED;
+     U_CHAR *limit ATTRIBUTE_UNUSED;
+     FILE_BUF *op;
+     struct directive *keyword ATTRIBUTE_UNUSED;
+{
+  if (if_stack == instack[indepth].if_stack)
+    error ("unbalanced #endif");
+  else {
+    IF_STACK_FRAME *temp = if_stack;
+    if_stack = if_stack->next;
+    free (temp);
+    output_line_command (&instack[indepth], op, 1, same_file);
+  }
+}
+
+/*
+ * Skip a comment, assuming the input ptr immediately follows the
+ * initial slash-star.  Bump line counter as necessary.
+ * (The canonical line counter is &ip->lineno).
+ * Don't use this routine (or the next one) if bumping the line
+ * counter is not sufficient to deal with newlines in the string.
+ */
+U_CHAR *
+skip_to_end_of_comment (ip, line_counter)
+     register FILE_BUF *ip;
+     int *line_counter;                /* place to remember newlines, or NULL */
+{
+  register U_CHAR *limit = ip->buf + ip->length;
+  register U_CHAR *bp = ip->bufp;
+  FILE_BUF *op = &outbuf;      /* JF */
+  int output = put_out_comments && !line_counter;
+
+       /* JF this line_counter stuff is a crock to make sure the
+          comment is only put out once, no matter how many times
+          the comment is skipped.  It almost works */
+  if (output) {
+    *op->bufp++ = '/';
+    *op->bufp++ = '*';
+  }
+  while (bp < limit) {
+    if (output)
+      *op->bufp++ = *bp;
+    switch (*bp++) {
+    case '/':
+      if (warn_comments && bp < limit && *bp == '*')
+       warning("`/*' within comment");
+      break;
+    case '\n':
+      if (line_counter != NULL)
+       ++*line_counter;
+      if (output)
+       ++op->lineno;
+      break;
+    case '*':
+      if (*bp == '\\' && bp[1] == '\n')
+       newline_fix (bp);
+      if (*bp == '/') {
+        if (output)
+         *op->bufp++ = '/';
+       ip->bufp = ++bp;
+       return bp;
+      }
+      break;
+    }
+  }
+  ip->bufp = bp;
+  return bp;
+}
+
+/*
+ * Skip over a quoted string.  BP points to the opening quote.
+ * Returns a pointer after the closing quote.  Don't go past LIMIT.
+ * START_LINE is the line number of the starting point (but it need
+ * not be valid if the starting point is inside a macro expansion).
+ *
+ * The input stack state is not changed.
+ *
+ * If COUNT_NEWLINES is nonzero, it points to an int to increment
+ * for each newline passed.
+ *
+ * If BACKSLASH_NEWLINES_P is nonzero, store 1 thru it
+ * if we pass a backslash-newline.
+ *
+ * If EOFP is nonzero, set *EOFP to 1 if the string is unterminated.
+ */
+U_CHAR *
+skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p, eofp)
+     register U_CHAR *bp;
+     register U_CHAR *limit;
+     int start_line;
+     int *count_newlines;
+     int *backslash_newlines_p;
+     int *eofp;
+{
+  register U_CHAR c, match;
+
+  match = *bp++;
+  while (1) {
+    if (bp >= limit) {
+      error_with_line (line_for_error (start_line),
+                      "unterminated string or character constant");
+      if (eofp)
+       *eofp = 1;
+      break;
+    }
+    c = *bp++;
+    if (c == '\\') {
+      while (*bp == '\\' && bp[1] == '\n') {
+       if (backslash_newlines_p)
+         *backslash_newlines_p = 1;
+       if (count_newlines)
+         ++*count_newlines;
+       bp += 2;
+      }
+      if (*bp == '\n' && count_newlines) {
+       if (backslash_newlines_p)
+         *backslash_newlines_p = 1;
+       ++*count_newlines;
+      }
+      bp++;
+    } else if (c == '\n') {
+      /* Unterminated strings and character constants are 'legal'.  */
+      bp--;    /* Don't consume the newline. */
+      if (eofp)
+       *eofp = 1;
+      break;
+    } else if (c == match)
+      break;
+  }
+  return bp;
+}
+\f
+/*
+ * write out a #line command, for instance, after an #include file.
+ * If CONDITIONAL is nonzero, we can omit the #line if it would
+ * appear to be a no-op, and we can output a few newlines instead
+ * if we want to increase the line number by a small amount.
+ * FILE_CHANGE says whether we are entering a file, leaving, or neither.
+ */
+
+void
+output_line_command (ip, op, conditional, file_change)
+     FILE_BUF *ip, *op;
+     int conditional;
+     enum file_change_code file_change;
+{
+  int len;
+  char line_cmd_buf[500];
+
+  if (no_line_commands
+      || ip->fname == NULL
+      || no_output) {
+    op->lineno = ip->lineno;
+    return;
+  }
+
+  if (conditional) {
+    if (ip->lineno == op->lineno)
+      return;
+
+    /* If the inherited line number is a little too small,
+       output some newlines instead of a #line command.  */
+    if (ip->lineno > op->lineno && ip->lineno < op->lineno + 8) {
+      check_expand (op, 10);
+      while (ip->lineno > op->lineno) {
+       *op->bufp++ = '\n';
+       op->lineno++;
+      }
+      return;
+    }
+  }
+
+  sprintf (line_cmd_buf, "# %d \"%s\"", ip->lineno, ip->fname);
+  if (file_change != same_file)
+    strcat (line_cmd_buf, file_change == enter_file ? " 1" : " 2");
+  len = strlen (line_cmd_buf);
+  line_cmd_buf[len++] = '\n';
+  check_expand (op, len + 1);
+  if (op->bufp > op->buf && op->bufp[-1] != '\n')
+    *op->bufp++ = '\n';
+  memcpy (op->bufp, line_cmd_buf, len);
+  op->bufp += len;
+  op->lineno = ip->lineno;
+}
+\f
+
+/* Expand a macro call.
+   HP points to the symbol that is the macro being called.
+   Put the result of expansion onto the input stack
+   so that subsequent input by our caller will use it.
+
+   If macro wants arguments, caller has already verified that
+   an argument list follows; arguments come from the input stack.  */
+
+void
+macroexpand (hp, op)
+     HASHNODE *hp;
+     FILE_BUF *op;
+{
+  int nargs;
+  DEFINITION *defn = hp->value.defn;
+  register U_CHAR *xbuf;
+  int xbuf_len;
+  int start_line = instack[indepth].lineno;
+
+  CHECK_DEPTH (return;);
+
+  /* it might not actually be a macro.  */
+  if (hp->type != T_MACRO) {
+    special_symbol (hp, op);
+    return;
+  }
+
+  nargs = defn->nargs;
+
+  if (nargs >= 0) {
+    register int i;
+    struct argdata *args;
+    const char *parse_error = 0;
+
+    args = (struct argdata *) alloca ((nargs + 1) * sizeof (struct argdata));
+
+    for (i = 0; i < nargs; i++) {
+      args[i].raw = args[i].expanded = (U_CHAR *) "";
+      args[i].raw_length = args[i].expand_length
+       = args[i].stringified_length = 0;
+      args[i].free1 = args[i].free2 = 0;
+    }
+
+    /* Parse all the macro args that are supplied.  I counts them.
+       The first NARGS args are stored in ARGS.
+       The rest are discarded.  */
+    i = 0;
+    do {
+      /* Discard the open-parenthesis or comma before the next arg.  */
+      ++instack[indepth].bufp;
+      parse_error
+       = macarg ((i < nargs || (nargs == 0 && i == 0)) ? &args[i] : 0);
+      if (parse_error)
+       {
+         error_with_line (line_for_error (start_line), parse_error);
+         break;
+       }
+      i++;
+    } while (*instack[indepth].bufp != ')');
+
+    /* If we got one arg but it was just whitespace, call that 0 args.  */
+    if (i == 1) {
+      register U_CHAR *bp = args[0].raw;
+      register U_CHAR *lim = bp + args[0].raw_length;
+      while (bp != lim && is_space[*bp]) bp++;
+      if (bp == lim)
+       i = 0;
+    }
+
+    if (nargs == 0 && i > 0)
+      error ("arguments given to macro `%s'", hp->name);
+    else if (i < nargs) {
+      /* traditional C allows foo() if foo wants one argument.  */
+      if (nargs == 1 && i == 0)
+       ;
+      else if (i == 0)
+       error ("no args to macro `%s'", hp->name);
+      else if (i == 1)
+       error ("only 1 arg to macro `%s'", hp->name);
+      else
+       error ("only %d args to macro `%s'", i, hp->name);
+    } else if (i > nargs)
+      error ("too many (%d) args to macro `%s'", i, hp->name);
+
+    /* Swallow the closeparen.  */
+    ++instack[indepth].bufp;
+
+    /* If macro wants zero args, we parsed the arglist for checking only.
+       Read directly from the macro definition.  */
+    if (nargs == 0) {
+      xbuf = defn->expansion;
+      xbuf_len = defn->length;
+    } else {
+      register U_CHAR *exp = defn->expansion;
+      register int offset;     /* offset in expansion,
+                                  copied a piece at a time */
+      register int totlen;     /* total amount of exp buffer filled so far */
+
+      register struct reflist *ap;
+
+      /* Macro really takes args.  Compute the expansion of this call.  */
+
+      /* Compute length in characters of the macro's expansion.  */
+      xbuf_len = defn->length;
+      for (ap = defn->pattern; ap != NULL; ap = ap->next) {
+       if (ap->stringify)
+         xbuf_len += args[ap->argno].stringified_length;
+       else 
+         xbuf_len += args[ap->argno].raw_length;
+      }
+
+      xbuf = (U_CHAR *) xmalloc (xbuf_len + 1);
+
+      /* Generate in XBUF the complete expansion
+        with arguments substituted in.
+        TOTLEN is the total size generated so far.
+        OFFSET is the index in the definition
+        of where we are copying from.  */
+      offset = totlen = 0;
+      for (ap = defn->pattern; ap != NULL; ap = ap->next) {
+       register struct argdata *arg = &args[ap->argno];
+
+       for (i = 0; i < ap->nchars; i++)
+         xbuf[totlen++] = exp[offset++];
+
+       if (ap->stringify != 0) {
+         int arglen = arg->raw_length;
+         int escaped = 0;
+         int in_string = 0;
+         int c;
+         i = 0;
+         while (i < arglen
+                && (c = arg->raw[i], is_space[c]))
+           i++;
+         while (i < arglen
+                && (c = arg->raw[arglen - 1], is_space[c]))
+           arglen--;
+         for (; i < arglen; i++) {
+           c = arg->raw[i];
+
+           /* Special markers Newline Space
+              generate nothing for a stringified argument.  */
+           if (c == '\n' && arg->raw[i+1] != '\n') {
+             i++;
+             continue;
+           }
+
+           /* Internal sequences of whitespace are replaced by one space
+              except within an string or char token.  */
+           if (! in_string
+               && (c == '\n' ? arg->raw[i+1] == '\n' : is_space[c])) {
+             while (1) {
+               /* Note that Newline Space does occur within whitespace
+                  sequences; consider it part of the sequence.  */
+               if (c == '\n' && is_space[arg->raw[i+1]])
+                 i += 2;
+               else if (c != '\n' && is_space[c])
+                 i++;
+               else break;
+               c = arg->raw[i];
+             }
+             i--;
+             c = ' ';
+           }
+
+           if (escaped)
+             escaped = 0;
+           else {
+             if (c == '\\')
+               escaped = 1;
+             if (in_string) {
+               if (c == in_string)
+                 in_string = 0;
+             } else if (c == '\"' || c == '\'')
+               in_string = c;
+           }
+
+           /* Escape these chars */
+           if (c == '\"' || (in_string && c == '\\'))
+             xbuf[totlen++] = '\\';
+           if (isprint (c))
+             xbuf[totlen++] = c;
+           else {
+             sprintf ((char *) &xbuf[totlen], "\\%03o", (unsigned int) c);
+             totlen += 4;
+           }
+         }
+       } else {
+         U_CHAR *p1 = arg->raw;
+         U_CHAR *l1 = p1 + arg->raw_length;
+
+         if (ap->raw_before) {
+           while (p1 != l1 && is_space[*p1]) p1++;
+           while (p1 != l1 && is_idchar[*p1])
+             xbuf[totlen++] = *p1++;
+           /* Delete any no-reexpansion marker that follows
+              an identifier at the beginning of the argument
+              if the argument is concatenated with what precedes it.  */
+           if (p1[0] == '\n' && p1[1] == '-')
+             p1 += 2;
+         }
+         if (ap->raw_after) {
+           /* Arg is concatenated after: delete trailing whitespace,
+              whitespace markers, and no-reexpansion markers.  */
+           while (p1 != l1) {
+             if (is_space[l1[-1]]) l1--;
+             else if (l1[-1] == '-') {
+               U_CHAR *p2 = l1 - 1;
+               /* If a `-' is preceded by an odd number of newlines then it
+                  and the last newline are a no-reexpansion marker.  */
+               while (p2 != p1 && p2[-1] == '\n') p2--;
+               if ((l1 - 1 - p2) & 1) {
+                 l1 -= 2;
+               }
+               else break;
+             }
+             else break;
+           }
+         }
+         memmove (xbuf + totlen, p1, l1 - p1);
+         totlen += l1 - p1;
+       }
+
+       if (totlen > xbuf_len)
+         abort ();
+      }
+
+      /* if there is anything left of the definition
+        after handling the arg list, copy that in too. */
+
+      for (i = offset; i < defn->length; i++)
+       xbuf[totlen++] = exp[i];
+
+      xbuf[totlen] = 0;
+      xbuf_len = totlen;
+
+      for (i = 0; i < nargs; i++) {
+       if (args[i].free1 != 0)
+         free (args[i].free1);
+       if (args[i].free2 != 0)
+         free (args[i].free2);
+      }
+    }
+  } else {
+    xbuf = defn->expansion;
+    xbuf_len = defn->length;
+  }
+
+  /* Now put the expansion on the input stack
+     so our caller will commence reading from it.  */
+  {
+    register FILE_BUF *ip2;
+
+    ip2 = &instack[++indepth];
+
+    ip2->fname = 0;
+    ip2->lineno = 0;
+    ip2->buf = xbuf;
+    ip2->length = xbuf_len;
+    ip2->bufp = xbuf;
+    ip2->free_ptr = (nargs > 0) ? xbuf : 0;
+    ip2->macro = hp;
+    ip2->if_stack = if_stack;
+  }
+}
+\f
+/*
+ * Parse a macro argument and store the info on it into *ARGPTR.
+ * Return nonzero to indicate a syntax error.
+ */
+
+const char *
+macarg (argptr)
+     register struct argdata *argptr;
+{
+  FILE_BUF *ip = &instack[indepth];
+  int paren = 0;
+  int newlines = 0;
+  int comments = 0;
+
+  /* Try to parse as much of the argument as exists at this
+     input stack level.  */
+  U_CHAR *bp = macarg1 (ip->bufp, ip->buf + ip->length,
+                       &paren, &newlines, &comments);
+
+  /* If we find the end of the argument at this level,
+     set up *ARGPTR to point at it in the input stack.  */
+  if (!(ip->fname != 0 && (newlines != 0 || comments != 0))
+      && bp != ip->buf + ip->length) {
+    if (argptr != 0) {
+      argptr->raw = ip->bufp;
+      argptr->raw_length = bp - ip->bufp;
+    }
+    ip->bufp = bp;
+  } else {
+    /* This input stack level ends before the macro argument does.
+       We must pop levels and keep parsing.
+       Therefore, we must allocate a temporary buffer and copy
+       the macro argument into it.  */
+    int bufsize = bp - ip->bufp;
+    int extra = newlines;
+    U_CHAR *buffer = (U_CHAR *) xmalloc (bufsize + extra + 1);
+    int final_start = 0;
+
+    memcpy (buffer, ip->bufp, bufsize);
+    ip->bufp = bp;
+    ip->lineno += newlines;
+
+    while (bp == ip->buf + ip->length) {
+      if (instack[indepth].macro == 0) {
+       free (buffer);
+       return "unterminated macro call";
+      }
+      ip->macro->type = T_MACRO;
+      if (ip->free_ptr)
+       free (ip->free_ptr);
+      ip = &instack[--indepth];
+      newlines = 0;
+      comments = 0;
+      bp = macarg1 (ip->bufp, ip->buf + ip->length, &paren,
+                   &newlines, &comments);
+      final_start = bufsize;
+      bufsize += bp - ip->bufp;
+      extra += newlines;
+      buffer = (U_CHAR *) xrealloc (buffer, bufsize + extra + 1);
+      memcpy (buffer + bufsize - (bp - ip->bufp), ip->bufp, bp - ip->bufp);
+      ip->bufp = bp;
+      ip->lineno += newlines;
+    }
+
+    /* Now, if arg is actually wanted, record its raw form,
+       discarding comments and duplicating newlines in whatever
+       part of it did not come from a macro expansion.
+       EXTRA space has been preallocated for duplicating the newlines.
+       FINAL_START is the index of the start of that part.  */
+    if (argptr != 0) {
+      argptr->raw = buffer;
+      argptr->raw_length = bufsize;
+      argptr->free1 = buffer;
+      argptr->newlines = newlines;
+      argptr->comments = comments;
+      if ((newlines || comments) && ip->fname != 0)
+       argptr->raw_length
+         = final_start +
+           discard_comments (argptr->raw + final_start,
+                             argptr->raw_length - final_start,
+                             newlines);
+      argptr->raw[argptr->raw_length] = 0;
+      if (argptr->raw_length > bufsize + extra)
+       abort ();
+    }
+  }
+
+  /* If we are not discarding this argument,
+     macroexpand it and compute its length as stringified.
+     All this info goes into *ARGPTR.  */
+
+  if (argptr != 0) {
+    FILE_BUF obuf;
+    register U_CHAR *buf, *lim;
+    register int totlen;
+
+    obuf = expand_to_temp_buffer (argptr->raw,
+                                 argptr->raw + argptr->raw_length,
+                                 1);
+
+    argptr->expanded = obuf.buf;
+    argptr->expand_length = obuf.length;
+    argptr->free2 = obuf.buf;
+
+    buf = argptr->raw;
+    lim = buf + argptr->raw_length;
+
+    totlen = 0;
+    while (buf != lim) {
+      register U_CHAR c = *buf++;
+      totlen++;
+      /* Internal sequences of whitespace are replaced by one space
+        in most cases, but not always.  So count all the whitespace
+        in case we need to keep it all.  */
+      if (c == '\"' || c == '\\') /* escape these chars */
+       totlen++;
+      else if (!isprint (c))
+       totlen += 3;
+    }
+    argptr->stringified_length = totlen;
+  }
+  return 0;
+}
+
+/* Scan text from START (inclusive) up to LIMIT (exclusive),
+   counting parens in *DEPTHPTR,
+   and return if reach LIMIT
+   or before a `)' that would make *DEPTHPTR negative
+   or before a comma when *DEPTHPTR is zero.
+   Single and double quotes are matched and termination
+   is inhibited within them.  Comments also inhibit it.
+   Value returned is pointer to stopping place.
+
+   Increment *NEWLINES each time a newline is passed.
+   Set *COMMENTS to 1 if a comment is seen.  */
+
+U_CHAR *
+macarg1 (start, limit, depthptr, newlines, comments)
+     U_CHAR *start;
+     register U_CHAR *limit;
+     int *depthptr, *newlines, *comments;
+{
+  register U_CHAR *bp = start;
+
+  while (bp < limit) {
+    switch (*bp) {
+    case '(':
+      (*depthptr)++;
+      break;
+    case ')':
+      if (--(*depthptr) < 0)
+       return bp;
+      break;
+    case '\\':
+      /* Traditionally, backslash makes following char not special.  */
+      if (bp + 1 < limit)
+       {
+         bp++;
+         /* But count source lines anyway.  */
+         if (*bp == '\n')
+           ++*newlines;
+       }
+      break;
+    case '\n':
+      ++*newlines;
+      break;
+    case '/':
+      if (bp[1] == '\\' && bp[2] == '\n')
+       newline_fix (bp + 1);
+      if (bp[1] != '*' || bp + 1 >= limit)
+       break;
+      *comments = 1;
+      bp += 2;
+      while (bp + 1 < limit) {
+       if (bp[0] == '*'
+           && bp[1] == '\\' && bp[2] == '\n')
+         newline_fix (bp + 1);
+       if (bp[0] == '*' && bp[1] == '/')
+         break;
+       if (*bp == '\n') ++*newlines;
+       bp++;
+      }
+      bp += 1;
+      break;
+    case '\'':
+    case '\"':
+      {
+       int quotec;
+       for (quotec = *bp++; bp + 1 < limit && *bp != quotec; bp++) {
+         if (*bp == '\\') {
+           bp++;
+           if (*bp == '\n')
+             ++*newlines;
+           while (*bp == '\\' && bp[1] == '\n') {
+             bp += 2;
+           }
+         } else if (*bp == '\n') {
+           ++*newlines;
+           if (quotec == '\'')
+             break;
+         }
+       }
+      }
+      break;
+    case ',':
+      if ((*depthptr) == 0)
+       return bp;
+      break;
+    }
+    bp++;
+  }
+
+  return bp;
+}
+
+/* Discard comments and duplicate newlines
+   in the string of length LENGTH at START,
+   except inside of string constants.
+   The string is copied into itself with its beginning staying fixed.  
+
+   NEWLINES is the number of newlines that must be duplicated.
+   We assume that that much extra space is available past the end
+   of the string.  */
+
+int
+discard_comments (start, length, newlines)
+     U_CHAR *start;
+     int length;
+     int newlines;
+{
+  register U_CHAR *ibp;
+  register U_CHAR *obp;
+  register U_CHAR *limit;
+  register int c;
+
+  /* If we have newlines to duplicate, copy everything
+     that many characters up.  Then, in the second part,
+     we will have room to insert the newlines
+     while copying down.
+     NEWLINES may actually be too large, because it counts
+     newlines in string constants, and we don't duplicate those.
+     But that does no harm.  */
+  if (newlines > 0) {
+    ibp = start + length;
+    obp = ibp + newlines;
+    limit = start;
+    while (limit != ibp)
+      *--obp = *--ibp;
+  }
+
+  ibp = start + newlines;
+  limit = start + length + newlines;
+  obp = start;
+
+  while (ibp < limit) {
+    *obp++ = c = *ibp++;
+    switch (c) {
+    case '\n':
+      /* Duplicate the newline.  */
+      *obp++ = '\n';
+      break;
+
+    case '\\':
+      if (*ibp == '\n') {
+       obp--;
+       ibp++;
+      }
+      break;
+
+    case '/':
+      if (*ibp == '\\' && ibp[1] == '\n')
+       newline_fix (ibp);
+      /* Delete any comment.  */
+      if (ibp[0] != '*' || ibp + 1 >= limit)
+       break;
+      obp--;
+      ibp++;
+      while (ibp + 1 < limit) {
+       if (ibp[0] == '*'
+           && ibp[1] == '\\' && ibp[2] == '\n')
+         newline_fix (ibp + 1);
+       if (ibp[0] == '*' && ibp[1] == '/')
+         break;
+       ibp++;
+      }
+      ibp += 2;
+      break;
+
+    case '\'':
+    case '\"':
+      /* Notice and skip strings, so that we don't
+        think that comments start inside them,
+        and so we don't duplicate newlines in them.  */
+      {
+       int quotec = c;
+       while (ibp < limit) {
+         *obp++ = c = *ibp++;
+         if (c == quotec)
+           break;
+         if (c == '\n' && quotec == '\'')
+           break;
+         if (c == '\\' && ibp < limit) {
+           while (*ibp == '\\' && ibp[1] == '\n')
+             ibp += 2;
+           *obp++ = *ibp++;
+         }
+       }
+      }
+      break;
+    }
+  }
+
+  return obp - start;
+}
+\f
+
+/* Core error handling routine.  */
+void
+v_message (mtype, line, msgid, ap)
+     enum msgtype mtype;
+     int line;
+     const char *msgid;
+     va_list ap;
+{
+  const char *fname = 0;
+  int i;
+
+  if (mtype == WARNING && inhibit_warnings)
+    return;
+
+  for (i = indepth; i >= 0; i--)
+    if (instack[i].fname != NULL) {
+      if (line == 0)
+       line = instack[i].lineno;
+      fname = instack[i].fname;
+      break;
+    }
+
+  if (fname)
+    fprintf (stderr, "%s:%d: ", fname, line);
+  else
+    fprintf (stderr, "%s: ", progname);
+
+  if (mtype == WARNING)
+    fputs ("warning: ", stderr);
+
+  vfprintf (stderr, msgid, ap);
+  putc ('\n', stderr);
+
+  if (mtype == ERROR)
+    errors++;
+}
+
+/*
+ * error - print error message and increment count of errors.
+ */
+void
+error VPARAMS ((const char *msgid, ...))
+{
+#ifndef ANSI_PROTOTYPES
+  const char *msgid;
+#endif
+  va_list ap;
+
+  VA_START(ap, msgid);
+  
+#ifndef ANSI_PROTOTYPES
+  msgid = va_arg (ap, const char *);
+#endif
+
+  v_message (ERROR, 0, msgid, ap);
+}
+
+void
+error_with_line VPARAMS ((int line, const char *msgid, ...))
+{
+#ifndef ANSI_PROTOTYPES
+  int line;
+  const char *msgid;
+#endif
+  va_list ap;
+
+  VA_START(ap, msgid);
+  
+#ifndef ANSI_PROTOTYPES
+  line = va_arg (ap, int);
+  msgid = va_arg (ap, const char *);
+#endif
+
+  v_message (ERROR, line, msgid, ap);
+}
+
+/* Error including a message from `errno'.  */
+void
+error_from_errno (name)
+     const char *name;
+{
+  error ("%s: %s", name, strerror (errno));
+}
+
+/* Print error message but don't count it.  */
+void
+warning VPARAMS ((const char *msgid, ...))
+{
+#ifndef ANSI_PROTOTYPES
+  const char *msgid;
+#endif
+  va_list ap;
+
+  VA_START(ap, msgid);
+  
+#ifndef ANSI_PROTOTYPES
+  msgid = va_arg (ap, const char *);
+#endif
+
+  v_message (WARNING, 0, msgid, ap);
+}
+
+void
+fatal VPARAMS ((const char *msgid, ...))
+{
+#ifndef ANSI_PROTOTYPES
+  const char *msgid;
+#endif
+  va_list ap;
+
+  VA_START(ap, msgid);
+  
+#ifndef ANSI_PROTOTYPES
+  msgid = va_arg (ap, const char *);
+#endif
+
+  v_message (FATAL, 0, msgid, ap);
+  exit (FATAL_EXIT_CODE);
+}
+
+/* More 'friendly' abort that prints the location at which we died.  */
+void
+fancy_abort (line, func)
+     int line;
+     const char *func;
+{
+  if (!func)
+    func = "?";
+  
+  fatal ("Internal error in \"%s\", at tradcpp.c:%d\n\
+Please submit a full bug report.\n\
+See %s for instructions.", func, line, GCCBUGURL);
+}
+
+void
+perror_with_name (name)
+     const char *name;
+{
+  fprintf (stderr, "%s: %s: %s\n", progname, name, strerror (errno));
+  errors++;
+}
+
+void
+pfatal_with_name (name)
+     const char *name;
+{
+  perror_with_name (name);
+  exit (FATAL_EXIT_CODE);
+}
+
+/* Return the line at which an error occurred.
+   The error is not necessarily associated with the current spot
+   in the input stack, so LINE says where.  LINE will have been
+   copied from ip->lineno for the current input level.
+   If the current level is for a file, we return LINE.
+   But if the current level is not for a file, LINE is meaningless.
+   In that case, we return the lineno of the innermost file.  */
+int
+line_for_error (line)
+     int line;
+{
+  int i;
+  int line1 = line;
+
+  for (i = indepth; i >= 0; ) {
+    if (instack[i].fname != 0)
+      return line1;
+    i--;
+    if (i < 0)
+      return 0;
+    line1 = instack[i].lineno;
+  }
+  return 0;
+}
+
+/*
+ * If OBUF doesn't have NEEDED bytes after OPTR, make it bigger.
+ *
+ * As things stand, nothing is ever placed in the output buffer to be
+ * removed again except when it's KNOWN to be part of an identifier,
+ * so flushing and moving down everything left, instead of expanding,
+ * should work ok.
+ */
+
+void
+grow_outbuf (obuf, needed)
+     register FILE_BUF *obuf;
+     register int needed;
+{
+  register U_CHAR *p;
+  int minsize;
+
+  if (obuf->length - (obuf->bufp - obuf->buf) > needed)
+    return;
+
+  /* Make it at least twice as big as it is now.  */
+  obuf->length *= 2;
+  /* Make it have at least 150% of the free space we will need.  */
+  minsize = (3 * needed) / 2 + (obuf->bufp - obuf->buf);
+  if (minsize > obuf->length)
+    obuf->length = minsize;
+
+  p = (U_CHAR *) xrealloc (obuf->buf, obuf->length);
+  obuf->bufp = p + (obuf->bufp - obuf->buf);
+  obuf->buf = p;
+}
+\f
+/* Symbol table for macro names and special symbols */
+
+/*
+ * install a name in the main hash table, even if it is already there.
+ *   name stops with first non alphanumeric, except leading '#'.
+ * caller must check against redefinition if that is desired.
+ * delete_macro () removes things installed by install () in fifo order.
+ * this is important because of the `defined' special symbol used
+ * in #if, and also if pushdef/popdef directives are ever implemented.
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ *
+ * caller must set the value, if any is desired.
+ */
+HASHNODE *
+install (name, len, type, hash)
+     const U_CHAR *name;
+     int len;
+     enum node_type type;
+     int hash;
+        /* watch out here if sizeof (U_CHAR *) != sizeof (int) */
+{
+  register HASHNODE *hp;
+  register int bucket;
+  register const U_CHAR *p;
+  U_CHAR *q;
+
+  if (len < 0) {
+    p = name;
+    while (is_idchar[*p])
+      p++;
+    len = p - name;
+  }
+
+  if (hash < 0)
+    hash = hashf (name, len, HASHSIZE);
+
+  hp = (HASHNODE *) xmalloc (sizeof (HASHNODE) + len + 1);
+  bucket = hash;
+  hp->bucket_hdr = &hashtab[bucket];
+  hp->next = hashtab[bucket];
+  hashtab[bucket] = hp;
+  hp->prev = NULL;
+  if (hp->next != NULL)
+    hp->next->prev = hp;
+  hp->type = type;
+  hp->length = len;
+  hp->name = q = ((U_CHAR *) hp) + sizeof (HASHNODE);
+  memcpy (q, name, len);
+  q[len] = 0;
+  return hp;
+}
+
+/*
+ * find the most recent hash node for name name (ending with first
+ * non-identifier char) installed by install
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+HASHNODE *
+lookup (name, len, hash)
+     const U_CHAR *name;
+     int len;
+     int hash;
+{
+  register const U_CHAR *bp;
+  register HASHNODE *bucket;
+
+  if (len < 0) {
+    for (bp = name; is_idchar[*bp]; bp++) ;
+    len = bp - name;
+  }
+
+  if (hash < 0)
+    hash = hashf (name, len, HASHSIZE);
+
+  bucket = hashtab[hash];
+  while (bucket) {
+    if (bucket->length == len
+       && strncmp ((char *)bucket->name, (char *)name, len) == 0)
+      return bucket;
+    bucket = bucket->next;
+  }
+  return NULL;
+}
+
+/*
+ * Delete a hash node.  Some weirdness to free junk from macros.
+ * More such weirdness will have to be added if you define more hash
+ * types that need it.
+ */
+
+/* Note that the DEFINITION of a macro is removed from the hash table
+   but its storage is not freed.  This would be a storage leak
+   except that it is not reasonable to keep undefining and redefining
+   large numbers of macros many times.
+   In any case, this is necessary, because a macro can be #undef'd
+   in the middle of reading the arguments to a call to it.
+   If #undef freed the DEFINITION, that would crash.  */
+void
+delete_macro (hp)
+     HASHNODE *hp;
+{
+
+  if (hp->prev != NULL)
+    hp->prev->next = hp->next;
+  if (hp->next != NULL)
+    hp->next->prev = hp->prev;
+
+  /* make sure that the bucket chain header that
+     the deleted guy was on points to the right thing afterwards. */
+  if (hp == *hp->bucket_hdr)
+    *hp->bucket_hdr = hp->next;
+
+  free (hp);
+}
+
+/*
+ * return hash function on name.  must be compatible with the one
+ * computed a step at a time, elsewhere
+ */
+int
+hashf (name, len, hashsize)
+     register const U_CHAR *name;
+     register int len;
+     int hashsize;
+{
+  register int r = 0;
+
+  while (len--)
+    r = HASHSTEP (r, *name++);
+
+  return MAKE_POS (r) % hashsize;
+}
+\f
+/* Dump all macro definitions as #defines to stdout.  */
+
+void
+dump_all_macros ()
+{
+  int bucket;
+
+  for (bucket = 0; bucket < HASHSIZE; bucket++) {
+    register HASHNODE *hp;
+
+    for (hp = hashtab[bucket]; hp; hp= hp->next) {
+      if (hp->type == T_MACRO) {
+       register DEFINITION *defn = hp->value.defn;
+       struct reflist *ap;
+       int offset;
+       int concat;
+
+
+       /* Print the definition of the macro HP.  */
+
+       printf ("#define %s", hp->name);
+       if (defn->nargs >= 0) {
+         int i;
+
+         printf ("(");
+         for (i = 0; i < defn->nargs; i++) {
+           dump_arg_n (defn, i);
+           if (i + 1 < defn->nargs)
+             printf (", ");
+         }
+         printf (")");
+       }
+
+       printf (" ");
+
+       offset = 0;
+       concat = 0;
+       for (ap = defn->pattern; ap != NULL; ap = ap->next) {
+         dump_defn_1 (defn->expansion, offset, ap->nchars);
+         if (ap->nchars != 0)
+           concat = 0;
+         offset += ap->nchars;
+         if (ap->stringify)
+           printf (" #");
+         if (ap->raw_before && !concat)
+           printf (" ## ");
+         concat = 0;
+         dump_arg_n (defn, ap->argno);
+         if (ap->raw_after) {
+           printf (" ## ");
+           concat = 1;
+         }
+       }
+       dump_defn_1 (defn->expansion, offset, defn->length - offset);
+       printf ("\n");
+      }
+    }
+  }
+}
+
+/* Output to stdout a substring of a macro definition.
+   BASE is the beginning of the definition.
+   Output characters START thru LENGTH.
+   Discard newlines outside of strings, thus
+   converting funny-space markers to ordinary spaces.  */
+void
+dump_defn_1 (base, start, length)
+     U_CHAR *base;
+     int start;
+     int length;
+{
+  U_CHAR *p = base + start;
+  U_CHAR *limit = base + start + length;
+
+  while (p < limit) {
+    if (*p != '\n')
+      putchar (*p);
+    else if (*p == '\"' || *p =='\'') {
+      U_CHAR *p1 = skip_quoted_string (p, limit, 0, 0, 0, 0);
+      fwrite (p, p1 - p, 1, stdout);
+      p = p1 - 1;
+    }
+    p++;
+  }
+}
+
+/* Print the name of argument number ARGNUM of macro definition DEFN.
+   Recall that DEFN->argnames contains all the arg names
+   concatenated in reverse order with comma-space in between.  */
+void
+dump_arg_n (defn, argnum)
+     DEFINITION *defn;
+     int argnum;
+{
+  register U_CHAR *p = defn->argnames;
+  while (argnum + 1 < defn->nargs) {
+    p = (U_CHAR *) strchr ((char *)p, ' ') + 1;
+    argnum++;
+  }
+
+  while (*p && *p != ',') {
+    putchar (*p);
+    p++;
+  }
+}
+\f
+/* Initialize syntactic classifications of characters.  */
+void
+initialize_char_syntax ()
+{
+  register int i;
+
+  /*
+   * Set up is_idchar and is_idstart tables.  These should be
+   * faster than saying (is_alpha (c) || c == '_'), etc.
+   * Must do set up these things before calling any routines tthat
+   * refer to them.
+   */
+  for (i = 'a'; i <= 'z'; i++) {
+    is_idchar[i - 'a' + 'A'] = 1;
+    is_idchar[i] = 1;
+    is_idstart[i - 'a' + 'A'] = 1;
+    is_idstart[i] = 1;
+  }
+  for (i = '0'; i <= '9'; i++)
+    is_idchar[i] = 1;
+  is_idchar['_'] = 1;
+  is_idstart['_'] = 1;
+
+  /* horizontal space table */
+  is_hor_space[' '] = 1;
+  is_hor_space['\t'] = 1;
+  is_hor_space['\v'] = 1;
+  is_hor_space['\f'] = 1;
+  is_hor_space['\r'] = 1;
+
+  is_space[' '] = 1;
+  is_space['\t'] = 1;
+  is_space['\v'] = 1;
+  is_space['\f'] = 1;
+  is_space['\n'] = 1;
+  is_space['\r'] = 1;
+}
+
+/* Initialize the built-in macros.  */
+#define DSC(x) U x, sizeof x - 1
+#define install_spec(name, type) \
+ install(DSC(name), type, -1);
+#define install_value(name, val) \
+ hp = install(DSC(name), T_CONST, -1); hp->value.cpval = val;
+void
+initialize_builtins ()
+{
+  HASHNODE *hp;
+
+  install_spec ("__BASE_FILE__",     T_BASE_FILE);
+  install_spec ("__DATE__",          T_DATE);
+  install_spec ("__FILE__",          T_FILE);
+  install_spec ("__TIME__",          T_TIME);
+  install_spec ("__VERSION__",       T_VERSION);
+  install_spec ("__INCLUDE_LEVEL__", T_INCLUDE_LEVEL);
+  install_spec ("__LINE__",          T_SPECLINE);
+
+  install_value ("__SIZE_TYPE__",         SIZE_TYPE);
+  install_value ("__PTRDIFF_TYPE__",      PTRDIFF_TYPE);
+  install_value ("__WCHAR_TYPE__",        WCHAR_TYPE);
+  install_value ("__REGISTER_PREFIX__",   REGISTER_PREFIX);
+  install_value ("__USER_LABEL_PREFIX__", user_label_prefix);
+}
+#undef DSC
+#undef install_spec
+#undef install_value
+\f
+/*
+ * process a given definition string, for initialization
+ * If STR is just an identifier, define it with value 1.
+ * If STR has anything after the identifier, then it should
+ * be identifier-space-definition.
+ */
+void
+make_definition (str)
+     U_CHAR *str;
+{
+  FILE_BUF *ip;
+  struct directive *kt;
+  U_CHAR *buf, *p;
+
+  buf = str;
+  p = str;
+  while (is_idchar[*p]) p++;
+  if (p == str) {
+    error ("malformed option `-D %s'", str);
+    return;
+  }
+  if (*p == 0) {
+    buf = (U_CHAR *) alloca (p - buf + 4);
+    strcpy ((char *)buf, (char *)str);
+    strcat ((char *)buf, " 1");
+  } else if (*p != ' ') {
+    error ("malformed option `-D %s'", str);
+    return;
+  } else {
+    U_CHAR *q;
+    /* Copy the entire option so we can modify it.  */
+    buf = (U_CHAR *) alloca (2 * strlen ((char *)str) + 1);
+    strncpy ((char *)buf, (char *)str, p - str);
+    /* Change the = to a space.  */
+    buf[p - str] = ' ';
+    /* Scan for any backslash-newline and remove it.  */
+    p++;
+    q = &buf[p - str];
+    while (*p) {
+      if (*p == '\\' && p[1] == '\n')
+       p += 2;
+      /* Change newline chars into newline-markers.  */
+      else if (*p == '\n')
+       {
+         *q++ = '\n';
+         *q++ = '\n';
+         p++;
+       }
+      else
+       *q++ = *p++;
+    }
+    *q = 0;
+  }
+  
+  ip = &instack[++indepth];
+  ip->fname = "*Initialization*";
+
+  ip->buf = ip->bufp = buf;
+  ip->length = strlen ((char *)buf);
+  ip->lineno = 1;
+  ip->macro = 0;
+  ip->free_ptr = 0;
+  ip->if_stack = if_stack;
+
+  for (kt = directive_table; kt->type != T_DEFINE; kt++)
+    ;
+
+  /* pass NULL as output ptr to do_define since we KNOW it never
+     does any output.... */
+  do_define (buf, buf + ip->length, NULL, kt);
+  --indepth;
+}
+
+/* JF, this does the work for the -U option */
+void
+make_undef (str)
+     U_CHAR *str;
+{
+  FILE_BUF *ip;
+  struct directive *kt;
+
+  ip = &instack[++indepth];
+  ip->fname = "*undef*";
+
+  ip->buf = ip->bufp = str;
+  ip->length = strlen ((char *)str);
+  ip->lineno = 1;
+  ip->macro = 0;
+  ip->free_ptr = 0;
+  ip->if_stack = if_stack;
+
+  for (kt = directive_table; kt->type != T_UNDEF; kt++)
+    ;
+
+  do_undef (str, str + ip->length, NULL, kt);
+  --indepth;
+}
+\f
+/* Add output to `deps_buffer' for the -M switch.
+   STRING points to the text to be output.
+   SIZE is the number of bytes, or 0 meaning output until a null.
+   If SIZE is nonzero, we break the line first, if it is long enough.  */
+void
+deps_output (string, size)
+     const char *string;
+     int size;
+{
+#ifndef MAX_OUTPUT_COLUMNS
+#define MAX_OUTPUT_COLUMNS 75
+#endif
+  if (size != 0 && deps_column != 0
+      && size + deps_column > MAX_OUTPUT_COLUMNS) {
+    deps_output ("\\\n  ", 0);
+    deps_column = 0;
+  }
+
+  if (size == 0)
+    size = strlen (string);
+
+  if (deps_size + size + 1 > deps_allocated_size) {
+    deps_allocated_size = deps_size + size + 50;
+    deps_allocated_size *= 2;
+    deps_buffer = (char *) xrealloc (deps_buffer, deps_allocated_size);
+  }
+  memcpy (&deps_buffer[deps_size], string, size);
+  deps_size += size;
+  deps_column += size;
+  deps_buffer[deps_size] = 0;
+}
+
+/* Get the file-mode and data size of the file open on FD
+   and store them in *MODE_POINTER and *SIZE_POINTER.  */
+
+int
+file_size_and_mode (fd, mode_pointer, size_pointer)
+     int fd;
+     int *mode_pointer;
+     long *size_pointer;
+{
+  struct stat sbuf;
+
+  if (fstat (fd, &sbuf) < 0) return -1;
+  if (mode_pointer) *mode_pointer = sbuf.st_mode;
+  if (size_pointer) *size_pointer = sbuf.st_size;
+  return 0;
+}