OSDN Git Service

update copyrights
[pf3gnuchains/gcc-fork.git] / gcc / cpplib.c
index 3fc39dc..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,77 +42,110 @@ struct directive
 struct if_stack
 {
   struct if_stack *next;
-  int lineno;                  /* line number where condition started */
-  int was_skipping;            /* value of pfile->skipping before this if */
-  const cpp_hashnode *cmacro;  /* 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 */
 };
 
+/* 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 unsigned int parse_include      PARAMS ((cpp_reader *, const U_CHAR *));
-static void push_conditional           PARAMS ((cpp_reader *, int, int,
-                                                const cpp_hashnode *));
-static void pass_thru_directive                PARAMS ((const U_CHAR *, size_t,
-                                                cpp_reader *, int));
-static int read_line_number            PARAMS ((cpp_reader *, int *));
-static const cpp_hashnode *parse_ifdef PARAMS ((cpp_reader *, const U_CHAR *));
-static const cpp_hashnode *detect_if_not_defined PARAMS ((cpp_reader *));
-
-/* 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.  */
 
@@ -122,846 +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->dirno = -1;
-  list->flags &= ~SYNTAX_INCLUDE;
+  /* 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->dirno = i;
-       if (dtable[i].flags & SYNTAX_INCLUDE)
-         list->flags |= SYNTAX_INCLUDE;
-       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);
+
+  /* Lex the directive name directly.  */
+  _cpp_lex_token (pfile, &dname);
 
-  if (CPP_IS_MACRO_BUFFER (CPP_BUFFER (pfile)))
+  if (dname.type == CPP_NAME)
     {
-      cpp_ice (pfile, "handle_directive called on macro buffer");
-      return 0;
+      unsigned int index = dname.val.node->directive_index;
+      if (index)
+       dir = &dtable[index - 1];
+    }
+  else if (dname.type == CPP_NUMBER)
+    {
+      /* # 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 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 (tok == CPP_NUMBER)
+  pfile->directive = dir;
+  if (dir)
     {
-      if (pfile->skipping || 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)->inc
-         && ! CPP_OPTION (pfile, preprocessed))
-       cpp_pedwarn (pfile, "# followed by integer");
-      i = T_LINE;
-      goto process_directive;
-    }
+      /* 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.  Don't complain about invalid directives
-        in skipped conditional groups (6.10 p4). */
-      if (!pfile->skipping && !CPP_OPTION (pfile, lang_asm))
-       cpp_error (pfile, "invalid preprocessing directive #%.*s",
-                  (int) len, 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;
+}
+
+/* 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;
 
-  /* If we are skipping a failed conditional group, all non-conditional
-     directives are ignored.  */
-  if (pfile->skipping && ORIGIN (dtable[i].flags) != COND)
-    return 0;
+  buffer = cpp_push_buffer (pfile, (const U_CHAR *) buf, count, type, 0);
 
-  /* In -traditional mode, a directive is ignored unless its # is in
-     column 1.  */
-  if (CPP_TRADITIONAL (pfile) && !hash_at_bol)
+  if (dir_no == T_PRAGMA)
     {
-      if (CPP_WTRADITIONAL (pfile))
-       cpp_warning (pfile, "ignoring #%s because of its indented #",
-                    dtable[i].name);
-      return 0;
+      /* 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;
     }
 
-  /* 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)
-    {
-      cpp_error (pfile, "#%s may not be used inside a macro argument",
-                dtable[i].name);
-      _cpp_skip_rest_of_line (pfile);
-      return 1;
-    }
+  start_directive (pfile);
+  pfile->state.prevent_expansion++;
+  (void) (*dtable[dir_no].handler) (pfile);
+  pfile->state.prevent_expansion--;
+  check_eol (pfile);
+  end_directive (pfile, 1);
 
-  /* 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);
+  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;
 
-  /* -Wtraditional gives warnings about directives with inappropriate
-     indentation of #.  */
-  if (CPP_WTRADITIONAL (pfile))
+  /* 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.  */
+
+  if (token.type != CPP_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
     {
-      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);
+      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);
 
- process_directive:
-  (void) (*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);  
+
+  /* 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);
 
-/* Process a #define command.  */
+      _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;
 {
-  cpp_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++;
-  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_until (pfile, list, CPP_VSPACE);
-
-  /* First token on the line must be a NAME.  There may not be any
-     tokens in the list (if we had #define all by itself on a line).  */
-  if (list->tokens_used == 0
-      || 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--;
-  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)
+  /* Allow macro expansion.  */
+  cpp_get_token (pfile, header);
+  if (header->type != CPP_STRING && header->type != CPP_HEADER_NAME)
     {
-      cpp_error (pfile, "#%s expects \"FILENAME\" or <FILENAME>", name);
-      CPP_SET_WRITTEN (pfile, old_written);
-      _cpp_skip_rest_of_line (pfile);
-      return 0;
+      if (header->type != CPP_LESS)
+       {
+         cpp_error (pfile, "#%s expects \"FILENAME\" or <FILENAME>", dir);
+         return 1;
+       }
+      if (glue_header_name (pfile, header))
+       return 1;
     }
 
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
+  if (header->val.str.len == 0)
     {
-      cpp_error (pfile, "junk at end of #%s", name);
-      _cpp_skip_rest_of_line (pfile);
+      cpp_error (pfile, "empty file name in #%s", dir);
+      return 1;
     }
 
-  CPP_SET_WRITTEN (pfile, old_written);
-
-  if (len == 0)
-    cpp_error (pfile, "empty file name in #%s", name);
+  if (!is_pragma)
+    {
+      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);
+    }
 
-  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_IN_SYSTEM_HEADER (pfile) && !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)->inc->foundhere)
-       search_start = CPP_BUFFER (pfile)->inc->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");
-      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_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_PUTC (pfile, '\0');  /* not terminated for us */
-  new_lineno = strtoul ((const char *) (pfile->token_buffer + old_written),
-                       &x, 10);
-  if (x[0] != '\0')
-    {
-      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");
 
-  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");
+         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->inc->sysp = 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->inc->sysp = 0;
-             read_line_number (pfile, &action_number);
+             reason = FC_LEAVE;
+             flag = read_flag (pfile, flag);
            }
-         if (action_number == 3)
+         if (flag == 3)
            {
-             ip->inc->sysp = 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->inc->sysp = 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->inc->name))
-           ip->nominal_fname = ip->inc->name;
-         else
-           ip->nominal_fname = _cpp_fake_include (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, "second token after #line is not a string");
-      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;
-  cpp_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);
-
-  /* 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_token str;
 
-  cpp_error (pfile, "invalid #ident");
-  _cpp_skip_rest_of_line (pfile);
-  CPP_SET_WRITTEN (pfile, old_written);  /* discard directive */
+  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);
 
-  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 (!CPP_IN_SYSTEM_HEADER (pfile))
-    cpp_warning (pfile, "#pragma once is obsolete");
-      
-  if (CPP_PREV_BUFFER (ip) == NULL)
-    cpp_warning (pfile, "#pragma once outside include file");
-  else
-    ip->inc->cmacro = NEVER_REREAD;
+  /* top level */
+  cpp_register_pragma (pfile, 0, "poison", do_pragma_poison);
+  cpp_register_pragma (pfile, 0, "once", do_pragma_once);
 
-  return 1;
+  /* GCC namespace */
+  cpp_register_pragma_space (pfile, "GCC");
+
+  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;
+
+  p = pfile->pragmas;
+  pfile->state.prevent_expansion++;
+  cpp_start_lookahead (pfile);
 
-  token = _cpp_get_directive_token (pfile);
-  if (token == CPP_VSPACE)
-    return 0;
-  else if (token != CPP_STRING)
+ 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;
+  cpp_token tok;
   cpp_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);
 
+  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
@@ -970,313 +1085,282 @@ 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->inc->sysp = 1;
+    cpp_make_system_header (pfile, 1, 0);
 
-  return 1;
+  check_eol (pfile);
 }
-/* Just ignore #sccs, on systems where we define it at all.  */
-#ifdef SCCS_DIRECTIVE
-static int
-do_sccs (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;
 {
-  _cpp_skip_rest_of_line (pfile);
-  return 0;
+  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)
+    {
+      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);
+    }
 }
-#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 the
-   hash node for SYMBOL.  Otherwise, return NULL.  */
-
-static const cpp_hashnode *
-detect_if_not_defined (pfile)
+/* Check syntax is "(string-literal)".  Returns 0 on success.  */
+static int
+get__Pragma_string (pfile, string)
      cpp_reader *pfile;
+     cpp_token *string;
 {
-  const cpp_hashnode *cmacro = 0;
-  enum cpp_ttype token;
-  unsigned int base_offset;
-  unsigned int token_offset;
-  unsigned int need_rparen = 0;
-  unsigned int token_len;
-
-  if (pfile->skipping || 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)
-    {
-      token_offset = CPP_WRITTEN (pfile);
-      need_rparen = 1;
-      token = _cpp_get_directive_token (pfile);
-    }
-  if (token != CPP_NAME)
-    goto restore;
+  cpp_token paren;
 
-  token_len = CPP_WRITTEN (pfile) - token_offset;
+  cpp_get_token (pfile, &paren);
+  if (paren.type != CPP_OPEN_PAREN)
+    return 1;
 
-  /* ...then the ')', if necessary, */
-  if (need_rparen && _cpp_get_directive_token (pfile) != CPP_CLOSE_PAREN)
-    goto restore;
+  cpp_get_token (pfile, string);
+  if (string->type != CPP_STRING && string->type != CPP_WSTRING)
+    return 1;
 
-  /* ...and make sure there's nothing else on the line.  */
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
-    goto restore;
+  cpp_get_token (pfile, &paren);
+  return paren.type != CPP_CLOSE_PAREN;
+}
 
-  /* We have a legitimate controlling macro for this header.  */
-  cmacro = cpp_lookup (pfile, pfile->token_buffer + token_offset, token_len);
+/* 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;
+{
+  const unsigned char *src, *limit;
+  unsigned char *dest, *result;
 
- restore:
-  CPP_SET_WRITTEN (pfile, base_offset);
-  pfile->no_macro_expand--;
-  CPP_GOTO_MARK (pfile);
+  dest = result = (unsigned char *) xmalloc (in->len);
+  for (src = in->text, limit = src + in->len; src < limit;)
+    {
+      /* We know there is a character following the backslash.  */
+      if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
+       src++;
+      *dest++ = *src++;
+    }
 
-  return cmacro;
+  *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 const cpp_hashnode *
-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);
-  const cpp_hashnode *node = 0;
-
-  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);
-      goto done;
-    }
-  else if (token == CPP_NAME)
+  if (get__Pragma_string (pfile, &string))
     {
-      node = cpp_lookup (pfile, ident, len);
-    }
-  else
-    {
-      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", name);
-    }
-  _cpp_skip_rest_of_line (pfile);
-  
- done:
-  CPP_SET_WRITTEN (pfile, old_written); /* Pop */
-  return node;
+  buffer = destringize (&string.val.str, &len);
+  run_directive (pfile, T_PRAGMA, BUF_PRAGMA, (char *) buffer, len);
+  free ((PTR) buffer);
 }
 
-/* #ifdef is dead simple.  */
+/* Just ignore #sccs, on systems where we define it at all.  */
+#ifdef SCCS_DIRECTIVE
+static void
+do_sccs (pfile)
+     cpp_reader *pfile ATTRIBUTE_UNUSED;
+{
+}
+#endif
 
-static int
+static void
 do_ifdef (pfile)
      cpp_reader *pfile;
 {
-  int def = 0;
-  const cpp_hashnode *node = parse_ifdef (pfile, dtable[T_IFDEF].name);
-  if (node)
+  int skip = 1;
+
+  if (! pfile->buffer->was_skipping)
     {
-      if (node->type == T_POISON)
-       cpp_error (pfile, "attempt to use poisoned `%s'", node->name);
-      else
-       def = (node->type != T_VOID);
+      const cpp_hashnode *node = lex_macro_node (pfile);
+
+      if (node)
+       skip = node->type != NT_MACRO;
+
+      if (node)
+       check_eol (pfile);
     }
-  push_conditional (pfile, !def, T_IFDEF, 0);
-  return 0;
-}
 
-/* #ifndef is a tad more complex, because we need to check for a
-   no-reinclusion wrapper.  */
+  push_conditional (pfile, skip, T_IFDEF, 0);
+}
 
-static int
+static void
 do_ifndef (pfile)
      cpp_reader *pfile;
 {
-  int start_of_file;
-  int def = 0;
-  const cpp_hashnode *cmacro;
+  int skip = 1;
+  const cpp_hashnode *node = 0;
 
-  start_of_file = pfile->only_seen_white == 2;
-  cmacro = parse_ifdef (pfile, dtable[T_IFNDEF].name);
-  if (cmacro)
+  if (! pfile->buffer->was_skipping)
     {
-      if (cmacro->type == T_POISON)
-       cpp_error (pfile, "attempt to use poisoned `%s'", cmacro->name);
-      else
-       def = (cmacro->type != T_VOID);
+      node = lex_macro_node (pfile);
+      if (node)
+       skip = node->type == NT_MACRO;
+
+      if (node)
+       check_eol (pfile);
     }
-  push_conditional (pfile, def, T_IFNDEF,
-                   start_of_file ? cmacro : 0);
-  return 0;
+
+  push_conditional (pfile, skip, T_IFNDEF, node);
 }
 
-/* #if is straightforward; just call _cpp_parse_expr, then conditional_skip.
-   Also, check for a reinclude preventer of the form #if !defined (MACRO).  */
+/* #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
+static void
 do_if (pfile)
      cpp_reader *pfile;
 {
+  int skip = 1;
   const cpp_hashnode *cmacro = 0;
-  int value = 0;
 
-  if (! pfile->skipping)
+  if (! pfile->buffer->was_skipping)
     {
-      cmacro = detect_if_not_defined (pfile);  
-      value = _cpp_parse_expr (pfile);
+      /* Controlling macro of #if ! defined ()  */
+      pfile->mi_ind_cmacro = 0;
+      skip = _cpp_parse_expr (pfile) == 0;
+      cmacro = pfile->mi_ind_cmacro;
     }
-  push_conditional (pfile, value == 0, T_IF, cmacro);
-  return 0;
+
+  push_conditional (pfile, skip, T_IF, cmacro);
 }
 
-/* #else flips pfile->skipping and continues without changing
+/* 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.  */
 
-static int
+static void
 do_else (pfile)
      cpp_reader *pfile;
 {
-  struct if_stack *ifs = CPP_BUFFER (pfile)->if_stack;
-
-  validate_else (pfile, dtable[T_ELSE].name);
+  cpp_buffer *buffer = pfile->buffer;
+  struct if_stack *ifs = buffer->if_stack;
 
   if (ifs == NULL)
+    cpp_error (pfile, "#else without #if");
+  else
     {
-      cpp_error (pfile, "#else without #if");
-      return 0;
-    }
-  if (ifs->type == T_ELSE)
-    {
-      cpp_error (pfile, "#else after #else");
-      cpp_error_with_line (pfile, ifs->lineno, 1, "the conditional began here");
-    }
+      if (ifs->type == T_ELSE)
+       {
+         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;
 
-  /* #ifndef can't have its special treatment for containing the whole file
-     if it has a #else clause.  */
-  ifs->cmacro = 0;
+      /* 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;
 
-  ifs->type = T_ELSE;
-  if (! ifs->was_skipping)
-    {
-      /* If pfile->skipping is 2, one of the blocks in an #if/#elif/... chain
-        succeeded, so we mustn't do the else block.  */
-      if (pfile->skipping < 2)
-       pfile->skipping = ! pfile->skipping;
+      /* Invalidate any controlling macro.  */
+      ifs->mi_cmacro = 0;
     }
-  return 0;
+
+  check_eol (pfile);
 }
 
-/*
- * handle a #elif directive by not changing if_stack either.
- * see the comment above do_else.
- */
+/* handle a #elif directive by not changing if_stack either.  see the
+   comment above do_else.  */
 
-static int
+static void
 do_elif (pfile)
      cpp_reader *pfile;
 {
-  struct if_stack *ifs = CPP_BUFFER (pfile)->if_stack;
+  cpp_buffer *buffer = pfile->buffer;
+  struct if_stack *ifs = buffer->if_stack;
 
   if (ifs == NULL)
+    cpp_error (pfile, "#elif without #if");
+  else
     {
-      cpp_error (pfile, "#elif without #if");
-      return 0;
-    }
-  if (ifs->type == T_ELSE)
-    {
-      cpp_error (pfile, "#elif after #else");
-      cpp_error_with_line (pfile, ifs->lineno, 1, "the conditional began here");
-    }
+      if (ifs->type == T_ELSE)
+       {
+         cpp_error (pfile, "#elif after #else");
+         cpp_error_with_line (pfile, ifs->pos.line, ifs->pos.col,
+                              "the conditional began here");
+       }
+      ifs->type = T_ELIF;
 
-  ifs->type = T_ELIF;
-  if (ifs->was_skipping)
-    _cpp_skip_rest_of_line (pfile);
-  else if (pfile->skipping != 1)
-    {
-      _cpp_skip_rest_of_line (pfile);
-      pfile->skipping = 2;  /* one block succeeded, so don't do any others */
-    }
-  else
-    pfile->skipping = ! _cpp_parse_expr (pfile);
+      /* 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;
 
-  return 0;
+         /* Invalidate any controlling macro.  */
+         ifs->mi_cmacro = 0;
+       }
+    }
 }
 
-
 /* #endif pops the if stack and resets pfile->skipping.  */
 
-static int
+static void
 do_endif (pfile)
      cpp_reader *pfile;
 {
-  struct if_stack *ifs = CPP_BUFFER (pfile)->if_stack;
-
-  validate_else (pfile, dtable[T_ENDIF].name);
+  cpp_buffer *buffer = pfile->buffer;
+  struct if_stack *ifs = buffer->if_stack;
 
   if (ifs == NULL)
     cpp_error (pfile, "#endif without #if");
   else
     {
-      CPP_BUFFER (pfile)->if_stack = ifs->next;
-      pfile->skipping = ifs->was_skipping;
-      pfile->potential_control_macro = ifs->cmacro;
-      free (ifs);
+      /* 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);
 }
 
 /* Push an if_stack entry and set pfile->skipping accordingly.
@@ -1291,228 +1375,246 @@ push_conditional (pfile, skip, type, cmacro)
      const cpp_hashnode *cmacro;
 {
   struct if_stack *ifs;
+  cpp_buffer *buffer = pfile->buffer;
 
-  ifs = (struct if_stack *) xmalloc (sizeof (struct if_stack));
-  ifs->lineno = CPP_BUFFER (pfile)->lineno;
-  ifs->next = CPP_BUFFER (pfile)->if_stack;
-  ifs->cmacro = cmacro;
-  ifs->was_skipping = pfile->skipping;
+  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;
 
-  if (!pfile->skipping)
-    pfile->skipping = skip;
-
-  CPP_BUFFER (pfile)->if_stack = ifs;
+  buffer->was_skipping = skip;
+  buffer->if_stack = ifs;
 }
 
-/* Issue -pedantic warning for text which is not a comment following
-   an #else or #endif.  */
+/* 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 void
-validate_else (pfile, directive)
+static int
+parse_answer (pfile, answerp, type)
      cpp_reader *pfile;
-     const U_CHAR *directive;
+     struct answer **answerp;
+     int type;
 {
-  if (CPP_PEDANTIC (pfile))
+  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)
     {
-      long old_written = CPP_WRITTEN (pfile);
-      pfile->no_macro_expand++;
-      if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
-       cpp_pedwarn (pfile, "ISO C forbids text after #%s", directive);
-      CPP_SET_WRITTEN (pfile, old_written);
-      pfile->no_macro_expand--;
-    }
-  _cpp_skip_rest_of_line (pfile);
-}
+      /* In a conditional no answer is a test for any answer.  It
+         could be followed by any token.  */
+      if (type == T_IF)
+       return 0;
 
-/* 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.  Then force skipping off.  */
-void
-_cpp_unwind_if_stack (pfile, pbuf)
-     cpp_reader *pfile;
-     cpp_buffer *pbuf;
-{
-  struct if_stack *ifs, *nifs;
+      /* #unassert with no answer is valid - it removes all answers.  */
+      if (type == T_UNASSERT && paren.type == CPP_EOF)
+       return 0;
 
-  for (ifs = pbuf->if_stack; ifs; ifs = nifs)
-    {
-      cpp_error_with_line (pfile, ifs->lineno, 1, "unterminated #%s",
-                          dtable[ifs->type].name);
-      nifs = ifs->next;
-      free (ifs);
+      cpp_error (pfile, "missing '(' after predicate");
+      return 1;
     }
-  pfile->skipping = 0;
-}
 
-#define WARNING(msgid) do { cpp_warning(pfile, msgid); goto error; } while (0)
-#define ERROR(msgid) do { cpp_error(pfile, msgid); goto error; } while (0)
-#define ICE(msgid) do { cpp_ice(pfile, msgid); goto error; } while (0)
-static int
-do_assert (pfile)
-     cpp_reader *pfile;
-{
-  long old_written;
-  U_CHAR *sym;
-  size_t len;
-  cpp_hashnode *hp;
-  struct predicate *pred = 0;
-  enum cpp_ttype type;
-
-  old_written = CPP_WRITTEN (pfile);
-  pfile->no_macro_expand++;
-
-  CPP_PUTC (pfile, '#');       /* force token out of macro namespace */
-  type = _cpp_get_directive_token (pfile);
-  if (type == CPP_VSPACE)
-    ERROR ("#assert without predicate");
-  else if (type != CPP_NAME)
-    ERROR ("assertion predicate is not an identifier");
-
-  sym = pfile->token_buffer + old_written;
-  len = CPP_WRITTEN (pfile) - old_written;
-  hp = cpp_lookup (pfile, sym, len);
-
-  if (_cpp_get_directive_token (pfile) != CPP_OPEN_PAREN)
-    ERROR ("missing token-sequence in #assert");
-
-  pred = (struct predicate *) xmalloc (sizeof (struct predicate));
-  _cpp_init_toklist (&pred->answer, NO_DUMMY_TOKEN);
-
-  if (_cpp_scan_until (pfile, &pred->answer, CPP_CLOSE_PAREN)
-      != CPP_CLOSE_PAREN)
-    ERROR ("missing close paren in #assert");
-
-  if (_cpp_get_directive_token (pfile) != CPP_CLOSE_PAREN)
-    ICE ("impossible token, expecting ) in do_assert");
+  for (;;)
+    {
+      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];
+       }
 
-  if (_cpp_get_directive_token (pfile) != CPP_VSPACE)
-    ERROR ("junk at end of #assert");
+      cpp_get_token (pfile, token);
+      if (token->type == CPP_CLOSE_PAREN)
+       break;
 
-  if (hp->type == T_ASSERTION)
-    {
-      /* Check for reassertion.  */
-      const struct predicate *old;
-
-      for (old = hp->value.pred; old; old = old->next)
-       if (_cpp_equiv_toklists (&pred->answer, &old->answer))
-         /* We used to warn about this, but SVR4 cc doesn't, so let's
-            match that (also consistent with #define).  goto error will
-            clean up.  */
-         goto error;
-      pred->next = hp->value.pred;
-    }
-  else
-    {
-      hp->type = T_ASSERTION;
-      pred->next = 0;
+      if (token->type == CPP_EOF)
+       {
+         cpp_error (pfile, "missing ')' to complete answer");
+         return 1;
+       }
+      answer->count++;
     }
-  
-  _cpp_squeeze_toklist (&pred->answer);
-  hp->value.pred = pred;
-  pfile->no_macro_expand--;
-  CPP_SET_WRITTEN (pfile, old_written);
-  return 0;
 
- error:
-  _cpp_skip_rest_of_line (pfile);
-  pfile->no_macro_expand--;
-  CPP_SET_WRITTEN (pfile, old_written);
-  if (pred)
+  if (answer->count == 0)
     {
-      _cpp_free_toklist (&pred->answer);
-      free (pred);
+      cpp_error (pfile, "predicate's answer is empty");
+      return 1;
     }
+
+  /* Drop whitespace at start.  */
+  answer->first->flags &= ~PREV_WHITE;
+  *answerp = answer;
+
+  if (type == T_ASSERT || type == T_UNASSERT)
+    check_eol (pfile);
   return 0;
 }
 
-static int
-do_unassert (pfile)
+/* 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;
 {
-  long old_written;
-  U_CHAR *sym;
-  size_t len;
-  cpp_hashnode *hp;
-  cpp_toklist ans;
-  enum cpp_ttype type;
-  int specific = 0;
+  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)
+    {
+      unsigned int len = predicate.val.node->length;
+      unsigned char *sym = alloca (len + 1);
 
-  old_written = CPP_WRITTEN (pfile);
-  pfile->no_macro_expand++;
+      /* 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);
+    }
 
-  CPP_PUTC (pfile, '#');       /* force token out of macro namespace */
-  if (_cpp_get_directive_token (pfile) != CPP_NAME)
-    ERROR ("#unassert must be followed by an identifier");
+  pfile->state.prevent_expansion--;
+  return result;
+}
 
-  sym = pfile->token_buffer + old_written;
-  len = CPP_WRITTEN (pfile) - old_written;
-  hp = cpp_lookup (pfile, sym, len);
+/* 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;
 
-  type = _cpp_get_directive_token (pfile);
-  if (type == CPP_OPEN_PAREN)
+  for (result = &node->value.answers; *result; result = &(*result)->next)
     {
-      specific = 1;
-      _cpp_init_toklist (&ans, NO_DUMMY_TOKEN);
-
-      if (_cpp_scan_until (pfile, &ans, CPP_CLOSE_PAREN)
-         != CPP_CLOSE_PAREN)
-       ERROR ("missing close paren in #unassert");
+      struct answer *answer = *result;
 
-      if (_cpp_get_directive_token (pfile) != CPP_CLOSE_PAREN)
-       ICE ("impossible token, expecting ) in do_unassert");
+      if (answer->count == candidate->count)
+       {
+         for (i = 0; i < answer->count; i++)
+           if (! _cpp_equiv_tokens (&answer->first[i], &candidate->first[i]))
+             break;
 
-      type = _cpp_get_directive_token (pfile);
+         if (i == answer->count)
+           break;
+       }
     }
 
-  if (type != CPP_VSPACE)
-    ERROR ("junk at end of #unassert");
+  return result;
+}
+
+/* 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));
 
-  if (hp->type != T_ASSERTION)
-    /* Not an error to #unassert something that isn't asserted.
-       goto error to clean up.  */
-    goto error;
+  /* We don't commit the memory for the answer - it's temporary only.  */
+  return node == 0;
+}
 
-  if (specific)
+static void
+do_assert (pfile)
+     cpp_reader *pfile;
+{
+  struct answer *new_answer;
+  cpp_hashnode *node;
+  
+  node = parse_assertion (pfile, &new_answer, T_ASSERT);
+  if (node)
     {
-      /* Find this specific answer and remove it.  */
-      struct predicate *o, *p;
-
-      for (p = NULL, o = hp->value.pred; o; p = o, o = o->next)
-       if (_cpp_equiv_toklists (&ans, &o->answer))
-         {
-           if (p)
-             p->next = o->next;
-           else
-             hp->value.pred = o->next;
-
-           _cpp_free_toklist (&o->answer);
-           free (o);
-           break;
-         }
+      /* 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)));
     }
-  else
+}
+
+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)
     {
-      struct predicate *o, *p;
-      for (o = hp->value.pred; o; o = p)
+      if (answer)
        {
-         p = o->next;
-         _cpp_free_toklist ((cpp_toklist *) &o->answer);
-         free (o);
+         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;
        }
-      hp->value.pred = NULL;
+      else
+       _cpp_free_definition (node);
     }
 
-  if (hp->value.pred == NULL)
-    hp->type = T_VOID;  /* Last answer for this predicate deleted.  */
-
- error:
-  _cpp_skip_rest_of_line (pfile);
-  pfile->no_macro_expand--;
-  CPP_SET_WRITTEN (pfile, old_written);
-  if (specific)
-    _cpp_free_toklist (&ans);
-  return 0;
+  /* We don't commit the memory for the answer - it's temporary only.  */
 }
 
 /* These are for -D, -U, -A.  */
@@ -1530,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.  */
@@ -1565,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. */
@@ -1584,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. */
@@ -1597,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;
 {
-  cpp_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);
 }