1 /* $NetBSD: files.c,v 1.37 2020/03/07 19:26:13 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * All advertising materials mentioning features or use of this software 12 * must display the following acknowledgement: 13 * This product includes software developed by the University of 14 * California, Lawrence Berkeley Laboratories. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * from: @(#)files.c 8.1 (Berkeley) 6/6/93 41 */ 42 43 #if HAVE_NBTOOL_CONFIG_H 44 #include "nbtool_config.h" 45 #endif 46 47 #include <sys/cdefs.h> 48 __RCSID("$NetBSD: files.c,v 1.37 2020/03/07 19:26:13 christos Exp $"); 49 50 #include <sys/param.h> 51 #include <assert.h> 52 #include <errno.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <util.h> 57 #include "defs.h" 58 59 extern const char *yyfile; 60 61 int nallfiles; 62 size_t nselfiles; 63 struct files **selfiles; 64 65 /* 66 * We check that each full path name is unique. File base names 67 * should generally also be unique, e.g., having both a net/xx.c and 68 * a kern/xx.c (or, worse, a net/xx.c and a new/xx.c++) is probably 69 * wrong, but is permitted under some conditions. 70 */ 71 static struct hashtab *basetab; /* file base names */ 72 static struct hashtab *pathtab; /* full path names */ 73 74 static struct files **unchecked; 75 76 static void addfiletoattr(const char *, struct files *); 77 static int checkaux(const char *, void *); 78 static int fixcount(const char *, void *); 79 static int fixfsel(const char *, void *); 80 static int fixsel(const char *, void *); 81 82 void 83 initfiles(void) 84 { 85 86 basetab = ht_new(); 87 pathtab = ht_new(); 88 TAILQ_INIT(&allfiles); 89 TAILQ_INIT(&allcfiles); 90 TAILQ_INIT(&allsfiles); 91 TAILQ_INIT(&allofiles); 92 unchecked = &TAILQ_FIRST(&allfiles); 93 } 94 95 void 96 addfile(const char *path, struct condexpr *optx, u_char flags, const char *rule) 97 { 98 struct files *fi; 99 const char *dotp, *tail; 100 size_t baselen; 101 size_t dirlen; 102 int needc, needf; 103 char base[200]; 104 char dir[MAXPATHLEN]; 105 106 /* check various errors */ 107 needc = flags & FI_NEEDSCOUNT; 108 needf = flags & FI_NEEDSFLAG; 109 if (needc && needf) { 110 cfgerror("cannot mix needs-count and needs-flag"); 111 goto bad; 112 } 113 if (optx == NULL && (needc || needf)) { 114 cfgerror("nothing to %s for %s", needc ? "count" : "flag", 115 path); 116 goto bad; 117 } 118 if (*path == '/') { 119 cfgerror("path must be relative"); 120 goto bad; 121 } 122 123 /* find last part of pathname, and same without trailing suffix */ 124 tail = strrchr(path, '/'); 125 if (tail == NULL) { 126 dirlen = 0; 127 tail = path; 128 } else { 129 dirlen = (size_t)(tail - path); 130 tail++; 131 } 132 memcpy(dir, path, dirlen); 133 dir[dirlen] = '\0'; 134 135 dotp = strrchr(tail, '.'); 136 if (dotp == NULL || dotp[1] == 0 || 137 (baselen = (size_t)(dotp - tail)) >= sizeof(base)) { 138 cfgerror("invalid pathname `%s'", path); 139 goto bad; 140 } 141 142 /* 143 * Commit this file to memory. We will decide later whether it 144 * will be used after all. 145 */ 146 fi = ecalloc(1, sizeof *fi); 147 if (ht_insert(pathtab, path, fi)) { 148 free(fi); 149 if ((fi = ht_lookup(pathtab, path)) == NULL) 150 panic("addfile: ht_lookup(%s)", path); 151 152 /* 153 * If it's a duplicate entry, it is must specify a make 154 * rule, and only a make rule, and must come from 155 * a different source file than the original entry. 156 * If it does otherwise, it is disallowed. This allows 157 * machine-dependent files to override the compilation 158 * options for specific files. 159 */ 160 if (rule != NULL && optx == NULL && flags == 0 && 161 yyfile != fi->fi_where.w_srcfile) { 162 fi->fi_mkrule = rule; 163 return; 164 } 165 cfgerror("duplicate file %s", path); 166 cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, 167 "here is the original definition"); 168 goto bad; 169 } 170 memcpy(base, tail, baselen); 171 base[baselen] = '\0'; 172 fi->fi_where.w_srcfile = yyfile; 173 fi->fi_where.w_srcline = currentline(); 174 fi->fi_flags = flags; 175 fi->fi_path = path; 176 fi->fi_tail = tail; 177 fi->fi_base = intern(base); 178 fi->fi_dir = intern(dir); 179 fi->fi_prefix = SLIST_EMPTY(&prefixes) ? NULL : 180 SLIST_FIRST(&prefixes)->pf_prefix; 181 fi->fi_buildprefix = SLIST_EMPTY(&buildprefixes) ? NULL : 182 SLIST_FIRST(&buildprefixes)->pf_prefix; 183 fi->fi_len = strlen(path); 184 fi->fi_suffix = path[fi->fi_len - 1]; 185 fi->fi_optx = optx; 186 fi->fi_optf = NULL; 187 fi->fi_mkrule = rule; 188 fi->fi_attr = NULL; 189 fi->fi_order = (int)nallfiles + (includedepth << 16); 190 switch (fi->fi_suffix) { 191 case 'c': 192 TAILQ_INSERT_TAIL(&allcfiles, fi, fi_snext); 193 TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); 194 break; 195 case 'S': 196 fi->fi_suffix = 's'; 197 /* FALLTHRU */ 198 case 's': 199 TAILQ_INSERT_TAIL(&allsfiles, fi, fi_snext); 200 TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); 201 break; 202 case 'o': 203 TAILQ_INSERT_TAIL(&allofiles, fi, fi_snext); 204 TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); 205 break; 206 default: 207 cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, 208 "unknown suffix"); 209 break; 210 } 211 CFGDBG(3, "file added `%s' at order score %d", fi->fi_path, fi->fi_order); 212 nallfiles++; 213 return; 214 bad: 215 if (optx != NULL) { 216 condexpr_destroy(optx); 217 } 218 } 219 220 static void 221 addfiletoattr(const char *name, struct files *fi) 222 { 223 struct attr *a; 224 225 a = ht_lookup(attrtab, name); 226 if (a == NULL) { 227 CFGDBG(1, "attr `%s' not found", name); 228 } else { 229 fi->fi_attr = a; 230 TAILQ_INSERT_TAIL(&a->a_files, fi, fi_anext); 231 } 232 } 233 234 /* 235 * We have finished reading some "files" file, either ../../conf/files 236 * or ./files.$machine. Make sure that everything that is flagged as 237 * needing a count is reasonable. (This prevents ../../conf/files from 238 * depending on some machine-specific device.) 239 */ 240 void 241 checkfiles(void) 242 { 243 struct files *fi, *last; 244 245 last = NULL; 246 for (fi = *unchecked; fi != NULL; 247 last = fi, fi = TAILQ_NEXT(fi, fi_next)) { 248 if ((fi->fi_flags & FI_NEEDSCOUNT) != 0) 249 (void)expr_eval(fi->fi_optx, checkaux, fi); 250 } 251 if (last != NULL) 252 unchecked = &TAILQ_NEXT(last, fi_next); 253 } 254 255 /* 256 * Auxiliary function for checkfiles, called from expr_eval. 257 * We are not actually interested in the expression's value. 258 */ 259 static int 260 checkaux(const char *name, void *context) 261 { 262 struct files *fi = context; 263 264 if (ht_lookup(devbasetab, name) == NULL) { 265 cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, 266 "`%s' is not a countable device", 267 name); 268 /* keep fixfiles() from complaining again */ 269 fi->fi_flags |= FI_HIDDEN; 270 } 271 return (0); 272 } 273 274 static int 275 cmpfiles(const void *a, const void *b) 276 { 277 const struct files * const *fia = a, * const *fib = b; 278 int sa = (*fia)->fi_order; 279 int sb = (*fib)->fi_order; 280 281 if (sa < sb) 282 return -1; 283 else if (sa > sb) 284 return 1; 285 else 286 return 0; 287 } 288 289 /* 290 * We have finished reading everything. Tack the files down: calculate 291 * selection and counts as needed. Check that the object files built 292 * from the selected sources do not collide. 293 */ 294 int 295 fixfiles(void) 296 { 297 struct files *fi, *ofi; 298 struct nvlist *flathead, **flatp; 299 int err, sel; 300 struct config *cf; 301 char swapname[100]; 302 303 /* Place these files at last. */ 304 int onallfiles = nallfiles; 305 nallfiles = 1 << 30; 306 addfile("devsw.c", NULL, 0, NULL); 307 addfile("ioconf.c", NULL, 0, NULL); 308 309 TAILQ_FOREACH(cf, &allcf, cf_next) { 310 (void)snprintf(swapname, sizeof(swapname), "swap%s.c", 311 cf->cf_name); 312 addfile(intern(swapname), NULL, 0, NULL); 313 } 314 nallfiles = onallfiles; 315 316 err = 0; 317 TAILQ_FOREACH(fi, &allfiles, fi_next) { 318 319 /* Skip files that generated counted-device complaints. */ 320 if (fi->fi_flags & FI_HIDDEN) 321 continue; 322 323 if (fi->fi_optx != NULL) { 324 if (fi->fi_optx->cx_type == CX_ATOM) { 325 addfiletoattr(fi->fi_optx->cx_u.atom, fi); 326 } 327 flathead = NULL; 328 flatp = &flathead; 329 sel = expr_eval(fi->fi_optx, 330 fi->fi_flags & FI_NEEDSCOUNT ? fixcount : 331 fi->fi_flags & FI_NEEDSFLAG ? fixfsel : 332 fixsel, 333 &flatp); 334 fi->fi_optf = flathead; 335 if (!sel) 336 continue; 337 } 338 if (fi->fi_attr && fi->fi_attr->a_deselected) { 339 CFGDBG(5, "file `%s' deselected because attr `%s' was", 340 fi->fi_path, fi->fi_attr->a_name); 341 continue; 342 } 343 344 /* We like this file. Make sure it generates a unique .o. */ 345 if (ht_insert(basetab, fi->fi_base, fi)) { 346 if ((ofi = ht_lookup(basetab, fi->fi_base)) == NULL) 347 panic("fixfiles ht_lookup(%s)", fi->fi_base); 348 /* 349 * If the new file comes from a different source, 350 * allow the new one to override the old one. 351 */ 352 if (fi->fi_path != ofi->fi_path) { 353 if (ht_replace(basetab, fi->fi_base, fi) != 1) 354 panic("fixfiles ht_replace(%s)", 355 fi->fi_base); 356 ofi->fi_flags &= (u_char)~FI_SEL; 357 ofi->fi_flags |= FI_HIDDEN; 358 } else { 359 cfgxerror(fi->fi_where.w_srcfile, fi->fi_where.w_srcline, 360 "object file collision on %s.o, from %s", 361 fi->fi_base, fi->fi_path); 362 cfgxerror(ofi->fi_where.w_srcfile, ofi->fi_where.w_srcline, 363 "here is the previous file: %s", 364 ofi->fi_path); 365 err = 1; 366 } 367 } 368 fi->fi_flags |= FI_SEL; 369 nselfiles++; 370 CFGDBG(3, "file selected `%s'", fi->fi_path); 371 372 /* Add other files to the default "netbsd" attribute. */ 373 if (fi->fi_attr == NULL) { 374 addfiletoattr(allattr.a_name, fi); 375 } 376 CFGDBG(3, "file `%s' belongs to attr `%s'", fi->fi_path, 377 fi->fi_attr->a_name); 378 } 379 380 /* Order files. */ 381 selfiles = malloc(nselfiles * sizeof(fi)); 382 unsigned i = 0; 383 TAILQ_FOREACH(fi, &allfiles, fi_next) { 384 if ((fi->fi_flags & FI_SEL) == 0) 385 continue; 386 selfiles[i++] = fi; 387 } 388 assert(i <= nselfiles); 389 nselfiles = i; 390 qsort(selfiles, nselfiles, (unsigned)sizeof(fi), cmpfiles); 391 return (err); 392 } 393 394 395 /* 396 * We have finished reading everything. Tack the devsws down: calculate 397 * selection. 398 */ 399 int 400 fixdevsw(void) 401 { 402 int error; 403 struct devm *dm, *res; 404 struct hashtab *fixdevmtab; 405 char mstr[16]; 406 407 error = 0; 408 fixdevmtab = ht_new(); 409 410 TAILQ_FOREACH(dm, &alldevms, dm_next) { 411 res = ht_lookup(fixdevmtab, intern(dm->dm_name)); 412 if (res != NULL) { 413 if (res->dm_cmajor != dm->dm_cmajor || 414 res->dm_bmajor != dm->dm_bmajor) { 415 cfgxerror(res->dm_where.w_srcfile, 416 res->dm_where.w_srcline, 417 "device-major '%s' " 418 "block %d, char %d redefined" 419 " at %s:%d as block %d, char %d", 420 res->dm_name, 421 res->dm_bmajor, res->dm_cmajor, 422 dm->dm_where.w_srcfile, dm->dm_where.w_srcline, 423 dm->dm_bmajor, dm->dm_cmajor); 424 } else { 425 cfgxerror(res->dm_where.w_srcfile, 426 res->dm_where.w_srcline, 427 "device-major '%s' " 428 "(block %d, char %d) duplicated" 429 " at %s:%d", 430 dm->dm_name, dm->dm_bmajor, 431 dm->dm_cmajor, 432 dm->dm_where.w_srcfile, 433 dm->dm_where.w_srcline); 434 } 435 error = 1; 436 goto out; 437 } 438 if (ht_insert(fixdevmtab, intern(dm->dm_name), dm)) { 439 panic("fixdevsw: %s char %d block %d", 440 dm->dm_name, dm->dm_cmajor, dm->dm_bmajor); 441 } 442 443 if (dm->dm_opts != NULL && 444 !expr_eval(dm->dm_opts, fixsel, NULL)) 445 continue; 446 447 if (dm->dm_cmajor != NODEVMAJOR) { 448 if (ht_lookup(cdevmtab, intern(dm->dm_name)) != NULL) { 449 cfgxerror(dm->dm_where.w_srcfile, 450 dm->dm_where.w_srcline, 451 "device-major of character device '%s' " 452 "is already defined", dm->dm_name); 453 error = 1; 454 goto out; 455 } 456 (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_cmajor); 457 if (ht_lookup(cdevmtab, intern(mstr)) != NULL) { 458 cfgxerror(dm->dm_where.w_srcfile, dm->dm_where.w_srcline, 459 "device-major of character major '%d' " 460 "is already defined", dm->dm_cmajor); 461 error = 1; 462 goto out; 463 } 464 if (ht_insert(cdevmtab, intern(dm->dm_name), dm) || 465 ht_insert(cdevmtab, intern(mstr), dm)) { 466 panic("fixdevsw: %s character major %d", 467 dm->dm_name, dm->dm_cmajor); 468 } 469 } 470 if (dm->dm_bmajor != NODEVMAJOR) { 471 if (ht_lookup(bdevmtab, intern(dm->dm_name)) != NULL) { 472 cfgxerror(dm->dm_where.w_srcfile, dm->dm_where.w_srcline, 473 "device-major of block device '%s' " 474 "is already defined", dm->dm_name); 475 error = 1; 476 goto out; 477 } 478 (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_bmajor); 479 if (ht_lookup(bdevmtab, intern(mstr)) != NULL) { 480 cfgxerror(dm->dm_where.w_srcfile, dm->dm_where.w_srcline, 481 "device-major of block major '%d' " 482 "is already defined", dm->dm_bmajor); 483 error = 1; 484 goto out; 485 } 486 if (ht_insert(bdevmtab, intern(dm->dm_name), dm) || 487 ht_insert(bdevmtab, intern(mstr), dm)) { 488 panic("fixdevsw: %s block major %d", 489 dm->dm_name, dm->dm_bmajor); 490 } 491 } 492 } 493 494 out: 495 ht_free(fixdevmtab); 496 return (error); 497 } 498 499 /* 500 * Called when evaluating a needs-count expression. Make sure the 501 * atom is a countable device. The expression succeeds iff there 502 * is at least one of them (note that while `xx*' will not always 503 * set xx's d_umax > 0, you cannot mix '*' and needs-count). The 504 * mkheaders() routine wants a flattened, in-order list of the 505 * atoms for `#define name value' lines, so we build that as we 506 * are called to eval each atom. 507 */ 508 static int 509 fixcount(const char *name, void *context) 510 { 511 struct nvlist ***p = context; 512 struct devbase *dev; 513 struct nvlist *nv; 514 515 dev = ht_lookup(devbasetab, name); 516 if (dev == NULL) /* cannot occur here; we checked earlier */ 517 panic("fixcount(%s)", name); 518 nv = newnv(name, NULL, NULL, dev->d_umax, NULL); 519 **p = nv; 520 *p = &nv->nv_next; 521 (void)ht_insert(needcnttab, name, nv); 522 return (dev->d_umax != 0); 523 } 524 525 /* 526 * Called from fixfiles when eval'ing a selection expression for a 527 * file that will generate a .h with flags. We will need the flat list. 528 */ 529 static int 530 fixfsel(const char *name, void *context) 531 { 532 struct nvlist ***p = context; 533 struct nvlist *nv; 534 int sel; 535 536 sel = ht_lookup(selecttab, name) != NULL; 537 nv = newnv(name, NULL, NULL, sel, NULL); 538 **p = nv; 539 *p = &nv->nv_next; 540 return (sel); 541 } 542 543 /* 544 * As for fixfsel above, but we do not need the flat list. 545 */ 546 static int 547 /*ARGSUSED*/ 548 fixsel(const char *name, void *context) 549 { 550 551 return (ht_lookup(selecttab, name) != NULL); 552 } 553 554 /* 555 * Eval an expression tree. Calls the given function on each node, 556 * passing it the given context & the name; return value is &/|/! of 557 * results of evaluating atoms. 558 * 559 * No short circuiting ever occurs. fn must return 0 or 1 (otherwise 560 * our mixing of C's bitwise & boolean here may give surprises). 561 */ 562 int 563 expr_eval(struct condexpr *expr, int (*fn)(const char *, void *), void *ctx) 564 { 565 int lhs, rhs; 566 567 switch (expr->cx_type) { 568 569 case CX_ATOM: 570 return ((*fn)(expr->cx_atom, ctx)); 571 572 case CX_NOT: 573 return (!expr_eval(expr->cx_not, fn, ctx)); 574 575 case CX_AND: 576 lhs = expr_eval(expr->cx_and.left, fn, ctx); 577 rhs = expr_eval(expr->cx_and.right, fn, ctx); 578 return (lhs & rhs); 579 580 case CX_OR: 581 lhs = expr_eval(expr->cx_or.left, fn, ctx); 582 rhs = expr_eval(expr->cx_or.right, fn, ctx); 583 return (lhs | rhs); 584 } 585 panic("invalid condexpr type %d", (int)expr->cx_type); 586 /* NOTREACHED */ 587 return (0); 588 } 589 590 #ifdef DEBUG 591 /* 592 * Print expression tree. 593 */ 594 void 595 prexpr(struct nvlist *expr) 596 { 597 static void pr0(); 598 599 printf("expr ="); 600 pr0(expr); 601 printf("\n"); 602 (void)fflush(stdout); 603 } 604 605 static void 606 pr0(struct nvlist *e) 607 { 608 609 switch (e->nv_num) { 610 case FX_ATOM: 611 printf(" %s", e->nv_name); 612 return; 613 case FX_NOT: 614 printf(" (!"); 615 break; 616 case FX_AND: 617 printf(" (&"); 618 break; 619 case FX_OR: 620 printf(" (|"); 621 break; 622 default: 623 printf(" (?%lld?", e->nv_num); 624 break; 625 } 626 if (e->nv_ptr) 627 pr0(e->nv_ptr); 628 pr0(e->nv_next); 629 printf(")"); 630 } 631 #endif 632