#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
extern const char *yyfile;
static struct hashtab *basetab;
static struct hashtab *pathtab;
static struct files **nextfile;
static struct files **unchecked;
static struct objects **nextobject;
static int checkaux(const char *, void *);
static int fixcount(const char *, void *);
static int fixfsel(const char *, void *);
static int fixsel(const char *, void *);
static int expr_eval(struct nvlist *,
int (*)(const char *, void *), void *);
static void expr_free(struct nvlist *);
#ifdef DEBUG
static void pr0();
#endif
void
initfiles(void)
{
basetab = ht_new();
pathtab = ht_new();
nextfile = &allfiles;
unchecked = &allfiles;
nextobject = &allobjects;
}
void
addfile(struct nvlist *nvpath, struct nvlist *optx, int flags, const char *rule)
{
struct files *fi;
const char *dotp, *dotp1, *tail, *path, *tail1 = NULL;
struct nvlist *nv;
size_t baselen;
int needc, needf;
char base[200];
needc = flags & FI_NEEDSCOUNT;
needf = flags & FI_NEEDSFLAG;
if (needc && needf) {
error("cannot mix needs-count and needs-flag");
goto bad;
}
if (optx == NULL && (needc || needf)) {
error("nothing to %s", needc ? "count" : "flag");
goto bad;
}
for (nv = nvpath; nv; nv = nv->nv_next) {
path = nv->nv_name;
tail = strrchr(path, '/');
if (tail == NULL)
tail = path;
else
tail++;
dotp = strrchr(tail, '.');
if (dotp == NULL || dotp[1] == 0 ||
(baselen = dotp - tail) >= sizeof(base)) {
error("invalid pathname `%s'", path);
goto bad;
}
if (tail1 &&
(dotp - tail != dotp1 - tail1 ||
strncmp(tail1, tail, dotp - tail)))
error("different production from %s %s",
nvpath->nv_name, tail);
tail1 = tail;
dotp1 = dotp;
}
fi = emalloc(sizeof *fi);
if (ht_insert(pathtab, path, fi)) {
free(fi);
if ((fi = ht_lookup(pathtab, path)) == NULL)
panic("addfile: ht_lookup(%s)", path);
error("duplicate file %s", path);
xerror(fi->fi_srcfile, fi->fi_srcline,
"here is the original definition");
}
memcpy(base, tail, baselen);
base[baselen] = 0;
fi->fi_next = NULL;
fi->fi_srcfile = yyfile;
fi->fi_srcline = currentline();
fi->fi_flags = flags;
fi->fi_nvpath = nvpath;
fi->fi_base = intern(base);
fi->fi_optx = optx;
fi->fi_optf = NULL;
fi->fi_mkrule = rule;
*nextfile = fi;
nextfile = &fi->fi_next;
return;
bad:
expr_free(optx);
}
void
addobject(const char *path, struct nvlist *optx, int flags)
{
struct objects *oi;
oi = emalloc(sizeof *oi);
if (ht_insert(pathtab, path, oi)) {
free(oi);
if ((oi = ht_lookup(pathtab, path)) == NULL)
panic("addfile: ht_lookup(%s)", path);
error("duplicate file %s", path);
xerror(oi->oi_srcfile, oi->oi_srcline,
"here is the original definition");
}
oi->oi_next = NULL;
oi->oi_srcfile = yyfile;
oi->oi_srcline = currentline();
oi->oi_flags = flags;
oi->oi_path = path;
oi->oi_optx = optx;
oi->oi_optf = NULL;
*nextobject = oi;
nextobject = &oi->oi_next;
}
void
checkfiles(void)
{
struct files *fi, *last;
last = NULL;
for (fi = *unchecked; fi != NULL; last = fi, fi = fi->fi_next)
if ((fi->fi_flags & FI_NEEDSCOUNT) != 0)
(void)expr_eval(fi->fi_optx, checkaux, fi);
if (last != NULL)
unchecked = &last->fi_next;
}
static int
checkaux(const char *name, void *context)
{
struct files *fi = context;
if (ht_lookup(devbasetab, name) == NULL) {
xerror(fi->fi_srcfile, fi->fi_srcline,
"`%s' is not a countable device",
name);
fi->fi_flags |= FI_HIDDEN;
}
return (0);
}
int
fixfiles(void)
{
struct files *fi, *ofi;
struct nvlist *flathead, **flatp;
int err, sel;
err = 0;
for (fi = allfiles; fi != NULL; fi = fi->fi_next) {
if (fi->fi_flags & FI_HIDDEN)
continue;
if (fi->fi_optx != NULL) {
flathead = NULL;
flatp = &flathead;
sel = expr_eval(fi->fi_optx,
fi->fi_flags & FI_NEEDSCOUNT ? fixcount :
fi->fi_flags & FI_NEEDSFLAG ? fixfsel :
fixsel,
&flatp);
fi->fi_optf = flathead;
if (!sel)
continue;
}
if (ht_insert(basetab, fi->fi_base, fi)) {
if ((ofi = ht_lookup(basetab, fi->fi_base)) == NULL)
panic("fixfiles ht_lookup(%s)", fi->fi_base);
if (fi->fi_nvpath != ofi->fi_nvpath) {
if (ht_replace(basetab, fi->fi_base, fi) != 1)
panic("fixfiles ht_replace(%s)",
fi->fi_base);
ofi->fi_flags &= ~FI_SEL;
ofi->fi_flags |= FI_HIDDEN;
} else {
xerror(fi->fi_srcfile, fi->fi_srcline,
"object file collision on %s.o, from %s",
fi->fi_base, fi->fi_nvpath->nv_name);
xerror(ofi->fi_srcfile, ofi->fi_srcline,
"here is the previous file: %s",
ofi->fi_nvpath->nv_name);
err = 1;
}
}
fi->fi_flags |= FI_SEL;
}
return (err);
}
int
fixobjects(void)
{
struct objects *oi;
struct nvlist *flathead, **flatp;
int err, sel;
err = 0;
for (oi = allobjects; oi != NULL; oi = oi->oi_next) {
if (oi->oi_optx != NULL) {
flathead = NULL;
flatp = &flathead;
sel = expr_eval(oi->oi_optx,
oi->oi_flags & OI_NEEDSFLAG ? fixfsel :
fixsel,
&flatp);
oi->oi_optf = flathead;
if (!sel)
continue;
}
oi->oi_flags |= OI_SEL;
}
return (err);
}
static int
fixcount(const char *name, void *context)
{
struct nvlist ***p = context;
struct devbase *dev;
struct nvlist *nv;
dev = ht_lookup(devbasetab, name);
if (dev == NULL)
panic("fixcount(%s)", name);
nv = newnv(name, NULL, NULL, dev->d_umax, NULL);
**p = nv;
*p = &nv->nv_next;
(void)ht_insert(needcnttab, name, nv);
return (dev->d_umax != 0);
}
static int
fixfsel(const char *name, void *context)
{
struct nvlist ***p = context;
struct nvlist *nv;
int sel;
sel = ht_lookup(selecttab, name) != NULL;
nv = newnv(name, NULL, NULL, sel, NULL);
**p = nv;
*p = &nv->nv_next;
return (sel);
}
static int
fixsel(const char *name, void *context)
{
return (ht_lookup(selecttab, name) != NULL);
}
static int
expr_eval(struct nvlist *expr, int (*fn)(const char *, void *), void *context)
{
int lhs, rhs;
switch (expr->nv_int) {
case FX_ATOM:
return ((*fn)(expr->nv_name, context));
case FX_NOT:
return (!expr_eval(expr->nv_next, fn, context));
case FX_AND:
lhs = expr_eval(expr->nv_ptr, fn, context);
rhs = expr_eval(expr->nv_next, fn, context);
return (lhs & rhs);
case FX_OR:
lhs = expr_eval(expr->nv_ptr, fn, context);
rhs = expr_eval(expr->nv_next, fn, context);
return (lhs | rhs);
}
panic("expr_eval %d", expr->nv_int);
return (0);
}
static void
expr_free(struct nvlist *expr)
{
struct nvlist *rhs;
for (; expr != NULL; expr = rhs) {
switch (expr->nv_int) {
case FX_ATOM:
case FX_NOT:
break;
case FX_AND:
case FX_OR:
expr_free(expr->nv_ptr);
break;
default:
panic("expr_free %d", expr->nv_int);
}
rhs = expr->nv_next;
nvfree(expr);
}
}
#ifdef DEBUG
void
prexpr(struct nvlist *expr)
{
printf("expr =");
pr0(expr);
printf("\n");
(void)fflush(stdout);
}
static void
pr0(struct nvlist *e)
{
switch (e->nv_int) {
case FX_ATOM:
printf(" %s", e->nv_name);
return;
case FX_NOT:
printf(" (!");
break;
case FX_AND:
printf(" (&");
break;
case FX_OR:
printf(" (|");
break;
default:
printf(" (?%d?", e->nv_int);
break;
}
if (e->nv_ptr)
pr0(e->nv_ptr);
pr0(e->nv_next);
printf(")");
}
#endif