xref: /netbsd/usr.bin/config/files.c (revision f079f926)
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