xref: /netbsd-src/usr.bin/config/files.c (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1 /*	$NetBSD: files.c,v 1.36 2016/09/09 21:09:11 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.36 2016/09/09 21:09:11 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_srcfile) {
162 			fi->fi_mkrule = rule;
163 			return;
164 		}
165 		cfgerror("duplicate file %s", path);
166 		cfgxerror(fi->fi_srcfile, fi->fi_srcline,
167 		    "here is the original definition");
168 		goto bad;
169 	}
170 	memcpy(base, tail, baselen);
171 	base[baselen] = '\0';
172 	fi->fi_srcfile = yyfile;
173 	fi->fi_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_srcfile, fi->fi_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_srcfile, fi->fi_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_srcfile, fi->fi_srcline,
360 				    "object file collision on %s.o, from %s",
361 				    fi->fi_base, fi->fi_path);
362 				cfgxerror(ofi->fi_srcfile, ofi->fi_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_srcfile, res->dm_srcline,
416 					"device-major '%s' "
417 					"block %d, char %d redefined"
418 					" at %s:%d as block %d, char %d",
419 					res->dm_name,
420 					res->dm_bmajor, res->dm_cmajor,
421 					dm->dm_srcfile, dm->dm_srcline,
422 					dm->dm_bmajor, dm->dm_cmajor);
423 			} else {
424 				cfgxerror(res->dm_srcfile, res->dm_srcline,
425 					"device-major '%s' "
426 					"(block %d, char %d) duplicated"
427 					" at %s:%d",
428 					dm->dm_name, dm->dm_bmajor,
429 					dm->dm_cmajor,
430 					dm->dm_srcfile, dm->dm_srcline);
431 			}
432 			error = 1;
433 			goto out;
434 		}
435 		if (ht_insert(fixdevmtab, intern(dm->dm_name), dm)) {
436 			panic("fixdevsw: %s char %d block %d",
437 			      dm->dm_name, dm->dm_cmajor, dm->dm_bmajor);
438 		}
439 
440 		if (dm->dm_opts != NULL &&
441 		    !expr_eval(dm->dm_opts, fixsel, NULL))
442 			continue;
443 
444 		if (dm->dm_cmajor != NODEVMAJOR) {
445 			if (ht_lookup(cdevmtab, intern(dm->dm_name)) != NULL) {
446 				cfgxerror(dm->dm_srcfile, dm->dm_srcline,
447 				       "device-major of character device '%s' "
448 				       "is already defined", dm->dm_name);
449 				error = 1;
450 				goto out;
451 			}
452 			(void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_cmajor);
453 			if (ht_lookup(cdevmtab, intern(mstr)) != NULL) {
454 				cfgxerror(dm->dm_srcfile, dm->dm_srcline,
455 				       "device-major of character major '%d' "
456 				       "is already defined", dm->dm_cmajor);
457 				error = 1;
458 				goto out;
459 			}
460 			if (ht_insert(cdevmtab, intern(dm->dm_name), dm) ||
461 			    ht_insert(cdevmtab, intern(mstr), dm)) {
462 				panic("fixdevsw: %s character major %d",
463 				      dm->dm_name, dm->dm_cmajor);
464 			}
465 		}
466 		if (dm->dm_bmajor != NODEVMAJOR) {
467 			if (ht_lookup(bdevmtab, intern(dm->dm_name)) != NULL) {
468 				cfgxerror(dm->dm_srcfile, dm->dm_srcline,
469 				       "device-major of block device '%s' "
470 				       "is already defined", dm->dm_name);
471 				error = 1;
472 				goto out;
473 			}
474 			(void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_bmajor);
475 			if (ht_lookup(bdevmtab, intern(mstr)) != NULL) {
476 				cfgxerror(dm->dm_srcfile, dm->dm_srcline,
477 				       "device-major of block major '%d' "
478 				       "is already defined", dm->dm_bmajor);
479 				error = 1;
480 				goto out;
481 			}
482 			if (ht_insert(bdevmtab, intern(dm->dm_name), dm) ||
483 			    ht_insert(bdevmtab, intern(mstr), dm)) {
484 				panic("fixdevsw: %s block major %d",
485 				      dm->dm_name, dm->dm_bmajor);
486 			}
487 		}
488 	}
489 
490 out:
491 	ht_free(fixdevmtab);
492 	return (error);
493 }
494 
495 /*
496  * Called when evaluating a needs-count expression.  Make sure the
497  * atom is a countable device.  The expression succeeds iff there
498  * is at least one of them (note that while `xx*' will not always
499  * set xx's d_umax > 0, you cannot mix '*' and needs-count).  The
500  * mkheaders() routine wants a flattened, in-order list of the
501  * atoms for `#define name value' lines, so we build that as we
502  * are called to eval each atom.
503  */
504 static int
505 fixcount(const char *name, void *context)
506 {
507 	struct nvlist ***p = context;
508 	struct devbase *dev;
509 	struct nvlist *nv;
510 
511 	dev = ht_lookup(devbasetab, name);
512 	if (dev == NULL)	/* cannot occur here; we checked earlier */
513 		panic("fixcount(%s)", name);
514 	nv = newnv(name, NULL, NULL, dev->d_umax, NULL);
515 	**p = nv;
516 	*p = &nv->nv_next;
517 	(void)ht_insert(needcnttab, name, nv);
518 	return (dev->d_umax != 0);
519 }
520 
521 /*
522  * Called from fixfiles when eval'ing a selection expression for a
523  * file that will generate a .h with flags.  We will need the flat list.
524  */
525 static int
526 fixfsel(const char *name, void *context)
527 {
528 	struct nvlist ***p = context;
529 	struct nvlist *nv;
530 	int sel;
531 
532 	sel = ht_lookup(selecttab, name) != NULL;
533 	nv = newnv(name, NULL, NULL, sel, NULL);
534 	**p = nv;
535 	*p = &nv->nv_next;
536 	return (sel);
537 }
538 
539 /*
540  * As for fixfsel above, but we do not need the flat list.
541  */
542 static int
543 /*ARGSUSED*/
544 fixsel(const char *name, void *context)
545 {
546 
547 	return (ht_lookup(selecttab, name) != NULL);
548 }
549 
550 /*
551  * Eval an expression tree.  Calls the given function on each node,
552  * passing it the given context & the name; return value is &/|/! of
553  * results of evaluating atoms.
554  *
555  * No short circuiting ever occurs.  fn must return 0 or 1 (otherwise
556  * our mixing of C's bitwise & boolean here may give surprises).
557  */
558 int
559 expr_eval(struct condexpr *expr, int (*fn)(const char *, void *), void *ctx)
560 {
561 	int lhs, rhs;
562 
563 	switch (expr->cx_type) {
564 
565 	case CX_ATOM:
566 		return ((*fn)(expr->cx_atom, ctx));
567 
568 	case CX_NOT:
569 		return (!expr_eval(expr->cx_not, fn, ctx));
570 
571 	case CX_AND:
572 		lhs = expr_eval(expr->cx_and.left, fn, ctx);
573 		rhs = expr_eval(expr->cx_and.right, fn, ctx);
574 		return (lhs & rhs);
575 
576 	case CX_OR:
577 		lhs = expr_eval(expr->cx_or.left, fn, ctx);
578 		rhs = expr_eval(expr->cx_or.right, fn, ctx);
579 		return (lhs | rhs);
580 	}
581 	panic("invalid condexpr type %d", (int)expr->cx_type);
582 	/* NOTREACHED */
583 	return (0);
584 }
585 
586 #ifdef DEBUG
587 /*
588  * Print expression tree.
589  */
590 void
591 prexpr(struct nvlist *expr)
592 {
593 	static void pr0();
594 
595 	printf("expr =");
596 	pr0(expr);
597 	printf("\n");
598 	(void)fflush(stdout);
599 }
600 
601 static void
602 pr0(struct nvlist *e)
603 {
604 
605 	switch (e->nv_num) {
606 	case FX_ATOM:
607 		printf(" %s", e->nv_name);
608 		return;
609 	case FX_NOT:
610 		printf(" (!");
611 		break;
612 	case FX_AND:
613 		printf(" (&");
614 		break;
615 	case FX_OR:
616 		printf(" (|");
617 		break;
618 	default:
619 		printf(" (?%lld?", e->nv_num);
620 		break;
621 	}
622 	if (e->nv_ptr)
623 		pr0(e->nv_ptr);
624 	pr0(e->nv_next);
625 	printf(")");
626 }
627 #endif
628