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