OSDN Git Service

update copyrights
[pf3gnuchains/gcc-fork.git] / gcc / cpplib.c
index e249555..c915f77 100644 (file)
@@ -1,4 +1,4 @@
-/* CPP Library.
+/* CPP Library. (Directive handling.)
    Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
    1999, 2000 Free Software Foundation, Inc.
    Contributed by Per Bothner, 1994-95.
@@ -25,16 +25,15 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "cpplib.h"
 #include "cpphash.h"
 #include "intl.h"
+#include "obstack.h"
 #include "symcat.h"
 
-/* `struct directive' defines one #-directive, including how to handle it.  */
-
-struct directive
+/* Chained list of answers to an assertion.  */
+struct answer
 {
-  directive_handler func;      /* Function to handle directive.  */
-  const U_CHAR *name;          /* Name of directive.  */
-  unsigned short length;       /* Length of name.  */
-  unsigned short flags;                /* Flags describing this directive.  */
+  struct answer *next;
+  unsigned int count;
+  cpp_token first[1];
 };
 
 /* Stack of conditionals currently in progress
@@ -43,81 +42,110 @@ struct directive
 struct if_stack
 {
   struct if_stack *next;
-  int lineno;                  /* line number where condition started */
-  int if_succeeded;            /* truth of last condition in this group */
-  const U_CHAR *control_macro; /* macro name for #ifndef around entire file */
+  cpp_lexer_pos pos;           /* line and column where condition started */
+  const cpp_hashnode *mi_cmacro;/* macro name for #ifndef around entire file */
+  unsigned char was_skipping;  /* Value of pfile->skipping before this if.  */
   int type;                    /* type of last directive seen in this group */
 };
-typedef struct if_stack IF_STACK;
+
+/* Values for the origin field of struct directive.  KANDR directives
+   come from traditional (K&R) C.  STDC89 directives come from the
+   1989 C standard.  EXTENSION directives are extensions.  */
+#define KANDR          0
+#define STDC89         1
+#define EXTENSION      2
+
+/* Values for the flags field of struct directive.  COND indicates a
+   conditional; IF_COND an opening conditional.  INCL means to treat
+   "..." and <...> as q-char and h-char sequences respectively.  IN_I
+   means this directive should be handled even if -fpreprocessed is in
+   effect (these are the directives with callback hooks).  */
+#define COND           (1 << 0)
+#define IF_COND                (1 << 1)
+#define INCL           (1 << 2)
+#define IN_I           (1 << 3)
+
+/* Defines one #-directive, including how to handle it.  */
+typedef void (*directive_handler) PARAMS ((cpp_reader *));
+typedef struct directive directive;
+struct directive
+{
+  directive_handler handler;   /* Function to handle directive.  */
+  const U_CHAR *name;          /* Name of directive.  */
+  unsigned short length;       /* Length of name.  */
+  unsigned char origin;                /* Origin of directive.  */
+  unsigned char flags;         /* Flags describing this directive.  */
+};
 
 /* Forward declarations.  */
 
-static void validate_else              PARAMS ((cpp_reader *, const U_CHAR *));
-static int parse_ifdef                 PARAMS ((cpp_reader *, const U_CHAR *));
-static unsigned int parse_include      PARAMS ((cpp_reader *, const U_CHAR *));
-static int conditional_skip            PARAMS ((cpp_reader *, int, int,
-                                                U_CHAR *));
-static int skip_if_group               PARAMS ((cpp_reader *));
-static void pass_thru_directive                PARAMS ((const U_CHAR *, size_t,
-                                                cpp_reader *, int));
-static int read_line_number            PARAMS ((cpp_reader *, int *));
-static U_CHAR *detect_if_not_defined   PARAMS ((cpp_reader *));
-static int consider_directive_while_skipping
-                                       PARAMS ((cpp_reader *, IF_STACK *));
-
-/* Values for the flags field of the table below.  KANDR and COND
-   directives come from traditional (K&R) C.  The difference is, if we
-   care about it while skipping a failed conditional block, its origin
-   is COND.  STDC89 directives come from the 1989 C standard.
-   EXTENSION directives are extensions, with origins noted below.  */
-
-#define KANDR       0
-#define COND        1
-#define STDC89      2
-#define EXTENSION   3
-
-#define ORIGIN_MASK 3
-#define ORIGIN(f) ((f) & ORIGIN_MASK)
-#define TRAD_DIRECT_P(f) (ORIGIN (f) == KANDR || ORIGIN (f) == COND)
+static void skip_rest_of_line  PARAMS ((cpp_reader *));
+static void check_eol          PARAMS ((cpp_reader *));
+static void start_directive    PARAMS ((cpp_reader *));
+static void end_directive      PARAMS ((cpp_reader *, int));
+static void run_directive      PARAMS ((cpp_reader *, int,
+                                        enum cpp_buffer_type,
+                                        const char *, size_t));
+static int glue_header_name    PARAMS ((cpp_reader *, cpp_token *));
+static int  parse_include      PARAMS ((cpp_reader *, cpp_token *));
+static void push_conditional   PARAMS ((cpp_reader *, int, int,
+                                        const cpp_hashnode *));
+static unsigned int read_flag  PARAMS ((cpp_reader *, unsigned int));
+static int  strtoul_for_line   PARAMS ((const U_CHAR *, unsigned int,
+                                        unsigned long *));
+static void do_diagnostic      PARAMS ((cpp_reader *, enum error_type, int));
+static cpp_hashnode *lex_macro_node    PARAMS ((cpp_reader *));
+static void do_pragma_once     PARAMS ((cpp_reader *));
+static void do_pragma_poison   PARAMS ((cpp_reader *));
+static void do_pragma_system_header    PARAMS ((cpp_reader *));
+static void do_pragma_dependency       PARAMS ((cpp_reader *));
+static int get__Pragma_string  PARAMS ((cpp_reader *, cpp_token *));
+static unsigned char *destringize      PARAMS ((const cpp_string *,
+                                                unsigned int *));
+static int parse_answer PARAMS ((cpp_reader *, struct answer **, int));
+static cpp_hashnode *parse_assertion PARAMS ((cpp_reader *, struct answer **,
+                                             int));
+static struct answer ** find_answer PARAMS ((cpp_hashnode *,
+                                            const struct answer *));
+static void handle_assertion   PARAMS ((cpp_reader *, const char *, int));
 
 /* This is the table of directive handlers.  It is ordered by
    frequency of occurrence; the numbers at the end are directive
    counts from all the source code I have lying around (egcs and libc
    CVS as of 1999-05-18, plus grub-0.5.91, linux-2.2.9, and
-   pcmcia-cs-3.0.9).
-
-   The entries with a dash and a name after the count are extensions,
-   of which all but #warning and #include_next are deprecated.  The name
-   is where the extension appears to have come from.  */
+   pcmcia-cs-3.0.9).  This is no longer important as directive lookup
+   is now O(1).  All extensions other than #warning and #include_next
+   are deprecated.  The name is where the extension appears to have
+   come from.  */
+
+#define DIRECTIVE_TABLE                                                        \
+D(define,      T_DEFINE = 0,   KANDR,     IN_I)           /* 270554 */ \
+D(include,     T_INCLUDE,      KANDR,     INCL)           /*  52262 */ \
+D(endif,       T_ENDIF,        KANDR,     COND)           /*  45855 */ \
+D(ifdef,       T_IFDEF,        KANDR,     COND | IF_COND) /*  22000 */ \
+D(if,          T_IF,           KANDR,     COND | IF_COND) /*  18162 */ \
+D(else,                T_ELSE,         KANDR,     COND)           /*   9863 */ \
+D(ifndef,      T_IFNDEF,       KANDR,     COND | IF_COND) /*   9675 */ \
+D(undef,       T_UNDEF,        KANDR,     IN_I)           /*   4837 */ \
+D(line,                T_LINE,         KANDR,     IN_I)           /*   2465 */ \
+D(elif,                T_ELIF,         KANDR,     COND)           /*    610 */ \
+D(error,       T_ERROR,        STDC89,    0)              /*    475 */ \
+D(pragma,      T_PRAGMA,       STDC89,    IN_I)           /*    195 */ \
+D(warning,     T_WARNING,      EXTENSION, 0)              /*     22 */ \
+D(include_next,        T_INCLUDE_NEXT, EXTENSION, INCL)           /*     19 */ \
+D(ident,       T_IDENT,        EXTENSION, IN_I)           /*     11 */ \
+D(import,      T_IMPORT,       EXTENSION, INCL)           /* 0 ObjC */ \
+D(assert,      T_ASSERT,       EXTENSION, 0)              /* 0 SVR4 */ \
+D(unassert,    T_UNASSERT,     EXTENSION, 0)              /* 0 SVR4 */ \
+SCCS_ENTRY                                                /* 0 SVR4? */
 
 /* #sccs is not always recognized.  */
 #ifdef SCCS_DIRECTIVE
-# define SCCS_ENTRY D(sccs, T_SCCS, EXTENSION)         /*     0 - SVR2? */
+# define SCCS_ENTRY D(sccs, T_SCCS, EXTENSION, 0)
 #else
 # define SCCS_ENTRY /* nothing */
 #endif
 
-#define DIRECTIVE_TABLE                                                         \
-D(define,      T_DEFINE = 0,   KANDR)                     /* 270554 */ \
-D(include,     T_INCLUDE,      KANDR | SYNTAX_INCLUDE)    /*  52262 */ \
-D(endif,       T_ENDIF,        COND)                      /*  45855 */ \
-D(ifdef,       T_IFDEF,        COND)                      /*  22000 */ \
-D(if,          T_IF,           COND)                      /*  18162 */ \
-D(else,                T_ELSE,         COND)                       /*  9863 */ \
-D(ifndef,      T_IFNDEF,       COND)                       /*  9675 */ \
-D(undef,       T_UNDEF,        KANDR)                      /*  4837 */ \
-D(line,                T_LINE,         KANDR)                      /*  2465 */ \
-D(elif,                T_ELIF,         COND)                       /*   610 */ \
-D(error,       T_ERROR,        STDC89)                     /*   475 */ \
-D(pragma,      T_PRAGMA,       STDC89)                     /*   195 */ \
-D(warning,     T_WARNING,      EXTENSION)                  /*    22 GNU */ \
-D(include_next,        T_INCLUDE_NEXT, EXTENSION | SYNTAX_INCLUDE) /*    19 GNU */ \
-D(ident,       T_IDENT,        EXTENSION)                  /*    11 SVR4 */ \
-D(import,      T_IMPORT,       EXTENSION | SYNTAX_INCLUDE) /*     0 ObjC */ \
-D(assert,      T_ASSERT,       EXTENSION)                  /*     0 SVR4 */ \
-D(unassert,    T_UNASSERT,     EXTENSION)                  /*     0 SVR4 */ \
-SCCS_ENTRY
-
 /* Use the table to generate a series of prototypes, an enum for the
    directive names, and an array of directive handlers.  */
 
@@ -126,861 +154,929 @@ SCCS_ENTRY
    pointers to functions returning void.  */
 
 /* Don't invoke CONCAT2 with any whitespace or K&R cc will fail. */
-#define D(name, t, f) static int CONCAT2(do_,name) PARAMS ((cpp_reader *));
+#define D(name, t, o, f) static void CONCAT2(do_,name) PARAMS ((cpp_reader *));
 DIRECTIVE_TABLE
 #undef D
 
-#define D(n, tag, f) tag,
+#define D(n, tag, o, f) tag,
 enum
 {
+  T_BAD_DIRECTIVE,
   DIRECTIVE_TABLE
   N_DIRECTIVES
 };
 #undef D
 
 /* Don't invoke CONCAT2 with any whitespace or K&R cc will fail. */
-#define D(name, t, flags) \
+#define D(name, t, origin, flags) \
 { CONCAT2(do_,name), (const U_CHAR *) STRINGX(name), \
-  sizeof STRINGX(name) - 1, flags },
-static const struct directive dtable[] =
+  sizeof STRINGX(name) - 1, origin, flags },
+static const directive dtable[] =
 {
 DIRECTIVE_TABLE
 };
 #undef D
 #undef DIRECTIVE_TABLE
 
-/* Check if a token's name matches that of a known directive.  Put in
-   this file to save exporting dtable and other unneeded information.  */
-void
-_cpp_check_directive (list, token)
-     cpp_toklist *list;
-     cpp_token *token;
+/* Skip any remaining tokens in a directive.  */
+static void
+skip_rest_of_line (pfile)
+     cpp_reader *pfile;
 {
-  const U_CHAR *name = token->val.name.text;
-  size_t len = token->val.name.len;
-  unsigned int i;
+  cpp_token token;
 
-  list->dir_handler = 0;
-  list->dir_flags = 0;
+  /* Discard all input lookaheads.  */
+  while (pfile->la_read)
+    _cpp_release_lookahead (pfile);
 
-  for (i = 0; i < N_DIRECTIVES; i++)
-    if (dtable[i].length == len && !ustrncmp (dtable[i].name, name, len)) 
-      {
-       list->dir_handler = dtable[i].func;
-       list->dir_flags = dtable[i].flags;
-       break;
-      }
+  /* Discard all stacked contexts.  */
+  while (pfile->context != &pfile->base_context)
+    _cpp_pop_context (pfile);
+
+  /* Sweep up all tokens remaining on the line.  */
+  pfile->state.prevent_expansion++;
+  while (!pfile->state.next_bol)
+    _cpp_lex_token (pfile, &token);
+  pfile->state.prevent_expansion--;
+}
+
+/* Ensure there are no stray tokens at the end of a directive.  */
+static void
+check_eol (pfile)
+     cpp_reader *pfile;
+{
+  if (!pfile->state.next_bol)
+    {
+      cpp_token token;
+
+      _cpp_lex_token (pfile, &token);
+      if (token.type != CPP_EOF)
+       cpp_pedwarn (pfile, "extra tokens at end of #%s directive",
+                    pfile->directive->name);
+    }
+}
+
+/* Called when entering a directive, _Pragma or command-line directive.  */
+static void
+start_directive (pfile)
+     cpp_reader *pfile;
+{
+  cpp_buffer *buffer = pfile->buffer;
+
+  /* Setup in-directive state.  */
+  pfile->state.in_directive = 1;
+  pfile->state.save_comments = 0;
+
+  /* Some handlers need the position of the # for diagnostics.  */
+  pfile->directive_pos = pfile->lexer_pos;
+
+  /* Don't save directive tokens for external clients.  */
+  pfile->la_saved = pfile->la_write;
+  pfile->la_write = 0;
+
+  /* Turn off skipping.  */
+  buffer->was_skipping = pfile->skipping;
+  pfile->skipping = 0;
 }
 
-/* Handle a possible # directive.
-   '#' has already been read.  */
+/* Called when leaving a directive, _Pragma or command-line directive.  */
+static void
+end_directive (pfile, skip_line)
+     cpp_reader *pfile;
+     int skip_line;
+{
+  cpp_buffer *buffer = pfile->buffer;
+
+  /* Restore pfile->skipping before skip_rest_of_line, so that e.g.
+     __VA_ARGS__ in the rest of the directive doesn't warn.  */
+  pfile->skipping = buffer->was_skipping;
+
+  /* We don't skip for an assembler #.  */
+  if (skip_line)
+    skip_rest_of_line (pfile);
+
+  /* Restore state.  */
+  pfile->la_write = pfile->la_saved;
+  pfile->state.save_comments = ! CPP_OPTION (pfile, discard_comments);
+  pfile->state.in_directive = 0;
+  pfile->state.angled_headers = 0;
+  pfile->state.line_extension = 0;
+  pfile->directive = 0;
+}
 
+/* Check if a token's name matches that of a known directive.  Put in
+   this file to save exporting dtable and other unneeded information.  */
 int
-_cpp_handle_directive (pfile)
+_cpp_handle_directive (pfile, indented)
      cpp_reader *pfile;
+     int indented;
 {
-  int i;
-  int hash_at_bol;
-  unsigned int len;
-  U_CHAR *ident;
-  long old_written = CPP_WRITTEN (pfile);
-  enum cpp_ttype tok;
+  cpp_buffer *buffer = pfile->buffer;
+  const directive *dir = 0;
+  cpp_token dname;
+  int skip = 1;
+
+  start_directive (pfile);
 
-  if (CPP_IS_MACRO_BUFFER (CPP_BUFFER (pfile)))
+  /* Lex the directive name directly.  */
+  _cpp_lex_token (pfile, &dname);
+
+  if (dname.type == CPP_NAME)
+    {
+      unsigned int index = dname.val.node->directive_index;
+      if (index)
+       dir = &dtable[index - 1];
+    }
+  else if (dname.type == CPP_NUMBER)
     {
-      cpp_ice (pfile, "handle_directive called on macro buffer");
-      return 0;
+      /* # followed by a number is equivalent to #line.  Do not
+        recognize this form in assembly language source files or
+        skipped conditional groups.  Complain about this form if
+        we're being pedantic, but not if this is regurgitated input
+        (preprocessed or fed back in by the C++ frontend).  */
+      if (! buffer->was_skipping && CPP_OPTION (pfile, lang) != CLK_ASM)
+       {
+         dir = &dtable[T_LINE];
+         pfile->state.line_extension = 1;
+         _cpp_push_token (pfile, &dname, &pfile->directive_pos);
+         if (CPP_PEDANTIC (pfile) && ! CPP_OPTION (pfile, preprocessed))
+           cpp_pedwarn (pfile, "# followed by integer");
+       }
     }
 
-  /* -traditional directives are recognized only with the # in column 1.  */
-  hash_at_bol = CPP_IN_COLUMN_1 (pfile);
-
-  /* Scan the next token, then pretend we didn't.  */
-  CPP_SET_MARK (pfile);
-  pfile->no_macro_expand++;
-  tok = _cpp_get_directive_token (pfile);
-  pfile->no_macro_expand--;
-
-  ident = pfile->token_buffer + old_written;
-  len = CPP_PWRITTEN (pfile) - ident;
-  CPP_SET_WRITTEN (pfile, old_written);
-  CPP_GOTO_MARK (pfile);
-
-  /* # followed by a number is equivalent to #line.  Do not recognize
-     this form in assembly language source files.  Complain about this
-     form if we're being pedantic, but not if this is regurgitated
-     input (preprocessed or fed back in by the C++ frontend).  */
-  if (tok == CPP_NUMBER)
+  pfile->directive = dir;
+  if (dir)
     {
-      if (CPP_OPTION (pfile, lang_asm))
-       return 0;
+      /* Make sure we lex headers correctly, whether skipping or not.  */
+      pfile->state.angled_headers = dir->flags & INCL;
 
-      if (CPP_PEDANTIC (pfile)
-         && CPP_BUFFER (pfile)->ihash
-         && ! CPP_OPTION (pfile, preprocessed))
-       cpp_pedwarn (pfile, "# followed by integer");
-      do_line (pfile);
-      return 1;
-    }
+      /* If we are rescanning preprocessed input, only directives tagged
+        with IN_I are honored, and the warnings below are suppressed.  */
+      if (! CPP_OPTION (pfile, preprocessed) || dir->flags & IN_I)
+       {
+         /* Traditionally, a directive is ignored unless its # is in
+            column 1.  Therefore in code intended to work with K+R
+            compilers, directives added by C89 must have their #
+            indented, and directives present in traditional C must
+            not.  This is true even of directives in skipped
+            conditional blocks.  */
+         if (CPP_WTRADITIONAL (pfile))
+           {
+             if (indented && dir->origin == KANDR)
+               cpp_warning (pfile,
+                            "traditional C ignores #%s with the # indented",
+                            dir->name);
+             else if (!indented && dir->origin != KANDR)
+               cpp_warning (pfile,
+            "suggest hiding #%s from traditional C with an indented #",
+                            dir->name);
+           }
 
-  /* If we are rescanning preprocessed input, don't obey any directives
-     other than # nnn.  */
-  else if (CPP_OPTION (pfile, preprocessed))
-    return 0;
+         /* If we are skipping a failed conditional group, all
+            non-conditional directives are ignored.  */
+         if (! buffer->was_skipping || (dir->flags & COND))
+           {
+             /* Issue -pedantic warnings for extensions.   */
+             if (CPP_PEDANTIC (pfile) && dir->origin == EXTENSION)
+               cpp_pedwarn (pfile, "#%s is a GCC extension", dir->name);
 
-  /* A line of just # becomes blank.  */
-  else if (tok == CPP_VSPACE)
-    return 1;
+             /* If we have a directive that is not an opening
+                conditional, invalidate any control macro.  */
+             if (! (dir->flags & IF_COND))
+               pfile->mi_state = MI_FAILED;
 
-  /* A NAME token might in fact be a directive!  */
-  else if (tok == CPP_NAME)
+             (*dir->handler) (pfile);
+           }
+       }
+    }
+  else if (dname.type != CPP_EOF && ! pfile->skipping)
     {
-      for (i = 0; i < N_DIRECTIVES; i++)
+      /* An unknown directive.  Don't complain about it in assembly
+        source: we don't know where the comments are, and # may
+        introduce assembler pseudo-ops.  Don't complain about invalid
+        directives in skipped conditional groups (6.10 p4).  */
+      if (CPP_OPTION (pfile, lang) == CLK_ASM)
        {
-         if (dtable[i].length == len
-             && !ustrncmp (dtable[i].name, ident, len)) 
-           goto real_directive;
+         /* Output the # and lookahead token for the assembler.  */
+         _cpp_push_token (pfile, &dname, &pfile->directive_pos);
+         skip = 0;
        }
-      /* Don't complain about invalid directives in assembly source,
-        we don't know where the comments are, and # may introduce
-        assembler pseudo-ops.  */
-      if (!CPP_OPTION (pfile, lang_asm))
-       cpp_error (pfile, "invalid preprocessing directive #%s", ident);
-      return 0;
+      else
+       cpp_error (pfile, "invalid preprocessing directive #%s",
+                  cpp_token_as_text (pfile, &dname));
     }
-  /* And anything else means the # wasn't a directive marker.   */
-  else
-    return 0;
 
- real_directive:
+  end_directive (pfile, skip);
+  return skip;
+}
 
-  /* In -traditional mode, a directive is ignored unless its # is in
-     column 1.  */
-  if (CPP_TRADITIONAL (pfile) && !hash_at_bol)
-    {
-      if (CPP_WTRADITIONAL (pfile))
-       cpp_warning (pfile, "ignoring #%s because of its indented #",
-                    dtable[i].name);
-      return 0;
-    }
+/* Directive handler wrapper used by the command line option
+   processor.  */
+static void
+run_directive (pfile, dir_no, type, buf, count)
+     cpp_reader *pfile;
+     int dir_no;
+     enum cpp_buffer_type type;
+     const char *buf;
+     size_t count;
+{
+  unsigned int output_line = pfile->lexer_pos.output_line;
+  cpp_buffer *buffer;
+
+  buffer = cpp_push_buffer (pfile, (const U_CHAR *) buf, count, type, 0);
 
-  /* no_directives is set when we are parsing macro arguments.  Directives
-     in macro arguments are undefined behavior (C99 6.10.3.11); this
-     implementation chooses to make them hard errors.  */
-  if (pfile->no_directives)
+  if (dir_no == T_PRAGMA)
     {
-      cpp_error (pfile, "#%s may not be used inside a macro argument",
-                dtable[i].name);
-      _cpp_skip_rest_of_line (pfile);
-      return 1;
+      /* A kludge to avoid line markers for _Pragma.  */
+      pfile->lexer_pos.output_line = output_line;
+      /* Avoid interpretation of directives in a _Pragma string.  */
+      pfile->state.next_bol = 0;
     }
 
-  /* Issue -pedantic warnings for extended directives.   */
-  if (CPP_PEDANTIC (pfile) && ORIGIN (dtable[i].flags) == EXTENSION)
-    cpp_pedwarn (pfile, "ISO C does not allow #%s", dtable[i].name);
+  start_directive (pfile);
+  pfile->state.prevent_expansion++;
+  (void) (*dtable[dir_no].handler) (pfile);
+  pfile->state.prevent_expansion--;
+  check_eol (pfile);
+  end_directive (pfile, 1);
+
+  cpp_pop_buffer (pfile);
+}
+
+/* Checks for validity the macro name in #define, #undef, #ifdef and
+   #ifndef directives.  */
+static cpp_hashnode *
+lex_macro_node (pfile)
+     cpp_reader *pfile;
+{
+  cpp_token token;
+
+  /* Lex the macro name directly.  */
+  _cpp_lex_token (pfile, &token);
+
+  /* The token immediately after #define must be an identifier.  That
+     identifier is not allowed to be "defined".  See predefined macro
+     names (6.10.8.4).  In C++, it is not allowed to be any of the
+     <iso646.h> macro names (which are keywords in C++) either.  */
 
-  /* -Wtraditional gives warnings about directives with inappropriate
-     indentation of #.  */
-  if (CPP_WTRADITIONAL (pfile))
+  if (token.type != CPP_NAME)
     {
-      if (!hash_at_bol && TRAD_DIRECT_P (dtable[i].flags))
-       cpp_warning (pfile, "traditional C ignores #%s with the # indented",
-                    dtable[i].name);
-      else if (hash_at_bol && ! TRAD_DIRECT_P (dtable[i].flags))
-       cpp_warning (pfile,
-               "suggest hiding #%s from traditional C with an indented #",
-                    dtable[i].name);
+      if (token.type == CPP_EOF)
+       cpp_error (pfile, "no macro name given in #%s directive",
+                  pfile->directive->name);
+      else if (token.flags & NAMED_OP)
+       cpp_error (pfile,
+                  "\"%s\" cannot be used as a macro name as it is an operator in C++",
+                  token.val.node->name);
+      else
+       cpp_error (pfile, "macro names must be identifiers");
+    }
+  else
+    {
+      cpp_hashnode *node = token.val.node;
+
+      /* In Objective C, some keywords begin with '@', but general
+        identifiers do not, and you're not allowed to #define them.  */
+      if (node == pfile->spec_nodes.n_defined || node->name[0] == '@')
+       cpp_error (pfile, "\"%s\" cannot be used as a macro name", node->name);
+      else if (!(node->flags & NODE_POISONED))
+       return node;
     }
 
-  /* Unfortunately, it's necessary to scan the directive name again,
-     now we know we're going to consume it.  FIXME.  */
+  return 0;
+}
 
-  pfile->no_macro_expand++;
-  _cpp_get_directive_token (pfile);
-  pfile->no_macro_expand--;
-  CPP_SET_WRITTEN (pfile, old_written);
+/* Process a #define directive.  Most work is done in cppmacro.c.  */
+static void
+do_define (pfile)
+     cpp_reader *pfile;
+{
+  cpp_hashnode *node = lex_macro_node (pfile);
 
-  /* Some directives (e.g. #if) may return a request to execute
-     another directive handler immediately.  No directive ever
-     requests that #define be executed immediately, so it is safe for
-     the loop to terminate when some function returns 0 (== T_DEFINE).  */
-  while ((i = dtable[i].func (pfile)));
-  return 1;
+  if (node)
+    {
+      if (_cpp_create_definition (pfile, node))
+       if (pfile->cb.define)
+         (*pfile->cb.define) (pfile, node);
+    }
 }
 
-/* Pass a directive through to the output file.
-   BUF points to the contents of the directive, as a contiguous string.
-   LEN is the length of the string pointed to by BUF.
-   KEYWORD is the keyword-table entry for the directive.  */
-
+/* Handle #undef.  Marks the identifier NT_VOID in the hash table.  */
 static void
-pass_thru_directive (buf, len, pfile, keyword)
-     const U_CHAR *buf;
-     size_t len;
+do_undef (pfile)
      cpp_reader *pfile;
-     int keyword;
 {
-  const struct directive *kt = &dtable[keyword];
-  register unsigned klen = kt->length;
-
-  CPP_RESERVE (pfile, 1 + klen + len);
-  CPP_PUTC_Q (pfile, '#');
-  CPP_PUTS_Q (pfile, kt->name, klen);
-  if (len != 0 && buf[0] != ' ')
-    CPP_PUTC_Q (pfile, ' ');
-  CPP_PUTS_Q (pfile, buf, len);
-}
+  cpp_hashnode *node = lex_macro_node (pfile);  
 
-/* Process a #define command.  */
+  /* 6.10.3.5 paragraph 2: [#undef] is ignored if the specified identifier
+     is not currently defined as a macro name.  */
+  if (node && node->type == NT_MACRO)
+    {
+      if (pfile->cb.undef)
+       (*pfile->cb.undef) (pfile, node);
+
+      if (node->flags & NODE_BUILTIN)
+       cpp_warning (pfile, "undefining \"%s\"", node->name);
+
+      _cpp_free_definition (node);
+    }
+  check_eol (pfile);
+}
 
+/* Helper routine used by parse_include.  Reinterpret the current line
+   as an h-char-sequence (< ... >); we are looking at the first token
+   after the <.  Returns zero on success.  */
 static int
-do_define (pfile)
+glue_header_name (pfile, header)
      cpp_reader *pfile;
+     cpp_token *header;
 {
-  HASHNODE *node;
-  int len;
-  const U_CHAR *sym;
-  cpp_toklist *list = &pfile->directbuf;
+  cpp_token token;
+  unsigned char *buffer, *token_mem;
+  size_t len, total_len = 0, capacity = 1024;
 
-  pfile->no_macro_expand++;
-  pfile->parsing_define_directive++;
-  CPP_OPTION (pfile, discard_comments)++;
+  /* To avoid lexed tokens overwriting our glued name, we can only
+     allocate from the string pool once we've lexed everything.  */
 
-  _cpp_scan_line (pfile, list);
-
-  /* First token on the line must be a NAME.  There must be at least
-     one token (the VSPACE at the end).  */
-  if (TOK_TYPE (list, 0) != CPP_NAME)
+  buffer = (unsigned char *) xmalloc (capacity);
+  for (;;)
     {
-      cpp_error_with_line (pfile, list->line, TOK_COL (list, 0),
-                          "#define must be followed by an identifier");
-      goto out;
-    }
+      cpp_get_token (pfile, &token);
 
-  sym = TOK_NAME (list, 0);
-  len = TOK_LEN (list, 0);
+      if (token.type == CPP_GREATER || token.type == CPP_EOF)
+       break;
 
-  /* That NAME is not allowed to be "defined".  (Not clear if the
-     standard requires this.)  */
-  if (len == 7 && !ustrncmp (sym, U"defined", 7))
-    {
-      cpp_error_with_line (pfile, list->line, TOK_COL (list, 0),
-                          "\"defined\" is not a legal macro name");
-      goto out;
+      len = cpp_token_len (&token);
+      if (total_len + len > capacity)
+       {
+         capacity = (capacity + len) * 2;
+         buffer = (unsigned char *) xrealloc (buffer, capacity);
+       }
+
+      if (token.flags & PREV_WHITE)
+       buffer[total_len++] = ' ';
+
+      total_len = cpp_spell_token (pfile, &token, &buffer[total_len]) - buffer;
     }
 
-  node = _cpp_lookup (pfile, sym, len);
-  /* Check for poisoned identifiers now.  All other checks
-     are done in cpphash.c.  */
-  if (node->type == T_POISON)
+  if (token.type == CPP_EOF)
+    cpp_error (pfile, "missing terminating > character");
+  else
     {
-      cpp_error (pfile, "redefining poisoned `%.*s'", len, sym);
-      goto out;
+      token_mem = _cpp_pool_alloc (&pfile->ident_pool, total_len);
+      memcpy (token_mem, buffer, total_len);
+
+      header->type = CPP_HEADER_NAME;
+      header->flags &= ~PREV_WHITE;
+      header->val.str.len = total_len;
+      header->val.str.text = token_mem;
     }
-    
-  if (_cpp_create_definition (pfile, list, node) == 0)
-    goto out;
-
-  if (CPP_OPTION (pfile, debug_output)
-      || CPP_OPTION (pfile, dump_macros) == dump_definitions)
-    _cpp_dump_definition (pfile, node);
-  else if (CPP_OPTION (pfile, dump_macros) == dump_names)
-    pass_thru_directive (sym, len, pfile, T_DEFINE);
-
- out:
-  pfile->no_macro_expand--;
-  pfile->parsing_define_directive--;
-  CPP_OPTION (pfile, discard_comments)--;
-  return 0;
-}
 
-/* Handle #include and #import.  */
+  free ((PTR) buffer);
+  return token.type == CPP_EOF;
+}
 
-static unsigned int
-parse_include (pfile, name)
+/* Parse the header name of #include, #include_next, #import and
+   #pragma dependency.  Returns zero on success.  */
+static int
+parse_include (pfile, header)
      cpp_reader *pfile;
-     const U_CHAR *name;
+     cpp_token *header;
 {
-  long old_written = CPP_WRITTEN (pfile);
-  enum cpp_ttype token;
-  int len;
-
-  pfile->parsing_include_directive++;
-  token = _cpp_get_directive_token (pfile);
-  pfile->parsing_include_directive--;
+  int is_pragma = pfile->directive == &dtable[T_PRAGMA];
+  const unsigned char *dir;
 
-  len = CPP_WRITTEN (pfile) - old_written;
+  if (is_pragma)
+    dir = U"pragma dependency";
+  else
+    dir = pfile->directive->name;
 
-  if (token == CPP_STRING)
-    ; /* No special treatment required.  */
-#ifdef VMS
-  else if (token == CPP_NAME)
+  /* Allow macro expansion.  */
+  cpp_get_token (pfile, header);
+  if (header->type != CPP_STRING && header->type != CPP_HEADER_NAME)
     {
-      /* Support '#include xyz' like VAX-C.  It is taken as
-         '#include <xyz.h>' and generates a warning.  */
-      cpp_warning (pfile, "#%s filename is obsolete, use #%s <filename.h>",
-                  name, name);
-
-      /* Rewrite the token to <xyz.h>.  */
-      CPP_RESERVE (pfile, 4);
-      len += 4;
-      memmove (pfile->token_buffer + old_written + 1,
-              pfile->token_buffer + old_written,
-              CPP_WRITTEN (pfile) - old_written);
-      pfile->token_buffer[old_written] = '<';
-      CPP_PUTS_Q (pfile, ".h>", 2);
+      if (header->type != CPP_LESS)
+       {
+         cpp_error (pfile, "#%s expects \"FILENAME\" or <FILENAME>", dir);
+         return 1;
+       }
+      if (glue_header_name (pfile, header))
+       return 1;
     }
-#endif
-  else
+
+  if (header->val.str.len == 0)
     {
-      cpp_error (pfile, "`#%s' expects \"FILENAME\" or <FILENAME>", name);
-      CPP_SET_WRITTEN (pfile, old_written);
-      _cpp_skip_rest_of_line (pfile);
-      return 0;
+      cpp_error (pfile, "empty file name in #%s", dir);
+      return 1;
     }
 
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
+  if (!is_pragma)
     {
-      cpp_error (pfile, "junk at end of `#%s'", name);
-      _cpp_skip_rest_of_line (pfile);
+      check_eol (pfile);
+      /* Get out of macro context, if we are.  */
+      skip_rest_of_line (pfile);
+      if (pfile->cb.include)
+       (*pfile->cb.include) (pfile, dir, header);
     }
 
-  CPP_SET_WRITTEN (pfile, old_written);
-
-  if (len == 0)
-    cpp_error (pfile, "empty file name in `#%s'", name);
-
-  return len;
+  return 0;
 }
 
-static int
+static void
 do_include (pfile)
      cpp_reader *pfile;
 {
-  unsigned int len;
-  U_CHAR *token;
-
-  len = parse_include (pfile, dtable[T_INCLUDE].name);
-  if (len == 0)
-    return 0;
-  token = (U_CHAR *) alloca (len + 1);
-  memcpy (token, CPP_PWRITTEN (pfile), len);
-  token[len] = '\0';
-  
-  if (CPP_OPTION (pfile, dump_includes))
-    pass_thru_directive (token, len, pfile, T_INCLUDE);
+  cpp_token header;
 
-  _cpp_execute_include (pfile, token, len, 0, 0);
-  return 0;
+  if (!parse_include (pfile, &header))
+    _cpp_execute_include (pfile, &header, 0, 0);
 }
 
-static int
+static void
 do_import (pfile)
      cpp_reader *pfile;
 {
-  unsigned int len;
-  U_CHAR *token;
+  cpp_token header;
 
-  if (CPP_OPTION (pfile, warn_import)
-      && !CPP_BUFFER (pfile)->system_header_p && !pfile->import_warning)
+  if (!pfile->import_warning && CPP_OPTION (pfile, warn_import))
     {
       pfile->import_warning = 1;
       cpp_warning (pfile,
           "#import is obsolete, use an #ifndef wrapper in the header file");
     }
 
-  len = parse_include (pfile, dtable[T_IMPORT].name);
-  if (len == 0)
-    return 0;
-  token = (U_CHAR *) alloca (len + 1);
-  memcpy (token, CPP_PWRITTEN (pfile), len);
-  token[len] = '\0';
-  
-  if (CPP_OPTION (pfile, dump_includes))
-    pass_thru_directive (token, len, pfile, T_IMPORT);
-
-  _cpp_execute_include (pfile, token, len, 1, 0);
-  return 0;
+  if (!parse_include (pfile, &header))
+    _cpp_execute_include (pfile, &header, 1, 0);
 }
 
-static int
+static void
 do_include_next (pfile)
      cpp_reader *pfile;
 {
-  unsigned int len;
-  U_CHAR *token;
-  struct file_name_list *search_start = 0;
-
-  len = parse_include (pfile, dtable[T_INCLUDE_NEXT].name);
-  if (len == 0)
-    return 0;
-  token = (U_CHAR *) alloca (len + 1);
-  memcpy (token, CPP_PWRITTEN (pfile), len);
-  token[len] = '\0';
-  
-  if (CPP_OPTION (pfile, dump_includes))
-    pass_thru_directive (token, len, pfile, T_INCLUDE_NEXT);
-
-  /* For #include_next, skip in the search path past the dir in which the
-     containing file was found.  Treat files specified using an absolute path
-     as if there are no more directories to search.  Treat the primary source
-     file like any other included source, but generate a warning.  */
-  if (CPP_PREV_BUFFER (CPP_BUFFER (pfile)))
-    {
-      if (CPP_BUFFER (pfile)->ihash->foundhere != ABSOLUTE_PATH)
-       search_start = CPP_BUFFER (pfile)->ihash->foundhere->next;
-    }
-  else
-    cpp_warning (pfile, "#include_next in primary source file");
+  cpp_token header;
 
-  _cpp_execute_include (pfile, token, len, 0, search_start);
-  return 0;
+  if (!parse_include (pfile, &header))
+    _cpp_execute_include (pfile, &header, 0, 1);
 }
 
-/* Subroutine of do_line.  Read next token from PFILE without adding it to
-   the output buffer.  If it is a number between 1 and 4, store it in *NUM
-   and return 1; otherwise, return 0 and complain if we aren't at the end
-   of the directive.  */
+/* Subroutine of do_line.  Read possible flags after file name.  LAST
+   is the last flag seen; 0 if this is the first flag. Return the flag
+   if it is valid, 0 at the end of the directive. Otherwise complain.  */
 
-static int
-read_line_number (pfile, num)
+static unsigned int
+read_flag (pfile, last)
      cpp_reader *pfile;
-     int *num;
+     unsigned int last;
 {
-  long save_written = CPP_WRITTEN (pfile);
-  U_CHAR *p;
-  enum cpp_ttype token = _cpp_get_directive_token (pfile);
-  p = pfile->token_buffer + save_written;
+  cpp_token token;
 
-  if (token == CPP_NUMBER && p + 1 == CPP_PWRITTEN (pfile)
-      && p[0] >= '1' && p[0] <= '4')
+  _cpp_lex_token (pfile, &token);
+  if (token.type == CPP_NUMBER && token.val.str.len == 1)
     {
-      *num = p[0] - '0';
-      CPP_SET_WRITTEN (pfile, save_written);
-      return 1;
+      unsigned int flag = token.val.str.text[0] - '0';
+
+      if (flag > last && flag <= 4
+         && (flag != 4 || last == 3)
+         && (flag != 2 || last == 0))
+       return flag;
     }
-  else
+
+  if (token.type != CPP_EOF)
+    cpp_error (pfile, "invalid flag \"%s\" in line directive",
+              cpp_token_as_text (pfile, &token));
+  return 0;
+}
+
+/* Another subroutine of do_line.  Convert a number in STR, of length
+   LEN, to binary; store it in NUMP, and return 0 if the number was
+   well-formed, 1 if not.  Temporary, hopefully.  */
+static int
+strtoul_for_line (str, len, nump)
+     const U_CHAR *str;
+     unsigned int len;
+     unsigned long *nump;
+{
+  unsigned long reg = 0;
+  U_CHAR c;
+  while (len--)
     {
-      if (token != CPP_VSPACE && token != CPP_EOF)
-       cpp_error (pfile, "invalid format `#line' command");
-      CPP_SET_WRITTEN (pfile, save_written);
-      return 0;
+      c = *str++;
+      if (!ISDIGIT (c))
+       return 1;
+      reg *= 10;
+      reg += c - '0';
     }
+  *nump = reg;
+  return 0;
 }
 
 /* Interpret #line command.
    Note that the filename string (if any) is treated as if it were an
    include filename.  That means no escape handling.  */
 
-static int
+static void
 do_line (pfile)
      cpp_reader *pfile;
 {
-  cpp_buffer *ip = CPP_BUFFER (pfile);
-  unsigned int new_lineno;
-  long old_written = CPP_WRITTEN (pfile);
-  enum cpp_ttype token;
-  char *x;
-
-  token = _cpp_get_directive_token (pfile);
-
-  if (token != CPP_NUMBER)
-    {
-      cpp_error (pfile, "token after `#line' is not an integer");
-      goto bad_line_directive;
-    }
-
-  CPP_PUTC (pfile, '\0');  /* not terminated for us */
-  new_lineno = strtoul ((const char *) (pfile->token_buffer + old_written),
-                       &x, 10);
-  if (x[0] != '\0')
+  cpp_buffer *buffer = pfile->buffer;
+  const char *filename = buffer->nominal_fname;
+  unsigned int lineno = buffer->lineno;
+  enum cpp_fc_reason reason = FC_RENAME;
+  unsigned long new_lineno;
+  unsigned int cap;
+  cpp_token token;
+
+  /* C99 raised the minimum limit on #line numbers.  */
+  cap = CPP_OPTION (pfile, c99) ? 2147483647 : 32767;
+
+  /* #line commands expand macros.  */
+  cpp_get_token (pfile, &token);
+  if (token.type != CPP_NUMBER
+      || strtoul_for_line (token.val.str.text, token.val.str.len, &new_lineno))
     {
-      cpp_error (pfile, "token after `#line' is not an integer");
-      goto bad_line_directive;
+      cpp_error (pfile, "\"%s\" after #line is not a positive integer",
+                cpp_token_as_text (pfile, &token));
+      return;
     }      
-  CPP_SET_WRITTEN (pfile, old_written);
-
-  if (CPP_PEDANTIC (pfile) && (new_lineno <= 0 || new_lineno > 32767))
-    cpp_pedwarn (pfile, "line number out of range in `#line' command");
 
-  token = _cpp_get_directive_token (pfile);
+  if (CPP_PEDANTIC (pfile) && (new_lineno == 0 || new_lineno > cap))
+    cpp_pedwarn (pfile, "line number out of range");
 
-  if (token == CPP_STRING)
+  cpp_get_token (pfile, &token);
+  if (token.type == CPP_STRING)
     {
-      U_CHAR *fname = pfile->token_buffer + old_written + 1;
-      U_CHAR *end_name = CPP_PWRITTEN (pfile) - 1;
-      int action_number = 0;
+      char *fname;
+      unsigned int len;
+
+      /* FIXME: memory leak.  */
+      len = token.val.str.len;
+      fname = xmalloc (len + 1);
+      memcpy (fname, token.val.str.text, len);
+      fname[len] = '\0';
+    
+      _cpp_simplify_pathname (fname);
 
-      if (read_line_number (pfile, &action_number))
+      if (! pfile->state.line_extension)
+       check_eol (pfile);
+      else
        {
-         if (CPP_PEDANTIC (pfile))
-           cpp_pedwarn (pfile, "garbage at end of `#line' command");
+         int flag = 0, sysp = 0;
 
-         /* This is somewhat questionable: change the buffer stack
-            depth so that output_line_command thinks we've stacked
-            another buffer. */
-         if (action_number == 1)
+         flag = read_flag (pfile, flag);
+         if (flag == 1)
            {
-             pfile->buffer_stack_depth++;
-             ip->system_header_p = 0;
-             read_line_number (pfile, &action_number);
+             reason = FC_ENTER;
+             flag = read_flag (pfile, flag);
            }
-         else if (action_number == 2)
+         else if (flag == 2)
            {
-             pfile->buffer_stack_depth--;
-             ip->system_header_p = 0;
-             read_line_number (pfile, &action_number);
+             reason = FC_LEAVE;
+             flag = read_flag (pfile, flag);
            }
-         if (action_number == 3)
+         if (flag == 3)
            {
-             ip->system_header_p = 1;
-             read_line_number (pfile, &action_number);
+             sysp = 1;
+             flag = read_flag (pfile, flag);
+             if (flag == 4)
+               sysp = 2, read_flag (pfile, flag);
            }
-         if (action_number == 4)
+
+         if (reason == FC_ENTER)
            {
-             ip->system_header_p = 2;
-             read_line_number (pfile, &action_number);
+             cpp_push_buffer (pfile, 0, 0, BUF_FAKE, fname);
+             buffer = pfile->buffer;
            }
+         else if (reason == FC_LEAVE)
+           {
+             if (buffer->type != BUF_FAKE)
+               cpp_warning (pfile, "file \"%s\" left but not entered",
+                            buffer->nominal_fname);
+             else
+               {
+                 cpp_pop_buffer (pfile);
+                 buffer = pfile->buffer;
+                 if (strcmp (buffer->nominal_fname, fname))
+                   cpp_warning (pfile, "expected to return to file \"%s\"",
+                                buffer->nominal_fname);
+                 if (buffer->lineno + 1 != new_lineno)
+                   cpp_warning (pfile, "expected to return to line number %u",
+                                buffer->lineno + 1);
+                 if (buffer->sysp != sysp)
+                   cpp_warning (pfile, "header flags for \"%s\" have changed",
+                                buffer->nominal_fname);
+               }
+           }
+         buffer->sysp = sysp;
        }
-      
-      *end_name = '\0';
-      
-      if (strcmp ((const char *)fname, ip->nominal_fname))
-       {
-         if (!strcmp ((const char *)fname, ip->ihash->name))
-           ip->nominal_fname = ip->ihash->name;
-         else
-           ip->nominal_fname = _cpp_fake_ihash (pfile, (const char *)fname);
-       }
+      buffer->nominal_fname = fname;
     }
-  else if (token != CPP_VSPACE && token != CPP_EOF)
+  else if (token.type != CPP_EOF)
     {
-      cpp_error (pfile, "token after `#line %d' is not a string", new_lineno);
-      goto bad_line_directive;
+      cpp_error (pfile, "\"%s\" is not a valid filename",
+                cpp_token_as_text (pfile, &token));
+      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.  */
-  ip->lineno = new_lineno - 1;
-  CPP_SET_WRITTEN (pfile, old_written);
-  return 0;
-
- bad_line_directive:
-  _cpp_skip_rest_of_line (pfile);
-  CPP_SET_WRITTEN (pfile, old_written);
-  return 0;
+  /* Our line number is incremented after the directive is processed.  */
+  buffer->lineno = new_lineno - 1;
+  _cpp_do_file_change (pfile, reason, filename, lineno);
 }
 
-/* Remove the definition of a symbol from the symbol table.
-   According to the C standard, it is not an error to undef
-   something that has no definitions. */
-static int
-do_undef (pfile)
+/* Arrange the file_change callback.  */
+void
+_cpp_do_file_change (pfile, reason, from_file, from_lineno)
      cpp_reader *pfile;
+     enum cpp_fc_reason reason;
+     const char *from_file;
+     unsigned int from_lineno;
 {
-  int len;
-  HASHNODE *hp;
-  U_CHAR *name;
-  long here = CPP_WRITTEN (pfile);
-  enum cpp_ttype token;
-
-  pfile->no_macro_expand++;
-  token = _cpp_get_directive_token (pfile);
-  pfile->no_macro_expand--;
-
-  if (token != CPP_NAME)
-    {
-      cpp_error (pfile, "token after #undef is not an identifier");
-      _cpp_skip_rest_of_line (pfile);
-      return 0;
-    }
-  len = CPP_WRITTEN (pfile) - here;
-
-  token = _cpp_get_directive_token (pfile);
-  if (token != CPP_VSPACE)
-  {
-      cpp_pedwarn (pfile, "junk on line after #undef");
-      _cpp_skip_rest_of_line (pfile);
-  }
-
-  name = pfile->token_buffer + here;
-  CPP_SET_WRITTEN (pfile, here);
-
-  hp = _cpp_lookup (pfile, name, len);
-  if (hp->type == T_VOID)
-    ; /* Not defined in the first place - do nothing.  */
-  else if (hp->type == T_POISON)
-    cpp_error (pfile, "cannot undefine poisoned \"%s\"", hp->name);
-  else
+  if (pfile->cb.file_change)
     {
-      /* If we are generating additional info for debugging (with -g) we
-        need to pass through all effective #undef commands.  */
-      if (CPP_OPTION (pfile, debug_output))
-       pass_thru_directive (hp->name, len, pfile, T_UNDEF);
+      cpp_file_change fc;
+      cpp_buffer *buffer = pfile->buffer;
 
-      if (hp->type != T_MACRO && hp->type != T_FMACRO
-         && hp->type != T_EMPTY && hp->type != T_IDENTITY)
-       cpp_warning (pfile, "undefining `%s'", hp->name);
+      fc.reason = reason;
+      fc.to.filename = buffer->nominal_fname;
+      fc.to.lineno = buffer->lineno + 1;
+      fc.sysp = buffer->sysp;
+      fc.externc = CPP_OPTION (pfile, cplusplus) && buffer->sysp == 2;
 
-      _cpp_free_definition (hp);
-      hp->type = T_VOID;
+      /* Caller doesn't need to handle FC_ENTER.  */
+      if (reason == FC_ENTER)
+       {
+         if (buffer->prev)
+           {
+             from_file = buffer->prev->nominal_fname;
+             from_lineno = buffer->prev->lineno;
+           }
+         else
+           from_file = 0;
+       }
+      /* Special case for file "foo.i" with "# 1 foo.c" on first line.  */
+      else if (reason == FC_RENAME && ! buffer->prev
+              && pfile->directive_pos.line == 1)
+       from_file = 0;
+
+      fc.from.filename = from_file;
+      fc.from.lineno = from_lineno;
+      pfile->cb.file_change (pfile, &fc);
     }
-
-  return 0;
 }
 
 /*
- * Report an error detected by the program we are processing.
- * Use the text of the line in the error message.
- * (We use error because it prints the filename & line#.)
+ * Report a warning or error detected by the program we are
+ * processing.  Use the directive's tokens in the error message.
  */
 
-static int
-do_error (pfile)
+static void
+do_diagnostic (pfile, code, print_dir)
      cpp_reader *pfile;
+     enum error_type code;
+     int print_dir;
 {
-  const U_CHAR *text, *limit;
-
-  _cpp_skip_hspace (pfile);
-  text = CPP_BUFFER (pfile)->cur;
-  _cpp_skip_rest_of_line (pfile);
-  limit = CPP_BUFFER (pfile)->cur;
-
-  cpp_error (pfile, "#error %.*s", (int)(limit - text), text);
-  return 0;
+  if (_cpp_begin_message (pfile, code, NULL, 0))
+    {
+      if (print_dir)
+       fprintf (stderr, "#%s ", pfile->directive->name);
+      pfile->state.prevent_expansion++;
+      cpp_output_line (pfile, stderr);
+      pfile->state.prevent_expansion--;
+    }
 }
 
-/*
- * Report a warning detected by the program we are processing.
- * Use the text of the line in the warning message, then continue.
- */
+static void
+do_error (pfile)
+     cpp_reader *pfile;
+{
+  do_diagnostic (pfile, ERROR, 1);
+}
 
-static int
+static void
 do_warning (pfile)
      cpp_reader *pfile;
 {
-  const U_CHAR *text, *limit;
-
-  _cpp_skip_hspace (pfile);
-  text = CPP_BUFFER (pfile)->cur;
-  _cpp_skip_rest_of_line (pfile);
-  limit = CPP_BUFFER (pfile)->cur;
-
-  cpp_warning (pfile, "#warning %.*s", (int)(limit - text), text);
-  return 0;
+  do_diagnostic (pfile, WARNING, 1);
 }
 
 /* Report program identification.  */
 
-static int
+static void
 do_ident (pfile)
      cpp_reader *pfile;
 {
-  long old_written = CPP_WRITTEN (pfile);
-
-  CPP_PUTS (pfile, "#ident ", 7);
+  cpp_token str;
 
-  /* Next token should be a string constant.  */
-  if (_cpp_get_directive_token (pfile) == CPP_STRING)
-    /* And then a newline.  */
-    if (_cpp_get_directive_token (pfile) == CPP_VSPACE)
-      /* Good - ship it.  */
-      return 0;
+  cpp_get_token (pfile, &str);
+  if (str.type != CPP_STRING)
+    cpp_error (pfile, "invalid #ident");
+  else if (pfile->cb.ident)
+    (*pfile->cb.ident) (pfile, &str.val.str);
 
-  cpp_error (pfile, "invalid #ident");
-  _cpp_skip_rest_of_line (pfile);
-  CPP_SET_WRITTEN (pfile, old_written);  /* discard directive */
-
-  return 0;
+  check_eol (pfile);
 }
 
 /* Pragmata handling.  We handle some of these, and pass the rest on
    to the front end.  C99 defines three pragmas and says that no macro
    expansion is to be performed on them; whether or not macro
    expansion happens for other pragmas is implementation defined.
-   This implementation never macro-expands the text after #pragma.
-
-   We currently do not support the _Pragma operator.  Support for that
-   has to be coordinated with the front end.  Proposed implementation:
-   both #pragma blah blah and _Pragma("blah blah") become
-   __builtin_pragma(blah blah) and we teach the parser about that.  */
+   This implementation never macro-expands the text after #pragma.  */
 
 /* Sub-handlers for the pragmas needing treatment here.
    They return 1 if the token buffer is to be popped, 0 if not. */
-static int do_pragma_once              PARAMS ((cpp_reader *));
-static int do_pragma_implementation    PARAMS ((cpp_reader *));
-static int do_pragma_poison            PARAMS ((cpp_reader *));
-static int do_pragma_system_header     PARAMS ((cpp_reader *));
-static int do_pragma_default           PARAMS ((cpp_reader *));
+struct pragma_entry
+{
+  struct pragma_entry *next;
+  const char *name;
+  size_t len;
+  int isnspace;
+  union {
+    void (*handler) PARAMS ((cpp_reader *));
+    struct pragma_entry *space;
+  } u;
+};
 
-static int
-do_pragma (pfile)
+void
+cpp_register_pragma (pfile, space, name, handler)
      cpp_reader *pfile;
+     const char *space;
+     const char *name;
+     void (*handler) PARAMS ((cpp_reader *));
 {
-  long here, key;
-  U_CHAR *buf;
-  int pop;
-  enum cpp_ttype token;
-
-  here = CPP_WRITTEN (pfile);
-  CPP_PUTS (pfile, "#pragma ", 8);
-
-  key = CPP_WRITTEN (pfile);
-  pfile->no_macro_expand++;
-  token = _cpp_get_directive_token (pfile);
-  if (token != CPP_NAME)
+  struct pragma_entry **x, *new;
+  size_t len;
+
+  x = &pfile->pragmas;
+  if (space)
     {
-      if (token == CPP_VSPACE)
-       goto empty;
-      else
-       goto skip;
+      struct pragma_entry *p = pfile->pragmas;
+      len = strlen (space);
+      while (p)
+       {
+         if (p->isnspace && p->len == len && !memcmp (p->name, space, len))
+           {
+             x = &p->u.space;
+             goto found;
+           }
+         p = p->next;
+       }
+      cpp_ice (pfile, "unknown #pragma namespace %s", space);
+      return;
     }
 
-  buf = pfile->token_buffer + key;
-  CPP_PUTC (pfile, ' ');
-
-#define tokis(x) !strncmp((char *) buf, x, sizeof(x) - 1)
-  if (tokis ("once"))
-    pop = do_pragma_once (pfile);
-  else if (tokis ("implementation"))
-    pop = do_pragma_implementation (pfile);
-  else if (tokis ("poison"))
-    pop = do_pragma_poison (pfile);
-  else if (tokis ("system_header"))
-    pop = do_pragma_system_header (pfile);
-  else
-    pop = do_pragma_default (pfile);
-#undef tokis
-
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
-    goto skip;
-
-  if (pop)
-    CPP_SET_WRITTEN (pfile, here);
-  pfile->no_macro_expand--;
-  return 0;
+ found:
+  new = xnew (struct pragma_entry);
+  new->name = name;
+  new->len = strlen (name);
+  new->isnspace = 0;
+  new->u.handler = handler;
 
- skip:
-  cpp_error (pfile, "malformed #pragma directive");
-  _cpp_skip_rest_of_line (pfile);
- empty:
-  CPP_SET_WRITTEN (pfile, here);
-  pfile->no_macro_expand--;
-  return 0;
+  new->next = *x;
+  *x = new;
 }
 
-static int
-do_pragma_default (pfile)
+void
+cpp_register_pragma_space (pfile, space)
      cpp_reader *pfile;
+     const char *space;
 {
-  while (_cpp_get_directive_token (pfile) != CPP_VSPACE)
-    CPP_PUTC (pfile, ' ');
-  return 0;
-}
+  struct pragma_entry *new;
+  const struct pragma_entry *p = pfile->pragmas;
+  size_t len = strlen (space);
 
-static int
-do_pragma_once (pfile)
+  while (p)
+    {
+      if (p->isnspace && p->len == len && !memcmp (p->name, space, len))
+       /* Multiple different callers are allowed to register the same
+          namespace.  */
+       return;
+      p = p->next;
+    }
+
+  new = xnew (struct pragma_entry);
+  new->name = space;
+  new->len = len;
+  new->isnspace = 1;
+  new->u.space = 0;
+
+  new->next = pfile->pragmas;
+  pfile->pragmas = new;
+}
+  
+void
+_cpp_init_internal_pragmas (pfile)
      cpp_reader *pfile;
 {
-  cpp_buffer *ip = CPP_BUFFER (pfile);
-
-  /* Allow #pragma once in system headers, since that's not the user's
-     fault.  */
-  if (!ip->system_header_p)
-    cpp_warning (pfile, "`#pragma once' is obsolete");
-      
-  if (CPP_PREV_BUFFER (ip) == NULL)
-    cpp_warning (pfile, "`#pragma once' outside include file");
-  else
-    ip->ihash->control_macro = U"";  /* never repeat */
+  /* top level */
+  cpp_register_pragma (pfile, 0, "poison", do_pragma_poison);
+  cpp_register_pragma (pfile, 0, "once", do_pragma_once);
+
+  /* GCC namespace */
+  cpp_register_pragma_space (pfile, "GCC");
 
-  return 1;
+  cpp_register_pragma (pfile, "GCC", "poison", do_pragma_poison);
+  cpp_register_pragma (pfile, "GCC", "system_header", do_pragma_system_header);
+  cpp_register_pragma (pfile, "GCC", "dependency", do_pragma_dependency);
 }
 
-static int
-do_pragma_implementation (pfile)
+static void
+do_pragma (pfile)
      cpp_reader *pfile;
 {
-  /* Be quiet about `#pragma implementation' for a file only if it hasn't
-     been included yet.  */
-  enum cpp_ttype token;
-  long written = CPP_WRITTEN (pfile);
-  U_CHAR *name;
-  char *copy;
+  const struct pragma_entry *p;
+  cpp_token tok;
+  const cpp_hashnode *node;
+  const U_CHAR *name;
   size_t len;
+  int drop = 0;
 
-  token = _cpp_get_directive_token (pfile);
-  if (token == CPP_VSPACE)
-    return 0;
-  else if (token != CPP_STRING)
+  p = pfile->pragmas;
+  pfile->state.prevent_expansion++;
+  cpp_start_lookahead (pfile);
+
+ new_space:
+  cpp_get_token (pfile, &tok);
+  if (tok.type == CPP_NAME)
     {
-      cpp_error (pfile, "malformed #pragma implementation");
-      return 1;
+      node = tok.val.node;
+      name = node->name;
+      len = node->length;
+      while (p)
+       {
+         if (strlen (p->name) == len && !memcmp (p->name, name, len))
+           {
+             if (p->isnspace)
+               {
+                 p = p->u.space;
+                 goto new_space;
+               }
+             else
+               {
+                 (*p->u.handler) (pfile);
+                 drop = 1;
+                 break;
+               }
+           }
+         p = p->next;
+       }
     }
 
-  /* Trim the leading and trailing quote marks from the string.  */
-  name = pfile->token_buffer + written + 1;
-  len = CPP_PWRITTEN (pfile) - name;
-  copy = alloca (len);
-  memcpy (copy, name, len - 1);
-  copy[len - 1] = '\0';
-  
-  if (cpp_included (pfile, copy))
-    cpp_warning (pfile,
-        "`#pragma implementation' for `%s' appears after file is included",
-                copy);
-  return 0;
+  cpp_stop_lookahead (pfile, drop);
+  pfile->state.prevent_expansion--;
+
+  if (!drop && pfile->cb.def_pragma)
+    (*pfile->cb.def_pragma) (pfile);
 }
 
-static int
+static void
+do_pragma_once (pfile)
+     cpp_reader *pfile;
+{
+  cpp_warning (pfile, "#pragma once is obsolete");
+  if (pfile->buffer->prev == NULL)
+    cpp_warning (pfile, "#pragma once in main file");
+  else
+    _cpp_never_reread (pfile->buffer->inc);
+
+  check_eol (pfile);
+}
+
+static void
 do_pragma_poison (pfile)
      cpp_reader *pfile;
 {
   /* Poison these symbols so that all subsequent usage produces an
      error message.  */
-  U_CHAR *p;
-  HASHNODE *hp;
-  long written;
-  size_t len;
-  enum cpp_ttype token;
-  int writeit;
-
-  /* As a rule, don't include #pragma poison commands in output,  
-     unless the user asks for them.  */
-  writeit = (CPP_OPTION (pfile, debug_output)
-            || CPP_OPTION (pfile, dump_macros) == dump_definitions
-            || CPP_OPTION (pfile, dump_macros) == dump_names);
+  cpp_token tok;
+  cpp_hashnode *hp;
 
+  pfile->state.poisoned_ok = 1;
   for (;;)
     {
-      written = CPP_WRITTEN (pfile);
-      token = _cpp_get_directive_token (pfile);
-      if (token == CPP_VSPACE)
+      _cpp_lex_token (pfile, &tok);
+      if (tok.type == CPP_EOF)
        break;
-      if (token != CPP_NAME)
+      if (tok.type != CPP_NAME)
        {
-         cpp_error (pfile, "invalid #pragma poison directive");
-         _cpp_skip_rest_of_line (pfile);
-         return 1;
+         cpp_error (pfile, "invalid #pragma GCC poison directive");
+         break;
        }
 
-      p = pfile->token_buffer + written;
-      len = CPP_PWRITTEN (pfile) - p;
-      hp = _cpp_lookup (pfile, p, len);
-      if (hp->type == T_POISON)
-       ;  /* It is allowed to poison the same identifier twice.  */
-      else
-       {
-         if (hp->type != T_VOID)
-           cpp_warning (pfile, "poisoning existing macro `%s'", hp->name);
-         _cpp_free_definition (hp);
-         hp->type = T_POISON;
-       }
-      if (writeit)
-       CPP_PUTC (pfile, ' ');
+      hp = tok.val.node;
+      if (hp->flags & NODE_POISONED)
+       continue;
+
+      if (hp->type == NT_MACRO)
+       cpp_warning (pfile, "poisoning existing macro \"%s\"", hp->name);
+      _cpp_free_definition (hp);
+      hp->flags |= NODE_POISONED | NODE_DIAGNOSTIC;
     }
-  return !writeit;
+  pfile->state.poisoned_ok = 0;
+
+#if 0                          /* Doesn't quite work yet.  */
+  if (tok.type == CPP_EOF && pfile->cb.poison)
+    (*pfile->cb.poison) (pfile);
+#endif
 }
 
 /* Mark the current header as a system header.  This will suppress
@@ -989,646 +1085,536 @@ do_pragma_poison (pfile)
    conforming C, but cannot be certain that their headers appear in a
    system include directory.  To prevent abuse, it is rejected in the
    primary source file.  */
-static int
+static void
 do_pragma_system_header (pfile)
      cpp_reader *pfile;
 {
-  cpp_buffer *ip = cpp_file_buffer (pfile);
-  if (CPP_PREV_BUFFER (ip) == NULL)
-    cpp_warning (pfile, "#pragma system_header outside include file");
+  cpp_buffer *buffer = pfile->buffer;
+
+  if (buffer->prev == 0)
+    cpp_warning (pfile, "#pragma system_header ignored outside include file");
   else
-    ip->system_header_p = 1;
+    cpp_make_system_header (pfile, 1, 0);
 
-  return 1;
-}
-/* Just ignore #sccs, on systems where we define it at all.  */
-#ifdef SCCS_DIRECTIVE
-static int
-do_sccs (pfile)
-     cpp_reader *pfile;
-{
-  _cpp_skip_rest_of_line (pfile);
-  return 0;
+  check_eol (pfile);
 }
-#endif
-
-/* We've found an `#if' directive.  If the only thing before it in
-   this file is white space, and if it is of the form
-   `#if ! defined SYMBOL', then SYMBOL is a possible controlling macro
-   for inclusion of this file.  (See redundant_include_p in cppfiles.c
-   for an explanation of controlling macros.)  If so, return a
-   malloced copy of SYMBOL.  Otherwise, return NULL.  */
 
-static U_CHAR *
-detect_if_not_defined (pfile)
+/* Check the modified date of the current include file against a specified
+   file. Issue a diagnostic, if the specified file is newer. We use this to
+   determine if a fixed header should be refixed.  */
+static void
+do_pragma_dependency (pfile)
      cpp_reader *pfile;
 {
-  U_CHAR *control_macro = 0;
-  enum cpp_ttype token;
-  unsigned int base_offset;
-  unsigned int token_offset;
-  unsigned int need_rparen = 0;
-  unsigned int token_len;
-
-  if (pfile->only_seen_white != 2)
-    return NULL;
-
-  /* Save state required for restore.  */
-  pfile->no_macro_expand++;
-  CPP_SET_MARK (pfile);
-  base_offset = CPP_WRITTEN (pfile);
-
-  /* Look for `!', */
-  if (_cpp_get_directive_token (pfile) != CPP_OTHER
-      || CPP_WRITTEN (pfile) != (size_t) base_offset + 1
-      || CPP_PWRITTEN (pfile)[-1] != '!')
-    goto restore;
-
-  /* ...then `defined', */
-  token_offset = CPP_WRITTEN (pfile);
-  token = _cpp_get_directive_token (pfile);
-  if (token != CPP_NAME)
-    goto restore;
-  if (ustrncmp (pfile->token_buffer + token_offset, U"defined", 7))
-    goto restore;
-
-  /* ...then an optional '(' and the name, */
-  token_offset = CPP_WRITTEN (pfile);
-  token = _cpp_get_directive_token (pfile);
-  if (token == CPP_OPEN_PAREN)
+  cpp_token header, msg;
+  int ordering;
+  if (parse_include (pfile, &header))
+    return;
+
+  ordering = _cpp_compare_file_date (pfile, &header);
+  if (ordering < 0)
+    cpp_warning (pfile, "cannot find source %s",
+                cpp_token_as_text (pfile, &header));
+  else if (ordering > 0)
     {
-      token_offset = CPP_WRITTEN (pfile);
-      need_rparen = 1;
-      token = _cpp_get_directive_token (pfile);
+      cpp_warning (pfile, "current file is older than %s",
+                  cpp_token_as_text (pfile, &header));
+      cpp_start_lookahead (pfile);
+      cpp_get_token (pfile, &msg);
+      cpp_stop_lookahead (pfile, msg.type == CPP_EOF);
+      if (msg.type != CPP_EOF)
+       do_diagnostic (pfile, WARNING, 0);
     }
-  if (token != CPP_NAME)
-    goto restore;
-
-  token_len = CPP_WRITTEN (pfile) - token_offset;
-
-  /* ...then the ')', if necessary, */
-  if (need_rparen && _cpp_get_directive_token (pfile) != CPP_CLOSE_PAREN)
-    goto restore;
-
-  /* ...and make sure there's nothing else on the line.  */
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
-    goto restore;
-
-  /* We have a legitimate controlling macro for this header.  */
-  control_macro = (U_CHAR *) xmalloc (token_len + 1);
-  memcpy (control_macro, pfile->token_buffer + token_offset, token_len);
-  control_macro[token_len] = '\0';
-
- restore:
-  CPP_SET_WRITTEN (pfile, base_offset);
-  pfile->no_macro_expand--;
-  CPP_GOTO_MARK (pfile);
-
-  return control_macro;
 }
 
-/*
- * #if is straightforward; just call _cpp_parse_expr, then conditional_skip.
- * Also, check for a reinclude preventer of the form #if !defined (MACRO).
- */
-
+/* Check syntax is "(string-literal)".  Returns 0 on success.  */
 static int
-do_if (pfile)
+get__Pragma_string (pfile, string)
      cpp_reader *pfile;
+     cpp_token *string;
 {
-  U_CHAR *control_macro = detect_if_not_defined (pfile);
-  int value = _cpp_parse_expr (pfile);
-  return conditional_skip (pfile, value == 0, T_IF, control_macro);
-}
+  cpp_token paren;
 
-/*
- * handle a #elif directive by not changing  if_stack  either.
- * see the comment above do_else.
- */
+  cpp_get_token (pfile, &paren);
+  if (paren.type != CPP_OPEN_PAREN)
+    return 1;
 
-static int
-do_elif (pfile)
-     cpp_reader *pfile;
+  cpp_get_token (pfile, string);
+  if (string->type != CPP_STRING && string->type != CPP_WSTRING)
+    return 1;
+
+  cpp_get_token (pfile, &paren);
+  return paren.type != CPP_CLOSE_PAREN;
+}
+
+/* Returns a malloced buffer containing a destringized cpp_string by
+   removing the first \ of \" and \\ sequences.  */
+static unsigned char *
+destringize (in, len)
+     const cpp_string *in;
+     unsigned int *len;
 {
-  if (pfile->if_stack == CPP_BUFFER (pfile)->if_stack)
-    {
-      cpp_error (pfile, "`#elif' not within a conditional");
-      return 0;
-    }
-  else
-    {
-      if (pfile->if_stack->type == T_ELSE)
-       {
-         cpp_error (pfile, "`#elif' after `#else'");
-         cpp_error_with_line (pfile, pfile->if_stack->lineno, 0,
-                              "the conditional began here");
-       }
-      pfile->if_stack->type = T_ELIF;
-    }
+  const unsigned char *src, *limit;
+  unsigned char *dest, *result;
 
-  if (pfile->if_stack->if_succeeded)
+  dest = result = (unsigned char *) xmalloc (in->len);
+  for (src = in->text, limit = src + in->len; src < limit;)
     {
-      _cpp_skip_rest_of_line (pfile);
-      return skip_if_group (pfile);
+      /* We know there is a character following the backslash.  */
+      if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
+       src++;
+      *dest++ = *src++;
     }
-  if (_cpp_parse_expr (pfile) == 0)
-    return skip_if_group (pfile);
 
-  ++pfile->if_stack->if_succeeded;     /* continue processing input */
-  return 0;
+  *len = dest - result;
+  return result;
 }
 
-/* Parse an #ifdef or #ifndef directive.  Returns 1 for defined, 0 for
-   not defined; the macro tested is left in the token buffer (but
-   popped).  */
-
-static int
-parse_ifdef (pfile, name)
+void
+_cpp_do__Pragma (pfile)
      cpp_reader *pfile;
-     const U_CHAR *name;
 {
-  U_CHAR *ident;
+  cpp_token string;
+  unsigned char *buffer;
   unsigned int len;
-  enum cpp_ttype token;
-  long old_written = CPP_WRITTEN (pfile);
-  int defined;
 
-  pfile->no_macro_expand++;
-  token = _cpp_get_directive_token (pfile);
-  pfile->no_macro_expand--;
-
-  ident = pfile->token_buffer + old_written;
-  len = CPP_WRITTEN (pfile) - old_written;
-
-  if (token == CPP_VSPACE)
-    {
-      if (! CPP_TRADITIONAL (pfile))
-       cpp_pedwarn (pfile, "`#%s' with no argument", name);
-      defined = 0;
-      goto done;
-    }
-  else if (token == CPP_NAME)
+  if (get__Pragma_string (pfile, &string))
     {
-      defined = cpp_defined (pfile, ident, len);
-      CPP_PUTC (pfile, '\0');  /* so it can be copied with xstrdup */
-    }
-  else
-    {
-      defined = 0;
-      if (! CPP_TRADITIONAL (pfile))
-       cpp_error (pfile, "`#%s' with invalid argument", name);
+      cpp_error (pfile, "_Pragma takes a parenthesized string literal");
+      return;
     }
 
-  if (!CPP_TRADITIONAL (pfile))
-    {
-      if (_cpp_get_directive_token (pfile) == CPP_VSPACE)
-       goto done;
-      
-      cpp_pedwarn (pfile, "garbage at end of `#%s' argument", name);
-    }
-  _cpp_skip_rest_of_line (pfile);
-  
- done:
-  CPP_SET_WRITTEN (pfile, old_written); /* Pop */
-  return defined;
+  buffer = destringize (&string.val.str, &len);
+  run_directive (pfile, T_PRAGMA, BUF_PRAGMA, (char *) buffer, len);
+  free ((PTR) buffer);
 }
 
-/* #ifdef is dead simple.  */
-
-static int
-do_ifdef (pfile)
-     cpp_reader *pfile;
+/* Just ignore #sccs, on systems where we define it at all.  */
+#ifdef SCCS_DIRECTIVE
+static void
+do_sccs (pfile)
+     cpp_reader *pfile ATTRIBUTE_UNUSED;
 {
-  int skip = ! parse_ifdef (pfile, dtable[T_IFDEF].name);
-  return conditional_skip (pfile, skip, T_IFDEF, 0);
 }
+#endif
 
-/* #ifndef is a tad more complex, because we need to check for a
-   no-reinclusion wrapper.  */
-
-static int
-do_ifndef (pfile)
+static void
+do_ifdef (pfile)
      cpp_reader *pfile;
 {
-  int start_of_file, skip;
-  U_CHAR *control_macro = 0;
+  int skip = 1;
 
-  start_of_file = pfile->only_seen_white == 2;
-  skip = parse_ifdef (pfile, dtable[T_IFNDEF].name);
+  if (! pfile->buffer->was_skipping)
+    {
+      const cpp_hashnode *node = lex_macro_node (pfile);
 
-  if (start_of_file && !skip)
-    control_macro = uxstrdup (CPP_PWRITTEN (pfile));
+      if (node)
+       skip = node->type != NT_MACRO;
 
-  return conditional_skip (pfile, skip, T_IFNDEF, control_macro);
-}
+      if (node)
+       check_eol (pfile);
+    }
 
-/* Push TYPE on stack; then, if SKIP is nonzero, skip ahead.
-   If this is a #ifndef starting at the beginning of a file,
-   CONTROL_MACRO is the macro name tested by the #ifndef.
-   Otherwise, CONTROL_MACRO is 0.  */
+  push_conditional (pfile, skip, T_IFDEF, 0);
+}
 
-static int
-conditional_skip (pfile, skip, type, control_macro)
+static void
+do_ifndef (pfile)
      cpp_reader *pfile;
-     int skip;
-     int type;
-     U_CHAR *control_macro;
 {
-  IF_STACK *temp;
-
-  temp = (IF_STACK *) xcalloc (1, sizeof (IF_STACK));
-  temp->lineno = CPP_BUFFER (pfile)->lineno;
-  temp->next = pfile->if_stack;
-  temp->control_macro = control_macro;
-  pfile->if_stack = temp;
+  int skip = 1;
+  const cpp_hashnode *node = 0;
 
-  pfile->if_stack->type = type;
+  if (! pfile->buffer->was_skipping)
+    {
+      node = lex_macro_node (pfile);
+      if (node)
+       skip = node->type == NT_MACRO;
 
-  if (skip != 0)
-    return skip_if_group (pfile);
+      if (node)
+       check_eol (pfile);
+    }
 
-  ++pfile->if_stack->if_succeeded;
-  return 0;
+  push_conditional (pfile, skip, T_IFNDEF, node);
 }
 
-/* Subroutine of skip_if_group.  Examine one preprocessing directive
-   and return 0 if skipping should continue, or the directive number
-   of the directive that ends the block if it should halt.
-
-   Also adjusts the if_stack as appropriate.  The `#' has been read,
-   but not the identifier. */
+/* #if cooperates with parse_defined to handle multiple-include
+   optimisations.  If macro expansions or identifiers appear in the
+   expression, we cannot treat it as a controlling conditional, since
+   their values could change in the future.  */
 
-static int
-consider_directive_while_skipping (pfile, stack)
-    cpp_reader *pfile;
-    IF_STACK *stack; 
+static void
+do_if (pfile)
+     cpp_reader *pfile;
 {
-  long ident;
-  int i, hash_at_bol;
-  unsigned int len;
-  IF_STACK *temp;
-
-  /* -traditional directives are recognized only with the # in column 1.  */
-  hash_at_bol = CPP_IN_COLUMN_1 (pfile);
+  int skip = 1;
+  const cpp_hashnode *cmacro = 0;
 
-  ident = CPP_WRITTEN (pfile);
-  if (_cpp_get_directive_token (pfile) != CPP_NAME)
-    return 0;
-  len = CPP_WRITTEN (pfile) - ident;
-
-  for (i = 0; i < N_DIRECTIVES; i++)
+  if (! pfile->buffer->was_skipping)
     {
-      if (dtable[i].length == len
-         && !ustrncmp (dtable[i].name, pfile->token_buffer + ident, len)) 
-       goto real_directive;
+      /* Controlling macro of #if ! defined ()  */
+      pfile->mi_ind_cmacro = 0;
+      skip = _cpp_parse_expr (pfile) == 0;
+      cmacro = pfile->mi_ind_cmacro;
     }
-  return 0;
-
- real_directive:
 
-  /* If it's not a directive of interest to us, return now.  */
-  if (ORIGIN (dtable[i].flags) != COND)
-    return 0;
-
-  /* First, deal with -traditional and -Wtraditional.
-     All COND directives are from K+R.  */
-
-  if (! hash_at_bol)
-    {
-      if (CPP_TRADITIONAL (pfile))
-       {
-         if (CPP_WTRADITIONAL (pfile))
-           cpp_warning (pfile, "ignoring #%s because of its indented #",
-                        dtable[i].name);
-         return 0;
-       }
-      if (CPP_WTRADITIONAL (pfile))
-       cpp_warning (pfile, "traditional C ignores %s with the # indented",
-                    dtable[i].name);
-    }
-  
-  switch (i)
-    {
-    default:
-      cpp_ice (pfile, "non COND directive in switch in c_d_w_s");
-      return 0;
-
-    case T_IF:
-    case T_IFDEF:
-    case T_IFNDEF:
-      temp = (IF_STACK *) xcalloc (1, sizeof (IF_STACK));
-      temp->lineno = CPP_BUFFER (pfile)->lineno;
-      temp->next = pfile->if_stack;
-      temp->type = i;
-      pfile->if_stack = temp;
-      return 0;
-
-    case T_ELSE:
-      if (pfile->if_stack != stack)
-       validate_else (pfile, dtable[i].name);
-      /* fall through */
-    case T_ELIF:
-      if (pfile->if_stack == stack)
-       return i;
-
-      pfile->if_stack->type = i;
-      return 0;
-
-    case T_ENDIF:
-      if (pfile->if_stack != stack)
-       validate_else (pfile, dtable[i].name);
-
-      if (pfile->if_stack == stack)
-       return i;
-                   
-      temp = pfile->if_stack;
-      pfile->if_stack = temp->next;
-      free (temp);
-      return 0;
-    }
+  push_conditional (pfile, skip, T_IF, cmacro);
 }
 
-/* Skip to #endif, #else, or #elif.  Consumes the directive that
-   causes it to stop, but not its argument.  Returns the number of
-   that directive, which must be passed back up to
-   _cpp_handle_directive, which will execute it.  */
-static int
-skip_if_group (pfile)
-    cpp_reader *pfile;
-{
-  enum cpp_ttype token;
-  IF_STACK *save_if_stack = pfile->if_stack; /* don't pop past here */
-  long old_written;
-  int ret = 0;
+/* Flip skipping state if appropriate and continue without changing
+   if_stack; this is so that the error message for missing #endif's
+   etc. will point to the original #if.  */
 
-  /* We are no longer at the start of the file.  */
-  pfile->only_seen_white = 0;
+static void
+do_else (pfile)
+     cpp_reader *pfile;
+{
+  cpp_buffer *buffer = pfile->buffer;
+  struct if_stack *ifs = buffer->if_stack;
 
-  old_written = CPP_WRITTEN (pfile);
-  pfile->no_macro_expand++;
-  for (;;)
+  if (ifs == NULL)
+    cpp_error (pfile, "#else without #if");
+  else
     {
-      /* We are at the end of a line.
-        XXX Serious layering violation here.  */
-      int c = CPP_BUF_PEEK (CPP_BUFFER (pfile));
-      if (c == EOF)
-       break;  /* Caller will issue error.  */
-      else if (c != '\n')
-       cpp_ice (pfile, "character %c at end of line in skip_if_group", c);
-      CPP_BUFFER (pfile)->cur++;
-      CPP_BUMP_LINE (pfile);
-      CPP_SET_WRITTEN (pfile, old_written);
-      pfile->only_seen_white = 1;
-
-      token = _cpp_get_directive_token (pfile);
-
-      if (token == CPP_DIRECTIVE)
+      if (ifs->type == T_ELSE)
        {
-         ret = consider_directive_while_skipping (pfile, save_if_stack);
-         if (ret)
-           break;
+         cpp_error (pfile, "#else after #else");
+         cpp_error_with_line (pfile, ifs->pos.line, ifs->pos.col,
+                              "the conditional began here");
        }
+      ifs->type = T_ELSE;
+
+      /* Buffer->was_skipping is 1 if all conditionals in this chain
+        have been false, 2 if a conditional has been true.  */
+      if (! ifs->was_skipping && buffer->was_skipping != 2)
+       buffer->was_skipping = ! buffer->was_skipping;
 
-      if (token != CPP_VSPACE)
-       _cpp_skip_rest_of_line (pfile);
+      /* Invalidate any controlling macro.  */
+      ifs->mi_cmacro = 0;
     }
-  CPP_SET_WRITTEN (pfile, old_written);
-  pfile->no_macro_expand--;
-  return ret;
+
+  check_eol (pfile);
 }
 
-/*
- * 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.
- */
+/* handle a #elif directive by not changing if_stack either.  see the
+   comment above do_else.  */
 
-static int
-do_else (pfile)
+static void
+do_elif (pfile)
      cpp_reader *pfile;
 {
-  validate_else (pfile, dtable[T_ELSE].name);
-  _cpp_skip_rest_of_line (pfile);
+  cpp_buffer *buffer = pfile->buffer;
+  struct if_stack *ifs = buffer->if_stack;
 
-  if (pfile->if_stack == CPP_BUFFER (pfile)->if_stack)
-    {
-      cpp_error (pfile, "`#else' not within a conditional");
-      return 0;
-    }
+  if (ifs == NULL)
+    cpp_error (pfile, "#elif without #if");
   else
     {
-      /* #ifndef can't have its special treatment for containing the whole file
-        if it has a #else clause.  */
-      pfile->if_stack->control_macro = 0;
-
-      if (pfile->if_stack->type == T_ELSE)
+      if (ifs->type == T_ELSE)
        {
-         cpp_error (pfile, "`#else' after `#else'");
-         cpp_error_with_line (pfile, pfile->if_stack->lineno, 0,
+         cpp_error (pfile, "#elif after #else");
+         cpp_error_with_line (pfile, ifs->pos.line, ifs->pos.col,
                               "the conditional began here");
        }
-      pfile->if_stack->type = T_ELSE;
-    }
+      ifs->type = T_ELIF;
 
-  if (pfile->if_stack->if_succeeded)
-    return skip_if_group (pfile);
-  
-  ++pfile->if_stack->if_succeeded;     /* continue processing input */
-  return 0;
+      /* Don't evaluate #elif if our higher level is skipping.  */
+      if (! ifs->was_skipping)
+       {
+         /* Buffer->was_skipping is 1 if all conditionals in this
+            chain have been false, 2 if a conditional has been true.  */
+         if (buffer->was_skipping == 1)
+           buffer->was_skipping = ! _cpp_parse_expr (pfile);
+         else
+           buffer->was_skipping = 2;
+
+         /* Invalidate any controlling macro.  */
+         ifs->mi_cmacro = 0;
+       }
+    }
 }
 
-/*
- * unstack after #endif command
- */
+/* #endif pops the if stack and resets pfile->skipping.  */
 
-static int
+static void
 do_endif (pfile)
      cpp_reader *pfile;
 {
-  validate_else (pfile, dtable[T_ENDIF].name);
-  _cpp_skip_rest_of_line (pfile);
+  cpp_buffer *buffer = pfile->buffer;
+  struct if_stack *ifs = buffer->if_stack;
 
-  if (pfile->if_stack == CPP_BUFFER (pfile)->if_stack)
-    cpp_error (pfile, "`#endif' not within a conditional");
+  if (ifs == NULL)
+    cpp_error (pfile, "#endif without #if");
   else
     {
-      IF_STACK *temp = pfile->if_stack;
-      pfile->if_stack = temp->next;
-      if (temp->control_macro != 0)
-       pfile->potential_control_macro = temp->control_macro;
-      free (temp);
+      /* If potential control macro, we go back outside again.  */
+      if (ifs->next == 0 && ifs->mi_cmacro)
+       {
+         pfile->mi_state = MI_OUTSIDE;
+         pfile->mi_cmacro = ifs->mi_cmacro;
+       }
+
+      buffer->if_stack = ifs->next;
+      buffer->was_skipping = ifs->was_skipping;
+      obstack_free (pfile->buffer_ob, ifs);
     }
-  return 0;
+
+  check_eol (pfile);
 }
 
-/* Issue -pedantic warning for text which is not a comment following
-   an #else or #endif.  Do not warn in system headers, as this is harmless
-   and very common on old systems.  */
+/* Push an if_stack entry and set pfile->skipping accordingly.
+   If this is a #ifndef starting at the beginning of a file,
+   CMACRO is the macro name tested by the #ifndef.  */
 
 static void
-validate_else (pfile, directive)
+push_conditional (pfile, skip, type, cmacro)
      cpp_reader *pfile;
-     const U_CHAR *directive;
+     int skip;
+     int type;
+     const cpp_hashnode *cmacro;
 {
-  long old_written;
-  if (! CPP_PEDANTIC (pfile))
-    return;
+  struct if_stack *ifs;
+  cpp_buffer *buffer = pfile->buffer;
+
+  ifs = xobnew (pfile->buffer_ob, struct if_stack);
+  ifs->pos = pfile->directive_pos;
+  ifs->next = buffer->if_stack;
+  ifs->was_skipping = buffer->was_skipping;
+  ifs->type = type;
+  if (pfile->mi_state == MI_OUTSIDE && pfile->mi_cmacro == 0)
+    ifs->mi_cmacro = cmacro;
+  else
+    ifs->mi_cmacro = 0;
 
-  old_written = CPP_WRITTEN (pfile);
-  pfile->no_macro_expand++;
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
-    cpp_pedwarn (pfile,
-                "text following `#%s' violates ANSI standard", directive);
-  CPP_SET_WRITTEN (pfile, old_written);
-  pfile->no_macro_expand--;
+  buffer->was_skipping = skip;
+  buffer->if_stack = ifs;
 }
 
-/* Called when we reach the end of a macro buffer.  Walk back up the
-   conditional stack till we reach its level at entry to this file,
-   issuing error messages.  */
-void
-_cpp_unwind_if_stack (pfile, pbuf)
+/* Read the tokens of the answer into the macro pool.  Only commit the
+   memory if we intend it as permanent storage, i.e. the #assert case.
+   Returns 0 on success.  */
+
+static int
+parse_answer (pfile, answerp, type)
      cpp_reader *pfile;
-     cpp_buffer *pbuf;
+     struct answer **answerp;
+     int type;
 {
-  struct if_stack *ifs, *nifs;
-
-  for (ifs = pfile->if_stack;
-       ifs != pbuf->if_stack;
-       ifs = nifs)
+  cpp_token paren, *token;
+  struct answer *answer;
+
+  if (POOL_FRONT (&pfile->macro_pool) + sizeof (struct answer) >
+      POOL_LIMIT (&pfile->macro_pool))
+    _cpp_next_chunk (&pfile->macro_pool, sizeof (struct answer), 0);
+  answer = (struct answer *) POOL_FRONT (&pfile->macro_pool);
+  answer->count = 0;
+
+  /* In a conditional, it is legal to not have an open paren.  We
+     should save the following token in this case.  */
+  if (type == T_IF)
+    cpp_start_lookahead (pfile);
+  cpp_get_token (pfile, &paren);
+  if (type == T_IF)
+    cpp_stop_lookahead (pfile, paren.type == CPP_OPEN_PAREN);
+
+  /* If not a paren, see if we're OK.  */
+  if (paren.type != CPP_OPEN_PAREN)
     {
-      cpp_error_with_line (pfile, ifs->lineno, 0,
-                          "unterminated `#%s' conditional",
-                          dtable[ifs->type].name);
+      /* In a conditional no answer is a test for any answer.  It
+         could be followed by any token.  */
+      if (type == T_IF)
+       return 0;
+
+      /* #unassert with no answer is valid - it removes all answers.  */
+      if (type == T_UNASSERT && paren.type == CPP_EOF)
+       return 0;
 
-      nifs = ifs->next;
-      free (ifs);
+      cpp_error (pfile, "missing '(' after predicate");
+      return 1;
     }
-  pfile->if_stack = ifs;
-}
 
-static int
-do_assert (pfile)
-     cpp_reader *pfile;
-{
-  long old_written;
-  U_CHAR *sym;
-  int ret;
-  HASHNODE *base, *this;
-  size_t blen, tlen;
-
-  old_written = CPP_WRITTEN (pfile);   /* remember where it starts */
-  ret = _cpp_parse_assertion (pfile);
-  if (ret == 0)
-    goto error;
-  else if (ret == 1)
+  for (;;)
     {
-      cpp_error (pfile, "missing token-sequence in #assert");
-      goto error;
+      token = &answer->first[answer->count];
+      /* Check we have room for the token.  */
+      if ((unsigned char *) (token + 1) >= POOL_LIMIT (&pfile->macro_pool))
+       {
+         _cpp_next_chunk (&pfile->macro_pool, sizeof (cpp_token),
+                          (unsigned char **) &answer);
+         token = &answer->first[answer->count];
+       }
+
+      cpp_get_token (pfile, token);
+      if (token->type == CPP_CLOSE_PAREN)
+       break;
+
+      if (token->type == CPP_EOF)
+       {
+         cpp_error (pfile, "missing ')' to complete answer");
+         return 1;
+       }
+      answer->count++;
     }
-  tlen = CPP_WRITTEN (pfile) - old_written;
 
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
+  if (answer->count == 0)
     {
-      cpp_error (pfile, "junk at end of #assert");
-      goto error;
+      cpp_error (pfile, "predicate's answer is empty");
+      return 1;
     }
-  sym = pfile->token_buffer + old_written;
 
-  this = _cpp_lookup (pfile, sym, tlen);
-  if (this->type == T_ASSERT)
+  /* Drop whitespace at start.  */
+  answer->first->flags &= ~PREV_WHITE;
+  *answerp = answer;
+
+  if (type == T_ASSERT || type == T_UNASSERT)
+    check_eol (pfile);
+  return 0;
+}
+
+/* Parses an assertion, returning a pointer to the hash node of the
+   predicate, or 0 on error.  If an answer was supplied, it is placed
+   in ANSWERP, otherwise it is set to 0.  */
+static cpp_hashnode *
+parse_assertion (pfile, answerp, type)
+     cpp_reader *pfile;
+     struct answer **answerp;
+     int type;
+{
+  cpp_hashnode *result = 0;
+  cpp_token predicate;
+
+  /* We don't expand predicates or answers.  */
+  pfile->state.prevent_expansion++;
+
+  *answerp = 0;
+  cpp_get_token (pfile, &predicate);
+  if (predicate.type == CPP_EOF)
+    cpp_error (pfile, "assertion without predicate");
+  else if (predicate.type != CPP_NAME)
+    cpp_error (pfile, "predicate must be an identifier");
+  else if (parse_answer (pfile, answerp, type) == 0)
     {
-      cpp_warning (pfile, "%s re-asserted", sym);
-      goto error;
+      unsigned int len = predicate.val.node->length;
+      unsigned char *sym = alloca (len + 1);
+
+      /* Prefix '#' to get it out of macro namespace.  */
+      sym[0] = '#';
+      memcpy (sym + 1, predicate.val.node->name, len);
+      result = cpp_lookup (pfile, sym, len + 1);
     }
-      
-  blen = ustrchr (sym, '(') - sym;
-  base = _cpp_lookup (pfile, sym, blen);
-  if (base->type == T_VOID)
+
+  pfile->state.prevent_expansion--;
+  return result;
+}
+
+/* Returns a pointer to the pointer to the answer in the answer chain,
+   or a pointer to NULL if the answer is not in the chain.  */
+static struct answer **
+find_answer (node, candidate)
+     cpp_hashnode *node;
+     const struct answer *candidate;
+{
+  unsigned int i;
+  struct answer **result;
+
+  for (result = &node->value.answers; *result; result = &(*result)->next)
     {
-      base->type = T_ASSERT;
-      base->value.aschain = 0;
+      struct answer *answer = *result;
+
+      if (answer->count == candidate->count)
+       {
+         for (i = 0; i < answer->count; i++)
+           if (! _cpp_equiv_tokens (&answer->first[i], &candidate->first[i]))
+             break;
+
+         if (i == answer->count)
+           break;
+       }
     }
 
-  this->type = T_ASSERT;
-  this->value.aschain = base->value.aschain;
-  base->value.aschain = this;
+  return result;
+}
 
- error:
-  _cpp_skip_rest_of_line (pfile);
-  CPP_SET_WRITTEN (pfile, old_written);
-  return 0;
+/* Test an assertion within a preprocessor conditional.  Returns
+   non-zero on failure, zero on success.  On success, the result of
+   the test is written into VALUE.  */
+int
+_cpp_test_assertion (pfile, value)
+     cpp_reader *pfile;
+     int *value;
+{
+  struct answer *answer;
+  cpp_hashnode *node;
+
+  node = parse_assertion (pfile, &answer, T_IF);
+  if (node)
+    *value = (node->type == NT_ASSERTION &&
+             (answer == 0 || *find_answer (node, answer) != 0));
+
+  /* We don't commit the memory for the answer - it's temporary only.  */
+  return node == 0;
 }
 
-static int
-do_unassert (pfile)
+static void
+do_assert (pfile)
      cpp_reader *pfile;
 {
-  int ret;
-  long old_written;
-  U_CHAR *sym;
-  long baselen, thislen;
-  HASHNODE *base, *this, *next;
-
-  old_written = CPP_WRITTEN (pfile);
-  ret = _cpp_parse_assertion (pfile);
-  if (ret == 0)
-    goto out;
-  thislen = CPP_WRITTEN (pfile) - old_written;
-
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
+  struct answer *new_answer;
+  cpp_hashnode *node;
+  
+  node = parse_assertion (pfile, &new_answer, T_ASSERT);
+  if (node)
     {
-      cpp_error (pfile, "junk at end of #unassert");
-      goto out;
+      /* Place the new answer in the answer list.  First check there
+         is not a duplicate.  */
+      new_answer->next = 0;
+      if (node->type == NT_ASSERTION)
+       {
+         if (*find_answer (node, new_answer))
+           {
+             cpp_warning (pfile, "\"%s\" re-asserted", node->name + 1);
+             return;
+           }
+         new_answer->next = node->value.answers;
+       }
+      node->type = NT_ASSERTION;
+      node->value.answers = new_answer;
+      POOL_COMMIT (&pfile->macro_pool, (sizeof (struct answer)
+                                       + (new_answer->count - 1)
+                                       * sizeof (cpp_token)));
     }
-  sym = pfile->token_buffer + old_written;
-  CPP_SET_WRITTEN (pfile, old_written);
+}
 
-  if (ret == 1)
+static void
+do_unassert (pfile)
+     cpp_reader *pfile;
+{
+  cpp_hashnode *node;
+  struct answer *answer;
+  
+  node = parse_assertion (pfile, &answer, T_UNASSERT);
+  /* It isn't an error to #unassert something that isn't asserted.  */
+  if (node && node->type == NT_ASSERTION)
     {
-      base = _cpp_lookup (pfile, sym, thislen);
-      if (base->type == T_VOID)
-       goto out;  /* It isn't an error to #undef what isn't #defined,
-                     so it isn't an error to #unassert what isn't
-                     #asserted either. */
-
-      for (this = base->value.aschain; this; this = next)
-        {
-         next = this->value.aschain;
-         this->value.aschain = NULL;
-         this->type = T_VOID;
+      if (answer)
+       {
+         struct answer **p = find_answer (node, answer), *temp;
+
+         /* Remove the answer from the list.  */
+         temp = *p;
+         if (temp)
+           *p = temp->next;
+
+         /* Did we free the last answer?  */
+         if (node->value.answers == 0)
+           node->type = NT_VOID;
        }
-      base->value.aschain = NULL;
-      base->type = T_VOID;
-    }
-  else
-    {
-      baselen = ustrchr (sym, '(') - sym;
-      base = _cpp_lookup (pfile, sym, baselen);
-      if (base->type == T_VOID) goto out;
-      this = _cpp_lookup (pfile, sym, thislen);
-      if (this->type == T_VOID) goto out;
-
-      next = base;
-      while (next->value.aschain != this)
-       next = next->value.aschain;
-
-      next->value.aschain = this->value.aschain;
-      this->value.aschain = NULL;
-      this->type = T_VOID;
-
-      if (base->value.aschain == NULL)
-       /* Last answer for this predicate deleted. */
-       base->type = T_VOID;
+      else
+       _cpp_free_definition (node);
     }
-  return 0;
 
- out:
-  _cpp_skip_rest_of_line (pfile);
-  CPP_SET_WRITTEN (pfile, old_written);
-  return 0;
+  /* We don't commit the memory for the answer - it's temporary only.  */
 }
 
 /* These are for -D, -U, -A.  */
@@ -1646,33 +1632,34 @@ cpp_define (pfile, str)
   char *buf, *p;
   size_t count;
 
-  p = strchr (str, '=');
   /* Copy the entire option so we can modify it. 
      Change the first "=" in the string to a space.  If there is none,
-     tack " 1" on the end.  Then add a newline and a NUL.  */
-  
+     tack " 1" on the end.  */
+
+  /* Length including the null.  */  
+  count = strlen (str);
+  buf = (char *) alloca (count + 2);
+  memcpy (buf, str, count);
+
+  p = strchr (str, '=');
   if (p)
-    {
-      count = strlen (str) + 2;
-      buf = (char *) alloca (count);
-      memcpy (buf, str, count - 2);
-      buf[p - str] = ' ';
-      buf[count - 2] = '\n';
-      buf[count - 1] = '\0';
-    }
+    buf[p - str] = ' ';
   else
     {
-      count = strlen (str) + 4;
-      buf = (char *) alloca (count);
-      memcpy (buf, str, count - 4);
-      strcpy (&buf[count-4], " 1\n");
+      buf[count++] = ' ';
+      buf[count++] = '1';
     }
 
-  if (cpp_push_buffer (pfile, (U_CHAR *)buf, count - 1) != NULL)
-    {
-      do_define (pfile);
-      cpp_pop_buffer (pfile);
-    }
+  run_directive (pfile, T_DEFINE, BUF_CL_OPTION, buf, count);
+}
+
+/* Slight variant of the above for use by initialize_builtins.  */
+void
+_cpp_define_builtin (pfile, str)
+     cpp_reader *pfile;
+     const char *str;
+{
+  run_directive (pfile, T_DEFINE, BUF_BUILTIN, str, strlen (str));
 }
 
 /* Process MACRO as if it appeared as the body of an #undef.  */
@@ -1681,17 +1668,7 @@ cpp_undef (pfile, macro)
      cpp_reader *pfile;
      const char *macro;
 {
-  /* Copy the string so we can append a newline.  */
-  size_t len = strlen (macro);
-  char *buf = (char *) alloca (len + 2);
-  memcpy (buf, macro, len);
-  buf[len]     = '\n';
-  buf[len + 1] = '\0';
-  if (cpp_push_buffer (pfile, (U_CHAR *)buf, len + 1) != NULL)
-    {
-      do_undef (pfile);
-      cpp_pop_buffer (pfile);
-    }
+  run_directive (pfile, T_UNDEF, BUF_CL_OPTION, macro, strlen (macro));
 }
 
 /* Process the string STR as if it appeared as the body of a #assert. */
@@ -1700,11 +1677,7 @@ cpp_assert (pfile, str)
      cpp_reader *pfile;
      const char *str;
 {
-  if (cpp_push_buffer (pfile, (const U_CHAR *)str, strlen (str)) != NULL)
-    {
-      do_assert (pfile);
-      cpp_pop_buffer (pfile);
-    }
+  handle_assertion (pfile, str, T_ASSERT);
 }
 
 /* Process STR as if it appeared as the body of an #unassert. */
@@ -1713,25 +1686,171 @@ cpp_unassert (pfile, str)
      cpp_reader *pfile;
      const char *str;
 {
-  if (cpp_push_buffer (pfile, (const U_CHAR *)str, strlen (str)) != NULL)
+  handle_assertion (pfile, str, T_UNASSERT);
+}  
+
+/* Common code for cpp_assert (-A) and cpp_unassert (-A-).  */
+static void
+handle_assertion (pfile, str, type)
+     cpp_reader *pfile;
+     const char *str;
+     int type;
+{
+  size_t count = strlen (str);
+  const char *p = strchr (str, '=');
+
+  if (p)
     {
-      do_unassert (pfile);
-      cpp_pop_buffer (pfile);
+      /* Copy the entire option so we can modify it.  Change the first
+        "=" in the string to a '(', and tack a ')' on the end.  */
+      char *buf = (char *) alloca (count + 1);
+
+      memcpy (buf, str, count);
+      buf[p - str] = '(';
+      buf[count++] = ')';
+      str = buf;
     }
-}  
 
-/* Determine whether the identifier ID, of length LEN, is a defined macro.  */
-int
-cpp_defined (pfile, id, len)
+  run_directive (pfile, type, BUF_CL_OPTION, str, count);
+}
+
+/* Push a new buffer on the buffer stack.  Returns the new buffer; it
+   doesn't fail.  It does not generate a file change call back; that
+   is the responsibility of the caller.  */
+cpp_buffer *
+cpp_push_buffer (pfile, buffer, len, type, filename)
      cpp_reader *pfile;
-     const U_CHAR *id;
-     int len;
+     const U_CHAR *buffer;
+     size_t len;
+     enum cpp_buffer_type type;
+     const char *filename;
 {
-  HASHNODE *hp = _cpp_lookup (pfile, id, len);
-  if (hp->type == T_POISON)
+  cpp_buffer *new = xobnew (pfile->buffer_ob, cpp_buffer);
+
+  if (type == BUF_FAKE)
     {
-      cpp_error (pfile, "attempt to use poisoned `%s'", hp->name);
-      return 0;
+      /* A copy of the current buffer, just with a new name and type.  */
+      memcpy (new, pfile->buffer, sizeof (cpp_buffer));
+      new->type = BUF_FAKE;
     }
-  return (hp->type != T_VOID);
+  else
+    {
+      if (type == BUF_BUILTIN)
+       filename = _("<builtin>");
+      else if (type == BUF_CL_OPTION)
+       filename = _("<command line>");
+      else if (type == BUF_PRAGMA)
+       filename = "<_Pragma>";
+
+      /* Clears, amongst other things, if_stack and mi_cmacro.  */
+      memset (new, 0, sizeof (cpp_buffer));
+
+      new->line_base = new->buf = new->cur = buffer;
+      new->rlimit = buffer + len;
+      new->sysp = 0;
+
+      /* No read ahead or extra char initially.  */
+      new->read_ahead = EOF;
+      new->extra_char = EOF;
+
+      /* Preprocessed files, builtins, _Pragma and command line
+        options don't do trigraph and escaped newline processing.  */
+      new->from_stage3 = type != BUF_FILE || CPP_OPTION (pfile, preprocessed);
+
+      pfile->lexer_pos.output_line = 1;
+    }
+
+  new->nominal_fname = filename;
+  new->type = type;
+  new->prev = pfile->buffer;
+  new->pfile = pfile;
+  new->include_stack_listed = 0;
+  new->lineno = 1;
+
+  pfile->state.next_bol = 1;
+  pfile->buffer_stack_depth++;
+  pfile->buffer = new;
+
+  return new;
+}
+
+/* If called from do_line, pops a single buffer.  Otherwise pops all
+   buffers until a real file is reached.  Generates appropriate
+   call-backs.  */
+cpp_buffer *
+cpp_pop_buffer (pfile)
+     cpp_reader *pfile;
+{
+  cpp_buffer *buffer;
+  struct if_stack *ifs;
+
+  for (;;)
+    {
+      buffer = pfile->buffer;
+      /* Walk back up the conditional stack till we reach its level at
+        entry to this file, issuing error messages.  */
+      for (ifs = buffer->if_stack; ifs; ifs = ifs->next)
+       cpp_error_with_line (pfile, ifs->pos.line, ifs->pos.col,
+                            "unterminated #%s", dtable[ifs->type].name);
+
+      if (buffer->type == BUF_FAKE)
+       buffer->prev->cur = buffer->cur;
+      else if (buffer->type == BUF_FILE)
+       _cpp_pop_file_buffer (pfile, buffer);
+
+      pfile->buffer = buffer->prev;
+      pfile->buffer_stack_depth--;
+
+      /* Callbacks only generated for faked or real files.  */
+      if (buffer->type != BUF_FILE && buffer->type != BUF_FAKE)
+       break;
+         
+      /* No callback for EOF of last file.  */
+      if (!pfile->buffer)
+       break;
+
+      /* do_line does its own call backs.  */
+      pfile->buffer->include_stack_listed = 0;
+      if (pfile->directive == &dtable[T_LINE])
+       break;
+
+      _cpp_do_file_change (pfile, FC_LEAVE, buffer->nominal_fname,
+                          buffer->lineno);
+      if (pfile->buffer->type == BUF_FILE)
+       break;
+
+      cpp_warning (pfile, "file \"%s\" entered but not left",
+                  buffer->nominal_fname);
+    }
+
+  obstack_free (pfile->buffer_ob, buffer);
+  return pfile->buffer;
+}
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+void
+_cpp_init_stacks (pfile)
+     cpp_reader *pfile;
+{
+  int i;
+  cpp_hashnode *node;
+
+  pfile->buffer_ob = xnew (struct obstack);
+  obstack_init (pfile->buffer_ob);
+
+  /* Register the directives.  */
+  for (i = 1; i < N_DIRECTIVES; i++)
+    {
+      node = cpp_lookup (pfile, dtable[i - 1].name, dtable[i - 1].length);
+      node->directive_index = i;
+    }
+}
+
+void
+_cpp_cleanup_stacks (pfile)
+     cpp_reader *pfile;
+{
+  obstack_free (pfile->buffer_ob, 0);
+  free (pfile->buffer_ob);
 }