xref: /openbsd-src/usr.sbin/config/files.c (revision ac9b4aacc1da35008afea06a5d23c2f2dea9b93e)
1 /*	$OpenBSD: files.c,v 1.18 2011/10/02 22:20:49 edd Exp $	*/
2 /*	$NetBSD: files.c,v 1.6 1996/03/17 13:18:17 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This software was developed by the Computer Systems Engineering group
9  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10  * contributed to Berkeley.
11  *
12  * All advertising materials mentioning features or use of this software
13  * must display the following acknowledgement:
14  *	This product includes software developed by the University of
15  *	California, Lawrence Berkeley Laboratories.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  * 3. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *	from: @(#)files.c	8.1 (Berkeley) 6/6/93
42  */
43 
44 #include <sys/param.h>
45 
46 #include <errno.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 
51 #include "config.h"
52 
53 extern const char *yyfile;
54 
55 /*
56  * We check that each full path name is unique.  File base names
57  * should generally also be unique, e.g., having both a net/xx.c and
58  * a kern/xx.c (or, worse, a net/xx.c and a new/xx.c++) is probably
59  * wrong, but is permitted under some conditions.
60  */
61 static struct hashtab *basetab;		/* file base names */
62 static struct hashtab *pathtab;		/* full path names */
63 
64 static struct files **nextfile;
65 static struct files **unchecked;
66 
67 static struct objects **nextobject;
68 
69 static int	checkaux(const char *, void *);
70 static int	fixcount(const char *, void *);
71 static int	fixfsel(const char *, void *);
72 static int	fixsel(const char *, void *);
73 static int	expr_eval(struct nvlist *,
74 		    int (*)(const char *, void *), void *);
75 static void	expr_free(struct nvlist *);
76 
77 #ifdef DEBUG
78 static void	pr0();
79 #endif
80 
81 void
82 initfiles(void)
83 {
84 
85 	basetab = ht_new();
86 	pathtab = ht_new();
87 	nextfile = &allfiles;
88 	unchecked = &allfiles;
89 	nextobject = &allobjects;
90 }
91 
92 void
93 addfile(struct nvlist *nvpath, struct nvlist *optx, int flags, const char *rule,
94     const char *lintrule)
95 {
96 	struct files *fi;
97 	const char *dotp, *dotp1, *tail, *path, *tail1 = NULL;
98 	struct nvlist *nv;
99 	size_t baselen;
100 	int needc, needf;
101 	char base[200];
102 
103 	/* check various errors */
104 	needc = flags & FI_NEEDSCOUNT;
105 	needf = flags & FI_NEEDSFLAG;
106 	if (needc && needf) {
107 		error("cannot mix needs-count and needs-flag");
108 		goto bad;
109 	}
110 	if (optx == NULL && (needc || needf)) {
111 		error("nothing to %s", needc ? "count" : "flag");
112 		goto bad;
113 	}
114 
115 	for (nv = nvpath; nv; nv = nv->nv_next) {
116 		path = nv->nv_name;
117 
118 		/* find last part of pathname, and same without trailing suffix */
119 		tail = strrchr(path, '/');
120 		if (tail == NULL)
121 			tail = path;
122 		else
123 			tail++;
124 		dotp = strrchr(tail, '.');
125 		if (dotp == NULL || dotp[1] == 0 ||
126 		    (baselen = dotp - tail) >= sizeof(base)) {
127 			error("invalid pathname `%s'", path);
128 			goto bad;
129 		}
130 
131 		/*
132 		 * Ensure all tailnames are identical, because .o
133 		 * filenames must be identical too.
134 		 */
135 		if (tail1 &&
136 		    (dotp - tail != dotp1 - tail1 ||
137 		    strncmp(tail1, tail, dotp - tail)))
138 			error("different production from %s %s",
139 			    nvpath->nv_name, tail);
140 		tail1 = tail;
141 		dotp1 = dotp;
142 	}
143 
144 	/*
145 	 * Commit this file to memory.  We will decide later whether it
146 	 * will be used after all.
147 	 */
148 	fi = emalloc(sizeof *fi);
149 	if (ht_insert(pathtab, path, fi)) {
150 		free(fi);
151 		if ((fi = ht_lookup(pathtab, path)) == NULL)
152 			panic("addfile: ht_lookup(%s)", path);
153 		error("duplicate file %s", path);
154 		xerror(fi->fi_srcfile, fi->fi_srcline,
155 		    "here is the original definition");
156 	}
157 	memcpy(base, tail, baselen);
158 	base[baselen] = 0;
159 	fi->fi_next = NULL;
160 	fi->fi_srcfile = yyfile;
161 	fi->fi_srcline = currentline();
162 	fi->fi_flags = flags;
163 	fi->fi_nvpath = nvpath;
164 	fi->fi_base = intern(base);
165 	fi->fi_optx = optx;
166 	fi->fi_optf = NULL;
167 	fi->fi_mkrule[0] = rule;
168 	fi->fi_mkrule[1] = lintrule;
169 	*nextfile = fi;
170 	nextfile = &fi->fi_next;
171 	return;
172 bad:
173 	expr_free(optx);
174 }
175 
176 void
177 addobject(const char *path, struct nvlist *optx, int flags)
178 {
179 	struct objects *oi;
180 
181 	/*
182 	 * Commit this object to memory.  We will decide later whether it
183 	 * will be used after all.
184 	 */
185 	oi = emalloc(sizeof *oi);
186 	if (ht_insert(pathtab, path, oi)) {
187 		free(oi);
188 		if ((oi = ht_lookup(pathtab, path)) == NULL)
189 			panic("addfile: ht_lookup(%s)", path);
190 		error("duplicate file %s", path);
191 		xerror(oi->oi_srcfile, oi->oi_srcline,
192 		    "here is the original definition");
193 	}
194 	oi->oi_next = NULL;
195 	oi->oi_srcfile = yyfile;
196 	oi->oi_srcline = currentline();
197 	oi->oi_flags = flags;
198 	oi->oi_path = path;
199 	oi->oi_optx = optx;
200 	oi->oi_optf = NULL;
201 	*nextobject = oi;
202 	nextobject = &oi->oi_next;
203 }
204 
205 /*
206  * We have finished reading some "files" file, either ../../conf/files
207  * or ./files.$machine.  Make sure that everything that is flagged as
208  * needing a count is reasonable.  (This prevents ../../conf/files from
209  * depending on some machine-specific device.)
210  */
211 void
212 checkfiles(void)
213 {
214 	struct files *fi, *last;
215 
216 	last = NULL;
217 	for (fi = *unchecked; fi != NULL; last = fi, fi = fi->fi_next)
218 		if ((fi->fi_flags & FI_NEEDSCOUNT) != 0)
219 			(void)expr_eval(fi->fi_optx, checkaux, fi);
220 	if (last != NULL)
221 		unchecked = &last->fi_next;
222 }
223 
224 /*
225  * Auxiliary function for checkfiles, called from expr_eval.
226  * We are not actually interested in the expression's value.
227  */
228 static int
229 checkaux(const char *name, void *context)
230 {
231 	struct files *fi = context;
232 
233 	if (ht_lookup(devbasetab, name) == NULL) {
234 		xerror(fi->fi_srcfile, fi->fi_srcline,
235 		    "`%s' is not a countable device",
236 		    name);
237 		/* keep fixfiles() from complaining again */
238 		fi->fi_flags |= FI_HIDDEN;
239 	}
240 	return (0);
241 }
242 
243 /*
244  * We have finished reading everything.  Tack the files down: calculate
245  * selection and counts as needed.  Check that the object files built
246  * from the selected sources do not collide.
247  */
248 int
249 fixfiles(void)
250 {
251 	struct files *fi, *ofi;
252 	struct nvlist *flathead, **flatp;
253 	int err, sel;
254 
255 	err = 0;
256 	for (fi = allfiles; fi != NULL; fi = fi->fi_next) {
257 		/* Skip files that generated counted-device complaints. */
258 		if (fi->fi_flags & FI_HIDDEN)
259 			continue;
260 
261 		/* Optional: see if it is to be included. */
262 		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_nvpath != ofi->fi_nvpath) {
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_nvpath->nv_name);
293 				xerror(ofi->fi_srcfile, ofi->fi_srcline,
294 				    "here is the previous file: %s",
295 				    ofi->fi_nvpath->nv_name);
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 	for (oi = allobjects; oi != NULL; oi = oi->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  * Called when evaluating a needs-count expression.  Make sure the
337  * atom is a countable device.  The expression succeeds iff there
338  * is at least one of them (note that while `xx*' will not always
339  * set xx's d_umax > 0, you cannot mix '*' and needs-count).  The
340  * mkheaders() routine wants a flattened, in-order list of the
341  * atoms for `#define name value' lines, so we build that as we
342  * are called to eval each atom.
343  */
344 static int
345 fixcount(const char *name, void *context)
346 {
347 	struct nvlist ***p = context;
348 	struct devbase *dev;
349 	struct nvlist *nv;
350 
351 	dev = ht_lookup(devbasetab, name);
352 	if (dev == NULL)	/* cannot occur here; we checked earlier */
353 		panic("fixcount(%s)", name);
354 	nv = newnv(name, NULL, NULL, dev->d_umax, NULL);
355 	**p = nv;
356 	*p = &nv->nv_next;
357 	(void)ht_insert(needcnttab, name, nv);
358 	return (dev->d_umax != 0);
359 }
360 
361 /*
362  * Called from fixfiles when eval'ing a selection expression for a
363  * file that will generate a .h with flags.  We will need the flat list.
364  */
365 static int
366 fixfsel(const char *name, void *context)
367 {
368 	struct nvlist ***p = context;
369 	struct nvlist *nv;
370 	int sel;
371 
372 	sel = ht_lookup(selecttab, name) != NULL;
373 	nv = newnv(name, NULL, NULL, sel, NULL);
374 	**p = nv;
375 	*p = &nv->nv_next;
376 	return (sel);
377 }
378 
379 /*
380  * As for fixfsel above, but we do not need the flat list.
381  */
382 static int
383 fixsel(const char *name, void *context)
384 {
385 
386 	return (ht_lookup(selecttab, name) != NULL);
387 }
388 
389 /*
390  * Eval an expression tree.  Calls the given function on each node,
391  * passing it the given context & the name; return value is &/|/! of
392  * results of evaluating atoms.
393  *
394  * No short circuiting ever occurs.  fn must return 0 or 1 (otherwise
395  * our mixing of C's bitwise & boolean here may give surprises).
396  */
397 static int
398 expr_eval(struct nvlist *expr, int (*fn)(const char *, void *), void *context)
399 {
400 	int lhs, rhs;
401 
402 	switch (expr->nv_int) {
403 
404 	case FX_ATOM:
405 		return ((*fn)(expr->nv_name, context));
406 
407 	case FX_NOT:
408 		return (!expr_eval(expr->nv_next, fn, context));
409 
410 	case FX_AND:
411 		lhs = expr_eval(expr->nv_ptr, fn, context);
412 		rhs = expr_eval(expr->nv_next, fn, context);
413 		return (lhs & rhs);
414 
415 	case FX_OR:
416 		lhs = expr_eval(expr->nv_ptr, fn, context);
417 		rhs = expr_eval(expr->nv_next, fn, context);
418 		return (lhs | rhs);
419 	}
420 	panic("expr_eval %d", expr->nv_int);
421 	return (0);
422 }
423 
424 /*
425  * Free an expression tree.
426  */
427 static void
428 expr_free(struct nvlist *expr)
429 {
430 	struct nvlist *rhs;
431 
432 	/* This loop traverses down the RHS of each subexpression. */
433 	for (; expr != NULL; expr = rhs) {
434 		switch (expr->nv_int) {
435 
436 		/* Atoms and !-exprs have no left hand side. */
437 		case FX_ATOM:
438 		case FX_NOT:
439 			break;
440 
441 		/* For AND and OR nodes, free the LHS. */
442 		case FX_AND:
443 		case FX_OR:
444 			expr_free(expr->nv_ptr);
445 			break;
446 
447 		default:
448 			panic("expr_free %d", expr->nv_int);
449 		}
450 		rhs = expr->nv_next;
451 		nvfree(expr);
452 	}
453 }
454 
455 #ifdef DEBUG
456 /*
457  * Print expression tree.
458  */
459 void
460 prexpr(struct nvlist *expr)
461 {
462 	printf("expr =");
463 	pr0(expr);
464 	printf("\n");
465 	(void)fflush(stdout);
466 }
467 
468 static void
469 pr0(struct nvlist *e)
470 {
471 
472 	switch (e->nv_int) {
473 	case FX_ATOM:
474 		printf(" %s", e->nv_name);
475 		return;
476 	case FX_NOT:
477 		printf(" (!");
478 		break;
479 	case FX_AND:
480 		printf(" (&");
481 		break;
482 	case FX_OR:
483 		printf(" (|");
484 		break;
485 	default:
486 		printf(" (?%d?", e->nv_int);
487 		break;
488 	}
489 	if (e->nv_ptr)
490 		pr0(e->nv_ptr);
491 	pr0(e->nv_next);
492 	printf(")");
493 }
494 #endif
495