xref: /freebsd/usr.sbin/config/mkoptions.cc (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
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 
45 static	struct users {
46 	int	u_default;
47 	int	u_min;
48 	int	u_max;
49 } users = { 8, 2, 512 };
50 
51 static char *lower(char *);
52 static void read_options(void);
53 static void do_option(char *);
54 static char *tooption(char *);
55 
56 void
57 options(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 
133 static void
134 do_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  */
283 static char *
284 tooption(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 
308 static void
309 check_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 
322 static void
323 insert_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 
337 static void
338 update_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 
357 static int
358 read_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  */
416 static void
417 read_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 
432 static char *
433 lower(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