diff options
Diffstat (limited to 'function.c')
-rw-r--r-- | function.c | 1132 |
1 files changed, 656 insertions, 476 deletions
@@ -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)); } |