OSDN Git Service

* cpphash.h (struct cpp_buffer): Move saved_flags from cpp_reader.
[pf3gnuchains/gcc-fork.git] / gcc / cpplex.c
index f0414a0..3ff23b4 100644 (file)
@@ -20,3317 +20,1300 @@ You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+/* This lexer works with a single pass of the file.  Recently I
+   re-wrote it to minimize the places where we step backwards in the
+   input stream, to make future changes to support multi-byte
+   character sets fairly straight-forward.
+
+   There is now only one routine where we do step backwards:
+   skip_escaped_newlines.  This routine could probably also be changed
+   so that it doesn't need to step back.  One possibility is to use a
+   trick similar to that used in lex_period and lex_percent.  Two
+   extra characters might be needed, but skip_escaped_newlines itself
+   would probably be the only place that needs to be aware of that,
+   and changes to the remaining routines would probably only be needed
+   if they process a backslash.  */
+
 #include "config.h"
 #include "system.h"
-#include "intl.h"
 #include "cpplib.h"
 #include "cpphash.h"
+#include "symcat.h"
 
-#define PEEKBUF(BUFFER, N) \
-  ((BUFFER)->rlimit - (BUFFER)->cur > (N) ? (BUFFER)->cur[N] : EOF)
-#define GETBUF(BUFFER) \
-  ((BUFFER)->cur < (BUFFER)->rlimit ? *(BUFFER)->cur++ : EOF)
-#define FORWARDBUF(BUFFER, N) ((BUFFER)->cur += (N))
-
-#define PEEKN(N) PEEKBUF (CPP_BUFFER (pfile), N)
-#define FORWARD(N) FORWARDBUF (CPP_BUFFER (pfile), (N))
-#define GETC() GETBUF (CPP_BUFFER (pfile))
-#define PEEKC() PEEKBUF (CPP_BUFFER (pfile), 0)
-
-static void skip_block_comment PARAMS ((cpp_reader *));
-static void skip_line_comment  PARAMS ((cpp_reader *));
-static int maybe_macroexpand   PARAMS ((cpp_reader *, long));
-static int skip_comment                PARAMS ((cpp_reader *, int));
-static int copy_comment                PARAMS ((cpp_reader *, int));
-static void skip_string                PARAMS ((cpp_reader *, int));
-static void parse_string       PARAMS ((cpp_reader *, int));
-static U_CHAR *find_position   PARAMS ((U_CHAR *, U_CHAR *, unsigned long *));
-static void null_warning        PARAMS ((cpp_reader *, unsigned int));
-
-static void safe_fwrite                PARAMS ((cpp_reader *, const U_CHAR *,
-                                        size_t, FILE *));
-static void output_line_command        PARAMS ((cpp_reader *, cpp_printer *,
-                                        unsigned int));
-static void bump_column                PARAMS ((cpp_printer *, unsigned int,
-                                        unsigned int));
-static void expand_name_space   PARAMS ((cpp_toklist *, unsigned int));
-static void expand_token_space PARAMS ((cpp_toklist *));
-static void init_token_list    PARAMS ((cpp_reader *, cpp_toklist *, int));
-static void pedantic_whitespace        PARAMS ((cpp_reader *, U_CHAR *,
-                                        unsigned int));
-
-#define auto_expand_name_space(list) \
-    expand_name_space ((list), 1 + (list)->name_cap / 2)
-
-#ifdef NEW_LEXER
-
-static void expand_comment_space PARAMS ((cpp_toklist *));
-void init_trigraph_map PARAMS ((void));
-static unsigned char* trigraph_replace PARAMS ((cpp_reader *, unsigned char *,
-                                               unsigned char *));
-static const unsigned char *backslash_start PARAMS ((cpp_reader *,
-                                                    const unsigned char *));
-static int skip_block_comment2 PARAMS ((cpp_reader *));
-static int skip_line_comment2 PARAMS ((cpp_reader *));
-static void skip_whitespace PARAMS ((cpp_reader *, int));
-static void parse_name PARAMS ((cpp_reader *, cpp_toklist *, cpp_name *));
-static void parse_number PARAMS ((cpp_reader *, cpp_toklist *, cpp_name *));
-static void parse_string2 PARAMS ((cpp_reader *, cpp_toklist *, cpp_name *,
-                                 unsigned int));
-static int trigraph_ok PARAMS ((cpp_reader *, const unsigned char *));
-static void save_comment PARAMS ((cpp_toklist *, const unsigned char *,
-                                 unsigned int, unsigned int, unsigned int));
-void _cpp_lex_line PARAMS ((cpp_reader *, cpp_toklist *));
-
-static void _cpp_output_list PARAMS ((cpp_reader *, cpp_toklist *));
-
-static unsigned char * spell_token PARAMS ((cpp_reader *, cpp_token *,
-                                           unsigned char *, int));
-
-typedef unsigned int (* speller) PARAMS ((unsigned char *, cpp_toklist *,
-                                         cpp_token *));
-
-/* Macros on a cpp_name.  */
-#define INIT_NAME(list, name) \
-  do {(name).len = 0; \
-      (name).text = (list)->namebuf + (list)->name_used;} while (0)
-
-#define IS_DIRECTIVE(list) (TOK_TYPE (list, 0) == CPP_HASH)
-
-/* Maybe put these in the ISTABLE eventually.  */
-#define IS_HSPACE(c) ((c) == ' ' || (c) == '\t')
-#define IS_NEWLINE(c) ((c) == '\n' || (c) == '\r')
-
-/* Handle LF, CR, CR-LF and LF-CR style newlines.  Assumes next
-   character, if any, is in buffer.  */
-#define handle_newline(cur, limit, c) \
-  do {\
-  if ((cur) < (limit) && *(cur) == '\r' + '\n' - c) \
-    (cur)++; \
-  CPP_BUMP_LINE_CUR (pfile, (cur)); \
-  pfile->col_adjust = 0; \
-  } while (0)
-
-#define IMMED_TOKEN() (!(cur_token->flags & PREV_WHITESPACE))
-#define PREV_TOKEN_TYPE (cur_token[-1].type)
-
-#define PUSH_TOKEN(ttype) cur_token++->type = ttype
-#define REVISE_TOKEN(ttype) cur_token[-1].type = ttype
-#define BACKUP_TOKEN(ttype) (--cur_token)->type = ttype
-#define BACKUP_DIGRAPH(ttype) do { \
-  BACKUP_TOKEN(ttype); cur_token->flags |= DIGRAPH;} while (0)
+/* Tokens with SPELL_STRING store their spelling in the token list,
+   and it's length in the token->val.name.len.  */
+enum spell_type
+{
+  SPELL_OPERATOR = 0,
+  SPELL_CHAR,
+  SPELL_IDENT,
+  SPELL_STRING,
+  SPELL_NONE
+};
+
+struct token_spelling
+{
+  enum spell_type category;
+  const unsigned char *name;
+};
+
+const unsigned char *digraph_spellings [] = {U"%:", U"%:%:", U"<:",
+                                            U":>", U"<%", U"%>"};
+
+#define OP(e, s) { SPELL_OPERATOR, U s           },
+#define TK(e, s) { s,              U STRINGX (e) },
+const struct token_spelling token_spellings [N_TTYPES] = {TTYPE_TABLE };
+#undef OP
+#undef TK
+
+#define TOKEN_SPELL(token) (token_spellings[(token)->type].category)
+#define TOKEN_NAME(token) (token_spellings[(token)->type].name)
+
+static cppchar_t handle_newline PARAMS ((cpp_buffer *, cppchar_t));
+static cppchar_t skip_escaped_newlines PARAMS ((cpp_buffer *, cppchar_t));
+static cppchar_t get_effective_char PARAMS ((cpp_buffer *));
+
+static int skip_block_comment PARAMS ((cpp_reader *));
+static int skip_line_comment PARAMS ((cpp_reader *));
+static void adjust_column PARAMS ((cpp_reader *));
+static void skip_whitespace PARAMS ((cpp_reader *, cppchar_t));
+static cpp_hashnode *parse_identifier PARAMS ((cpp_reader *, cppchar_t));
+static void parse_number PARAMS ((cpp_reader *, cpp_string *, cppchar_t, int));
+static int unescaped_terminator_p PARAMS ((cpp_reader *, const U_CHAR *));
+static void parse_string PARAMS ((cpp_reader *, cpp_token *, cppchar_t));
+static void unterminated PARAMS ((cpp_reader *, int));
+static int trigraph_ok PARAMS ((cpp_reader *, cppchar_t));
+static void save_comment PARAMS ((cpp_reader *, cpp_token *, const U_CHAR *));
+static void lex_percent PARAMS ((cpp_buffer *, cpp_token *));
+static void lex_dot PARAMS ((cpp_reader *, cpp_token *));
+static int name_p PARAMS ((cpp_reader *, const cpp_string *));
+
+static cpp_chunk *new_chunk PARAMS ((unsigned int));
+static int chunk_suitable PARAMS ((cpp_pool *, cpp_chunk *, unsigned int));
 
-/* An upper bound on the number of bytes needed to spell a token,
-   including preceding whitespace.  */
-#define TOKEN_LEN(token) (5 + (token_spellings[token->type].type > \
-                              SPELL_NONE ? token->val.name.len: 0))
+/* Utility routine:
 
-#endif
+   Compares, the token TOKEN to the NUL-terminated string STRING.
+   TOKEN must be a CPP_NAME.  Returns 1 for equal, 0 for unequal.  */
 
-/* Order here matters.  Those beyond SPELL_NONE store their spelling
-   in the token list, and it's length in the token->val.name.len.  */
-#define SPELL_OPERATOR 0
-#define SPELL_CHAR     2       /* FIXME: revert order after transition. */
-#define SPELL_NONE     1
-#define SPELL_IDENT    3
-#define SPELL_STRING   4
-
-#define T(e, s) {SPELL_OPERATOR, (const U_CHAR *) s},
-#define I(e, s) {SPELL_IDENT, s},
-#define S(e, s) {SPELL_STRING, s},
-#define C(e, s) {SPELL_CHAR, s},
-#define N(e, s) {SPELL_NONE, s},
-
-static const struct token_spelling
+int
+cpp_ideq (token, string)
+     const cpp_token *token;
+     const char *string;
 {
-  U_CHAR type;
-  const U_CHAR *spelling;
-} token_spellings [N_TTYPES + 1] = {TTYPE_TABLE {0, 0} };
-
-#undef T
-#undef I
-#undef S
-#undef C
-#undef N
+  if (token->type != CPP_NAME)
+    return 0;
 
-/* Re-allocates PFILE->token_buffer so it will hold at least N more chars.  */
+  return !ustrcmp (token->val.node->name, (const U_CHAR *) string);
+}
 
-void
-_cpp_grow_token_buffer (pfile, n)
-     cpp_reader *pfile;
-     long n;
+/* Call when meeting a newline.  Returns the character after the newline
+   (or carriage-return newline combination), or EOF.  */
+static cppchar_t
+handle_newline (buffer, newline_char)
+     cpp_buffer *buffer;
+     cppchar_t newline_char;
 {
-  long old_written = CPP_WRITTEN (pfile);
-  pfile->token_buffer_size = n + 2 * pfile->token_buffer_size;
-  pfile->token_buffer = (U_CHAR *)
-    xrealloc(pfile->token_buffer, pfile->token_buffer_size);
-  CPP_SET_WRITTEN (pfile, old_written);
-}
+  cppchar_t next = EOF;
 
-/* Allocate a new cpp_buffer for PFILE, and push it on the input buffer stack.
-   If BUFFER != NULL, then use the LENGTH characters in BUFFER
-   as the new input buffer.
-   Return the new buffer, or NULL on failure.  */
+  buffer->col_adjust = 0;
+  buffer->lineno++;
+  buffer->line_base = buffer->cur;
 
-cpp_buffer *
-cpp_push_buffer (pfile, buffer, length)
-     cpp_reader *pfile;
-     const U_CHAR *buffer;
-     long length;
-{
-  cpp_buffer *buf = CPP_BUFFER (pfile);
-  cpp_buffer *new;
-  if (++pfile->buffer_stack_depth == CPP_STACK_MAX)
+  /* Handle CR-LF and LF-CR combinations, get the next character.  */
+  if (buffer->cur < buffer->rlimit)
     {
-      cpp_fatal (pfile, "macro or `#include' recursion too deep");
-      return NULL;
+      next = *buffer->cur++;
+      if (next + newline_char == '\r' + '\n')
+       {
+         buffer->line_base = buffer->cur;
+         if (buffer->cur < buffer->rlimit)
+           next = *buffer->cur++;
+         else
+           next = EOF;
+       }
     }
 
-  new = (cpp_buffer *) xcalloc (1, sizeof (cpp_buffer));
-
-  new->if_stack = pfile->if_stack;
-  new->buf = new->cur = buffer;
-  new->rlimit = buffer + length;
-  new->prev = buf;
-  new->mark = NULL;
-  new->line_base = NULL;
-
-  CPP_BUFFER (pfile) = new;
-  return new;
+  buffer->read_ahead = next;
+  return next;
 }
 
-cpp_buffer *
-cpp_pop_buffer (pfile)
+/* Subroutine of skip_escaped_newlines; called when a trigraph is
+   encountered.  It warns if necessary, and returns true if the
+   trigraph should be honoured.  FROM_CHAR is the third character of a
+   trigraph, and presumed to be the previous character for position
+   reporting.  */
+static int
+trigraph_ok (pfile, from_char)
      cpp_reader *pfile;
+     cppchar_t from_char;
 {
-  cpp_buffer *buf = CPP_BUFFER (pfile);
-  if (ACTIVE_MARK_P (pfile))
-    cpp_ice (pfile, "mark active in cpp_pop_buffer");
-
-  if (buf->ihash)
+  int accept = CPP_OPTION (pfile, trigraphs);
+  
+  /* Don't warn about trigraphs in comments.  */
+  if (CPP_OPTION (pfile, warn_trigraphs) && !pfile->state.lexing_comment)
     {
-      _cpp_unwind_if_stack (pfile, buf);
-      if (buf->buf)
-       free ((PTR) buf->buf);
-      if (pfile->system_include_depth)
-       pfile->system_include_depth--;
-      if (pfile->potential_control_macro)
+      cpp_buffer *buffer = pfile->buffer;
+      if (accept)
+       cpp_warning_with_line (pfile, buffer->lineno, CPP_BUF_COL (buffer) - 2,
+                              "trigraph ??%c converted to %c",
+                              (int) from_char,
+                              (int) _cpp_trigraph_map[from_char]);
+      else if (buffer->cur != buffer->last_Wtrigraphs)
        {
-         buf->ihash->control_macro = pfile->potential_control_macro;
-         pfile->potential_control_macro = 0;
+         buffer->last_Wtrigraphs = buffer->cur;
+         cpp_warning_with_line (pfile, buffer->lineno,
+                                CPP_BUF_COL (buffer) - 2,
+                                "trigraph ??%c ignored", (int) from_char);
        }
-      pfile->input_stack_listing_current = 0;
     }
-  else if (buf->macro)
+
+  return accept;
+}
+
+/* Assumes local variables buffer and result.  */
+#define ACCEPT_CHAR(t) \
+  do { result->type = t; buffer->read_ahead = EOF; } while (0)
+
+/* When we move to multibyte character sets, add to these something
+   that saves and restores the state of the multibyte conversion
+   library.  This probably involves saving and restoring a "cookie".
+   In the case of glibc it is an 8-byte structure, so is not a high
+   overhead operation.  In any case, it's out of the fast path.  */
+#define SAVE_STATE() do { saved_cur = buffer->cur; } while (0)
+#define RESTORE_STATE() do { buffer->cur = saved_cur; } while (0)
+
+/* Skips any escaped newlines introduced by NEXT, which is either a
+   '?' or a '\\'.  Returns the next character, which will also have
+   been placed in buffer->read_ahead.  This routine performs
+   preprocessing stages 1 and 2 of the ISO C standard.  */
+static cppchar_t
+skip_escaped_newlines (buffer, next)
+     cpp_buffer *buffer;
+     cppchar_t next;
+{
+  /* Only do this if we apply stages 1 and 2.  */
+  if (!buffer->from_stage3)
     {
-      HASHNODE *m = buf->macro;
-  
-      m->disabled = 0;
-      if ((m->type == T_FMACRO && buf->mapped)
-         || m->type == T_SPECLINE || m->type == T_FILE
-         || m->type == T_BASE_FILE || m->type == T_INCLUDE_LEVEL
-         || m->type == T_STDC)
-       free ((PTR) buf->buf);
+      cppchar_t next1;
+      const unsigned char *saved_cur;
+      int space;
+
+      do
+       {
+         if (buffer->cur == buffer->rlimit)
+           break;
+      
+         SAVE_STATE ();
+         if (next == '?')
+           {
+             next1 = *buffer->cur++;
+             if (next1 != '?' || buffer->cur == buffer->rlimit)
+               {
+                 RESTORE_STATE ();
+                 break;
+               }
+
+             next1 = *buffer->cur++;
+             if (!_cpp_trigraph_map[next1]
+                 || !trigraph_ok (buffer->pfile, next1))
+               {
+                 RESTORE_STATE ();
+                 break;
+               }
+
+             /* We have a full trigraph here.  */
+             next = _cpp_trigraph_map[next1];
+             if (next != '\\' || buffer->cur == buffer->rlimit)
+               break;
+             SAVE_STATE ();
+           }
+
+         /* We have a backslash, and room for at least one more character.  */
+         space = 0;
+         do
+           {
+             next1 = *buffer->cur++;
+             if (!is_nvspace (next1))
+               break;
+             space = 1;
+           }
+         while (buffer->cur < buffer->rlimit);
+
+         if (!is_vspace (next1))
+           {
+             RESTORE_STATE ();
+             break;
+           }
+
+         if (space && !buffer->pfile->state.lexing_comment)
+           cpp_warning (buffer->pfile,
+                        "backslash and newline separated by space");
+
+         next = handle_newline (buffer, next1);
+         if (next == EOF)
+           cpp_pedwarn (buffer->pfile, "backslash-newline at end of file");
+       }
+      while (next == '\\' || next == '?');
     }
-  CPP_BUFFER (pfile) = CPP_PREV_BUFFER (buf);
-  free (buf);
-  pfile->buffer_stack_depth--;
-  return CPP_BUFFER (pfile);
+
+  buffer->read_ahead = next;
+  return next;
 }
 
-/* Deal with the annoying semantics of fwrite.  */
-static void
-safe_fwrite (pfile, buf, len, fp)
-     cpp_reader *pfile;
-     const U_CHAR *buf;
-     size_t len;
-     FILE *fp;
+/* Obtain the next character, after trigraph conversion and skipping
+   an arbitrary string of escaped newlines.  The common case of no
+   trigraphs or escaped newlines falls through quickly.  */
+static cppchar_t
+get_effective_char (buffer)
+     cpp_buffer *buffer;
 {
-  size_t count;
+  cppchar_t next = EOF;
 
-  while (len)
+  if (buffer->cur < buffer->rlimit)
     {
-      count = fwrite (buf, 1, len, fp);
-      if (count == 0)
-       goto error;
-      len -= count;
-      buf += count;
+      next = *buffer->cur++;
+
+      /* '?' can introduce trigraphs (and therefore backslash); '\\'
+        can introduce escaped newlines, which we want to skip, or
+        UCNs, which, depending upon lexer state, we will handle in
+        the future.  */
+      if (next == '?' || next == '\\')
+       next = skip_escaped_newlines (buffer, next);
     }
-  return;
 
- error:
-  cpp_notice_from_errno (pfile, CPP_OPTION (pfile, out_fname));
+  buffer->read_ahead = next;
+  return next;
 }
 
-/* Notify the compiler proper that the current line number has jumped,
-   or the current file name has changed.  */
-
-static void
-output_line_command (pfile, print, line)
+/* Skip a C-style block comment.  We find the end of the comment by
+   seeing if an asterisk is before every '/' we encounter.  Returns
+   non-zero if comment terminated by EOF, zero otherwise.  */
+static int
+skip_block_comment (pfile)
      cpp_reader *pfile;
-     cpp_printer *print;
-     unsigned int line;
 {
-  cpp_buffer *ip = cpp_file_buffer (pfile);
-  enum { same = 0, enter, leave, rname } change;
-  static const char * const codes[] = { "", " 1", " 2", "" };
-
-  if (CPP_OPTION (pfile, no_line_commands))
-    return;
-
-  /* Determine whether the current filename has changed, and if so,
-     how.  'nominal_fname' values are unique, so they can be compared
-     by comparing pointers.  */
-  if (ip->nominal_fname == print->last_fname)
-    change = same;
-  else
+  cpp_buffer *buffer = pfile->buffer;
+  cppchar_t c = EOF, prevc = EOF;
+
+  pfile->state.lexing_comment = 1;
+  while (buffer->cur != buffer->rlimit)
     {
-      if (pfile->buffer_stack_depth == print->last_bsd)
-       change = rname;
-      else
+      prevc = c, c = *buffer->cur++;
+
+    next_char:
+      /* FIXME: For speed, create a new character class of characters
+        of interest inside block comments.  */
+      if (c == '?' || c == '\\')
+       c = skip_escaped_newlines (buffer, c);
+
+      /* People like decorating comments with '*', so check for '/'
+        instead for efficiency.  */
+      if (c == '/')
        {
-         if (pfile->buffer_stack_depth > print->last_bsd)
-           change = enter;
-         else
-           change = leave;
-         print->last_bsd = pfile->buffer_stack_depth;
+         if (prevc == '*')
+           break;
+
+         /* Warn about potential nested comments, but not if the '/'
+            comes immediately before the true comment delimeter.
+            Don't bother to get it right across escaped newlines.  */
+         if (CPP_OPTION (pfile, warn_comments)
+             && buffer->cur != buffer->rlimit)
+           {
+             prevc = c, c = *buffer->cur++;
+             if (c == '*' && buffer->cur != buffer->rlimit)
+               {
+                 prevc = c, c = *buffer->cur++;
+                 if (c != '/') 
+                   cpp_warning_with_line (pfile, CPP_BUF_LINE (buffer),
+                                          CPP_BUF_COL (buffer),
+                                          "\"/*\" within comment");
+               }
+             goto next_char;
+           }
        }
-      print->last_fname = ip->nominal_fname;
-    }
-  /* If the current file has not changed, we can output a few newlines
-     instead if we want to increase the line number by a small amount.
-     We cannot do this if print->lineno is zero, because that means we
-     haven't output any line commands yet.  (The very first line
-     command output is a `same_file' command.)  */
-  if (change == same && print->lineno != 0
-      && line >= print->lineno && line < print->lineno + 8)
-    {
-      while (line > print->lineno)
+      else if (is_vspace (c))
        {
-         putc ('\n', print->outf);
-         print->lineno++;
+         prevc = c, c = handle_newline (buffer, c);
+         goto next_char;
        }
-      return;
+      else if (c == '\t')
+       adjust_column (pfile);
     }
 
-#ifndef NO_IMPLICIT_EXTERN_C
-  if (CPP_OPTION (pfile, cplusplus))
-    fprintf (print->outf, "# %u \"%s\"%s%s%s\n", line, ip->nominal_fname,
-            codes[change],
-            ip->system_header_p ? " 3" : "",
-            (ip->system_header_p == 2) ? " 4" : "");
-  else
-#endif
-    fprintf (print->outf, "# %u \"%s\"%s%s\n", line, ip->nominal_fname,
-            codes[change],
-            ip->system_header_p ? " 3" : "");
-  print->lineno = line;
+  pfile->state.lexing_comment = 0;
+  buffer->read_ahead = EOF;
+  return c != '/' || prevc != '*';
 }
 
-/* Write the contents of the token_buffer to the output stream, and
-   clear the token_buffer.  Also handles generating line commands and
-   keeping track of file transitions.  */
-
-void
-cpp_output_tokens (pfile, print)
+/* Skip a C++ line comment.  Handles escaped newlines.  Returns
+   non-zero if a multiline comment.  The following new line, if any,
+   is left in buffer->read_ahead.  */
+static int
+skip_line_comment (pfile)
      cpp_reader *pfile;
-     cpp_printer *print;
 {
-  cpp_buffer *ip;
+  cpp_buffer *buffer = pfile->buffer;
+  unsigned int orig_lineno = buffer->lineno;
+  cppchar_t c;
 
-  if (CPP_WRITTEN (pfile) - print->written)
+  pfile->state.lexing_comment = 1;
+  do
     {
-      if (CPP_PWRITTEN (pfile)[-1] == '\n' && print->lineno)
-       print->lineno++;
-      safe_fwrite (pfile, pfile->token_buffer,
-                  CPP_WRITTEN (pfile) - print->written, print->outf);
-    }
+      c = EOF;
+      if (buffer->cur == buffer->rlimit)
+       break;
 
-  ip = cpp_file_buffer (pfile);
-  if (ip)
-    output_line_command (pfile, print, CPP_BUF_LINE (ip));
+      c = *buffer->cur++;
+      if (c == '?' || c == '\\')
+       c = skip_escaped_newlines (buffer, c);
+    }
+  while (!is_vspace (c));
 
-  CPP_SET_WRITTEN (pfile, print->written);
+  pfile->state.lexing_comment = 0;
+  buffer->read_ahead = c;      /* Leave any newline for caller.  */
+  return orig_lineno != buffer->lineno;
 }
 
-/* Helper for cpp_output_list - increases the column number to match
-   what we expect it to be.  */
-
+/* pfile->buffer->cur is one beyond the \t character.  Update
+   col_adjust so we track the column correctly.  */
 static void
-bump_column (print, from, to)
-     cpp_printer *print;
-     unsigned int from, to;
+adjust_column (pfile)
+     cpp_reader *pfile;
 {
-  unsigned int tabs, spcs;
-  unsigned int delta = to - from;
-
-  /* Only if FROM is 0, advance by tabs.  */
-  if (from == 0)
-    tabs = delta / 8, spcs = delta % 8;
-  else
-    tabs = 0, spcs = delta;
+  cpp_buffer *buffer = pfile->buffer;
+  unsigned int col = CPP_BUF_COL (buffer) - 1; /* Zero-based column.  */
 
-  while (tabs--) putc ('\t', print->outf);
-  while (spcs--) putc (' ', print->outf);
+  /* Round it up to multiple of the tabstop, but subtract 1 since the
+     tab itself occupies a character position.  */
+  buffer->col_adjust += (CPP_OPTION (pfile, tabstop)
+                        - col % CPP_OPTION (pfile, tabstop)) - 1;
 }
 
-/* Write out the list L onto pfile->token_buffer.  This function is
-   incomplete:
-
-   1) pfile->token_buffer is not going to continue to exist.
-   2) At the moment, tokens don't carry the information described
-   in cpplib.h; they are all strings.
-   3) The list has to be a complete line, and has to be written starting
-   at the beginning of a line.  */
-
-void
-cpp_output_list (pfile, print, list)
+/* Skips whitespace, saving the next non-whitespace character.
+   Adjusts pfile->col_adjust to account for tabs.  Without this,
+   tokens might be assigned an incorrect column.  */
+static void
+skip_whitespace (pfile, c)
      cpp_reader *pfile;
-     cpp_printer *print;
-     const cpp_toklist *list;
+     cppchar_t c;
 {
-  unsigned int i;
-  unsigned int curcol = 1;
+  cpp_buffer *buffer = pfile->buffer;
+  unsigned int warned = 0;
 
-  /* XXX Probably does not do what is intended.  */
-  if (print->lineno != list->line)
-    output_line_command (pfile, print, list->line);
-  
-  for (i = 0; i < list->tokens_used; i++)
+  do
     {
-      if (TOK_TYPE (list, i) == CPP_VSPACE)
-       {
-         output_line_command (pfile, print, list->tokens[i].aux);
-         continue;
-       }
-         
-      if (curcol < TOK_COL (list, i))
+      /* Horizontal space always OK.  */
+      if (c == ' ')
+       ;
+      else if (c == '\t')
+       adjust_column (pfile);
+      /* Just \f \v or \0 left.  */
+      else if (c == '\0')
        {
-         /* Insert space to bring the column to what it should be.  */
-         bump_column (print, curcol - 1, TOK_COL (list, i));
-         curcol = TOK_COL (list, i);
+         if (!warned)
+           {
+             cpp_warning (pfile, "null character(s) ignored");
+             warned = 1;
+           }
        }
-      /* XXX We may have to insert space to prevent an accidental
-        token paste.  */
-      safe_fwrite (pfile, TOK_NAME (list, i), TOK_LEN (list, i), print->outf);
-      curcol += TOK_LEN (list, i);
+      else if (pfile->state.in_directive && CPP_PEDANTIC (pfile))
+       cpp_pedwarn_with_line (pfile, CPP_BUF_LINE (buffer),
+                              CPP_BUF_COL (buffer),
+                              "%s in preprocessing directive",
+                              c == '\f' ? "form feed" : "vertical tab");
+
+      c = EOF;
+      if (buffer->cur == buffer->rlimit)
+       break;
+      c = *buffer->cur++;
     }
+  /* We only want non-vertical space, i.e. ' ' \t \f \v \0. */
+  while (is_nvspace (c));
+
+  /* Remember the next character.  */
+  buffer->read_ahead = c;
+}
+
+/* See if the characters of a number token are valid in a name (no
+   '.', '+' or '-').  */
+static int
+name_p (pfile, string)
+     cpp_reader *pfile;
+     const cpp_string *string;
+{
+  unsigned int i;
+
+  for (i = 0; i < string->len; i++)
+    if (!is_idchar (string->text[i]))
+      return 0;
+
+  return 1;  
 }
 
-/* Scan a string (which may have escape marks), perform macro expansion,
-   and write the result to the token_buffer.  */
+/* Parse an identifier, skipping embedded backslash-newlines.
+   Calculate the hash value of the token while parsing, for improved
+   performance.  The hashing algorithm *must* match cpp_lookup().  */
 
-void
-_cpp_expand_to_buffer (pfile, buf, length)
+static cpp_hashnode *
+parse_identifier (pfile, c)
      cpp_reader *pfile;
-     const U_CHAR *buf;
-     int length;
+     cppchar_t c;
 {
-  cpp_buffer *stop;
-  enum cpp_ttype token;
-  U_CHAR *buf1;
+  cpp_hashnode *result;
+  cpp_buffer *buffer = pfile->buffer;
+  unsigned char *dest, *limit;
+  unsigned int r = 0, saw_dollar = 0;
 
-  if (length < 0)
+  dest = POOL_FRONT (&pfile->ident_pool);
+  limit = POOL_LIMIT (&pfile->ident_pool);
+
+  do
     {
-      cpp_ice (pfile, "length < 0 in cpp_expand_to_buffer");
-      return;
-    }
+      do
+       {
+         /* Need room for terminating null.  */
+         if (dest + 1 >= limit)
+           limit = _cpp_next_chunk (&pfile->ident_pool, 0, &dest);
 
-  /* Copy the buffer, because it might be in an unsafe place - for
-     example, a sequence on the token_buffer, where the pointers will
-     be invalidated if we enlarge the token_buffer.  */
-  buf1 = alloca (length);
-  memcpy (buf1, buf, length);
+         *dest++ = c;
+         r = HASHSTEP (r, c);
 
-  /* Set up the input on the input stack.  */
-  stop = CPP_BUFFER (pfile);
-  if (cpp_push_buffer (pfile, buf1, length) == NULL)
-    return;
-  CPP_BUFFER (pfile)->has_escapes = 1;
+         if (c == '$')
+           saw_dollar++;
 
-  /* Scan the input, create the output.  */
-  for (;;)
-    {
-      token = cpp_get_token (pfile);
-      if (token == CPP_EOF && CPP_BUFFER (pfile) == stop)
+         c = EOF;
+         if (buffer->cur == buffer->rlimit)
+           break;
+
+         c = *buffer->cur++;
+       }
+      while (is_idchar (c));
+
+      /* Potential escaped newline?  */
+      if (c != '?' && c != '\\')
        break;
+      c = skip_escaped_newlines (buffer, c);
     }
-}
+  while (is_idchar (c));
 
-/* Scan until CPP_BUFFER (PFILE) is exhausted, discarding output.  */
+  /* Remember the next character.  */
+  buffer->read_ahead = c;
 
-void
-cpp_scan_buffer_nooutput (pfile)
-     cpp_reader *pfile;
-{
-  cpp_buffer *stop = CPP_PREV_BUFFER (CPP_BUFFER (pfile));
-  enum cpp_ttype token;
-  unsigned int old_written = CPP_WRITTEN (pfile);
-  /* In no-output mode, we can ignore everything but directives.  */
-  for (;;)
+  /* $ is not a identifier character in the standard, but is commonly
+     accepted as an extension.  Don't warn about it in skipped
+     conditional blocks.  */
+  if (saw_dollar && CPP_PEDANTIC (pfile) && ! pfile->skipping)
+    cpp_pedwarn (pfile, "'$' character(s) in identifier");
+
+  /* Identifiers are null-terminated.  */
+  *dest = '\0';
+
+  /* This routine commits the memory if necessary.  */
+  result = _cpp_lookup_with_hash (pfile,
+                                 dest - POOL_FRONT (&pfile->ident_pool), r);
+
+  /* Some identifiers require diagnostics when lexed.  */
+  if (result->flags & NODE_DIAGNOSTIC && !pfile->skipping)
     {
-      if (! pfile->only_seen_white)
-       _cpp_skip_rest_of_line (pfile);
-      token = cpp_get_token (pfile);
-      if (token == CPP_EOF && CPP_BUFFER (pfile) == stop)
-       break;
+      /* It is allowed to poison the same identifier twice.  */
+      if ((result->flags & NODE_POISONED) && !pfile->state.poisoned_ok)
+       cpp_error (pfile, "attempt to use poisoned \"%s\"", result->name);
+
+      /* Constraint 6.10.3.5: __VA_ARGS__ should only appear in the
+        replacement list of a variadic macro.  */
+      if (result == pfile->spec_nodes.n__VA_ARGS__
+         && !pfile->state.va_args_ok)
+       cpp_pedwarn (pfile, "__VA_ARGS__ can only appear in the expansion of a C99 variadic macro");
     }
-  CPP_SET_WRITTEN (pfile, old_written);
-}
 
-/* Scan until CPP_BUFFER (pfile) is exhausted, writing output to PRINT.  */
+  return result;
+}
 
-void
-cpp_scan_buffer (pfile, print)
+/* Parse a number, skipping embedded backslash-newlines.  */
+static void
+parse_number (pfile, number, c, leading_period)
      cpp_reader *pfile;
-     cpp_printer *print;
+     cpp_string *number;
+     cppchar_t c;
+     int leading_period;
 {
-  cpp_buffer *stop = CPP_PREV_BUFFER (CPP_BUFFER (pfile));
-  enum cpp_ttype token;
+  cpp_buffer *buffer = pfile->buffer;
+  cpp_pool *pool = &pfile->ident_pool;
+  unsigned char *dest, *limit;
 
-  for (;;)
+  dest = POOL_FRONT (pool);
+  limit = POOL_LIMIT (pool);
+
+  /* Place a leading period.  */
+  if (leading_period)
+    {
+      if (dest >= limit)
+       limit = _cpp_next_chunk (pool, 0, &dest);
+      *dest++ = '.';
+    }
+  
+  do
     {
-      token = cpp_get_token (pfile);
-      if (token == CPP_EOF || token == CPP_VSPACE
-         /* XXX Temporary kluge - force flush after #include only */
-         || (token == CPP_DIRECTIVE
-             && CPP_BUFFER (pfile)->nominal_fname != print->last_fname))
+      do
        {
-         cpp_output_tokens (pfile, print);
-         if (token == CPP_EOF && CPP_BUFFER (pfile) == stop)
-           return;
+         /* Need room for terminating null.  */
+         if (dest + 1 >= limit)
+           limit = _cpp_next_chunk (pool, 0, &dest);
+         *dest++ = c;
+
+         c = EOF;
+         if (buffer->cur == buffer->rlimit)
+           break;
+
+         c = *buffer->cur++;
        }
+      while (is_numchar (c) || c == '.' || VALID_SIGN (c, dest[-1]));
+
+      /* Potential escaped newline?  */
+      if (c != '?' && c != '\\')
+       break;
+      c = skip_escaped_newlines (buffer, c);
     }
-}
+  while (is_numchar (c) || c == '.' || VALID_SIGN (c, dest[-1]));
 
-/* Return the topmost cpp_buffer that corresponds to a file (not a macro).  */
+  /* Remember the next character.  */
+  buffer->read_ahead = c;
 
-cpp_buffer *
-cpp_file_buffer (pfile)
-     cpp_reader *pfile;
-{
-  cpp_buffer *ip;
+  /* Null-terminate the number.  */
+  *dest = '\0';
 
-  for (ip = CPP_BUFFER (pfile); ip; ip = CPP_PREV_BUFFER (ip))
-    if (ip->ihash != NULL)
-      return ip;
-  return NULL;
+  number->text = POOL_FRONT (pool);
+  number->len = dest - number->text;
+  POOL_COMMIT (pool, number->len + 1);
 }
 
-/* Token-buffer helper functions.  */
-
-/* Expand a token list's string space.  */
+/* Subroutine of parse_string.  Emits error for unterminated strings.  */
 static void
-expand_name_space (list, len)
-     cpp_toklist *list;
-     unsigned int len;
+unterminated (pfile, term)
+     cpp_reader *pfile;
+     int term;
 {
-  const U_CHAR *old_namebuf;
+  cpp_error (pfile, "missing terminating %c character", term);
 
-  old_namebuf = list->namebuf;
-  list->name_cap += len;
-  list->namebuf = (unsigned char *) xrealloc (list->namebuf, list->name_cap);
-
-  /* Fix up token text pointers.  */
-  if (list->namebuf != old_namebuf)
+  if (term == '\"' && pfile->mlstring_pos.line
+      && pfile->mlstring_pos.line != pfile->lexer_pos.line)
     {
-      unsigned int i;
-
-      for (i = 0; i < list->tokens_used; i++)
-       if (token_spellings[list->tokens[i].type].type > SPELL_NONE)
-         list->tokens[i].val.name.text += (list->namebuf - old_namebuf);
+      cpp_error_with_line (pfile, pfile->mlstring_pos.line,
+                          pfile->mlstring_pos.col,
+                          "possible start of unterminated string literal");
+      pfile->mlstring_pos.line = 0;
     }
 }
 
-/* Expand the number of tokens in a list.  */
-static void
-expand_token_space (list)
-     cpp_toklist *list;
-{
-  list->tokens_cap *= 2;
-  list->tokens = (cpp_token *)
-    xrealloc (list->tokens - 1, (list->tokens_cap + 1) * sizeof (cpp_token));
-  list->tokens++;              /* Skip the dummy.  */
-}
-
-/* Initialize a token list.  We allocate an extra token in front of
-   the token list, as this allows us to always peek at the previous
-   token without worrying about underflowing the list.  */
-static void
-init_token_list (pfile, list, recycle)
+/* Subroutine of parse_string.  */
+static int
+unescaped_terminator_p (pfile, dest)
      cpp_reader *pfile;
-     cpp_toklist *list;
-     int recycle;
+     const unsigned char *dest;
 {
-  /* Recycling a used list saves 3 free-malloc pairs.  */
-  if (!recycle)
-    {
-      /* Initialize token space.  Put a dummy token before the start
-         that will fail matches.  */
-      list->tokens_cap = 256;  /* 4K's worth.  */
-      list->tokens = (cpp_token *)
-       xmalloc ((list->tokens_cap + 1) * sizeof (cpp_token));
-      list->tokens[0].type = CPP_EOF;
-      list->tokens++;
-
-      /* Initialize name space.  */
-      list->name_cap = 1024;
-      list->namebuf = (unsigned char *) xmalloc (list->name_cap);
-
-      /* Only create a comment space on demand.  */
-      list->comments_cap = 0;
-      list->comments = 0;
-    }
+  const unsigned char *start, *temp;
+
+  /* In #include-style directives, terminators are not escapeable.  */
+  if (pfile->state.angled_headers)
+    return 1;
+
+  start = POOL_FRONT (&pfile->ident_pool);
 
-  list->tokens_used = 0;
-  list->name_used = 0;
-  list->comments_used = 0;
-  if (pfile->buffer)
-    list->line = pfile->buffer->lineno;
-  list->dir_handler = 0;
-  list->dir_flags = 0;
+  /* An odd number of consecutive backslashes represents an escaped
+     terminator.  */
+  for (temp = dest; temp > start && temp[-1] == '\\'; temp--)
+    ;
+
+  return ((dest - temp) & 1) == 0;
 }
 
-/* Scan an entire line and create a token list for it.  Does not
-   macro-expand or execute directives.  */
+/* Parses a string, character constant, or angle-bracketed header file
+   name.  Handles embedded trigraphs and escaped newlines.
 
-void
-_cpp_scan_line (pfile, list)
+   Multi-line strings are allowed, but they are deprecated within
+   directives.  */
+static void
+parse_string (pfile, token, terminator)
      cpp_reader *pfile;
-     cpp_toklist *list;
+     cpp_token *token;
+     cppchar_t terminator;
 {
-  int i, col;
-  long written, len;
-  enum cpp_ttype type;
-  int space_before;
+  cpp_buffer *buffer = pfile->buffer;
+  cpp_pool *pool = &pfile->ident_pool;
+  unsigned char *dest, *limit;
+  cppchar_t c;
+  unsigned int nulls = 0;
 
-  init_token_list (pfile, list, 1);
+  dest = POOL_FRONT (pool);
+  limit = POOL_LIMIT (pool);
 
-  written = CPP_WRITTEN (pfile);
-  i = 0;
-  space_before = 0;
   for (;;)
     {
-      col = CPP_BUFFER (pfile)->cur - CPP_BUFFER (pfile)->line_base;
-      type = _cpp_lex_token (pfile);
-      len = CPP_WRITTEN (pfile) - written;
-      CPP_SET_WRITTEN (pfile, written);
-      if (type == CPP_HSPACE)
+      if (buffer->cur == buffer->rlimit)
        {
-         if (CPP_PEDANTIC (pfile))
-           pedantic_whitespace (pfile, pfile->token_buffer + written, len);
-         space_before = 1;
-         continue;
+         c = EOF;
+         unterminated (pfile, terminator);
+         break;
        }
-      else if (type == CPP_COMMENT)
-       /* Only happens when processing -traditional macro definitions.
-          Do not give this a token entry, but do not change space_before
-          either.  */
-       continue;
+      c = *buffer->cur++;
 
-      if (list->tokens_used >= list->tokens_cap)
-       expand_token_space (list);
-      if (list->name_used + len >= list->name_cap)
-       expand_name_space (list, list->name_used + len + 1 - list->name_cap);
+    have_char:
+      /* Handle trigraphs, escaped newlines etc.  */
+      if (c == '?' || c == '\\')
+       c = skip_escaped_newlines (buffer, c);
 
-      if (type == CPP_MACRO)
-       type = CPP_NAME;
+      if (c == terminator && unescaped_terminator_p (pfile, dest))
+       {
+         c = EOF;
+         break;
+       }
+      else if (is_vspace (c))
+       {
+         /* In assembly language, silently terminate string and
+            character literals at end of line.  This is a kludge
+            around not knowing where comments are.  */
+         if (CPP_OPTION (pfile, lang) == CLK_ASM && terminator != '>')
+           break;
 
-      list->tokens_used++;
-      TOK_TYPE  (list, i) = type;
-      TOK_COL   (list, i) = col;
-      TOK_FLAGS (list, i) = space_before ? PREV_WHITESPACE : 0;
-      
-      if (type == CPP_VSPACE)
-       break;
+         /* Character constants and header names may not extend over
+            multiple lines.  In Standard C, neither may strings.
+            Unfortunately, we accept multiline strings as an
+            extension, except in #include family directives.  */
+         if (terminator != '"' || pfile->state.angled_headers)
+           {
+             unterminated (pfile, terminator);
+             break;
+           }
 
-      TOK_LEN (list, i) = len;
-      if (token_spellings[type].type > SPELL_NONE)
+         if (pfile->mlstring_pos.line == 0)
+           {
+             pfile->mlstring_pos = pfile->lexer_pos;
+             if (CPP_PEDANTIC (pfile))
+               cpp_pedwarn (pfile, "multi-line string constant");
+           }
+             
+         handle_newline (buffer, c);  /* Stores to read_ahead.  */
+         c = '\n';
+       }
+      else if (c == '\0')
        {
-         memcpy (list->namebuf + list->name_used, CPP_PWRITTEN (pfile), len);
-         TOK_NAME (list, i) = list->namebuf + list->name_used;
-         list->name_used += len;
+         if (nulls++ == 0)
+           cpp_warning (pfile, "null character(s) preserved in literal");
        }
-      else
-       TOK_NAME (list, i) = token_spellings[type].spelling;
-      i++;
-      space_before = 0;
+
+      /* No terminating null for strings - they could contain nulls.  */
+      if (dest >= limit)
+       limit = _cpp_next_chunk (pool, 0, &dest);
+      *dest++ = c;
+
+      /* If we had a new line, the next character is in read_ahead.  */
+      if (c != '\n')
+       continue;
+      c = buffer->read_ahead;
+      if (c != EOF)
+       goto have_char;
     }
-  TOK_AUX (list, i) = CPP_BUFFER (pfile)->lineno + 1;
 
-  /* XXX Temporary kluge: put back the newline.  */
-  FORWARD(-1);
-}
+  /* Remember the next character.  */
+  buffer->read_ahead = c;
 
+  token->val.str.text = POOL_FRONT (pool);
+  token->val.str.len = dest - token->val.str.text;
+  POOL_COMMIT (pool, token->val.str.len);
+}
 
-/* Skip a C-style block comment.  We know it's a comment, and point is
-   at the second character of the starter.  */
+/* The stored comment includes the comment start and any terminator.  */
 static void
-skip_block_comment (pfile)
+save_comment (pfile, token, from)
      cpp_reader *pfile;
+     cpp_token *token;
+     const unsigned char *from;
 {
-  unsigned int line, col;
-  const U_CHAR *limit, *cur;
-
-  FORWARD(1);
-  line = CPP_BUF_LINE (CPP_BUFFER (pfile));
-  col = CPP_BUF_COL (CPP_BUFFER (pfile));
-  limit = CPP_BUFFER (pfile)->rlimit;
-  cur = CPP_BUFFER (pfile)->cur;
-
-  while (cur < limit)
-    {
-      char c = *cur++;
-      if (c == '\n' || c == '\r')
-       {
-         /* \r cannot be a macro escape marker here. */
-         if (!ACTIVE_MARK_P (pfile))
-           CPP_BUMP_LINE_CUR (pfile, cur);
-       }
-      else if (c == '*')
-       {
-         /* Check for teminator.  */
-         if (cur < limit && *cur == '/')
-           goto out;
-
-         /* Warn about comment starter embedded in comment.  */
-         if (cur[-2] == '/' && CPP_OPTION (pfile, warn_comments))
-           cpp_warning_with_line (pfile, CPP_BUFFER (pfile)->lineno,
-                                  cur - CPP_BUFFER (pfile)->line_base,
-                                  "'/*' within comment");
-       }
-    }
+  unsigned char *buffer;
+  unsigned int len;
+  
+  len = pfile->buffer->cur - from + 1; /* + 1 for the initial '/'.  */
+  /* C++ comments probably (not definitely) have moved past a new
+     line, which we don't want to save in the comment.  */
+  if (pfile->buffer->read_ahead != EOF)
+    len--;
+  buffer = _cpp_pool_alloc (&pfile->ident_pool, len);
+  
+  token->type = CPP_COMMENT;
+  token->val.str.len = len;
+  token->val.str.text = buffer;
 
-  cpp_error_with_line (pfile, line, col, "unterminated comment");
-  cur--;
- out:
-  CPP_BUFFER (pfile)->cur = cur + 1;
+  buffer[0] = '/';
+  memcpy (buffer + 1, from, len - 1);
 }
 
-/* Skip a C++/Chill line comment.  We know it's a comment, and point
-   is at the second character of the initiator.  */
+/* Subroutine of lex_token to handle '%'.  A little tricky, since we
+   want to avoid stepping back when lexing %:%X.  */
 static void
-skip_line_comment (pfile)
-     cpp_reader *pfile;
+lex_percent (buffer, result)
+     cpp_buffer *buffer;
+     cpp_token *result;
 {
-  FORWARD(1);
-  for (;;)
+  cppchar_t c;
+
+  result->type = CPP_MOD;
+  /* Parsing %:%X could leave an extra character.  */
+  if (buffer->extra_char == EOF)
+    c = get_effective_char (buffer);
+  else
     {
-      int c = GETC ();
+      c = buffer->read_ahead = buffer->extra_char;
+      buffer->extra_char = EOF;
+    }
 
-      /* We don't have to worry about EOF in here.  */
-      if (c == '\n')
+  if (c == '=')
+    ACCEPT_CHAR (CPP_MOD_EQ);
+  else if (CPP_OPTION (buffer->pfile, digraphs))
+    {
+      if (c == ':')
        {
-         /* Don't consider final '\n' to be part of comment.  */
-         FORWARD(-1);
-         return;
+         result->flags |= DIGRAPH;
+         ACCEPT_CHAR (CPP_HASH);
+         if (get_effective_char (buffer) == '%')
+           {
+             buffer->extra_char = get_effective_char (buffer);
+             if (buffer->extra_char == ':')
+               {
+                 buffer->extra_char = EOF;
+                 ACCEPT_CHAR (CPP_PASTE);
+               }
+             else
+               /* We'll catch the extra_char when we're called back.  */
+               buffer->read_ahead = '%';
+           }
        }
-      else if (c == '\r')
+      else if (c == '>')
        {
-         /* \r cannot be a macro escape marker here. */
-         if (!ACTIVE_MARK_P (pfile))
-           CPP_BUMP_LINE (pfile);
-         if (CPP_OPTION (pfile, warn_comments))
-           cpp_warning (pfile, "backslash-newline within line comment");
+         result->flags |= DIGRAPH;
+         ACCEPT_CHAR (CPP_CLOSE_BRACE);
        }
     }
 }
 
-/* Skip a comment - C, C++, or Chill style.  M is the first character
-   of the comment marker.  If this really is a comment, skip to its
-   end and return ' '.  If this is not a comment, return M (which will
-   be '/' or '-').  */
-
-static int
-skip_comment (pfile, m)
+/* Subroutine of lex_token to handle '.'.  This is tricky, since we
+   want to avoid stepping back when lexing '...' or '.123'.  In the
+   latter case we should also set a flag for parse_number.  */
+static void
+lex_dot (pfile, result)
      cpp_reader *pfile;
-     int m;
+     cpp_token *result;
 {
-  if (m == '/' && PEEKC() == '*')
+  cpp_buffer *buffer = pfile->buffer;
+  cppchar_t c;
+
+  /* Parsing ..X could leave an extra character.  */
+  if (buffer->extra_char == EOF)
+    c = get_effective_char (buffer);
+  else
     {
-      skip_block_comment (pfile);
-      return ' ';
+      c = buffer->read_ahead = buffer->extra_char;
+      buffer->extra_char = EOF;
     }
-  else if (m == '/' && PEEKC() == '/')
+
+  /* All known character sets have 0...9 contiguous.  */
+  if (c >= '0' && c <= '9')
     {
-      if (CPP_BUFFER (pfile)->system_header_p)
-       {
-         /* We silently allow C++ comments in system headers, irrespective
-            of conformance mode, because lots of busted systems do that
-            and trying to clean it up in fixincludes is a nightmare.  */
-         skip_line_comment (pfile);
-         return ' ';
-       }
-      else if (CPP_OPTION (pfile, cplusplus_comments))
+      result->type = CPP_NUMBER;
+      parse_number (pfile, &result->val.str, c, 1);
+    }
+  else
+    {
+      result->type = CPP_DOT;
+      if (c == '.')
        {
-         if (! CPP_BUFFER (pfile)->warned_cplusplus_comments)
+         buffer->extra_char = get_effective_char (buffer);
+         if (buffer->extra_char == '.')
            {
-             if (CPP_WTRADITIONAL (pfile))
-               cpp_pedwarn (pfile,
-                       "C++ style comments are not allowed in traditional C");
-             else if (CPP_OPTION (pfile, c89) && CPP_PEDANTIC (pfile))
-               cpp_pedwarn (pfile,
-                       "C++ style comments are not allowed in ISO C89");
-             if (CPP_WTRADITIONAL (pfile)
-                 || (CPP_OPTION (pfile, c89) && CPP_PEDANTIC (pfile)))
-               cpp_pedwarn (pfile,
-                          "(this will be reported only once per input file)");
-             CPP_BUFFER (pfile)->warned_cplusplus_comments = 1;
+             buffer->extra_char = EOF;
+             ACCEPT_CHAR (CPP_ELLIPSIS);
            }
-         skip_line_comment (pfile);
-         return ' ';
+         else
+           /* We'll catch the extra_char when we're called back.  */
+           buffer->read_ahead = '.';
        }
-      else
-       return m;
-    }
-  else if (m == '-' && PEEKC() == '-'
-          && CPP_OPTION (pfile, chill))
-    {
-      skip_line_comment (pfile);
-      return ' ';
+      else if (c == '*' && CPP_OPTION (pfile, cplusplus))
+       ACCEPT_CHAR (CPP_DOT_STAR);
     }
-  else
-    return m;
 }
 
-/* Identical to skip_comment except that it copies the comment into the
-   token_buffer.  This is used if !discard_comments.  */
-static int
-copy_comment (pfile, m)
+void
+_cpp_lex_token (pfile, result)
      cpp_reader *pfile;
-     int m;
+     cpp_token *result;
 {
-  const U_CHAR *start = CPP_BUFFER (pfile)->cur;  /* XXX Layering violation */
-  const U_CHAR *limit;
-
-  if (skip_comment (pfile, m) == m)
-    return m;
-
-  limit = CPP_BUFFER (pfile)->cur;
-  CPP_RESERVE (pfile, limit - start + 2);
-  CPP_PUTC_Q (pfile, m);
-  for (; start <= limit; start++)
-    if (*start != '\r')
-      CPP_PUTC_Q (pfile, *start);
-
-  return ' ';
-}
-
-static void
-null_warning (pfile, count)
-     cpp_reader *pfile;
-     unsigned int count;
-{
-  if (count == 1)
-    cpp_warning (pfile, "embedded null character ignored");
-  else
-    cpp_warning (pfile, "embedded null characters ignored");
-}
-
-/* Skip whitespace \-newline and comments.  Does not macro-expand.  */
-
-void
-_cpp_skip_hspace (pfile)
-     cpp_reader *pfile;
-{
-  unsigned int null_count = 0;
-  int c;
-
-  while (1)
+  cppchar_t c;
+  cpp_buffer *buffer;
+  const unsigned char *comment_start;
+  unsigned char bol;
+
+ skip:
+  bol = pfile->state.next_bol;
+ done_directive:
+  buffer = pfile->buffer;
+  pfile->state.next_bol = 0;
+  result->flags = buffer->saved_flags;
+  buffer->saved_flags = 0;
+ next_char:
+  pfile->lexer_pos.line = buffer->lineno;
+ next_char2:
+  pfile->lexer_pos.col = CPP_BUF_COLUMN (buffer, buffer->cur);
+
+  c = buffer->read_ahead;
+  if (c == EOF && buffer->cur < buffer->rlimit)
     {
-      c = GETC();
-      if (c == EOF)
-       goto out;
-      else if (is_hspace(c))
-       {
-         if ((c == '\f' || c == '\v') && CPP_PEDANTIC (pfile))
-           cpp_pedwarn (pfile, "%s in preprocessing directive",
-                        c == '\f' ? "formfeed" : "vertical tab");
-         else if (c == '\0')
-           null_count++;
-       }
-      else if (c == '\r')
-       {
-         /* \r is a backslash-newline marker if !has_escapes, and
-            a deletable-whitespace or no-reexpansion marker otherwise. */
-         if (CPP_BUFFER (pfile)->has_escapes)
-           {
-             if (PEEKC() == ' ')
-               FORWARD(1);
-             else
-               break;
-           }
-         else
-           CPP_BUMP_LINE (pfile);
-       }
-      else if (c == '/' || c == '-')
-       {
-         c = skip_comment (pfile, c);
-         if (c  != ' ')
-           break;
-       }
-      else
-       break;
+      c = *buffer->cur++;
+      pfile->lexer_pos.col++;
     }
-  FORWARD(-1);
- out:
-  if (null_count)
-    null_warning (pfile, null_count);
-}
-
-/* Read and discard the rest of the current line.  */
 
-void
-_cpp_skip_rest_of_line (pfile)
-     cpp_reader *pfile;
-{
-  for (;;)
+ do_switch:
+  buffer->read_ahead = EOF;
+  switch (c)
     {
-      int c = GETC();
-      switch (c)
-       {
-       case '\n':
-         FORWARD(-1);
-       case EOF:
-         return;
-
-       case '\r':
-         if (! CPP_BUFFER (pfile)->has_escapes)
-           CPP_BUMP_LINE (pfile);
-         break;
-         
-       case '\'':
-       case '\"':
-         skip_string (pfile, c);
-         break;
-
-       case '/':
-       case '-':
-         skip_comment (pfile, c);
-         break;
+    case EOF:
+      /* Non-empty files should end in a newline.  Ignore for command
+        line and _Pragma buffers.  */
+      if (pfile->lexer_pos.col != 0 && !buffer->from_stage3)
+       cpp_pedwarn (pfile, "no newline at end of file");
+      pfile->state.next_bol = 1;
+      pfile->skipping = 0;     /* In case missing #endif.  */
+      result->type = CPP_EOF;
+      /* Don't do MI optimisation.  */
+      return;
 
-       case '\f':
-       case '\v':
-         if (CPP_PEDANTIC (pfile))
-           cpp_pedwarn (pfile, "%s in preprocessing directive",
-                        c == '\f' ? "formfeed" : "vertical tab");
-         break;
+    case ' ': case '\t': case '\f': case '\v': case '\0':
+      skip_whitespace (pfile, c);
+      result->flags |= PREV_WHITE;
+      goto next_char2;
 
+    case '\n': case '\r':
+      if (!pfile->state.in_directive)
+       {
+         handle_newline (buffer, c);
+         bol = 1;
+         pfile->lexer_pos.output_line = buffer->lineno;
+         /* This is a new line, so clear any white space flag.
+            Newlines in arguments are white space (6.10.3.10);
+            parse_arg takes care of that.  */
+         result->flags &= ~(PREV_WHITE | AVOID_LPASTE);
+         goto next_char;
        }
-    }
-}
 
-/* Parse an identifier starting with C.  */
+      /* Don't let directives spill over to the next line.  */
+      buffer->read_ahead = c;
+      pfile->state.next_bol = 1;
+      result->type = CPP_EOF;
+      /* Don't break; pfile->skipping might be true.  */
+      return;
 
-void
-_cpp_parse_name (pfile, c)
-     cpp_reader *pfile;
-     int c;
-{
-  for (;;)
-  {
-      if (! is_idchar(c))
+    case '?':
+    case '\\':
+      /* These could start an escaped newline, or '?' a trigraph.  Let
+        skip_escaped_newlines do all the work.  */
       {
-         FORWARD (-1);
-         break;
+       unsigned int lineno = buffer->lineno;
+
+       c = skip_escaped_newlines (buffer, c);
+       if (lineno != buffer->lineno)
+         /* We had at least one escaped newline of some sort, and the
+            next character is in buffer->read_ahead.  Update the
+            token's line and column.  */
+           goto next_char;
+
+       /* We are either the original '?' or '\\', or a trigraph.  */
+       result->type = CPP_QUERY;
+       buffer->read_ahead = EOF;
+       if (c == '\\')
+         goto random_char;
+       else if (c != '?')
+         goto do_switch;
       }
+      break;
 
-      if (c == '$' && CPP_PEDANTIC (pfile))
-       cpp_pedwarn (pfile, "`$' in identifier");
-
-      CPP_RESERVE(pfile, 2); /* One more for final NUL.  */
-      CPP_PUTC_Q (pfile, c);
-      c = GETC();
-      if (c == EOF)
-       break;
-  }
-  return;
-}
-
-/* Parse and skip over a string starting with C.  A single quoted
-   string is treated like a double -- some programs (e.g., troff) are
-   perverse this way.  (However, a single quoted string is not allowed
-   to extend over multiple lines.)  */
-static void
-skip_string (pfile, c)
-     cpp_reader *pfile;
-     int c;
-{
-  unsigned int start_line, start_column;
-  unsigned int null_count = 0;
-
-  start_line = CPP_BUF_LINE (CPP_BUFFER (pfile));
-  start_column = CPP_BUF_COL (CPP_BUFFER (pfile));
-  while (1)
-    {
-      int cc = GETC();
-      switch (cc)
-       {
-       case EOF:
-         cpp_error_with_line (pfile, start_line, start_column,
-                              "unterminated string or character constant");
-         if (pfile->multiline_string_line != start_line
-             && pfile->multiline_string_line != 0)
-           cpp_error_with_line (pfile,
-                                pfile->multiline_string_line, -1,
-                        "possible real start of unterminated constant");
-         pfile->multiline_string_line = 0;
-         goto out;
-
-       case '\0':
-         null_count++;
-         break;
-         
-       case '\n':
-         CPP_BUMP_LINE (pfile);
-         /* In Fortran and assembly language, silently terminate
-            strings of either variety at end of line.  This is a
-            kludge around not knowing where comments are in these
-            languages.  */
-         if (CPP_OPTION (pfile, lang_fortran)
-             || CPP_OPTION (pfile, lang_asm))
-           {
-             FORWARD(-1);
-             goto out;
-           }
-         /* Character constants may not extend over multiple lines.
-            In Standard C, neither may strings.  We accept multiline
-            strings as an extension.  */
-         if (c == '\'')
-           {
-             cpp_error_with_line (pfile, start_line, start_column,
-                                  "unterminated character constant");
-             FORWARD(-1);
-             goto out;
-           }
-         if (CPP_PEDANTIC (pfile) && pfile->multiline_string_line == 0)
-           cpp_pedwarn_with_line (pfile, start_line, start_column,
-                                  "string constant runs past end of line");
-         if (pfile->multiline_string_line == 0)
-           pfile->multiline_string_line = start_line;
-         break;
-
-       case '\r':
-         if (CPP_BUFFER (pfile)->has_escapes)
-           {
-             cpp_ice (pfile, "\\r escape inside string constant");
-             FORWARD(1);
-           }
-         else
-           /* Backslash newline is replaced by nothing at all.  */
-           CPP_BUMP_LINE (pfile);
-         break;
-
-       case '\\':
-         FORWARD(1);
-         break;
-
-       case '\"':
-       case '\'':
-         if (cc == c)
-           goto out;
-         break;
-       }
-    }
-
- out:
-  if (null_count == 1)
-    cpp_warning (pfile, "null character in string or character constant");
-  else if (null_count > 1)
-    cpp_warning (pfile, "null characters in string or character constant");
-}
-
-/* Parse a string and copy it to the output.  */
-
-static void
-parse_string (pfile, c)
-     cpp_reader *pfile;
-     int c;
-{
-  const U_CHAR *start = CPP_BUFFER (pfile)->cur;  /* XXX Layering violation */
-  const U_CHAR *limit;
-
-  skip_string (pfile, c);
-
-  limit = CPP_BUFFER (pfile)->cur;
-  CPP_RESERVE (pfile, limit - start + 2);
-  CPP_PUTC_Q (pfile, c);
-  for (; start < limit; start++)
-    if (*start != '\r')
-      CPP_PUTC_Q (pfile, *start);
-}
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+      result->type = CPP_NUMBER;
+      parse_number (pfile, &result->val.str, c, 0);
+      break;
 
-/* Read an assertion into the token buffer, converting to
-   canonical form: `#predicate(a n swe r)'  The next non-whitespace
-   character to read should be the first letter of the predicate.
-   Returns 0 for syntax error, 1 for bare predicate, 2 for predicate
-   with answer (see callers for why). In case of 0, an error has been
-   printed. */
-int
-_cpp_parse_assertion (pfile)
-     cpp_reader *pfile;
-{
-  int c, dropwhite;
-  _cpp_skip_hspace (pfile);
-  c = PEEKC();
-  if (c == '\n')
-    {
-      cpp_error (pfile, "assertion without predicate");
-      return 0;
-    }
-  else if (! is_idstart(c))
-    {
-      cpp_error (pfile, "assertion predicate is not an identifier");
-      return 0;
-    }
-  CPP_PUTC(pfile, '#');
-  FORWARD(1);
-  _cpp_parse_name (pfile, c);
+    case '$':
+      if (!CPP_OPTION (pfile, dollars_in_ident))
+       goto random_char;
+      /* Fall through... */
 
-  c = PEEKC();
-  if (c != '(')
-    {
-      if (is_hspace(c) || c == '\r')
-       _cpp_skip_hspace (pfile);
-      c = PEEKC();
-    }
-  if (c != '(')
-    return 1;
+    case '_':
+    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+    case 'y': case 'z':
+    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+    case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+    case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+    case 'Y': case 'Z':
+      result->type = CPP_NAME;
+      result->val.node = parse_identifier (pfile, c);
 
-  CPP_PUTC(pfile, '(');
-  FORWARD(1);
-  dropwhite = 1;
-  while ((c = GETC()) != ')')
-    {
-      if (is_space(c))
+      /* 'L' may introduce wide characters or strings.  */
+      if (result->val.node == pfile->spec_nodes.n_L)
        {
-         if (! dropwhite)
+         c = buffer->read_ahead; /* For make_string.  */
+         if (c == '\'' || c == '"')
            {
-             CPP_PUTC(pfile, ' ');
-             dropwhite = 1;
+             ACCEPT_CHAR (c == '"' ? CPP_WSTRING: CPP_WCHAR);
+             goto make_string;
            }
        }
-      else if (c == '\n' || c == EOF)
+      /* Convert named operators to their proper types.  */
+      else if (result->val.node->flags & NODE_OPERATOR)
        {
-         if (c == '\n') FORWARD(-1);
-         cpp_error (pfile, "un-terminated assertion answer");
-         return 0;
+         result->flags |= NAMED_OP;
+         result->type = result->val.node->value.operator;
        }
-      else if (c == '\r')
-       /* \r cannot be a macro escape here. */
-       CPP_BUMP_LINE (pfile);
-      else
-       {
-         CPP_PUTC (pfile, c);
-         dropwhite = 0;
-       }
-    }
-
-  if (pfile->limit[-1] == ' ')
-    pfile->limit[-1] = ')';
-  else if (pfile->limit[-1] == '(')
-    {
-      cpp_error (pfile, "empty token sequence in assertion");
-      return 0;
-    }
-  else
-    CPP_PUTC (pfile, ')');
-
-  return 2;
-}
-
-/* Get the next token, and add it to the text in pfile->token_buffer.
-   Return the kind of token we got.  */
-
-enum cpp_ttype
-_cpp_lex_token (pfile)
-     cpp_reader *pfile;
-{
-  register int c, c2;
-  enum cpp_ttype token;
-
-  if (CPP_BUFFER (pfile) == NULL)
-    return CPP_EOF;
+      break;
 
get_next:
-  c = GETC();
-  switch (c)
-    {
-    case EOF:
-      return CPP_EOF;
   case '\'':
+    case '"':
+      result->type = c == '"' ? CPP_STRING: CPP_CHAR;
+    make_string:
+      parse_string (pfile, result, c);
+      break;
 
     case '/':
-      if (PEEKC () == '=')
-       goto op2;
+      /* A potential block or line comment.  */
+      comment_start = buffer->cur;
+      result->type = CPP_DIV;
+      c = get_effective_char (buffer);
+      if (c == '=')
+       ACCEPT_CHAR (CPP_DIV_EQ);
+      if (c != '/' && c != '*')
+       break;
 
-    comment:
-      if (CPP_OPTION (pfile, discard_comments))
-       c = skip_comment (pfile, c);
-      else
-       c = copy_comment (pfile, c);
-      if (c != ' ')
-       goto randomchar;
-         
-      /* Comments are equivalent to spaces.
-        For -traditional, a comment is equivalent to nothing.  */
-      if (!CPP_OPTION (pfile, discard_comments))
-       return CPP_COMMENT;
-      else if (CPP_TRADITIONAL (pfile))
+      if (c == '*')
        {
-         if (pfile->parsing_define_directive)
-           return CPP_COMMENT;
-         goto get_next;
+         if (skip_block_comment (pfile))
+           cpp_error_with_line (pfile, pfile->lexer_pos.line,
+                                pfile->lexer_pos.col,
+                                "unterminated comment");
        }
       else
        {
-         CPP_PUTC (pfile, c);
-         return CPP_HSPACE;
-       }
-
-    case '#':
-      CPP_PUTC (pfile, c);
-
-    hash:
-      if (pfile->parsing_if_directive)
-       {
-         CPP_ADJUST_WRITTEN (pfile, -1);
-         if (_cpp_parse_assertion (pfile))
-           return CPP_ASSERTION;
-         return CPP_OTHER;
-       }
+         if (!CPP_OPTION (pfile, cplusplus_comments)
+             && !CPP_IN_SYSTEM_HEADER (pfile))
+           break;
 
-      if (pfile->parsing_define_directive)
-       {
-         c2 = PEEKC ();
-         if (c2 == '#')
-           {
-             FORWARD (1);
-             CPP_PUTC (pfile, c2);
-           }
-         else if (c2 == '%' && PEEKN (1) == ':')
+         /* Warn about comments only if pedantically GNUC89, and not
+            in system headers.  */
+         if (CPP_OPTION (pfile, lang) == CLK_GNUC89 && CPP_PEDANTIC (pfile)
+             && ! buffer->warned_cplusplus_comments)
            {
-             /* Digraph: "%:" == "#".  */
-             FORWARD (1);
-             CPP_RESERVE (pfile, 2);
-             CPP_PUTC_Q (pfile, c2);
-             CPP_PUTC_Q (pfile, GETC ());
+             cpp_pedwarn (pfile,
+                          "C++ style comments are not allowed in ISO C89");
+             cpp_pedwarn (pfile,
+                          "(this will be reported only once per input file)");
+             buffer->warned_cplusplus_comments = 1;
            }
-         else
-           return CPP_HASH;
 
-         return CPP_PASTE;
+         /* Skip_line_comment updates buffer->read_ahead.  */
+         if (skip_line_comment (pfile))
+           cpp_warning_with_line (pfile, pfile->lexer_pos.line,
+                                  pfile->lexer_pos.col,
+                                  "multi-line comment");
        }
 
-      if (!pfile->only_seen_white)
-       return CPP_OTHER;
-
-      /* Remove the "#" or "%:" from the token buffer.  */
-      CPP_ADJUST_WRITTEN (pfile, (c == '#' ? -1 : -2));
-      return CPP_DIRECTIVE;
-
-    case '\"':
-    case '\'':
-      parse_string (pfile, c);
-      return c == '\'' ? CPP_CHAR : CPP_STRING;
-
-    case '$':
-      if (!CPP_OPTION (pfile, dollars_in_ident))
-       goto randomchar;
-      goto letter;
-
-    case ':':
-      c2 = PEEKC ();
-      /* Digraph: ":>" == "]".  */
-      if (c2 == '>'
-         || (c2 == ':' && CPP_OPTION (pfile, cplusplus)))
-       goto op2;
-      goto randomchar;
-
-    case '&':
-    case '+':
-    case '|':
-      c2 = PEEKC ();
-      if (c2 == c || c2 == '=')
-       goto op2;
-      goto randomchar;
-
-    case '%':
-      /* Digraphs: "%:" == "#", "%>" == "}".  */
-      c2 = PEEKC ();
-      if (c2 == ':')
+      /* Skipping the comment has updated buffer->read_ahead.  */
+      if (!pfile->state.save_comments)
        {
-         FORWARD (1);
-         CPP_RESERVE (pfile, 2);
-         CPP_PUTC_Q (pfile, c);
-         CPP_PUTC_Q (pfile, c2);
-         goto hash;
+         result->flags |= PREV_WHITE;
+         goto next_char;
        }
-      else if (c2 == '>')
-       {
-         FORWARD (1);
-         CPP_RESERVE (pfile, 2);
-         CPP_PUTC_Q (pfile, c);
-         CPP_PUTC_Q (pfile, c2);
-         return CPP_OPEN_BRACE;
-       }
-      /* else fall through */
-
-    case '*':
-    case '!':
-    case '=':
-    case '^':
-      if (PEEKC () == '=')
-       goto op2;
-      goto randomchar;
 
-    case '-':
-      c2 = PEEKC ();
-      if (c2 == '-')
-       {
-         if (CPP_OPTION (pfile, chill))
-           goto comment;  /* Chill style comment */
-         else
-           goto op2;
-       }
-      else if (c2 == '=')
-       goto op2;
-      else if (c2 == '>')
-       {
-         if (CPP_OPTION (pfile, cplusplus) && PEEKN (1) == '*')
-           {
-             /* In C++, there's a ->* operator.  */
-             token = CPP_OTHER;
-             CPP_RESERVE (pfile, 4);
-             CPP_PUTC_Q (pfile, c);
-             CPP_PUTC_Q (pfile, GETC ());
-             CPP_PUTC_Q (pfile, GETC ());
-             return token;
-           }
-         goto op2;
-       }
-      goto randomchar;
+      /* Save the comment as a token in its own right.  */
+      save_comment (pfile, result, comment_start);
+      /* Don't do MI optimisation.  */
+      return;
 
     case '<':
-      if (pfile->parsing_include_directive)
-       {
-         for (;;)
-           {
-             CPP_PUTC (pfile, c);
-             if (c == '>')
-               break;
-             c = GETC ();
-             if (c == '\n' || c == EOF)
-               {
-                 cpp_error (pfile,
-                            "missing '>' in `#include <FILENAME>'");
-                 break;
-               }
-             else if (c == '\r')
-               {
-                 if (!CPP_BUFFER (pfile)->has_escapes)
-                   {
-                     /* Backslash newline is replaced by nothing. */
-                     CPP_ADJUST_WRITTEN (pfile, -1);
-                     CPP_BUMP_LINE (pfile);
-                   }
-                 else
-                   {
-                     /* We might conceivably get \r- or \r<space> in
-                        here.  Just delete 'em. */
-                     int d = GETC();
-                     if (d != '-' && d != ' ')
-                       cpp_ice (pfile, "unrecognized escape \\r%c", d);
-                     CPP_ADJUST_WRITTEN (pfile, -1);
-                   }                     
-               }
-           }
-         return CPP_STRING;
-       }
-      /* Digraphs: "<%" == "{", "<:" == "[".  */
-      c2 = PEEKC ();
-      if (c2 == '%')
+      if (pfile->state.angled_headers)
        {
-         FORWARD (1);
-         CPP_RESERVE (pfile, 2);
-         CPP_PUTC_Q (pfile, c);
-         CPP_PUTC_Q (pfile, c2);
-         return CPP_CLOSE_BRACE;
+         result->type = CPP_HEADER_NAME;
+         c = '>';              /* terminator.  */
+         goto make_string;
        }
-      else if (c2 == ':')
-       goto op2;
-      /* else fall through */
-    case '>':
-      c2 = PEEKC ();
-      if (c2 == '=')
-       goto op2;
-      /* GNU C++ supports MIN and MAX operators <? and >?.  */
-      if (c2 != c && (!CPP_OPTION (pfile, cplusplus) || c2 != '?'))
-       goto randomchar;
-      FORWARD(1);
-      CPP_RESERVE (pfile, 3);
-      CPP_PUTC_Q (pfile, c);
-      CPP_PUTC_Q (pfile, c2);
-      if (PEEKC () == '=')
-       CPP_PUTC_Q (pfile, GETC ());
-      return CPP_OTHER;
 
-    case '.':
-      c2 = PEEKC ();
-      if (ISDIGIT (c2))
+      result->type = CPP_LESS;
+      c = get_effective_char (buffer);
+      if (c == '=')
+       ACCEPT_CHAR (CPP_LESS_EQ);
+      else if (c == '<')
        {
-         CPP_PUTC (pfile, c);
-         c = GETC ();
-         goto number;
+         ACCEPT_CHAR (CPP_LSHIFT);
+         if (get_effective_char (buffer) == '=')
+           ACCEPT_CHAR (CPP_LSHIFT_EQ);
        }
-
-      /* In C++ there's a .* operator.  */
-      if (CPP_OPTION (pfile, cplusplus) && c2 == '*')
-       goto op2;
-
-      if (c2 == '.' && PEEKN(1) == '.')
+      else if (c == '?' && CPP_OPTION (pfile, cplusplus))
        {
-         CPP_RESERVE (pfile, 3);
-         CPP_PUTC_Q (pfile, '.');
-         CPP_PUTC_Q (pfile, '.');
-         CPP_PUTC_Q (pfile, '.');
-         FORWARD (2);
-         return CPP_ELLIPSIS;
+         ACCEPT_CHAR (CPP_MIN);
+         if (get_effective_char (buffer) == '=')
+           ACCEPT_CHAR (CPP_MIN_EQ);
        }
-      goto randomchar;
-
-    op2:
-      CPP_RESERVE (pfile, 2);
-      CPP_PUTC_Q (pfile, c);
-      CPP_PUTC_Q (pfile, GETC ());
-      return CPP_OTHER;
-
-    case 'L':
-      c2 = PEEKC ();
-      if ((c2 == '\'' || c2 == '\"') && !CPP_TRADITIONAL (pfile))
+      else if (c == ':' && CPP_OPTION (pfile, digraphs))
        {
-         CPP_PUTC (pfile, c);
-         c = GETC ();
-         parse_string (pfile, c);
-         return c == '\'' ? CPP_WCHAR : CPP_WSTRING;
+         ACCEPT_CHAR (CPP_OPEN_SQUARE);
+         result->flags |= DIGRAPH;
        }
-      goto letter;
-
-    case '0': case '1': case '2': case '3': case '4':
-    case '5': case '6': case '7': case '8': case '9':
-    number:
-    c2  = '.';
-    for (;;)
-      {
-       CPP_RESERVE (pfile, 2);
-       CPP_PUTC_Q (pfile, c);
-       c = PEEKC ();
-       if (c == EOF)
-         break;
-       if (!is_numchar(c) && c != '.'
-           && ((c2 != 'e' && c2 != 'E'
-                && ((c2 != 'p' && c2 != 'P')
-                    || CPP_OPTION (pfile, c89)))
-               || (c != '+' && c != '-')))
-         break;
-       FORWARD(1);
-       c2= c;
-      }
-    return CPP_NUMBER;
-    case 'b': case 'c': case 'd': case 'h': case 'o':
-    case 'B': case 'C': case 'D': case 'H': case 'O':
-      if (CPP_OPTION (pfile, chill) && PEEKC () == '\'')
+      else if (c == '%' && CPP_OPTION (pfile, digraphs))
        {
-         CPP_RESERVE (pfile, 2);
-         CPP_PUTC_Q (pfile, c);
-         CPP_PUTC_Q (pfile, '\'');
-         FORWARD(1);
-         for (;;)
-           {
-             c = GETC();
-             if (c == EOF)
-               goto chill_number_eof;
-             if (!is_numchar(c))
-               break;
-             CPP_PUTC (pfile, c);
-           }
-         if (c == '\'')
-           {
-             CPP_RESERVE (pfile, 2);
-             CPP_PUTC_Q (pfile, c);
-             return CPP_STRING;
-           }
-         else
-           {
-             FORWARD(-1);
-           chill_number_eof:
-             return CPP_NUMBER;
-           }
+         ACCEPT_CHAR (CPP_OPEN_BRACE);
+         result->flags |= DIGRAPH;
        }
-      else
-       goto letter;
-    case '_':
-    case 'a': case 'e': case 'f': case 'g': case 'i': case 'j':
-    case 'k': case 'l': case 'm': case 'n': case 'p': case 'q':
-    case 'r': case 's': case 't': case 'u': case 'v': case 'w':
-    case 'x': case 'y': case 'z':
-    case 'A': case 'E': case 'F': case 'G': case 'I': case 'J':
-    case 'K': case 'M': case 'N': case 'P': case 'Q': case 'R':
-    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
-    case 'Y': case 'Z':
-    letter:
-    _cpp_parse_name (pfile, c);
-    return CPP_MACRO;
-
-    case ' ':  case '\t':  case '\v': case '\f': case '\0':
-      {
-       int null_count = 0;
-
-       for (;;)
-         {
-           if (c == '\0')
-             null_count++;
-           else
-             CPP_PUTC (pfile, c);
-           c = PEEKC ();
-           if (c == EOF || !is_hspace(c))
-             break;
-           FORWARD(1);
-         }
-       if (null_count)
-         null_warning (pfile, null_count);
-       return CPP_HSPACE;
-      }
+      break;
 
-    case '\r':
-      if (CPP_BUFFER (pfile)->has_escapes)
-       {
-         c = GETC ();
-         if (c == '-')
-           {
-             if (pfile->output_escapes)
-               CPP_PUTS (pfile, "\r-", 2);
-             _cpp_parse_name (pfile, GETC ());
-             return CPP_NAME;
-           }
-         else if (c == ' ')
-           {
-             /* "\r " means a space, but only if necessary to prevent
-                accidental token concatenation.  */
-             CPP_RESERVE (pfile, 2);
-             if (pfile->output_escapes)
-               CPP_PUTC_Q (pfile, '\r');
-             CPP_PUTC_Q (pfile, c);
-             return CPP_HSPACE;
-           }
-         else
-           {
-             cpp_ice (pfile, "unrecognized escape \\r%c", c);
-             goto get_next;
-           }
-       }
-      else
+    case '>':
+      result->type = CPP_GREATER;
+      c = get_effective_char (buffer);
+      if (c == '=')
+       ACCEPT_CHAR (CPP_GREATER_EQ);
+      else if (c == '>')
        {
-         /* Backslash newline is ignored. */
-         if (!ACTIVE_MARK_P (pfile))
-           CPP_BUMP_LINE (pfile);
-         goto get_next;
+         ACCEPT_CHAR (CPP_RSHIFT);
+         if (get_effective_char (buffer) == '=')
+           ACCEPT_CHAR (CPP_RSHIFT_EQ);
        }
-
-    case '\n':
-      CPP_PUTC (pfile, c);
-      return CPP_VSPACE;
-
-    case '(': token = CPP_OPEN_PAREN;  goto char1;
-    case ')': token = CPP_CLOSE_PAREN; goto char1;
-    case '{': token = CPP_OPEN_BRACE;  goto char1;
-    case '}': token = CPP_CLOSE_BRACE; goto char1;
-    case ',': token = CPP_COMMA;       goto char1;
-    case ';': token = CPP_SEMICOLON;   goto char1;
-
-    randomchar:
-    default:
-      token = CPP_OTHER;
-    char1:
-      CPP_PUTC (pfile, c);
-      return token;
-    }
-}
-
-/* Check for and expand a macro, which is from WRITTEN to CPP_WRITTEN (pfile).
-   Caller is expected to have checked no_macro_expand.  */
-static int
-maybe_macroexpand (pfile, written)
-     cpp_reader *pfile;
-     long written;
-{
-  U_CHAR *macro = pfile->token_buffer + written;
-  size_t len = CPP_WRITTEN (pfile) - written;
-  HASHNODE *hp = _cpp_lookup (pfile, macro, len);
-
-  /* _cpp_lookup never returns null.  */
-  if (hp->type == T_VOID)
-    return 0;
-  if (hp->disabled || hp->type == T_IDENTITY)
-    {
-      if (pfile->output_escapes)
+      else if (c == '?' && CPP_OPTION (pfile, cplusplus))
        {
-         /* Insert a no-reexpand marker before IDENT.  */
-         CPP_RESERVE (pfile, 2);
-         CPP_ADJUST_WRITTEN (pfile, 2);
-         macro = pfile->token_buffer + written;
-
-         memmove (macro + 2, macro, len);
-         macro[0] = '\r';
-         macro[1] = '-';
+         ACCEPT_CHAR (CPP_MAX);
+         if (get_effective_char (buffer) == '=')
+           ACCEPT_CHAR (CPP_MAX_EQ);
        }
-      return 0;
-    }
-  if (hp->type == T_EMPTY)
-    {
-      /* Special case optimization: macro expands to nothing.  */
-      CPP_SET_WRITTEN (pfile, written);
-      CPP_PUTC_Q (pfile, ' ');
-      return 1;
-    }
+      break;
 
-  /* If macro wants an arglist, verify that a '(' follows.  */
-  if (hp->type == T_FMACRO)
-    {
-      int macbuf_whitespace = 0;
-      int c;
-
-      while (CPP_IS_MACRO_BUFFER (CPP_BUFFER (pfile)))
-       {
-         const U_CHAR *point = CPP_BUFFER (pfile)->cur;
-         for (;;)
-           {
-             _cpp_skip_hspace (pfile);
-             c = PEEKC ();
-             if (c == '\n')
-               FORWARD(1);
-             else
-               break;
-           }
-         if (point != CPP_BUFFER (pfile)->cur)
-           macbuf_whitespace = 1;
-         if (c == '(')
-           goto is_macro_call;
-         else if (c != EOF)
-           goto not_macro_call;
-         cpp_pop_buffer (pfile);
-       }
-
-      CPP_SET_MARK (pfile);
-      for (;;)
-       {
-         _cpp_skip_hspace (pfile);
-         c = PEEKC ();
-         if (c == '\n')
-           FORWARD(1);
-         else
-           break;
-       }
-      CPP_GOTO_MARK (pfile);
-
-      if (c != '(')
-       {
-       not_macro_call:
-         if (macbuf_whitespace)
-           CPP_PUTC (pfile, ' ');
-         return 0;
-       }
-    }
-
- is_macro_call:
-  /* This is now known to be a macro call.
-     Expand the macro, reading arguments as needed,
-     and push the expansion on the input stack.  */
-  _cpp_macroexpand (pfile, hp);
-  CPP_SET_WRITTEN (pfile, written);
-  return 1;
-}
-
-/* Complain about \v or \f in a preprocessing directive (constraint
-   violation, C99 6.10 para 5).  Caller has checked CPP_PEDANTIC.  */
-static void
-pedantic_whitespace (pfile, p, len)
-     cpp_reader *pfile;
-     U_CHAR *p;
-     unsigned int len;
-{
-  while (len)
-    {
-      if (*p == '\v')
-       cpp_pedwarn (pfile, "vertical tab in preprocessing directive");
-      else if (*p == '\f')
-       cpp_pedwarn (pfile, "form feed in preprocessing directive");
-      p++;
-      len--;
-    }
-}
-
-
-enum cpp_ttype
-cpp_get_token (pfile)
-     cpp_reader *pfile;
-{
-  enum cpp_ttype token;
-  long written = CPP_WRITTEN (pfile);
-
- get_next:
-  token = _cpp_lex_token (pfile);
-
-  switch (token)
-    {
-    default:
-      pfile->potential_control_macro = 0;
-      pfile->only_seen_white = 0;
-      return token;
-
-    case CPP_VSPACE:
-      if (pfile->only_seen_white == 0)
-       pfile->only_seen_white = 1;
-      CPP_BUMP_LINE (pfile);
-      return token;
-
-    case CPP_HSPACE:
-    case CPP_COMMENT:
-      return token;
-
-    case CPP_DIRECTIVE:
-      pfile->potential_control_macro = 0;
-      if (_cpp_handle_directive (pfile))
-       return CPP_DIRECTIVE;
-      pfile->only_seen_white = 0;
-      CPP_PUTC (pfile, '#');
-      return CPP_OTHER;
-
-    case CPP_MACRO:
-      pfile->potential_control_macro = 0;
-      pfile->only_seen_white = 0;
-      if (! pfile->no_macro_expand
-         && maybe_macroexpand (pfile, written))
-       goto get_next;
-      return CPP_NAME;
-
-    case CPP_EOF:
-      if (CPP_BUFFER (pfile) == NULL)
-       return CPP_EOF;
-      if (CPP_IS_MACRO_BUFFER (CPP_BUFFER (pfile)))
-       {
-         cpp_pop_buffer (pfile);
-         goto get_next;
-       }
-      cpp_pop_buffer (pfile);
-      return CPP_EOF;
-    }
-}
-
-/* Like cpp_get_token, but skip spaces and comments.  */
-
-enum cpp_ttype
-cpp_get_non_space_token (pfile)
-     cpp_reader *pfile;
-{
-  int old_written = CPP_WRITTEN (pfile);
-  for (;;)
-    {
-      enum cpp_ttype token = cpp_get_token (pfile);
-      if (token != CPP_COMMENT && token != CPP_HSPACE && token != CPP_VSPACE)
-       return token;
-      CPP_SET_WRITTEN (pfile, old_written);
-    }
-}
-
-/* Like cpp_get_token, except that it does not execute directives,
-   does not consume vertical space, and discards horizontal space.  */
-enum cpp_ttype
-_cpp_get_directive_token (pfile)
-     cpp_reader *pfile;
-{
-  long old_written;
-  enum cpp_ttype token;
-  int at_bol;
-
- get_next:
-  at_bol = (CPP_BUFFER (pfile)->cur == CPP_BUFFER (pfile)->line_base);
-  old_written = CPP_WRITTEN (pfile);
-  token = _cpp_lex_token (pfile);
-  switch (token)
-    {
-    default:
-      return token;
-
-    case CPP_VSPACE:
-      /* Put it back and return VSPACE.  */
-      FORWARD(-1);
-      CPP_ADJUST_WRITTEN (pfile, -1);
-      return CPP_VSPACE;
-
-    case CPP_HSPACE:
-      /* The purpose of this rather strange check is to prevent pedantic
-        warnings for ^L in an #ifdefed out block.  */
-      if (CPP_PEDANTIC (pfile) && ! at_bol)
-       pedantic_whitespace (pfile, pfile->token_buffer + old_written,
-                            CPP_WRITTEN (pfile) - old_written);
-      CPP_SET_WRITTEN (pfile, old_written);
-      goto get_next;
-      return CPP_HSPACE;
-
-    case CPP_DIRECTIVE:
-      /* Don't execute the directive, but don't smash it to OTHER either.  */
-      CPP_PUTC (pfile, '#');
-      return CPP_DIRECTIVE;
-
-    case CPP_MACRO:
-      if (! pfile->no_macro_expand
-         && maybe_macroexpand (pfile, old_written))
-       goto get_next;
-      return CPP_NAME;
-
-    case CPP_EOF:
-      if (CPP_IS_MACRO_BUFFER (CPP_BUFFER (pfile)))
-       {
-         cpp_pop_buffer (pfile);
-         goto get_next;
-       }
-      else
-       /* This can happen for files that don't end with a newline,
-          and for cpp_define and friends.  Pretend they do, so
-          callers don't have to deal.  A warning will be issued by
-          someone else, if necessary.  */
-       return CPP_VSPACE;
-    }
-}
-
-/* Determine the current line and column.  Used only by read_and_prescan. */
-static U_CHAR *
-find_position (start, limit, linep)
-     U_CHAR *start;
-     U_CHAR *limit;
-     unsigned long *linep;
-{
-  unsigned long line = *linep;
-  U_CHAR *lbase = start;
-  while (start < limit)
-    {
-      U_CHAR ch = *start++;
-      if (ch == '\n' || ch == '\r')
-       {
-         line++;
-         lbase = start;
-       }
-    }
-  *linep = line;
-  return lbase;
-}
-
-/* The following table is used by _cpp_read_and_prescan.  If we have
-   designated initializers, it can be constant data; otherwise, it is
-   set up at runtime by _cpp_init_input_buffer.  */
-
-#ifndef UCHAR_MAX
-#define UCHAR_MAX 255  /* assume 8-bit bytes */
-#endif
-
-#if (GCC_VERSION >= 2007)
-#define init_chartab()  /* nothing */
-#define CHARTAB __extension__ static const U_CHAR chartab[UCHAR_MAX + 1] = {
-#define END };
-#define s(p, v) [p] = v,
-#else
-#define CHARTAB static U_CHAR chartab[UCHAR_MAX + 1] = { 0 }; \
- static void init_chartab PARAMS ((void)) { \
- unsigned char *x = chartab;
-#define END }
-#define s(p, v) x[p] = v;
-#endif
-
-/* Table of characters that can't be handled in the inner loop.
-   Also contains the mapping between trigraph third characters and their
-   replacements.  */
-#define SPECCASE_CR        1
-#define SPECCASE_BACKSLASH 2
-#define SPECCASE_QUESTION  3
-CHARTAB
-  s('\r', SPECCASE_CR)
-  s('\\', SPECCASE_BACKSLASH)
-  s('?',  SPECCASE_QUESTION)
-
-  s('=', '#')  s(')', ']')     s('!', '|')
-  s('(', '[')  s('\'', '^')    s('>', '}')
-  s('/', '\\') s('<', '{')     s('-', '~')
-END
-
-#undef CHARTAB
-#undef END
-#undef s
-
-#define NORMAL(c) ((chartab[c]) == 0 || (chartab[c]) > SPECCASE_QUESTION)
-#define NONTRI(c) ((c) <= SPECCASE_QUESTION)
-
-/* Read the entire contents of file DESC into buffer BUF.  LEN is how
-   much memory to allocate initially; more will be allocated if
-   necessary.  Convert end-of-line markers (\n, \r, \r\n, \n\r) to
-   canonical form (\n).  If enabled, convert and/or warn about
-   trigraphs.  Convert backslash-newline to a one-character escape
-   (\r) and remove it from "embarrassing" places (i.e. the middle of a
-   token).  If there is no newline at the end of the file, add one and
-   warn.  Returns -1 on failure, or the actual length of the data to
-   be scanned.
-
-   This function does a lot of work, and can be a serious performance
-   bottleneck.  It has been tuned heavily; make sure you understand it
-   before hacking.  The common case - no trigraphs, Unix style line
-   breaks, backslash-newline set off by whitespace, newline at EOF -
-   has been optimized at the expense of the others.  The performance
-   penalty for DOS style line breaks (\r\n) is about 15%.
-   
-   Warnings lose particularly heavily since we have to determine the
-   line number, which involves scanning from the beginning of the file
-   or from the last warning.  The penalty for the absence of a newline
-   at the end of reload1.c is about 60%.  (reload1.c is 329k.)
-
-   If your file has more than one kind of end-of-line marker, you
-   will get messed-up line numbering.
-   
-   So that the cases of the switch statement do not have to concern
-   themselves with the complications of reading beyond the end of the
-   buffer, the buffer is guaranteed to have at least 3 characters in
-   it (or however many are left in the file, if less) on entry to the
-   switch.  This is enough to handle trigraphs and the "\\\n\r" and
-   "\\\r\n" cases.
-   
-   The end of the buffer is marked by a '\\', which, being a special
-   character, guarantees we will exit the fast-scan loops and perform
-   a refill. */
-long
-_cpp_read_and_prescan (pfile, fp, desc, len)
-     cpp_reader *pfile;
-     cpp_buffer *fp;
-     int desc;
-     size_t len;
-{
-  U_CHAR *buf = (U_CHAR *) xmalloc (len);
-  U_CHAR *ip, *op, *line_base;
-  U_CHAR *ibase;
-  unsigned long line;
-  unsigned int deferred_newlines;
-  size_t offset;
-  int count = 0;
-
-  offset = 0;
-  deferred_newlines = 0;
-  op = buf;
-  line_base = buf;
-  line = 1;
-  ibase = pfile->input_buffer + 3;
-  ip = ibase;
-  ip[-1] = '\0';  /* Guarantee no match with \n for SPECCASE_CR */
-
-  for (;;)
-    {
-      U_CHAR *near_buff_end;
-
-      count = read (desc, ibase, pfile->input_buffer_len);
-      if (count < 0)
-       goto error;
-      
-      ibase[count] = '\\';  /* Marks end of buffer */
-      if (count)
-       {
-         near_buff_end = pfile->input_buffer + count;
-         offset += count;
-         if (offset > len)
-           {
-             size_t delta_op;
-             size_t delta_line_base;
-             len = offset * 2;
-             if (offset > len)
-               /* len overflowed.
-                  This could happen if the file is larger than half the
-                  maximum address space of the machine. */
-               goto too_big;
-
-             delta_op = op - buf;
-             delta_line_base = line_base - buf;
-             buf = (U_CHAR *) xrealloc (buf, len);
-             op = buf + delta_op;
-             line_base = buf + delta_line_base;
-           }
-       }
-      else
-       {
-         if (ip == ibase)
-           break;
-         /* Allow normal processing of the (at most 2) remaining
-            characters.  The end-of-buffer marker is still present
-            and prevents false matches within the switch. */
-         near_buff_end = ibase - 1;
-       }
-
-      for (;;)
-       {
-         unsigned int span;
-
-         /* Deal with \-newline, potentially in the middle of a token. */
-         if (deferred_newlines)
-           {
-             if (op != buf && ! is_space (op[-1]) && op[-1] != '\r')
-               {
-                 /* Previous was not white space.  Skip to white
-                    space, if we can, before outputting the \r's */
-                 span = 0;
-                 while (ip[span] != ' '
-                        && ip[span] != '\t'
-                        && ip[span] != '\n'
-                        && NORMAL(ip[span]))
-                   span++;
-                 memcpy (op, ip, span);
-                 op += span;
-                 ip += span;
-                 if (! NORMAL(ip[0]))
-                   goto do_speccase;
-               }
-             while (deferred_newlines)
-               deferred_newlines--, *op++ = '\r';
-           }
-
-         /* Copy as much as we can without special treatment. */
-         span = 0;
-         while (NORMAL (ip[span])) span++;
-         memcpy (op, ip, span);
-         op += span;
-         ip += span;
-
-       do_speccase:
-         if (ip > near_buff_end) /* Do we have enough chars? */
-           break;
-         switch (chartab[*ip++])
-           {
-           case SPECCASE_CR:  /* \r */
-             if (ip[-2] != '\n')
-               {
-                 if (*ip == '\n')
-                   ip++;
-                 *op++ = '\n';
-               }
-             break;
-
-           case SPECCASE_BACKSLASH:  /* \ */
-             if (*ip == '\n')
-               {
-                 deferred_newlines++;
-                 ip++;
-                 if (*ip == '\r') ip++;
-               }
-             else if (*ip == '\r')
-               {
-                 deferred_newlines++;
-                 ip++;
-                 if (*ip == '\n') ip++;
-               }
-             else
-               *op++ = '\\';
-             break;
-
-           case SPECCASE_QUESTION: /* ? */
-             {
-               unsigned int d, t;
-
-               *op++ = '?'; /* Normal non-trigraph case */
-               if (ip[0] != '?')
-                 break;
-                   
-               d = ip[1];
-               t = chartab[d];
-               if (NONTRI (t))
-                 break;
-
-               if (CPP_OPTION (pfile, warn_trigraphs))
-                 {
-                   unsigned long col;
-                   line_base = find_position (line_base, op, &line);
-                   col = op - line_base + 1;
-                   if (CPP_OPTION (pfile, trigraphs))
-                     cpp_warning_with_line (pfile, line, col,
-                                            "trigraph ??%c converted to %c", d, t);
-                   else
-                     cpp_warning_with_line (pfile, line, col,
-                                            "trigraph ??%c ignored", d);
-                 }
-
-               ip += 2;
-               if (CPP_OPTION (pfile, trigraphs))
-                 {
-                   op[-1] = t;     /* Overwrite '?' */
-                   if (t == '\\')
-                     {
-                       op--;
-                       *--ip = '\\';
-                       goto do_speccase; /* May need buffer refill */
-                     }
-                 }
-               else
-                 {
-                   *op++ = '?';
-                   *op++ = d;
-                 }
-             }
-             break;
-           }
-       }
-      /* Copy previous char plus unprocessed (at most 2) chars
-        to beginning of buffer, refill it with another
-        read(), and continue processing */
-      memmove (ip - count - 1, ip - 1, 4 - (ip - near_buff_end));
-      ip -= count;
-    }
-
-  if (offset == 0)
-    return 0;
-
-  if (op[-1] != '\n')
-    {
-      unsigned long col;
-      line_base = find_position (line_base, op, &line);
-      col = op - line_base + 1;
-      cpp_warning_with_line (pfile, line, col, "no newline at end of file");
-      if (offset + 1 > len)
-       {
-         len += 1;
-         if (offset + 1 > len)
-           goto too_big;
-         buf = (U_CHAR *) xrealloc (buf, len);
-         op = buf + offset;
-       }
-      *op++ = '\n';
-    }
-
-  fp->buf = ((len - offset < 20) ? buf : (U_CHAR *)xrealloc (buf, op - buf));
-  return op - buf;
-
- too_big:
-  cpp_notice (pfile, "%s is too large (>%lu bytes)", fp->ihash->name,
-             (unsigned long)offset);
-  free (buf);
-  return -1;
-
- error:
-  cpp_error_from_errno (pfile, fp->ihash->name);
-  free (buf);
-  return -1;
-}
-
-/* Allocate pfile->input_buffer, and initialize chartab[]
-   if it hasn't happened already.  */
-void
-_cpp_init_input_buffer (pfile)
-     cpp_reader *pfile;
-{
-  U_CHAR *tmp;
-
-  init_chartab ();
-  init_token_list (pfile, &pfile->directbuf, 0);
-
-  /* Determine the appropriate size for the input buffer.  Normal C
-     source files are smaller than eight K.  */
-  /* 8Kbytes of buffer proper, 1 to detect running off the end without
-     address arithmetic all the time, and 3 for pushback during buffer
-     refill, in case there's a potential trigraph or end-of-line
-     digraph at the end of a block. */
-
-  tmp = (U_CHAR *) xmalloc (8192 + 1 + 3);
-  pfile->input_buffer = tmp;
-  pfile->input_buffer_len = 8192;
-}
-
-/* Utility routine:
-   Compares, in the manner of strcmp(3), the token beginning at TOKEN
-   and extending for LEN characters to the NUL-terminated string
-   STRING.  Typical usage:
-
-   if (! cpp_idcmp (pfile->token_buffer + here, CPP_WRITTEN (pfile) - here,
-                 "inline"))
-     { ... }
- */
-
-int
-cpp_idcmp (token, len, string)
-     const U_CHAR *token;
-     size_t len;
-     const char *string;
-{
-  size_t len2 = strlen (string);
-  int r;
-
-  if ((r = memcmp (token, string, MIN (len, len2))))
-    return r;
-
-  /* The longer of the two strings sorts after the shorter.  */
-  if (len == len2)
-    return 0;
-  else if (len < len2)
-    return -1;
-  else
-    return 1;
-}
-
-#ifdef NEW_LEXER
-
-/* Lexing algorithm.
-
- The original lexer in cpplib was made up of two passes: a first pass
- that replaced trigraphs and deleted esacped newlines, and a second
- pass that tokenized the result of the first pass.  Tokenisation was
- performed by peeking at the next character in the input stream.  For
- example, if the input stream contained "!=", the handler for the !
- character would peek at the next character, and if it were a '='
- would skip over it, and return a "!=" token, otherwise it would
- return just the "!" token.
-
- To implement a single-pass lexer, this peeking ahead is unworkable.
- An arbitrary number of escaped newlines, and trigraphs (in particular
- ??/ which translates to the escape \), could separate the '!' and '='
- in the input stream, yet the next token is still a "!=".
-
- Suppose instead that we lex by one logical line at a time, producing
- a token list or stack for each logical line, and when seeing the '!'
- push a CPP_NOT token on the list.  Then if the '!' is part of a
- longer token ("!=") we know we must see the remainder of the token by
- the time we reach the end of the logical line.  Thus we can have the
- '=' handler look at the previous token (at the end of the list / top
- of the stack) and see if it is a "!" token, and if so, instead of
- pushing a "=" token revise the existing token to be a "!=" token.
-
- This works in the presence of escaped newlines, because the '\' would
- have been pushed on the top of the stack as a CPP_BACKSLASH.  The
- newline ('\n' or '\r') handler looks at the token at the top of the
- stack to see if it is a CPP_BACKSLASH, and if so discards both.
- Otherwise it pushes the newline (CPP_VSPACE) token as normal.  Hence
- the '=' handler would never see any intervening escaped newlines.
-
- To make trigraphs work in this context, as in precedence trigraphs
- are highest and converted before anything else, the '?' handler does
- lookahead to see if it is a trigraph, and if so skips the trigraph
- and pushes the token it represents onto the top of the stack.  This
- also works in the particular case of a CPP_BACKSLASH trigraph.
-
- To the preprocessor, whitespace is only significant to the point of
- knowing whether whitespace precedes a particular token.  For example,
- the '=' handler needs to know whether there was whitespace between it
- and a "!" token on the top of the stack, to make the token conversion
- decision correctly.  So each token has a PREV_WHITESPACE flag to
- indicate this - the standard permits consecutive whitespace to be
- regarded as a single space.  The compiler front ends are not
- interested in whitespace at all; they just require a token stream.
- Another place where whitespace is significant to the preprocessor is
- a #define statment - if there is whitespace between the macro name
- and an initial "(" token the macro is "object-like", otherwise it is
- a function-like macro that takes arguments.
-
- However, all is not rosy.  Parsing of identifiers, numbers, comments
- and strings becomes trickier because of the possibility of raw
- trigraphs and escaped newlines in the input stream.
-
- The trigraphs are three consecutive characters beginning with two
- question marks.  A question mark is not valid as part of a number or
- identifier, so parsing of a number or identifier terminates normally
- upon reaching it, returning to the mainloop which handles the
- trigraph just like it would in any other position.  Similarly for the
- backslash of a backslash-newline combination.  So we just need the
- escaped-newline dropper in the mainloop to check if the token on the
- top of the stack after dropping the escaped newline is a number or
- identifier, and if so to continue the processing it as if nothing had
- happened.
-
- For strings, we replace trigraphs whenever we reach a quote or
- newline, because there might be a backslash trigraph escaping them.
- We need to be careful that we start trigraph replacing from where we
- left off previously, because it is possible for a first scan to leave
- "fake" trigraphs that a second scan would pick up as real (e.g. the
- sequence "????/\n=" would find a fake ??= trigraph after removing the
- escaped newline.)
-
- For line comments, on reaching a newline we scan the previous
- character(s) to see if it escaped, and continue if it is.  Block
- comments ignore everything and just focus on finding the comment
- termination mark.  The only difficult thing, and it is surprisingly
- tricky, is checking if an asterisk precedes the final slash since
- they could be separated by escaped newlines.  If the preprocessor is
- invoked with the output comments option, we don't bother removing
- escaped newlines and replacing trigraphs for output.
-
- Finally, numbers can begin with a period, which is pushed initially
- as a CPP_DOT token in its own right.  The digit handler checks if the
- previous token was a CPP_DOT not separated by whitespace, and if so
- pops it off the stack and pushes a period into the number's buffer
- before calling the number parser.
-
-*/
-
-static const unsigned char *digraph_spellings [] = {U"%:", U"%:%:", U"<:",
-                                                   U":>", U"<%", U"%>"};
-static unsigned char trigraph_map[256];
-
-static void
-expand_comment_space (list)
-     cpp_toklist *list;
-{
-  if (list->comments_cap == 0)
-    {
-      list->comments_cap = 10;
-      list->comments = (cpp_token *)
-       xmalloc (list->comments_cap * sizeof (cpp_token));
-    }
-  else
-    {
-      list->comments_cap *= 2;
-      list->comments = (cpp_token *)
-       xrealloc (list->comments, list->comments_cap);
-    }
-}
-
-void
-cpp_free_token_list (list)
-     cpp_toklist *list;
-{
-  if (list->comments)
-    free (list->comments);
-  free (list->tokens - 1);     /* Backup over dummy token.  */
-  free (list->namebuf);
-  free (list);
-}
-
-void
-init_trigraph_map ()
-{
-  trigraph_map['='] = '#';
-  trigraph_map['('] = '[';
-  trigraph_map[')'] = ']';
-  trigraph_map['/'] = '\\';
-  trigraph_map['\''] = '^';
-  trigraph_map['<'] = '{';
-  trigraph_map['>'] = '}';
-  trigraph_map['!'] = '|';
-  trigraph_map['-'] = '~';
-}
-
-/* Call when a trigraph is encountered.  It warns if necessary, and
-   returns true if the trigraph should be honoured.  END is the third
-   character of a trigraph in the input stream.  */
-static int
-trigraph_ok (pfile, end)
-     cpp_reader *pfile;
-     const unsigned char *end;
-{
-  int accept = CPP_OPTION (pfile, trigraphs);
-  
-  if (CPP_OPTION (pfile, warn_trigraphs))
-    {
-      unsigned int col = end - 1 - pfile->buffer->line_base;
-      if (accept)
-       cpp_warning_with_line (pfile, pfile->buffer->lineno, col, 
-                              "trigraph ??%c converted to %c",
-                              (int) *end, (int) trigraph_map[*end]);
-      else
-       cpp_warning_with_line (pfile, pfile->buffer->lineno, col,
-                              "trigraph ??%c ignored", (int) *end);
-    }
-  return accept;
-}
-
-/* Scan a string for trigraphs, warning or replacing them inline as
-   appropriate.  When parsing a string, we must call this routine
-   before processing a newline character (if trigraphs are enabled),
-   since the newline might be escaped by a preceding backslash
-   trigraph sequence.  Returns a pointer to the end of the name after
-   replacement.  */
-
-static unsigned char*
-trigraph_replace (pfile, src, limit)
-     cpp_reader *pfile;
-     unsigned char *src;
-     unsigned char* limit;
-{
-  unsigned char *dest;
-
-  /* Starting with src[1], find two consecutive '?'.  The case of no
-     trigraphs is streamlined.  */
-  
-  for (; src + 1 < limit; src += 2)
-    {
-      if (src[0] != '?')
-       continue;
-
-      /* Make src point to the 1st (NOT 2nd) of two consecutive '?'s.  */
-      if (src[-1] == '?')
-       src--;
-      else if (src + 2 == limit || src[1] != '?')
-       continue;
-
-      /* Check if it really is a trigraph.  */
-      if (trigraph_map[src[2]] == 0)
-       continue;
-
-      dest = src;
-      goto trigraph_found;
-    }
-  return limit;
-
-  /* Now we have a trigraph, we need to scan the remaining buffer, and
-     copy-shifting its contents left if replacement is enabled.  */
-  for (; src + 2 < limit; dest++, src++)
-    if ((*dest = *src) == '?' && src[1] == '?' && trigraph_map[src[2]])
-      {
-      trigraph_found:
-       src += 2;
-       if (trigraph_ok (pfile, pfile->buffer->cur - (limit - src)))
-         *dest = trigraph_map[*src];
-      }
-  
-  /* Copy remaining (at most 2) characters.  */
-  while (src < limit)
-    *dest++ = *src++;
-  return dest;
-}
-
-/* If CUR is a backslash or the end of a trigraphed backslash, return
-   a pointer to its beginning, otherwise NULL.  We don't read beyond
-   the buffer start, because there is the start of the comment in the
-   buffer.  */
-static const unsigned char *
-backslash_start (pfile, cur)
-     cpp_reader *pfile;
-     const unsigned char *cur;
-{
-  if (cur[0] == '\\')
-    return cur;
-  if (cur[0] == '/' && cur[-1] == '?' && cur[-2] == '?'
-      && trigraph_ok (pfile, cur))
-    return cur - 2;
-  return 0;
-}
-
-/* Skip a C-style block comment.  This is probably the trickiest
-   handler.  We find the end of the comment by seeing if an asterisk
-   is before every '/' we encounter.  The nasty complication is that a
-   previous asterisk may be separated by one or more escaped newlines.
-   Returns non-zero if comment terminated by EOF, zero otherwise.  */
-static int
-skip_block_comment2 (pfile)
-     cpp_reader *pfile;
-{
-  cpp_buffer *buffer = pfile->buffer;
-  const unsigned char *char_after_star = 0;
-  register const unsigned char *cur = buffer->cur;
-  int seen_eof = 0;
-  
-  /* Inner loop would think the comment has ended if the first comment
-     character is a '/'.  Avoid this and keep the inner loop clean by
-     skipping such a character.  */
-  if (cur < buffer->rlimit && cur[0] == '/')
-    cur++;
-
-  for (; cur < buffer->rlimit; )
-    {
-      unsigned char c = *cur++;
-
-      /* People like decorating comments with '*', so check for
-        '/' instead for efficiency.  */
-      if (c == '/')
-       {
-         if (cur[-2] == '*' || cur - 1 == char_after_star)
-           goto out;
-
-         /* Warn about potential nested comments, but not when
-            the final character inside the comment is a '/'.
-            Don't bother to get it right across escaped newlines.  */
-         if (CPP_OPTION (pfile, warn_comments) && cur + 1 < buffer->rlimit
-             && cur[0] == '*' && cur[1] != '/') 
-           {
-             buffer->cur = cur;
-             cpp_warning (pfile, "'/*' within comment");
-           }
-       }
-      else if (IS_NEWLINE(c))
-       {
-         const unsigned char* bslash = backslash_start (pfile, cur - 2);
-
-         handle_newline (cur, buffer->rlimit, c);
-         /* Work correctly if there is an asterisk before an
-            arbirtrarily long sequence of escaped newlines.  */
-         if (bslash && (bslash[-1] == '*' || bslash == char_after_star))
-           char_after_star = cur;
-         else
-           char_after_star = 0;
-       }
-    }
-  seen_eof = 1;
-
- out:
-  buffer->cur = cur;
-  return seen_eof;
-}
-
-/* Skip a C++ or Chill line comment.  Handles escaped newlines.
-   Returns non-zero if a multiline comment.  */
-static int
-skip_line_comment2 (pfile)
-     cpp_reader *pfile;
-{
-  cpp_buffer *buffer = pfile->buffer;
-  register const unsigned char *cur = buffer->cur;
-  int multiline = 0;
-
-  for (; cur < buffer->rlimit; )
-    {
-      unsigned char c = *cur++;
-
-      if (IS_NEWLINE (c))
-       {
-         /* Check for a (trigaph?) backslash escaping the newline.  */
-         if (!backslash_start (pfile, cur - 2))
-           goto out;
-         multiline = 1;
-         handle_newline (cur, buffer->rlimit, c);
-       }
-    }
-  cur++;
-
- out:
-  buffer->cur = cur - 1;       /* Leave newline for caller.  */
-  return multiline;
-}
-
-/* Skips whitespace, stopping at next non-whitespace character.
-   Adjusts pfile->col_adjust to account for tabs.  This enables tokens
-   to be assigned the correct column.  */
-static void
-skip_whitespace (pfile, in_directive)
-     cpp_reader *pfile;
-     int in_directive;
-{
-  cpp_buffer *buffer = pfile->buffer;
-  register const unsigned char *cur = buffer->cur;
-  unsigned short null_count = 0;
-
-  for (; cur < buffer->rlimit; )
-    {
-      unsigned char c = *cur++;
-
-      if (c == '\t')
-       {
-         unsigned int col = CPP_BUF_COLUMN (buffer, cur - 1);
-         pfile->col_adjust += (CPP_OPTION (pfile, tabstop) - 1
-                               - col % CPP_OPTION(pfile, tabstop));
-       }
-      if (IS_HSPACE(c))                /* FIXME: Fix ISTABLE.  */
-       continue;
-      if (!is_space(c) || IS_NEWLINE (c)) /* Main loop handles newlines.  */
-       goto out;
-      if (c == '\0')
-       null_count++;
-      /* Mut be '\f' or '\v' */
-      else if (in_directive && CPP_PEDANTIC (pfile))
-       cpp_pedwarn (pfile, "%s in preprocessing directive",
-                    c == '\f' ? "formfeed" : "vertical tab");
-    }
-  cur++;
-
- out:
-  buffer->cur = cur - 1;
-  if (null_count)
-    cpp_warning (pfile, null_count > 1 ? "embedded null characters ignored"
-                : "embedded null character ignored");
-}
-
-/* Parse (append) an identifier.  */
-static void
-parse_name (pfile, list, name)
-     cpp_reader *pfile;
-     cpp_toklist *list;
-     cpp_name *name;
-{
-  const unsigned char *name_limit;
-  unsigned char *namebuf;
-  cpp_buffer *buffer = pfile->buffer;
-  register const unsigned char *cur = buffer->cur;
-
- expanded:
-  name_limit = list->namebuf + list->name_cap;
-  namebuf = list->namebuf + list->name_used;
-
-  for (; cur < buffer->rlimit && namebuf < name_limit; )
-    {
-      unsigned char c = *namebuf = *cur; /* Copy a single char.  */
-
-      if (! is_idchar(c))
-       goto out;
-      namebuf++;
-      cur++;
-      if (c == '$' && CPP_PEDANTIC (pfile))
-       {
-         buffer->cur = cur;
-         cpp_pedwarn (pfile, "'$' character in identifier");
-       }
-    }
-
-  /* Run out of name space?  */
-  if (cur < buffer->rlimit)
-    {
-      list->name_used = namebuf - list->namebuf;
-      auto_expand_name_space (list);
-      goto expanded;
-    }
-
- out:
-  buffer->cur = cur;
-  name->len = namebuf - name->text;
-  list->name_used = namebuf - list->namebuf;
-}
-
-/* Parse (append) a number.  */
-
-#define VALID_SIGN(c, prevc) \
-  (((c) == '+' || (c) == '-') && \
-   ((prevc) == 'e' || (prevc) == 'E' \
-    || (((prevc) == 'p' || (prevc) == 'P') && !CPP_OPTION (pfile, c89))))
-
-static void
-parse_number (pfile, list, name)
-     cpp_reader *pfile;
-     cpp_toklist *list;
-     cpp_name *name;
-{
-  const unsigned char *name_limit;
-  unsigned char *namebuf;
-  cpp_buffer *buffer = pfile->buffer;
-  register const unsigned char *cur = buffer->cur;
-
- expanded:
-  name_limit = list->namebuf + list->name_cap;
-  namebuf = list->namebuf + list->name_used;
-
-  for (; cur < buffer->rlimit && namebuf < name_limit; )
-    {
-      unsigned char c = *namebuf = *cur; /* Copy a single char.  */
-
-      /* Perhaps we should accept '$' here if we accept it for
-         identifiers.  We know namebuf[-1] is safe, because for c to
-         be a sign we must have pushed at least one character.  */
-      if (!is_numchar (c) && c != '.' && ! VALID_SIGN (c, namebuf[-1]))
-       goto out;
-
-      namebuf++;
-      cur++;
-    }
-
-  /* Run out of name space?  */
-  if (cur < buffer->rlimit)
-    {
-      list->name_used = namebuf - list->namebuf;
-      auto_expand_name_space (list);
-      goto expanded;
-    }
-  
- out:
-  buffer->cur = cur;
-  name->len = namebuf - name->text;
-  list->name_used = namebuf - list->namebuf;
-}
-
-/* Places a string terminated by an unescaped TERMINATOR into a
-   cpp_name, which should be expandable and thus at the top of the
-   list's stack.  Handles embedded trigraphs, if necessary, and
-   escaped newlines.
-
-   Can be used for character constants (terminator = '\''), string
-   constants ('"') and angled headers ('>').  Multi-line strings are
-   allowed, except for within directives.  */
-
-static void
-parse_string2 (pfile, list, name, terminator)
-     cpp_reader *pfile;
-     cpp_toklist *list;
-     cpp_name *name;
-     unsigned int terminator;
-{
-  cpp_buffer *buffer = pfile->buffer;
-  register const unsigned char *cur = buffer->cur;
-  const unsigned char *name_limit;
-  unsigned char *namebuf;
-  unsigned int null_count = 0;
-  int trigraphed_len = 0;
-
- expanded:
-  name_limit = list->namebuf + list->name_cap;
-  namebuf = list->namebuf + list->name_used;
-
-  for (; cur < buffer->rlimit && namebuf < name_limit; )
-    {
-      unsigned int c = *namebuf++ = *cur++; /* Copy a single char.  */
-
-      if (c == '\0')
-       null_count++;
-      else if (c == terminator || IS_NEWLINE (c))
-       {
-         /* Needed for trigraph_replace and multiline string warning.  */
-         buffer->cur = cur;
-
-         /* Scan for trigraphs before checking if backslash-escaped.  */
-         if (CPP_OPTION (pfile, trigraphs)
-             || CPP_OPTION (pfile, warn_trigraphs))
-           {
-             namebuf = trigraph_replace (pfile, name->text + trigraphed_len,
-                                           namebuf);
-             trigraphed_len = namebuf - 2 - (name->text + trigraphed_len);
-             if (trigraphed_len < 0)
-               trigraphed_len = 0;
-           }
-
-         namebuf--;     /* Drop the newline / terminator from the name.  */
-         if (IS_NEWLINE (c))
-           {
-             /* Drop a backslash newline, and continue. */
-             if (namebuf[-1] == '\\')
-               {
-                 handle_newline (cur, buffer->rlimit, c);
-                 namebuf--;
-                 continue;
-               }
-
-             cur--;
-
-             /* In Fortran and assembly language, silently terminate
-                strings of either variety at end of line.  This is a
-                kludge around not knowing where comments are in these
-                languages.  */
-             if (CPP_OPTION (pfile, lang_fortran)
-                 || CPP_OPTION (pfile, lang_asm))
-               goto out;
-
-             /* Character constants, headers and asserts may not
-                extend over multiple lines.  In Standard C, neither
-                may strings.  We accept multiline strings as an
-                extension, but not in directives.  */
-             if (terminator != '"' || IS_DIRECTIVE (list))
-               goto unterminated;
-               
-             cur++;  /* Move forwards again.  */
-
-             if (pfile->multiline_string_line == 0)
-               {
-                 pfile->multiline_string_line = list->line;
-                 if (CPP_PEDANTIC (pfile))
-                   cpp_pedwarn (pfile, "multi-line string constant");
-               }
-
-             *namebuf++ = '\n';
-             handle_newline (cur, buffer->rlimit, c);
-           }
-         else
-           {
-             unsigned char *temp;
-
-             /* An odd number of consecutive backslashes represents
-                an escaped terminator.  */
-             temp = namebuf - 1;
-             while (temp >= name->text && *temp == '\\')
-               temp--;
-
-             if ((namebuf - temp) & 1)
-               goto out;
-             namebuf++;
-           }
-       }
-    }
-
-  /* Run out of name space?  */
-  if (cur < buffer->rlimit)
-    {
-      list->name_used = namebuf - list->namebuf;
-      auto_expand_name_space (list);
-      goto expanded;
-    }
-
-  /* We may not have trigraph-replaced the input for this code path,
-     but as the input is in error by being unterminated we don't
-     bother.  Prevent warnings about no newlines at EOF.  */
-  if (IS_NEWLINE(cur[-1]))
-    cur--;
-
- unterminated:
-  cpp_error (pfile, "missing terminating %c character", (int) terminator);
-
-  if (terminator == '\"' && pfile->multiline_string_line != list->line
-      && pfile->multiline_string_line != 0)
-    {
-      cpp_error_with_line (pfile, pfile->multiline_string_line, -1,
-                          "possible start of unterminated string literal");
-      pfile->multiline_string_line = 0;
-    }
-  
- out:
-  buffer->cur = cur;
-  name->len = namebuf - name->text;
-  list->name_used = namebuf - list->namebuf;
-
-  if (null_count > 0)
-    cpp_warning (pfile, (null_count > 1 ? "null characters preserved"
-                        : "null character preserved"));
-}
-
-/* The character TYPE helps us distinguish comment types: '*' = C
-   style, '-' = Chill-style and '/' = C++ style.  For code simplicity,
-   the stored comment includes the comment start and any terminator.  */
-
-#define COMMENT_START_LEN 2
-static void
-save_comment (list, from, len, tok_no, type)
-     cpp_toklist *list;
-     const unsigned char *from;
-     unsigned int len;
-     unsigned int tok_no;
-     unsigned int type;
-{
-  cpp_token *comment;
-  unsigned char *buffer;
-  
-  len += COMMENT_START_LEN;
-
-  if (list->comments_used == list->comments_cap)
-    expand_comment_space (list);
-
-  if (list->name_used + len > list->name_cap)
-    expand_name_space (list, len);
-
-  buffer = list->namebuf + list->name_used;
-
-  comment = &list->comments[list->comments_used++];
-  comment->type = CPP_COMMENT;
-  comment->aux = tok_no;
-  comment->val.name.len = len;
-  comment->val.name.text = buffer;
-
-  if (type == '*')
-    {
-      *buffer++ = '/';
-      *buffer++ = '*';
-    }
-  else
-    {
-      *buffer++ = type;
-      *buffer++ = type;
-    }
-
-  memcpy (buffer, from, len - COMMENT_START_LEN);
-  list->name_used += len;
-}
-
-/*
- *  The tokenizer's main loop.  Returns a token list, representing a
- *  logical line in the input file, terminated with a CPP_VSPACE
- *  token.  On EOF, a token list containing the single CPP_EOF token
- *  is returned.
- *
- *  Implementation relies almost entirely on lookback, rather than
- *  looking forwards.  This means that tokenization requires just
- *  a single pass of the file, even in the presence of trigraphs and
- *  escaped newlines, providing significant performance benefits.
- *  Trigraph overhead is negligible if they are disabled, and low
- *  even when enabled.
- */
-
-void
-_cpp_lex_line (pfile, list)
-     cpp_reader *pfile;
-     cpp_toklist *list;
-{
-  cpp_token *cur_token, *token_limit;
-  cpp_buffer *buffer = pfile->buffer;
-  register const unsigned char *cur = buffer->cur;
-  unsigned char flags = 0;
-
-  pfile->col_adjust = 0;
- expanded:
-  token_limit = list->tokens + list->tokens_cap;
-  cur_token = list->tokens + list->tokens_used;
-
-  for (; cur < buffer->rlimit && cur_token < token_limit;)
-    {
-      unsigned char c = *cur++;
-
-      /* Optimize whitespace skipping, as most tokens are probably
-        separated by whitespace. (' ' '\t' '\v' '\f' '\0').  */
-
-      if (is_hspace ((unsigned int) c))
-       {
-         /* Step back to get the null warning and tab correction.  */
-         buffer->cur = cur - 1;
-         skip_whitespace (pfile, IS_DIRECTIVE (list));
-         cur = buffer->cur;
-
-         flags = PREV_WHITESPACE;
-         if (cur == buffer->rlimit)
-           break;
-         c = *cur++;
-       }
-
-      /* Initialize current token.  Its type is set in the switch.  */
-      cur_token->col = CPP_BUF_COLUMN (buffer, cur);
-      cur_token->flags = flags;
-      flags = 0;
-
-      switch (c)
-       {
-       case '0': case '1': case '2': case '3': case '4':
-       case '5': case '6': case '7': case '8': case '9':
-         cur--;                /* Backup character.  */
-         if (PREV_TOKEN_TYPE == CPP_DOT && IMMED_TOKEN ())
-           {
-             /* Prepend an immediately previous CPP_DOT token.  */
-             cur_token--;
-             if (list->name_cap == list->name_used)
-               auto_expand_name_space (list);
-
-             cur_token->val.name.len = 1;
-             cur_token->val.name.text = list->namebuf + list->name_used;
-             list->namebuf[list->name_used++] = '.';
-           }
-         else
-           INIT_NAME (list, cur_token->val.name);
-
-       continue_number:
-         buffer->cur = cur;
-         parse_number (pfile, list, &cur_token->val.name);
-         cur = buffer->cur;
-
-         PUSH_TOKEN (CPP_NUMBER); /* Number not yet interpreted.  */
-         break;
-
-       letter:
-       case '_':
-       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
-       case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
-       case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
-       case 's': case 't': case 'u': case 'v': case 'w': case 'x':
-       case 'y': case 'z':
-       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
-       case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
-       case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
-       case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
-       case 'Y': case 'Z':
-         cur--;                     /* Backup character.  */
-         INIT_NAME (list, cur_token->val.name);
-         cur_token->type = CPP_NAME; /* Identifier, macro etc.  */
-
-       continue_name:
-         buffer->cur = cur;
-         parse_name (pfile, list, &cur_token->val.name);
-         cur = buffer->cur;
-
-         /* Find handler for newly created / extended directive.  */
-         if (IS_DIRECTIVE (list) && cur_token == &list->tokens[1])
-           _cpp_check_directive (list, cur_token);
-         cur_token++;
-         break;
-
-       case '\'':
-         /* Fall through.  */
-       case '\"':
-         cur_token->type = c == '\'' ? CPP_CHAR : CPP_STRING;
-         /* Do we have a wide string?  */
-         if (cur_token[-1].type == CPP_NAME && IMMED_TOKEN ()
-             && cur_token[-1].val.name.len == 1
-             && cur_token[-1].val.name.text[0] == 'L'
-             && !CPP_TRADITIONAL (pfile))
-           {
-             /* No need for 'L' any more.  */
-             list->name_used--;
-             (--cur_token)->type = (c == '\'' ? CPP_WCHAR : CPP_WSTRING);
-           }
-
-       do_parse_string:
-         /* Here c is one of ' " or >.  */
-         INIT_NAME (list, cur_token->val.name);
-         buffer->cur = cur;
-         parse_string2 (pfile, list, &cur_token->val.name, c);
-         cur = buffer->cur;
-         cur_token++;
-         break;
-
-       case '/':
-         cur_token->type = CPP_DIV;
-         if (IMMED_TOKEN ())
-           {
-             if (PREV_TOKEN_TYPE == CPP_DIV)
-               {
-                 /* We silently allow C++ comments in system headers,
-                    irrespective of conformance mode, because lots of
-                    broken systems do that and trying to clean it up
-                    in fixincludes is a nightmare.  */
-                 if (buffer->system_header_p)
-                   goto do_line_comment;
-                 else if (CPP_OPTION (pfile, cplusplus_comments))
-                   {
-                     if (CPP_OPTION (pfile, c89) && CPP_PEDANTIC (pfile)
-                         && ! buffer->warned_cplusplus_comments)
-                       {
-                         buffer->cur = cur;
-                         cpp_pedwarn (pfile,
-                            "C++ style comments are not allowed in ISO C89");
-                         cpp_pedwarn (pfile,
-                         "(this will be reported only once per input file)");
-                         buffer->warned_cplusplus_comments = 1;
-                       }
-                   do_line_comment:
-                     buffer->cur = cur;
-                     if (cur[-2] != c)
-                       cpp_warning (pfile,
-                                    "comment start split across lines");
-                     if (skip_line_comment2 (pfile))
-                       cpp_error_with_line (pfile, list->line,
-                                            cur_token[-1].col,
-                                            "multi-line comment");
-                     if (!CPP_OPTION (pfile, discard_comments))
-                       save_comment (list, cur, buffer->cur - cur,
-                                     cur_token - 1 - list->tokens, c);
-                     cur = buffer->cur;
-
-                     /* Back-up to first '-' or '/'.  */
-                     cur_token -= 2;
-                     if (!CPP_OPTION (pfile, traditional))
-                       flags = PREV_WHITESPACE;
-                   }
-               }
-           }
-         cur_token++;
-         break;
-                     
-       case '*':
-         cur_token->type = CPP_MULT;
-         if (IMMED_TOKEN ())
-           {
-             if (PREV_TOKEN_TYPE == CPP_DIV)
-               {
-                 buffer->cur = cur;
-                 if (cur[-2] != '/')
-                   cpp_warning (pfile,
-                                "comment start '/*' split across lines");
-                 if (skip_block_comment2 (pfile))
-                   cpp_error_with_line (pfile, list->line, cur_token[-1].col,
-                                        "unterminated comment");
-                 else if (buffer->cur[-2] != '*')
-                   cpp_warning (pfile,
-                                "comment end '*/' split across lines");
-                 if (!CPP_OPTION (pfile, discard_comments))
-                   save_comment (list, cur, buffer->cur - cur,
-                                cur_token - 1 - list->tokens, c);
-                 cur = buffer->cur;
-
-                 cur_token--;
-                 if (!CPP_OPTION (pfile, traditional))
-                   flags = PREV_WHITESPACE;
-                 break;
-               }
-             else if (CPP_OPTION (pfile, cplusplus))
-               {
-                 /* In C++, there are .* and ->* operators.  */
-                 if (PREV_TOKEN_TYPE == CPP_DEREF)
-                   BACKUP_TOKEN (CPP_DEREF_STAR);
-                 else if (PREV_TOKEN_TYPE == CPP_DOT)
-                   BACKUP_TOKEN (CPP_DOT_STAR);
-               }
-           }
-         cur_token++;
-         break;
-
-       case '\n':
-       case '\r':
-         handle_newline (cur, buffer->rlimit, c);
-         if (PREV_TOKEN_TYPE == CPP_BACKSLASH && IMMED_TOKEN ())
-           {
-             /* Remove the escaped newline.  Then continue to process
-                any interrupted name or number.  */
-             cur_token--;
-             if (IMMED_TOKEN ())
-               {
-                 cur_token--;
-                 if (cur_token->type == CPP_NAME)
-                   goto continue_name;
-                 else if (cur_token->type == CPP_NUMBER)
-                   goto continue_number;
-                 cur_token++;
-               }
-             /* Remember whitespace setting.  */
-             flags = cur_token->flags;
-             break;
-           }
-         if (PREV_TOKEN_TYPE == CPP_BACKSLASH)
-           {
-             buffer->cur = cur;
-             cpp_warning (pfile, "backslash and newline separated by space");
-           }
-         PUSH_TOKEN (CPP_VSPACE);
-         goto out;
-
-       case '-':
-         if (IMMED_TOKEN () && PREV_TOKEN_TYPE == CPP_MINUS)
-           {
-             if (CPP_OPTION (pfile, chill))
-               goto do_line_comment;
-             REVISE_TOKEN (CPP_MINUS_MINUS);
-           }
-         else
-           PUSH_TOKEN (CPP_MINUS);
-         break;
+    case '%':
+      lex_percent (buffer, result);
+      if (result->type == CPP_HASH)
+       goto do_hash;
+      break;
 
-         /* The digraph flag checking ensures that ## and %:%:
-            are interpreted as CPP_PASTE, but #%: and %:# are not.  */
-       make_hash:
-       case '#':
-         if (PREV_TOKEN_TYPE == CPP_HASH && IMMED_TOKEN ()
-             && ((cur_token->flags ^ cur_token[-1].flags) & DIGRAPH) == 0)
-           REVISE_TOKEN (CPP_PASTE);
-         else
-           PUSH_TOKEN (CPP_HASH);
-         break;
+    case '.':
+      lex_dot (pfile, result);
+      break;
 
-       case ':':
-         cur_token->type = CPP_COLON;
-         if (IMMED_TOKEN ())
-           {
-             if (PREV_TOKEN_TYPE == CPP_COLON
-                 && CPP_OPTION (pfile, cplusplus))
-               BACKUP_TOKEN (CPP_SCOPE);
-             /* Digraph: "<:" is a '['  */
-             else if (PREV_TOKEN_TYPE == CPP_LESS)
-               BACKUP_DIGRAPH (CPP_OPEN_SQUARE);
-             /* Digraph: "%:" is a '#'  */
-             else if (PREV_TOKEN_TYPE == CPP_MOD)
-               {
-                 (--cur_token)->flags |= DIGRAPH;
-                 goto make_hash;
-               }
-           }
-         cur_token++;
-         break;
+    case '+':
+      result->type = CPP_PLUS;
+      c = get_effective_char (buffer);
+      if (c == '=')
+       ACCEPT_CHAR (CPP_PLUS_EQ);
+      else if (c == '+')
+       ACCEPT_CHAR (CPP_PLUS_PLUS);
+      break;
 
-       case '&':
-         if (IMMED_TOKEN () && PREV_TOKEN_TYPE == CPP_AND)
-           REVISE_TOKEN (CPP_AND_AND);
-         else
-           PUSH_TOKEN (CPP_AND);
-         break;
+    case '-':
+      result->type = CPP_MINUS;
+      c = get_effective_char (buffer);
+      if (c == '>')
+       {
+         ACCEPT_CHAR (CPP_DEREF);
+         if (CPP_OPTION (pfile, cplusplus)
+             && get_effective_char (buffer) == '*')
+           ACCEPT_CHAR (CPP_DEREF_STAR);
+       }
+      else if (c == '=')
+       ACCEPT_CHAR (CPP_MINUS_EQ);
+      else if (c == '-')
+       ACCEPT_CHAR (CPP_MINUS_MINUS);
+      break;
 
-       make_or:
-       case '|':
-         if (IMMED_TOKEN () && PREV_TOKEN_TYPE == CPP_OR)
-           REVISE_TOKEN (CPP_OR_OR);
-         else
-           PUSH_TOKEN (CPP_OR);
-         break;
+    case '*':
+      result->type = CPP_MULT;
+      if (get_effective_char (buffer) == '=')
+       ACCEPT_CHAR (CPP_MULT_EQ);
+      break;
 
-       case '+':
-         if (IMMED_TOKEN () && PREV_TOKEN_TYPE == CPP_PLUS)
-           REVISE_TOKEN (CPP_PLUS_PLUS);
-         else
-           PUSH_TOKEN (CPP_PLUS);
-         break;
+    case '=':
+      result->type = CPP_EQ;
+      if (get_effective_char (buffer) == '=')
+       ACCEPT_CHAR (CPP_EQ_EQ);
+      break;
 
-       case '=':
-           /* This relies on equidistance of "?=" and "?" tokens.  */
-         if (IMMED_TOKEN () && PREV_TOKEN_TYPE <= CPP_LAST_EQ)
-           REVISE_TOKEN (PREV_TOKEN_TYPE + (CPP_EQ_EQ - CPP_EQ));
-         else
-           PUSH_TOKEN (CPP_EQ);
-         break;
+    case '!':
+      result->type = CPP_NOT;
+      if (get_effective_char (buffer) == '=')
+       ACCEPT_CHAR (CPP_NOT_EQ);
+      break;
 
-       case '>':
-         cur_token->type = CPP_GREATER;
-         if (IMMED_TOKEN ())
-           {
-             if (PREV_TOKEN_TYPE == CPP_GREATER)
-               BACKUP_TOKEN (CPP_RSHIFT);
-             else if (PREV_TOKEN_TYPE == CPP_MINUS)
-               BACKUP_TOKEN (CPP_DEREF);
-             /* Digraph: ":>" is a ']'  */
-             else if (PREV_TOKEN_TYPE == CPP_COLON)
-               BACKUP_DIGRAPH (CPP_CLOSE_SQUARE);
-             /* Digraph: "%>" is a '}'  */
-             else if (PREV_TOKEN_TYPE == CPP_MOD)
-               BACKUP_DIGRAPH (CPP_CLOSE_BRACE);
-           }
-         cur_token++;
-         break;
+    case '&':
+      result->type = CPP_AND;
+      c = get_effective_char (buffer);
+      if (c == '=')
+       ACCEPT_CHAR (CPP_AND_EQ);
+      else if (c == '&')
+       ACCEPT_CHAR (CPP_AND_AND);
+      break;
          
-       case '<':
-         if (IMMED_TOKEN () && PREV_TOKEN_TYPE == CPP_LESS)
-           {
-             REVISE_TOKEN (CPP_LSHIFT);
-             break;
-           }
-         /* Is this the beginning of a header name?  */
-         if (list->dir_flags & SYNTAX_INCLUDE)
-           {
-             c = '>';  /* Terminator.  */
-             cur_token->type = CPP_HEADER_NAME;
-             goto do_parse_string;
-           }
-         PUSH_TOKEN (CPP_LESS);
-         break;
+    case '#':
+      c = buffer->extra_char;  /* Can be set by error condition below.  */
+      if (c != EOF)
+       {
+         buffer->read_ahead = c;
+         buffer->extra_char = EOF;
+       }
+      else
+       c = get_effective_char (buffer);
 
-       case '%':
-         /* Digraph: "<%" is a '{'  */
-         cur_token->type = CPP_MOD;
-         if (IMMED_TOKEN () && PREV_TOKEN_TYPE == CPP_LESS)
-           BACKUP_DIGRAPH (CPP_OPEN_BRACE);
-         cur_token++;
+      if (c == '#')
+       {
+         ACCEPT_CHAR (CPP_PASTE);
          break;
+       }
 
-       case '?':
-         if (cur + 1 < buffer->rlimit && *cur == '?'
-             && trigraph_map[cur[1]] && trigraph_ok (pfile, cur + 1))
+      result->type = CPP_HASH;
+    do_hash:
+      if (bol)
+       {
+         if (pfile->state.parsing_args)
            {
-             /* Handle trigraph.  */
-             cur++;
-             switch (*cur++)
-               {
-               case '(': goto make_open_square;
-               case ')': goto make_close_square;
-               case '<': goto make_open_brace;
-               case '>': goto make_close_brace;
-               case '=': goto make_hash;
-               case '!': goto make_or;
-               case '-': goto make_complement;
-               case '/': goto make_backslash;
-               case '\'': goto make_xor;
-               }
+             /* 6.10.3 paragraph 11: If there are sequences of
+                preprocessing tokens within the list of arguments that
+                would otherwise act as preprocessing directives, the
+                behavior is undefined.
+
+                This implementation will report a hard error, terminate
+                the macro invocation, and proceed to process the
+                directive.  */
+             cpp_error (pfile,
+                        "directives may not be used inside a macro argument");
+
+             /* Put a '#' in lookahead, return CPP_EOF for parse_arg.  */
+             buffer->extra_char = buffer->read_ahead;
+             buffer->read_ahead = '#';
+             pfile->state.next_bol = 1;
+             result->type = CPP_EOF;
+
+             /* Get whitespace right - newline_in_args sets it.  */
+             if (pfile->lexer_pos.col == 1)
+               result->flags &= ~(PREV_WHITE | AVOID_LPASTE);
            }
-         if (IMMED_TOKEN () && CPP_OPTION (pfile, cplusplus))
+         else
            {
-             /* GNU C++ defines <? and >? operators.  */
-             if (PREV_TOKEN_TYPE == CPP_LESS)
-               {
-                 REVISE_TOKEN (CPP_MIN);
-                 break;
-               }
-             else if (PREV_TOKEN_TYPE == CPP_GREATER)
-               {
-                 REVISE_TOKEN (CPP_MAX);
-                 break;
-               }
+             /* This is the hash introducing a directive.  */
+             if (_cpp_handle_directive (pfile, result->flags & PREV_WHITE))
+               goto done_directive; /* bol still 1.  */
+             /* This is in fact an assembler #.  */
            }
-         PUSH_TOKEN (CPP_QUERY);
-         break;
+       }
+      break;
 
-       case '.':
-         if (PREV_TOKEN_TYPE == CPP_DOT && cur_token[-2].type == CPP_DOT
-             && IMMED_TOKEN ()
-             && !(cur_token[-1].flags & PREV_WHITESPACE))
-           {
-             cur_token -= 2;
-             PUSH_TOKEN (CPP_ELLIPSIS);
-           }
-         else
-           PUSH_TOKEN (CPP_DOT);
-         break;
+    case '|':
+      result->type = CPP_OR;
+      c = get_effective_char (buffer);
+      if (c == '=')
+       ACCEPT_CHAR (CPP_OR_EQ);
+      else if (c == '|')
+       ACCEPT_CHAR (CPP_OR_OR);
+      break;
 
-       make_complement:
-       case '~': PUSH_TOKEN (CPP_COMPL); break;
-       make_xor:
-       case '^': PUSH_TOKEN (CPP_XOR); break;
-       make_open_brace:
-       case '{': PUSH_TOKEN (CPP_OPEN_BRACE); break;
-       make_close_brace:
-       case '}': PUSH_TOKEN (CPP_CLOSE_BRACE); break;
-       make_open_square:
-       case '[': PUSH_TOKEN (CPP_OPEN_SQUARE); break;
-       make_close_square:
-       case ']': PUSH_TOKEN (CPP_CLOSE_SQUARE); break;
-       make_backslash:
-       case '\\': PUSH_TOKEN (CPP_BACKSLASH); break;
-       case '!': PUSH_TOKEN (CPP_NOT); break;
-       case ',': PUSH_TOKEN (CPP_COMMA); break;
-       case ';': PUSH_TOKEN (CPP_SEMICOLON); break;
-       case '(': PUSH_TOKEN (CPP_OPEN_PAREN); break;
-       case ')': PUSH_TOKEN (CPP_CLOSE_PAREN); break;
-
-       case '$':
-         if (CPP_OPTION (pfile, dollars_in_ident))
-           goto letter;
-         /* Fall through */
-       default:
-         cur_token->aux = c;
-         cur_token->val.name.len = 0; /* FIXME: needed for transition only */
-         PUSH_TOKEN (CPP_OTHER);
-         break;
+    case '^':
+      result->type = CPP_XOR;
+      if (get_effective_char (buffer) == '=')
+       ACCEPT_CHAR (CPP_XOR_EQ);
+      break;
+
+    case ':':
+      result->type = CPP_COLON;
+      c = get_effective_char (buffer);
+      if (c == ':' && CPP_OPTION (pfile, cplusplus))
+       ACCEPT_CHAR (CPP_SCOPE);
+      else if (c == '>' && CPP_OPTION (pfile, digraphs))
+       {
+         result->flags |= DIGRAPH;
+         ACCEPT_CHAR (CPP_CLOSE_SQUARE);
        }
-    }
+      break;
 
-  /* Run out of token space?  */
-  if (cur_token == token_limit)
-    {
-      list->tokens_used = cur_token - list->tokens;
-      expand_token_space (list);
-      goto expanded;
-    }
+    case '~': result->type = CPP_COMPL; break;
+    case ',': result->type = CPP_COMMA; break;
+    case '(': result->type = CPP_OPEN_PAREN; break;
+    case ')': result->type = CPP_CLOSE_PAREN; break;
+    case '[': result->type = CPP_OPEN_SQUARE; break;
+    case ']': result->type = CPP_CLOSE_SQUARE; break;
+    case '{': result->type = CPP_OPEN_BRACE; break;
+    case '}': result->type = CPP_CLOSE_BRACE; break;
+    case ';': result->type = CPP_SEMICOLON; break;
+
+    case '@':
+      if (CPP_OPTION (pfile, objc))
+       {
+         /* In Objective C, '@' may begin keywords or strings, like
+            @keyword or @"string".  It would be nice to call
+            get_effective_char here and test the result.  However, we
+            would then need to pass 2 characters to parse_identifier,
+            making it ugly and slowing down its main loop.  Instead,
+            we assume we have an identifier, and recover if not.  */
+         result->type = CPP_NAME;
+         result->val.node = parse_identifier (pfile, c);
+         if (result->val.node->length != 1)
+           break;
 
-  cur_token->type = CPP_EOF;
-  cur_token->flags = flags;
+         /* OK, so it wasn't an identifier.  Maybe a string?  */
+         if (buffer->read_ahead == '"')
+           {
+             c = '"';
+             ACCEPT_CHAR (CPP_OSTRING);
+             goto make_string;
+           }
+       }
+      goto random_char;
 
-  if (cur_token != &list->tokens[0])
-    {
-      /* Next call back will get just a CPP_EOF.  */
-      buffer->cur = cur;
-      cpp_warning (pfile, "no newline at end of file");
-      PUSH_TOKEN (CPP_VSPACE);
+    random_char:
+    default:
+      result->type = CPP_OTHER;
+      result->val.c = c;
+      break;
     }
 
- out:
-  buffer->cur = cur;
+  if (pfile->skipping)
+    goto skip;
 
-  list->tokens_used = cur_token - list->tokens;
+  /* If not in a directive, this token invalidates controlling macros.  */
+  if (!pfile->state.in_directive)
+    pfile->mi_state = MI_FAILED;
+}
 
-  /* FIXME:  take this check out and put it in the caller.
-     list->directive == 0 indicates an unknown directive (but null
-     directive is OK).  This is the first time we can be sure the
-     directive is invalid, and thus warn about it, because it might
-     have been split by escaped newlines.  Also, don't complain about
-     invalid directives in assembly source, we don't know where the
-     comments are, and # may introduce assembler pseudo-ops.  */
+/* An upper bound on the number of bytes needed to spell a token,
+   including preceding whitespace.  */
+unsigned int
+cpp_token_len (token)
+     const cpp_token *token;
+{
+  unsigned int len;
 
-  if (IS_DIRECTIVE (list) && list->dir_handler == 0
-      && list->tokens[1].type != CPP_VSPACE
-      && !CPP_OPTION (pfile, lang_asm))
-    cpp_error_with_line (pfile, list->line, list->tokens[1].col,
-                        "invalid preprocessing directive");
+  switch (TOKEN_SPELL (token))
+    {
+    default:           len = 0;                        break;
+    case SPELL_STRING: len = token->val.str.len;       break;
+    case SPELL_IDENT:  len = token->val.node->length;  break;
+    }
+  /* 1 for whitespace, 4 for comment delimeters.  */
+  return len + 5;
 }
 
 /* Write the spelling of a token TOKEN to BUFFER.  The buffer must
-   already contain the enough space to hold the token's spelling.  If
-   WHITESPACE is true, and the token was preceded by whitespace,
-   output a single space before the token proper.  Returns a pointer
-   to the character after the last character written.  */
-
-static unsigned char *
-spell_token (pfile, token, buffer, whitespace)
+   already contain the enough space to hold the token's spelling.
+   Returns a pointer to the character after the last character
+   written.  */
+unsigned char *
+cpp_spell_token (pfile, token, buffer)
      cpp_reader *pfile;                /* Would be nice to be rid of this...  */
-     cpp_token *token;
+     const cpp_token *token;
      unsigned char *buffer;
-     int whitespace;
 {
-  /* Whitespace will not be wanted by handlers of the # and ##
-     operators calling this function, but will be wanted by the
-     function that writes out the preprocessed file.  */
-  if (whitespace && token->flags & PREV_WHITESPACE)
-    *buffer++ = ' ';
-
-  switch (token_spellings[token->type].type)
+  switch (TOKEN_SPELL (token))
     {
     case SPELL_OPERATOR:
       {
@@ -3339,8 +1322,10 @@ spell_token (pfile, token, buffer, whitespace)
 
        if (token->flags & DIGRAPH)
          spelling = digraph_spellings[token->type - CPP_FIRST_DIGRAPH];
+       else if (token->flags & NAMED_OP)
+         goto spell_ident;
        else
-         spelling = token_spellings[token->type].spelling;
+         spelling = TOKEN_NAME (token);
        
        while ((c = *spelling++) != '\0')
          *buffer++ = c;
@@ -3348,107 +1333,514 @@ spell_token (pfile, token, buffer, whitespace)
       break;
 
     case SPELL_IDENT:
-      memcpy (buffer, token->val.name.text, token->val.name.len);
-      buffer += token->val.name.len;
+      spell_ident:
+      memcpy (buffer, token->val.node->name, token->val.node->length);
+      buffer += token->val.node->length;
       break;
 
     case SPELL_STRING:
       {
-       unsigned char c;
-
-       if (token->type == CPP_WSTRING || token->type == CPP_WCHAR)
-         *buffer++ = 'L';
-       c = '\'';
-       if (token->type == CPP_STRING || token->type == CPP_WSTRING)
-         c = '"';
-       *buffer++ = c;
-       memcpy (buffer, token->val.name.text, token->val.name.len);
-       buffer += token->val.name.len;
-       *buffer++ = c;
+       int left, right, tag;
+       switch (token->type)
+         {
+         case CPP_STRING:      left = '"';  right = '"';  tag = '\0'; break;
+         case CPP_WSTRING:     left = '"';  right = '"';  tag = 'L';  break;
+         case CPP_OSTRING:     left = '"';  right = '"';  tag = '@';  break;
+         case CPP_CHAR:        left = '\''; right = '\''; tag = '\0'; break;
+         case CPP_WCHAR:       left = '\''; right = '\''; tag = 'L';  break;
+         case CPP_HEADER_NAME: left = '<';  right = '>';  tag = '\0'; break;
+         default:              left = '\0'; right = '\0'; tag = '\0'; break;
+         }
+       if (tag) *buffer++ = tag;
+       if (left) *buffer++ = left;
+       memcpy (buffer, token->val.str.text, token->val.str.len);
+       buffer += token->val.str.len;
+       if (right) *buffer++ = right;
       }
       break;
 
     case SPELL_CHAR:
-      *buffer++ = token->aux;
+      *buffer++ = token->val.c;
       break;
 
     case SPELL_NONE:
-      cpp_ice (pfile, "Unspellable token");
+      cpp_ice (pfile, "Unspellable token %s", TOKEN_NAME (token));
       break;
     }
 
   return buffer;
 }
 
-/* Temporary function for illustrative purposes.  */
+/* Returns a token as a null-terminated string.  The string is
+   temporary, and automatically freed later.  Useful for diagnostics.  */
+unsigned char *
+cpp_token_as_text (pfile, token)
+     cpp_reader *pfile;
+     const cpp_token *token;
+{
+  unsigned int len = cpp_token_len (token);
+  unsigned char *start = _cpp_pool_alloc (&pfile->ident_pool, len), *end;
+
+  end = cpp_spell_token (pfile, token, start);
+  end[0] = '\0';
+
+  return start;
+}
+
+/* Used by C front ends.  Should really move to using cpp_token_as_text.  */
+const char *
+cpp_type2name (type)
+     enum cpp_ttype type;
+{
+  return (const char *) token_spellings[type].name;
+}
+
+/* Writes the spelling of token to FP.  Separate from cpp_spell_token
+   for efficiency - to avoid double-buffering.  Also, outputs a space
+   if PREV_WHITE is flagged.  */
 void
-_cpp_lex_file (pfile)
-     cpp_reader* pfile;
+cpp_output_token (token, fp)
+     const cpp_token *token;
+     FILE *fp;
+{
+  if (token->flags & PREV_WHITE)
+    putc (' ', fp);
+
+  switch (TOKEN_SPELL (token))
+    {
+    case SPELL_OPERATOR:
+      {
+       const unsigned char *spelling;
+
+       if (token->flags & DIGRAPH)
+         spelling = digraph_spellings[token->type - CPP_FIRST_DIGRAPH];
+       else if (token->flags & NAMED_OP)
+         goto spell_ident;
+       else
+         spelling = TOKEN_NAME (token);
+
+       ufputs (spelling, fp);
+      }
+      break;
+
+    spell_ident:
+    case SPELL_IDENT:
+      ufputs (token->val.node->name, fp);
+    break;
+
+    case SPELL_STRING:
+      {
+       int left, right, tag;
+       switch (token->type)
+         {
+         case CPP_STRING:      left = '"';  right = '"';  tag = '\0'; break;
+         case CPP_WSTRING:     left = '"';  right = '"';  tag = 'L';  break;
+         case CPP_OSTRING:     left = '"';  right = '"';  tag = '@';  break;
+         case CPP_CHAR:        left = '\''; right = '\''; tag = '\0'; break;
+         case CPP_WCHAR:       left = '\''; right = '\''; tag = 'L';  break;
+         case CPP_HEADER_NAME: left = '<';  right = '>';  tag = '\0'; break;
+         default:              left = '\0'; right = '\0'; tag = '\0'; break;
+         }
+       if (tag) putc (tag, fp);
+       if (left) putc (left, fp);
+       fwrite (token->val.str.text, 1, token->val.str.len, fp);
+       if (right) putc (right, fp);
+      }
+      break;
+
+    case SPELL_CHAR:
+      putc (token->val.c, fp);
+      break;
+
+    case SPELL_NONE:
+      /* An error, most probably.  */
+      break;
+    }
+}
+
+/* Compare two tokens.  */
+int
+_cpp_equiv_tokens (a, b)
+     const cpp_token *a, *b;
+{
+  if (a->type == b->type && a->flags == b->flags)
+    switch (TOKEN_SPELL (a))
+      {
+      default:                 /* Keep compiler happy.  */
+      case SPELL_OPERATOR:
+       return 1;
+      case SPELL_CHAR:
+       return a->val.c == b->val.c; /* Character.  */
+      case SPELL_NONE:
+       return (a->type != CPP_MACRO_ARG || a->val.arg_no == b->val.arg_no);
+      case SPELL_IDENT:
+       return a->val.node == b->val.node;
+      case SPELL_STRING:
+       return (a->val.str.len == b->val.str.len
+               && !memcmp (a->val.str.text, b->val.str.text,
+                           a->val.str.len));
+      }
+
+  return 0;
+}
+
+#if 0
+/* Compare two token lists.  */
+int
+_cpp_equiv_toklists (a, b)
+     const struct toklist *a, *b;
+{
+  unsigned int i, count;
+
+  count = a->limit - a->first;
+  if (count != (b->limit - b->first))
+    return 0;
+
+  for (i = 0; i < count; i++)
+    if (! _cpp_equiv_tokens (&a->first[i], &b->first[i]))
+      return 0;
+
+  return 1;
+}
+#endif
+
+/* Determine whether two tokens can be pasted together, and if so,
+   what the resulting token is.  Returns CPP_EOF if the tokens cannot
+   be pasted, or the appropriate type for the merged token if they
+   can.  */
+enum cpp_ttype
+cpp_can_paste (pfile, token1, token2, digraph)
+     cpp_reader * pfile;
+     const cpp_token *token1, *token2;
+     int* digraph;
 {
-  int recycle;
-  cpp_toklist* list;
+  enum cpp_ttype a = token1->type, b = token2->type;
+  int cxx = CPP_OPTION (pfile, cplusplus);
+
+  /* Treat named operators as if they were ordinary NAMEs.  */
+  if (token1->flags & NAMED_OP)
+    a = CPP_NAME;
+  if (token2->flags & NAMED_OP)
+    b = CPP_NAME;
 
-  init_trigraph_map ();
-  list = (cpp_toklist *) xmalloc (sizeof (cpp_toklist));
+  if (a <= CPP_LAST_EQ && b == CPP_EQ)
+    return a + (CPP_EQ_EQ - CPP_EQ);
 
-  for (recycle = 0; ;)
+  switch (a)
     {
-      init_token_list (pfile, list, recycle);
-      recycle = 1;
+    case CPP_GREATER:
+      if (b == a) return CPP_RSHIFT;
+      if (b == CPP_QUERY && cxx)       return CPP_MAX;
+      if (b == CPP_GREATER_EQ) return CPP_RSHIFT_EQ;
+      break;
+    case CPP_LESS:
+      if (b == a) return CPP_LSHIFT;
+      if (b == CPP_QUERY && cxx)       return CPP_MIN;
+      if (b == CPP_LESS_EQ)    return CPP_LSHIFT_EQ;
+      if (CPP_OPTION (pfile, digraphs))
+       {
+         if (b == CPP_COLON)
+           {*digraph = 1; return CPP_OPEN_SQUARE;} /* <: digraph */
+         if (b == CPP_MOD)
+           {*digraph = 1; return CPP_OPEN_BRACE;}      /* <% digraph */
+       }
+      break;
 
-      _cpp_lex_line (pfile, list);
-      if (list->tokens[0].type == CPP_EOF)
-       break;
+    case CPP_PLUS: if (b == a) return CPP_PLUS_PLUS; break;
+    case CPP_AND:  if (b == a) return CPP_AND_AND; break;
+    case CPP_OR:   if (b == a) return CPP_OR_OR;   break;
+
+    case CPP_MINUS:
+      if (b == a)              return CPP_MINUS_MINUS;
+      if (b == CPP_GREATER)    return CPP_DEREF;
+      break;
+    case CPP_COLON:
+      if (b == a && cxx)       return CPP_SCOPE;
+      if (b == CPP_GREATER && CPP_OPTION (pfile, digraphs))
+       {*digraph = 1; return CPP_CLOSE_SQUARE;} /* :> digraph */
+      break;
 
-      if (list->dir_handler)
+    case CPP_MOD:
+      if (CPP_OPTION (pfile, digraphs))
        {
-         if (list->dir_handler (pfile))
-           {
-             list = (cpp_toklist *) xmalloc (sizeof (cpp_toklist));
-             recycle = 0;
-           }
+         if (b == CPP_GREATER)
+           {*digraph = 1; return CPP_CLOSE_BRACE;}  /* %> digraph */
+         if (b == CPP_COLON)
+           {*digraph = 1; return CPP_HASH;}         /* %: digraph */
        }
-      else
-       _cpp_output_list (pfile, list);
+      break;
+    case CPP_DEREF:
+      if (b == CPP_MULT && cxx)        return CPP_DEREF_STAR;
+      break;
+    case CPP_DOT:
+      if (b == CPP_MULT && cxx)        return CPP_DOT_STAR;
+      if (b == CPP_NUMBER)     return CPP_NUMBER;
+      break;
+
+    case CPP_HASH:
+      if (b == a && (token1->flags & DIGRAPH) == (token2->flags & DIGRAPH))
+       /* %:%: digraph */
+       {*digraph = (token1->flags & DIGRAPH); return CPP_PASTE;}
+      break;
+
+    case CPP_NAME:
+      if (b == CPP_NAME)       return CPP_NAME;
+      if (b == CPP_NUMBER
+         && name_p (pfile, &token2->val.str)) return CPP_NAME;
+      if (b == CPP_CHAR
+         && token1->val.node == pfile->spec_nodes.n_L) return CPP_WCHAR;
+      if (b == CPP_STRING
+         && token1->val.node == pfile->spec_nodes.n_L) return CPP_WSTRING;
+      break;
+
+    case CPP_NUMBER:
+      if (b == CPP_NUMBER)     return CPP_NUMBER;
+      if (b == CPP_NAME)       return CPP_NUMBER;
+      if (b == CPP_DOT)                return CPP_NUMBER;
+      /* Numbers cannot have length zero, so this is safe.  */
+      if ((b == CPP_PLUS || b == CPP_MINUS)
+         && VALID_SIGN ('+', token1->val.str.text[token1->val.str.len - 1]))
+       return CPP_NUMBER;
+      break;
+
+    case CPP_OTHER:
+      if (CPP_OPTION (pfile, objc) && token1->val.c == '@')
+       {
+         if (b == CPP_NAME)    return CPP_NAME;
+         if (b == CPP_STRING)  return CPP_OSTRING;
+       }
+
+    default:
+      break;
     }
+
+  return CPP_EOF;
 }
 
-/* Temporary function for illustrative purposes.  */
-static void
-_cpp_output_list (pfile, list)
+/* Returns nonzero if a space should be inserted to avoid an
+   accidental token paste for output.  For simplicity, it is
+   conservative, and occasionally advises a space where one is not
+   needed, e.g. "." and ".2".  */
+
+int
+cpp_avoid_paste (pfile, token1, token2)
      cpp_reader *pfile;
-     cpp_toklist *list;
+     const cpp_token *token1, *token2;
 {
-  cpp_token *token, *comment, *comment_before = 0;
+  enum cpp_ttype a = token1->type, b = token2->type;
+  cppchar_t c;
+
+  if (token1->flags & NAMED_OP)
+    a = CPP_NAME;
+  if (token2->flags & NAMED_OP)
+    b = CPP_NAME;
+
+  c = EOF;
+  if (token2->flags & DIGRAPH)
+    c = digraph_spellings[b - CPP_FIRST_DIGRAPH][0];
+  else if (token_spellings[b].category == SPELL_OPERATOR)
+    c = token_spellings[b].name[0];
+
+  /* Quickly get everything that can paste with an '='.  */
+  if (a <= CPP_LAST_EQ && c == '=')
+    return 1;
 
-  if (list->comments_used > 0)
+  switch (a)
     {
-      comment = &list->comments[0];
-      comment_before = &list->tokens[comment->aux];
+    case CPP_GREATER:  return c == '>' || c == '?';
+    case CPP_LESS:     return c == '<' || c == '?' || c == '%' || c == ':';
+    case CPP_PLUS:     return c == '+';
+    case CPP_MINUS:    return c == '-' || c == '>';
+    case CPP_DIV:      return c == '/' || c == '*'; /* Comments.  */
+    case CPP_MOD:      return c == ':' || c == '>';
+    case CPP_AND:      return c == '&';
+    case CPP_OR:       return c == '|';
+    case CPP_COLON:    return c == ':' || c == '>';
+    case CPP_DEREF:    return c == '*';
+    case CPP_DOT:      return c == '.' || c == '%' || b == CPP_NUMBER;
+    case CPP_HASH:     return c == '#' || c == '%'; /* Digraph form.  */
+    case CPP_NAME:     return ((b == CPP_NUMBER
+                                && name_p (pfile, &token2->val.str))
+                               || b == CPP_NAME
+                               || b == CPP_CHAR || b == CPP_STRING); /* L */
+    case CPP_NUMBER:   return (b == CPP_NUMBER || b == CPP_NAME
+                               || c == '.' || c == '+' || c == '-');
+    case CPP_OTHER:    return (CPP_OPTION (pfile, objc)
+                               && token1->val.c == '@'
+                               && (b == CPP_NAME || b == CPP_STRING));
+    default:           break;
     }
 
-  token = &list->tokens[0];
-  do
+  return 0;
+}
+
+/* Output all the remaining tokens on the current line, and a newline
+   character, to FP.  Leading whitespace is removed.  */
+void
+cpp_output_line (pfile, fp)
+     cpp_reader *pfile;
+     FILE *fp;
+{
+  cpp_token token;
+
+  cpp_get_token (pfile, &token);
+  token.flags &= ~PREV_WHITE;
+  while (token.type != CPP_EOF)
     {
-      /* Output comments if -C.  */
-      while (token == comment_before)
-       {
-         /* Make space for the comment, and copy it out.  */
-         CPP_RESERVE (pfile, TOKEN_LEN (comment));
-         pfile->limit = spell_token (pfile, comment, pfile->limit, 0);
-
-         /* Stop if no comments left, or no more comments appear
-             before the current token.  */
-         comment++;
-         if (comment == list->comments + list->comments_used)
-           break;
-         comment_before = &list->tokens[comment->aux];
-       }
+      cpp_output_token (&token, fp);
+      cpp_get_token (pfile, &token);
+    }
+
+  putc ('\n', fp);
+}
+
+/* Memory pools.  */
+
+struct dummy
+{
+  char c;
+  union
+  {
+    double d;
+    int *p;
+  } u;
+};
+
+#define DEFAULT_ALIGNMENT (offsetof (struct dummy, u))
+
+static int
+chunk_suitable (pool, chunk, size)
+     cpp_pool *pool;
+     cpp_chunk *chunk;
+     unsigned int size;
+{
+  /* Being at least twice SIZE means we can use memcpy in
+     _cpp_next_chunk rather than memmove.  Besides, it's a good idea
+     anyway.  */
+  return (chunk && pool->locked != chunk
+         && (unsigned int) (chunk->limit - chunk->base) >= size * 2);
+}
+
+/* Returns the end of the new pool.  PTR points to a char in the old
+   pool, and is updated to point to the same char in the new pool.  */
+unsigned char *
+_cpp_next_chunk (pool, len, ptr)
+     cpp_pool *pool;
+     unsigned int len;
+     unsigned char **ptr;
+{
+  cpp_chunk *chunk = pool->cur->next;
+
+  /* LEN is the minimum size we want in the new pool.  */
+  len += POOL_ROOM (pool);
+  if (! chunk_suitable (pool, chunk, len))
+    {
+      chunk = new_chunk (POOL_SIZE (pool) * 2 + len);
 
-      CPP_RESERVE (pfile, TOKEN_LEN (token));
-      pfile->limit = spell_token (pfile, token, pfile->limit, 1);
+      chunk->next = pool->cur->next;
+      pool->cur->next = chunk;
     }
-  while (token++->type != CPP_VSPACE);
+
+  /* Update the pointer before changing chunk's front.  */
+  if (ptr)
+    *ptr += chunk->base - POOL_FRONT (pool);
+
+  memcpy (chunk->base, POOL_FRONT (pool), POOL_ROOM (pool));
+  chunk->front = chunk->base;
+
+  pool->cur = chunk;
+  return POOL_LIMIT (pool);
 }
 
-#endif
+static cpp_chunk *
+new_chunk (size)
+     unsigned int size;
+{
+  unsigned char *base;
+  cpp_chunk *result;
+
+  size = ALIGN (size, DEFAULT_ALIGNMENT);
+  base = (unsigned char *) xmalloc (size + sizeof (cpp_chunk));
+  /* Put the chunk descriptor at the end.  Then chunk overruns will
+     cause obvious chaos.  */
+  result = (cpp_chunk *) (base + size);
+  result->base = base;
+  result->front = base;
+  result->limit = base + size;
+  result->next = 0;
+
+  return result;
+}
+
+void
+_cpp_init_pool (pool, size, align, temp)
+     cpp_pool *pool;
+     unsigned int size, align, temp;
+{
+  if (align == 0)
+    align = DEFAULT_ALIGNMENT;
+  if (align & (align - 1))
+    abort ();
+  pool->align = align;
+  pool->cur = new_chunk (size);
+  pool->locked = 0;
+  pool->locks = 0;
+  if (temp)
+    pool->cur->next = pool->cur;
+}
+
+void
+_cpp_lock_pool (pool)
+     cpp_pool *pool;
+{
+  if (pool->locks++ == 0)
+    pool->locked = pool->cur;
+}
+
+void
+_cpp_unlock_pool (pool)
+     cpp_pool *pool;
+{
+  if (--pool->locks == 0)
+    pool->locked = 0;
+}
+
+void
+_cpp_free_pool (pool)
+     cpp_pool *pool;
+{
+  cpp_chunk *chunk = pool->cur, *next;
+
+  do
+    {
+      next = chunk->next;
+      free (chunk->base);
+      chunk = next;
+    }
+  while (chunk && chunk != pool->cur);
+}
+
+/* Reserve LEN bytes from a memory pool.  */
+unsigned char *
+_cpp_pool_reserve (pool, len)
+     cpp_pool *pool;
+     unsigned int len;
+{
+  len = ALIGN (len, pool->align);
+  if (len > (unsigned int) POOL_ROOM (pool))
+    _cpp_next_chunk (pool, len, 0);
+
+  return POOL_FRONT (pool);
+}
+
+/* Allocate LEN bytes from a memory pool.  */
+unsigned char *
+_cpp_pool_alloc (pool, len)
+     cpp_pool *pool;
+     unsigned int len;
+{
+  unsigned char *result = _cpp_pool_reserve (pool, len);
+
+  POOL_COMMIT (pool, len);
+  return result;
+}