summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog37
-rw-r--r--NEWS6
-rw-r--r--expand.c2
-rw-r--r--function.c263
-rw-r--r--job.c4
-rw-r--r--tests/ChangeLog6
-rw-r--r--tests/scripts/functions/call16
7 files changed, 208 insertions, 126 deletions
diff --git a/ChangeLog b/ChangeLog
index 9dbdb6b..175081c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2000-01-11 Paul D. Smith <psmith@gnu.org>
+
+ Resolve PR/xxxx: don't automatically evaluate the $(call ...)
+ function's arguments. While we're here, clean up argument passing
+ protocol to always use simple nul-terminated strings, instead of
+ sometimes using offset pointers to mark the end of arguments.
+ This change also fixes PR/1517.
+ Both PR's by Damien GIBOU <damien.gibou@st.com>.
+
+ * function.c (struct function_table_entry): Remove the negative
+ required_args hack; put in explicit min and max # of arguments.
+ (function_table): Add in the max value. Turn off the expand bit
+ for func_call.
+ (expand_builtin_function): Test against minimum_args instead of
+ the obsolete required_args.
+ (handle_function): Rewrite this. We don't try to be fancy and
+ pass one style of arguments to expanded functions and another
+ style to non-expanded functions: pass pointers to nul-terminated
+ strings to all functions.
+ (func_call): Rewrite this. If we are invoking a builtin function
+ and it's supposed to have its arguments expanded, do that (since
+ it's not done by handle_function for $(call ...) anymore). For
+ non-builtins, just add the variables as before but mark them as
+ recursive so they'll be expanded later, as needed.
+ (func_if): All arguments are vanilla nul-terminated strings:
+ remove trickery with "argv[1]-1".
+ (func_foreach): Ditto.
+
+ * expand.c (expand_argument): If the second arg is NULL, expand
+ the entire first argument.
+
+ * job.c (new_job): Zero the child struct. This change was just
+ made to keep some heap checking software happy, not because there
+ was an actual bug (the important memory was being cleared properly).
+
1999-12-15 Paul D. Smith <psmith@gnu.org>
* variable.c (print_variable): Print the variable with += if the
@@ -12,7 +47,7 @@
* dir.c (dir_setup_glob): On 64 bit ReliantUNIX (5.44 and above)
in LFS mode, stat() is actually a macro for stat64(). Assignment
- doesn't work in that case. So, if stat() is a macro, make a local
+ doesn't work in that case. So, stat is a macro, make a local
wrapper function to invoke it.
(local_stat): Wrapper function, if needed.
Reported by Andrej Borsenkow <Andrej.Borsenkow@mow.siemens.ru>.
diff --git a/NEWS b/NEWS
index 628b478..0b96212 100644
--- a/NEWS
+++ b/NEWS
@@ -32,6 +32,12 @@ Version 3.79
makefile is always run serially regardless of the value of -j. Any
submakes will still be run in parallel if -j was specified.
+* The $(call ...) function doesn't expand its arguments automatically
+ anymore. This allows you to put builtin functions like "if" and
+ "foreach", which also have special expansion rules, in a $(call ...)
+ function and have it work properly. This was suggested by Damien
+ GIBOU <damien.gibou@st.com>.
+
* The -d (--debug) option has changed: it now allows optional flags
controlling the amount and type of debugging output. By default only
a minimal amount information is generated, displaying the names of
diff --git a/expand.c b/expand.c
index 0387368..a7cfcdb 100644
--- a/expand.c
+++ b/expand.c
@@ -424,7 +424,7 @@ expand_argument (str, end)
{
char *tmp;
- if (*end == '\0')
+ if (!end || *end == '\0')
tmp = str;
else
{
diff --git a/function.c b/function.c
index 90ab76a..ca66b91 100644
--- a/function.c
+++ b/function.c
@@ -33,9 +33,10 @@ Boston, MA 02111-1307, USA. */
struct function_table_entry
{
const char *name;
- int len;
- int required_args;
- int expand_args;
+ unsigned char len;
+ unsigned char minimum_args;
+ unsigned char maximum_args;
+ char expand_args;
char *(*func_ptr) PARAMS((char *output, char **argv, const char*funcname));
};
@@ -815,9 +816,9 @@ func_foreach (o, argv, funcname)
const char *funcname;
{
/* expand only the first two. */
- char *varname = expand_argument (argv[0], argv[1] - 1);
- char *list = expand_argument (argv[1], argv[2] -1);
- char *body = savestring (argv[2], argv[3] - argv[2] - 1);
+ char *varname = expand_argument (argv[0], NULL);
+ char *list = expand_argument (argv[1], NULL);
+ char *body = argv[2];
int doneany = 0;
char *list_iterator = list;
@@ -857,7 +858,6 @@ func_foreach (o, argv, funcname)
pop_variable_scope ();
free (varname);
free (list);
- free (body);
return o;
}
@@ -1090,7 +1090,7 @@ func_if (o, argv, funcname)
const char *funcname;
{
char *begp = argv[0];
- char *endp = argv[1]-1;
+ char *endp = begp + strlen (argv[0]);
int result = 0;
/* Find the result of the condition: if we have a value, and it's not
@@ -1101,7 +1101,7 @@ func_if (o, argv, funcname)
if (begp < endp)
{
- char *expansion = expand_argument (begp, endp);
+ char *expansion = expand_argument (begp, NULL);
result = strlen (expansion);
free (expansion);
@@ -1113,20 +1113,14 @@ func_if (o, argv, funcname)
argv += 1 + !result;
- if (argv[0] != NULL && argv[1] != NULL)
+ if (argv[0])
{
char *expansion;
- char **argend = argv+1;
- /* If we're doing the else-clause, make sure we concatenate any
- potential extra arguments into the last argument. */
- if (!result)
- while (argend[1])
- ++argend;
-
- expansion = expand_argument (*argv, *argend-1);
+ expansion = expand_argument (argv[0], NULL);
o = variable_buffer_output (o, expansion, strlen (expansion));
+
free (expansion);
}
@@ -1634,11 +1628,8 @@ func_not (char* o, char **argv, char *funcname)
some efficiency by moving most often used functions to the start of the
table.
- If REQUIRED_ARGS is positive, the function takes exactly that many
- arguments. All subsequent text is included with the last argument. So,
- since $(sort a,b,c) takes only one argument, it will be the full string
- "a,b,c". If the value is negative, it's the minimum number of arguments.
- A function can have more, but if it has less an error is generated.
+ If MAXIMUM_ARGS is 0, that means there is no maximum and all
+ comma-separated values are treated as arguments.
EXPAND_ARGS means that all arguments should be expanded before invocation.
Functions that do namespace tricks (foreach) don't automatically expand. */
@@ -1648,36 +1639,36 @@ static char *func_call PARAMS((char *o, char **argv, const char *funcname));
static struct function_table_entry function_table[] =
{
- /* Name/size */ /* ARG EXP? Function */
- { STRING_SIZE_TUPLE("addprefix"), 2, 1, func_addsuffix_addprefix},
- { STRING_SIZE_TUPLE("addsuffix"), 2, 1, func_addsuffix_addprefix},
- { STRING_SIZE_TUPLE("basename"), 1, 1, func_basename_dir},
- { STRING_SIZE_TUPLE("dir"), 1, 1, func_basename_dir},
- { STRING_SIZE_TUPLE("notdir"), 1, 1, func_notdir_suffix},
- { STRING_SIZE_TUPLE("subst"), 3, 1, func_subst},
- { STRING_SIZE_TUPLE("suffix"), 1, 1, func_notdir_suffix},
- { STRING_SIZE_TUPLE("filter"), 2, 1, func_filter_filterout},
- { STRING_SIZE_TUPLE("filter-out"), 2, 1, func_filter_filterout},
- { STRING_SIZE_TUPLE("findstring"), 2, 1, func_findstring},
- { STRING_SIZE_TUPLE("firstword"), 1, 1, func_firstword},
- { STRING_SIZE_TUPLE("join"), 2, 1, func_join},
- { STRING_SIZE_TUPLE("patsubst"), 3, 1, func_patsubst},
- { STRING_SIZE_TUPLE("shell"), 1, 1, func_shell},
- { STRING_SIZE_TUPLE("sort"), 1, 1, func_sort},
- { STRING_SIZE_TUPLE("strip"), 1, 1, func_strip},
- { STRING_SIZE_TUPLE("wildcard"), 1, 1, func_wildcard},
- { STRING_SIZE_TUPLE("word"), 2, 1, func_word},
- { STRING_SIZE_TUPLE("wordlist"), 3, 1, func_wordlist},
- { STRING_SIZE_TUPLE("words"), 1, 1, func_words},
- { STRING_SIZE_TUPLE("origin"), 1, 1, func_origin},
- { STRING_SIZE_TUPLE("foreach"), 3, 0, func_foreach},
- { STRING_SIZE_TUPLE("call"), -1, 1, func_call},
- { STRING_SIZE_TUPLE("error"), 1, 1, func_error},
- { STRING_SIZE_TUPLE("warning"), 1, 1, func_error},
- { STRING_SIZE_TUPLE("if"), -2, 0, func_if},
+ /* Name/size */ /* MIN MAX EXP? Function */
+ { STRING_SIZE_TUPLE("addprefix"), 2, 2, 1, func_addsuffix_addprefix},
+ { STRING_SIZE_TUPLE("addsuffix"), 2, 2, 1, func_addsuffix_addprefix},
+ { STRING_SIZE_TUPLE("basename"), 1, 1, 1, func_basename_dir},
+ { STRING_SIZE_TUPLE("dir"), 1, 1, 1, func_basename_dir},
+ { STRING_SIZE_TUPLE("notdir"), 1, 1, 1, func_notdir_suffix},
+ { STRING_SIZE_TUPLE("subst"), 3, 3, 1, func_subst},
+ { STRING_SIZE_TUPLE("suffix"), 1, 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"), 1, 1, 1, func_firstword},
+ { STRING_SIZE_TUPLE("join"), 2, 2, 1, func_join},
+ { STRING_SIZE_TUPLE("patsubst"), 3, 3, 1, func_patsubst},
+ { STRING_SIZE_TUPLE("shell"), 1, 1, 1, func_shell},
+ { STRING_SIZE_TUPLE("sort"), 1, 1, 1, func_sort},
+ { STRING_SIZE_TUPLE("strip"), 1, 1, 1, func_strip},
+ { STRING_SIZE_TUPLE("wildcard"), 1, 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"), 1, 1, 1, func_words},
+ { STRING_SIZE_TUPLE("origin"), 1, 1, 1, func_origin},
+ { STRING_SIZE_TUPLE("foreach"), 3, 3, 0, func_foreach},
+ { STRING_SIZE_TUPLE("call"), 1, 0, 0, func_call},
+ { STRING_SIZE_TUPLE("error"), 1, 1, 1, func_error},
+ { STRING_SIZE_TUPLE("warning"), 1, 1, 1, func_error},
+ { STRING_SIZE_TUPLE("if"), 2, 3, 0, func_if},
#ifdef EXPERIMENTAL
- { STRING_SIZE_TUPLE("eq"), 2, 1, func_eq},
- { STRING_SIZE_TUPLE("not"), 1, 1, func_not},
+ { STRING_SIZE_TUPLE("eq"), 2, 2, 1, func_eq},
+ { STRING_SIZE_TUPLE("not"), 1, 1, 1, func_not},
#endif
{ 0 }
};
@@ -1692,11 +1683,7 @@ expand_builtin_function (o, argc, argv, entry_p)
char **argv;
struct function_table_entry *entry_p;
{
- int min = (entry_p->required_args > 0
- ? entry_p->required_args
- : -entry_p->required_args);
-
- if (argc < min)
+ if (argc < entry_p->minimum_args)
fatal (reading_file,
_("Insufficient number of arguments (%d) to function `%s'"),
argc, entry_p->name);
@@ -1721,39 +1708,36 @@ handle_function (op, stringp)
const struct function_table_entry *entry_p;
char openparen = (*stringp)[0];
char closeparen = openparen == '(' ? ')' : '}';
- char *beg = *stringp + 1;
- char *endref;
+ char *beg;
+ char *end;
int count = 0;
- char *argbeg;
register char *p;
char **argv, **argvp;
int nargs;
+ beg = *stringp + 1;
+
entry_p = lookup_function (function_table, beg);
if (!entry_p)
return 0;
- /* We have found a call to a builtin function. Find the end of the
- arguments, and do the function. */
-
- endref = beg + entry_p->len;
+ /* We found a builtin function. Find the beginning of its arguments (skip
+ whitespace after the name). */
- /* Space after function name isn't part of the args. */
- p = next_token (endref);
- argbeg = p;
+ beg = next_token (beg + entry_p->len);
/* Find the end of the function invocation, counting nested use of
whichever kind of parens we use. Since we're looking, count commas
to get a rough estimate of how many arguments we might have. The
count might be high, but it'll never be low. */
- for (nargs=1; *p != '\0'; ++p)
- if (*p == ',')
+ for (nargs=1, end=beg; *end != '\0'; ++end)
+ if (*end == ',')
++nargs;
- else if (*p == openparen)
+ else if (*end == openparen)
++count;
- else if (*p == closeparen && --count < 0)
+ else if (*end == closeparen && --count < 0)
break;
if (count >= 0)
@@ -1761,47 +1745,63 @@ handle_function (op, stringp)
_("unterminated call to function `%s': missing `%c'"),
entry_p->name, closeparen);
+ *stringp = end;
+
/* Get some memory to store the arg pointers. */
argvp = argv = (char **) alloca (sizeof(char *) * (nargs + 2));
- /* Chop the string into arguments, then store the end pointer and a nul.
- If REQUIRED_ARGS is positive, as soon as we hit that many assume the
- rest of the string is part of the last argument. */
- *argvp = argbeg;
- nargs = 1;
- while (entry_p->required_args < 0 || nargs < entry_p->required_args)
+ /* Chop the string into arguments, then a nul. As soon as we hit
+ MAXIMUM_ARGS (if it's >0) assume the rest of the string is part of the
+ last argument.
+
+ If we're expanding, store pointers to the expansion of each one. If
+ not, make a duplicate of the string and point into that, nul-terminating
+ each argument. */
+
+ if (!entry_p->expand_args)
{
- char *next = find_next_argument (openparen, closeparen, *argvp, p);
+ int len = end - beg;
- if (!next)
- break;
+ p = xmalloc (len+1);
+ memcpy (p, beg, len);
+ p[len] = '\0';
+ beg = p;
+ end = beg + len;
+ }
+
+ p = beg;
+ nargs = 0;
+ for (p=beg, nargs=0; p < end; ++argvp)
+ {
+ char *next;
- *(++argvp) = next+1;
++nargs;
- }
- *(++argvp) = p+1;
- *(++argvp) = 0;
+ if (nargs == entry_p->maximum_args
+ || (! (next = find_next_argument (openparen, closeparen, p, end))))
+ next = end;
- /* If we should expand, do it. */
- if (entry_p->expand_args)
- {
- for (argvp=argv; argvp[1] != 0; ++argvp)
- *argvp = expand_argument (*argvp, argvp[1]-1);
+ if (entry_p->expand_args)
+ *argvp = expand_argument (p, next);
+ else
+ {
+ *argvp = p;
+ *next = '\0';
+ }
- /* end pointer doesn't make sense for expanded stuff. */
- *argvp = 0;
+ p = next + 1;
}
+ *argvp = NULL;
/* Finally! Run the function... */
*op = expand_builtin_function (*op, nargs, argv, entry_p);
- /* If we allocated memory for the expanded args, free it again. */
+ /* Free memory. */
if (entry_p->expand_args)
for (argvp=argv; *argvp != 0; ++argvp)
free (*argvp);
-
- *stringp = p;
+ else
+ free (beg);
return 1;
}
@@ -1818,47 +1818,73 @@ func_call (o, argv, funcname)
const char *funcname;
{
char *fname;
+ char *cp;
int flen;
char *body;
int i;
const struct function_table_entry *entry_p;
- /* Calling nothing is a no-op. */
- if (*argv[0] == '\0')
- return o;
+ fname = expand_argument (argv[0], NULL);
/* There is no way to define a variable with a space in the name, so strip
- trailing whitespace as a favor to the user. */
-
- flen = strlen (argv[0]);
- fname = argv[0] + flen - 1;
- while (isspace ((unsigned char)*fname))
- --fname;
- fname[1] = '\0';
-
- flen = fname - argv[0] + 1;
- fname = argv[0];
+ leading and trailing whitespace as a favor to the user. */
+ cp = fname;
+ while (*cp != '\0' && isspace ((unsigned char)*cp))
+ ++cp;
+ argv[0] = cp;
+
+ cp += strlen(cp) - 1;
+ while (cp > argv[0] && isspace ((unsigned char)*cp))
+ --cp;
+ cp[1] = '\0';
+
+ /* Calling nothing is a no-op */
+ if (*argv[0] == '\0')
+ {
+ free (fname);
+ return o;
+ }
/* Are we invoking a builtin function? */
- entry_p = lookup_function (function_table, fname);
+ entry_p = lookup_function (function_table, argv[0]);
if (entry_p)
{
+ char **av;
+
+ free (fname);
+
+ /* How many arguments do we have? */
for (i=0; argv[i+1]; ++i)
- ;
+ ;
+
+ /* If we need to expand arguments, do it now. */
+ if (entry_p->expand_args)
+ for (av=argv+1; *av; ++av)
+ *av = expand_argument (*av, NULL);
+
+ o = expand_builtin_function (o, i, argv+1, entry_p);
- return expand_builtin_function (o, i, argv + 1, entry_p);
+ /* What we expanded we must free... */
+ if (entry_p->expand_args)
+ for (av=argv+1; *av; ++av)
+ free (*av);
+
+ return o;
}
- /* No, so the first argument is the name of a variable to be expanded and
- interpreted as a function. Create the variable reference. */
+ /* Not a builtin, so the first argument is the name of a variable to be
+ expanded and interpreted as a function. Create the variable
+ reference. */
+ flen = strlen (argv[0]);
+
body = alloca (flen + 4);
- body[0]='$';
- body[1]='(';
- strcpy (body + 2, fname);
- body[flen+2]=')';
- body[flen+3]= '\0';
+ body[0] = '$';
+ body[1] = '(';
+ memcpy (body + 2, fname, flen);
+ body[flen+2] = ')';
+ body[flen+3] = '\0';
/* Set up arguments $(1) .. $(N). $(0) is the function name. */
@@ -1869,7 +1895,7 @@ func_call (o, argv, funcname)
char num[11];
sprintf (num, "%d", i);
- define_variable (num, strlen (num), *argv, o_automatic, 0);
+ define_variable (num, strlen (num), *argv, o_automatic, 1);
}
/* Expand the body in the context of the arguments, adding the result to
@@ -1879,5 +1905,6 @@ func_call (o, argv, funcname)
pop_variable_scope ();
+ free (fname);
return o + strlen(o);
}
diff --git a/job.c b/job.c
index e79b6a7..c920b9f 100644
--- a/job.c
+++ b/job.c
@@ -1341,11 +1341,9 @@ new_job (file)
`struct child', and add that to the chain. */
c = (struct child *) xmalloc (sizeof (struct child));
+ bzero ((char *)c, sizeof (struct child));
c->file = file;
c->command_lines = lines;
- c->command_line = 0;
- c->command_ptr = 0;
- c->environment = 0;
c->sh_batch_file = NULL;
/* Fetch the first command line to be run. */
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 5a6ba71..e613d8d 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,9 @@
+2000-01-11 Paul D. Smith <psmith@gnu.org>
+
+ * scripts/functions/call: Add a test for PR/1517 and PR/1527: make
+ sure $(call ...) doesn't eval its arguments and that you can
+ invoke foreach from it without infinitely looping.
+
1999-12-15 Paul D. Smith <psmith@gnu.org>
* scripts/targets/INTERMEDIATE: Add a test for PR/1423: make sure
diff --git a/tests/scripts/functions/call b/tests/scripts/functions/call
index 3303d5b..8b6aa36 100644
--- a/tests/scripts/functions/call
+++ b/tests/scripts/functions/call
@@ -9,7 +9,7 @@ open(MAKEFILE, "> $makefile");
# The Contents of the MAKEFILE ...
print MAKEFILE <<'EOMAKE';
-# Simple, just reverse something
+# Simple, just reverse two things
#
reverse = $2 $1
@@ -21,9 +21,19 @@ map = $(foreach a,$2,$(call $1,$a))
#
my-notdir = $(call notdir,$(1))
+# Test using non-expanded builtins
+#
+my-foreach = $(foreach $(1),$(2),$(3))
+my-if = $(if $(1),$(2),$(3))
+
all: ; @echo '$(call reverse,bar,foo)'; \
echo '$(call map,origin,MAKE reverse map)'; \
- echo '$(call my-notdir,a/b c/d e/f)'
+ echo '$(call my-notdir,a/b c/d e/f)'; \
+ echo '$(call my-foreach)'; \
+ echo '$(call my-foreach,a,,,)'; \
+ echo '$(call my-foreach,a,x y z,$(a)$(a))'; \
+ echo '$(call my-if,a,b,c)'; \
+ echo '$(call my-if,,$(warning don't print this),ok)'
EOMAKE
@@ -32,7 +42,7 @@ EOMAKE
close(MAKEFILE);
&run_make_with_options($makefile, "", &get_logfile);
-$answer = "foo bar\ndefault file file\nb d f\n";
+$answer = "foo bar\ndefault file file\nb d f\n\n\nxx yy zz\nb\nok\n";
&compare_output($answer, &get_logfile(1));
1;