xref: /openbsd/usr.sbin/config/files.c (revision a6445c1d)
1 /*	$OpenBSD: files.c,v 1.19 2012/09/17 17:36:13 espie 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 {
95 	struct files *fi;
96 	const char *dotp, *dotp1, *tail, *path, *tail1 = NULL;
97 	struct nvlist *nv;
98 	size_t baselen;
99 	int needc, needf;
100 	char base[200];
101 
102 	/* check various errors */
103 	needc = flags & FI_NEEDSCOUNT;
104 	needf = flags & FI_NEEDSFLAG;
105 	if (needc && needf) {
106 		error("cannot mix needs-count and needs-flag");
107 		goto bad;
108 	}
109 	if (optx == NULL && (needc || needf)) {
110 		error("nothing to %s", needc ? "count" : "flag");
111 		goto bad;
112 	}
113 
114 	for (nv = nvpath; nv; nv = nv->nv_next) {
115 		path = nv->nv_name;
116 
117 		/* find last part of pathname, and same without trailing suffix */
118 		tail = strrchr(path, '/');
119 		if (tail == NULL)
120 			tail = path;
121 		else
122 			tail++;
123 		dotp = strrchr(tail, '.');
124 		if (dotp == NULL || dotp[1] == 0 ||
125 		    (baselen = dotp - tail) >= sizeof(base)) {
126 			error("invalid pathname `%s'", path);
127 			goto bad;
128 		}
129 
130 		/*
131 		 * Ensure all tailnames are identical, because .o
132 		 * filenames must be identical too.
133 		 */
134 		if (tail1 &&
135 		    (dotp - tail != dotp1 - tail1 ||
136 		    strncmp(tail1, tail, dotp - tail)))
137 			error("different production from %s %s",
138 			    nvpath->nv_name, tail);
139 		tail1 = tail;
140 		dotp1 = dotp;
141 	}
142 
143 	/*
144 	 * Commit this file to memory.  We will decide later whether it
145 	 * will be used after all.
146 	 */
147 	fi = emalloc(sizeof *fi);
148 	if (ht_insert(pathtab, path, fi)) {
149 		free(fi);
150 		if ((fi = ht_lookup(pathtab, path)) == NULL)
151 			panic("addfile: ht_lookup(%s)", path);
152 		error("duplicate file %s", path);
153 		xerror(fi->fi_srcfile, fi->fi_srcline,
154 		    "here is the original definition");
155 	}
156 	memcpy(base, tail, baselen);
157 	base[baselen] = 0;
158 	fi->fi_next = NULL;
159 	fi->fi_srcfile = yyfile;
160 	fi->fi_srcline = currentline();
161 	fi->fi_flags = flags;
162 	fi->fi_nvpath = nvpath;
163 	fi->fi_base = intern(base);
164 	fi->fi_optx = optx;
165 	fi->fi_optf = NULL;
166 	fi->fi_mkrule = rule;
167 	*nextfile = fi;
168 	nextfile = &fi->fi_next;
169 	return;
170 bad:
171 	expr_free(optx);
172 }
173 
174 void
175 addobject(const char *path, struct nvlist *optx, int flags)
176 {
177 	struct objects *oi;
178 
179 	/*
180 	 * Commit this object to memory.  We will decide later whether it
181 	 * will be used after all.
182 	 */
183 	oi = emalloc(sizeof *oi);
184 	if (ht_insert(pathtab, path, oi)) {
185 		free(oi);
186 		if ((oi = ht_lookup(pathtab, path)) == NULL)
187 			panic("addfile: ht_lookup(%s)", path);
188 		error("duplicate file %s", path);
189 		xerror(oi->oi_srcfile, oi->oi_srcline,
190 		    "here is the original definition");
191 	}
192 	oi->oi_next = NULL;
193 	oi->oi_srcfile = yyfile;
194 	oi->oi_srcline = currentline();
195 	oi->oi_flags = flags;
196 	oi->oi_path = path;
197 	oi->oi_optx = optx;
198 	oi->oi_optf = NULL;
199 	*nextobject = oi;
200 	nextobject = &oi->oi_next;
201 }
202 
203 /*
204  * We have finished reading some "files" file, either ../../conf/files
205  * or ./files.$machine.  Make sure that everything that is flagged as
206  * needing a count is reasonable.  (This prevents ../../conf/files from
207  * depending on some machine-specific device.)
208  */
209 void
210 checkfiles(void)
211 {
212 	struct files *fi, *last;
213 
214 	last = NULL;
215 	for (fi = *unchecked; fi != NULL; last = fi, fi = fi->fi_next)
216 		if ((fi->fi_flags & FI_NEEDSCOUNT) != 0)
217 			(void)expr_eval(fi->fi_optx, checkaux, fi);
218 	if (last != NULL)
219 		unchecked = &last->fi_next;
220 }
221 
222 /*
223  * Auxiliary function for checkfiles, called from expr_eval.
224  * We are not actually interested in the expression's value.
225  */
226 static int
227 checkaux(const char *name, void *context)
228 {
229 	struct files *fi = context;
230 
231 	if (ht_lookup(devbasetab, name) == NULL) {
232 		xerror(fi->fi_srcfile, fi->fi_srcline,
233 		    "`%s' is not a countable device",
234 		    name);
235 		/* keep fixfiles() from complaining again */
236 		fi->fi_flags |= FI_HIDDEN;
237 	}
238 	return (0);
239 }
240 
241 /*
242  * We have finished reading everything.  Tack the files down: calculate
243  * selection and counts as needed.  Check that the object files built
244  * from the selected sources do not collide.
245  */
246 int
247 fixfiles(void)
248 {
249 	struct files *fi, *ofi;
250 	struct nvlist *flathead, **flatp;
251 	int err, sel;
252 
253 	err = 0;
254 	for (fi = allfiles; fi != NULL; fi = fi->fi_next) {
255 		/* Skip files that generated counted-device complaints. */
256 		if (fi->fi_flags & FI_HIDDEN)
257 			continue;
258 
259 		/* Optional: see if it is to be included. */
260 		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_nvpath != ofi->fi_nvpath) {
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 				xerror(fi->fi_srcfile, fi->fi_srcline,
289 				    "object file collision on %s.o, from %s",
290 				    fi->fi_base, fi->fi_nvpath->nv_name);
291 				xerror(ofi->fi_srcfile, ofi->fi_srcline,
292 				    "here is the previous file: %s",
293 				    ofi->fi_nvpath->nv_name);
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 	for (oi = allobjects; oi != NULL; oi = oi->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  * Called when evaluating a needs-count expression.  Make sure the
335  * atom is a countable device.  The expression succeeds iff there
336  * is at least one of them (note that while `xx*' will not always
337  * set xx's d_umax > 0, you cannot mix '*' and needs-count).  The
338  * mkheaders() routine wants a flattened, in-order list of the
339  * atoms for `#define name value' lines, so we build that as we
340  * are called to eval each atom.
341  */
342 static int
343 fixcount(const char *name, void *context)
344 {
345 	struct nvlist ***p = context;
346 	struct devbase *dev;
347 	struct nvlist *nv;
348 
349 	dev = ht_lookup(devbasetab, name);
350 	if (dev == NULL)	/* cannot occur here; we checked earlier */
351 		panic("fixcount(%s)", name);
352 	nv = newnv(name, NULL, NULL, dev->d_umax, NULL);
353 	**p = nv;
354 	*p = &nv->nv_next;
355 	(void)ht_insert(needcnttab, name, nv);
356 	return (dev->d_umax != 0);
357 }
358 
359 /*
360  * Called from fixfiles when eval'ing a selection expression for a
361  * file that will generate a .h with flags.  We will need the flat list.
362  */
363 static int
364 fixfsel(const char *name, void *context)
365 {
366 	struct nvlist ***p = context;
367 	struct nvlist *nv;
368 	int sel;
369 
370 	sel = ht_lookup(selecttab, name) != NULL;
371 	nv = newnv(name, NULL, NULL, sel, NULL);
372 	**p = nv;
373 	*p = &nv->nv_next;
374 	return (sel);
375 }
376 
377 /*
378  * As for fixfsel above, but we do not need the flat list.
379  */
380 static int
381 fixsel(const char *name, void *context)
382 {
383 
384 	return (ht_lookup(selecttab, name) != NULL);
385 }
386 
387 /*
388  * Eval an expression tree.  Calls the given function on each node,
389  * passing it the given context & the name; return value is &/|/! of
390  * results of evaluating atoms.
391  *
392  * No short circuiting ever occurs.  fn must return 0 or 1 (otherwise
393  * our mixing of C's bitwise & boolean here may give surprises).
394  */
395 static int
396 expr_eval(struct nvlist *expr, int (*fn)(const char *, void *), void *context)
397 {
398 	int lhs, rhs;
399 
400 	switch (expr->nv_int) {
401 
402 	case FX_ATOM:
403 		return ((*fn)(expr->nv_name, context));
404 
405 	case FX_NOT:
406 		return (!expr_eval(expr->nv_next, fn, context));
407 
408 	case FX_AND:
409 		lhs = expr_eval(expr->nv_ptr, fn, context);
410 		rhs = expr_eval(expr->nv_next, fn, context);
411 		return (lhs & rhs);
412 
413 	case FX_OR:
414 		lhs = expr_eval(expr->nv_ptr, fn, context);
415 		rhs = expr_eval(expr->nv_next, fn, context);
416 		return (lhs | rhs);
417 	}
418 	panic("expr_eval %d", expr->nv_int);
419 	return (0);
420 }
421 
422 /*
423  * Free an expression tree.
424  */
425 static void
426 expr_free(struct nvlist *expr)
427 {
428 	struct nvlist *rhs;
429 
430 	/* This loop traverses down the RHS of each subexpression. */
431 	for (; expr != NULL; expr = rhs) {
432 		switch (expr->nv_int) {
433 
434 		/* Atoms and !-exprs have no left hand side. */
435 		case FX_ATOM:
436 		case FX_NOT:
437 			break;
438 
439 		/* For AND and OR nodes, free the LHS. */
440 		case FX_AND:
441 		case FX_OR:
442 			expr_free(expr->nv_ptr);
443 			break;
444 
445 		default:
446 			panic("expr_free %d", expr->nv_int);
447 		}
448 		rhs = expr->nv_next;
449 		nvfree(expr);
450 	}
451 }
452 
453 #ifdef DEBUG
454 /*
455  * Print expression tree.
456  */
457 void
458 prexpr(struct nvlist *expr)
459 {
460 	printf("expr =");
461 	pr0(expr);
462 	printf("\n");
463 	(void)fflush(stdout);
464 }
465 
466 static void
467 pr0(struct nvlist *e)
468 {
469 
470 	switch (e->nv_int) {
471 	case FX_ATOM:
472 		printf(" %s", e->nv_name);
473 		return;
474 	case FX_NOT:
475 		printf(" (!");
476 		break;
477 	case FX_AND:
478 		printf(" (&");
479 		break;
480 	case FX_OR:
481 		printf(" (|");
482 		break;
483 	default:
484 		printf(" (?%d?", e->nv_int);
485 		break;
486 	}
487 	if (e->nv_ptr)
488 		pr0(e->nv_ptr);
489 	pr0(e->nv_next);
490 	printf(")");
491 }
492 #endif
493