/***********************************************************************
* *
* 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 binding routines
*/
#include "make.h"
#include "options.h"
/*
* embedded spaces in file name wreak havoc
* we wreak hack to get through
*/
#define HACKSPACE(f,s) for (s = f; s = strchr(s, ' '); *s++ = (state.test & 0x00080000) ? '?' : FILE_SPACE)
#if DEBUG
#define DEBUGSOURCE(n,r,p) \
do \
{ \
{ \
List_t* q; \
for (q = p; q; q = q->next) \
} \
} while (0)
#else
#define DEBUGSOURCE(n,r,p)
#endif
#if _WINIX
/*
* we have a system in which some directories preserve
* mixed case entries but ignore case on name lookup
* maybe they can get a patent on that
* hey, maybe we can get a patent on this
*/
static int
file_compare(register const char* s, register const char* t)
{
}
static unsigned int
file_hash(const char* s)
{
register const unsigned char* p = (const unsigned char*)s;
register unsigned int h = 0;
register unsigned int c;
while (c = *p++)
{
if (isupper(c))
c = tolower(c);
HASHPART(h, c);
}
return h;
}
static int
rule_compare(register const char* s, register const char* t)
{
register int c;
register int d;
register int x;
x = (*s == '.') ? 0 : -1;
while (c = *s++)
{
if (!x)
{
if (c == '%')
x = (*(s - 2) == '.') ? 1 : -1;
else if (c != '.' && !isupper(c))
x = -1;
}
if ((d = *t++) != c)
{
if (x > 0)
{
if (isupper(c))
c = tolower(c);
else if (isupper(d))
d = tolower(d);
if (c == d)
continue;
}
return c - d;
}
}
return c - *t;
}
static unsigned int
rule_hash(const char* s)
{
register const unsigned char* p = (const unsigned char*)s;
register unsigned int h = 0;
register unsigned int c;
register int x;
x = (*s == '.') ? 0 : -1;
while (c = *p++)
{
if (!x)
{
if (c == '%')
x = (*(p - 2) == '.') ? 1 : -1;
else if (c != '.' && !isupper(c))
x = -1;
}
if (x > 0 && isupper(c))
c = tolower(c);
HASHPART(h, c);
}
return h;
}
#endif
/*
* initialize the hash tables
*/
void
inithash(void)
{
#if _WINIX
table.file = hashalloc(NiL, HASH_set, HASH_ALLOCATE, HASH_compare, file_compare, HASH_hash, file_hash, HASH_name, "files", 0);
table.rule = hashalloc(NiL, HASH_compare, rule_compare, HASH_hash, rule_hash, HASH_name, "atoms", 0);
#else
#endif
table.dir = hashalloc(NiL, HASH_set, HASH_ALLOCATE, HASH_namesize, sizeof(Fileid_t), HASH_name, "directories", 0);
optinit();
}
/*
* determine if a directory (archive) has already been scanned
*/
{
register Dir_t* d;
Rule_t* x;
{
r->time = 0;
return 0;
}
if ((d = getdir(&id)) && state.alias && d->directory == (S_ISDIR(st.st_mode) != 0) && (!state.mam.statix || S_ISDIR(st.st_mode)))
{
/*
* internal.unbind causes directory rescan
*/
if (r->name == d->name || (r->dynamic & D_alias) && makerule(r->name)->name == d->name || (x = makerule(d->name)) == r)
return d;
/*
* the physical directory alias can go either way
* but we bias the choice towards shorter pathnames
*/
{
Rule_t* t;
t = r;
r = x;
x = t;
}
message((-2, "%s %s is also specified as %s", (r->property & P_archive) ? "archive" : "directory", unbound(r), x->name));
return 0;
}
putdir(0, d);
#if _WINIX
d->ignorecase = d->directory;
#endif
return d;
}
/*
* add a directory (archive) entry to the file hash
*/
{
register File_t* f;
register File_t* n;
register char* s;
/*
* this test avoids duplicate entries for systems that
* support viewed or covered directories in the pathname
* system calls
*
* we assume that the cover directories are read in order
* from top to bottom
*
* the entries for a directory and its covered directories
* all have the same file.dir value
*/
{
if (d->archive)
{
#if DEBUG
#endif
}
return n;
}
#if DEBUG
message((-12, "%s: %s %s%s", d->name, name, timestr(date), d->ignorecase ? " [ignorecase]" : null));
#endif
f->next = n;
f->dir = d;
putfile(0, f);
#if _WINIX
{
s++;
{
*--s = 0;
*s = '.';
}
}
#endif
return f;
}
/*
* add new file r to the directory hash at dir
*/
void
{
register char* s;
register char* t;
char* nam;
Rule_t* z;
Dir_t* d;
do
{
*s = 0;
{
*t = 0;
/*
* sequential scan is OK since this is uncommon
*/
{
{
{
break;
}
}
}
if (t)
*t = '/';
}
*s++ = '/';
} while (s = strchr(s, '/'));
}
/*
* scan directory r and record all its entries
*/
void
{
register Dir_t* d;
char* s;
int n;
return;
else
s = r->name;
if (s > r->name && *s == '/')
*s-- = 0;
if ((s - r->name + 1) != n)
{
if (d = unique(r))
{
s = r->name;
{
{
if (!r->view && ((state.questionable & 0x00000800) || !(r->property & P_target)) && *s == '/' && (strncmp(s, internal.pwd, internal.pwdlen) || *(s + internal.pwdlen) != '/'))
}
return;
}
}
else if (r->time)
}
}
typedef struct Globstate_s
{
char* name;
int view;
int root;
} Globstate_t;
#if 0
static void*
{
void* p;
return p;
}
#endif
/*
* glob() diropen for 2d views
*/
static void*
{
const char* dir;
register int i;
register int n;
return 0;
if (*path == '/')
{
if (!strncmp(path, state.view[i].root, n = state.view[i].rootlen) && (!*(path + n) || *(path + n) == '/'))
{
if (!*(path += n + 1))
break;
}
}
return (void*)gs;
if (*path != '/')
{
else
{
}
return (void*)gs;
}
return 0;
}
/*
* glob() dirnext for 2d views
*/
static char*
{
char* s;
for (;;)
{
{
continue;
continue;
#endif
}
return 0;
do
{
return 0;
else
{
}
}
}
/*
* glob() dirclose for 2d views
*/
static void
{
}
/*
* glob() type for 2d views
*/
static int
{
register int i;
register int n;
int root;
i = 0;
{
root = 0;
if (*path == '/')
{
if (!strncmp(path, state.view[i].root, n = state.view[i].rootlen) && (!*(path + n) || *(path + n) == '/'))
{
if (!*(path += n + 1))
root = 1;
break;
}
}
{
if (root)
else
{
}
break;
}
}
i = 0;
i = GLOB_DIR;
i = GLOB_DEV;
i = GLOB_EXE;
else
i = GLOB_REG;
return i;
}
/*
* return a vector of the top view of files in all views matching pattern s
* the vector is placed on the current stack
*/
char**
{
register char** q;
register char** p;
register char** x;
int i;
int f;
if (!gp)
{
f |= GLOB_STACK;
}
{
}
{
if (i != GLOB_NOMATCH && !trap())
return nope;
}
{
if (!p || !streq(*q, *p))
{
*x++ = *q;
p = q;
}
*x = 0;
}
}
/*
* enter r as an alias for x
* path is the canonical path name for x
* d is the bind directory for path
* a pointer to r merged with x is returned
*/
static Rule_t*
{
char* s;
int i;
int n;
int na = 0;
Rule_t* z;
Rule_t* a[3];
{
a[na++] = x;
return r;
}
{
return x;
}
#if DEBUG
error(2, "bindalias: path=%s r=%s%s%s x=%s%s%s", path, r->name, r->uname ? "==" : null, r->uname ? r->uname : null, x->name, x->uname ? "==" : null, x->uname ? x->uname : null);
#endif
{
{
a[na++] = z;
#if DEBUG
#endif
}
if (z != x && z != r)
{
#if DEBUG
#endif
a[na++] = x;
x = z;
}
}
{
}
else
{
}
if (d)
{
{
}
else if (s[0] == '.' && s[1] == '.' && s[2] == '/' && ((n = strlen(r->name)) < (i = strlen(s)) || r->name[n - i - 1] != '/' || !streq(s, r->name + n - i)))
}
{
if ((!x->uname || x->uname[0] != '.' && x->uname[1] != '.' && x->uname[2] != '/') && ((s = getbound(x->name)) || x->uname && (s = getbound(x->uname))))
{
}
else if ((!r->uname || r->uname[0] != '.' && r->uname[1] != '.' && r->uname[2] != '/') && ((s = getbound(r->name)) || r->uname && (s = getbound(r->uname))))
{
}
{
r->name[n] = 0;
r->name[n] = '/';
}
else
{
s = 0;
}
for (i = 0; i < na; i++)
{
if (s)
}
for (i = 0; i < na; i++)
}
return x;
}
/*
* return local view rule for r if defined
* force forces the rule to be allocated
* 0 always returned if !state && == r or if not in local view
*/
static Rule_t*
{
register char* s;
register Rule_t* x;
char* p;
char* v;
return force ? 0 : r;
if (!r->view)
return 0;
{
switch (*s++)
{
case 0:
case '/':
x = makerule(s);
{
return x;
}
return 0;
}
}
p = 0;
s = r->name;
while (*s && *s == *v++)
if (*s++ == '/')
p = s;
if (p)
{
v--;
while (s = strchr(s, '/'))
if (!strcmp(++s, v))
{
*--s = 0;
*s = '/';
x = makerule(v);
{
merge(r, x, MERGE_ATTR);
if (!x->uname)
if (!x->time)
return x;
}
return 0;
}
}
return 0;
}
/*
* bind a rule to a file
*/
{
register Rule_t* d;
register File_t* f;
register char* s;
List_t* p;
char* dir = 0;
char* base;
char* b;
int found;
int i;
int n;
int c;
int ndirs;
int allocated = 0;
int aliased = 0;
int reassoc = 0;
unsigned int view;
Rule_t* a;
Rule_t* x;
Rule_t* z;
{
return 0;
{
a = r;
if (r == a)
{
}
}
return r;
if (!name)
}
for (;;)
{
found = 0;
view = 0;
od = 0;
/*
* at this point name!=r->name is possible
*/
#if _WINIX
if (*name == '/' || isalpha(*name) && *(name + 1) == ':' && (*(name + 2) == '/' || *(name + 2) == '\\'))
#else
if (*name == '/')
#endif
{
canon(s);
{
f = 0;
found = 1;
}
{
{
if (!*(s += n + 1))
{
{
f = 0;
found = 1;
#if 0 /* not sure about this */
view = i;
#endif
break;
}
}
break;
}
}
break;
}
/*
* dir contains the directory path name component (usually 0)
* base contains the base path name component
*/
if (*dir)
else
{
dir = 0;
}
/*
* gather the directories to search in reverse order
*/
#if DEBUG
#endif
ndirs = 0;
{
if (!r)
else
a = 0;
{
{
x = source(x);
{
DEBUGSOURCE(ndirs, x, p);
}
}
if ((flags & BIND_MAKEFILE) && (x = catrule(internal.source->name, external.source, NiL, 0)) && x != a)
{
x = source(x);
{
DEBUGSOURCE(ndirs, x, p);
}
}
{
z = source(z);
{
DEBUGSOURCE(ndirs, z, p);
}
}
}
if (a)
{
a = source(a);
{
DEBUGSOURCE(ndirs, a, p);
}
}
}
#if DEBUG
#endif
/*
* the nested loops preserve the directory search order
*
* . current directory
* <source>.<pattern> alternate directories (may pre-empt)
* <source>.x suffix association directories
* <source> default source directories
*/
ofiles = 0;
/*
* first check if r is an archive member
*/
if (r && r->active && (r->active->parent->target->property & P_archive) && !(r->dynamic & D_membertoo))
{
i = 0;
{
i = 1;
{
}
}
{
c = base[i];
base[i] = 0;
{
{
{
}
}
}
base[i] = c;
}
}
/*
* now check the directories
*/
for (i = ndirs; i-- > 0;)
{
{
d = p->rule;
if (!(d->mark & M_directory))
{
/*UNDENT*/
d->mark |= M_directory;
if (c && (d->property & P_terminal))
{
continue;
}
{
dirscan(d);
}
od = d;
if (s)
{
if (c)
{
if (s = strchr(s, '/'))
*s = 0;
{
if (s)
*s = '/';
continue;
}
else
{
break;
if (!f && !(z->uname))
{
if (s)
*s = '/';
continue;
}
if (s)
{
*s = '/';
}
}
if (s)
else
d = z;
}
else
for (;;)
if (*s++ != '.' || *s++ != '.' || *s++ != '/')
{
break;
}
{
dirscan(d);
}
}
continue;
else if (!files && name[0] == '.' && (name[1] == 0 || name[1] == '/' || name[1] == '.' && (name[2] == 0 || name[2] == '/' || state.fsview && name[2] == '.' && (name[3] == 0 || name[3] == '/'))))
{
}
#if DEBUG
#endif
{
{
{
else
canon(s);
{
x = 0;
{
if (!r)
}
if (x)
{
}
tm = 0;
else
{
}
}
else
if (tm)
{
{
found = 1;
goto clear;
}
break;
}
}
}
}
if (ofiles)
{
ofiles = 0;
}
/*INDENT*/
}
}
}
/*
* clear the visit marks
*/
for (i = ndirs; i-- > 0;)
if (!found && r && (name == r->name || reassoc) && (a = associate(internal.bind_p, r, NiL, NiL)) && (b = call(a, name)) && (s = getarg(&b, NiL)))
{
char* t;
/*
* [+|-] value [time]
*
* - name = name, time = time(value)
* + name = value, time = OLDTIME
* name = value, time = time(time)
*/
{
n = *s;
}
else
n = 0;
{
a = getrule(s);
if (n == '+')
{
}
else
{
if (t)
{
{
if (allocated)
else
allocated = 1;
continue;
}
{
tmxsetmtime(&st, 0);
}
else if (!a->time)
{
}
else
{
}
}
if (n == '-' || *b)
{
if (*s)
{
x = makerule(s);
if (!(state.questionable & 0x00000004) && r != x && !r->time && !x->time && !(r->property & P_virtual))
{
return bind(r);
}
}
}
else
{
aliased = 1;
canon(s);
}
}
od = 0;
found = 1;
}
else
}
break;
}
if (!found && state.targetcontext && r && name != r->name && (x = getrule(base)) && (x->dynamic & D_context))
{
for (i = ndirs; i-- > 0;)
{
{
d = p->rule;
if (!(d->mark & M_directory))
{
d->mark |= M_directory;
{
canon(s);
{
found = 1;
view = 0;
goto context;
}
}
}
}
}
for (i = ndirs; i-- > 0;)
}
if (found)
{
/*
* the file exists with:
*
* buf canonical file path name
* od original directory pointer
* st file stat() info
* tm file time
* view view index of dir containing file
*/
view = 0;
#if DEBUG
message((-11, "bindfile(%s): path=%s rule=%s alias=%s view=%d time=%s", name, b, r ? r->name : (char*)0, (x = getrule(b)) && x != r ? x->name : (char*)0, view, timestr(tm)));
#endif
if (!r)
{
{
r->dynamic |= D_membertoo;
}
{
}
if (!r->view && *b == '/')
{
{
}
}
if (x = getrule(b))
{
else if (x == r && (r->property & P_terminal))
{
putrule(b, 0);
x = 0;
}
}
if (x && x != r)
{
{
found = 0;
}
}
else
{
/*
* bind the rule to file name in b
* saving the unbound name
*/
s = r->name;
if (r->name != s)
{
r->uname = s;
if (od && (s != name || state.mam.statix || (n = strlen(r->name)) < (i = strlen(s)) || r->name[n - i - 1] != '/' || !streq(s, r->name + n - i)))
}
}
}
}
else if (!r)
{
goto done;
}
/*
* propagate pattern association attributes
*/
bindattribute(r);
/*
* archive binding and final checks
*/
if (found)
{
if (r->scan == SCAN_IGNORE)
arscan(r);
/*
* if name is partially qualified then check for
* alias bindings in the path suffixes of name
*/
while (s = strchr(s, '/'))
}
{
if (r->property & P_dontcare)
}
done:
if (allocated)
return r;
}
/*
* propagate pattern association attributes
*/
void
{
register Rule_t* x;
register Rule_t* z;
{
negate(z, r);
}
}
/*
* bind a rule, possibly changing the rule name
*/
{
register Rule_t* b;
if (!r)
return 0;
return r;
{
case 0:
return b;
break;
case P_state:
{
return b;
}
break;
case P_virtual:
break;
}
bindattribute(r);
return r;
}
/*
* rebind rule r
* op > 0 skips bindfile()
* op < 0 skips statetime()
*/
void
{
char* t;
Rule_t* x;
{
if (r->uname)
oldname(r);
if (op > 0)
else
{
r = x;
else
}
if (op >= 0)
r->dynamic |= D_triggered;
{
}
else if (op >= 0)
{
}
}
else if (r->property & P_statevar)
{
if (op <= 0)
return;
if (op > 0)
}
}
/*
* remove binding on r
* candidates have s==0 or r->mark==1
* h!=0 for alias reference
*/
int
unbind(const char* s, char* v, void* h)
{
if (!s || !h && (r->mark & M_mark) || h && (r->dynamic & D_alias) && (makerule(r->name)->mark & M_mark))
{
message((-2, "unbind(%s)%s%s", r->name, h ? " check-alias" : null, s && streq(s, r->name) ? null : " diff-hash"));
if (r->property & P_metarule)
r->uname = 0;
else
{
int u = 0;
{
if (r->uname)
{
oldname(r);
u = 1;
}
}
else if (r->property & P_staterule)
{
return 0;
}
r->must = 0;
r->scan = 0;
r->time = 0;
{
r->preview = 0;
r->view = 0;
}
}
}
return 0;
}
/*
* append views of absolute path s to source list z
*/
static List_t*
{
int i;
int j;
int n;
Rule_t* x;
char* t;
return z;
{
{
s += n;
j = i;
{
if (!(x = getrule(t)))
x = makerule(t);
if (!(x->mark & M_directory))
{
x->mark |= M_directory;
}
}
break;
}
}
return z;
}
/*
* fix up .SOURCE prereqs after user assertion
*/
{
register Rule_t* x;
return r;
{
x = r;
#if _HUH_2001_10_31
#endif
r->property |= P_readonly;
}
dynamic(r);
{
register char* s;
register char* t;
register List_t* p;
int dot;
unsigned int view;
List_t* z;
/*
* recompute view cross product
*
* . unit multiplication operand
* A absolute path rooted at /
* R path relative to .
*
* lhs rhs cross-product
* ---- ----- -------------
* . . . *note (1)*
* . A A *note (2)*
* . R R
* A . A
* A R A/R
* A A A *note (2)*
* R . R
* R A A *note (2)*
* R R R/R
*
* (1) the first . lhs operand produces a . in the product
*
* (2) the first A rhs operand is placed in the product
*/
z = &lst;
z->next = 0;
if (state.strictview)
{
/*
* this follows 3d fs semantics
*/
{
{
pathcanon(t, 0, 0);
if (!(x = getrule(t)))
x = makerule(t);
if (!(x->mark & M_directory))
{
x->mark |= M_directory;
}
if (*(t = unbound(x)) == '/')
}
{
{
#if BINDINDEX
#else
#endif
{
if (dot)
else
}
else
pathcanon(s, 0, 0);
x = makerule(s);
{
}
if (!(x->mark & M_directory))
{
x->mark |= M_directory;
}
}
}
}
}
else
{
List_t* q;
int dotted = 0;
q = r->prereqs;
do
{
{
#if BINDINDEX
#else
#endif
{
dotted = 1;
#if BINDINDEX
#else
#endif
}
for (p = q; p; p = p->next)
{
{
break;
}
{
if (*t == '.' && !*(t + 1))
{
if (!dotted)
{
dotted = 1;
pathcanon(t, 0, 0);
}
if (dot)
continue;
}
else
{
if (dot)
else
}
pathcanon(t, 0, 0);
x = makerule(t);
{
}
if (!(x->mark & M_directory))
{
x->mark |= M_directory;
}
}
}
}
{
pathcanon(t, 0, 0);
if (!(x = getrule(t)))
x = makerule(t);
if (!(x->mark & M_directory))
{
x->mark |= M_directory;
}
if (*(s = unbound(x)) == '/')
}
} while (q = p);
}
}
return r;
}
#if BINDINDEX
/*
* copy path name of r to s
* end of s is returned
*/
char*
{
{
{
*s++ = '/';
}
{
*s++ = '/';
}
}
}
#endif
/*
* return local view path name for r
*/
char*
{
register Rule_t* x;
int i;
{
register char* s = r->name;
if (*s == '/' || iscontext(s))
return s;
{
x->property |= P_internal;
x->name = s;
}
return x->uname;
}
return r->name;
if (x = localrule(r, 1))
return x->name;
{
{
if (*r->name != '/')
return r->name;
if (strneq(r->name, state.view[i].path, state.view[i].pathlen) && r->name[state.view[i].pathlen] == '/')
}
return r->uname;
}
return r->name;
}