1 /* $NetBSD: files.c,v 1.7 2007/11/30 23:19:18 dsl 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/param.h> 48 #include <errno.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <util.h> 53 #include "defs.h" 54 55 extern const char *yyfile; 56 57 /* 58 * We check that each full path name is unique. File base names 59 * should generally also be unique, e.g., having both a net/xx.c and 60 * a kern/xx.c (or, worse, a net/xx.c and a new/xx.c++) is probably 61 * wrong, but is permitted under some conditions. 62 */ 63 static struct hashtab *basetab; /* file base names */ 64 static struct hashtab *pathtab; /* full path names */ 65 66 static struct files **unchecked; 67 68 static int checkaux(const char *, void *); 69 static int fixcount(const char *, void *); 70 static int fixfsel(const char *, void *); 71 static int fixsel(const char *, void *); 72 static int expr_eval(struct nvlist *, 73 int (*)(const char *, void *), void *); 74 static void expr_free(struct nvlist *); 75 76 void 77 initfiles(void) 78 { 79 80 basetab = ht_new(); 81 pathtab = ht_new(); 82 TAILQ_INIT(&allfiles); 83 unchecked = &TAILQ_FIRST(&allfiles); 84 TAILQ_INIT(&allobjects); 85 } 86 87 void 88 addfile(const char *path, struct nvlist *optx, int flags, const char *rule) 89 { 90 struct files *fi; 91 const char *dotp, *tail; 92 size_t baselen; 93 int needc, needf; 94 char base[200]; 95 96 /* check various errors */ 97 needc = flags & FI_NEEDSCOUNT; 98 needf = flags & FI_NEEDSFLAG; 99 if (needc && needf) { 100 cfgerror("cannot mix needs-count and needs-flag"); 101 goto bad; 102 } 103 if (optx == NULL && (needc || needf)) { 104 cfgerror("nothing to %s for %s", needc ? "count" : "flag", 105 path); 106 goto bad; 107 } 108 109 /* find last part of pathname, and same without trailing suffix */ 110 tail = strrchr(path, '/'); 111 if (tail == NULL) 112 tail = path; 113 else 114 tail++; 115 dotp = strrchr(tail, '.'); 116 if (dotp == NULL || dotp[1] == 0 || 117 (baselen = dotp - tail) >= sizeof(base)) { 118 cfgerror("invalid pathname `%s'", path); 119 goto bad; 120 } 121 122 /* 123 * Commit this file to memory. We will decide later whether it 124 * will be used after all. 125 */ 126 fi = ecalloc(1, sizeof *fi); 127 if (ht_insert(pathtab, path, fi)) { 128 free(fi); 129 if ((fi = ht_lookup(pathtab, path)) == NULL) 130 panic("addfile: ht_lookup(%s)", path); 131 132 /* 133 * If it's a duplicate entry, it is must specify a make 134 * rule, and only a make rule, and must come from 135 * a different source file than the original entry. 136 * If it does otherwise, it is disallowed. This allows 137 * machine-dependent files to override the compilation 138 * options for specific files. 139 */ 140 if (rule != NULL && optx == NULL && flags == 0 && 141 yyfile != fi->fi_srcfile) { 142 fi->fi_mkrule = rule; 143 return; 144 } 145 cfgerror("duplicate file %s", path); 146 cfgxerror(fi->fi_srcfile, fi->fi_srcline, 147 "here is the original definition"); 148 goto bad; 149 } 150 memcpy(base, tail, baselen); 151 base[baselen] = 0; 152 fi->fi_srcfile = yyfile; 153 fi->fi_srcline = currentline(); 154 fi->fi_flags = flags; 155 fi->fi_path = path; 156 fi->fi_tail = tail; 157 fi->fi_base = intern(base); 158 fi->fi_prefix = SLIST_EMPTY(&prefixes) ? NULL : 159 SLIST_FIRST(&prefixes)->pf_prefix; 160 fi->fi_optx = optx; 161 fi->fi_optf = NULL; 162 fi->fi_mkrule = rule; 163 TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); 164 return; 165 bad: 166 expr_free(optx); 167 } 168 169 void 170 addobject(const char *path, struct nvlist *optx, int flags) 171 { 172 struct objects *oi; 173 174 /* 175 * Commit this object to memory. We will decide later whether it 176 * will be used after all. 177 */ 178 oi = ecalloc(1, sizeof *oi); 179 if (ht_insert(pathtab, path, oi)) { 180 free(oi); 181 if ((oi = ht_lookup(pathtab, path)) == NULL) 182 panic("addfile: ht_lookup(%s)", path); 183 cfgerror("duplicate file %s", path); 184 cfgxerror(oi->oi_srcfile, oi->oi_srcline, 185 "here is the original definition"); 186 } 187 oi->oi_srcfile = yyfile; 188 oi->oi_srcline = currentline(); 189 oi->oi_flags = flags; 190 oi->oi_path = path; 191 oi->oi_prefix = SLIST_EMPTY(&prefixes) ? NULL : 192 SLIST_FIRST(&prefixes)->pf_prefix; 193 oi->oi_optx = optx; 194 oi->oi_optf = NULL; 195 TAILQ_INSERT_TAIL(&allobjects, oi, oi_next); 196 return; 197 } 198 199 /* 200 * We have finished reading some "files" file, either ../../conf/files 201 * or ./files.$machine. Make sure that everything that is flagged as 202 * needing a count is reasonable. (This prevents ../../conf/files from 203 * depending on some machine-specific device.) 204 */ 205 void 206 checkfiles(void) 207 { 208 struct files *fi, *last; 209 210 last = NULL; 211 for (fi = *unchecked; fi != NULL; 212 last = fi, fi = TAILQ_NEXT(fi, fi_next)) { 213 if ((fi->fi_flags & FI_NEEDSCOUNT) != 0) 214 (void)expr_eval(fi->fi_optx, checkaux, fi); 215 } 216 if (last != NULL) 217 unchecked = &TAILQ_NEXT(last, fi_next); 218 } 219 220 /* 221 * Auxiliary function for checkfiles, called from expr_eval. 222 * We are not actually interested in the expression's value. 223 */ 224 static int 225 checkaux(const char *name, void *context) 226 { 227 struct files *fi = context; 228 229 if (ht_lookup(devbasetab, name) == NULL) { 230 cfgxerror(fi->fi_srcfile, fi->fi_srcline, 231 "`%s' is not a countable device", 232 name); 233 /* keep fixfiles() from complaining again */ 234 fi->fi_flags |= FI_HIDDEN; 235 } 236 return (0); 237 } 238 239 /* 240 * We have finished reading everything. Tack the files down: calculate 241 * selection and counts as needed. Check that the object files built 242 * from the selected sources do not collide. 243 */ 244 int 245 fixfiles(void) 246 { 247 struct files *fi, *ofi; 248 struct nvlist *flathead, **flatp; 249 int err, sel; 250 251 err = 0; 252 TAILQ_FOREACH(fi, &allfiles, fi_next) { 253 254 /* Skip files that generated counted-device complaints. */ 255 if (fi->fi_flags & FI_HIDDEN) 256 continue; 257 258 /* Optional: see if it is to be included. */ 259 if (fi->fi_flags & FIT_FORCESELECT) 260 { 261 /* include it */ ; 262 } 263 else if (fi->fi_optx != NULL) { 264 flathead = NULL; 265 flatp = &flathead; 266 sel = expr_eval(fi->fi_optx, 267 fi->fi_flags & FI_NEEDSCOUNT ? fixcount : 268 fi->fi_flags & FI_NEEDSFLAG ? fixfsel : 269 fixsel, 270 &flatp); 271 fi->fi_optf = flathead; 272 if (!sel) 273 continue; 274 } 275 276 /* We like this file. Make sure it generates a unique .o. */ 277 if (ht_insert(basetab, fi->fi_base, fi)) { 278 if ((ofi = ht_lookup(basetab, fi->fi_base)) == NULL) 279 panic("fixfiles ht_lookup(%s)", fi->fi_base); 280 /* 281 * If the new file comes from a different source, 282 * allow the new one to override the old one. 283 */ 284 if (fi->fi_path != ofi->fi_path) { 285 if (ht_replace(basetab, fi->fi_base, fi) != 1) 286 panic("fixfiles ht_replace(%s)", 287 fi->fi_base); 288 ofi->fi_flags &= ~FI_SEL; 289 ofi->fi_flags |= FI_HIDDEN; 290 } else { 291 cfgxerror(fi->fi_srcfile, fi->fi_srcline, 292 "object file collision on %s.o, from %s", 293 fi->fi_base, fi->fi_path); 294 cfgxerror(ofi->fi_srcfile, ofi->fi_srcline, 295 "here is the previous file: %s", 296 ofi->fi_path); 297 err = 1; 298 } 299 } 300 fi->fi_flags |= FI_SEL; 301 } 302 return (err); 303 } 304 305 /* 306 * We have finished reading everything. Tack the objects down: calculate 307 * selection. 308 */ 309 int 310 fixobjects(void) 311 { 312 struct objects *oi; 313 struct nvlist *flathead, **flatp; 314 int err, sel; 315 316 err = 0; 317 TAILQ_FOREACH(oi, &allobjects, oi_next) { 318 /* Optional: see if it is to be included. */ 319 if (oi->oi_optx != NULL) { 320 flathead = NULL; 321 flatp = &flathead; 322 sel = expr_eval(oi->oi_optx, 323 oi->oi_flags & OI_NEEDSFLAG ? fixfsel : 324 fixsel, 325 &flatp); 326 oi->oi_optf = flathead; 327 if (!sel) 328 continue; 329 } 330 331 oi->oi_flags |= OI_SEL; 332 } 333 return (err); 334 } 335 336 /* 337 * We have finished reading everything. Tack the devsws down: calculate 338 * selection. 339 */ 340 int 341 fixdevsw(void) 342 { 343 int error; 344 struct devm *dm, *res; 345 struct hashtab *fixdevmtab; 346 char mstr[16]; 347 348 error = 0; 349 fixdevmtab = ht_new(); 350 351 TAILQ_FOREACH(dm, &alldevms, dm_next) { 352 res = ht_lookup(fixdevmtab, intern(dm->dm_name)); 353 if (res != NULL) { 354 if (res->dm_cmajor != dm->dm_cmajor || 355 res->dm_bmajor != dm->dm_bmajor) { 356 cfgxerror(res->dm_srcfile, res->dm_srcline, 357 "device-major '%s' " 358 "block %d, char %d redefined" 359 " at %s:%d as block %d, char %d", 360 res->dm_name, 361 res->dm_bmajor, res->dm_cmajor, 362 dm->dm_srcfile, dm->dm_srcline, 363 dm->dm_bmajor, dm->dm_cmajor); 364 } else { 365 cfgxerror(res->dm_srcfile, res->dm_srcline, 366 "device-major '%s' " 367 "(block %d, char %d) duplicated" 368 " at %s:%d", 369 dm->dm_name, dm->dm_bmajor, 370 dm->dm_cmajor, 371 dm->dm_srcfile, dm->dm_srcline); 372 } 373 error = 1; 374 goto out; 375 } 376 if (ht_insert(fixdevmtab, intern(dm->dm_name), dm)) { 377 panic("fixdevsw: %s char %d block %d", 378 dm->dm_name, dm->dm_cmajor, dm->dm_bmajor); 379 } 380 381 if (dm->dm_opts != NULL && 382 !expr_eval(dm->dm_opts, fixsel, NULL)) 383 continue; 384 385 if (dm->dm_cmajor != -1) { 386 if (ht_lookup(cdevmtab, intern(dm->dm_name)) != NULL) { 387 cfgxerror(dm->dm_srcfile, dm->dm_srcline, 388 "device-major of character device '%s' " 389 "is already defined", dm->dm_name); 390 error = 1; 391 goto out; 392 } 393 (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_cmajor); 394 if (ht_lookup(cdevmtab, intern(mstr)) != NULL) { 395 cfgxerror(dm->dm_srcfile, dm->dm_srcline, 396 "device-major of character major '%d' " 397 "is already defined", dm->dm_cmajor); 398 error = 1; 399 goto out; 400 } 401 if (ht_insert(cdevmtab, intern(dm->dm_name), dm) || 402 ht_insert(cdevmtab, intern(mstr), dm)) { 403 panic("fixdevsw: %s character major %d", 404 dm->dm_name, dm->dm_cmajor); 405 } 406 } 407 if (dm->dm_bmajor != -1) { 408 if (ht_lookup(bdevmtab, intern(dm->dm_name)) != NULL) { 409 cfgxerror(dm->dm_srcfile, dm->dm_srcline, 410 "device-major of block device '%s' " 411 "is already defined", dm->dm_name); 412 error = 1; 413 goto out; 414 } 415 (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_bmajor); 416 if (ht_lookup(bdevmtab, intern(mstr)) != NULL) { 417 cfgxerror(dm->dm_srcfile, dm->dm_srcline, 418 "device-major of block major '%d' " 419 "is already defined", dm->dm_bmajor); 420 error = 1; 421 goto out; 422 } 423 if (ht_insert(bdevmtab, intern(dm->dm_name), dm) || 424 ht_insert(bdevmtab, intern(mstr), dm)) { 425 panic("fixdevsw: %s block major %d", 426 dm->dm_name, dm->dm_bmajor); 427 } 428 } 429 } 430 431 out: 432 ht_free(fixdevmtab); 433 return (error); 434 } 435 436 /* 437 * Called when evaluating a needs-count expression. Make sure the 438 * atom is a countable device. The expression succeeds iff there 439 * is at least one of them (note that while `xx*' will not always 440 * set xx's d_umax > 0, you cannot mix '*' and needs-count). The 441 * mkheaders() routine wants a flattened, in-order list of the 442 * atoms for `#define name value' lines, so we build that as we 443 * are called to eval each atom. 444 */ 445 static int 446 fixcount(const char *name, void *context) 447 { 448 struct nvlist ***p = context; 449 struct devbase *dev; 450 struct nvlist *nv; 451 452 dev = ht_lookup(devbasetab, name); 453 if (dev == NULL) /* cannot occur here; we checked earlier */ 454 panic("fixcount(%s)", name); 455 nv = newnv(name, NULL, NULL, dev->d_umax, NULL); 456 **p = nv; 457 *p = &nv->nv_next; 458 (void)ht_insert(needcnttab, name, nv); 459 return (dev->d_umax != 0); 460 } 461 462 /* 463 * Called from fixfiles when eval'ing a selection expression for a 464 * file that will generate a .h with flags. We will need the flat list. 465 */ 466 static int 467 fixfsel(const char *name, void *context) 468 { 469 struct nvlist ***p = context; 470 struct nvlist *nv; 471 int sel; 472 473 sel = ht_lookup(selecttab, name) != NULL; 474 nv = newnv(name, NULL, NULL, sel, NULL); 475 **p = nv; 476 *p = &nv->nv_next; 477 return (sel); 478 } 479 480 /* 481 * As for fixfsel above, but we do not need the flat list. 482 */ 483 static int 484 /*ARGSUSED*/ 485 fixsel(const char *name, void *context) 486 { 487 488 return (ht_lookup(selecttab, name) != NULL); 489 } 490 491 /* 492 * Eval an expression tree. Calls the given function on each node, 493 * passing it the given context & the name; return value is &/|/! of 494 * results of evaluating atoms. 495 * 496 * No short circuiting ever occurs. fn must return 0 or 1 (otherwise 497 * our mixing of C's bitwise & boolean here may give surprises). 498 */ 499 static int 500 expr_eval(struct nvlist *expr, int (*fn)(const char *, void *), void *context) 501 { 502 int lhs, rhs; 503 504 switch (expr->nv_int) { 505 506 case FX_ATOM: 507 return ((*fn)(expr->nv_name, context)); 508 509 case FX_NOT: 510 return (!expr_eval(expr->nv_next, fn, context)); 511 512 case FX_AND: 513 lhs = expr_eval(expr->nv_ptr, fn, context); 514 rhs = expr_eval(expr->nv_next, fn, context); 515 return (lhs & rhs); 516 517 case FX_OR: 518 lhs = expr_eval(expr->nv_ptr, fn, context); 519 rhs = expr_eval(expr->nv_next, fn, context); 520 return (lhs | rhs); 521 } 522 panic("expr_eval %d", expr->nv_int); 523 /* NOTREACHED */ 524 return (0); 525 } 526 527 /* 528 * Free an expression tree. 529 */ 530 static void 531 expr_free(struct nvlist *expr) 532 { 533 struct nvlist *rhs; 534 535 /* This loop traverses down the RHS of each subexpression. */ 536 for (; expr != NULL; expr = rhs) { 537 switch (expr->nv_int) { 538 539 /* Atoms and !-exprs have no left hand side. */ 540 case FX_ATOM: 541 case FX_NOT: 542 break; 543 544 /* For AND and OR nodes, free the LHS. */ 545 case FX_AND: 546 case FX_OR: 547 expr_free(expr->nv_ptr); 548 break; 549 550 default: 551 panic("expr_free %d", expr->nv_int); 552 } 553 rhs = expr->nv_next; 554 nvfree(expr); 555 } 556 } 557 558 #ifdef DEBUG 559 /* 560 * Print expression tree. 561 */ 562 void 563 prexpr(struct nvlist *expr) 564 { 565 static void pr0(); 566 567 printf("expr ="); 568 pr0(expr); 569 printf("\n"); 570 (void)fflush(stdout); 571 } 572 573 static void 574 pr0(struct nvlist *e) 575 { 576 577 switch (e->nv_int) { 578 case FX_ATOM: 579 printf(" %s", e->nv_name); 580 return; 581 case FX_NOT: 582 printf(" (!"); 583 break; 584 case FX_AND: 585 printf(" (&"); 586 break; 587 case FX_OR: 588 printf(" (|"); 589 break; 590 default: 591 printf(" (?%d?", e->nv_int); 592 break; 593 } 594 if (e->nv_ptr) 595 pr0(e->nv_ptr); 596 pr0(e->nv_next); 597 printf(")"); 598 } 599 #endif 600