#include "make.h"
#include <assert.h>
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"
#define VARIABLE_BUFFER_ZONE 5
static unsigned int variable_buffer_length;
char *variable_buffer;
char *
variable_buffer_output (ptr, string, length)
char *ptr, *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 ()
{
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 (v, file)
struct variable *v;
struct file *file;
{
char *value;
struct variable_set_list *save = 0;
if (v->expanding)
{
if (!v->exp_count)
fatal (reading_file,
_("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 (file)
current_variable_set_list = save;
return value;
}
#ifdef __GNUC__
__inline
#endif
static char *
reference_variable (o, name, length)
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')
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 (line, string, length)
register 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 ? 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 (reading_file, _("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 = strchr (subst_beg, '=');
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 *value = (v->recursive ? recursively_expand (v)
: v->value);
char *pattern, *percent;
if (free_beg)
{
*subst_end = '\0';
pattern = subst_beg;
}
else
{
pattern = (char *) alloca (subst_end - subst_beg
+ 1);
bcopy (subst_beg, pattern, subst_end - subst_beg);
pattern[subst_end - subst_beg] = '\0';
}
percent = find_percent (pattern);
if (percent != 0)
{
char *replace;
if (free_beg)
{
*replace_end = '\0';
replace = replace_beg;
}
else
{
replace = (char *) alloca (replace_end
- replace_beg
+ 1);
bcopy (replace_beg, replace,
replace_end - replace_beg);
replace[replace_end - replace_beg] = '\0';
}
o = patsubst_expand (o, value, pattern, replace,
percent, (char *) 0);
}
else
o = subst_expand (o, value,
pattern, replace_beg,
strlen (pattern),
end - replace_beg,
0, 1);
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;
{
char name[5];
name[0] = '$';
name[1] = '(';
name[2] = *p;
name[3] = ')';
name[4] = '\0';
p1 = allocated_variable_expand (name);
o = variable_buffer_output (o, p1, strlen (p1));
free (p1);
}
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 (line)
char *line;
{
return variable_expand_string(NULL, line, (long)-1);
}
char *
expand_argument (str, end)
char *str, *end;
{
char *tmp;
if (str == end)
return xstrdup("");
if (!end || *end == '\0')
tmp = str;
else
{
tmp = (char *) alloca (end - str + 1);
bcopy (str, tmp, end - str);
tmp[end - str] = '\0';
}
return allocated_variable_expand (tmp);
}
static char *
variable_expand_for_file (line, file)
char *line;
register 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 (name, length, set)
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);
return variable_buffer_output (buf, v->value, strlen (v->value));
}
static char *
allocated_variable_append (v)
const struct variable *v;
{
char *val, *retval;
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;
retval = allocated_variable_expand (val);
free (val);
return retval;
}
char *
allocated_variable_expand_for_file (line, 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;
}