/* $NetBSD: files.c,v 1.36.16.2 2024/08/23 17:21:05 martin Exp $ */ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratories. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)files.c 8.1 (Berkeley) 6/6/93 */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include __RCSID("$NetBSD: files.c,v 1.36.16.2 2024/08/23 17:21:05 martin Exp $"); #include #include #include #include #include #include #include #include "defs.h" extern const char *yyfile; int nallfiles; size_t nselfiles; struct files **selfiles; /* * We check that each full path name is unique. File base names * should generally also be unique, e.g., having both a net/xx.c and * a kern/xx.c (or, worse, a net/xx.c and a new/xx.c++) is probably * wrong, but is permitted under some conditions. */ static struct hashtab *basetab; /* file base names */ static struct hashtab *pathtab; /* full path names */ static struct files **unchecked; static void addfiletoattr(const char *, struct files *); static int checkaux(const char *, void *); static int fixcount(const char *, void *); static int fixfsel(const char *, void *); static int fixsel(const char *, void *); void initfiles(void) { basetab = ht_new(); pathtab = ht_new(); TAILQ_INIT(&allfiles); TAILQ_INIT(&allcfiles); TAILQ_INIT(&allsfiles); TAILQ_INIT(&allofiles); unchecked = &TAILQ_FIRST(&allfiles); } void addfile(const char *path, struct condexpr *optx, u_char flags, const char *rule) { struct files *fi; const char *dotp, *tail; size_t baselen; size_t dirlen; int needc, needf; char base[200]; char dir[MAXPATHLEN]; /* check various errors */ needc = flags & FI_NEEDSCOUNT; needf = flags & FI_NEEDSFLAG; if (needc && needf) { cfgerror("cannot mix needs-count and needs-flag"); goto bad; } if (optx == NULL && (needc || needf)) { cfgerror("nothing to %s for %s", needc ? "count" : "flag", path); goto bad; } if (*path == '/') { cfgerror("path must be relative"); goto bad; } /* find last part of pathname, and same without trailing suffix */ tail = strrchr(path, '/'); if (tail == NULL) { dirlen = 0; tail = path; } else { dirlen = (size_t)(tail - path); tail++; } memcpy(dir, path, dirlen); dir[dirlen] = '\0'; dotp = strrchr(tail, '.'); if (dotp == NULL || dotp[1] == 0 || (baselen = (size_t)(dotp - tail)) >= sizeof(base)) { cfgerror("invalid pathname `%s'", path); goto bad; } /* * Commit this file to memory. We will decide later whether it * will be used after all. */ fi = ecalloc(1, sizeof *fi); if (ht_insert(pathtab, path, fi)) { free(fi); if ((fi = ht_lookup(pathtab, path)) == NULL) panic("addfile: ht_lookup(%s)", path); /* * If it's a duplicate entry, it is must specify a make * rule, and only a make rule, and must come from * a different source file than the original entry. * If it does otherwise, it is disallowed. This allows * machine-dependent files to override the compilation * options for specific files. */ if (rule != NULL && optx == NULL && flags == 0 && yyfile != fi->fi_where.w_srcfile) { fi->fi_mkrule = rule; return; } cfgerror("duplicate file %s", path); cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, "here is the original definition"); goto bad; } memcpy(base, tail, baselen); base[baselen] = '\0'; fi->fi_where.w_srcfile = yyfile; fi->fi_where.w_srcline = currentline(); fi->fi_flags = flags; fi->fi_path = path; fi->fi_tail = tail; fi->fi_base = intern(base); fi->fi_dir = intern(dir); fi->fi_prefix = SLIST_EMPTY(&prefixes) ? NULL : SLIST_FIRST(&prefixes)->pf_prefix; fi->fi_buildprefix = SLIST_EMPTY(&buildprefixes) ? NULL : SLIST_FIRST(&buildprefixes)->pf_prefix; fi->fi_len = strlen(path); fi->fi_suffix = path[fi->fi_len - 1]; fi->fi_optx = optx; fi->fi_optf = NULL; fi->fi_mkrule = rule; fi->fi_attr = NULL; fi->fi_order = (int)nallfiles + (includedepth << 16); switch (fi->fi_suffix) { case 'c': TAILQ_INSERT_TAIL(&allcfiles, fi, fi_snext); TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); break; case 'S': fi->fi_suffix = 's'; /* FALLTHRU */ case 's': TAILQ_INSERT_TAIL(&allsfiles, fi, fi_snext); TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); break; case 'o': TAILQ_INSERT_TAIL(&allofiles, fi, fi_snext); TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); break; default: cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, "unknown suffix"); break; } CFGDBG(3, "file added `%s' at order score %d", fi->fi_path, fi->fi_order); nallfiles++; return; bad: if (optx != NULL) { condexpr_destroy(optx); } } static void addfiletoattr(const char *name, struct files *fi) { struct attr *a; a = ht_lookup(attrtab, name); if (a == NULL) { CFGDBG(1, "attr `%s' not found", name); } else { fi->fi_attr = a; TAILQ_INSERT_TAIL(&a->a_files, fi, fi_anext); } } /* * We have finished reading some "files" file, either ../../conf/files * or ./files.$machine. Make sure that everything that is flagged as * needing a count is reasonable. (This prevents ../../conf/files from * depending on some machine-specific device.) */ void checkfiles(void) { struct files *fi, *last; last = NULL; for (fi = *unchecked; fi != NULL; last = fi, fi = TAILQ_NEXT(fi, fi_next)) { if ((fi->fi_flags & FI_NEEDSCOUNT) != 0) (void)expr_eval(fi->fi_optx, checkaux, fi); } if (last != NULL) unchecked = &TAILQ_NEXT(last, fi_next); } /* * Auxiliary function for checkfiles, called from expr_eval. * We are not actually interested in the expression's value. */ static int checkaux(const char *name, void *context) { struct files *fi = context; if (ht_lookup(devbasetab, name) == NULL) { cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, "`%s' is not a countable device", name); /* keep fixfiles() from complaining again */ fi->fi_flags |= FI_HIDDEN; } return (0); } static int cmpfiles(const void *a, const void *b) { const struct files * const *fia = a, * const *fib = b; int sa = (*fia)->fi_order; int sb = (*fib)->fi_order; if (sa < sb) return -1; else if (sa > sb) return +1; else abort(); /* no ties possible */ } /* * We have finished reading everything. Tack the files down: calculate * selection and counts as needed. Check that the object files built * from the selected sources do not collide. */ int fixfiles(void) { struct files *fi, *ofi; struct nvlist *flathead, **flatp; int err, sel; struct config *cf; char swapname[100]; /* Place these files at last. */ int onallfiles = nallfiles; nallfiles = 1 << 30; addfile("devsw.c", NULL, 0, NULL); addfile("ioconf.c", NULL, 0, NULL); TAILQ_FOREACH(cf, &allcf, cf_next) { (void)snprintf(swapname, sizeof(swapname), "swap%s.c", cf->cf_name); addfile(intern(swapname), NULL, 0, NULL); } nallfiles = onallfiles; err = 0; TAILQ_FOREACH(fi, &allfiles, fi_next) { /* Skip files that generated counted-device complaints. */ if (fi->fi_flags & FI_HIDDEN) continue; if (fi->fi_optx != NULL) { if (fi->fi_optx->cx_type == CX_ATOM) { addfiletoattr(fi->fi_optx->cx_u.atom, fi); } 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 (fi->fi_attr && fi->fi_attr->a_deselected) { CFGDBG(5, "file `%s' deselected because attr `%s' was", fi->fi_path, fi->fi_attr->a_name); continue; } /* We like this file. Make sure it generates a unique .o. */ 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 the new file comes from a different source, * allow the new one to override the old one. */ if (fi->fi_path != ofi->fi_path) { if (ht_replace(basetab, fi->fi_base, fi) != 1) panic("fixfiles ht_replace(%s)", fi->fi_base); ofi->fi_flags &= (u_char)~FI_SEL; ofi->fi_flags |= FI_HIDDEN; } else { cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, "object file collision on %s.o, from %s", fi->fi_base, fi->fi_path); cfgxerror(ofi->fi_where.w_srcfile, ofi->fi_where.w_srcline, "here is the previous file: %s", ofi->fi_path); err = 1; } } fi->fi_flags |= FI_SEL; nselfiles++; CFGDBG(3, "file selected `%s'", fi->fi_path); /* Add other files to the default "netbsd" attribute. */ if (fi->fi_attr == NULL) { addfiletoattr(allattr.a_name, fi); } CFGDBG(3, "file `%s' belongs to attr `%s'", fi->fi_path, fi->fi_attr->a_name); } /* Order files. */ selfiles = malloc(nselfiles * sizeof(fi)); unsigned i = 0; TAILQ_FOREACH(fi, &allfiles, fi_next) { if ((fi->fi_flags & FI_SEL) == 0) continue; selfiles[i++] = fi; } assert(i <= nselfiles); nselfiles = i; qsort(selfiles, nselfiles, (unsigned)sizeof(fi), cmpfiles); return (err); } /* * We have finished reading everything. Tack the devsws down: calculate * selection. */ int fixdevsw(void) { int error; struct devm *dm, *res; struct hashtab *fixdevmtab; char mstr[16]; error = 0; fixdevmtab = ht_new(); TAILQ_FOREACH(dm, &alldevms, dm_next) { res = ht_lookup(fixdevmtab, intern(dm->dm_name)); if (res != NULL) { if (res->dm_cmajor != dm->dm_cmajor || res->dm_bmajor != dm->dm_bmajor) { cfgxerror(res->dm_where.w_srcfile, res->dm_where.w_srcline, "device-major '%s' " "block %d, char %d redefined" " at %s:%d as block %d, char %d", res->dm_name, res->dm_bmajor, res->dm_cmajor, dm->dm_where.w_srcfile, dm->dm_where.w_srcline, dm->dm_bmajor, dm->dm_cmajor); } else { cfgxerror(res->dm_where.w_srcfile, res->dm_where.w_srcline, "device-major '%s' " "(block %d, char %d) duplicated" " at %s:%d", dm->dm_name, dm->dm_bmajor, dm->dm_cmajor, dm->dm_where.w_srcfile, dm->dm_where.w_srcline); } error = 1; goto out; } if (ht_insert(fixdevmtab, intern(dm->dm_name), dm)) { panic("fixdevsw: %s char %d block %d", dm->dm_name, dm->dm_cmajor, dm->dm_bmajor); } if (dm->dm_opts != NULL && !expr_eval(dm->dm_opts, fixsel, NULL)) continue; if (dm->dm_cmajor != NODEVMAJOR) { if (ht_lookup(cdevmtab, intern(dm->dm_name)) != NULL) { cfgxerror(dm->dm_where.w_srcfile, dm->dm_where.w_srcline, "device-major of character device '%s' " "is already defined", dm->dm_name); error = 1; goto out; } (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_cmajor); if (ht_lookup(cdevmtab, intern(mstr)) != NULL) { cfgxerror(dm->dm_where.w_srcfile, dm->dm_where.w_srcline, "device-major of character major '%d' " "is already defined", dm->dm_cmajor); error = 1; goto out; } if (ht_insert(cdevmtab, intern(dm->dm_name), dm) || ht_insert(cdevmtab, intern(mstr), dm)) { panic("fixdevsw: %s character major %d", dm->dm_name, dm->dm_cmajor); } } if (dm->dm_bmajor != NODEVMAJOR) { if (ht_lookup(bdevmtab, intern(dm->dm_name)) != NULL) { cfgxerror(dm->dm_where.w_srcfile, dm->dm_where.w_srcline, "device-major of block device '%s' " "is already defined", dm->dm_name); error = 1; goto out; } (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_bmajor); if (ht_lookup(bdevmtab, intern(mstr)) != NULL) { cfgxerror(dm->dm_where.w_srcfile, dm->dm_where.w_srcline, "device-major of block major '%d' " "is already defined", dm->dm_bmajor); error = 1; goto out; } if (ht_insert(bdevmtab, intern(dm->dm_name), dm) || ht_insert(bdevmtab, intern(mstr), dm)) { panic("fixdevsw: %s block major %d", dm->dm_name, dm->dm_bmajor); } } } out: ht_free(fixdevmtab); return (error); } /* * Called when evaluating a needs-count expression. Make sure the * atom is a countable device. The expression succeeds iff there * is at least one of them (note that while `xx*' will not always * set xx's d_umax > 0, you cannot mix '*' and needs-count). The * mkheaders() routine wants a flattened, in-order list of the * atoms for `#define name value' lines, so we build that as we * are called to eval each atom. */ 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) /* cannot occur here; we checked earlier */ 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); } /* * Called from fixfiles when eval'ing a selection expression for a * file that will generate a .h with flags. We will need the flat list. */ 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); } /* * As for fixfsel above, but we do not need the flat list. */ static int /*ARGSUSED*/ fixsel(const char *name, void *context) { return (ht_lookup(selecttab, name) != NULL); } /* * Eval an expression tree. Calls the given function on each node, * passing it the given context & the name; return value is &/|/! of * results of evaluating atoms. * * No short circuiting ever occurs. fn must return 0 or 1 (otherwise * our mixing of C's bitwise & boolean here may give surprises). */ int expr_eval(struct condexpr *expr, int (*fn)(const char *, void *), void *ctx) { int lhs, rhs; switch (expr->cx_type) { case CX_ATOM: return ((*fn)(expr->cx_atom, ctx)); case CX_NOT: return (!expr_eval(expr->cx_not, fn, ctx)); case CX_AND: lhs = expr_eval(expr->cx_and.left, fn, ctx); rhs = expr_eval(expr->cx_and.right, fn, ctx); return (lhs & rhs); case CX_OR: lhs = expr_eval(expr->cx_or.left, fn, ctx); rhs = expr_eval(expr->cx_or.right, fn, ctx); return (lhs | rhs); } panic("invalid condexpr type %d", (int)expr->cx_type); /* NOTREACHED */ return (0); } #ifdef DEBUG /* * Print expression tree. */ void prexpr(struct nvlist *expr) { static void pr0(); printf("expr ="); pr0(expr); printf("\n"); (void)fflush(stdout); } static void pr0(struct nvlist *e) { switch (e->nv_num) { 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(" (?%lld?", e->nv_num); break; } if (e->nv_ptr) pr0(e->nv_ptr); pr0(e->nv_next); printf(")"); } #endif