#include "make.h"
#include <assert.h>
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"
const struct floc **expanding_var = &reading_file;
#define VARIABLE_BUFFER_ZONE 5
static unsigned int variable_buffer_length;
char *variable_buffer;
char *
variable_buffer_output (char *ptr, char *string, unsigned int length)
{
register unsigned int newlen = length + (ptr - variable_buffer);
if ((newlen + VARIABLE_BUFFER_ZONE) > variable_buffer_length)
{
unsigned int offset = ptr - variable_buffer;
variable_buffer_length = (newlen + 100 > 2 * variable_buffer_length
? newlen + 100
: 2 * variable_buffer_length);
variable_buffer = (char *) xrealloc (variable_buffer,
variable_buffer_length);
ptr = variable_buffer + offset;
}
bcopy (string, ptr, length);
return ptr + length;
}
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
char *
#else
static char *
#endif
initialize_variable_output (void)
{
if (variable_buffer == 0)
{
variable_buffer_length = 200;
variable_buffer = (char *) xmalloc (variable_buffer_length);
variable_buffer[0] = '\0';
}
return variable_buffer;
}
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
char *
save_variable_output(savelenp)
int *savelenp;
{
char *save;
save = variable_buffer;
*savelenp = variable_buffer_length;
variable_buffer = 0;
variable_buffer_length = 0;
return (save);
}
void
restore_variable_output (save, savelen)
char *save;
int savelen;
{
if (variable_buffer != 0)
free (variable_buffer);
variable_buffer = save;
variable_buffer_length = savelen;
}
#endif
static char *allocated_variable_append PARAMS ((const struct variable *v));
char *
recursively_expand_for_file (struct variable *v, struct file *file)
{
char *value;
const struct floc *this_var;
const struct floc **saved_varp;
struct variable_set_list *save = 0;
int set_reading = 0;
saved_varp = expanding_var;
if (v->fileinfo.filenm)
{
this_var = &v->fileinfo;
expanding_var = &this_var;
}
if (!reading_file)
{
set_reading = 1;
reading_file = &v->fileinfo;
}
if (v->expanding)
{
if (!v->exp_count)
fatal (*expanding_var,
_("Recursive variable `%s' references itself (eventually)"),
v->name);
--v->exp_count;
}
if (file)
{
save = current_variable_set_list;
current_variable_set_list = file->variables;
}
v->expanding = 1;
if (v->append)
value = allocated_variable_append (v);
else
value = allocated_variable_expand (v->value);
v->expanding = 0;
if (set_reading)
reading_file = 0;
if (file)
current_variable_set_list = save;
expanding_var = saved_varp;
return value;
}
#ifdef __GNUC__
__inline
#endif
static char *
reference_variable (char *o, char *name, unsigned int length)
{
register struct variable *v;
char *value;
v = lookup_variable (name, length);
if (v == 0)
warn_undefined (name, length);
if (v == 0 || (*v->value == '\0' && !v->append))
return o;
value = (v->recursive ? recursively_expand (v) : v->value);
o = variable_buffer_output (o, value, strlen (value));
if (v->recursive)
free (value);
return o;
}
char *
variable_expand_string (char *line, char *string, long length)
{
register struct variable *v;
register char *p, *o, *p1;
char save_char = '\0';
unsigned int line_offset;
if (!line)
line = initialize_variable_output();
p = string;
o = line;
line_offset = line - variable_buffer;
if (length >= 0)
{
save_char = string[length];
string[length] = '\0';
}
while (1)
{
p1 = strchr (p, '$');
o = variable_buffer_output (o, p, p1 != 0 ? (unsigned int)(p1 - p) : strlen (p) + 1);
if (p1 == 0)
break;
p = p1 + 1;
switch (*p)
{
case '$':
o = variable_buffer_output (o, p, 1);
break;
case '(':
case '{':
{
char openparen = *p;
char closeparen = (openparen == '(') ? ')' : '}';
register char *beg = p + 1;
int free_beg = 0;
char *op, *begp;
char *end, *colon;
op = o;
begp = p;
if (handle_function (&op, &begp))
{
o = op;
p = begp;
break;
}
end = strchr (beg, closeparen);
if (end == 0)
fatal (*expanding_var, _("unterminated variable reference"));
p1 = lindex (beg, end, '$');
if (p1 != 0)
{
int count = 0;
for (p = beg; *p != '\0'; ++p)
{
if (*p == openparen)
++count;
else if (*p == closeparen && --count < 0)
break;
}
if (count < 0)
{
beg = expand_argument (beg, p);
free_beg = 1;
end = strchr (beg, '\0');
}
}
else
p = end;
colon = lindex (beg, end, ':');
if (colon)
{
char *subst_beg, *subst_end, *replace_beg, *replace_end;
subst_beg = colon + 1;
subst_end = lindex (subst_beg, end, '=');
if (subst_end == 0)
colon = 0;
else
{
replace_beg = subst_end + 1;
replace_end = end;
v = lookup_variable (beg, colon - beg);
if (v == 0)
warn_undefined (beg, colon - beg);
if (v != 0 && *v->value != '\0')
{
char *pattern, *replace, *ppercent, *rpercent;
char *value = (v->recursive
? recursively_expand (v)
: v->value);
pattern = (char *) alloca (subst_end - subst_beg + 2);
*(pattern++) = '%';
bcopy (subst_beg, pattern, subst_end - subst_beg);
pattern[subst_end - subst_beg] = '\0';
replace = (char *) alloca (replace_end
- replace_beg + 2);
*(replace++) = '%';
bcopy (replace_beg, replace,
replace_end - replace_beg);
replace[replace_end - replace_beg] = '\0';
ppercent = find_percent (pattern);
if (ppercent)
{
++ppercent;
rpercent = 0;
}
else
{
ppercent = pattern;
rpercent = replace;
--pattern;
--replace;
}
o = patsubst_expand (o, value, pattern, replace,
ppercent, rpercent);
if (v->recursive)
free (value);
}
}
}
if (colon == 0)
o = reference_variable (o, beg, end - beg);
if (free_beg)
free (beg);
}
break;
case '\0':
break;
default:
if (isblank ((unsigned char)p[-1]))
break;
o = reference_variable (o, p, 1);
break;
}
if (*p == '\0')
break;
else
++p;
}
if (save_char)
string[length] = save_char;
(void)variable_buffer_output (o, "", 1);
return (variable_buffer + line_offset);
}
char *
variable_expand (char *line)
{
return variable_expand_string(NULL, line, (long)-1);
}
char *
expand_argument (const char *str, const char *end)
{
char *tmp;
if (str == end)
return xstrdup("");
if (!end || *end == '\0')
return allocated_variable_expand ((char *)str);
tmp = (char *) alloca (end - str + 1);
bcopy (str, tmp, end - str);
tmp[end - str] = '\0';
return allocated_variable_expand (tmp);
}
char *
variable_expand_for_file (char *line, struct file *file)
{
char *result;
struct variable_set_list *save;
if (file == 0)
return variable_expand (line);
save = current_variable_set_list;
current_variable_set_list = file->variables;
if (file->cmds && file->cmds->fileinfo.filenm)
reading_file = &file->cmds->fileinfo;
else
reading_file = 0;
result = variable_expand (line);
current_variable_set_list = save;
reading_file = 0;
return result;
}
static char *
variable_append (const char *name, unsigned int length,
const struct variable_set_list *set)
{
const struct variable *v;
char *buf = 0;
if (!set)
return initialize_variable_output ();
v = lookup_variable_in_set (name, length, set->set);
if (!v)
return variable_append (name, length, set->next);
if (v->append)
buf = variable_append (name, length, set->next);
else
buf = initialize_variable_output ();
if (buf > variable_buffer)
buf = variable_buffer_output (buf, " ", 1);
if (! v->recursive)
return variable_buffer_output (buf, v->value, strlen (v->value));
buf = variable_expand_string (buf, v->value, strlen (v->value));
return (buf + strlen (buf));
}
static char *
allocated_variable_append (const struct variable *v)
{
char *val;
char *obuf = variable_buffer;
unsigned int olen = variable_buffer_length;
variable_buffer = 0;
val = variable_append (v->name, strlen (v->name), current_variable_set_list);
variable_buffer_output (val, "", 1);
val = variable_buffer;
variable_buffer = obuf;
variable_buffer_length = olen;
return val;
}
char *
allocated_variable_expand_for_file (char *line, struct file *file)
{
char *value;
char *obuf = variable_buffer;
unsigned int olen = variable_buffer_length;
variable_buffer = 0;
value = variable_expand_for_file (line, file);
#if 0
value = xrealloc (value, strlen (value))
#endif
variable_buffer = obuf;
variable_buffer_length = olen;
return value;
}
void
install_variable_buffer (char **bufp, unsigned int *lenp)
{
*bufp = variable_buffer;
*lenp = variable_buffer_length;
variable_buffer = 0;
initialize_variable_output ();
}
void
restore_variable_buffer (char *buf, unsigned int len)
{
free (variable_buffer);
variable_buffer = buf;
variable_buffer_length = len;
}