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