include/
[external/binutils.git] / gdb / macroexp.c
index e39f81a..752a939 100644 (file)
@@ -1,12 +1,12 @@
 /* C preprocessor macro expansion for GDB.
-   Copyright 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2007, 2008, 2009 Free Software Foundation, Inc.
    Contributed by Red Hat, Inc.
 
    This file is part of GDB.
 
    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 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -15,9 +15,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
 #include "gdb_obstack.h"
@@ -25,6 +23,7 @@
 #include "macrotab.h"
 #include "macroexp.h"
 #include "gdb_assert.h"
+#include "c-lang.h"
 
 
 \f
@@ -81,9 +80,6 @@ struct macro_buffer
 static void
 init_buffer (struct macro_buffer *b, int n)
 {
-  /* Small value for initial testing.  */
-  n = 1;
-
   b->size = n;
   if (n > 0)
     b->text = (char *) xmalloc (n);
@@ -176,8 +172,8 @@ appendmem (struct macro_buffer *b, char *addr, int len)
 /* Recognizing preprocessor tokens.  */
 
 
-static int
-is_whitespace (int c)
+int
+macro_is_whitespace (int c)
 {
   return (c == ' '
           || c == '\t'
@@ -187,15 +183,15 @@ is_whitespace (int c)
 }
 
 
-static int
-is_digit (int c)
+int
+macro_is_digit (int c)
 {
   return ('0' <= c && c <= '9');
 }
 
 
-static int
-is_identifier_nondigit (int c)
+int
+macro_is_identifier_nondigit (int c)
 {
   return (c == '_'
           || ('a' <= c && c <= 'z')
@@ -236,7 +232,7 @@ get_comment (struct macro_buffer *tok, char *p, char *end)
             return 1;
           }
 
-      error ("Unterminated comment in macro expansion.");
+      error (_("Unterminated comment in macro expansion."));
     }
   else if (p[0] == '/'
            && p[1] == '/')
@@ -260,13 +256,13 @@ static int
 get_identifier (struct macro_buffer *tok, char *p, char *end)
 {
   if (p < end
-      && is_identifier_nondigit (*p))
+      && macro_is_identifier_nondigit (*p))
     {
       char *tok_start = p;
 
       while (p < end
-             && (is_identifier_nondigit (*p)
-                 || is_digit (*p)))
+             && (macro_is_identifier_nondigit (*p)
+                 || macro_is_digit (*p)))
         p++;
 
       set_token (tok, tok_start, p);
@@ -282,21 +278,23 @@ static int
 get_pp_number (struct macro_buffer *tok, char *p, char *end)
 {
   if (p < end
-      && (is_digit (*p)
-          || *p == '.'))
+      && (macro_is_digit (*p)
+          || (*p == '.'
+             && p + 2 <= end
+             && macro_is_digit (p[1]))))
     {
       char *tok_start = p;
 
       while (p < end)
         {
-          if (is_digit (*p)
-              || is_identifier_nondigit (*p)
-              || *p == '.')
-            p++;
-          else if (p + 2 <= end
-                   && strchr ("eEpP.", *p)
-                   && (p[1] == '+' || p[1] == '-'))
+         if (p + 2 <= end
+             && strchr ("eEpP", *p)
+             && (p[1] == '+' || p[1] == '-'))
             p += 2;
+          else if (macro_is_digit (*p)
+                  || macro_is_identifier_nondigit (*p)
+                  || *p == '.')
+            p++;
           else
             break;
         }
@@ -323,14 +321,17 @@ get_character_constant (struct macro_buffer *tok, char *p, char *end)
      way GDB's C/C++ lexer does.  So we call parse_escape in utils.c
      to handle escape sequences.  */
   if ((p + 1 <= end && *p == '\'')
-      || (p + 2 <= end && p[0] == 'L' && p[1] == '\''))
+      || (p + 2 <= end
+         && (p[0] == 'L' || p[0] == 'u' || p[0] == 'U')
+         && p[1] == '\''))
     {
       char *tok_start = p;
       char *body_start;
+      int char_count = 0;
 
       if (*p == '\'')
         p++;
-      else if (*p == 'L')
+      else if (*p == 'L' || *p == 'u' || *p == 'U')
         p += 2;
       else
         gdb_assert (0);
@@ -339,22 +340,25 @@ get_character_constant (struct macro_buffer *tok, char *p, char *end)
       for (;;)
         {
           if (p >= end)
-            error ("Unmatched single quote.");
+            error (_("Unmatched single quote."));
           else if (*p == '\'')
             {
-              if (p == body_start)
-                error ("A character constant must contain at least one "
-                       "character.");
+              if (!char_count)
+                error (_("A character constant must contain at least one "
+                       "character."));
               p++;
               break;
             }
           else if (*p == '\\')
             {
               p++;
-              parse_escape (&p);
+             char_count += c_parse_escape (&p, NULL);
             }
           else
-            p++;
+           {
+             p++;
+             char_count++;
+           }
         }
 
       set_token (tok, tok_start, p);
@@ -373,16 +377,16 @@ static int
 get_string_literal (struct macro_buffer *tok, char *p, char *end)
 {
   if ((p + 1 <= end
-       && *p == '\"')
+       && *p == '"')
       || (p + 2 <= end
-          && p[0] == 'L'
-          && p[1] == '\"'))
+          && (p[0] == 'L' || p[0] == 'u' || p[0] == 'U')
+          && p[1] == '"'))
     {
       char *tok_start = p;
 
-      if (*p == '\"')
+      if (*p == '"')
         p++;
-      else if (*p == 'L')
+      else if (*p == 'L' || *p == 'u' || *p == 'U')
         p += 2;
       else
         gdb_assert (0);
@@ -390,19 +394,19 @@ get_string_literal (struct macro_buffer *tok, char *p, char *end)
       for (;;)
         {
           if (p >= end)
-            error ("Unterminated string in expression.");
-          else if (*p == '\"')
+            error (_("Unterminated string in expression."));
+          else if (*p == '"')
             {
               p++;
               break;
             }
           else if (*p == '\n')
-            error ("Newline characters may not appear in string "
-                   "constants.");
+            error (_("Newline characters may not appear in string "
+                   "constants."));
           else if (*p == '\\')
             {
               p++;
-              parse_escape (&p);
+              c_parse_escape (&p, NULL);
             }
           else
             p++;
@@ -421,16 +425,27 @@ get_punctuator (struct macro_buffer *tok, char *p, char *end)
 {
   /* Here, speed is much less important than correctness and clarity.  */
 
-  /* ISO/IEC 9899:1999 (E)  Section 6.4.6  Paragraph 1  */
+  /* ISO/IEC 9899:1999 (E)  Section 6.4.6  Paragraph 1.
+     Note that this table is ordered in a special way.  A punctuator
+     which is a prefix of another punctuator must appear after its
+     "extension".  Otherwise, the wrong token will be returned.  */
   static const char * const punctuators[] = {
-    "[", "]", "(", ")", "{", "}", ".", "->", 
-    "++", "--", "&", "*", "+", "-", "~", "!",
-    "/", "%", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", 
-    "^", "|", "&&", "||",
-    "?", ":", ";", "...",
-    "=", "*=", "/=", "%=", "+=", "-=", "<<=", ">>=", "&=", "^=", "|=",
-    ",", "#", "##",
-    "<:", ":>", "<%", "%>", "%:", "%:%:",
+    "[", "]", "(", ")", "{", "}", "?", ";", ",", "~",
+    "...", ".",
+    "->", "--", "-=", "-",
+    "++", "+=", "+",
+    "*=", "*",
+    "!=", "!",
+    "&&", "&=", "&",
+    "/=", "/",
+    "%>", "%:%:", "%:", "%=", "%",
+    "^=", "^",
+    "##", "#",
+    ":>", ":",
+    "||", "|=", "|",
+    "<<=", "<<", "<=", "<:", "<%", "<",
+    ">>=", ">>", ">=", ">",
+    "==", "=",
     0
   };
 
@@ -490,7 +505,7 @@ get_token (struct macro_buffer *tok,
      only occur after a #include, which we will never see.  */
 
   while (p < end)
-    if (is_whitespace (*p))
+    if (macro_is_whitespace (*p))
       p++;
     else if (get_comment (tok, p, end))
       p += tok->len;
@@ -616,9 +631,55 @@ append_tokens_without_splicing (struct macro_buffer *dest,
   /* As far as I know, there's no case where inserting a space isn't
      enough to prevent a splice.  */
   internal_error (__FILE__, __LINE__,
-                  "unable to avoid splicing tokens during macro expansion");
+                  _("unable to avoid splicing tokens during macro expansion"));
 }
 
+/* Stringify an argument, and insert it into DEST.  ARG is the text to
+   stringify; it is LEN bytes long.  */
+
+static void
+stringify (struct macro_buffer *dest, char *arg, int len)
+{
+  /* Trim initial whitespace from ARG.  */
+  while (len > 0 && macro_is_whitespace (*arg))
+    {
+      ++arg;
+      --len;
+    }
+
+  /* Trim trailing whitespace from ARG.  */
+  while (len > 0 && macro_is_whitespace (arg[len - 1]))
+    --len;
+
+  /* Insert the string.  */
+  appendc (dest, '"');
+  while (len > 0)
+    {
+      /* We could try to handle strange cases here, like control
+        characters, but there doesn't seem to be much point.  */
+      if (macro_is_whitespace (*arg))
+       {
+         /* Replace a sequence of whitespace with a single space.  */
+         appendc (dest, ' ');
+         while (len > 1 && macro_is_whitespace (arg[1]))
+           {
+             ++arg;
+             --len;
+           }
+       }
+      else if (*arg == '\\' || *arg == '"')
+       {
+         appendc (dest, '\\');
+         appendc (dest, *arg);
+       }
+      else
+       appendc (dest, *arg);
+      ++arg;
+      --len;
+    }
+  appendc (dest, '"');
+  dest->last_token = dest->len;
+}
 
 \f
 /* Expanding macros!  */
@@ -668,6 +729,11 @@ currently_rescanning (struct macro_name_list *list, const char *name)
 
    If SRC doesn't contain a properly terminated argument list, then
    raise an error.
+   
+   For a variadic macro, NARGS holds the number of formal arguments to
+   the macro.  For a GNU-style variadic macro, this should be the
+   number of named arguments.  For a non-variadic macro, NARGS should
+   be -1.
 
    Otherwise, return a pointer to the first element of an array of
    macro buffers referring to the argument texts, and set *ARGC_P to
@@ -688,7 +754,8 @@ currently_rescanning (struct macro_name_list *list, const char *name)
    following the invocation.  */
 
 static struct macro_buffer *
-gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
+gather_arguments (const char *name, struct macro_buffer *src,
+                 int nargs, int *argc_p)
 {
   struct macro_buffer tok;
   int args_len, args_size;
@@ -715,7 +782,7 @@ gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
   get_token (&tok, src);
 
   args_len = 0;
-  args_size = 1;                /* small for initial testing */
+  args_size = 6;
   args = (struct macro_buffer *) xmalloc (sizeof (*args) * args_size);
 
   for (;;)
@@ -741,7 +808,7 @@ gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
           char *start = src->text;
 
           if (! get_token (&tok, src))
-            error ("Malformed argument list for macro `%s'.", name);
+            error (_("Malformed argument list for macro `%s'."), name);
       
           /* Is tok an opening paren?  */
           if (tok.len == 1 && tok.text[0] == '(')
@@ -754,6 +821,20 @@ gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
                  the end of the argument list.  */
               if (depth == 0)
                 {
+                 /* In the varargs case, the last argument may be
+                    missing.  Add an empty argument in this case.  */
+                 if (nargs != -1 && args_len == nargs - 1)
+                   {
+                     /* Make sure we have room for the argument.  */
+                     if (args_len >= args_size)
+                       {
+                         args_size++;
+                         args = xrealloc (args, sizeof (*args) * args_size);
+                       }
+                     arg = &args[args_len++];
+                     set_token (arg, src->text, src->text);
+                   }
+
                   discard_cleanups (back_to);
                   *argc_p = args_len;
                   return args;
@@ -763,8 +844,11 @@ gather_arguments (const char *name, struct macro_buffer *src, int *argc_p)
             }
 
           /* If tok is a comma at top level, then that's the end of
-             the current argument.  */
-          else if (tok.len == 1 && tok.text[0] == ',' && depth == 0)
+             the current argument.  However, if we are handling a
+             variadic macro and we are computing the last argument, we
+             want to include the comma and remaining tokens.  */
+          else if (tok.len == 1 && tok.text[0] == ',' && depth == 0
+                  && (nargs == -1 || args_len < nargs))
             break;
 
           /* Extend the current argument to enclose this token.  If
@@ -795,17 +879,57 @@ static void scan (struct macro_buffer *dest,
                   void *lookup_baton);
 
 
+/* A helper function for substitute_args.
+   
+   ARGV is a vector of all the arguments; ARGC is the number of
+   arguments.  IS_VARARGS is true if the macro being substituted is a
+   varargs macro; in this case VA_ARG_NAME is the name of the
+   "variable" argument.  VA_ARG_NAME is ignored if IS_VARARGS is
+   false.
+
+   If the token TOK is the name of a parameter, return the parameter's
+   index.  If TOK is not an argument, return -1.  */
+
+static int
+find_parameter (const struct macro_buffer *tok,
+               int is_varargs, const struct macro_buffer *va_arg_name,
+               int argc, const char * const *argv)
+{
+  int i;
+
+  if (! tok->is_identifier)
+    return -1;
+
+  for (i = 0; i < argc; ++i)
+    if (tok->len == strlen (argv[i]) && ! memcmp (tok->text, argv[i], tok->len))
+      return i;
+
+  if (is_varargs && tok->len == va_arg_name->len
+      && ! memcmp (tok->text, va_arg_name->text, tok->len))
+    return argc - 1;
+
+  return -1;
+}
 /* Given the macro definition DEF, being invoked with the actual
    arguments given by ARGC and ARGV, substitute the arguments into the
    replacement list, and store the result in DEST.
 
+   IS_VARARGS should be true if DEF is a varargs macro.  In this case,
+   VA_ARG_NAME should be the name of the "variable" argument -- either
+   __VA_ARGS__ for c99-style varargs, or the final argument name, for
+   GNU-style varargs.  If IS_VARARGS is false, this parameter is
+   ignored.
+
    If it is necessary to expand macro invocations in one of the
    arguments, use LOOKUP_FUNC and LOOKUP_BATON to find the macro
    definitions, and don't expand invocations of the macros listed in
    NO_LOOP.  */
+
 static void
 substitute_args (struct macro_buffer *dest, 
                  struct macro_definition *def,
+                int is_varargs, const struct macro_buffer *va_arg_name,
                  int argc, struct macro_buffer *argv,
                  struct macro_name_list *no_loop,
                  macro_lookup_ftype *lookup_func,
@@ -813,6 +937,17 @@ substitute_args (struct macro_buffer *dest,
 {
   /* A macro buffer for the macro's replacement list.  */
   struct macro_buffer replacement_list;
+  /* The token we are currently considering.  */
+  struct macro_buffer tok;
+  /* The replacement list's pointer from just before TOK was lexed.  */
+  char *original_rl_start;
+  /* We have a single lookahead token to handle token splicing.  */
+  struct macro_buffer lookahead;
+  /* The lookahead token might not be valid.  */
+  int lookahead_valid;
+  /* The replacement list's pointer from just before LOOKAHEAD was
+     lexed.  */
+  char *lookahead_rl_start;
 
   init_shared_buffer (&replacement_list, (char *) def->replacement,
                       strlen (def->replacement));
@@ -820,16 +955,14 @@ substitute_args (struct macro_buffer *dest,
   gdb_assert (dest->len == 0);
   dest->last_token = 0;
 
+  original_rl_start = replacement_list.text;
+  if (! get_token (&tok, &replacement_list))
+    return;
+  lookahead_rl_start = replacement_list.text;
+  lookahead_valid = get_token (&lookahead, &replacement_list);
+
   for (;;)
     {
-      struct macro_buffer tok;
-      char *original_rl_start = replacement_list.text;
-      int substituted = 0;
-      
-      /* Find the next token in the replacement list.  */
-      if (! get_token (&tok, &replacement_list))
-        break;
-
       /* Just for aesthetics.  If we skipped some whitespace, copy
          that to DEST.  */
       if (tok.text > original_rl_start)
@@ -841,46 +974,161 @@ substitute_args (struct macro_buffer *dest,
       /* Is this token the stringification operator?  */
       if (tok.len == 1
           && tok.text[0] == '#')
-        error ("Stringification is not implemented yet.");
+       {
+         int arg;
 
-      /* Is this token the splicing operator?  */
-      if (tok.len == 2
-          && tok.text[0] == '#'
-          && tok.text[1] == '#')
-        error ("Token splicing is not implemented yet.");
+         if (!lookahead_valid)
+           error (_("Stringification operator requires an argument."));
 
-      /* Is this token an identifier?  */
-      if (tok.is_identifier)
-        {
-          int i;
-
-          /* Is it the magic varargs parameter?  */
-          if (tok.len == 11
-              && ! memcmp (tok.text, "__VA_ARGS__", 11))
-            error ("Variable-arity macros not implemented yet.");
-
-          /* Is it one of the parameters?  */
-          for (i = 0; i < def->argc; i++)
-            if (tok.len == strlen (def->argv[i])
-                && ! memcmp (tok.text, def->argv[i], tok.len))
-              {
-                struct macro_buffer arg_src;
-
-                /* Expand any macro invocations in the argument text,
-                   and append the result to dest.  Remember that scan
-                   mutates its source, so we need to scan a new buffer
-                   referring to the argument's text, not the argument
-                   itself.  */
-                init_shared_buffer (&arg_src, argv[i].text, argv[i].len);
-                scan (dest, &arg_src, no_loop, lookup_func, lookup_baton);
-                substituted = 1;
-                break;
-              }
-        }
+         arg = find_parameter (&lookahead, is_varargs, va_arg_name,
+                               def->argc, def->argv);
+         if (arg == -1)
+           error (_("Argument to stringification operator must name "
+                    "a macro parameter."));
 
-      /* If it wasn't a parameter, then just copy it across.  */
-      if (! substituted)
-        append_tokens_without_splicing (dest, &tok);
+         stringify (dest, argv[arg].text, argv[arg].len);
+
+         /* Read one token and let the loop iteration code handle the
+            rest.  */
+         lookahead_rl_start = replacement_list.text;
+         lookahead_valid = get_token (&lookahead, &replacement_list);
+       }
+      /* Is this token the splicing operator?  */
+      else if (tok.len == 2
+              && tok.text[0] == '#'
+              && tok.text[1] == '#')
+       error (_("Stray splicing operator"));
+      /* Is the next token the splicing operator?  */
+      else if (lookahead_valid
+              && lookahead.len == 2
+              && lookahead.text[0] == '#'
+              && lookahead.text[1] == '#')
+       {
+         int arg, finished = 0;
+         int prev_was_comma = 0;
+
+         /* Note that GCC warns if the result of splicing is not a
+            token.  In the debugger there doesn't seem to be much
+            benefit from doing this.  */
+
+         /* Insert the first token.  */
+         if (tok.len == 1 && tok.text[0] == ',')
+           prev_was_comma = 1;
+         else
+           {
+             int arg = find_parameter (&tok, is_varargs, va_arg_name,
+                                       def->argc, def->argv);
+             if (arg != -1)
+               appendmem (dest, argv[arg].text, argv[arg].len);
+             else
+               appendmem (dest, tok.text, tok.len);
+           }
+
+         /* Apply a possible sequence of ## operators.  */
+         for (;;)
+           {
+             if (! get_token (&tok, &replacement_list))
+               error (_("Splicing operator at end of macro"));
+
+             /* Handle a comma before a ##.  If we are handling
+                varargs, and the token on the right hand side is the
+                varargs marker, and the final argument is empty or
+                missing, then drop the comma.  This is a GNU
+                extension.  There is one ambiguous case here,
+                involving pedantic behavior with an empty argument,
+                but we settle that in favor of GNU-style (GCC uses an
+                option).  If we aren't dealing with varargs, we
+                simply insert the comma.  */
+             if (prev_was_comma)
+               {
+                 if (! (is_varargs
+                        && tok.len == va_arg_name->len
+                        && !memcmp (tok.text, va_arg_name->text, tok.len)
+                        && argv[argc - 1].len == 0))
+                   appendmem (dest, ",", 1);
+                 prev_was_comma = 0;
+               }
+
+             /* Insert the token.  If it is a parameter, insert the
+                argument.  If it is a comma, treat it specially.  */
+             if (tok.len == 1 && tok.text[0] == ',')
+               prev_was_comma = 1;
+             else
+               {
+                 int arg = find_parameter (&tok, is_varargs, va_arg_name,
+                                           def->argc, def->argv);
+                 if (arg != -1)
+                   appendmem (dest, argv[arg].text, argv[arg].len);
+                 else
+                   appendmem (dest, tok.text, tok.len);
+               }
+
+             /* Now read another token.  If it is another splice, we
+                loop.  */
+             original_rl_start = replacement_list.text;
+             if (! get_token (&tok, &replacement_list))
+               {
+                 finished = 1;
+                 break;
+               }
+
+             if (! (tok.len == 2
+                    && tok.text[0] == '#'
+                    && tok.text[1] == '#'))
+               break;
+           }
+
+         if (prev_was_comma)
+           {
+             /* We saw a comma.  Insert it now.  */
+             appendmem (dest, ",", 1);
+           }
+
+          dest->last_token = dest->len;
+         if (finished)
+           lookahead_valid = 0;
+         else
+           {
+             /* Set up for the loop iterator.  */
+             lookahead = tok;
+             lookahead_rl_start = original_rl_start;
+             lookahead_valid = 1;
+           }
+       }
+      else
+       {
+         /* Is this token an identifier?  */
+         int substituted = 0;
+         int arg = find_parameter (&tok, is_varargs, va_arg_name,
+                                   def->argc, def->argv);
+
+         if (arg != -1)
+           {
+             struct macro_buffer arg_src;
+
+             /* Expand any macro invocations in the argument text,
+                and append the result to dest.  Remember that scan
+                mutates its source, so we need to scan a new buffer
+                referring to the argument's text, not the argument
+                itself.  */
+             init_shared_buffer (&arg_src, argv[arg].text, argv[arg].len);
+             scan (dest, &arg_src, no_loop, lookup_func, lookup_baton);
+             substituted = 1;
+           }
+
+         /* If it wasn't a parameter, then just copy it across.  */
+         if (! substituted)
+           append_tokens_without_splicing (dest, &tok);
+       }
+
+      if (! lookahead_valid)
+       break;
+
+      tok = lookahead;
+      original_rl_start = lookahead_rl_start;
+
+      lookahead_rl_start = replacement_list.text;
+      lookahead_valid = get_token (&lookahead, &replacement_list);
     }
 }
 
@@ -927,17 +1175,43 @@ expand (const char *id,
   else if (def->kind == macro_function_like)
     {
       struct cleanup *back_to = make_cleanup (null_cleanup, 0);
-      int argc;
+      int argc = 0;
       struct macro_buffer *argv = NULL;
       struct macro_buffer substituted;
       struct macro_buffer substituted_src;
-
-      if (def->argc >= 1
-          && strcmp (def->argv[def->argc - 1], "...") == 0)
-        error ("Varargs macros not implemented yet.");
+      struct macro_buffer va_arg_name;
+      int is_varargs = 0;
+
+      if (def->argc >= 1)
+       {
+         if (strcmp (def->argv[def->argc - 1], "...") == 0)
+           {
+             /* In C99-style varargs, substitution is done using
+                __VA_ARGS__.  */
+             init_shared_buffer (&va_arg_name, "__VA_ARGS__",
+                                 strlen ("__VA_ARGS__"));
+             is_varargs = 1;
+           }
+         else
+           {
+             int len = strlen (def->argv[def->argc - 1]);
+             if (len > 3
+                 && strcmp (def->argv[def->argc - 1] + len - 3, "...") == 0)
+               {
+                 /* In GNU-style varargs, the name of the
+                    substitution parameter is the name of the formal
+                    argument without the "...".  */
+                 init_shared_buffer (&va_arg_name,
+                                     (char *) def->argv[def->argc - 1],
+                                     len - 3);
+                 is_varargs = 1;
+               }
+           }
+       }
 
       make_cleanup (free_current_contents, &argv);
-      argv = gather_arguments (id, src, &argc);
+      argv = gather_arguments (id, src, is_varargs ? def->argc : -1,
+                              &argc);
 
       /* If we couldn't find any argument list, then we don't expand
          this macro.  */
@@ -951,14 +1225,18 @@ expand (const char *id,
          this macro.  */
       if (argc != def->argc)
         {
+         if (is_varargs && argc >= def->argc - 1)
+           {
+             /* Ok.  */
+           }
           /* Remember that a sequence of tokens like "foo()" is a
              valid invocation of a macro expecting either zero or one
              arguments.  */
-          if (! (argc == 1
-                 && argv[0].len == 0
-                 && def->argc == 0))
-            error ("Wrong number of arguments to macro `%s' "
-                   "(expected %d, got %d).",
+          else if (! (argc == 1
+                     && argv[0].len == 0
+                     && def->argc == 0))
+            error (_("Wrong number of arguments to macro `%s' "
+                   "(expected %d, got %d)."),
                    id, def->argc, argc);
         }
 
@@ -970,8 +1248,8 @@ expand (const char *id,
          expand an argument until we see how it's being used.  */
       init_buffer (&substituted, 0);
       make_cleanup (cleanup_macro_buffer, &substituted);
-      substitute_args (&substituted, def, argc, argv, no_loop,
-                       lookup_func, lookup_baton);
+      substitute_args (&substituted, def, is_varargs, &va_arg_name,
+                      argc, argv, no_loop, lookup_func, lookup_baton);
 
       /* Now `substituted' is the macro's replacement list, with all
          argument values substituted into it properly.  Re-scan it for
@@ -991,7 +1269,7 @@ expand (const char *id,
       return 1;
     }
   else
-    internal_error (__FILE__, __LINE__, "bad macro definition kind");
+    internal_error (__FILE__, __LINE__, _("bad macro definition kind"));
 }
 
 
@@ -1122,7 +1400,7 @@ macro_expand_once (const char *source,
                    macro_lookup_ftype *lookup_func,
                    void *lookup_func_baton)
 {
-  error ("Expand-once not implemented yet.");
+  error (_("Expand-once not implemented yet."));
 }