Index: src/function.c
===================================================================
--- src/function.c	(revision 645)
+++ src/function.c	(revision 647)
@@ -877,6 +877,146 @@ func_foreach (char *o, char **argv, cons
   return o;
 }
 
+static char *
+func_xargs (char *o, char **argv, const char *funcname UNUSED)
+{
+  /* expand only the first three.  */
+  char *flags = expand_argument (argv[0], NULL);
+  char *varname = expand_argument (argv[1], NULL);
+  char *list = expand_argument (argv[2], NULL);
+  char *body = argv[3];
+
+  char *list_iterator = list;
+  char *flags_iterator = flags;
+  int doneany = 0, max_len = 0, max_words = 0;
+  bool use_nl_sep = 0, use_tab_after_nl = 0;
+
+  char *p, *f, *var_buffer;
+  unsigned int len, cur_var_len, cur_var_words, var_buffer_size;
+  variable_t *var;
+
+  /* loop through FLAGS, interpreting them word-at-a-time. */
+  while ((f = find_next_token (&flags_iterator, &len)) != 0)
+    {
+      if ((f[0] != '-') || (f[1] != 's' && f[1] != 'n' && tolower (f[1]) != 'l'))
+        fatal (reading_file, "Invalid xargs flag `%s'", f);
+      else if (tolower (f[1]) == 'l')
+        {
+          use_nl_sep = 1;
+          use_tab_after_nl = (f[1] == 'L');
+        }
+      else
+        {
+          int ival, numf;
+          numf = sscanf (f + 2, "%i", &ival);
+          if (numf != 1)
+            fatal (reading_file, "Invalid xargs parameter `%s' to `-%c' switch", f + 2, f[1]);
+          else if (f[1] == 's')
+            max_len = ival;
+          else /* if (f[1] == 'n') */
+            max_words = ival;
+        }
+    }
+
+  push_new_variable_scope ();
+  var = define_variable (varname, strlen (varname), "", o_automatic, 0);
+  free (var->value);
+  var->value = 0;
+  cur_var_len = 0;
+  cur_var_words = 0;
+  /* We assemble the concatenated words of VAR into a buffer which
+  we re-use for each expansion of TEXT.  We allow it to grow to any
+  maximum size it needs and never shrink it, we just free it at the
+  end of this function.  Start with any reasonable small size; we
+  will grow it quickly if needed. */
+  var_buffer_size = 512;
+  var_buffer = xmalloc (var_buffer_size);
+  var_buffer[cur_var_len] = 0;
+
+  /* loop through LIST,  put the value in VAR and expand BODY */
+  /* In fact we should assemble multiple words into VAR 
+  according to the flags with which we were invoked.  */
+  while ((p = find_next_token (&list_iterator, &len)) != 0)
+    {
+      /* If there is still room to append to VAR, then let's
+      do so, and let it build up to a healthy size before we
+      expand TEXT with it in. */
+      bool full_up = (max_words && ((cur_var_words + 1) > max_words))
+                  || (max_len && ((cur_var_len + (cur_var_len ? 1 : 0) + len + 1) > max_len));
+      if (!full_up)
+        {
+          /* Do we need to extend the current var buffer? */
+          if (var_buffer_size < (cur_var_len + 2 + len))
+            {
+              /* Yes, so double it in size. */
+              char *new_var_buffer;
+              new_var_buffer = xmalloc (var_buffer_size * 2);
+              memcpy (new_var_buffer, var_buffer, var_buffer_size);
+              free (var_buffer);
+              var_buffer = new_var_buffer;
+              var_buffer_size *= 2;
+            }
+          /* Now append new token (word) to the end of var buffer.  Need
+          a separating space if this isn't the very first entry.  */
+          if (cur_var_len)
+            var_buffer[cur_var_len++] = ' ';
+          memcpy (var_buffer + cur_var_len, p, len);
+          cur_var_len += len;
+          cur_var_words++;
+          var_buffer[cur_var_len] = 0;
+          /* Will now loop back and see if next word fits too. */
+        }
+      else
+        {
+          char *result = 0;
+          /* The string in VAR is as big as we will allow it, so calculate
+          the result of expanding TEXT with the current setting of VAR,
+          then clear down VAR and start again for next time. */
+          var_buffer[cur_var_len] = 0;
+          var->value = var_buffer;
+          result = allocated_variable_expand (body);
+          o = variable_buffer_output (o, result, strlen (result));
+          o = variable_buffer_output (o, use_nl_sep ? (use_tab_after_nl ? "\n\t" : "\n") : " ", use_tab_after_nl ? 2 : 1);
+          doneany = 1;
+          free (result);
+          /* So clear var buffer and start re-accumulating, beginning with 
+          the token we just failed to fit before we arrived here. */
+          memcpy (var_buffer, p, len);
+          cur_var_len = len;
+          cur_var_words = 1;
+          var_buffer[cur_var_len] = 0;
+        }
+    }
+
+  /* May need to expand TEXT one more time if there is any
+  partial line left over in VAR. */
+  if (cur_var_len)
+    {
+      char *result = 0;
+      var_buffer[cur_var_len] = 0;
+      var->value = var_buffer;
+      result = allocated_variable_expand (body);
+      o = variable_buffer_output (o, result, strlen (result));
+      o = variable_buffer_output (o, use_nl_sep ? (use_tab_after_nl ? "\n\t" : "\n") : " ", use_tab_after_nl ? 2 : 1);
+      doneany = 1;
+      free (result);
+    }
+
+  /* We don't need the var buffer any more. */
+  free (var_buffer);
+  var->value = 0;
+
+  /* Kill any superfluous trailing separator string.  */
+  if (doneany)
+    o -= (use_tab_after_nl ? 2 : 1);
+
+  pop_variable_scope (false);
+  free (varname);
+  free (list);
+
+  return o;
+}
+
 struct a_word
 {
   struct a_word *next;
@@ -2047,7 +2187,7 @@ func_abspath (char *o, char **argv, cons
    comma-separated values are treated as arguments.
 
    EXPAND_ARGS means that all arguments should be expanded before invocation.
-   Functions that do namespace tricks (foreach) don't automatically expand.  */
+   Functions that do namespace tricks (foreach, xargs) don't automatically expand.  */
 
 static char *func_call (char *p_o, char **argv, const char *p_funcname);
 
@@ -2083,6 +2223,7 @@ static struct function_table_entry funct
   { STRING_SIZE_TUPLE("words"),         0,  1,  1,  func_words},
   { STRING_SIZE_TUPLE("origin"),        0,  1,  1,  func_origin},
   { STRING_SIZE_TUPLE("foreach"),       3,  3,  0,  func_foreach},
+  { STRING_SIZE_TUPLE("xargs"),         4,  4,  0,  func_xargs},
   { STRING_SIZE_TUPLE("call"),          1,  0,  1,  func_call},
   { STRING_SIZE_TUPLE("info"),          0,  1,  1,  func_error},
   { STRING_SIZE_TUPLE("error"),         0,  1,  1,  func_error},
Index: doc/make.texi
===================================================================
--- doc/make.texi	(revision 645)
+++ doc/make.texi	(revision 647)
@@ -271,7 +271,7 @@ Functions for Transforming Text
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
 * Conditional Functions::       Functions that implement conditions.
-* Foreach Function::            Repeat some text with controlled variation.
+* Foreach and Xargs Functions:: Repeat some text with controlled variation.
 * Call Function::               Expand a user-defined function.
 * Value Function::              Return the un-expanded value of a variable.
 * Eval Function::               Evaluate the arguments as makefile syntax.
@@ -6029,7 +6029,7 @@ call, just as a variable might be substi
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
 * Conditional Functions::       Functions that implement conditions.
-* Foreach Function::            Repeat some text with controlled variation.
+* Foreach and Xargs Functions:: Repeat some text with controlled variation.
 * Call Function::               Expand a user-defined function.
 * Value Function::              Return the un-expanded value of a variable.
 * Eval Function::               Evaluate the arguments as makefile syntax.
@@ -6634,7 +6634,7 @@ the file names to refer to an existing f
 @code{wildcard} function to test for existence.
 @end table
 
-@node Conditional Functions, Foreach Function, File Name Functions, Functions
+@node Conditional Functions, Foreach and Xargs Functions, File Name Functions, Functions
 @section Functions for Conditionals
 @findex if
 @cindex conditional expansion
@@ -6708,7 +6708,7 @@ representation of false).
 
 @end table
 
-@node Foreach Function, Call Function, Conditional Functions, Functions
+@node Foreach and Xargs Functions, Call Function, Conditional Functions, Functions
 @section The @code{foreach} Function
 @findex foreach
 @cindex words, iterating over
@@ -6796,7 +6796,59 @@ might be useful if the value of @code{fi
 whose name is @samp{Esta escrito en espanol!} (es un nombre bastante largo,
 no?), but it is more likely to be a mistake.
 
-@node Call Function, Value Function, Foreach Function, Functions
+@section The @code{xargs} Function
+@findex xargs
+@cindex words, iterating over
+
+The @code{xargs} function is very similar to the @code{foreach} function.  It
+differs in that it allows more than one substitution to be performed into each
+of the repeated uses of the text.
+
+The syntax of the @code{xargs} function is:
+
+@example
+$(xargs @var{flags},@var{var},@var{list},@var{text})
+@end example
+
+@noindent
+The first three arguments, @var{flags}, @var{var} and @var{list}, are expanded
+before anything else is done; note that the last argument, @var{text}, is
+@strong{not} expanded at the same time.  Then, one or more words at a time from
+the expanded value of @var{list} are concatenated into the variable named by
+the expanded value of @var{var}, and @var{text} is expanded.  Presumably @var{text}
+contains references to that variable, so its expansion will be different
+each time.
+
+The result is that @var{text} is expanded many times, each time with one or more
+whitespace-separated words from @var{list}, until all the words in @var{list}
+have been exhausted.  The multiple expansions of @var{text} are concatenated,
+with spaces between them, to make the result of @code{xargs}.
+
+The @var{flags} passed to the @code{xargs} function control the way in which
+it assembles words from @var{list} into @var{var} each time it expands 
+@var{text}, and are similar to the option switches available to the
+@code{xargs} command-line utility.  Each switch must be concatenated to its
+argument to form a single whitespace-separated word in @var{flags}.  The
+recognized options are:
+
+@example
+@code{-nMAX-ARGS}  - set maximum number of words to assemble
+                     in @var{var} per expansion of @var{text}.
+@end example
+@example
+@code{-sMAX-CHARS} - set maximum number of characters to assemble
+                     in @var{var} per expansion of @var{text}.
+@end example
+@example
+@code{-l}          - append a newline between each of the multiple
+                     expansions of @var{text}, instead of a single space.
+@end example
+@example
+@code{-L}          - append a newline and a TAB between each of the multiple
+                     expansions of @var{text}, instead of a single space.
+@end example
+
+@node Call Function, Value Function, Foreach and Xargs Functions, Functions
 @section The @code{call} Function
 @findex call
 @cindex functions, user defined
@@ -10434,7 +10486,7 @@ Return a string describing the flavor of
 
 Evaluate @var{text} with @var{var} bound to each word in @var{words},
 and concatenate the results.@*
-@xref{Foreach Function, ,The @code{foreach} Function}.
+@xref{Foreach and Xargs Functions, ,The @code{foreach} Function}.
 
 @item $(call @var{var},@var{param},@dots{})
 
