Imported Upstream version 4.0
[platform/upstream/make.git] / function.c
index e2f6c8c..ecce627 100644 (file)
@@ -1,7 +1,5 @@
 /* Builtin function expansion for GNU Make.
-Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
-1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-2010 Free Software Foundation, Inc.
+Copyright (C) 1988-2013 Free Software Foundation, Inc.
 This file is part of GNU Make.
 
 GNU Make is free software; you can redistribute it and/or modify it under the
@@ -16,7 +14,7 @@ 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.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#include "make.h"
+#include "makeint.h"
 #include "filedef.h"
 #include "variable.h"
 #include "dep.h"
@@ -31,12 +29,16 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 struct function_table_entry
   {
+    union {
+      char *(*func_ptr) (char *output, char **argv, const char *fname);
+      gmk_func_ptr alloc_func_ptr;
+    } fptr;
     const char *name;
     unsigned char len;
     unsigned char minimum_args;
     unsigned char maximum_args;
-    char expand_args;
-    char *(*func_ptr) (char *output, char **argv, const char *fname);
+    unsigned char expand_args:1;
+    unsigned char alloc_fn:1;
   };
 
 static unsigned long
@@ -85,42 +87,42 @@ subst_expand (char *o, const char *text, const char *subst, const char *replace,
       /* The first occurrence of "" in any string is its end.  */
       o = variable_buffer_output (o, t, strlen (t));
       if (rlen > 0)
-       o = variable_buffer_output (o, replace, rlen);
+        o = variable_buffer_output (o, replace, rlen);
       return o;
     }
 
   do
     {
       if (by_word && slen == 0)
-       /* When matching by words, the empty string should match
-          the end of each word, rather than the end of the whole text.  */
-       p = end_of_token (next_token (t));
+        /* When matching by words, the empty string should match
+           the end of each word, rather than the end of the whole text.  */
+        p = end_of_token (next_token (t));
       else
-       {
-         p = strstr (t, subst);
-         if (p == 0)
-           {
-             /* No more matches.  Output everything left on the end.  */
-             o = variable_buffer_output (o, t, strlen (t));
-             return o;
-           }
-       }
+        {
+          p = strstr (t, subst);
+          if (p == 0)
+            {
+              /* No more matches.  Output everything left on the end.  */
+              o = variable_buffer_output (o, t, strlen (t));
+              return o;
+            }
+        }
 
       /* Output everything before this occurrence of the string to replace.  */
       if (p > t)
-       o = variable_buffer_output (o, t, p - t);
+        o = variable_buffer_output (o, t, p - t);
 
       /* If we're substituting only by fully matched words,
-        or only at the ends of words, check that this case qualifies.  */
+         or only at the ends of words, check that this case qualifies.  */
       if (by_word
           && ((p > text && !isblank ((unsigned char)p[-1]))
-              || (p[slen] != '\0' && !isblank ((unsigned char)p[slen]))))
-       /* Struck out.  Output the rest of the string that is
-          no longer to be replaced.  */
-       o = variable_buffer_output (o, subst, slen);
+              || ! STOP_SET (p[slen], MAP_BLANK|MAP_NUL)))
+        /* Struck out.  Output the rest of the string that is
+           no longer to be replaced.  */
+        o = variable_buffer_output (o, subst, slen);
       else if (rlen > 0)
-       /* Output the replacement string.  */
-       o = variable_buffer_output (o, replace, rlen);
+        /* Output the replacement string.  */
+        o = variable_buffer_output (o, replace, rlen);
 
       /* Advance T past the string to be replaced.  */
       t = p + slen;
@@ -167,7 +169,7 @@ patsubst_expand_pat (char *o, const char *text,
   if (!pattern_percent)
     /* With no % in the pattern, this is just a simple substitution.  */
     return subst_expand (o, text, pattern, replace,
-                        strlen (pattern), strlen (replace), 1);
+                         strlen (pattern), strlen (replace), 1);
 
   /* Record the length of PATTERN before and after the %
      so we don't have to compute it more than once.  */
@@ -180,53 +182,53 @@ patsubst_expand_pat (char *o, const char *text,
 
       /* Is it big enough to match?  */
       if (len < pattern_prepercent_len + pattern_postpercent_len)
-       fail = 1;
+        fail = 1;
 
       /* Does the prefix match? */
       if (!fail && pattern_prepercent_len > 0
-         && (*t != *pattern
-             || t[pattern_prepercent_len - 1] != pattern_percent[-2]
-             || !strneq (t + 1, pattern + 1, pattern_prepercent_len - 1)))
-       fail = 1;
+          && (*t != *pattern
+              || t[pattern_prepercent_len - 1] != pattern_percent[-2]
+              || !strneq (t + 1, pattern + 1, pattern_prepercent_len - 1)))
+        fail = 1;
 
       /* Does the suffix match? */
       if (!fail && pattern_postpercent_len > 0
-         && (t[len - 1] != pattern_percent[pattern_postpercent_len - 1]
-             || t[len - pattern_postpercent_len] != *pattern_percent
-             || !strneq (&t[len - pattern_postpercent_len],
-                         pattern_percent, pattern_postpercent_len - 1)))
-       fail = 1;
+          && (t[len - 1] != pattern_percent[pattern_postpercent_len - 1]
+              || t[len - pattern_postpercent_len] != *pattern_percent
+              || !strneq (&t[len - pattern_postpercent_len],
+                          pattern_percent, pattern_postpercent_len - 1)))
+        fail = 1;
 
       if (fail)
-       /* It didn't match.  Output the string.  */
-       o = variable_buffer_output (o, t, len);
+        /* It didn't match.  Output the string.  */
+        o = variable_buffer_output (o, t, len);
       else
-       {
-         /* It matched.  Output the replacement.  */
-
-         /* Output the part of the replacement before the %.  */
-         o = variable_buffer_output (o, replace, replace_prepercent_len);
-
-         if (replace_percent != 0)
-           {
-             /* Output the part of the matched string that
-                matched the % in the pattern.  */
-             o = variable_buffer_output (o, t + pattern_prepercent_len,
-                                         len - (pattern_prepercent_len
-                                                + pattern_postpercent_len));
-             /* Output the part of the replacement after the %.  */
-             o = variable_buffer_output (o, replace_percent,
-                                         replace_postpercent_len);
-           }
-       }
+        {
+          /* It matched.  Output the replacement.  */
+
+          /* Output the part of the replacement before the %.  */
+          o = variable_buffer_output (o, replace, replace_prepercent_len);
+
+          if (replace_percent != 0)
+            {
+              /* Output the part of the matched string that
+                 matched the % in the pattern.  */
+              o = variable_buffer_output (o, t + pattern_prepercent_len,
+                                          len - (pattern_prepercent_len
+                                                 + pattern_postpercent_len));
+              /* Output the part of the replacement after the %.  */
+              o = variable_buffer_output (o, replace_percent,
+                                          replace_postpercent_len);
+            }
+        }
 
       /* Output a space, but not if the replacement is "".  */
       if (fail || replace_prepercent_len > 0
-         || (replace_percent != 0 && len + replace_postpercent_len > 0))
-       {
-         o = variable_buffer_output (o, " ", 1);
-         doneany = 1;
-       }
+          || (replace_percent != 0 && len + replace_postpercent_len > 0))
+        {
+          o = variable_buffer_output (o, " ", 1);
+          doneany = 1;
+        }
     }
   if (doneany)
     /* Kill the last space.  */
@@ -267,19 +269,19 @@ patsubst_expand (char *o, const char *text, char *pattern, char *replace)
 static const struct function_table_entry *
 lookup_function (const char *s)
 {
+  struct function_table_entry function_table_entry_key;
   const char *e = s;
 
-  while (*e && ( (*e >= 'a' && *e <= 'z') || *e == '-'))
+  while (STOP_SET (*e, MAP_USERFUNC))
     e++;
-  if (*e == '\0' || isblank ((unsigned char) *e))
-    {
-      struct function_table_entry function_table_entry_key;
-      function_table_entry_key.name = s;
-      function_table_entry_key.len = e - s;
 
-      return hash_find_item (&function_table, &function_table_entry_key);
-    }
-  return 0;
+  if (e == s || !STOP_SET(*e, MAP_NUL|MAP_SPACE))
+    return NULL;
+
+  function_table_entry_key.name = s;
+  function_table_entry_key.len = e - s;
+
+  return hash_find_item (&function_table, &function_table_entry_key);
 }
 \f
 
@@ -297,7 +299,7 @@ pattern_matches (const char *pattern, const char *percent, const char *str)
       memcpy (new_chars, pattern, len);
       percent = find_percent (new_chars);
       if (percent == 0)
-       return streq (new_chars, str);
+        return streq (new_chars, str);
       pattern = new_chars;
     }
 
@@ -331,9 +333,9 @@ find_next_argument (char startparen, char endparen,
 
     else if (*ptr == endparen)
       {
-       --count;
-       if (count < 0)
-         return NULL;
+        --count;
+        if (count < 0)
+          return NULL;
       }
 
     else if (*ptr == ',' && !count)
@@ -355,8 +357,8 @@ string_glob (char *line)
   struct nameseq *chain;
   unsigned int idx;
 
-  chain = PARSE_FILE_SEQ (&line, struct nameseq, '\0', NULL,
-                          /* We do not want parse_file_seq to strip `./'s.
+  chain = PARSE_FILE_SEQ (&line, struct nameseq, MAP_NUL, NULL,
+                          /* We do not want parse_file_seq to strip './'s.
                              That would break examples like:
                              $(patsubst ./%.c,obj/%.o,$(wildcard ./?*.c)).  */
                           PARSEFS_NOSTRIP|PARSEFS_NOCACHE|PARSEFS_EXISTS);
@@ -428,17 +430,17 @@ func_join (char *o, char **argv, const char *funcname UNUSED)
 
       tp = find_next_token (&list1_iterator, &len1);
       if (tp != 0)
-       o = variable_buffer_output (o, tp, len1);
+        o = variable_buffer_output (o, tp, len1);
 
       pp = find_next_token (&list2_iterator, &len2);
       if (pp != 0)
-       o = variable_buffer_output (o, pp, len2);
+        o = variable_buffer_output (o, pp, len2);
 
       if (tp != 0 || pp != 0)
-       {
-         o = variable_buffer_output (o, " ", 1);
-         doneany = 1;
-       }
+        {
+          o = variable_buffer_output (o, " ", 1);
+          doneany = 1;
+        }
     }
   while (tp != 0 || pp != 0);
   if (doneany)
@@ -461,29 +463,29 @@ func_origin (char *o, char **argv, const char *funcname UNUSED)
       {
       default:
       case o_invalid:
-       abort ();
-       break;
+        abort ();
+        break;
       case o_default:
-       o = variable_buffer_output (o, "default", 7);
-       break;
+        o = variable_buffer_output (o, "default", 7);
+        break;
       case o_env:
-       o = variable_buffer_output (o, "environment", 11);
-       break;
+        o = variable_buffer_output (o, "environment", 11);
+        break;
       case o_file:
-       o = variable_buffer_output (o, "file", 4);
-       break;
+        o = variable_buffer_output (o, "file", 4);
+        break;
       case o_env_override:
-       o = variable_buffer_output (o, "environment override", 20);
-       break;
+        o = variable_buffer_output (o, "environment override", 20);
+        break;
       case o_command:
-       o = variable_buffer_output (o, "command line", 12);
-       break;
+        o = variable_buffer_output (o, "command line", 12);
+        break;
       case o_override:
-       o = variable_buffer_output (o, "override", 8);
-       break;
+        o = variable_buffer_output (o, "override", 8);
+        break;
       case o_automatic:
-       o = variable_buffer_output (o, "automatic", 9);
-       break;
+        o = variable_buffer_output (o, "automatic", 9);
+        break;
       }
 
   return o;
@@ -505,16 +507,6 @@ func_flavor (char *o, char **argv, const char *funcname UNUSED)
   return o;
 }
 
-#ifdef VMS
-# define IS_PATHSEP(c) ((c) == ']')
-#else
-# ifdef HAVE_DOS_PATHS
-#  define IS_PATHSEP(c) ((c) == '/' || (c) == '\\')
-# else
-#  define IS_PATHSEP(c) ((c) == '/')
-# endif
-#endif
-
 
 static char *
 func_notdir_suffix (char *o, char **argv, const char *funcname)
@@ -525,44 +517,40 @@ func_notdir_suffix (char *o, char **argv, const char *funcname)
   int doneany =0;
   unsigned int len=0;
 
-  int is_suffix = streq (funcname, "suffix");
+  int is_suffix = funcname[0] == 's';
   int is_notdir = !is_suffix;
+  int stop = MAP_PATHSEP | (is_suffix ? MAP_DOT : 0);
   while ((p2 = find_next_token (&list_iterator, &len)) != 0)
     {
-      const char *p = p2 + len;
+      const char *p = p2 + len - 1;
 
-
-      while (p >= p2 && (!is_suffix || *p != '.'))
-       {
-         if (IS_PATHSEP (*p))
-           break;
-         --p;
-       }
+      while (p >= p2 && ! STOP_SET (*p, stop))
+        --p;
 
       if (p >= p2)
-       {
-         if (is_notdir)
-           ++p;
-         else if (*p != '.')
-           continue;
-         o = variable_buffer_output (o, p, len - (p - p2));
-       }
+        {
+          if (is_notdir)
+            ++p;
+          else if (*p != '.')
+            continue;
+          o = variable_buffer_output (o, p, len - (p - p2));
+        }
 #ifdef HAVE_DOS_PATHS
       /* Handle the case of "d:foo/bar".  */
-      else if (streq (funcname, "notdir") && p2[0] && p2[1] == ':')
-       {
-         p = p2 + 2;
-         o = variable_buffer_output (o, p, len - (p - p2));
-       }
+      else if (is_notdir && p2[0] && p2[1] == ':')
+        {
+          p = p2 + 2;
+          o = variable_buffer_output (o, p, len - (p - p2));
+        }
 #endif
       else if (is_notdir)
-       o = variable_buffer_output (o, p2, len);
+        o = variable_buffer_output (o, p2, len);
 
       if (is_notdir || p >= p2)
-       {
-         o = variable_buffer_output (o, " ", 1);
-         doneany = 1;
-       }
+        {
+          o = variable_buffer_output (o, " ", 1);
+          doneany = 1;
+        }
     }
 
   if (doneany)
@@ -579,21 +567,17 @@ func_basename_dir (char *o, char **argv, const char *funcname)
   /* Expand the argument.  */
   const char *p3 = argv[0];
   const char *p2;
-  int doneany=0;
-  unsigned int len=0;
-
-  int is_basename= streq (funcname, "basename");
-  int is_dir= !is_basename;
+  int doneany = 0;
+  unsigned int len = 0;
 
+  int is_basename = funcname[0] == 'b';
+  int is_dir = !is_basename;
+  int stop = MAP_PATHSEP | (is_basename ? MAP_DOT : 0) | MAP_NUL;
   while ((p2 = find_next_token (&p3, &len)) != 0)
     {
-      const char *p = p2 + len;
-      while (p >= p2 && (!is_basename  || *p != '.'))
-        {
-          if (IS_PATHSEP (*p))
-            break;
-          --p;
-        }
+      const char *p = p2 + len - 1;
+      while (p >= p2 && ! STOP_SET (*p, stop))
+        --p;
 
       if (p >= p2 && (is_dir))
         o = variable_buffer_output (o, p2, ++p - p2);
@@ -634,7 +618,7 @@ func_addsuffix_addprefix (char *o, char **argv, const char *funcname)
 {
   int fixlen = strlen (argv[0]);
   const char *list_iterator = argv[1];
-  int is_addprefix = streq (funcname, "addprefix");
+  int is_addprefix = funcname[3] == 'p';
   int is_addsuffix = !is_addprefix;
 
   int doneany = 0;
@@ -644,10 +628,10 @@ func_addsuffix_addprefix (char *o, char **argv, const char *funcname)
   while ((p = find_next_token (&list_iterator, &len)) != 0)
     {
       if (is_addprefix)
-       o = variable_buffer_output (o, argv[0], fixlen);
+        o = variable_buffer_output (o, argv[0], fixlen);
       o = variable_buffer_output (o, p, len);
       if (is_addsuffix)
-       o = variable_buffer_output (o, argv[0], fixlen);
+        o = variable_buffer_output (o, argv[0], fixlen);
       o = variable_buffer_output (o, " ", 1);
       doneany = 1;
     }
@@ -663,7 +647,7 @@ static char *
 func_subst (char *o, char **argv, const char *funcname UNUSED)
 {
   o = subst_expand (o, argv[2], argv[0], argv[1], strlen (argv[0]),
-                   strlen (argv[1]), 0);
+                    strlen (argv[1]), 0);
 
   return o;
 }
@@ -706,7 +690,7 @@ func_words (char *o, char **argv, const char *funcname UNUSED)
   const char *word_iterator = argv[0];
   char buf[20];
 
-  while (find_next_token (&word_iterator, (unsigned int *) 0) != 0)
+  while (find_next_token (&word_iterator, NULL) != 0)
     ++i;
 
   sprintf (buf, "%d", i);
@@ -738,7 +722,7 @@ check_numeric (const char *s, const char *msg)
   strip_whitespace (&s, &end);
 
   for (; s <= end; ++s)
-    if (!ISDIGIT (*s))  /* ISDIGIT only evals its arg once: see make.h.  */
+    if (!ISDIGIT (*s))  /* ISDIGIT only evals its arg once: see makeint.h.  */
       break;
 
   if (s <= end || end - beg < 0)
@@ -755,12 +739,12 @@ func_word (char *o, char **argv, const char *funcname UNUSED)
   int i;
 
   /* Check the first argument.  */
-  check_numeric (argv[0], _("non-numeric first argument to `word' function"));
+  check_numeric (argv[0], _("non-numeric first argument to 'word' function"));
   i = atoi (argv[0]);
 
   if (i == 0)
     fatal (*expanding_var,
-           _("first argument to `word' function must be greater than 0"));
+           _("first argument to 'word' function must be greater than 0"));
 
   end_p = argv[1];
   while ((p = find_next_token (&end_p, 0)) != 0)
@@ -780,14 +764,14 @@ func_wordlist (char *o, char **argv, const char *funcname UNUSED)
 
   /* Check the arguments.  */
   check_numeric (argv[0],
-                _("non-numeric first argument to `wordlist' function"));
+                 _("non-numeric first argument to 'wordlist' function"));
   check_numeric (argv[1],
-                _("non-numeric second argument to `wordlist' function"));
+                 _("non-numeric second argument to 'wordlist' function"));
 
   start = atoi (argv[0]);
   if (start < 1)
     fatal (*expanding_var,
-           "invalid first argument to `wordlist' function: `%d'", start);
+           "invalid first argument to 'wordlist' function: '%d'", start);
 
   count = atoi (argv[1]) - start + 1;
 
@@ -896,7 +880,7 @@ a_word_hash_cmp (const void *x, const void *y)
   if (result)
     return result;
   return_STRING_COMPARE (((struct a_word const *) x)->str,
-                        ((struct a_word const *) y)->str);
+                         ((struct a_word const *) y)->str);
 }
 
 struct a_pattern
@@ -905,7 +889,6 @@ struct a_pattern
   char *str;
   char *percent;
   int length;
-  int save_c;
 };
 
 static char *
@@ -919,7 +902,7 @@ func_filter_filterout (char *o, char **argv, const char *funcname)
   struct a_pattern *pp;
 
   struct hash_table a_word_table;
-  int is_filter = streq (funcname, "filter");
+  int is_filter = funcname[CSTRLEN ("filter")] == '\0';
   const char *pat_iterator = argv[0];
   const char *word_iterator = argv[1];
   int literals = 0;
@@ -928,7 +911,9 @@ func_filter_filterout (char *o, char **argv, const char *funcname)
   char *p;
   unsigned int len;
 
-  /* Chop ARGV[0] up into patterns to match against the words.  */
+  /* Chop ARGV[0] up into patterns to match against the words.
+     We don't need to preserve it because our caller frees all the
+     argument memory anyway.  */
 
   pattail = &pathead;
   while ((p = find_next_token (&pat_iterator, &len)) != 0)
@@ -939,15 +924,16 @@ func_filter_filterout (char *o, char **argv, const char *funcname)
       pattail = &pat->next;
 
       if (*pat_iterator != '\0')
-       ++pat_iterator;
+        ++pat_iterator;
 
       pat->str = p;
-      pat->length = len;
-      pat->save_c = p[len];
       p[len] = '\0';
       pat->percent = find_percent (p);
       if (pat->percent == 0)
-       literals++;
+        literals++;
+
+      /* find_percent() might shorten the string so LEN is wrong.  */
+      pat->length = strlen (pat->str);
     }
   *pattail = 0;
 
@@ -962,7 +948,7 @@ func_filter_filterout (char *o, char **argv, const char *funcname)
       wordtail = &word->next;
 
       if (*word_iterator != '\0')
-       ++word_iterator;
+        ++word_iterator;
 
       p[len] = '\0';
       word->str = p;
@@ -980,11 +966,11 @@ func_filter_filterout (char *o, char **argv, const char *funcname)
       hash_init (&a_word_table, words, a_word_hash_1, a_word_hash_2,
                  a_word_hash_cmp);
       for (wp = wordhead; wp != 0; wp = wp->next)
-       {
-         struct a_word *owp = hash_insert (&a_word_table, wp);
-         if (owp)
-           wp->chain = owp;
-       }
+        {
+          struct a_word *owp = hash_insert (&a_word_table, wp);
+          if (owp)
+            wp->chain = owp;
+        }
     }
 
   if (words)
@@ -993,45 +979,42 @@ func_filter_filterout (char *o, char **argv, const char *funcname)
 
       /* Run each pattern through the words, killing words.  */
       for (pp = pathead; pp != 0; pp = pp->next)
-       {
-         if (pp->percent)
-           for (wp = wordhead; wp != 0; wp = wp->next)
-             wp->matched |= pattern_matches (pp->str, pp->percent, wp->str);
-         else if (hashing)
-           {
-             struct a_word a_word_key;
-             a_word_key.str = pp->str;
-             a_word_key.length = pp->length;
-             wp = hash_find_item (&a_word_table, &a_word_key);
-             while (wp)
-               {
-                 wp->matched |= 1;
-                 wp = wp->chain;
-               }
-           }
-         else
-           for (wp = wordhead; wp != 0; wp = wp->next)
-             wp->matched |= (wp->length == pp->length
-                             && strneq (pp->str, wp->str, wp->length));
-       }
+        {
+          if (pp->percent)
+            for (wp = wordhead; wp != 0; wp = wp->next)
+              wp->matched |= pattern_matches (pp->str, pp->percent, wp->str);
+          else if (hashing)
+            {
+              struct a_word a_word_key;
+              a_word_key.str = pp->str;
+              a_word_key.length = pp->length;
+              wp = hash_find_item (&a_word_table, &a_word_key);
+              while (wp)
+                {
+                  wp->matched |= 1;
+                  wp = wp->chain;
+                }
+            }
+          else
+            for (wp = wordhead; wp != 0; wp = wp->next)
+              wp->matched |= (wp->length == pp->length
+                              && strneq (pp->str, wp->str, wp->length));
+        }
 
       /* Output the words that matched (or didn't, for filter-out).  */
       for (wp = wordhead; wp != 0; wp = wp->next)
-       if (is_filter ? wp->matched : !wp->matched)
-         {
-           o = variable_buffer_output (o, wp->str, strlen (wp->str));
-           o = variable_buffer_output (o, " ", 1);
-           doneany = 1;
-         }
+        if (is_filter ? wp->matched : !wp->matched)
+          {
+            o = variable_buffer_output (o, wp->str, strlen (wp->str));
+            o = variable_buffer_output (o, " ", 1);
+            doneany = 1;
+          }
 
       if (doneany)
-       /* Kill the last space.  */
-       --o;
+        /* Kill the last space.  */
+        --o;
     }
 
-  for (pp = pathead; pp != 0; pp = pp->next)
-    pp->str[pp->length] = pp->save_c;
-
   if (hashing)
     hash_free (&a_word_table, 0);
 
@@ -1051,12 +1034,12 @@ func_strip (char *o, char **argv, const char *funcname UNUSED)
       const char *word_start;
 
       while (isspace ((unsigned char)*p))
-       ++p;
+        ++p;
       word_start = p;
       for (i=0; *p != '\0' && !isspace ((unsigned char)*p); ++p, ++i)
-       {}
+        {}
       if (!i)
-       break;
+        break;
       o = variable_buffer_output (o, word_start, i);
       o = variable_buffer_output (o, " ", 1);
       doneany = 1;
@@ -1096,7 +1079,8 @@ func_error (char *o, char **argv, const char *funcname)
     }
   strcpy (p, *argvp);
 
-  switch (*funcname) {
+  switch (*funcname)
+    {
     case 'e':
       fatal (reading_file, "%s", msg);
 
@@ -1105,13 +1089,13 @@ func_error (char *o, char **argv, const char *funcname)
       break;
 
     case 'i':
-      printf ("%s\n", msg);
-      fflush(stdout);
+      outputs (0, msg);
+      outputs (0, "\n");
       break;
 
     default:
       fatal (*expanding_var, "Internal error: func_error: '%s'", funcname);
-  }
+    }
 
   /* The warning function expands to the empty string.  */
   return o;
@@ -1129,25 +1113,17 @@ func_sort (char *o, char **argv, const char *funcname UNUSED)
   int wordi;
   char *p;
   unsigned int len;
-  int i;
 
   /* Find the maximum number of words we'll have.  */
   t = argv[0];
-  wordi = 1;
-  while (*t != '\0')
+  wordi = 0;
+  while ((p = find_next_token (&t, NULL)) != 0)
     {
-      char c = *(t++);
-
-      if (! isspace ((unsigned char)c))
-        continue;
-
+      ++t;
       ++wordi;
-
-      while (isspace ((unsigned char)*t))
-        ++t;
     }
 
-  words = xmalloc (wordi * sizeof (char *));
+  words = xmalloc ((wordi == 0 ? 1 : wordi) * sizeof (char *));
 
   /* Now assign pointers to each string in the array.  */
   t = argv[0];
@@ -1161,6 +1137,8 @@ func_sort (char *o, char **argv, const char *funcname UNUSED)
 
   if (wordi)
     {
+      int i;
+
       /* Now sort the list of words.  */
       qsort (words, wordi, sizeof (char *), alpha_compare);
 
@@ -1304,12 +1282,12 @@ static char *
 func_and (char *o, char **argv, const char *funcname UNUSED)
 {
   char *expansion;
-  int result;
 
   while (1)
     {
       const char *begp = *argv;
       const char *endp = begp + strlen (*argv) - 1;
+      int result;
 
       /* An empty condition is always false.  */
       strip_whitespace (&begp, &endp);
@@ -1371,7 +1349,7 @@ func_eval (char *o, char **argv, const char *funcname UNUSED)
 
   install_variable_buffer (&buf, &len);
 
-  eval_buffer (argv[0]);
+  eval_buffer (argv[0], NULL);
 
   restore_variable_buffer (buf, len);
 
@@ -1387,35 +1365,39 @@ func_value (char *o, char **argv, const char *funcname UNUSED)
 
   /* Copy its value into the output buffer without expanding it.  */
   if (v)
-    o = variable_buffer_output (o, v->value, strlen(v->value));
+    o = variable_buffer_output (o, v->value, strlen (v->value));
 
   return o;
 }
 
 /*
-  \r  is replaced on UNIX as well. Is this desirable?
+  \r is replaced on UNIX as well. Is this desirable?
  */
 static void
-fold_newlines (char *buffer, unsigned int *length)
+fold_newlines (char *buffer, unsigned int *length, int trim_newlines)
 {
   char *dst = buffer;
   char *src = buffer;
-  char *last_nonnl = buffer -1;
+  char *last_nonnl = buffer - 1;
   src[*length] = 0;
   for (; *src != '\0'; ++src)
     {
       if (src[0] == '\r' && src[1] == '\n')
-       continue;
+        continue;
       if (*src == '\n')
-       {
-         *dst++ = ' ';
-       }
+        {
+          *dst++ = ' ';
+        }
       else
-       {
-         last_nonnl = dst;
-         *dst++ = *src;
-       }
+        {
+          last_nonnl = dst;
+          *dst++ = *src;
+        }
     }
+
+  if (!trim_newlines && (last_nonnl < (dst - 2)))
+    last_nonnl = dst - 2;
+
   *(++last_nonnl) = '\0';
   *length = last_nonnl - buffer;
 }
@@ -1433,85 +1415,128 @@ int shell_function_pid = 0, shell_function_completed;
 #include "sub_proc.h"
 
 
-void
+int
 windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp)
 {
   SECURITY_ATTRIBUTES saAttr;
-  HANDLE hIn;
-  HANDLE hErr;
+  HANDLE hIn = INVALID_HANDLE_VALUE;
+  HANDLE hErr = INVALID_HANDLE_VALUE;
   HANDLE hChildOutRd;
   HANDLE hChildOutWr;
-  HANDLE hProcess;
+  HANDLE hProcess, tmpIn, tmpErr;
+  DWORD e;
 
+  /* Set status for return.  */
+  pipedes[0] = pipedes[1] = -1;
+  *pid_p = (pid_t)-1;
 
   saAttr.nLength = sizeof (SECURITY_ATTRIBUTES);
   saAttr.bInheritHandle = TRUE;
   saAttr.lpSecurityDescriptor = NULL;
 
-  if (DuplicateHandle (GetCurrentProcess(),
-                     GetStdHandle(STD_INPUT_HANDLE),
-                     GetCurrentProcess(),
-                     &hIn,
-                     0,
-                     TRUE,
-                     DUPLICATE_SAME_ACCESS) == FALSE) {
-    fatal (NILF, _("windows32_openpipe(): DuplicateHandle(In) failed (e=%ld)\n"),
-          GetLastError());
-
-  }
-  if (DuplicateHandle(GetCurrentProcess(),
-                     GetStdHandle(STD_ERROR_HANDLE),
-                     GetCurrentProcess(),
-                     &hErr,
-                     0,
-                     TRUE,
-                     DUPLICATE_SAME_ACCESS) == FALSE) {
-    fatal (NILF, _("windows32_open_pipe(): DuplicateHandle(Err) failed (e=%ld)\n"),
-          GetLastError());
-  }
-
-  if (!CreatePipe(&hChildOutRd, &hChildOutWr, &saAttr, 0))
-    fatal (NILF, _("CreatePipe() failed (e=%ld)\n"), GetLastError());
-
-  hProcess = process_init_fd(hIn, hChildOutWr, hErr);
+  /* Standard handles returned by GetStdHandle can be NULL or
+     INVALID_HANDLE_VALUE if the parent process closed them.  If that
+     happens, we open the null device and pass its handle to
+     process_begin below as the corresponding handle to inherit.  */
+  tmpIn = GetStdHandle (STD_INPUT_HANDLE);
+  if (DuplicateHandle (GetCurrentProcess (), tmpIn,
+                       GetCurrentProcess (), &hIn,
+                       0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE)
+    {
+      e = GetLastError ();
+      if (e == ERROR_INVALID_HANDLE)
+        {
+          tmpIn = CreateFile ("NUL", GENERIC_READ,
+                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                              OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+          if (tmpIn != INVALID_HANDLE_VALUE
+              && DuplicateHandle (GetCurrentProcess (), tmpIn,
+                                  GetCurrentProcess (), &hIn,
+                                  0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE)
+            CloseHandle (tmpIn);
+        }
+      if (hIn == INVALID_HANDLE_VALUE)
+        {
+          error (NILF, _("windows32_openpipe: DuplicateHandle(In) failed (e=%ld)\n"), e);
+          return -1;
+        }
+    }
+  tmpErr = GetStdHandle (STD_ERROR_HANDLE);
+  if (DuplicateHandle (GetCurrentProcess (), tmpErr,
+                       GetCurrentProcess (), &hErr,
+                       0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE)
+    {
+      e = GetLastError ();
+      if (e == ERROR_INVALID_HANDLE)
+        {
+          tmpErr = CreateFile ("NUL", GENERIC_WRITE,
+                               FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+          if (tmpErr != INVALID_HANDLE_VALUE
+              && DuplicateHandle (GetCurrentProcess (), tmpErr,
+                                  GetCurrentProcess (), &hErr,
+                                  0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE)
+            CloseHandle (tmpErr);
+        }
+      if (hErr == INVALID_HANDLE_VALUE)
+        {
+          error (NILF, _("windows32_openpipe: DuplicateHandle(Err) failed (e=%ld)\n"), e);
+          return -1;
+        }
+    }
+
+  if (! CreatePipe (&hChildOutRd, &hChildOutWr, &saAttr, 0))
+    {
+      error (NILF, _("CreatePipe() failed (e=%ld)\n"), GetLastError());
+      return -1;
+    }
+
+  hProcess = process_init_fd (hIn, hChildOutWr, hErr);
 
   if (!hProcess)
-    fatal (NILF, _("windows32_openpipe(): process_init_fd() failed\n"));
+    {
+      error (NILF, _("windows32_openpipe(): process_init_fd() failed\n"));
+      return -1;
+    }
 
   /* make sure that CreateProcess() has Path it needs */
-  sync_Path_environment();
-  /* `sync_Path_environment' may realloc `environ', so take note of
+  sync_Path_environment ();
+  /* 'sync_Path_environment' may realloc 'environ', so take note of
      the new value.  */
   envp = environ;
 
-  if (!process_begin(hProcess, command_argv, envp, command_argv[0], NULL)) {
-    /* register process for wait */
-    process_register(hProcess);
+  if (! process_begin (hProcess, command_argv, envp, command_argv[0], NULL))
+    {
+      /* register process for wait */
+      process_register (hProcess);
 
-    /* set the pid for returning to caller */
-    *pid_p = (pid_t) hProcess;
+      /* set the pid for returning to caller */
+      *pid_p = (pid_t) hProcess;
 
-  /* set up to read data from child */
-  pipedes[0] = _open_osfhandle((intptr_t) hChildOutRd, O_RDONLY);
+      /* set up to read data from child */
+      pipedes[0] = _open_osfhandle ((intptr_t) hChildOutRd, O_RDONLY);
 
-  /* this will be closed almost right away */
-  pipedes[1] = _open_osfhandle((intptr_t) hChildOutWr, O_APPEND);
-  } else {
-    /* reap/cleanup the failed process */
-       process_cleanup(hProcess);
+      /* this will be closed almost right away */
+      pipedes[1] = _open_osfhandle ((intptr_t) hChildOutWr, O_APPEND);
+      return 0;
+    }
+  else
+    {
+      /* reap/cleanup the failed process */
+      process_cleanup (hProcess);
 
-    /* close handles which were duplicated, they weren't used */
-       CloseHandle(hIn);
-       CloseHandle(hErr);
+      /* close handles which were duplicated, they weren't used */
+      if (hIn != INVALID_HANDLE_VALUE)
+        CloseHandle (hIn);
+      if (hErr != INVALID_HANDLE_VALUE)
+        CloseHandle (hErr);
 
-       /* close pipe handles, they won't be used */
-       CloseHandle(hChildOutRd);
-       CloseHandle(hChildOutWr);
+      /* close pipe handles, they won't be used */
+      CloseHandle (hChildOutRd);
+      CloseHandle (hChildOutWr);
 
-    /* set status for return */
-    pipedes[0] = pipedes[1] = -1;
-    *pid_p = (pid_t)-1;
-  }
+      return -1;
+    }
 }
 #endif
 
@@ -1521,7 +1546,7 @@ FILE *
 msdos_openpipe (int* pipedes, int *pidp, char *text)
 {
   FILE *fpipe=0;
-  /* MSDOS can't fork, but it has `popen'.  */
+  /* MSDOS can't fork, but it has 'popen'.  */
   struct variable *sh = lookup_variable ("SHELL", 5);
   int e;
   extern int dos_command_running, dos_status;
@@ -1536,7 +1561,7 @@ msdos_openpipe (int* pipedes, int *pidp, char *text)
     {
       char buf[PATH_MAX + 7];
       /* This makes sure $SHELL value is used by $(shell), even
-        though the target environment is not passed to it.  */
+         though the target environment is not passed to it.  */
       sprintf (buf, "SHELL=%s", sh->value);
       putenv (buf);
     }
@@ -1555,9 +1580,9 @@ msdos_openpipe (int* pipedes, int *pidp, char *text)
       pipedes[0] = -1;
       *pidp = -1;
       if (dos_status)
-       errno = EINTR;
+        errno = EINTR;
       else if (errno == 0)
-       errno = ENOMEM;
+        errno = ENOMEM;
       shell_function_completed = -1;
     }
   else
@@ -1578,15 +1603,24 @@ msdos_openpipe (int* pipedes, int *pidp, char *text)
 #ifdef VMS
 
 /* VMS can't do $(shell ...)  */
+
+char *
+func_shell_base (char *o, char **argv, int trim_newlines)
+{
+  fprintf (stderr, "This platform does not support shell\n");
+  die (EXIT_FAILURE);
+  return NULL;
+}
+
 #define func_shell 0
 
 #else
 #ifndef _AMIGA
-static char *
-func_shell (char *o, char **argv, const char *funcname UNUSED)
+char *
+func_shell_base (char *o, char **argv, int trim_newlines)
 {
   char *batch_filename = NULL;
-
+  int errfd;
 #ifdef __MSDOS__
   FILE *fpipe;
 #endif
@@ -1597,23 +1631,36 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
   pid_t pid;
 
 #ifndef __MSDOS__
+#ifdef WINDOWS32
+  /* Reset just_print_flag.  This is needed on Windows when batch files
+     are used to run the commands, because we normally refrain from
+     creating batch files under -n.  */
+  int j_p_f = just_print_flag;
+  just_print_flag = 0;
+#endif
+
   /* Construct the argument list.  */
   command_argv = construct_command_argv (argv[0], NULL, NULL, 0,
                                          &batch_filename);
   if (command_argv == 0)
-    return o;
+    {
+#ifdef WINDOWS32
+      just_print_flag = j_p_f;
+#endif
+      return o;
+    }
 #endif
 
-  /* Using a target environment for `shell' loses in cases like:
-     export var = $(shell echo foobie)
-     because target_environment hits a loop trying to expand $(var)
-     to put it in the environment.  This is even more confusing when
-     var was not explicitly exported, but just appeared in the
-     calling environment.
+  /* Using a target environment for 'shell' loses in cases like:
+       export var = $(shell echo foobie)
+       bad := $(var)
+     because target_environment hits a loop trying to expand $(var) to put it
+     in the environment.  This is even more confusing when 'var' was not
+     explicitly exported, but just appeared in the calling environment.
 
      See Savannah bug #10593.
 
-  envp = target_environment (NILF);
+  envp = target_environment (NULL);
   */
 
   envp = environ;
@@ -1628,6 +1675,12 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
   else
     error_prefix = "";
 
+  /* Set up the output in case the shell writes something.  */
+  output_start ();
+
+  errfd = (output_context && output_context->err >= 0
+           ? output_context->err : FD_STDERR);
+
 #if defined(__MSDOS__)
   fpipe = msdos_openpipe (pipedes, &pid, argv[0]);
   if (pipedes[0] < 0)
@@ -1637,11 +1690,14 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
     }
 #elif defined(WINDOWS32)
   windows32_openpipe (pipedes, &pid, command_argv, envp);
+  /* Restore the value of just_print_flag.  */
+  just_print_flag = j_p_f;
+
   if (pipedes[0] < 0)
     {
-      /* open of the pipe failed, mark as failed execution */
+      /* Open of the pipe failed, mark as failed execution.  */
       shell_function_completed = -1;
-
+      perror_with_name (error_prefix, "pipe");
       return o;
     }
   else
@@ -1657,15 +1713,22 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
   CLOSE_ON_EXEC(pipedes[1]);
   CLOSE_ON_EXEC(pipedes[0]);
   /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
-  pid = child_execute_job (0, pipedes[1], command_argv, envp);
+  pid = child_execute_job (FD_STDIN, pipedes[1], errfd, command_argv, envp);
   if (pid < 0)
     perror_with_name (error_prefix, "spawn");
 # else /* ! __EMX__ */
-  pid = vfork ();
+  pid = fork ();
   if (pid < 0)
     perror_with_name (error_prefix, "fork");
   else if (pid == 0)
-    child_execute_job (0, pipedes[1], command_argv, envp);
+    {
+#  ifdef SET_STACK_SIZE
+      /* Reset limits, if necessary.  */
+      if (stack_limit.rlim_cur)
+       setrlimit (RLIMIT_STACK, &stack_limit);
+#  endif
+      child_execute_job (FD_STDIN, pipedes[1], errfd, command_argv, envp);
+    }
   else
 # endif
 #endif
@@ -1685,10 +1748,10 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
       free (command_argv);
 
       /* Close the write side of the pipe.  We test for -1, since
-        pipedes[1] is -1 on MS-Windows, and some versions of MS
-        libraries barf when `close' is called with -1.  */
+         pipedes[1] is -1 on MS-Windows, and some versions of MS
+         libraries barf when 'close' is called with -1.  */
       if (pipedes[1] >= 0)
-       close (pipedes[1]);
+        close (pipedes[1]);
 #endif
 
       /* Set up and read from the pipe.  */
@@ -1698,23 +1761,23 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
 
       /* Read from the pipe until it gets EOF.  */
       for (i = 0; ; i += cc)
-       {
-         if (i == maxlen)
-           {
-             maxlen += 512;
-             buffer = xrealloc (buffer, maxlen + 1);
-           }
-
-         EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
-         if (cc <= 0)
-           break;
-       }
+        {
+          if (i == maxlen)
+            {
+              maxlen += 512;
+              buffer = xrealloc (buffer, maxlen + 1);
+            }
+
+          EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
+          if (cc <= 0)
+            break;
+        }
       buffer[i] = '\0';
 
       /* Close the read side of the pipe.  */
 #ifdef  __MSDOS__
       if (fpipe)
-       (void) pclose (fpipe);
+        (void) pclose (fpipe);
 #else
       (void) close (pipedes[0]);
 #endif
@@ -1722,34 +1785,35 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
       /* Loop until child_handler or reap_children()  sets
          shell_function_completed to the status of our child shell.  */
       while (shell_function_completed == 0)
-       reap_children (1, 0);
+        reap_children (1, 0);
 
-      if (batch_filename) {
-       DB (DB_VERBOSE, (_("Cleaning up temporary batch file %s\n"),
-                       batch_filename));
-       remove (batch_filename);
-       free (batch_filename);
-      }
+      if (batch_filename)
+        {
+          DB (DB_VERBOSE, (_("Cleaning up temporary batch file %s\n"),
+                           batch_filename));
+          remove (batch_filename);
+          free (batch_filename);
+        }
       shell_function_pid = 0;
 
       /* The child_handler function will set shell_function_completed
-        to 1 when the child dies normally, or to -1 if it
-        dies with status 127, which is most likely an exec fail.  */
+         to 1 when the child dies normally, or to -1 if it
+         dies with status 127, which is most likely an exec fail.  */
 
       if (shell_function_completed == -1)
-       {
-         /* This likely means that the execvp failed, so we should just
-            write the error message in the pipe from the child.  */
-         fputs (buffer, stderr);
-         fflush (stderr);
-       }
+        {
+          /* This likely means that the execvp failed, so we should just
+             write the error message in the pipe from the child.  */
+          fputs (buffer, stderr);
+          fflush (stderr);
+        }
       else
-       {
-         /* The child finished normally.  Replace all newlines in its output
-            with spaces, and put that in the variable output buffer.  */
-         fold_newlines (buffer, &i);
-         o = variable_buffer_output (o, buffer, i);
-       }
+        {
+          /* The child finished normally.  Replace all newlines in its output
+             with spaces, and put that in the variable output buffer.  */
+          fold_newlines (buffer, &i, trim_newlines);
+          o = variable_buffer_output (o, buffer, i);
+        }
 
       free (buffer);
     }
@@ -1757,12 +1821,12 @@ func_shell (char *o, char **argv, const char *funcname UNUSED)
   return o;
 }
 
-#else  /* _AMIGA */
+#else   /* _AMIGA */
 
 /* Do the Amiga version of func_shell.  */
 
-static char *
-func_shell (char *o, char **argv, const char *funcname)
+char *
+func_shell_base (char *o, char **argv, int trim_newlines)
 {
   /* Amiga can't fork nor spawn, but I can start a program with
      redirection of my choice.  However, this means that we
@@ -1792,7 +1856,7 @@ func_shell (char *o, char **argv, const char *funcname)
     return o;
 
   /* Note the mktemp() is a security hole, but this only runs on Amiga.
-     Ideally we would use main.c:open_tmpfile(), but this uses a special
+     Ideally we would use output_tmpfile(), but this uses a special
      Open(), not fopen(), and I'm not familiar enough with the code to mess
      with it.  */
   strcpy (tmp_output, "t:MakeshXXXXXXXX");
@@ -1827,33 +1891,39 @@ func_shell (char *o, char **argv, const char *funcname)
   do
     {
       if (i == maxlen)
-       {
-         maxlen += 512;
-         buffer = xrealloc (buffer, maxlen + 1);
-       }
+        {
+          maxlen += 512;
+          buffer = xrealloc (buffer, maxlen + 1);
+        }
 
       cc = Read (child_stdout, &buffer[i], maxlen - i);
       if (cc > 0)
-       i += cc;
+        i += cc;
     } while (cc > 0);
 
   Close (child_stdout);
 
-  fold_newlines (buffer, &i);
+  fold_newlines (buffer, &i, trim_newlines);
   o = variable_buffer_output (o, buffer, i);
   free (buffer);
   return o;
 }
 #endif  /* _AMIGA */
+
+char *
+func_shell (char *o, char **argv, const char *funcname UNUSED)
+{
+  return func_shell_base (o, argv, 1);
+}
 #endif  /* !VMS */
 
 #ifdef EXPERIMENTAL
 
 /*
-  equality. Return is string-boolean, ie, the empty string is false.
+  equality. Return is string-boolean, i.e., the empty string is false.
  */
 static char *
-func_eq (char *o, char **argv, char *funcname)
+func_eq (char *o, char **argv, char *funcname UNUSED)
 {
   int result = ! strcmp (argv[0], argv[1]);
   o = variable_buffer_output (o,  result ? "1" : "", result);
@@ -1865,7 +1935,7 @@ func_eq (char *o, char **argv, char *funcname)
   string-boolean not operator.
  */
 static char *
-func_not (char *o, char **argv, char *funcname)
+func_not (char *o, char **argv, char *funcname UNUSED)
 {
   const char *s = argv[0];
   int result = 0;
@@ -1879,15 +1949,19 @@ func_not (char *o, char **argv, char *funcname)
 \f
 
 #ifdef HAVE_DOS_PATHS
-#define IS_ABSOLUTE(n) (n[0] && n[1] == ':')
-#define ROOT_LEN 3
+# ifdef __CYGWIN__
+#  define IS_ABSOLUTE(n) ((n[0] && n[1] == ':') || STOP_SET (n[0], MAP_PATHSEP))
+# else
+#  define IS_ABSOLUTE(n) (n[0] && n[1] == ':')
+# endif
+# define ROOT_LEN 3
 #else
 #define IS_ABSOLUTE(n) (n[0] == '/')
 #define ROOT_LEN 1
 #endif
 
-/* Return the absolute name of file NAME which does not contain any `.',
-   `..' components nor any repeated path separators ('/').   */
+/* Return the absolute name of file NAME which does not contain any '.',
+   '..' components nor any repeated path separators ('/').   */
 
 static char *
 abspath (const char *name, char *apath)
@@ -1905,50 +1979,54 @@ abspath (const char *name, char *apath)
     {
       /* It is unlikely we would make it until here but just to make sure. */
       if (!starting_directory)
-       return NULL;
+        return NULL;
 
       strcpy (apath, starting_directory);
 
 #ifdef HAVE_DOS_PATHS
-      if (IS_PATHSEP(name[0]))
-       {
-         if (IS_PATHSEP(name[1]))
-           {
-             /* A UNC.  Don't prepend a drive letter.  */
-             apath[0] = name[0];
-             apath[1] = name[1];
-             root_len = 2;
-           }
-         /* We have /foo, an absolute file name except for the drive
-            letter.  Assume the missing drive letter is the current
-            drive, which we can get if we remove from starting_directory
-            everything past the root directory.  */
-         apath[root_len] = '\0';
-       }
+      if (STOP_SET (name[0], MAP_PATHSEP))
+        {
+          if (STOP_SET (name[1], MAP_PATHSEP))
+            {
+              /* A UNC.  Don't prepend a drive letter.  */
+              apath[0] = name[0];
+              apath[1] = name[1];
+              root_len = 2;
+            }
+          /* We have /foo, an absolute file name except for the drive
+             letter.  Assume the missing drive letter is the current
+             drive, which we can get if we remove from starting_directory
+             everything past the root directory.  */
+          apath[root_len] = '\0';
+        }
 #endif
 
       dest = strchr (apath, '\0');
     }
   else
     {
+#if defined(__CYGWIN__) && defined(HAVE_DOS_PATHS)
+      if (STOP_SET (name[0], MAP_PATHSEP))
+       root_len = 1;
+#endif
       strncpy (apath, name, root_len);
       apath[root_len] = '\0';
       dest = apath + root_len;
       /* Get past the root, since we already copied it.  */
       name += root_len;
 #ifdef HAVE_DOS_PATHS
-      if (!IS_PATHSEP(apath[2]))
-       {
-         /* Convert d:foo into d:./foo and increase root_len.  */
-         apath[2] = '.';
-         apath[3] = '/';
-         dest++;
-         root_len++;
-         /* strncpy above copied one character too many.  */
-         name--;
-       }
+      if (! STOP_SET (apath[root_len - 1], MAP_PATHSEP))
+        {
+          /* Convert d:foo into d:./foo and increase root_len.  */
+          apath[2] = '.';
+          apath[3] = '/';
+          dest++;
+          root_len++;
+          /* strncpy above copied one character too many.  */
+          name--;
+        }
       else
-       apath[2] = '/'; /* make sure it's a forward slash */
+        apath[root_len - 1] = '/'; /* make sure it's a forward slash */
 #endif
     }
 
@@ -1957,41 +2035,42 @@ abspath (const char *name, char *apath)
       unsigned long len;
 
       /* Skip sequence of multiple path-separators.  */
-      while (IS_PATHSEP(*start))
-       ++start;
+      while (STOP_SET (*start, MAP_PATHSEP))
+        ++start;
 
       /* Find end of path component.  */
-      for (end = start; *end != '\0' && !IS_PATHSEP(*end); ++end)
+      for (end = start; ! STOP_SET (*end, MAP_PATHSEP|MAP_NUL); ++end)
         ;
 
       len = end - start;
 
       if (len == 0)
-       break;
+        break;
       else if (len == 1 && start[0] == '.')
-       /* nothing */;
+        /* nothing */;
       else if (len == 2 && start[0] == '.' && start[1] == '.')
-       {
-         /* Back up to previous component, ignore if at root already.  */
-         if (dest > apath + root_len)
-           for (--dest; !IS_PATHSEP(dest[-1]); --dest);
-       }
+        {
+          /* Back up to previous component, ignore if at root already.  */
+          if (dest > apath + root_len)
+            for (--dest; ! STOP_SET (dest[-1], MAP_PATHSEP); --dest)
+              ;
+        }
       else
-       {
-         if (!IS_PATHSEP(dest[-1]))
+        {
+          if (! STOP_SET (dest[-1], MAP_PATHSEP))
             *dest++ = '/';
 
-         if (dest + len >= apath_limit)
+          if (dest + len >= apath_limit)
             return NULL;
 
-         dest = memcpy (dest, start, len);
+          dest = memcpy (dest, start, len);
           dest += len;
-         *dest = '\0';
-       }
+          *dest = '\0';
+        }
     }
 
   /* Unless it is root strip trailing separator.  */
-  if (dest > apath + root_len && IS_PATHSEP(dest[-1]))
+  if (dest > apath + root_len && STOP_SET (dest[-1], MAP_PATHSEP))
     --dest;
 
   *dest = '\0';
@@ -2008,30 +2087,35 @@ func_realpath (char *o, char **argv, const char *funcname UNUSED)
   const char *path = 0;
   int doneany = 0;
   unsigned int len = 0;
-#ifndef HAVE_REALPATH
-  struct stat st;
-#endif
-  PATH_VAR (in);
-  PATH_VAR (out);
 
   while ((path = find_next_token (&p, &len)) != 0)
     {
       if (len < GET_PATH_MAX)
         {
+          char *rp;
+          struct stat st;
+          PATH_VAR (in);
+          PATH_VAR (out);
+
           strncpy (in, path, len);
           in[len] = '\0';
 
-          if (
 #ifdef HAVE_REALPATH
-              realpath (in, out)
+          ENULLLOOP (rp, realpath (in, out));
 #else
-              abspath (in, out) && stat (out, &st) == 0
+          rp = abspath (in, out);
 #endif
-             )
+
+          if (rp)
             {
-              o = variable_buffer_output (o, out, strlen (out));
-              o = variable_buffer_output (o, " ", 1);
-              doneany = 1;
+              int r;
+              EINTRLOOP (r, stat (out, &st));
+              if (r == 0)
+                {
+                  o = variable_buffer_output (o, out, strlen (out));
+                  o = variable_buffer_output (o, " ", 1);
+                  doneany = 1;
+                }
             }
         }
     }
@@ -2044,6 +2128,45 @@ func_realpath (char *o, char **argv, const char *funcname UNUSED)
 }
 
 static char *
+func_file (char *o, char **argv, const char *funcname UNUSED)
+{
+  char *fn = argv[0];
+
+  if (fn[0] == '>')
+    {
+      FILE *fp;
+      const char *mode = "w";
+
+      /* We are writing a file.  */
+      ++fn;
+      if (fn[0] == '>')
+        {
+          mode = "a";
+          ++fn;
+        }
+      fn = next_token (fn);
+
+      fp = fopen (fn, mode);
+      if (fp == NULL)
+        fatal (reading_file, _("open: %s: %s"), fn, strerror (errno));
+      else
+        {
+          int l = strlen (argv[1]);
+          int nl = (l == 0 || argv[1][l-1] != '\n');
+
+          if (fputs (argv[1], fp) == EOF || (nl && fputc ('\n', fp) == EOF))
+            fatal (reading_file, _("write: %s: %s"), fn, strerror (errno));
+
+          fclose (fp);
+        }
+    }
+  else
+    fatal (reading_file, _("Invalid file operation: %s"), fn);
+
+  return o;
+}
+
+static char *
 func_abspath (char *o, char **argv, const char *funcname UNUSED)
 {
   /* Expand the argument.  */
@@ -2051,13 +2174,14 @@ func_abspath (char *o, char **argv, const char *funcname UNUSED)
   const char *path = 0;
   int doneany = 0;
   unsigned int len = 0;
-  PATH_VAR (in);
-  PATH_VAR (out);
 
   while ((path = find_next_token (&p, &len)) != 0)
     {
       if (len < GET_PATH_MAX)
         {
+          PATH_VAR (in);
+          PATH_VAR (out);
+
           strncpy (in, path, len);
           in[len] = '\0';
 
@@ -2091,48 +2215,51 @@ func_abspath (char *o, char **argv, const char *funcname UNUSED)
 
 static char *func_call (char *o, char **argv, const char *funcname);
 
+#define FT_ENTRY(_name, _min, _max, _exp, _func) \
+  { { (_func) }, STRING_SIZE_TUPLE(_name), (_min), (_max), (_exp), 0 }
 
 static struct function_table_entry function_table_init[] =
 {
- /* Name/size */                    /* MIN MAX EXP? Function */
-  { STRING_SIZE_TUPLE("abspath"),       0,  1,  1,  func_abspath},
-  { STRING_SIZE_TUPLE("addprefix"),     2,  2,  1,  func_addsuffix_addprefix},
-  { STRING_SIZE_TUPLE("addsuffix"),     2,  2,  1,  func_addsuffix_addprefix},
-  { STRING_SIZE_TUPLE("basename"),      0,  1,  1,  func_basename_dir},
-  { STRING_SIZE_TUPLE("dir"),           0,  1,  1,  func_basename_dir},
-  { STRING_SIZE_TUPLE("notdir"),        0,  1,  1,  func_notdir_suffix},
-  { STRING_SIZE_TUPLE("subst"),         3,  3,  1,  func_subst},
-  { STRING_SIZE_TUPLE("suffix"),        0,  1,  1,  func_notdir_suffix},
-  { STRING_SIZE_TUPLE("filter"),        2,  2,  1,  func_filter_filterout},
-  { STRING_SIZE_TUPLE("filter-out"),    2,  2,  1,  func_filter_filterout},
-  { STRING_SIZE_TUPLE("findstring"),    2,  2,  1,  func_findstring},
-  { STRING_SIZE_TUPLE("firstword"),     0,  1,  1,  func_firstword},
-  { STRING_SIZE_TUPLE("flavor"),        0,  1,  1,  func_flavor},
-  { STRING_SIZE_TUPLE("join"),          2,  2,  1,  func_join},
-  { STRING_SIZE_TUPLE("lastword"),      0,  1,  1,  func_lastword},
-  { STRING_SIZE_TUPLE("patsubst"),      3,  3,  1,  func_patsubst},
-  { STRING_SIZE_TUPLE("realpath"),      0,  1,  1,  func_realpath},
-  { STRING_SIZE_TUPLE("shell"),         0,  1,  1,  func_shell},
-  { STRING_SIZE_TUPLE("sort"),          0,  1,  1,  func_sort},
-  { STRING_SIZE_TUPLE("strip"),         0,  1,  1,  func_strip},
-  { STRING_SIZE_TUPLE("wildcard"),      0,  1,  1,  func_wildcard},
-  { STRING_SIZE_TUPLE("word"),          2,  2,  1,  func_word},
-  { STRING_SIZE_TUPLE("wordlist"),      3,  3,  1,  func_wordlist},
-  { 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("call"),          1,  0,  1,  func_call},
-  { STRING_SIZE_TUPLE("info"),          0,  1,  1,  func_error},
-  { STRING_SIZE_TUPLE("error"),         0,  1,  1,  func_error},
-  { STRING_SIZE_TUPLE("warning"),       0,  1,  1,  func_error},
-  { STRING_SIZE_TUPLE("if"),            2,  3,  0,  func_if},
-  { STRING_SIZE_TUPLE("or"),            1,  0,  0,  func_or},
-  { STRING_SIZE_TUPLE("and"),           1,  0,  0,  func_and},
-  { STRING_SIZE_TUPLE("value"),         0,  1,  1,  func_value},
-  { STRING_SIZE_TUPLE("eval"),          0,  1,  1,  func_eval},
+ /*         Name            MIN MAX EXP? Function */
+  FT_ENTRY ("abspath",       0,  1,  1,  func_abspath),
+  FT_ENTRY ("addprefix",     2,  2,  1,  func_addsuffix_addprefix),
+  FT_ENTRY ("addsuffix",     2,  2,  1,  func_addsuffix_addprefix),
+  FT_ENTRY ("basename",      0,  1,  1,  func_basename_dir),
+  FT_ENTRY ("dir",           0,  1,  1,  func_basename_dir),
+  FT_ENTRY ("notdir",        0,  1,  1,  func_notdir_suffix),
+  FT_ENTRY ("subst",         3,  3,  1,  func_subst),
+  FT_ENTRY ("suffix",        0,  1,  1,  func_notdir_suffix),
+  FT_ENTRY ("filter",        2,  2,  1,  func_filter_filterout),
+  FT_ENTRY ("filter-out",    2,  2,  1,  func_filter_filterout),
+  FT_ENTRY ("findstring",    2,  2,  1,  func_findstring),
+  FT_ENTRY ("firstword",     0,  1,  1,  func_firstword),
+  FT_ENTRY ("flavor",        0,  1,  1,  func_flavor),
+  FT_ENTRY ("join",          2,  2,  1,  func_join),
+  FT_ENTRY ("lastword",      0,  1,  1,  func_lastword),
+  FT_ENTRY ("patsubst",      3,  3,  1,  func_patsubst),
+  FT_ENTRY ("realpath",      0,  1,  1,  func_realpath),
+  FT_ENTRY ("shell",         0,  1,  1,  func_shell),
+  FT_ENTRY ("sort",          0,  1,  1,  func_sort),
+  FT_ENTRY ("strip",         0,  1,  1,  func_strip),
+  FT_ENTRY ("wildcard",      0,  1,  1,  func_wildcard),
+  FT_ENTRY ("word",          2,  2,  1,  func_word),
+  FT_ENTRY ("wordlist",      3,  3,  1,  func_wordlist),
+  FT_ENTRY ("words",         0,  1,  1,  func_words),
+  FT_ENTRY ("origin",        0,  1,  1,  func_origin),
+  FT_ENTRY ("foreach",       3,  3,  0,  func_foreach),
+  FT_ENTRY ("call",          1,  0,  1,  func_call),
+  FT_ENTRY ("info",          0,  1,  1,  func_error),
+  FT_ENTRY ("error",         0,  1,  1,  func_error),
+  FT_ENTRY ("warning",       0,  1,  1,  func_error),
+  FT_ENTRY ("if",            2,  3,  0,  func_if),
+  FT_ENTRY ("or",            1,  0,  0,  func_or),
+  FT_ENTRY ("and",           1,  0,  0,  func_and),
+  FT_ENTRY ("value",         0,  1,  1,  func_value),
+  FT_ENTRY ("eval",          0,  1,  1,  func_eval),
+  FT_ENTRY ("file",          1,  2,  1,  func_file),
 #ifdef EXPERIMENTAL
-  { STRING_SIZE_TUPLE("eq"),            2,  2,  1,  func_eq},
-  { STRING_SIZE_TUPLE("not"),           0,  1,  1,  func_not},
+  FT_ENTRY ("eq",            2,  2,  1,  func_eq),
+  FT_ENTRY ("not",           0,  1,  1,  func_not),
 #endif
 };
 
@@ -2145,23 +2272,38 @@ static char *
 expand_builtin_function (char *o, int argc, char **argv,
                          const struct function_table_entry *entry_p)
 {
+  char *p;
+
   if (argc < (int)entry_p->minimum_args)
     fatal (*expanding_var,
-           _("insufficient number of arguments (%d) to function `%s'"),
+           _("insufficient number of arguments (%d) to function '%s'"),
            argc, entry_p->name);
 
-  /* I suppose technically some function could do something with no
-     arguments, but so far none do, so just test it for all functions here
+  /* I suppose technically some function could do something with no arguments,
+     but so far no internal ones do, so just test it for all functions here
      rather than in each one.  We can change it later if necessary.  */
 
-  if (!argc)
+  if (!argc && !entry_p->alloc_fn)
     return o;
 
-  if (!entry_p->func_ptr)
+  if (!entry_p->fptr.func_ptr)
     fatal (*expanding_var,
-           _("unimplemented on this platform: function `%s'"), entry_p->name);
+           _("unimplemented on this platform: function '%s'"), entry_p->name);
 
-  return entry_p->func_ptr (o, argv, entry_p->name);
+  if (!entry_p->alloc_fn)
+    return entry_p->fptr.func_ptr (o, argv, entry_p->name);
+
+  /* This function allocates memory and returns it to us.
+     Write it to the variable buffer, then free it.  */
+
+  p = entry_p->fptr.alloc_func_ptr (entry_p->name, argc, argv);
+  if (p)
+    {
+      o = variable_buffer_output (o, p, strlen (p));
+      free (p);
+    }
+
+  return o;
 }
 
 /* Check for a function invocation in *STRINGP.  *STRINGP points at the
@@ -2209,8 +2351,8 @@ handle_function (char **op, const char **stringp)
 
   if (count >= 0)
     fatal (*expanding_var,
-          _("unterminated call to function `%s': missing `%c'"),
-          entry_p->name, closeparen);
+           _("unterminated call to function '%s': missing '%c'"),
+           entry_p->name, closeparen);
 
   *stringp = end;
 
@@ -2276,7 +2418,7 @@ handle_function (char **op, const char **stringp)
   if (entry_p->expand_args)
     for (argvp=argv; *argvp != 0; ++argvp)
       free (*argvp);
-  if (abeg)
+  else if (abeg)
     free (abeg);
 
   return 1;
@@ -2303,7 +2445,7 @@ func_call (char *o, char **argv, const char *funcname UNUSED)
   /* There is no way to define a variable with a space in the name, so strip
      leading and trailing whitespace as a favor to the user.  */
   fname = argv[0];
-  while (*fname != '\0' && isspace ((unsigned char)*fname))
+  while (isspace ((unsigned char)*fname))
     ++fname;
 
   cp = fname + strlen (fname) - 1;
@@ -2388,11 +2530,49 @@ func_call (char *o, char **argv, const char *funcname UNUSED)
 }
 
 void
+define_new_function (const gmk_floc *flocp, const char *name,
+                     unsigned int min, unsigned int max, unsigned int flags,
+                     gmk_func_ptr func)
+{
+  const char *e = name;
+  struct function_table_entry *ent;
+  size_t len;
+
+  while (STOP_SET (*e, MAP_USERFUNC))
+    e++;
+  len = e - name;
+
+  if (len == 0)
+    fatal (flocp, _("Empty function name\n"));
+  if (*name == '.' || *e != '\0')
+    fatal (flocp, _("Invalid function name: %s\n"), name);
+  if (len > 255)
+    fatal (flocp, _("Function name too long: %s\n"), name);
+  if (min > 255)
+    fatal (flocp, _("Invalid minimum argument count (%d) for function %s\n"),
+           min, name);
+  if (max > 255 || (max && max < min))
+    fatal (flocp, _("Invalid maximum argument count (%d) for function %s\n"),
+           max, name);
+
+  ent = xmalloc (sizeof (struct function_table_entry));
+  ent->name = name;
+  ent->len = len;
+  ent->minimum_args = min;
+  ent->maximum_args = max;
+  ent->expand_args = ANY_SET(flags, GMK_FUNC_NOEXPAND) ? 0 : 1;
+  ent->alloc_fn = 1;
+  ent->fptr.alloc_func_ptr = func;
+
+  hash_insert (&function_table, ent);
+}
+
+void
 hash_init_function_table (void)
 {
   hash_init (&function_table, FUNCTION_TABLE_ENTRIES * 2,
-            function_table_entry_hash_1, function_table_entry_hash_2,
-            function_table_entry_hash_cmp);
+             function_table_entry_hash_1, function_table_entry_hash_2,
+             function_table_entry_hash_cmp);
   hash_load (&function_table, function_table_init,
-            FUNCTION_TABLE_ENTRIES, sizeof (struct function_table_entry));
+             FUNCTION_TABLE_ENTRIES, sizeof (struct function_table_entry));
 }