/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1984-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* make rule support
*
* the attribute names defined in this file must agree with ATTRNAME
*/
#include "make.h"
#include "options.h"
/*
* return the NAME_* type for name
*/
int
{
register const char* s;
register int t;
register int q;
t = 0;
s = name;
switch (*s)
{
case 0:
return 0;
case MARK_CONTEXT:
q = NAME_context;
break;
case '.':
q = NAME_variable|NAME_intvar;
break;
case '-':
case '+':
return NAME_option;
case '(':
t = NAME_staterule;
q = 0;
break;
default:
q = istype(*s, C_ID1) ? (NAME_identifier|NAME_variable) : istype(*s, C_VARIABLE1) ? NAME_variable : 0;
break;
}
for (;;)
{
switch (*s++)
{
case 0:
s -= 2;
break;
case '/':
q |= NAME_path;
q &= ~(NAME_identifier|NAME_variable);
continue;
case '=':
if (q & (NAME_identifier|NAME_variable))
q |= NAME_assignment;
continue;
case '+':
if (*s != '=')
q &= ~(NAME_identifier|NAME_variable);
continue;
case '&':
if (*s == '=')
continue;
/*FALLTHROUGH*/
case '*':
case '?':
case '[':
case ']':
case '|':
q |= NAME_glob;
q &= ~(NAME_identifier|NAME_variable);
continue;
case '@':
case '!':
case '}':
if (*s == '(')
t |= NAME_glob;
q &= ~(NAME_identifier|NAME_variable);
continue;
case '$':
if (*s == '(')
t |= NAME_dynamic;
q &= ~(NAME_identifier|NAME_variable);
continue;
case '(':
if (s > name + 1)
{
t &= ~NAME_staterule;
q &= ~NAME_staterule;
}
continue;
case ')':
if (t & NAME_staterule)
{
t &= ~NAME_staterule;
q |= NAME_staterule;
}
else
{
q &= ~NAME_staterule;
q |= t & (NAME_glob|NAME_dynamic);
}
q &= ~(NAME_identifier|NAME_variable);
continue;
default:
q &= ~(NAME_identifier|NAME_variable);
q &= ~NAME_identifier;
continue;
}
break;
}
if ((q & NAME_context) && *s == MARK_CONTEXT)
{
if (e)
*e = (char*)(s + 1);
if (e)
*e = (char*)(s + 1);
return NAME_context;
}
if (q & NAME_staterule)
{
if (*s == ')')
return NAME_statevar;
return NAME_altstate;
return NAME_staterule;
}
if (q & NAME_dynamic)
return NAME_dynamic;
if (q & NAME_assignment)
return NAME_assignment;
if (q & NAME_glob)
return NAME_glob;
if (q & NAME_identifier)
return NAME_identifier;
if (q & NAME_variable)
return q & NAME_path;
}
/*
* map name s to rule r
* previous mappings must be reconciled with the prereq lists
*/
char*
{
register List_t* p;
Rule_t* q;
Rule_t* o;
static unsigned char warned;
if ((o = getrule(s)) == r)
return r->name;
s = putrule(0, r);
{
if (!warned)
{
warned++;
}
{
if (q == o)
if (p->rule == o)
p->rule = r;
}
}
return s;
}
/*
* return a pointer to a rule given its name,
* creating the rule if necessary
*/
{
register Rule_t* r;
int n;
if (name)
{
return r;
{
return r;
}
}
newrule(r);
if (!name)
if (n & (NAME_staterule|NAME_altstate))
{
r->dynamic |= D_compiled;
}
else if (n & NAME_statevar)
{
r->dynamic |= D_compiled;
}
r->dynamic |= D_compiled;
else
r->dynamic &= ~D_compiled;
#if DEBUG
#endif
return r;
}
/*
* check if a rule name is special
* special names are ignored in determining the default main target(s)
*/
int
{
register char* s;
return 1;
for (;;)
switch (*s++)
{
case 0:
return 1;
case '/':
case '$':
return 0;
}
return 0;
}
/*
* return joint list pointer for r
* static single element list returned for non-joint target
*/
{
return &tmp;
}
/*
* add a single non-duplicated prerequisite x to rule r
* op = {PREREQ_APPEND,PREREQ_DELETE,PREREQ_INSERT,PREREQ_LENGTH}
*/
void
{
register List_t* p;
register List_t* q;
if (x != r)
{
if (p = r->prereqs)
{
q = 0;
while (p)
{
if (x == p->rule)
{
if (op == PREREQ_DELETE)
{
if (q)
else
}
if (!(x->property & P_multiple))
break;
}
if (!p->next)
{
if (op != PREREQ_DELETE)
{
r->dynamic &= ~D_compiled;
if (op == PREREQ_LENGTH)
{
register int n;
p = r->prereqs;
q = 0;
for (;;)
{
{
if (q)
else
break;
}
q = p;
p = p->next;
}
}
else if (op == PREREQ_INSERT)
}
break;
}
q = p;
p = p->next;
}
}
else if (op != PREREQ_DELETE)
{
r->dynamic &= ~D_compiled;
}
}
}
/*
* return the pattern association rule with prefix a that matches r
* if r is 0 then s is the rule name
* if pos is not 0 then it is the last match position
* *pos must be 0 on first call
* *pos undefined when 0 returned
*/
{
register List_t* p;
register Rule_t* x;
register Rule_t* z;
List_t* u;
if (r)
{
return 0;
s = r->name;
}
do
{
u = 0;
if ((x = p->rule) != r)
{
if (x->property & P_attribute)
{
if (r && (hasattribute(r, x, NiL) || !r->scan && x->scan && (z = staterule(RULE, r, NiL, -1)) && z->scan == x->scan))
break;
}
u = p;
else if (metamatch(NiL, s, x->name) || r && r->uname && !(r->property & P_state) && metamatch(NiL, r->uname, x->name))
break;
}
if (p || (p = u))
{
if (pos)
*pos = p;
}
return 0;
}
/*
* check if r's prerequisite list or named attributes have changed
*
* NOTE: IGNORECHANGE(r,q) prerequisites are ignored in the comparison
*/
#define IGNORECHANGE(r,q) (((q)->property & (P_joint|P_ignore)) || ((q)->dynamic & D_alias) && getrule((q)->name) == r)
int
prereqchange(register Rule_t* r, register List_t* newprereqs, Rule_t* o, register List_t* oldprereqs)
{
register List_t* p;
return 0;
if ((r->property & (P_joint|P_target)) == (P_joint|P_target) && r != r->prereqs->rule->prereqs->rule)
return 0;
{
return 1;
}
more:
for (;;)
{
if (newprereqs)
{
else if (oldprereqs)
{
else if (newprereqs->rule == oldprereqs->rule || ((newprereqs->rule->dynamic ^ oldprereqs->rule->dynamic) & (D_alias|D_bound)) && getrule(newprereqs->rule) == getrule(oldprereqs->rule))
{
}
else
break;
}
else
break;
}
else
break;
}
if (newprereqs)
{
{
for (p = oldprereqs; p && (newprereqs->rule != p->rule || !((newprereqs->rule->dynamic ^ p->rule->dynamic) & (D_alias|D_bound)) || getrule(newprereqs->rule) != getrule(p->rule)); p = p->next);
if (p)
{
goto more;
}
else
}
return 1;
}
if (oldprereqs)
{
return 1;
}
return 0;
}
/*
* initialize immediate rule info
*/
static void
{
dynamic(r);
r->action = 0;
r->prereqs = 0;
}
/*
* reset all rule info as if we just started
*/
static int
reset(const char* s, char* v, void* h)
{
{
{
r->dynamic &= ~(D_entries|D_hasafter|D_hasbefore|D_hasmake|D_hasscope|D_hassemaphore|D_scanned|D_triggered);
}
else
}
return 0;
}
/*
* check rule r for immediate action
* should only be called if r->immediate==1 and r->target==1
*/
void
{
register List_t* p;
register Rule_t* x;
char* action;
char* e;
Var_t* v;
int i;
int g;
int u;
Flags_t a;
Seconds_t t;
{
{
if (a == V_scan)
else
v->property |= a;
}
}
{
}
{
if ((p->rule->dynamic & (D_bound|D_scanned)) && (!(p->rule->mark & M_bind) || (state.questionable & 0x02000000)))
}
{
{
x = p->rule;
x = bind(x);
{
}
}
}
{
int errors;
i = !prereqs;
errors = 0;
{
else
{
i = 1;
}
}
{
if (prereqs)
i = 1;
if (action)
{
if (!errors && i)
{
}
}
}
}
else if (r == internal.include)
{
i = COMP_INCLUDE;
{
x = p->rule;
i ^= COMP_DONTCARE;
{
}
else
}
}
{
if (p = prereqs)
{
if (*e)
t = 0;
else
p = p->next;
}
else
t = 0;
wakeup(t, p);
}
{
if (!prereqs)
savestate();
else
}
{
}
{
}
{
}
return;
return;
else
{
}
if (prereqs)
if (r->prereqs)
{
r->prereqs = 0;
}
r->action = 0;
{
r->prereqs = 0;
r->action = 0;
}
}
/*
* remove duplicate prerequisites from prerequisite list p
*/
void
{
register List_t* q;
register List_t* x;
for (x = p, q = 0; p; p = p->next)
{
{
if (q)
#if DEBUG
else
{
}
#endif
}
else
{
q = p;
}
}
while (x)
{
x = x->next;
}
}
/*
* do dynamic expansion of rule r's prerequisites
* the prerequisite list is permanently modified
*/
void
{
register char* s;
register List_t* p;
register List_t* q;
register List_t* t;
char** v;
char* buf;
int added;
int flags;
Rule_t* x;
Rule_t* u;
{
}
vec[1] = 0;
added = 0;
{
{
if (q)
else
u = p->rule;
flags = 0;
{
added = 1;
if (isglob(s))
else
*(v = vec) = s;
while (s = *v++)
{
x = makerule(s);
/*
* merge u into x
*/
/*
* replace u with x
*/
if (q)
q->next = t;
else
r->prereqs = t;
q = t;
}
}
}
else q = p;
}
if (added)
}
/*
* return non-zero if r has builtin attribute a
* if x!=0 then check if merge(x,r,MERGE_ATTR) would have attribute
*/
int
{
register Flags_t n;
register int attrname;
List_t* p;
if (a->property & P_attribute)
{
n = r->property;
if (x && !(n & P_readonly))
{
n |= x->property & ~(P_attribute|P_immediate|P_implicit|P_internal|P_operator|P_readonly|P_state|P_staterule|P_statevar|P_target);
n &= ~P_implicit;
}
/*
* the first group may conflict with a->attribute
*/
if (attrname)
{
}
{
if (x && (a->attribute & x->attribute & ~internal.ignore->attribute) && ((r->property & (P_attribute|P_use)) != P_attribute || r == internal.accept || r == internal.ignore || r == internal.retain)) return 1;
}
if (attrname)
{
/*
* the rest have no a->attribute conflicts
*/
/*
* the rest have a corresponding P_*
*/
if (a->property & n & ~(P_accept|P_attribute|P_ignore|P_internal|P_metarule|P_readonly|P_staterule|P_statevar|P_target))
{
}
}
}
else
{
if (attrname)
{
/*
* r->dynamic readonly attributes
*/
n = r->dynamic;
/*
* r->property readonly attributes
*/
n = r->property;
/*
* r->status readonly attributes
*/
n = r->status;
/*
* other readonly attributes
*/
}
/*
* r prerequisites as pseudo attributes
*/
if (a == p->rule)
return 1;
}
return 0;
}
/*
* return nonzero if r has an after prerequisite with exact property
*/
int
{
register List_t* p;
if (r->dynamic & D_hasafter)
return 1;
return 0;
}
/*
* merge <from> into <to> according to op
*/
void
{
register List_t* p;
{
{
/*
* this is a workaround to separate the view vs. local binding for this case:
* it may need to be examined for cases other than (state.mam.statix)
*/
if (!state.exec && state.mam.statix && (from->dynamic & D_alias) && (to->property & P_terminal) && from->uname && to->uname && *from->name != '/' && *to->name == '/')
{
}
return;
}
#if DEBUG
debug((-4, "merging %s%s into %s", (op & MERGE_ATTR) ? "attributes of " : null, from->name, to->name));
#endif
}
to->property |= from->property & (P_accept|P_after|P_always|P_archive|P_before|P_command|P_dontcare|P_force|P_foreground|P_functional|P_ignore|P_implicit|P_joint|P_local|P_make|P_multiple|P_parameter|P_read|P_repeat|P_terminal|P_virtual);
else
to->property |= from->property & (P_accept|P_after|P_always|P_archive|P_before|P_command|P_force|P_foreground|P_functional|P_implicit|P_joint|P_local|P_make|P_multiple|P_parameter|P_read|P_repeat|P_terminal|P_virtual);
{
if (!(op & MERGE_SCANNED))
{
if (op & MERGE_BOUND)
{
}
if (op & MERGE_BOUND)
{
}
}
}
else if (op & MERGE_FORCE)
{
if (from->attribute && from != internal.accept && from != internal.ignore && from != internal.retain && ((to->property & (P_attribute|P_use)) != P_attribute || to == internal.accept || to == internal.ignore || to == internal.retain))
}
else
{
if (from->attribute && from != internal.accept && from != internal.ignore && from != internal.retain && ((to->property & (P_attribute|P_use)) != P_attribute || to == internal.accept || to == internal.ignore || to == internal.retain))
}
}
/*
* merge <from> state rules into <to>
*/
void
{
register int i;
Rule_t* t;
char* s;
/*
* if RULE merges then RULE+1..STATERULES also merge
*/
else
return;
#if DEBUG
{
error(2, "MERGESTATE from: %s: %s time=[%s] event=[%s]", from->name, fromstate->name, timestr(fromstate->time), timestr(fromstate->event));
error(2, "MERGESTATE to: %s: %s time=[%s] event=[%s]", to->name, tostate->name, timestr(tostate->time), timestr(tostate->event));
}
#endif
{
/*
* the solution is conservative but ok
* since aliases probably don't change
* very often -- every target depending
* on <to> will be forced out of date rather
* than those that just depend on <from>
*/
}
{
{
/*
* merge in the other direction
*/
t = from;
to = t;
t = fromstate;
tostate = t;
}
{
}
}
}
/*
* negate <from> attributes in <to>
*/
void
{
to->property &= ~(from->property & (P_accept|P_after|P_always|P_archive|P_before|P_command|P_dontcare|P_force|P_functional|P_ignore|P_immediate|P_implicit|P_local|P_make|P_multiple|P_parameter|P_repeat|P_target|P_terminal|P_use|P_virtual));
}
/*
* make an internal rule pointer
*/
static Rule_t*
{
register Rule_t* r;
r = makerule(s);
r->dynamic |= D_compiled;
return r;
}
/*
* make an internal pattern association rule pointer
* NOTE: this is required to sync pre 2001-05-09 make objects
*/
static Rule_t*
{
register Rule_t* r;
register Rule_t* a;
register List_t* p;
if (!(p->rule->property & P_attribute) && p->rule->name[0] == '%' && p->rule->name[1] == ATTRNAME && (a = getrule(p->rule->name + 1)) && (a->property & P_attribute))
p->rule = a;
return r;
}
/*
* maintain atomic dir-rule names
*/
static int
diratom(const char* s, char* v, void* h)
{
NoP(s);
NoP(h);
return 0;
}
/*
* # outstanding jobs builtin
*/
static char*
{
}
/*
* getconf() builtin
*/
static char*
{
char* name;
char* path;
char* value;
args++;
{
path = 0;
args++;
}
value = 0;
}
/*
* getopts() builtin
*/
static char*
{
char* id;
char* usage;
char* prefix;
char* oid;
char* s;
Rule_t* r;
s = getval(">", 0);
if (*s == '-' && (id = *args++) && (r = getrule(*args++)) && (usage = r->action) && (prefix = *args))
{
oid = 0;
else
{
}
for (;;)
{
break;
{
case 0:
break;
case '?':
s = null;
break;
case ':':
s = null;
break;
default:
setvar(sfstruse(internal.wrk), opt_info.arg && *opt_info.arg ? opt_info.arg : opt_info.num ? "1" : null, 0);
continue;
}
break;
}
if (oid)
return s;
}
return null;
}
typedef int (*Systab_f)(void);
typedef struct Systab_s
{
const char* name;
} Systab_t;
{
{0}
};
/*
* void arg system call catchall builtin
*/
static char*
{
if (!*args)
return null;
sfprintf(internal.val, "%d", (call = (Systab_t*)strlook(systab, sizeof(systab[0]), *args)) ? (*call->call)() : -1);
}
/*
* external engine name initialization -- the rest are in initrule()
*
* NOTE: version.c may reference some of these names, not to mention
* non-engine source makefiles
*/
{
/*
* variable names
*/
"MAKEARGS",
"MAKECONVERT",
"MAKEFILE",
"MAKEFILES",
"MAKEIMPORT",
"MAKELIB",
"MAKE",
"OLDMAKE",
"PWD",
"MAKERULES",
"MAKESKIP",
"MAKEVERSION",
"MAKEPATH",
"VPATH",
/*
* infrequently used engine interface names
*/
".COMPDONE",
".COMPINIT",
".DONE",
".INIT",
".INTERRUPT",
".JOBDONE",
".MAKEDONE",
".MAKEINIT",
".MAKEPROMPT",
".MAKERUN",
".MAMNAME.",
".MAMACTION.",
".ORDER",
/*
* related file suffixes
*/
".ml",
".mo",
".mk",
".ms",
".mt",
};
/*
* initialize some attribute and internal rule pointers
*/
void
initrule(void)
{
Rule_t* r;
int i;
static int repeat;
/*
* dynamic rule attributes
*/
/*
* anonymous attributes (no internal handle)
*/
/*
* readonly rule attributes
*/
/*
* special rules and names
*/
/*
* pattern association rules
*/
/*
* builtin functions
*/
#if DEBUG
#endif
/*
* initialize the builtin attributes
*/
if (!repeat)
{
}
/*
* maintain atomic dir-rule names
*/
#if !BINDINDEX
{
r->view = i;
}
#endif
/*
* expand some dynamic values now for efficiency
*/
/*
* some things are only done once
*/
repeat++;
}
/*
* low level for initview()
*/
static List_t*
{
register int i;
register Rule_t* r;
if (!d)
d = s;
i = pathcanon(d, 0, 0) - d;
if (i > 2 && d[i - 1] == '/')
d[--i] = 0;
r = makerule(d);
if ((unique(r) || !r->time) && !streq(r->name, internal.dot->name) && !streq(r->name, internal.pwd))
{
{
#if BINDINDEX
r->dynamic |= D_bindindex;
#else
#endif
if (s != d)
{
i = pathcanon(s, 0, 0) - s;
if (i > 2 && s[i - 1] == '/')
s[--i] = 0;
r = makerule(s);
}
#if BINDINDEX
message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path->name, state.view[state.maxview].root));
#else
message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path, state.view[state.maxview].root));
#endif
}
else
}
return p;
}
/*
* initialize the views
*/
void
initview(void)
{
register char* s;
register char* t;
register int n;
int c;
int pwdlen;
char* pwd;
char* tok;
char* u;
List_t* p;
Rule_t* r;
{
if ((n = (s = colonlist(tmp, external.viewnode, 1, ' ')) != 0) || (s = colonlist(tmp, external.viewdot, 1, ' ')))
{
{
s = t;
}
}
for (n = 1; n <= MAXVIEW; n++)
{
{
break;
}
#if BINDINDEX
#else
#endif
}
}
else
{
{
{
if (*s != '/')
s = pathcanon(t, 0, 0);
if (*(s - 1) == '/')
*--s = 0;
n = s - t;
pwd = 0;
{
/*
* ksh pwd and ast getcwd() are logical
* others are physical
* this gets PWD and VPATH in sync
* if they're not
*/
{
c = 0;
for (;;)
{
{
break;
}
if (!(s = strrchr(u, '/')))
break;
*s = 0;
c = 1;
}
}
if (!pwd)
{
break;
}
}
if (pwd)
{
}
#if BINDINDEX
message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path->name, state.view[state.maxview].root));
#else
message((-2, "view[%d]: %s %s", state.maxview, state.view[state.maxview].path, state.view[state.maxview].root));
#endif
{
if (!c)
else
{
if (*s != '/')
}
}
break;
}
}
{
{
pathcanon(s, 0, 0);
if (!n || *s != '.' || *(s + 1) != '.' || *(s + 2) && *(s + 2) != '/')
else for (c = 0; c <= n; c++)
{
#if BINDINDEX
#else
#endif
}
}
}
}
}