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