make-dfsg/function.c
1993-10-29 21:50:48 +00:00

1230 lines
30 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Variable function expansion for GNU Make.
Copyright (C) 1988, 1989, 1991, 1992, 1993 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 terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Make is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR 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 GNU Make; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "make.h"
#include "variable.h"
#include "dep.h"
#include "commands.h"
#include "job.h"
static char *string_glob ();
/* Store into VARIABLE_BUFFER at O the result of scanning TEXT and replacing
each occurrence of SUBST with REPLACE. TEXT is null-terminated. SLEN is
the length of SUBST and RLEN is the length of REPLACE. If BY_WORD is
nonzero, substitutions are done only on matches which are complete
whitespace-delimited words. If SUFFIX_ONLY is nonzero, substitutions are
done only at the ends of whitespace-delimited words. */
char *
subst_expand (o, text, subst, replace, slen, rlen, by_word, suffix_only)
char *o;
char *text;
char *subst, *replace;
unsigned int slen, rlen;
int by_word, suffix_only;
{
register char *t = text;
register char *p;
if (slen == 0 && !by_word && !suffix_only)
{
/* 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);
return o;
}
while ((p = sindex (t, 0, subst, slen)) != 0)
{
/* Output everything before this occurrence of the string to replace. */
if (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. */
if ((by_word
&& ((p > t && !isblank (p[-1]))
|| (p[slen] != '\0' && !isblank (p[slen]))))
|| (suffix_only
&& (p[slen] != '\0' && !isblank (p[slen]))))
/* 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);
/* Advance T past the string to be replaced. */
t = p + slen;
}
/* Output everything left on the end. */
if (*t != '\0')
o = variable_buffer_output (o, t, strlen (t));
return o;
}
/* Store into VARIABLE_BUFFER at O the result of scanning TEXT
and replacing strings matching PATTERN with REPLACE.
If PATTERN_PERCENT is not nil, PATTERN has already been
run through find_percent, and PATTERN_PERCENT is the result.
If REPLACE_PERCENT is not nil, REPLACE has already been
run through find_percent, and REPLACE_PERCENT is the result. */
char *
patsubst_expand (o, text, pattern, replace, pattern_percent, replace_percent)
char *o;
char *text;
register char *pattern, *replace;
register char *pattern_percent, *replace_percent;
{
register int pattern_prepercent_len, pattern_postpercent_len;
register int replace_prepercent_len, replace_postpercent_len;
register char *t;
unsigned int len;
int doneany = 0;
/* We call find_percent on REPLACE before checking PATTERN so that REPLACE
will be collapsed before we call subst_expand if PATTERN has no %. */
if (replace_percent == 0)
replace_percent = find_percent (replace);
if (replace_percent != 0)
{
/* Record the length of REPLACE before and after the % so
we don't have to compute these lengths more than once. */
replace_prepercent_len = replace_percent - replace;
replace_postpercent_len = strlen (replace_percent + 1);
}
else
/* We store the length of the replacement
so we only need to compute it once. */
replace_prepercent_len = strlen (replace);
if (pattern_percent == 0)
pattern_percent = find_percent (pattern);
if (pattern_percent == 0)
/* With no % in the pattern, this is just a simple substitution. */
return subst_expand (o, text, pattern, replace,
strlen (pattern), strlen (replace), 1, 0);
/* Record the length of PATTERN before and after the %
so we don't have to compute it more than once. */
pattern_prepercent_len = pattern_percent - pattern;
pattern_postpercent_len = strlen (pattern_percent + 1);
while ((t = find_next_token (&text, &len)) != 0)
{
int fail = 0;
/* Is it big enough to match? */
if (len < pattern_prepercent_len + pattern_postpercent_len)
fail = 1;
/* Does the prefix match? */
if (!fail && pattern_prepercent_len > 0
&& (*t != *pattern
|| t[pattern_prepercent_len - 1] != pattern_percent[-1]
|| strncmp (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]
|| t[len - pattern_postpercent_len] != pattern_percent[1]
|| strncmp (&t[len - pattern_postpercent_len],
&pattern_percent[1], pattern_postpercent_len - 1)))
fail = 1;
if (fail)
/* 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 + 1,
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;
}
}
if (doneany)
/* Kill the last space. */
--o;
return o;
}
/* Handle variable-expansion-time functions such as $(dir foo/bar) ==> foo/ */
/* These enumeration constants distinguish the
various expansion-time built-in functions. */
enum function
{
function_subst,
function_addsuffix,
function_addprefix,
function_dir,
function_notdir,
function_suffix,
function_basename,
function_wildcard,
function_firstword,
function_word,
function_words,
function_findstring,
function_strip,
function_join,
function_patsubst,
function_filter,
function_filter_out,
function_foreach,
function_sort,
function_origin,
function_shell,
function_invalid
};
/* Greater than the length of any function name. */
#define MAXFUNCTIONLEN 11
/* The function names and lengths of names, for looking them up. */
static struct
{
char *name;
unsigned int len;
enum function function;
} function_table[] =
{
{ "subst", 5, function_subst },
{ "addsuffix", 9, function_addsuffix },
{ "addprefix", 9, function_addprefix },
{ "dir", 3, function_dir },
{ "notdir", 6, function_notdir },
{ "suffix", 6, function_suffix },
{ "basename", 8, function_basename },
{ "wildcard", 8, function_wildcard },
{ "firstword", 9, function_firstword },
{ "word", 4, function_word },
{ "words", 5, function_words },
{ "findstring", 10, function_findstring },
{ "strip", 5, function_strip },
{ "join", 4, function_join },
{ "patsubst", 8, function_patsubst },
{ "filter", 6, function_filter },
{ "filter-out", 10, function_filter_out },
{ "foreach", 7, function_foreach },
{ "sort", 4, function_sort },
{ "origin", 6, function_origin },
{ "shell", 5, function_shell },
{ 0, 0, function_invalid }
};
/* Return 1 if PATTERN matches WORD, 0 if not. */
int
pattern_matches (pattern, percent, word)
register char *pattern, *percent, *word;
{
unsigned int sfxlen, wordlen;
if (percent == 0)
{
unsigned int len = strlen (pattern) + 1;
char *new = (char *) alloca (len);
bcopy (pattern, new, len);
pattern = new;
percent = find_percent (pattern);
if (percent == 0)
return streq (pattern, word);
}
sfxlen = strlen (percent + 1);
wordlen = strlen (word);
if (wordlen < (percent - pattern) + sfxlen
|| strncmp (pattern, word, percent - pattern))
return 0;
return !strcmp (percent + 1, word + (wordlen - sfxlen));
}
int shell_function_pid = 0, shell_function_completed;
/* Perform the function specified by FUNCTION on the text at TEXT.
END is points to the end of the argument text (exclusive).
The output is written into VARIABLE_BUFFER starting at O. */
/* Note this absorbs a semicolon and is safe to use in conditionals. */
#define BADARGS(func) \
if (reading_filename != 0) \
makefile_fatal (reading_filename, *reading_lineno_ptr, \
"insufficient arguments to function `%s'", \
func); \
else \
fatal ("insufficient arguments to function `%s'", func)
static char *
expand_function (o, function, text, end)
char *o;
enum function function;
char *text;
char *end;
{
char *p, *p2, *p3;
unsigned int i, len;
int doneany = 0;
int count;
char endparen = *end, startparen = *end == ')' ? '(' : '{';
switch (function)
{
default:
abort ();
break;
case function_shell:
{
char **argv, **envp;
char *error_prefix;
int pipedes[2];
int pid;
/* Expand the command line. */
text = expand_argument (text, end);
/* Construct the argument list. */
argv = construct_command_argv (text, (char *) NULL, (struct file *) 0);
if (argv == 0)
break;
/* 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. */
#if 1
envp = environ;
#else
/* Construct the environment. */
envp = target_environment ((struct file *) 0);
#endif
/* For error messages. */
if (reading_filename != 0)
{
error_prefix = (char *) alloca (strlen (reading_filename) + 100);
sprintf (error_prefix,
"%s:%u: ", reading_filename, *reading_lineno_ptr);
}
else
error_prefix = "";
if (pipe (pipedes) < 0)
{
perror_with_name (error_prefix, "pipe");
break;
}
pid = vfork ();
if (pid < 0)
perror_with_name (error_prefix, "fork");
else if (pid == 0)
child_execute_job (0, pipedes[1], argv, envp);
else
{
/* We are the parent. */
char *buffer;
unsigned int maxlen;
int cc;
/* Free the storage only the child needed. */
free (argv[0]);
free ((char *) argv);
#if 0
for (i = 0; envp[i] != 0; ++i)
free (envp[i]);
free ((char *) envp);
#endif
/* Record the PID for reap_children. */
shell_function_pid = pid;
shell_function_completed = 0;
/* Set up and read from the pipe. */
maxlen = 200;
buffer = (char *) xmalloc (maxlen + 1);
/* Close the write side of the pipe. */
(void) close (pipedes[1]);
/* Read from the pipe until it gets EOF. */
i = 0;
do
{
if (i == maxlen)
{
maxlen += 512;
buffer = (char *) xrealloc (buffer, maxlen + 1);
}
errno = 0;
cc = read (pipedes[0], &buffer[i], maxlen - i);
if (cc > 0)
i += cc;
}
#ifdef EINTR
while (cc > 0 || errno == EINTR);
#else
while (cc > 0);
#endif
/* Close the read side of the pipe. */
(void) close (pipedes[0]);
/* Loop until child_handler sets shell_function_completed
to the status of our child shell. */
while (shell_function_completed == 0)
reap_children (1, 0);
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. */
if (shell_function_completed == -1)
{
/* This most likely means that the execvp failed,
so we should just write out the error message
that came in over 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. */
if (i > 0)
{
if (buffer[i - 1] == '\n')
buffer[--i] = '\0';
else
buffer[i] = '\0';
p = buffer;
while ((p = index (p, '\n')) != 0)
*p++ = ' ';
o = variable_buffer_output (o, buffer, i);
}
}
free (buffer);
}
free (text);
break;
}
case function_origin:
/* Expand the argument. */
text = expand_argument (text, end);
{
register struct variable *v = lookup_variable (text, strlen (text));
if (v == 0)
o = variable_buffer_output (o, "undefined", 9);
else
switch (v->origin)
{
default:
case o_invalid:
abort ();
break;
case o_default:
o = variable_buffer_output (o, "default", 7);
break;
case o_env:
o = variable_buffer_output (o, "environment", 11);
break;
case o_file:
o = variable_buffer_output (o, "file", 4);
break;
case o_env_override:
o = variable_buffer_output (o, "environment override", 20);
break;
case o_command:
o = variable_buffer_output (o, "command line", 12);
break;
case o_override:
o = variable_buffer_output (o, "override", 8);
break;
case o_automatic:
o = variable_buffer_output (o, "automatic", 9);
break;
}
}
free (text);
break;
case function_sort:
/* Expand the argument. */
text = expand_argument (text, end);
{
char **words = (char **) xmalloc (10 * sizeof (char *));
unsigned int nwords = 10;
register unsigned int wordi = 0;
char *t;
/* Chop TEXT into words and put them in WORDS. */
t = text;
while ((p = find_next_token (&t, &len)) != 0)
{
if (wordi >= nwords - 1)
{
nwords *= 2;
words = (char **) xrealloc ((char *) words,
nwords * sizeof (char *));
}
words[wordi++] = savestring (p, len);
}
if (wordi > 0)
{
/* Now sort the list of words. */
qsort ((char *) words, wordi, sizeof (char *), alpha_compare);
/* Now write the sorted list. */
for (i = 0; i < wordi; ++i)
{
len = strlen (words[i]);
if (i == wordi - 1 || strlen (words[i + 1]) != len
|| strcmp (words[i], words[i + 1]))
{
o = variable_buffer_output (o, words[i], len);
o = variable_buffer_output (o, " ", 1);
}
free (words[i]);
}
/* Kill the last space. */
--o;
}
free ((char *) words);
}
free (text);
break;
case function_foreach:
{
/* Get three comma-separated arguments but
expand only the first two. */
char *var, *list;
register struct variable *v;
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("foreach");
var = expand_argument (text, p);
p2 = p + 1;
count = 0;
for (p = p2; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("foreach");
list = expand_argument (p2, p);
++p;
text = savestring (p, end - p);
push_new_variable_scope ();
v = define_variable (var, strlen (var), "", o_automatic, 0);
p3 = list;
while ((p = find_next_token (&p3, &len)) != 0)
{
char *result;
char save = p[len];
p[len] = '\0';
v->value = p;
result = allocated_variable_expand (text);
p[len] = save;
o = variable_buffer_output (o, result, strlen (result));
o = variable_buffer_output (o, " ", 1);
doneany = 1;
free (result);
}
if (doneany)
/* Kill the last space. */
--o;
pop_variable_scope ();
free (var);
free (list);
free (text);
}
break;
case function_filter:
case function_filter_out:
{
struct word
{
struct word *next;
char *word;
int matched;
} *words, *wordtail, *wp;
/* Get two comma-separated arguments and expand each one. */
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS (function == function_filter ? "filter" : "filter-out");
p2 = expand_argument (text, p);
text = expand_argument (p + 1, end);
/* Chop TEXT up into words and then run each pattern through. */
words = wordtail = 0;
p3 = text;
while ((p = find_next_token (&p3, &len)) != 0)
{
struct word *w = (struct word *) alloca (sizeof (struct word));
if (words == 0)
words = w;
else
wordtail->next = w;
wordtail = w;
if (*p3 != '\0')
++p3;
p[len] = '\0';
w->word = p;
w->matched = 0;
}
if (words != 0)
{
wordtail->next = 0;
/* Run each pattern through the words, killing words. */
p3 = p2;
while ((p = find_next_token (&p3, &len)) != 0)
{
char *percent;
char save = p[len];
p[len] = '\0';
percent = find_percent (p);
for (wp = words; wp != 0; wp = wp->next)
wp->matched |= (percent == 0 ? streq (p, wp->word)
: pattern_matches (p, percent, wp->word));
p[len] = save;
}
/* Output the words that matched (or didn't, for filter-out). */
for (wp = words; wp != 0; wp = wp->next)
if (function == function_filter ? wp->matched : !wp->matched)
{
o = variable_buffer_output (o, wp->word, strlen (wp->word));
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
if (doneany)
/* Kill the last space. */
--o;
}
free (p2);
free (text);
}
break;
case function_patsubst:
/* Get three comma-separated arguments and expand each one. */
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("patsubst");
p2 = p;
count = 0;
for (++p; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("patsubst");
text = expand_argument (text, p2);
p3 = expand_argument (p2 + 1, p);
p2 = expand_argument (p + 1, end);
o = patsubst_expand (o, p2, text, p3, (char *) 0, (char *) 0);
free (text);
free (p3);
free (p2);
break;
case function_join:
/* Get two comma-separated arguments and expand each one. */
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("join");
text = expand_argument (text, p);
p = expand_argument (p + 1, end);
{
/* Write each word of the first argument directly followed
by the corresponding word of the second argument.
If the two arguments have a different number of words,
the excess words are just output separated by blanks. */
register char *tp, *pp;
p2 = text;
p3 = p;
do
{
unsigned int tlen, plen;
tp = find_next_token (&p2, &tlen);
if (tp != 0)
o = variable_buffer_output (o, tp, tlen);
pp = find_next_token (&p3, &plen);
if (pp != 0)
o = variable_buffer_output (o, pp, plen);
if (tp != 0 || pp != 0)
{
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
}
while (tp != 0 || pp != 0);
if (doneany)
/* Kill the last blank. */
--o;
}
free (text);
free (p);
break;
case function_strip:
/* Expand the argument. */
text = expand_argument (text, end);
p2 = text;
while ((p = find_next_token (&p2, &i)) != 0)
{
o = variable_buffer_output (o, p, i);
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
if (doneany)
/* Kill the last space. */
--o;
free (text);
break;
case function_wildcard:
text = expand_argument (text, end);
p = string_glob (text);
o = variable_buffer_output (o, p, strlen (p));
free (text);
break;
case function_subst:
/* Get three comma-separated arguments and expand each one. */
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("subst");
p2 = p;
count = 0;
for (++p; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("subst");
text = expand_argument (text, p2);
p3 = expand_argument (p2 + 1, p);
p2 = expand_argument (p + 1, end);
o = subst_expand (o, p2, text, p3, strlen (text), strlen (p3), 0, 0);
free (text);
free (p3);
free (p2);
break;
case function_firstword:
/* Expand the argument. */
text = expand_argument (text, end);
/* Find the first word in TEXT. */
p2 = text;
p = find_next_token (&p2, &i);
if (p != 0)
o = variable_buffer_output (o, p, i);
free (text);
break;
case function_word:
/* Get two comma-separated arguments and expand each one. */
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("word");
text = expand_argument (text, p);
p3 = expand_argument (p + 1, end);
/* Check the first argument. */
for (p2 = text; *p2 != '\0'; ++p2)
if (*p2 < '0' || *p2 > '9')
{
if (reading_filename != 0)
makefile_fatal (reading_filename, *reading_lineno_ptr,
"non-numeric first argument to `word' function");
else
fatal ("non-numeric first argument to `word' function");
}
i = (unsigned int) atoi (text);
if (i == 0)
{
if (reading_filename != 0)
makefile_fatal (reading_filename, *reading_lineno_ptr,
"the `word' function takes a one-origin \
index argument");
else
fatal ("the `word' function takes a one-origin index argument");
}
p2 = p3;
while ((p = find_next_token (&p2, &len)) != 0)
if (--i == 0)
break;
if (i == 0)
o = variable_buffer_output (o, p, len);
free (text);
free (p3);
break;
case function_words:
/* Expand the argument. */
text = expand_argument (text, end);
i = 0;
p2 = text;
while (find_next_token (&p2, (unsigned int *) 0) != 0)
++i;
{
char buf[20];
sprintf (buf, "%d", i);
o = variable_buffer_output (o, buf, strlen (buf));
}
free (text);
break;
case function_findstring:
/* Get two comma-separated arguments and expand each one. */
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS ("findstring");
text = expand_argument (text, p);
p = expand_argument (p + 1, end);
/* Find the first occurrence of the first string in the second. */
i = strlen (text);
if (sindex (p, 0, text, i) != 0)
o = variable_buffer_output (o, text, i);
free (p);
free (text);
break;
case function_addsuffix:
case function_addprefix:
/* Get two comma-separated arguments and expand each one. */
count = 0;
for (p = text; p < end; ++p)
{
if (*p == startparen)
++count;
else if (*p == endparen)
--count;
else if (*p == ',' && count <= 0)
break;
}
if (p == end)
BADARGS (function == function_addsuffix ? "addsuffix" : "addprefix");
text = expand_argument (text, p);
i = strlen (text);
p2 = expand_argument (p + 1, end);
p3 = p2;
while ((p = find_next_token (&p3, &len)) != 0)
{
if (function == function_addprefix)
o = variable_buffer_output (o, text, i);
o = variable_buffer_output (o, p, len);
if (function == function_addsuffix)
o = variable_buffer_output (o, text, i);
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
if (doneany)
/* Kill last space. */
--o;
free (p2);
free (text);
break;
case function_dir:
case function_basename:
/* Expand the argument. */
text = expand_argument (text, end);
p3 = text;
while ((p2 = find_next_token (&p3, &len)) != 0)
{
p = p2 + len;
while (p >= p2 && *p != (function == function_dir ? '/' : '.'))
--p;
if (p >= p2)
{
if (function == function_dir)
++p;
o = variable_buffer_output (o, p2, p - p2);
}
else if (function == function_dir)
o = variable_buffer_output (o, "./", 2);
else
/* The entire name is the basename. */
o = variable_buffer_output (o, p2, len);
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
if (doneany)
/* Kill last space. */
--o;
free (text);
break;
case function_notdir:
case function_suffix:
/* Expand the argument. */
text = expand_argument (text, end);
p3 = text;
while ((p2 = find_next_token (&p3, &len)) != 0)
{
p = p2 + len;
while (p >= p2 && *p != (function == function_notdir ? '/' : '.'))
--p;
if (p >= p2)
{
if (function == function_notdir)
++p;
o = variable_buffer_output (o, p, len - (p - p2));
}
else if (function == function_notdir)
o = variable_buffer_output (o, p2, len);
if (function == function_notdir || p >= p2)
{
o = variable_buffer_output (o, " ", 1);
doneany = 1;
}
}
if (doneany)
/* Kill last space. */
--o;
free (text);
break;
}
return o;
}
/* Check for a function invocation in *STRINGP. *STRINGP points at the
opening ( or { and is not null-terminated. If a function invocation
is found, expand it into the buffer at *OP, updating *OP, incrementing
*STRINGP past the reference and returning nonzero. If not, return zero. */
int
handle_function (op, stringp)
char **op;
char **stringp;
{
register unsigned int code;
unsigned int maxlen;
char *beg = *stringp + 1;
char *endref;
endref = lindex (beg, beg + MAXFUNCTIONLEN, '\0');
maxlen = endref != 0 ? endref - beg : MAXFUNCTIONLEN;
for (code = 0; function_table[code].name != 0; ++code)
{
if (maxlen < function_table[code].len)
continue;
endref = beg + function_table[code].len;
if (isblank (*endref)
&& !strncmp (function_table[code].name, beg,
function_table[code].len))
break;
}
if (function_table[code].name != 0)
{
/* We have found a call to an expansion-time function.
Find the end of the arguments, and do the function. */
char openparen = beg[-1], closeparen = openparen == '(' ? ')' : '}';
int count = 0;
char *argbeg;
register char *p;
/* Space after function name isn't part of the args. */
p = next_token (endref);
argbeg = p;
/* Count nested use of whichever kind of parens we use,
so that nested calls and variable refs work. */
for (; *p != '\0'; ++p)
{
if (*p == openparen)
++count;
else if (*p == closeparen && --count < 0)
break;
}
if (count >= 0)
{
static const char errmsg[]
= "unterminated call to function `%s': missing `%c'";
if (reading_filename == 0)
fatal (errmsg, function_table[code].name, closeparen);
else
makefile_fatal (reading_filename, *reading_lineno_ptr, errmsg,
function_table[code].name, closeparen);
}
/* We found the end; expand the function call. */
*op = expand_function (*op, function_table[code].function, argbeg, p);
*stringp = p;
return 1;
}
return 0;
}
/* Glob-expand LINE. The returned pointer is
only good until the next call to string_glob. */
static char *
string_glob (line)
char *line;
{
static char *result = 0;
static unsigned int length;
register struct nameseq *chain;
register unsigned int idx;
chain = multi_glob (parse_file_seq
(&line, '\0', sizeof (struct nameseq),
/* We do not want parse_file_seq to strip `./'s.
That would break examples like:
$(patsubst ./%.c,obj/%.o,$(wildcard ./*.c)). */
0),
sizeof (struct nameseq));
if (result == 0)
{
length = 100;
result = (char *) xmalloc (100);
}
idx = 0;
while (chain != 0)
{
register char *name = chain->name;
unsigned int len = strlen (name);
struct nameseq *next = chain->next;
free ((char *) chain);
chain = next;
/* multi_glob will pass names without globbing metacharacters
through as is, but we want only files that actually exist. */
if (file_exists_p (name))
{
if (idx + len + 1 > length)
{
length += (len + 1) * 2;
result = (char *) xrealloc (result, length);
}
bcopy (name, &result[idx], len);
idx += len;
result[idx++] = ' ';
}
free (name);
}
/* Kill the last space and terminate the string. */
if (idx == 0)
result[0] = '\0';
else
result[idx - 1] = '\0';
return result;
}