1/*
2 * Copyright (c) 1995  Peter Wemm
3 * Copyright (c) 1980, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 4. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#ifndef lint
32#if 0
33static char sccsid[] = "@(#)mkheaders.c	8.1 (Berkeley) 6/6/93";
34#endif
35static const char rcsid[] =
36  "$FreeBSD$";
37#endif /* not lint */
38
39/*
40 * Make all the .h files for the optional entries
41 */
42
43#include <ctype.h>
44#include <err.h>
45#include <stdio.h>
46#include <string.h>
47#include <sys/param.h>
48#include "config.h"
49#include "y.tab.h"
50
51static	struct users {
52	int	u_default;
53	int	u_min;
54	int	u_max;
55} users = { 8, 2, 512 };
56
57static char *lower(char *);
58static void read_options(void);
59static void do_option(char *);
60static char *tooption(char *);
61
62void
63options(void)
64{
65	char buf[40];
66	struct cputype *cp;
67	struct opt_list *ol;
68	struct opt *op;
69
70	/* Fake the cpu types as options. */
71	SLIST_FOREACH(cp, &cputype, cpu_next) {
72		op = (struct opt *)calloc(1, sizeof(*op));
73		if (op == NULL)
74			err(EXIT_FAILURE, "calloc");
75		op->op_name = ns(cp->cpu_name);
76		SLIST_INSERT_HEAD(&opt, op, op_next);
77	}
78
79	if (maxusers == 0) {
80		/* fprintf(stderr, "maxusers not specified; will auto-size\n"); */
81	} else if (maxusers < users.u_min) {
82		fprintf(stderr, "minimum of %d maxusers assumed\n",
83		    users.u_min);
84		maxusers = users.u_min;
85	} else if (maxusers > users.u_max)
86		fprintf(stderr, "warning: maxusers > %d (%d)\n",
87		    users.u_max, maxusers);
88
89	/* Fake MAXUSERS as an option. */
90	op = (struct opt *)calloc(1, sizeof(*op));
91	if (op == NULL)
92		err(EXIT_FAILURE, "calloc");
93	op->op_name = ns("MAXUSERS");
94	snprintf(buf, sizeof(buf), "%d", maxusers);
95	op->op_value = ns(buf);
96	SLIST_INSERT_HEAD(&opt, op, op_next);
97
98	read_options();
99
100	/* Fake the value of MACHINE_ARCH as an option if necessary */
101	SLIST_FOREACH(ol, &otab, o_next) {
102		if (strcasecmp(ol->o_name, machinearch) != 0)
103			continue;
104
105		op = (struct opt *)calloc(1, sizeof(*op));
106		if (op == NULL)
107			err(EXIT_FAILURE, "calloc");
108		op->op_name = ns(ol->o_name);
109		SLIST_INSERT_HEAD(&opt, op, op_next);
110		break;
111	}
112
113	SLIST_FOREACH(op, &opt, op_next) {
114		SLIST_FOREACH(ol, &otab, o_next) {
115			if (eq(op->op_name, ol->o_name) &&
116			    (ol->o_flags & OL_ALIAS)) {
117				fprintf(stderr, "Mapping option %s to %s.\n",
118				    op->op_name, ol->o_file);
119				op->op_name = ol->o_file;
120				break;
121			}
122		}
123	}
124	SLIST_FOREACH(ol, &otab, o_next)
125		do_option(ol->o_name);
126	SLIST_FOREACH(op, &opt, op_next) {
127		if (!op->op_ownfile && strncmp(op->op_name, "DEV_", 4)) {
128			fprintf(stderr, "%s: unknown option \"%s\"\n",
129			       PREFIX, op->op_name);
130			exit(1);
131		}
132	}
133}
134
135/*
136 * Generate an <options>.h file
137 */
138
139static void
140do_option(char *name)
141{
142	char *file, *inw;
143	const char *basefile;
144	struct opt_list *ol;
145	struct opt *op;
146	struct opt_head op_head;
147	FILE *inf, *outf;
148	char *value;
149	char *oldvalue;
150	int seen;
151	int tidy;
152
153	file = tooption(name);
154	/*
155	 * Check to see if the option was specified..
156	 */
157	value = NULL;
158	SLIST_FOREACH(op, &opt, op_next) {
159		if (eq(name, op->op_name)) {
160			oldvalue = value;
161			value = op->op_value;
162			if (value == NULL)
163				value = ns("1");
164			if (oldvalue != NULL && !eq(value, oldvalue))
165				fprintf(stderr,
166			    "%s: option \"%s\" redefined from %s to %s\n",
167				   PREFIX, op->op_name, oldvalue,
168				   value);
169			op->op_ownfile++;
170		}
171	}
172
173	remember(file);
174	inf = fopen(file, "r");
175	if (inf == NULL) {
176		outf = fopen(file, "w");
177		if (outf == NULL)
178			err(1, "%s", file);
179
180		/* was the option in the config file? */
181		if (value) {
182			fprintf(outf, "#define %s %s\n", name, value);
183		} /* else empty file */
184
185		(void)fclose(outf);
186		return;
187	}
188	basefile = "";
189	SLIST_FOREACH(ol, &otab, o_next)
190		if (eq(name, ol->o_name)) {
191			basefile = ol->o_file;
192			break;
193		}
194	oldvalue = NULL;
195	SLIST_INIT(&op_head);
196	seen = 0;
197	tidy = 0;
198	for (;;) {
199		char *cp;
200		char *invalue;
201
202		/* get the #define */
203		if ((inw = get_word(inf)) == NULL || inw == (char *)EOF)
204			break;
205		/* get the option name */
206		if ((inw = get_word(inf)) == NULL || inw == (char *)EOF)
207			break;
208		inw = ns(inw);
209		/* get the option value */
210		if ((cp = get_word(inf)) == NULL || cp == (char *)EOF)
211			break;
212		/* option value */
213		invalue = ns(cp); /* malloced */
214		if (eq(inw, name)) {
215			oldvalue = invalue;
216			invalue = value;
217			seen++;
218		}
219		SLIST_FOREACH(ol, &otab, o_next)
220			if (eq(inw, ol->o_name))
221				break;
222		if (!eq(inw, name) && !ol) {
223			fprintf(stderr,
224			    "WARNING: unknown option `%s' removed from %s\n",
225			    inw, file);
226			tidy++;
227		} else if (ol != NULL && !eq(basefile, ol->o_file)) {
228			fprintf(stderr,
229			    "WARNING: option `%s' moved from %s to %s\n",
230			    inw, basefile, ol->o_file);
231			tidy++;
232		} else {
233			op = (struct opt *) calloc(1, sizeof *op);
234			if (op == NULL)
235				err(EXIT_FAILURE, "calloc");
236			op->op_name = inw;
237			op->op_value = invalue;
238			SLIST_INSERT_HEAD(&op_head, op, op_next);
239		}
240
241		/* EOL? */
242		cp = get_word(inf);
243		if (cp == (char *)EOF)
244			break;
245	}
246	(void)fclose(inf);
247	if (!tidy && ((value == NULL && oldvalue == NULL) ||
248	    (value && oldvalue && eq(value, oldvalue)))) {
249		while (!SLIST_EMPTY(&op_head)) {
250			op = SLIST_FIRST(&op_head);
251			SLIST_REMOVE_HEAD(&op_head, op_next);
252			free(op->op_name);
253			free(op->op_value);
254			free(op);
255		}
256		return;
257	}
258
259	if (value && !seen) {
260		/* New option appears */
261		op = (struct opt *) calloc(1, sizeof *op);
262		if (op == NULL)
263			err(EXIT_FAILURE, "calloc");
264		op->op_name = ns(name);
265		op->op_value = value ? ns(value) : NULL;
266		SLIST_INSERT_HEAD(&op_head, op, op_next);
267	}
268
269	outf = fopen(file, "w");
270	if (outf == NULL)
271		err(1, "%s", file);
272	while (!SLIST_EMPTY(&op_head)) {
273		op = SLIST_FIRST(&op_head);
274		/* was the option in the config file? */
275		if (op->op_value) {
276			fprintf(outf, "#define %s %s\n",
277				op->op_name, op->op_value);
278		}
279		SLIST_REMOVE_HEAD(&op_head, op_next);
280		free(op->op_name);
281		free(op->op_value);
282		free(op);
283	}
284	(void)fclose(outf);
285}
286
287/*
288 * Find the filename to store the option spec into.
289 */
290static char *
291tooption(char *name)
292{
293	static char hbuf[MAXPATHLEN];
294	char nbuf[MAXPATHLEN];
295	struct opt_list *po;
296
297	/* "cannot happen"?  the otab list should be complete.. */
298	(void)strlcpy(nbuf, "options.h", sizeof(nbuf));
299
300	SLIST_FOREACH(po, &otab, o_next) {
301		if (eq(po->o_name, name)) {
302			strlcpy(nbuf, po->o_file, sizeof(nbuf));
303			break;
304		}
305	}
306
307	(void)strlcpy(hbuf, path(nbuf), sizeof(hbuf));
308	return (hbuf);
309}
310
311
312static void
313check_duplicate(const char *fname, const char *this)
314{
315	struct opt_list *po;
316
317	SLIST_FOREACH(po, &otab, o_next) {
318		if (eq(po->o_name, this)) {
319			fprintf(stderr, "%s: Duplicate option %s.\n",
320			    fname, this);
321			exit(1);
322		}
323	}
324}
325
326static void
327insert_option(const char *fname, char *this, char *val)
328{
329	struct opt_list *po;
330
331	check_duplicate(fname, this);
332	po = (struct opt_list *) calloc(1, sizeof *po);
333	if (po == NULL)
334		err(EXIT_FAILURE, "calloc");
335	po->o_name = this;
336	po->o_file = val;
337	po->o_flags = 0;
338	SLIST_INSERT_HEAD(&otab, po, o_next);
339}
340
341static void
342update_option(const char *this, char *val, int flags)
343{
344	struct opt_list *po;
345
346	SLIST_FOREACH(po, &otab, o_next) {
347		if (eq(po->o_name, this)) {
348			free(po->o_file);
349			po->o_file = val;
350			po->o_flags = flags;
351			return;
352		}
353	}
354	/*
355	 * Option not found, but that's OK, we just ignore it since it
356	 * may be for another arch.
357	 */
358	return;
359}
360
361static int
362read_option_file(const char *fname, int flags)
363{
364	FILE *fp;
365	char *wd, *this, *val;
366	char genopt[MAXPATHLEN];
367
368	fp = fopen(fname, "r");
369	if (fp == NULL)
370		return (0);
371	while ((wd = get_word(fp)) != (char *)EOF) {
372		if (wd == NULL)
373			continue;
374		if (wd[0] == '#') {
375			while (((wd = get_word(fp)) != (char *)EOF) && wd)
376				continue;
377			continue;
378		}
379		this = ns(wd);
380		val = get_word(fp);
381		if (val == (char *)EOF)
382			return (1);
383		if (val == NULL) {
384			if (flags) {
385				fprintf(stderr, "%s: compat file requires two"
386				    " words per line at %s\n", fname, this);
387				exit(1);
388			}
389			char *s = ns(this);
390			(void)snprintf(genopt, sizeof(genopt), "opt_%s.h",
391			    lower(s));
392			val = genopt;
393			free(s);
394		}
395		val = ns(val);
396		if (flags == 0)
397			insert_option(fname, this, val);
398		else
399			update_option(this, val, flags);
400	}
401	(void)fclose(fp);
402	return (1);
403}
404
405/*
406 * read the options and options.<machine> files
407 */
408static void
409read_options(void)
410{
411	char fname[MAXPATHLEN];
412
413	SLIST_INIT(&otab);
414	read_option_file("../../conf/options", 0);
415	(void)snprintf(fname, sizeof fname, "../../conf/options.%s",
416	    machinename);
417	if (!read_option_file(fname, 0)) {
418		(void)snprintf(fname, sizeof fname, "options.%s", machinename);
419		read_option_file(fname, 0);
420	}
421	read_option_file("../../conf/options-compat", OL_ALIAS);
422}
423
424static char *
425lower(char *str)
426{
427	char *cp = str;
428
429	while (*str) {
430		if (isupper(*str))
431			*str = tolower(*str);
432		str++;
433	}
434	return (cp);
435}
436