cpphash.h (struct cpp_reader): Make date and time strings.
authorNeil Booth <neil@daikokuya.co.uk>
Wed, 19 Jun 2002 05:40:08 +0000 (05:40 +0000)
committerNeil Booth <neil@gcc.gnu.org>
Wed, 19 Jun 2002 05:40:08 +0000 (05:40 +0000)
* cpphash.h (struct cpp_reader): Make date and time strings.
(_cpp_builtin_macro_text, _cpp_copy_replacement_text,
_cpp_replacement_text_len): New.
* cppinit.c (cpp_create_reader): Update.
(init_builtins): Register appropriate builtins for -traditional-cpp.
* cppmacro.c (new_number_token): Remove.
(_cpp_builtin_macro_text): New.
(builtin_macro): Use it.
(cpp_macro_definition): Update to handle traditional macros.
* cppmain.c (cb_line_change): Don't do column positioning for
traditional output.
* cpptrad.c (enum ls): Rename ls_fun_macro to ls_fun_open.  New
state ls_fun_close.
(skip_whitespace): Fix.
(maybe_start_funlike): Don't set state.parsing_args.
(scan_out_logical_line): Remove duplicate error.  Use lex_state
rather than state.parsing_args.
(push_replacement_text): Handle builtins.
(_cpp_replacement_text_len, _cpp_copy_replacement_text): New.

From-SVN: r54771

gcc/ChangeLog
gcc/cpphash.h
gcc/cppinit.c
gcc/cppmacro.c
gcc/cppmain.c
gcc/cpptrad.c

index 79fec5bfd3b2a2221b3f9a2f9f03772678ef27e0..327cc7c26eb2dd511f17ab5607ba0550873d3615 100644 (file)
@@ -1,3 +1,25 @@
+2002-06-19  Neil Booth  <neil@daikokuya.co.uk>
+
+       * cpphash.h (struct cpp_reader): Make date and time strings.
+       (_cpp_builtin_macro_text, _cpp_copy_replacement_text,
+       _cpp_replacement_text_len): New.
+       * cppinit.c (cpp_create_reader): Update.
+       (init_builtins): Register appropriate builtins for -traditional-cpp.
+       * cppmacro.c (new_number_token): Remove.
+       (_cpp_builtin_macro_text): New.
+       (builtin_macro): Use it.
+       (cpp_macro_definition): Update to handle traditional macros.
+       * cppmain.c (cb_line_change): Don't do column positioning for
+       traditional output.
+       * cpptrad.c (enum ls): Rename ls_fun_macro to ls_fun_open.  New
+       state ls_fun_close.
+       (skip_whitespace): Fix.
+       (maybe_start_funlike): Don't set state.parsing_args.
+       (scan_out_logical_line): Remove duplicate error.  Use lex_state
+       rather than state.parsing_args.
+       (push_replacement_text): Handle builtins.
+       (_cpp_replacement_text_len, _cpp_copy_replacement_text): New.
+
 2002-06-18  Hans-Peter Nilsson  <hp@axis.com>
             Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
 
index 98dfb49ca7c377c89e71008d9b5abcec95622862..ee9a47effc4ed6df40de180f4ebfd8c491625752 100644 (file)
@@ -370,9 +370,9 @@ struct cpp_reader
      for include files.  (Altered as we get more of them.)  */
   unsigned int max_include_len;
 
-  /* Date and time tokens.  Calculated together if either is requested.  */
-  cpp_token date;
-  cpp_token time;
+  /* Date and time text.  Calculated together if either is requested.  */
+  const uchar *date;
+  const uchar *time;
 
   /* EOF token, and a token forcing paste avoidance.  */
   cpp_token avoid_paste;
@@ -475,7 +475,8 @@ extern bool _cpp_save_parameter             PARAMS ((cpp_reader *, cpp_macro *,
 extern bool _cpp_arguments_ok          PARAMS ((cpp_reader *, cpp_macro *,
                                                 const cpp_hashnode *,
                                                 unsigned int));
-
+extern const uchar *_cpp_builtin_macro_text PARAMS ((cpp_reader *,
+                                                    cpp_hashnode *));
 /* In cpphash.c */
 extern void _cpp_init_hashtable                PARAMS ((cpp_reader *, hash_table *));
 extern void _cpp_destroy_hashtable     PARAMS ((cpp_reader *));
@@ -532,6 +533,8 @@ extern void _cpp_set_trad_context PARAMS ((cpp_reader *));
 extern bool _cpp_create_trad_definition PARAMS ((cpp_reader *, cpp_macro *));
 extern bool _cpp_expansions_different_trad PARAMS ((const cpp_macro *,
                                                    const cpp_macro *));
+extern uchar *_cpp_copy_replacement_text PARAMS ((const cpp_macro *, uchar *));
+extern size_t _cpp_replacement_text_len PARAMS ((const cpp_macro *));
 
 /* Utility routines and macros.  */
 #define DSC(str) (const uchar *)str, sizeof str - 1
index 411dbea1d138573d19f8d07201613ad320df4cde..1df4bacb12bcd654d0df9b81fe9b23b863e007b0 100644 (file)
@@ -512,7 +512,6 @@ cpp_create_reader (lang)
   pfile->state.save_comments = ! CPP_OPTION (pfile, discard_comments);
 
   /* Set up static tokens.  */
-  pfile->date.type = CPP_EOF;
   pfile->avoid_paste.type = CPP_PADDING;
   pfile->avoid_paste.val.source = NULL;
   pfile->eof.type = CPP_EOF;
@@ -640,6 +639,8 @@ static const struct builtin builtin_array[] =
   B("__BASE_FILE__",    BT_BASE_FILE),
   B("__LINE__",                 BT_SPECLINE),
   B("__INCLUDE_LEVEL__", BT_INCLUDE_LEVEL),
+  /* Keep builtins not used for -traditional-cpp at the end, and
+     update init_builtins() if any more are added.  */
   B("_Pragma",          BT_PRAGMA),
   B("__STDC__",                 BT_STDC),
 };
@@ -684,10 +685,12 @@ init_builtins (pfile)
      cpp_reader *pfile;
 {
   const struct builtin *b;
+  size_t n = ARRAY_SIZE (builtin_array);
 
-  for(b = builtin_array;
-      b < (builtin_array + ARRAY_SIZE (builtin_array));
-      b++)
+  if (CPP_OPTION (pfile, traditional))
+    n -= 2;
+
+  for(b = builtin_array; b < builtin_array + n; b++)
     {
       cpp_hashnode *hp = cpp_lookup (pfile, b->name, b->len);
       hp->type = NT_MACRO;
index 20149ec9b33bb0e512b6da0a91cf7a151081e627..99cdc19e0073d54cd38aafba906a742c31ebe2f1 100644 (file)
@@ -54,7 +54,6 @@ static const cpp_token *padding_token
 static void expand_arg PARAMS ((cpp_reader *, macro_arg *));
 static const cpp_token *new_string_token PARAMS ((cpp_reader *, uchar *,
                                                  unsigned int));
-static const cpp_token *new_number_token PARAMS ((cpp_reader *, unsigned int));
 static const cpp_token *stringify_arg PARAMS ((cpp_reader *, macro_arg *));
 static void paste_all_tokens PARAMS ((cpp_reader *, const cpp_token *));
 static bool paste_tokens PARAMS ((cpp_reader *, const cpp_token **,
@@ -93,24 +92,6 @@ new_string_token (pfile, text, len)
   return token;
 }
 
-/* Allocates and returns a CPP_NUMBER token evaluating to NUMBER.  */
-static const cpp_token *
-new_number_token (pfile, number)
-     cpp_reader *pfile;
-     unsigned int number;
-{
-  cpp_token *token = _cpp_temp_token (pfile);
-  /* 21 bytes holds all NUL-terminated unsigned 64-bit numbers.  */
-  unsigned char *buf = _cpp_unaligned_alloc (pfile, 21);
-
-  sprintf ((char *) buf, "%u", number);
-  token->type = CPP_NUMBER;
-  token->val.str.text = buf;
-  token->val.str.len = ustrlen (buf);
-  token->flags = 0;
-  return token;
-}
-
 static const char * const monthnames[] =
 {
   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -121,19 +102,20 @@ static const char * const monthnames[] =
    on the context stack.  Also handles _Pragma, for which no new token
    is created.  Returns 1 if it generates a new token context, 0 to
    return the token to the caller.  */
-static int
-builtin_macro (pfile, node)
+const uchar *
+_cpp_builtin_macro_text (pfile, node)
      cpp_reader *pfile;
      cpp_hashnode *node;
 {
-  const cpp_token *result;
+  const uchar *result = NULL;
+  unsigned int number = 1;
 
   switch (node->value.builtin)
     {
     default:
       cpp_error (pfile, DL_ICE, "invalid built-in macro \"%s\"",
                 NODE_NAME (node));
-      return 0;
+      break;
 
     case BT_FILE:
     case BT_BASE_FILE:
@@ -149,10 +131,12 @@ builtin_macro (pfile, node)
 
        name = map->to_file;
        len = strlen (name);
-       buf = _cpp_unaligned_alloc (pfile, len * 4 + 1);
-       len = cpp_quote_string (buf, (const unsigned char *) name, len) - buf;
-
-       result = new_string_token (pfile, buf, len);
+       buf = _cpp_unaligned_alloc (pfile, len * 4 + 3);
+       result = buf;
+       *buf = '"';
+       buf = cpp_quote_string (buf + 1, (const unsigned char *) name, len);
+       *buf++ = '"';
+       *buf = '\0';
       }
       break;
 
@@ -160,16 +144,18 @@ builtin_macro (pfile, node)
       /* The line map depth counts the primary source as level 1, but
         historically __INCLUDE_DEPTH__ has called the primary source
         level 0.  */
-      result = new_number_token (pfile, pfile->line_maps.depth - 1);
+      number = pfile->line_maps.depth - 1;
       break;
 
     case BT_SPECLINE:
       /* If __LINE__ is embedded in a macro, it must expand to the
         line of the macro's invocation, not its definition.
         Otherwise things like assert() will not work properly.  */
-      result = new_number_token (pfile,
-                                SOURCE_LINE (pfile->map,
-                                             pfile->cur_token[-1].line));
+      if (CPP_OPTION (pfile, traditional))
+       number = pfile->line;
+      else
+       number = pfile->cur_token[-1].line;
+      number = SOURCE_LINE (pfile->map, number);
       break;
 
       /* __STDC__ has the value 1 under normal circumstances.
@@ -179,23 +165,20 @@ builtin_macro (pfile, node)
         value 0.  */
     case BT_STDC:
       {
-       int stdc;
        enum c_lang lang = CPP_OPTION (pfile, lang);
        if (CPP_IN_SYSTEM_HEADER (pfile)
            && CPP_OPTION (pfile, stdc_0_in_system_headers)
            && !(lang == CLK_STDC89 || lang == CLK_STDC94
                 || lang == CLK_STDC99))  /* || lang == CLK_CXX98 ? */
-         stdc = 0;
+         number = 0;
        else
-         stdc = 1;
-
-       result = new_number_token (pfile, stdc);
+         number = 1;
       }
       break;
 
     case BT_DATE:
     case BT_TIME:
-      if (pfile->date.type == CPP_EOF)
+      if (pfile->date == NULL)
        {
          /* Allocate __DATE__ and __TIME__ strings from permanent
             storage.  We only do this once, and don't generate them
@@ -204,30 +187,46 @@ builtin_macro (pfile, node)
          time_t tt = time (NULL);
          struct tm *tb = localtime (&tt);
 
-         pfile->date.val.str.text =
-           _cpp_unaligned_alloc (pfile, sizeof ("Oct 11 1347"));
-         pfile->date.val.str.len = sizeof ("Oct 11 1347") - 1;
-         pfile->date.type = CPP_STRING;
-         pfile->date.flags = 0;
-         sprintf ((char *) pfile->date.val.str.text, "%s %2d %4d",
+         pfile->date = _cpp_unaligned_alloc (pfile,
+                                             sizeof ("\"Oct 11 1347\""));
+         sprintf ((char *) pfile->date, "\"%s %2d %4d\"",
                   monthnames[tb->tm_mon], tb->tm_mday, tb->tm_year + 1900);
 
-         pfile->time.val.str.text =
-           _cpp_unaligned_alloc (pfile, sizeof ("12:34:56"));
-         pfile->time.val.str.len = sizeof ("12:34:56") - 1;
-         pfile->time.type = CPP_STRING;
-         pfile->time.flags = 0;
-         sprintf ((char *) pfile->time.val.str.text, "%02d:%02d:%02d",
+         pfile->time = _cpp_unaligned_alloc (pfile, sizeof ("\"12:34:56\""));
+         sprintf ((char *) pfile->time, "\"%02d:%02d:%02d\"",
                   tb->tm_hour, tb->tm_min, tb->tm_sec);
        }
 
       if (node->value.builtin == BT_DATE)
-       result = &pfile->date;
+       result = pfile->date;
       else
-       result = &pfile->time;
+       result = pfile->time;
       break;
+    }
+
+  if (result == NULL)
+    {
+      /* 21 bytes holds all NUL-terminated unsigned 64-bit numbers.  */
+      result = _cpp_unaligned_alloc (pfile, 21);
+      sprintf ((char *) result, "%u", number);
+    }
+
+  return result;      
+}
+
+/* Convert builtin macros like __FILE__ to a token and push it on the
+   context stack.  Also handles _Pragma, for which no new token is
+   created.  Returns 1 if it generates a new token context, 0 to
+   return the token to the caller.  */
+static int
+builtin_macro (pfile, node)
+     cpp_reader *pfile;
+     cpp_hashnode *node;
+{
+  const uchar *buf;
 
-    case BT_PRAGMA:
+  if (node->value.builtin == BT_PRAGMA)
+    {
       /* Don't interpret _Pragma within directives.  The standard is
          not clear on this, but to me this makes most sense.  */
       if (pfile->state.in_directive)
@@ -237,7 +236,24 @@ builtin_macro (pfile, node)
       return 1;
     }
 
-  push_token_context (pfile, NULL, result, 1);
+  buf = _cpp_builtin_macro_text (pfile, node);
+
+  cpp_push_buffer (pfile, buf, ustrlen (buf), /* from_stage3 */ true, 1);
+
+  /* Tweak the column number the lexer will report.  */
+  pfile->buffer->col_adjust = pfile->cur_token[-1].col - 1;
+
+  /* We don't want a leading # to be interpreted as a directive.  */
+  pfile->buffer->saved_flags = 0;
+
+  /* Set pfile->cur_token as required by _cpp_lex_direct.  */
+  pfile->cur_token = _cpp_temp_token (pfile);
+  push_token_context (pfile, NULL, _cpp_lex_direct (pfile), 1);
+  if (pfile->buffer->cur != pfile->buffer->rlimit)
+    cpp_error (pfile, DL_ICE, "invalid built-in macro \"%s\"",
+              NODE_NAME (node));
+  _cpp_pop_buffer (pfile);
+
   return 1;
 }
 
@@ -1598,18 +1614,23 @@ cpp_macro_definition (pfile, node)
        len += NODE_LEN (macro->params[i]) + 1; /* "," */
     }
 
-  for (i = 0; i < macro->count; i++)
+  if (CPP_OPTION (pfile, traditional))
+    len += _cpp_replacement_text_len (macro);
+  else
     {
-      cpp_token *token = &macro->exp.tokens[i];
+      for (i = 0; i < macro->count; i++)
+       {
+         cpp_token *token = &macro->exp.tokens[i];
 
-      if (token->type == CPP_MACRO_ARG)
-       len += NODE_LEN (macro->params[token->val.arg_no - 1]);
-      else
-       len += cpp_token_len (token); /* Includes room for ' '.  */
-      if (token->flags & STRINGIFY_ARG)
-       len++;                  /* "#" */
-      if (token->flags & PASTE_LEFT)
-       len += 3;               /* " ##" */
+         if (token->type == CPP_MACRO_ARG)
+           len += NODE_LEN (macro->params[token->val.arg_no - 1]);
+         else
+           len += cpp_token_len (token); /* Includes room for ' '.  */
+         if (token->flags & STRINGIFY_ARG)
+           len++;                      /* "#" */
+         if (token->flags & PASTE_LEFT)
+           len += 3;           /* " ##" */
+       }
     }
 
   if (len > pfile->macro_buffer_len)
@@ -1652,8 +1673,10 @@ cpp_macro_definition (pfile, node)
      definition is the empty string.  */
   *buffer++ = ' ';
 
+  if (CPP_OPTION (pfile, traditional))
+    buffer = _cpp_copy_replacement_text (macro, buffer);
+  else if (macro->count)
   /* Expansion tokens.  */
-  if (macro->count)
     {
       for (i = 0; i < macro->count; i++)
        {
index 7ebc1ad2c5aea82b9fd8dfac9185f668cd90a7fb..dba64041bf5b14820ab5024e9a6a10f8716fcd23 100644 (file)
@@ -320,7 +320,6 @@ cb_line_change (pfile, token, parsing_args)
     return;
 
   maybe_print_line (print.map, token->line);
-  print.printed = 1;
   print.prev = 0;
   print.source = 0;
 
@@ -329,12 +328,16 @@ cb_line_change (pfile, token, parsing_args)
      will provide a space if PREV_WHITE.  Don't bother trying to
      reconstruct tabs; we can't get it right in general, and nothing
      ought to care.  Some things do care; the fault lies with them.  */
-  if (token->col > 2)
+  if (!CPP_OPTION (pfile, traditional))
     {
-      unsigned int spaces = token->col - 2;
+      print.printed = 1;
+      if (token->col > 2)
+       {
+         unsigned int spaces = token->col - 2;
 
-      while (spaces--)
-       putc (' ', print.outf);
+         while (spaces--)
+           putc (' ', print.outf);
+       }
     }
 }
 
index b29750dd6c5c8701e8faef2f85cefd6ef07da75b..1adceaf3271a556d31fcf5feffcf6aee93031271 100644 (file)
@@ -65,7 +65,8 @@ struct fun_macro
 
 /* Lexing state.  It is mostly used to prevent macro expansion.  */
 enum ls {ls_none = 0,          /* Normal state.  */
-        ls_fun_macro,          /* When looking for '('.  */
+        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.  */
@@ -262,7 +263,7 @@ skip_whitespace (pfile, cur, skip_comments)
       if (is_nvspace (c) && c)
        continue;
 
-      if (!c && cur != RLIMIT (pfile->context))
+      if (!c && cur - 1 != RLIMIT (pfile->context))
        continue;
 
       if (*cur == '/' && skip_comments)
@@ -409,8 +410,6 @@ maybe_start_funlike (pfile, node, start, macro)
   macro->node = node;
   macro->offset = start - pfile->out.base;
   macro->argc = 0;
-
-  pfile->state.parsing_args = 1;
 }
 
 /* Save the OFFSET of the start of the next argument to MACRO.  */
@@ -485,19 +484,17 @@ scan_out_logical_line (pfile, macro)
          cur--;
          if (!pfile->buffer->from_stage3)
            cpp_error (pfile, DL_PEDWARN, "no newline at end of file");
-         if (pfile->state.parsing_args == 2)
-           cpp_error (pfile, DL_ERROR,
-                      "unterminated argument list invoking macro \"%s\"",
-                      NODE_NAME (fmacro.node));
          pfile->line++;
          goto done;
 
        case '\r': case '\n':
          cur = handle_newline (pfile, cur - 1);
-         if (pfile->state.parsing_args == 2 && !pfile->state.in_directive)
+         if ((lex_state == ls_fun_open || lex_state == ls_fun_close)
+             && !pfile->state.in_directive)
            {
              /* Newlines in arguments become a space.  */
-             out[-1] = ' ';
+             if (lex_state == ls_fun_close)
+               out[-1] = ' ';
              continue;
            }
          goto done;
@@ -578,14 +575,17 @@ scan_out_logical_line (pfile, macro)
 
              if (node->type == NT_MACRO
                  /* Should we expand for ls_answer?  */
-                 && lex_state == ls_none
+                 && (lex_state == ls_none || lex_state == ls_fun_open)
                  && !pfile->state.prevent_expansion
                  && !recursive_macro (pfile, node))
                {
-                 if (node->value.macro->fun_like)
+                 /* 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_macro;
+                     lex_state = ls_fun_open;
                      continue;
                    }
                  else
@@ -594,6 +594,7 @@ scan_out_logical_line (pfile, macro)
                         output, and push its replacement text.  */
                      pfile->out.cur = out_start;
                      push_replacement_text (pfile, node);
+                     lex_state = ls_none;
                      goto new_context;
                    }
                }
@@ -622,10 +623,9 @@ scan_out_logical_line (pfile, macro)
          if (quote == 0)
            {
              paren_depth++;
-             if (lex_state == ls_fun_macro)
+             if (lex_state == ls_fun_open)
                {
-                 lex_state = ls_none;
-                 pfile->state.parsing_args = 2;
+                 lex_state = ls_fun_close;
                  paren_depth = 1;
                  out = pfile->out.base + fmacro.offset;
                  fmacro.args[0] = fmacro.offset;
@@ -638,7 +638,7 @@ scan_out_logical_line (pfile, macro)
          break;
 
        case ',':
-         if (quote == 0 && pfile->state.parsing_args == 2 && paren_depth == 1)
+         if (quote == 0 && lex_state == ls_fun_close && paren_depth == 1)
            save_argument (&fmacro, out - pfile->out.base);
          break;
 
@@ -646,11 +646,11 @@ scan_out_logical_line (pfile, macro)
          if (quote == 0)
            {
              paren_depth--;
-             if (pfile->state.parsing_args == 2 && paren_depth == 0)
+             if (lex_state == ls_fun_close && paren_depth == 0)
                {
                  cpp_macro *m = fmacro.node->value.macro;
 
-                 pfile->state.parsing_args = 0;
+                 lex_state = ls_none;
                  save_argument (&fmacro, out - pfile->out.base);
 
                  /* A single zero-length argument is no argument.  */
@@ -701,12 +701,9 @@ scan_out_logical_line (pfile, macro)
 
       /* Some of these transitions of state are syntax errors.  The
         ISO preprocessor will issue errors later.  */
-      if (lex_state == ls_fun_macro)
-       {
-         /* Missing '('.  */
-         lex_state = ls_none;
-         pfile->state.parsing_args = 0;
-       }
+      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)
@@ -722,11 +719,10 @@ scan_out_logical_line (pfile, macro)
   if (fmacro.buff)
     _cpp_release_buff (pfile, fmacro.buff);
 
-  if (pfile->state.parsing_args == 2)
+  if (lex_state == ls_fun_close)
     cpp_error (pfile, DL_ERROR,
               "unterminated argument list invoking macro \"%s\"",
               NODE_NAME (fmacro.node));
-  pfile->state.parsing_args = 0;
 }
 
 /* Push a context holding the replacement text of the macro NODE on
@@ -737,9 +733,22 @@ push_replacement_text (pfile, node)
      cpp_reader *pfile;
      cpp_hashnode *node;
 {
-  cpp_macro *macro = node->value.macro;
+  size_t len;
+  const uchar *text;
+
+  if (node->flags & NODE_BUILTIN)
+    {
+      text = _cpp_builtin_macro_text (pfile, node);
+      len = ustrlen (text);
+    }
+  else
+    {
+      cpp_macro *macro = node->value.macro;
+      text = macro->exp.text;
+      len = macro->count;
+    }
 
-  _cpp_push_text_context (pfile, node, macro->exp.text, macro->count);
+  _cpp_push_text_context (pfile, node, text, len);
 }
 
 /* Returns TRUE if traditional macro recursion is detected.  */
@@ -784,6 +793,71 @@ recursive_macro (pfile, 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 (macro)
+     const cpp_macro *macro;
+{
+  size_t len;
+
+  if (macro->fun_like)
+    {
+      const uchar *exp;
+
+      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 (macro, dest)
+     const cpp_macro *macro;
+     uchar *dest;
+{
+  if (macro->fun_like)
+    {
+      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.  */