summaryrefslogtreecommitdiff
path: root/function.c
diff options
context:
space:
mode:
Diffstat (limited to 'function.c')
-rw-r--r--function.c1132
1 files changed, 656 insertions, 476 deletions
diff --git a/function.c b/function.c
index e2f6c8c..ecce627 100644
--- a/function.c
+++ b/function.c
@@ -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);
}
@@ -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)
#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));
}