#include "make.h"
#include "filedef.h"
#include "rule.h"
#include "dep.h"
#include "debug.h"
#include "variable.h"
#include "job.h"
#include "commands.h"
static int
pattern_search PARAMS ((struct file *file, int archive,
unsigned int depth, unsigned int recursions));
int
try_implicit_rule (struct file *file, unsigned int depth)
{
DBF (DB_IMPLICIT, _("Looking for an implicit rule for `%s'.\n"));
if (pattern_search (file, 0, depth, 0))
return 1;
#ifndef NO_ARCHIVES
if (ar_name (file->name))
{
DBF (DB_IMPLICIT,
_("Looking for archive-member implicit rule for `%s'.\n"));
if (pattern_search (file, 1, depth, 0))
return 1;
}
#endif
return 0;
}
struct idep
{
struct idep *next;
char *name;
struct file *intermediate_file;
char *intermediate_pattern;
unsigned char had_stem;
unsigned char ignore_mtime;
};
static void
free_idep_chain (struct idep *p)
{
struct idep *n;
for (; p != 0; p = n)
{
n = p->next;
if (p->name)
{
struct file *f = p->intermediate_file;
if (f != 0
&& (f->stem < f->name || f->stem > f->name + strlen (f->name)))
free (f->stem);
free (p->name);
}
free (p);
}
}
static char *
get_next_word (char *buffer, unsigned int *length)
{
char *p = buffer, *beg;
char c;
while (isblank ((unsigned char)*p))
++p;
beg = p;
c = *(p++);
if (c == '\0')
return 0;
while (1)
{
char closeparen;
int count;
switch (c)
{
case '\0':
case ' ':
case '\t':
goto done_word;
case '$':
c = *(p++);
if (c == '$')
break;
if (c == '(')
closeparen = ')';
else if (c == '{')
closeparen = '}';
else
break;
for (count = 0; *p != '\0'; ++p)
{
if (*p == c)
++count;
else if (*p == closeparen && --count < 0)
{
++p;
break;
}
}
break;
case '|':
goto done;
default:
break;
}
c = *(p++);
}
done_word:
--p;
done:
if (length)
*length = p - beg;
return beg;
}
static int
pattern_search (struct file *file, int archive,
unsigned int depth, unsigned int recursions)
{
char *filename = archive ? strchr (file->name, '(') : file->name;
unsigned int namelen = strlen (filename);
char *lastslash;
struct file *intermediate_file = 0;
struct idep* deps = 0;
unsigned int remove_explicit_deps = 0;
register char *depname = (char *) alloca (namelen + max_pattern_dep_length);
register char *stem = 0;
register unsigned int stemlen = 0;
register unsigned int fullstemlen = 0;
struct rule **tryrules
= (struct rule **) xmalloc (num_pattern_rules * max_pattern_targets
* sizeof (struct rule *));
unsigned int nrules;
unsigned int *matches
= (unsigned int *) alloca (num_pattern_rules * sizeof (unsigned int));
char *checked_lastslash
= (char *) alloca (num_pattern_rules * sizeof (char));
unsigned int foundrule;
int intermed_ok;
int specific_rule_matched = 0;
unsigned int i = 0;
struct rule *rule;
struct dep *dep, *expl_d;
char *p, *vname;
struct idep *d;
struct idep **id_ptr;
struct dep **d_ptr;
PATH_VAR (stem_str);
#ifndef NO_ARCHIVES
if (archive || ar_name (filename))
lastslash = 0;
else
#endif
{
#ifdef VMS
lastslash = strrchr (filename, ']');
if (lastslash == 0)
lastslash = strrchr (filename, ':');
#else
lastslash = strrchr (filename, '/');
#ifdef HAVE_DOS_PATHS
{
char *bslash = strrchr (filename, '\\');
if (lastslash == 0 || bslash > lastslash)
lastslash = bslash;
if (lastslash == 0 && filename[0] && filename[1] == ':')
lastslash = filename + 1;
}
#endif
#endif
if (lastslash != 0 && lastslash[1] == '\0')
lastslash = 0;
}
nrules = 0;
for (rule = pattern_rules; rule != 0; rule = rule->next)
{
if (rule->deps != 0 && rule->cmds == 0)
continue;
if (rule->in_use)
{
DBS (DB_IMPLICIT, (_("Avoiding implicit rule recursion.\n")));
continue;
}
for (i = 0; rule->targets[i] != 0; ++i)
{
char *target = rule->targets[i];
char *suffix = rule->suffixes[i];
int check_lastslash;
if (recursions > 0 && target[1] == '\0' && !rule->terminal)
continue;
if (rule->lens[i] > namelen)
continue;
stem = filename + (suffix - target - 1);
stemlen = namelen - rule->lens[i] + 1;
check_lastslash = 0;
if (lastslash)
{
#ifdef VMS
check_lastslash = (strchr (target, ']') == 0
&& strchr (target, ':') == 0);
#else
check_lastslash = strchr (target, '/') == 0;
#ifdef HAVE_DOS_PATHS
if (check_lastslash)
{
char *b = strchr (target, '\\');
check_lastslash = !(b || (target[0] && target[1] == ':'));
}
#endif
#endif
}
if (check_lastslash)
{
unsigned int difference = lastslash - filename + 1;
if (difference > stemlen)
continue;
stemlen -= difference;
stem += difference;
}
if (check_lastslash)
{
if (stem > (lastslash + 1)
&& !strneq (target, lastslash + 1, stem - lastslash - 1))
continue;
}
else if (stem > filename
&& !strneq (target, filename, stem - filename))
continue;
if (*suffix != stem[stemlen]
|| (*suffix != '\0' && !streq (&suffix[1], &stem[stemlen + 1])))
continue;
if (target[1] != '\0')
specific_rule_matched = 1;
if (rule->deps == 0 && rule->cmds == 0)
continue;
tryrules[nrules] = rule;
matches[nrules] = i;
checked_lastslash[nrules] = check_lastslash;
++nrules;
}
}
if (specific_rule_matched)
for (i = 0; i < nrules; ++i)
if (!tryrules[i]->terminal)
{
register unsigned int j;
for (j = 0; tryrules[i]->targets[j] != 0; ++j)
if (tryrules[i]->targets[j][1] == '\0')
break;
if (tryrules[i]->targets[j] != 0)
tryrules[i] = 0;
}
initialize_file_variables (file, 0);
for (intermed_ok = 0; intermed_ok == !!intermed_ok; ++intermed_ok)
{
for (i = 0; i < nrules; i++)
{
struct file *f;
unsigned int failed = 0;
int check_lastslash;
int file_variables_set = 0;
rule = tryrules[i];
remove_explicit_deps = 0;
if (rule == 0)
continue;
if (intermed_ok && rule->terminal)
continue;
rule->in_use = 1;
stem = filename
+ (rule->suffixes[matches[i]] - rule->targets[matches[i]]) - 1;
stemlen = namelen - rule->lens[matches[i]] + 1;
check_lastslash = checked_lastslash[i];
if (check_lastslash)
{
stem += lastslash - filename + 1;
stemlen -= (lastslash - filename) + 1;
}
DBS (DB_IMPLICIT, (_("Trying pattern rule with stem `%.*s'.\n"),
(int) stemlen, stem));
strncpy (stem_str, stem, stemlen);
stem_str[stemlen] = '\0';
file->stem = stem_str;
for (dep = rule->deps; dep != 0; dep = dep->next)
{
unsigned int len;
char *p2;
unsigned int order_only = 0;
p = get_next_word (dep->name, &len);
while (1)
{
int add_dir = 0;
int had_stem = 0;
if (p == 0)
break;
for (p2 = p; p2 < p + len && *p2 != '%'; ++p2)
;
if (dep->need_2nd_expansion)
{
if (p2 < p + len)
{
register unsigned int i = p2 - p;
bcopy (p, depname, i);
bcopy ("$*", depname + i, 2);
bcopy (p2 + 1, depname + i + 2, len - i - 1);
depname[len + 2 - 1] = '\0';
if (check_lastslash)
add_dir = 1;
had_stem = 1;
}
else
{
bcopy (p, depname, len);
depname[len] = '\0';
}
if (!file_variables_set)
{
set_file_variables (file);
file_variables_set = 1;
}
p2 = variable_expand_for_file (depname, file);
}
else
{
if (p2 < p + len)
{
register unsigned int i = p2 - p;
bcopy (p, depname, i);
bcopy (stem_str, depname + i, stemlen);
bcopy (p2 + 1, depname + i + stemlen, len - i - 1);
depname[len + stemlen - 1] = '\0';
if (check_lastslash)
add_dir = 1;
had_stem = 1;
}
else
{
bcopy (p, depname, len);
depname[len] = '\0';
}
p2 = depname;
}
while (1)
{
id_ptr = &deps;
for (; *id_ptr; id_ptr = &(*id_ptr)->next)
;
*id_ptr = (struct idep *)
multi_glob (
parse_file_seq (&p2,
order_only ? '\0' : '|',
sizeof (struct idep),
1), sizeof (struct idep));
if (order_only || add_dir || had_stem)
{
unsigned long l = lastslash - filename + 1;
for (d = *id_ptr; d != 0; d = d->next)
{
if (order_only)
d->ignore_mtime = 1;
if (add_dir)
{
char *p = d->name;
d->name = xmalloc (strlen (p) + l + 1);
bcopy (filename, d->name, l);
bcopy (p, d->name + l, strlen (p) + 1);
free (p);
}
if (had_stem)
d->had_stem = 1;
}
}
if (!order_only && *p2)
{
++p2;
order_only = 1;
continue;
}
break;
}
p += len;
p = get_next_word (p, &len);
}
}
file->stem = 0;
for (d = deps; d != 0; d = d->next)
{
char *name = d->name;
if (file_impossible_p (name))
{
DBS (DB_IMPLICIT,
(d->had_stem
? _("Rejecting impossible implicit prerequisite `%s'.\n")
: _("Rejecting impossible rule prerequisite `%s'.\n"),
name));
tryrules[i] = 0;
failed = 1;
break;
}
DBS (DB_IMPLICIT,
(d->had_stem
? _("Trying implicit prerequisite `%s'.\n")
: _("Trying rule prerequisite `%s'.\n"), name));
for (expl_d = file->deps; expl_d != 0; expl_d = expl_d->next)
if (streq (dep_name (expl_d), name))
break;
if (expl_d != 0)
continue;
if (((f = lookup_file (name)) != 0)
|| file_exists_p (name))
continue;
vname = name;
if (vpath_search (&vname, (FILE_TIMESTAMP *) 0))
{
DBS (DB_IMPLICIT,
(_("Found prerequisite `%s' as VPATH `%s'\n"),
name,
vname));
free (vname);
continue;
}
if (intermed_ok)
{
if (intermediate_file == 0)
intermediate_file
= (struct file *) alloca (sizeof (struct file));
DBS (DB_IMPLICIT,
(_("Looking for a rule with intermediate file `%s'.\n"),
name));
bzero ((char *) intermediate_file, sizeof (struct file));
intermediate_file->name = name;
if (pattern_search (intermediate_file,
0,
depth + 1,
recursions + 1))
{
d->intermediate_file = intermediate_file;
d->intermediate_pattern = intermediate_file->name;
intermediate_file->name = xstrdup (name);
intermediate_file = 0;
continue;
}
if (intermediate_file->variables)
free_variable_set (intermediate_file->variables);
file_impossible (name);
}
failed = 1;
break;
}
rule->in_use = 0;
if (failed)
{
free_idep_chain (deps);
deps = 0;
}
else
break;
}
if (i < nrules)
break;
rule = 0;
}
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
if (rule == 0) {
if ((next_flag & NEXT_VPATH_FLAG) && file->old_name != 0) {
char *save_name = file->name;
file->name = file->old_name;
file->old_name = 0;
if (pattern_search(file, archive, depth, recursions))
rule = (struct rule *)1;
file->old_name = file->name;
file->name = save_name;
}
goto done;
}
#else
if (rule == 0)
goto done;
#endif
foundrule = i;
if (recursions > 0)
file->name = rule->targets[matches[foundrule]];
if (remove_explicit_deps)
{
dep = file->deps;
while (dep != 0)
{
struct dep *next = dep->next;
free_dep (dep);
dep = next;
}
file->deps = 0;
}
expl_d = file->deps;
d_ptr = &file->deps;
for (d = deps; d != 0; d = d->next)
{
register char *s;
if (d->intermediate_file != 0)
{
struct file *imf = d->intermediate_file;
register struct file *f = lookup_file (imf->name);
if (f != 0)
f->precious = 1;
else
f = enter_file (imf->name);
f->deps = imf->deps;
f->cmds = imf->cmds;
f->stem = imf->stem;
f->also_make = imf->also_make;
f->is_target = 1;
if (!f->precious)
{
imf = lookup_file (d->intermediate_pattern);
if (imf != 0 && imf->precious)
f->precious = 1;
}
f->intermediate = 1;
f->tried_implicit = 1;
for (dep = f->deps; dep != 0; dep = dep->next)
{
dep->file = enter_file (dep->name);
if (dep->name != dep->file->name)
free (dep->name);
dep->name = 0;
dep->file->tried_implicit |= dep->changed;
}
}
dep = alloc_dep ();
dep->ignore_mtime = d->ignore_mtime;
s = d->name;
d->name = 0;
if (recursions == 0)
{
dep->file = lookup_file (s);
if (dep->file == 0)
dep->file = enter_file (s);
else
free (s);
}
else
{
dep->name = s;
}
if (d->intermediate_file == 0 && tryrules[foundrule]->terminal)
{
if (dep->file == 0)
dep->changed = 1;
else
dep->file->tried_implicit = 1;
}
*d_ptr = dep;
d_ptr = &dep->next;
}
*d_ptr = expl_d;
if (!checked_lastslash[foundrule])
{
file->stem = savestring (stem, stemlen);
fullstemlen = stemlen;
}
else
{
int dirlen = (lastslash + 1) - filename;
fullstemlen = dirlen + stemlen;
file->stem = (char *) xmalloc (fullstemlen + 1);
bcopy (filename, file->stem, dirlen);
bcopy (stem, file->stem + dirlen, stemlen);
file->stem[fullstemlen] = '\0';
}
file->cmds = rule->cmds;
file->is_target = 1;
{
struct file *f = lookup_file (rule->targets[matches[foundrule]]);
if (f && f->precious)
file->precious = 1;
}
if (rule->targets[1] != 0)
for (i = 0; rule->targets[i] != 0; ++i)
if (i != matches[foundrule])
{
struct file *f;
struct dep *new = alloc_dep ();
new->name = p = (char *) xmalloc (rule->lens[i] + fullstemlen + 1);
bcopy (rule->targets[i], p,
rule->suffixes[i] - rule->targets[i] - 1);
p += rule->suffixes[i] - rule->targets[i] - 1;
bcopy (file->stem, p, fullstemlen);
p += fullstemlen;
bcopy (rule->suffixes[i], p,
rule->lens[i] - (rule->suffixes[i] - rule->targets[i]) + 1);
new->file = enter_file (new->name);
new->next = file->also_make;
f = lookup_file (rule->targets[i]);
if (f && f->precious)
new->file->precious = 1;
new->file->is_target = 1;
file->also_make = new;
}
done:
free_idep_chain (deps);
free (tryrules);
return rule != 0;
}