diff libcpp/traditional.c @ 0:a06113de4d67

first commit
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2009 14:47:48 +0900
parents
children 04ced10e8804
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libcpp/traditional.c	Fri Jul 17 14:47:48 2009 +0900
@@ -0,0 +1,1170 @@
+/* CPP Library - traditional lexical analysis and macro expansion.
+   Copyright (C) 2002, 2004, 2005, 2007, 2008, 2009
+   Free Software Foundation, Inc.
+   Contributed by Neil Booth, May 2002
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "cpplib.h"
+#include "internal.h"
+
+/* The replacement text of a function-like macro is stored as a
+   contiguous sequence of aligned blocks, each representing the text
+   between subsequent parameters.
+
+   Each block comprises the text between its surrounding parameters,
+   the length of that text, and the one-based index of the following
+   parameter.  The final block in the replacement text is easily
+   recognizable as it has an argument index of zero.  */
+
+struct block
+{
+  unsigned int text_len;
+  unsigned short arg_index;
+  uchar text[1];
+};
+
+#define BLOCK_HEADER_LEN offsetof (struct block, text)
+#define BLOCK_LEN(TEXT_LEN) CPP_ALIGN (BLOCK_HEADER_LEN + (TEXT_LEN))
+
+/* Structure holding information about a function-like macro
+   invocation.  */
+struct fun_macro
+{
+  /* Memory buffer holding the trad_arg array.  */
+  _cpp_buff *buff;
+
+  /* An array of size the number of macro parameters + 1, containing
+     the offsets of the start of each macro argument in the output
+     buffer.  The argument continues until the character before the
+     start of the next one.  */
+  size_t *args;
+
+  /* The hashnode of the macro.  */
+  cpp_hashnode *node;
+
+  /* The offset of the macro name in the output buffer.  */
+  size_t offset;
+
+  /* The line the macro name appeared on.  */
+  source_location line;
+
+  /* Zero-based index of argument being currently lexed.  */
+  unsigned int argc;
+};
+
+/* Lexing state.  It is mostly used to prevent macro expansion.  */
+enum ls {ls_none = 0,		/* Normal state.  */
+	 ls_fun_open,		/* When looking for '('.  */
+	 ls_fun_close,		/* When looking for ')'.  */
+	 ls_defined,		/* After defined.  */
+	 ls_defined_close,	/* Looking for ')' of defined().  */
+	 ls_hash,		/* After # in preprocessor conditional.  */
+	 ls_predicate,		/* After the predicate, maybe paren?  */
+	 ls_answer};		/* In answer to predicate.  */
+
+/* Lexing TODO: Maybe handle space in escaped newlines.  Stop lex.c
+   from recognizing comments and directives during its lexing pass.  */
+
+static const uchar *skip_whitespace (cpp_reader *, const uchar *, int);
+static cpp_hashnode *lex_identifier (cpp_reader *, const uchar *);
+static const uchar *copy_comment (cpp_reader *, const uchar *, int);
+static void check_output_buffer (cpp_reader *, size_t);
+static void push_replacement_text (cpp_reader *, cpp_hashnode *);
+static bool scan_parameters (cpp_reader *, cpp_macro *);
+static bool recursive_macro (cpp_reader *, cpp_hashnode *);
+static void save_replacement_text (cpp_reader *, cpp_macro *, unsigned int);
+static void maybe_start_funlike (cpp_reader *, cpp_hashnode *, const uchar *,
+				 struct fun_macro *);
+static void save_argument (struct fun_macro *, size_t);
+static void replace_args_and_push (cpp_reader *, struct fun_macro *);
+static size_t canonicalize_text (uchar *, const uchar *, size_t, uchar *);
+
+/* Ensures we have N bytes' space in the output buffer, and
+   reallocates it if not.  */
+static void
+check_output_buffer (cpp_reader *pfile, size_t n)
+{
+  /* We might need two bytes to terminate an unterminated comment, and
+     one more to terminate the line with a NUL.  */
+  n += 2 + 1;
+
+  if (n > (size_t) (pfile->out.limit - pfile->out.cur))
+    {
+      size_t size = pfile->out.cur - pfile->out.base;
+      size_t new_size = (size + n) * 3 / 2;
+
+      pfile->out.base = XRESIZEVEC (unsigned char, pfile->out.base, new_size);
+      pfile->out.limit = pfile->out.base + new_size;
+      pfile->out.cur = pfile->out.base + size;
+    }
+}
+
+/* Skip a C-style block comment in a macro as a result of -CC.
+   Buffer->cur points to the initial asterisk of the comment.  */
+static void
+skip_macro_block_comment (cpp_reader *pfile)
+{
+  const uchar *cur = pfile->buffer->cur;
+
+  cur++;
+  if (*cur == '/')
+    cur++;
+
+  /* People like decorating comments with '*', so check for '/'
+     instead for efficiency.  */
+  while(! (*cur++ == '/' && cur[-2] == '*') )
+    ;
+
+  pfile->buffer->cur = cur;
+}
+
+/* CUR points to the asterisk introducing a comment in the current
+   context.  IN_DEFINE is true if we are in the replacement text of a
+   macro.
+
+   The asterisk and following comment is copied to the buffer pointed
+   to by pfile->out.cur, which must be of sufficient size.
+   Unterminated comments are diagnosed, and correctly terminated in
+   the output.  pfile->out.cur is updated depending upon IN_DEFINE,
+   -C, -CC and pfile->state.in_directive.
+
+   Returns a pointer to the first character after the comment in the
+   input buffer.  */
+static const uchar *
+copy_comment (cpp_reader *pfile, const uchar *cur, int in_define)
+{
+  bool unterminated, copy = false;
+  source_location src_loc = pfile->line_table->highest_line;
+  cpp_buffer *buffer = pfile->buffer;
+
+  buffer->cur = cur;
+  if (pfile->context->prev)
+    unterminated = false, skip_macro_block_comment (pfile);
+  else
+    unterminated = _cpp_skip_block_comment (pfile);
+    
+  if (unterminated)
+    cpp_error_with_line (pfile, CPP_DL_ERROR, src_loc, 0,
+			 "unterminated comment");
+
+  /* Comments in directives become spaces so that tokens are properly
+     separated when the ISO preprocessor re-lexes the line.  The
+     exception is #define.  */
+  if (pfile->state.in_directive)
+    {
+      if (in_define)
+	{
+	  if (CPP_OPTION (pfile, discard_comments_in_macro_exp))
+	    pfile->out.cur--;
+	  else
+	    copy = true;
+	}
+      else
+	pfile->out.cur[-1] = ' ';
+    }
+  else if (CPP_OPTION (pfile, discard_comments))
+    pfile->out.cur--;
+  else
+    copy = true;
+
+  if (copy)
+    {
+      size_t len = (size_t) (buffer->cur - cur);
+      memcpy (pfile->out.cur, cur, len);
+      pfile->out.cur += len;
+      if (unterminated)
+	{
+	  *pfile->out.cur++ = '*';
+	  *pfile->out.cur++ = '/';
+	}
+    }
+
+  return buffer->cur;
+}
+
+/* CUR points to any character in the input buffer.  Skips over all
+   contiguous horizontal white space and NULs, including comments if
+   SKIP_COMMENTS, until reaching the first non-horizontal-whitespace
+   character or the end of the current context.  Escaped newlines are
+   removed.
+
+   The whitespace is copied verbatim to the output buffer, except that
+   comments are handled as described in copy_comment().
+   pfile->out.cur is updated.
+
+   Returns a pointer to the first character after the whitespace in
+   the input buffer.  */
+static const uchar *
+skip_whitespace (cpp_reader *pfile, const uchar *cur, int skip_comments)
+{
+  uchar *out = pfile->out.cur;
+
+  for (;;)
+    {
+      unsigned int c = *cur++;
+      *out++ = c;
+
+      if (is_nvspace (c))
+	continue;
+
+      if (c == '/' && *cur == '*' && skip_comments)
+	{
+	  pfile->out.cur = out;
+	  cur = copy_comment (pfile, cur, false /* in_define */);
+	  out = pfile->out.cur;
+	  continue;
+	}
+
+      out--;
+      break;
+    }
+
+  pfile->out.cur = out;
+  return cur - 1;
+}
+
+/* Lexes and outputs an identifier starting at CUR, which is assumed
+   to point to a valid first character of an identifier.  Returns
+   the hashnode, and updates out.cur.  */
+static cpp_hashnode *
+lex_identifier (cpp_reader *pfile, const uchar *cur)
+{
+  size_t len;
+  uchar *out = pfile->out.cur;
+  cpp_hashnode *result;
+
+  do
+    *out++ = *cur++;
+  while (is_numchar (*cur));
+
+  CUR (pfile->context) = cur;
+  len = out - pfile->out.cur;
+  result = CPP_HASHNODE (ht_lookup (pfile->hash_table, pfile->out.cur,
+				    len, HT_ALLOC));
+  pfile->out.cur = out;
+  return result;
+}
+
+/* Overlays the true file buffer temporarily with text of length LEN
+   starting at START.  The true buffer is restored upon calling
+   restore_buff().  */
+void
+_cpp_overlay_buffer (cpp_reader *pfile, const uchar *start, size_t len)
+{
+  cpp_buffer *buffer = pfile->buffer;
+
+  pfile->overlaid_buffer = buffer;
+  pfile->saved_cur = buffer->cur;
+  pfile->saved_rlimit = buffer->rlimit;
+  pfile->saved_line_base = buffer->next_line;
+  buffer->need_line = false;
+
+  buffer->cur = start;
+  buffer->line_base = start;
+  buffer->rlimit = start + len;
+}
+
+/* Restores a buffer overlaid by _cpp_overlay_buffer().  */
+void
+_cpp_remove_overlay (cpp_reader *pfile)
+{
+  cpp_buffer *buffer = pfile->overlaid_buffer;
+
+  buffer->cur = pfile->saved_cur;
+  buffer->rlimit = pfile->saved_rlimit;
+  buffer->line_base = pfile->saved_line_base;
+  buffer->need_line = true;
+
+  pfile->overlaid_buffer = NULL;
+}
+
+/* Reads a logical line into the output buffer.  Returns TRUE if there
+   is more text left in the buffer.  */
+bool
+_cpp_read_logical_line_trad (cpp_reader *pfile)
+{
+  do
+    {
+      if (pfile->buffer->need_line && !_cpp_get_fresh_line (pfile))
+	return false;
+    }
+  while (!_cpp_scan_out_logical_line (pfile, NULL) || pfile->state.skipping);
+
+  return pfile->buffer != NULL;
+}
+
+/* Set up state for finding the opening '(' of a function-like
+   macro.  */
+static void
+maybe_start_funlike (cpp_reader *pfile, cpp_hashnode *node, const uchar *start, struct fun_macro *macro)
+{
+  unsigned int n = node->value.macro->paramc + 1;
+
+  if (macro->buff)
+    _cpp_release_buff (pfile, macro->buff);
+  macro->buff = _cpp_get_buff (pfile, n * sizeof (size_t));
+  macro->args = (size_t *) BUFF_FRONT (macro->buff);
+  macro->node = node;
+  macro->offset = start - pfile->out.base;
+  macro->argc = 0;
+}
+
+/* Save the OFFSET of the start of the next argument to MACRO.  */
+static void
+save_argument (struct fun_macro *macro, size_t offset)
+{
+  macro->argc++;
+  if (macro->argc <= macro->node->value.macro->paramc)
+    macro->args[macro->argc] = offset;
+}
+
+/* Copies the next logical line in the current buffer (starting at
+   buffer->cur) to the output buffer.  The output is guaranteed to
+   terminate with a NUL character.  buffer->cur is updated.
+
+   If MACRO is non-NULL, then we are scanning the replacement list of
+   MACRO, and we call save_replacement_text() every time we meet an
+   argument.  */
+bool
+_cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro)
+{
+  bool result = true;
+  cpp_context *context;
+  const uchar *cur;
+  uchar *out;
+  struct fun_macro fmacro;
+  unsigned int c, paren_depth = 0, quote;
+  enum ls lex_state = ls_none;
+  bool header_ok;
+  const uchar *start_of_input_line;
+
+  fmacro.buff = NULL;
+  fmacro.args = NULL;
+  fmacro.node = NULL;
+  fmacro.offset = 0;
+  fmacro.line = 0;
+  fmacro.argc = 0;
+
+  quote = 0;
+  header_ok = pfile->state.angled_headers;
+  CUR (pfile->context) = pfile->buffer->cur;
+  RLIMIT (pfile->context) = pfile->buffer->rlimit;
+  pfile->out.cur = pfile->out.base;
+  pfile->out.first_line = pfile->line_table->highest_line;
+  /* start_of_input_line is needed to make sure that directives really,
+     really start at the first character of the line.  */
+  start_of_input_line = pfile->buffer->cur;
+ new_context:
+  context = pfile->context;
+  cur = CUR (context);
+  check_output_buffer (pfile, RLIMIT (context) - cur);
+  out = pfile->out.cur;
+
+  for (;;)
+    {
+      if (!context->prev
+	  && cur >= pfile->buffer->notes[pfile->buffer->cur_note].pos)
+	{
+	  pfile->buffer->cur = cur;
+	  _cpp_process_line_notes (pfile, false);
+	}
+      c = *cur++;
+      *out++ = c;
+
+      /* Whitespace should "continue" out of the switch,
+	 non-whitespace should "break" out of it.  */
+      switch (c)
+	{
+	case ' ':
+	case '\t':
+	case '\f':
+	case '\v':
+	case '\0':
+	  continue;
+
+	case '\n':
+	  /* If this is a macro's expansion, pop it.  */
+	  if (context->prev)
+	    {
+	      pfile->out.cur = out - 1;
+	      _cpp_pop_context (pfile);
+	      goto new_context;
+	    }
+
+	  /* Omit the newline from the output buffer.  */
+	  pfile->out.cur = out - 1;
+	  pfile->buffer->cur = cur;
+	  pfile->buffer->need_line = true;
+	  CPP_INCREMENT_LINE (pfile, 0);
+
+	  if ((lex_state == ls_fun_open || lex_state == ls_fun_close)
+	      && !pfile->state.in_directive
+	      && _cpp_get_fresh_line (pfile))
+	    {
+	      /* Newlines in arguments become a space, but we don't
+		 clear any in-progress quote.  */
+	      if (lex_state == ls_fun_close)
+		out[-1] = ' ';
+	      cur = pfile->buffer->cur;
+	      continue;
+	    }
+	  goto done;
+
+	case '<':
+	  if (header_ok)
+	    quote = '>';
+	  break;
+	case '>':
+	  if (c == quote)
+	    quote = 0;
+	  break;
+
+	case '"':
+	case '\'':
+	  if (c == quote)
+	    quote = 0;
+	  else if (!quote)
+	    quote = c;
+	  break;
+
+	case '\\':
+	  /* Skip escaped quotes here, it's easier than above.  */
+	  if (*cur == '\\' || *cur == '"' || *cur == '\'')
+	    *out++ = *cur++;
+	  break;
+
+	case '/':
+	  /* Traditional CPP does not recognize comments within
+	     literals.  */
+	  if (!quote && *cur == '*')
+	    {
+	      pfile->out.cur = out;
+	      cur = copy_comment (pfile, cur, macro != 0);
+	      out = pfile->out.cur;
+	      continue;
+	    }
+	  break;
+
+	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':
+	  if (!pfile->state.skipping && (quote == 0 || macro))
+	    {
+	      cpp_hashnode *node;
+	      uchar *out_start = out - 1;
+
+	      pfile->out.cur = out_start;
+	      node = lex_identifier (pfile, cur - 1);
+	      out = pfile->out.cur;
+	      cur = CUR (context);
+
+	      if (node->type == NT_MACRO
+		  /* Should we expand for ls_answer?  */
+		  && (lex_state == ls_none || lex_state == ls_fun_open)
+		  && !pfile->state.prevent_expansion)
+		{
+		  /* Macros invalidate MI optimization.  */
+		  pfile->mi_valid = false;
+		  if (! (node->flags & NODE_BUILTIN)
+		      && node->value.macro->fun_like)
+		    {
+		      maybe_start_funlike (pfile, node, out_start, &fmacro);
+		      lex_state = ls_fun_open;
+		      fmacro.line = pfile->line_table->highest_line;
+		      continue;
+		    }
+		  else if (!recursive_macro (pfile, node))
+		    {
+		      /* Remove the object-like macro's name from the
+			 output, and push its replacement text.  */
+		      pfile->out.cur = out_start;
+		      push_replacement_text (pfile, node);
+		      lex_state = ls_none;
+		      goto new_context;
+		    }
+		}
+	      else if (macro && (node->flags & NODE_MACRO_ARG) != 0)
+		{
+		  /* Found a parameter in the replacement text of a
+		     #define.  Remove its name from the output.  */
+		  pfile->out.cur = out_start;
+		  save_replacement_text (pfile, macro, node->value.arg_index);
+		  out = pfile->out.base;
+		}
+	      else if (lex_state == ls_hash)
+		{
+		  lex_state = ls_predicate;
+		  continue;
+		}
+	      else if (pfile->state.in_expression
+		       && node == pfile->spec_nodes.n_defined)
+		{
+		  lex_state = ls_defined;
+		  continue;
+		}
+	    }
+	  break;
+
+	case '(':
+	  if (quote == 0)
+	    {
+	      paren_depth++;
+	      if (lex_state == ls_fun_open)
+		{
+		  if (recursive_macro (pfile, fmacro.node))
+		    lex_state = ls_none;
+		  else
+		    {
+		      lex_state = ls_fun_close;
+		      paren_depth = 1;
+		      out = pfile->out.base + fmacro.offset;
+		      fmacro.args[0] = fmacro.offset;
+		    }
+		}
+	      else if (lex_state == ls_predicate)
+		lex_state = ls_answer;
+	      else if (lex_state == ls_defined)
+		lex_state = ls_defined_close;
+	    }
+	  break;
+
+	case ',':
+	  if (quote == 0 && lex_state == ls_fun_close && paren_depth == 1)
+	    save_argument (&fmacro, out - pfile->out.base);
+	  break;
+
+	case ')':
+	  if (quote == 0)
+	    {
+	      paren_depth--;
+	      if (lex_state == ls_fun_close && paren_depth == 0)
+		{
+		  cpp_macro *m = fmacro.node->value.macro;
+
+		  m->used = 1;
+		  lex_state = ls_none;
+		  save_argument (&fmacro, out - pfile->out.base);
+
+		  /* A single zero-length argument is no argument.  */
+		  if (fmacro.argc == 1
+		      && m->paramc == 0
+		      && out == pfile->out.base + fmacro.offset + 1)
+		    fmacro.argc = 0;
+
+		  if (_cpp_arguments_ok (pfile, m, fmacro.node, fmacro.argc))
+		    {
+		      /* Remove the macro's invocation from the
+			 output, and push its replacement text.  */
+		      pfile->out.cur = (pfile->out.base
+					     + fmacro.offset);
+		      CUR (context) = cur;
+		      replace_args_and_push (pfile, &fmacro);
+		      goto new_context;
+		    }
+		}
+	      else if (lex_state == ls_answer || lex_state == ls_defined_close)
+		lex_state = ls_none;
+	    }
+	  break;
+
+	case '#':
+	  if (cur - 1 == start_of_input_line
+	      /* A '#' from a macro doesn't start a directive.  */
+	      && !pfile->context->prev
+	      && !pfile->state.in_directive)
+	    {
+	      /* A directive.  With the way _cpp_handle_directive
+		 currently works, we only want to call it if either we
+		 know the directive is OK, or we want it to fail and
+		 be removed from the output.  If we want it to be
+		 passed through (the assembler case) then we must not
+		 call _cpp_handle_directive.  */
+	      pfile->out.cur = out;
+	      cur = skip_whitespace (pfile, cur, true /* skip_comments */);
+	      out = pfile->out.cur;
+
+	      if (*cur == '\n')
+		{
+		  /* Null directive.  Ignore it and don't invalidate
+		     the MI optimization.  */
+		  pfile->buffer->need_line = true;
+		  CPP_INCREMENT_LINE (pfile, 0);
+		  result = false;
+		  goto done;
+		}
+	      else
+		{
+		  bool do_it = false;
+
+		  if (is_numstart (*cur)
+		      && CPP_OPTION (pfile, lang) != CLK_ASM)
+		    do_it = true;
+		  else if (is_idstart (*cur))
+		    /* Check whether we know this directive, but don't
+		       advance.  */
+		    do_it = lex_identifier (pfile, cur)->is_directive;
+
+		  if (do_it || CPP_OPTION (pfile, lang) != CLK_ASM)
+		    {
+		      /* This is a kludge.  We want to have the ISO
+			 preprocessor lex the next token.  */
+		      pfile->buffer->cur = cur;
+		      _cpp_handle_directive (pfile, false /* indented */);
+		      result = false;
+		      goto done;
+		    }
+		}
+	    }
+
+	  if (pfile->state.in_expression)
+	    {
+	      lex_state = ls_hash;
+	      continue;
+	    }
+	  break;
+
+	default:
+	  break;
+	}
+
+      /* Non-whitespace disables MI optimization and stops treating
+	 '<' as a quote in #include.  */
+      header_ok = false;
+      if (!pfile->state.in_directive)
+	pfile->mi_valid = false;
+
+      if (lex_state == ls_none)
+	continue;
+
+      /* Some of these transitions of state are syntax errors.  The
+	 ISO preprocessor will issue errors later.  */
+      if (lex_state == ls_fun_open)
+	/* Missing '('.  */
+	lex_state = ls_none;
+      else if (lex_state == ls_hash
+	       || lex_state == ls_predicate
+	       || lex_state == ls_defined)
+	lex_state = ls_none;
+
+      /* ls_answer and ls_defined_close keep going until ')'.  */
+    }
+
+ done:
+  if (fmacro.buff)
+    _cpp_release_buff (pfile, fmacro.buff);
+
+  if (lex_state == ls_fun_close)
+    cpp_error_with_line (pfile, CPP_DL_ERROR, fmacro.line, 0,
+			 "unterminated argument list invoking macro \"%s\"",
+			 NODE_NAME (fmacro.node));
+  return result;
+}
+
+/* Push a context holding the replacement text of the macro NODE on
+   the context stack.  NODE is either object-like, or a function-like
+   macro with no arguments.  */
+static void
+push_replacement_text (cpp_reader *pfile, cpp_hashnode *node)
+{
+  size_t len;
+  const uchar *text;
+  uchar *buf;
+
+  if (node->flags & NODE_BUILTIN)
+    {
+      text = _cpp_builtin_macro_text (pfile, node);
+      len = ustrlen (text);
+      buf = _cpp_unaligned_alloc (pfile, len + 1);
+      memcpy (buf, text, len);
+      buf[len]='\n';
+      text = buf;
+    }
+  else
+    {
+      cpp_macro *macro = node->value.macro;
+      macro->used = 1;
+      text = macro->exp.text;
+      macro->traditional = 1;
+      len = macro->count;
+    }
+
+  _cpp_push_text_context (pfile, node, text, len);
+}
+
+/* Returns TRUE if traditional macro recursion is detected.  */
+static bool
+recursive_macro (cpp_reader *pfile, cpp_hashnode *node)
+{
+  bool recursing = !!(node->flags & NODE_DISABLED);
+
+  /* Object-like macros that are already expanding are necessarily
+     recursive.
+
+     However, it is possible to have traditional function-like macros
+     that are not infinitely recursive but recurse to any given depth.
+     Further, it is easy to construct examples that get ever longer
+     until the point they stop recursing.  So there is no easy way to
+     detect true recursion; instead we assume any expansion more than
+     20 deep since the first invocation of this macro must be
+     recursing.  */
+  if (recursing && node->value.macro->fun_like)
+    {
+      size_t depth = 0;
+      cpp_context *context = pfile->context;
+
+      do
+	{
+	  depth++;
+	  if (context->macro == node && depth > 20)
+	    break;
+	  context = context->prev;
+	}
+      while (context);
+      recursing = context != NULL;
+    }
+
+  if (recursing)
+    cpp_error (pfile, CPP_DL_ERROR,
+	       "detected recursion whilst expanding macro \"%s\"",
+	       NODE_NAME (node));
+
+  return recursing;
+}
+
+/* Return the length of the replacement text of a function-like or
+   object-like non-builtin macro.  */
+size_t
+_cpp_replacement_text_len (const cpp_macro *macro)
+{
+  size_t len;
+
+  if (macro->fun_like && (macro->paramc != 0))
+    {
+      const uchar *exp;
+
+      len = 0;
+      for (exp = macro->exp.text;;)
+	{
+	  struct block *b = (struct block *) exp;
+
+	  len += b->text_len;
+	  if (b->arg_index == 0)
+	    break;
+	  len += NODE_LEN (macro->params[b->arg_index - 1]);
+	  exp += BLOCK_LEN (b->text_len);
+	}
+    }
+  else
+    len = macro->count;
+  
+  return len;
+}
+
+/* Copy the replacement text of MACRO to DEST, which must be of
+   sufficient size.  It is not NUL-terminated.  The next character is
+   returned.  */
+uchar *
+_cpp_copy_replacement_text (const cpp_macro *macro, uchar *dest)
+{
+  if (macro->fun_like && (macro->paramc != 0))
+    {
+      const uchar *exp;
+
+      for (exp = macro->exp.text;;)
+	{
+	  struct block *b = (struct block *) exp;
+	  cpp_hashnode *param;
+
+	  memcpy (dest, b->text, b->text_len);
+	  dest += b->text_len;
+	  if (b->arg_index == 0)
+	    break;
+	  param = macro->params[b->arg_index - 1];
+	  memcpy (dest, NODE_NAME (param), NODE_LEN (param));
+	  dest += NODE_LEN (param);
+	  exp += BLOCK_LEN (b->text_len);
+	}
+    }
+  else
+    {
+      memcpy (dest, macro->exp.text, macro->count);
+      dest += macro->count;
+    }
+
+  return dest;
+}
+
+/* Push a context holding the replacement text of the macro NODE on
+   the context stack.  NODE is either object-like, or a function-like
+   macro with no arguments.  */
+static void
+replace_args_and_push (cpp_reader *pfile, struct fun_macro *fmacro)
+{
+  cpp_macro *macro = fmacro->node->value.macro;
+
+  if (macro->paramc == 0)
+    push_replacement_text (pfile, fmacro->node);
+  else
+    {
+      const uchar *exp;
+      uchar *p;
+      _cpp_buff *buff;
+      size_t len = 0;
+      int cxtquote = 0;
+
+      /* Get an estimate of the length of the argument-replaced text.
+	 This is a worst case estimate, assuming that every replacement
+	 text character needs quoting.  */
+      for (exp = macro->exp.text;;)
+	{
+	  struct block *b = (struct block *) exp;
+
+	  len += b->text_len;
+	  if (b->arg_index == 0)
+	    break;
+	  len += 2 * (fmacro->args[b->arg_index]
+		      - fmacro->args[b->arg_index - 1] - 1);
+	  exp += BLOCK_LEN (b->text_len);
+	}
+
+      /* Allocate room for the expansion plus \n.  */
+      buff = _cpp_get_buff (pfile, len + 1);
+
+      /* Copy the expansion and replace arguments.  */
+      /* Accumulate actual length, including quoting as necessary */
+      p = BUFF_FRONT (buff);
+      len = 0;
+      for (exp = macro->exp.text;;)
+	{
+	  struct block *b = (struct block *) exp;
+	  size_t arglen;
+	  int argquote;
+	  uchar *base;
+	  uchar *in;
+
+	  len += b->text_len;
+	  /* Copy the non-argument text literally, keeping
+	     track of whether matching quotes have been seen. */
+	  for (arglen = b->text_len, in = b->text; arglen > 0; arglen--)
+	    {
+	      if (*in == '"')
+		cxtquote = ! cxtquote;
+	      *p++ = *in++;
+	    }
+	  /* Done if no more arguments */
+	  if (b->arg_index == 0)
+	    break;
+	  arglen = (fmacro->args[b->arg_index]
+		    - fmacro->args[b->arg_index - 1] - 1);
+	  base = pfile->out.base + fmacro->args[b->arg_index - 1];
+	  in = base;
+#if 0
+	  /* Skip leading whitespace in the text for the argument to
+	     be substituted. To be compatible with gcc 2.95, we would
+	     also need to trim trailing whitespace. Gcc 2.95 trims
+	     leading and trailing whitespace, which may be a bug.  The
+	     current gcc testsuite explicitly checks that this leading
+	     and trailing whitespace in actual arguments is
+	     preserved. */
+	  while (arglen > 0 && is_space (*in))
+	    {
+	      in++;
+	      arglen--;
+	    }
+#endif
+	  for (argquote = 0; arglen > 0; arglen--)
+	    {
+	      if (cxtquote && *in == '"')
+		{
+		  if (in > base && *(in-1) != '\\')
+		    argquote = ! argquote;
+		  /* Always add backslash before double quote if argument
+		     is expanded in a quoted context */
+		  *p++ = '\\';
+		  len++;
+		}
+	      else if (cxtquote && argquote && *in == '\\')
+		{
+		  /* Always add backslash before a backslash in an argument
+		     that is expanded in a quoted context and also in the
+		     range of a quoted context in the argument itself. */
+		  *p++ = '\\';
+		  len++;
+		}
+	      *p++ = *in++;
+	      len++;
+	    }
+	  exp += BLOCK_LEN (b->text_len);
+	}
+
+      /* \n-terminate.  */
+      *p = '\n';
+      _cpp_push_text_context (pfile, fmacro->node, BUFF_FRONT (buff), len);
+
+      /* So we free buffer allocation when macro is left.  */
+      pfile->context->buff = buff;
+    }
+}
+
+/* Read and record the parameters, if any, of a function-like macro
+   definition.  Destroys pfile->out.cur.
+
+   Returns true on success, false on failure (syntax error or a
+   duplicate parameter).  On success, CUR (pfile->context) is just
+   past the closing parenthesis.  */
+static bool
+scan_parameters (cpp_reader *pfile, cpp_macro *macro)
+{
+  const uchar *cur = CUR (pfile->context) + 1;
+  bool ok;
+
+  for (;;)
+    {
+      cur = skip_whitespace (pfile, cur, true /* skip_comments */);
+
+      if (is_idstart (*cur))
+	{
+	  ok = false;
+	  if (_cpp_save_parameter (pfile, macro, lex_identifier (pfile, cur)))
+	    break;
+	  cur = skip_whitespace (pfile, CUR (pfile->context),
+				 true /* skip_comments */);
+	  if (*cur == ',')
+	    {
+	      cur++;
+	      continue;
+	    }
+	  ok = (*cur == ')');
+	  break;
+	}
+
+      ok = (*cur == ')' && macro->paramc == 0);
+      break;
+    }
+
+  if (!ok)
+    cpp_error (pfile, CPP_DL_ERROR, "syntax error in macro parameter list");
+
+  CUR (pfile->context) = cur + (*cur == ')');
+
+  return ok;
+}
+
+/* Save the text from pfile->out.base to pfile->out.cur as
+   the replacement text for the current macro, followed by argument
+   ARG_INDEX, with zero indicating the end of the replacement
+   text.  */
+static void
+save_replacement_text (cpp_reader *pfile, cpp_macro *macro,
+		       unsigned int arg_index)
+{
+  size_t len = pfile->out.cur - pfile->out.base;
+  uchar *exp;
+
+  if (macro->paramc == 0)
+    {
+      /* Object-like and function-like macros without parameters
+	 simply store their \n-terminated replacement text.  */
+      exp = _cpp_unaligned_alloc (pfile, len + 1);
+      memcpy (exp, pfile->out.base, len);
+      exp[len] = '\n';
+      macro->exp.text = exp;
+      macro->traditional = 1;
+      macro->count = len;
+    }
+  else
+    {
+      /* Store the text's length (unsigned int), the argument index
+	 (unsigned short, base 1) and then the text.  */
+      size_t blen = BLOCK_LEN (len);
+      struct block *block;
+
+      if (macro->count + blen > BUFF_ROOM (pfile->a_buff))
+	_cpp_extend_buff (pfile, &pfile->a_buff, macro->count + blen);
+
+      exp = BUFF_FRONT (pfile->a_buff);
+      block = (struct block *) (exp + macro->count);
+      macro->exp.text = exp;
+      macro->traditional = 1;
+
+      /* Write out the block information.  */
+      block->text_len = len;
+      block->arg_index = arg_index;
+      memcpy (block->text, pfile->out.base, len);
+
+      /* Lex the rest into the start of the output buffer.  */
+      pfile->out.cur = pfile->out.base;
+
+      macro->count += blen;
+
+      /* If we've finished, commit the memory.  */
+      if (arg_index == 0)
+	BUFF_FRONT (pfile->a_buff) += macro->count;
+    }
+}
+
+/* Analyze and save the replacement text of a macro.  Returns true on
+   success.  */
+bool
+_cpp_create_trad_definition (cpp_reader *pfile, cpp_macro *macro)
+{
+  const uchar *cur;
+  uchar *limit;
+  cpp_context *context = pfile->context;
+
+  /* The context has not been set up for command line defines, and CUR
+     has not been updated for the macro name for in-file defines.  */
+  pfile->out.cur = pfile->out.base;
+  CUR (context) = pfile->buffer->cur;
+  RLIMIT (context) = pfile->buffer->rlimit;
+  check_output_buffer (pfile, RLIMIT (context) - CUR (context));
+
+  /* Is this a function-like macro?  */
+  if (* CUR (context) == '(')
+    {
+      bool ok = scan_parameters (pfile, macro);
+
+      /* Remember the params so we can clear NODE_MACRO_ARG flags.  */
+      macro->params = (cpp_hashnode **) BUFF_FRONT (pfile->a_buff);
+
+      /* Setting macro to NULL indicates an error occurred, and
+	 prevents unnecessary work in _cpp_scan_out_logical_line.  */
+      if (!ok)
+	macro = NULL;
+      else
+	{
+	  BUFF_FRONT (pfile->a_buff) = (uchar *) &macro->params[macro->paramc];
+	  macro->fun_like = 1;
+	}
+    }
+
+  /* Skip leading whitespace in the replacement text.  */
+  pfile->buffer->cur
+    = skip_whitespace (pfile, CUR (context),
+		       CPP_OPTION (pfile, discard_comments_in_macro_exp));
+
+  pfile->state.prevent_expansion++;
+  _cpp_scan_out_logical_line (pfile, macro);
+  pfile->state.prevent_expansion--;
+
+  if (!macro)
+    return false;
+
+  /* Skip trailing white space.  */
+  cur = pfile->out.base;
+  limit = pfile->out.cur;
+  while (limit > cur && is_space (limit[-1]))
+    limit--;
+  pfile->out.cur = limit;
+  save_replacement_text (pfile, macro, 0);
+
+  return true;
+}
+
+/* Copy SRC of length LEN to DEST, but convert all contiguous
+   whitespace to a single space, provided it is not in quotes.  The
+   quote currently in effect is pointed to by PQUOTE, and is updated
+   by the function.  Returns the number of bytes copied.  */
+static size_t
+canonicalize_text (uchar *dest, const uchar *src, size_t len, uchar *pquote)
+{
+  uchar *orig_dest = dest;
+  uchar quote = *pquote;
+
+  while (len)
+    {
+      if (is_space (*src) && !quote)
+	{
+	  do
+	    src++, len--;
+	  while (len && is_space (*src));
+	  *dest++ = ' ';
+	}
+      else
+	{
+	  if (*src == '\'' || *src == '"')
+	    {
+	      if (!quote)
+		quote = *src;
+	      else if (quote == *src)
+		quote = 0;
+	    }
+	  *dest++ = *src++, len--;
+	}
+    }
+
+  *pquote = quote;
+  return dest - orig_dest;
+}
+
+/* Returns true if MACRO1 and MACRO2 have expansions different other
+   than in the form of their whitespace.  */
+bool
+_cpp_expansions_different_trad (const cpp_macro *macro1,
+				const cpp_macro *macro2)
+{
+  uchar *p1 = XNEWVEC (uchar, macro1->count + macro2->count);
+  uchar *p2 = p1 + macro1->count;
+  uchar quote1 = 0, quote2 = 0;
+  bool mismatch;
+  size_t len1, len2;
+
+  if (macro1->paramc > 0)
+    {
+      const uchar *exp1 = macro1->exp.text, *exp2 = macro2->exp.text;
+
+      mismatch = true;
+      for (;;)
+	{
+	  struct block *b1 = (struct block *) exp1;
+	  struct block *b2 = (struct block *) exp2;
+
+	  if (b1->arg_index != b2->arg_index)
+	    break;
+
+	  len1 = canonicalize_text (p1, b1->text, b1->text_len, &quote1);
+	  len2 = canonicalize_text (p2, b2->text, b2->text_len, &quote2);
+	  if (len1 != len2 || memcmp (p1, p2, len1))
+	    break;
+	  if (b1->arg_index == 0)
+	    {
+	      mismatch = false;
+	      break;
+	    }
+	  exp1 += BLOCK_LEN (b1->text_len);
+	  exp2 += BLOCK_LEN (b2->text_len);
+	}
+    }
+  else
+    {
+      len1 = canonicalize_text (p1, macro1->exp.text, macro1->count, &quote1);
+      len2 = canonicalize_text (p2, macro2->exp.text, macro2->count, &quote2);
+      mismatch = (len1 != len2 || memcmp (p1, p2, len1));
+    }
+
+  free (p1);
+  return mismatch;
+}