1/*	$OpenBSD: files.c,v 1.20 2015/01/16 06:40:16 deraadt 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 <errno.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48
49#include "config.h"
50
51extern const char *yyfile;
52
53/*
54 * We check that each full path name is unique.  File base names
55 * should generally also be unique, e.g., having both a net/xx.c and
56 * a kern/xx.c (or, worse, a net/xx.c and a new/xx.c++) is probably
57 * wrong, but is permitted under some conditions.
58 */
59static struct hashtab *basetab;		/* file base names */
60static struct hashtab *pathtab;		/* full path names */
61
62static struct files **nextfile;
63static struct files **unchecked;
64
65static struct objects **nextobject;
66
67static int	checkaux(const char *, void *);
68static int	fixcount(const char *, void *);
69static int	fixfsel(const char *, void *);
70static int	fixsel(const char *, void *);
71static int	expr_eval(struct nvlist *,
72		    int (*)(const char *, void *), void *);
73static void	expr_free(struct nvlist *);
74
75#ifdef DEBUG
76static void	pr0();
77#endif
78
79void
80initfiles(void)
81{
82
83	basetab = ht_new();
84	pathtab = ht_new();
85	nextfile = &allfiles;
86	unchecked = &allfiles;
87	nextobject = &allobjects;
88}
89
90void
91addfile(struct nvlist *nvpath, struct nvlist *optx, int flags, const char *rule)
92{
93	struct files *fi;
94	const char *dotp, *dotp1, *tail, *path, *tail1 = NULL;
95	struct nvlist *nv;
96	size_t baselen;
97	int needc, needf;
98	char base[200];
99
100	/* check various errors */
101	needc = flags & FI_NEEDSCOUNT;
102	needf = flags & FI_NEEDSFLAG;
103	if (needc && needf) {
104		error("cannot mix needs-count and needs-flag");
105		goto bad;
106	}
107	if (optx == NULL && (needc || needf)) {
108		error("nothing to %s", needc ? "count" : "flag");
109		goto bad;
110	}
111
112	for (nv = nvpath; nv; nv = nv->nv_next) {
113		path = nv->nv_name;
114
115		/* find last part of pathname, and same without trailing suffix */
116		tail = strrchr(path, '/');
117		if (tail == NULL)
118			tail = path;
119		else
120			tail++;
121		dotp = strrchr(tail, '.');
122		if (dotp == NULL || dotp[1] == 0 ||
123		    (baselen = dotp - tail) >= sizeof(base)) {
124			error("invalid pathname `%s'", path);
125			goto bad;
126		}
127
128		/*
129		 * Ensure all tailnames are identical, because .o
130		 * filenames must be identical too.
131		 */
132		if (tail1 &&
133		    (dotp - tail != dotp1 - tail1 ||
134		    strncmp(tail1, tail, dotp - tail)))
135			error("different production from %s %s",
136			    nvpath->nv_name, tail);
137		tail1 = tail;
138		dotp1 = dotp;
139	}
140
141	/*
142	 * Commit this file to memory.  We will decide later whether it
143	 * will be used after all.
144	 */
145	fi = emalloc(sizeof *fi);
146	if (ht_insert(pathtab, path, fi)) {
147		free(fi);
148		if ((fi = ht_lookup(pathtab, path)) == NULL)
149			panic("addfile: ht_lookup(%s)", path);
150		error("duplicate file %s", path);
151		xerror(fi->fi_srcfile, fi->fi_srcline,
152		    "here is the original definition");
153	}
154	memcpy(base, tail, baselen);
155	base[baselen] = 0;
156	fi->fi_next = NULL;
157	fi->fi_srcfile = yyfile;
158	fi->fi_srcline = currentline();
159	fi->fi_flags = flags;
160	fi->fi_nvpath = nvpath;
161	fi->fi_base = intern(base);
162	fi->fi_optx = optx;
163	fi->fi_optf = NULL;
164	fi->fi_mkrule = rule;
165	*nextfile = fi;
166	nextfile = &fi->fi_next;
167	return;
168bad:
169	expr_free(optx);
170}
171
172void
173addobject(const char *path, struct nvlist *optx, int flags)
174{
175	struct objects *oi;
176
177	/*
178	 * Commit this object to memory.  We will decide later whether it
179	 * will be used after all.
180	 */
181	oi = emalloc(sizeof *oi);
182	if (ht_insert(pathtab, path, oi)) {
183		free(oi);
184		if ((oi = ht_lookup(pathtab, path)) == NULL)
185			panic("addfile: ht_lookup(%s)", path);
186		error("duplicate file %s", path);
187		xerror(oi->oi_srcfile, oi->oi_srcline,
188		    "here is the original definition");
189	}
190	oi->oi_next = NULL;
191	oi->oi_srcfile = yyfile;
192	oi->oi_srcline = currentline();
193	oi->oi_flags = flags;
194	oi->oi_path = path;
195	oi->oi_optx = optx;
196	oi->oi_optf = NULL;
197	*nextobject = oi;
198	nextobject = &oi->oi_next;
199}
200
201/*
202 * We have finished reading some "files" file, either ../../conf/files
203 * or ./files.$machine.  Make sure that everything that is flagged as
204 * needing a count is reasonable.  (This prevents ../../conf/files from
205 * depending on some machine-specific device.)
206 */
207void
208checkfiles(void)
209{
210	struct files *fi, *last;
211
212	last = NULL;
213	for (fi = *unchecked; fi != NULL; last = fi, fi = fi->fi_next)
214		if ((fi->fi_flags & FI_NEEDSCOUNT) != 0)
215			(void)expr_eval(fi->fi_optx, checkaux, fi);
216	if (last != NULL)
217		unchecked = &last->fi_next;
218}
219
220/*
221 * Auxiliary function for checkfiles, called from expr_eval.
222 * We are not actually interested in the expression's value.
223 */
224static int
225checkaux(const char *name, void *context)
226{
227	struct files *fi = context;
228
229	if (ht_lookup(devbasetab, name) == NULL) {
230		xerror(fi->fi_srcfile, fi->fi_srcline,
231		    "`%s' is not a countable device",
232		    name);
233		/* keep fixfiles() from complaining again */
234		fi->fi_flags |= FI_HIDDEN;
235	}
236	return (0);
237}
238
239/*
240 * We have finished reading everything.  Tack the files down: calculate
241 * selection and counts as needed.  Check that the object files built
242 * from the selected sources do not collide.
243 */
244int
245fixfiles(void)
246{
247	struct files *fi, *ofi;
248	struct nvlist *flathead, **flatp;
249	int err, sel;
250
251	err = 0;
252	for (fi = allfiles; fi != NULL; fi = fi->fi_next) {
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_optx != NULL) {
259			flathead = NULL;
260			flatp = &flathead;
261			sel = expr_eval(fi->fi_optx,
262			    fi->fi_flags & FI_NEEDSCOUNT ? fixcount :
263			    fi->fi_flags & FI_NEEDSFLAG ? fixfsel :
264			    fixsel,
265			    &flatp);
266			fi->fi_optf = flathead;
267			if (!sel)
268				continue;
269		}
270
271		/* We like this file.  Make sure it generates a unique .o. */
272		if (ht_insert(basetab, fi->fi_base, fi)) {
273			if ((ofi = ht_lookup(basetab, fi->fi_base)) == NULL)
274				panic("fixfiles ht_lookup(%s)", fi->fi_base);
275			/*
276			 * If the new file comes from a different source,
277			 * allow the new one to override the old one.
278			 */
279			if (fi->fi_nvpath != ofi->fi_nvpath) {
280				if (ht_replace(basetab, fi->fi_base, fi) != 1)
281					panic("fixfiles ht_replace(%s)",
282					    fi->fi_base);
283				ofi->fi_flags &= ~FI_SEL;
284				ofi->fi_flags |= FI_HIDDEN;
285			} else {
286				xerror(fi->fi_srcfile, fi->fi_srcline,
287				    "object file collision on %s.o, from %s",
288				    fi->fi_base, fi->fi_nvpath->nv_name);
289				xerror(ofi->fi_srcfile, ofi->fi_srcline,
290				    "here is the previous file: %s",
291				    ofi->fi_nvpath->nv_name);
292				err = 1;
293			}
294		}
295		fi->fi_flags |= FI_SEL;
296	}
297	return (err);
298}
299
300/*
301 * We have finished reading everything.  Tack the objects down: calculate
302 * selection.
303 */
304int
305fixobjects(void)
306{
307	struct objects *oi;
308	struct nvlist *flathead, **flatp;
309	int err, sel;
310
311	err = 0;
312	for (oi = allobjects; oi != NULL; oi = oi->oi_next) {
313		/* Optional: see if it is to be included. */
314		if (oi->oi_optx != NULL) {
315			flathead = NULL;
316			flatp = &flathead;
317			sel = expr_eval(oi->oi_optx,
318			    oi->oi_flags & OI_NEEDSFLAG ? fixfsel :
319			    fixsel,
320			    &flatp);
321			oi->oi_optf = flathead;
322			if (!sel)
323				continue;
324		}
325
326		oi->oi_flags |= OI_SEL;
327	}
328	return (err);
329}
330
331/*
332 * Called when evaluating a needs-count expression.  Make sure the
333 * atom is a countable device.  The expression succeeds iff there
334 * is at least one of them (note that while `xx*' will not always
335 * set xx's d_umax > 0, you cannot mix '*' and needs-count).  The
336 * mkheaders() routine wants a flattened, in-order list of the
337 * atoms for `#define name value' lines, so we build that as we
338 * are called to eval each atom.
339 */
340static int
341fixcount(const char *name, void *context)
342{
343	struct nvlist ***p = context;
344	struct devbase *dev;
345	struct nvlist *nv;
346
347	dev = ht_lookup(devbasetab, name);
348	if (dev == NULL)	/* cannot occur here; we checked earlier */
349		panic("fixcount(%s)", name);
350	nv = newnv(name, NULL, NULL, dev->d_umax, NULL);
351	**p = nv;
352	*p = &nv->nv_next;
353	(void)ht_insert(needcnttab, name, nv);
354	return (dev->d_umax != 0);
355}
356
357/*
358 * Called from fixfiles when eval'ing a selection expression for a
359 * file that will generate a .h with flags.  We will need the flat list.
360 */
361static int
362fixfsel(const char *name, void *context)
363{
364	struct nvlist ***p = context;
365	struct nvlist *nv;
366	int sel;
367
368	sel = ht_lookup(selecttab, name) != NULL;
369	nv = newnv(name, NULL, NULL, sel, NULL);
370	**p = nv;
371	*p = &nv->nv_next;
372	return (sel);
373}
374
375/*
376 * As for fixfsel above, but we do not need the flat list.
377 */
378static int
379fixsel(const char *name, void *context)
380{
381
382	return (ht_lookup(selecttab, name) != NULL);
383}
384
385/*
386 * Eval an expression tree.  Calls the given function on each node,
387 * passing it the given context & the name; return value is &/|/! of
388 * results of evaluating atoms.
389 *
390 * No short circuiting ever occurs.  fn must return 0 or 1 (otherwise
391 * our mixing of C's bitwise & boolean here may give surprises).
392 */
393static int
394expr_eval(struct nvlist *expr, int (*fn)(const char *, void *), void *context)
395{
396	int lhs, rhs;
397
398	switch (expr->nv_int) {
399
400	case FX_ATOM:
401		return ((*fn)(expr->nv_name, context));
402
403	case FX_NOT:
404		return (!expr_eval(expr->nv_next, fn, context));
405
406	case FX_AND:
407		lhs = expr_eval(expr->nv_ptr, fn, context);
408		rhs = expr_eval(expr->nv_next, fn, context);
409		return (lhs & rhs);
410
411	case FX_OR:
412		lhs = expr_eval(expr->nv_ptr, fn, context);
413		rhs = expr_eval(expr->nv_next, fn, context);
414		return (lhs | rhs);
415	}
416	panic("expr_eval %d", expr->nv_int);
417	return (0);
418}
419
420/*
421 * Free an expression tree.
422 */
423static void
424expr_free(struct nvlist *expr)
425{
426	struct nvlist *rhs;
427
428	/* This loop traverses down the RHS of each subexpression. */
429	for (; expr != NULL; expr = rhs) {
430		switch (expr->nv_int) {
431
432		/* Atoms and !-exprs have no left hand side. */
433		case FX_ATOM:
434		case FX_NOT:
435			break;
436
437		/* For AND and OR nodes, free the LHS. */
438		case FX_AND:
439		case FX_OR:
440			expr_free(expr->nv_ptr);
441			break;
442
443		default:
444			panic("expr_free %d", expr->nv_int);
445		}
446		rhs = expr->nv_next;
447		nvfree(expr);
448	}
449}
450
451#ifdef DEBUG
452/*
453 * Print expression tree.
454 */
455void
456prexpr(struct nvlist *expr)
457{
458	printf("expr =");
459	pr0(expr);
460	printf("\n");
461	(void)fflush(stdout);
462}
463
464static void
465pr0(struct nvlist *e)
466{
467
468	switch (e->nv_int) {
469	case FX_ATOM:
470		printf(" %s", e->nv_name);
471		return;
472	case FX_NOT:
473		printf(" (!");
474		break;
475	case FX_AND:
476		printf(" (&");
477		break;
478	case FX_OR:
479		printf(" (|");
480		break;
481	default:
482		printf(" (?%d?", e->nv_int);
483		break;
484	}
485	if (e->nv_ptr)
486		pr0(e->nv_ptr);
487	pr0(e->nv_next);
488	printf(")");
489}
490#endif
491