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