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
initfiles(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
addfile(const char * path,struct condexpr * optx,u_char flags,const char * rule)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
addfiletoattr(const char * name,struct files * fi)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
checkfiles(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
checkaux(const char * name,void * context)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
cmpfiles(const void * a,const void * b)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
fixfiles(void)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
fixdevsw(void)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
fixcount(const char * name,void * context)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
fixfsel(const char * name,void * context)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*/
fixsel(const char * name,void * context)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
expr_eval(struct condexpr * expr,int (* fn)(const char *,void *),void * ctx)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
prexpr(struct nvlist * expr)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
pr0(struct nvlist * e)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