xref: /freebsd/usr.sbin/config/config.y (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
1 %union {
2 	char	*str;
3 	int	val;
4 	struct	file_list *file;
5 }
6 
7 %token	ARCH
8 %token	COMMA
9 %token	CONFIG
10 %token	CPU
11 %token	NOCPU
12 %token	DEVICE
13 %token	NODEVICE
14 %token	ENV
15 %token	ENVVAR
16 %token	EQUALS
17 %token	PLUSEQUALS
18 %token	HINTS
19 %token	IDENT
20 %token	MAXUSERS
21 %token	OPTIONS
22 %token	NOOPTION
23 %token	MAKEOPTIONS
24 %token	NOMAKEOPTION
25 %token	SEMICOLON
26 %token	INCLUDE
27 %token	INCLUDEOPTIONS
28 %token	FILES
29 
30 %token	<str>	ENVLINE
31 %token	<str>	ID
32 %token	<val>	NUMBER
33 
34 %type	<str>	Save_id
35 %type	<str>	Opt_value
36 %type	<str>	Dev
37 %token	<str>	PATH
38 
39 %{
40 
41 /*-
42  * SPDX-License-Identifier: BSD-3-Clause
43  *
44  * Copyright (c) 1988, 1993
45  *	The Regents of the University of California.  All rights reserved.
46  *
47  * Redistribution and use in source and binary forms, with or without
48  * modification, are permitted provided that the following conditions
49  * are met:
50  * 1. Redistributions of source code must retain the above copyright
51  *    notice, this list of conditions and the following disclaimer.
52  * 2. Redistributions in binary form must reproduce the above copyright
53  *    notice, this list of conditions and the following disclaimer in the
54  *    documentation and/or other materials provided with the distribution.
55  * 3. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  */
71 
72 #include <assert.h>
73 #include <ctype.h>
74 #include <err.h>
75 #include <stdio.h>
76 #include <string.h>
77 
78 #include "config.h"
79 
80 struct	device_head dtab;
81 char	*ident;
82 char	*env;
83 int	yyline;
84 const	char *yyfile;
85 struct  file_list_head ftab;
86 struct  files_name_head fntab;
87 struct	options_files_name_head optfntab;
88 char	errbuf[80];
89 int	maxusers;
90 
91 #define ns(s)	strdup(s)
92 int include(const char *, int);
93 int yyerror(const char *s);
94 int yywrap(void);
95 
96 static void newdev(char *name);
97 static void newfile(char *name);
98 static void newoptionsfile(char *name);
99 static void newenvvar(char *name, bool is_file);
100 static void rmdev_schedule(struct device_head *dh, char *name);
101 static void newopt(struct opt_head *list, char *name, char *value, int append, int dupe);
102 static void rmopt_schedule(struct opt_head *list, char *name);
103 
104 static char *
105 devopt(char *dev)
106 {
107 	char *ret = malloc(strlen(dev) + 5);
108 
109 	sprintf(ret, "DEV_%s", dev);
110 	raisestr(ret);
111 	return ret;
112 }
113 
114 %}
115 %%
116 Configuration:
117 	Many_specs
118 		;
119 
120 Many_specs:
121 	Many_specs Spec
122 		|
123 	/* lambda */
124 		;
125 
126 Spec:
127 	Device_spec SEMICOLON
128 		|
129 	Config_spec SEMICOLON
130 		|
131 	INCLUDE PATH SEMICOLON {
132 		if (incignore == 0)
133 			include($2, 0);
134 		};
135 		|
136 	INCLUDE ID SEMICOLON {
137 	          if (incignore == 0)
138 		  	include($2, 0);
139 		};
140 		|
141 	INCLUDEOPTIONS PATH SEMICOLON { newoptionsfile($2); };
142 		|
143 	INCLUDEOPTIONS ID SEMICOLON { newoptionsfile($2); };
144 		|
145 	FILES ID SEMICOLON { newfile($2); };
146 	        |
147 	SEMICOLON
148 		|
149 	error SEMICOLON
150 		;
151 
152 Config_spec:
153 	ARCH Save_id {
154 		if (machinename != NULL && !eq($2, machinename))
155 		    errx(1, "%s:%d: only one machine directive is allowed",
156 			yyfile, yyline);
157 		machinename = $2;
158 		machinearch = $2;
159 	      } |
160 	ARCH Save_id Save_id {
161 		/*
162 		 * Allow the machinearch to change with a second machine directive,
163 		 * but still enforce no changes to the machinename.
164 		 */
165 		if (machinename != NULL && !eq($2, machinename))
166 		    errx(1, "%s:%d: only one machine directive is allowed",
167 			yyfile, yyline);
168 		machinename = $2;
169 		machinearch = $3;
170 	      } |
171 	CPU Save_id {
172 		struct cputype *cp =
173 		    (struct cputype *)calloc(1, sizeof (struct cputype));
174 		if (cp == NULL)
175 			err(EXIT_FAILURE, "calloc");
176 		cp->cpu_name = $2;
177 		SLIST_INSERT_HEAD(&cputype, cp, cpu_next);
178 	      } |
179 	NOCPU Save_id {
180 		struct cputype *cp, *cp2;
181 		SLIST_FOREACH_SAFE(cp, &cputype, cpu_next, cp2) {
182 			if (eq(cp->cpu_name, $2)) {
183 				SLIST_REMOVE(&cputype, cp, cputype, cpu_next);
184 				free(cp);
185 			}
186 		}
187 	      } |
188 	OPTIONS Opt_list
189 		|
190 	NOOPTION NoOpt_list |
191 	MAKEOPTIONS Mkopt_list
192 		|
193 	NOMAKEOPTION Save_id { rmopt_schedule(&mkopt, $2); } |
194 	IDENT ID { ident = $2; } |
195 	MAXUSERS NUMBER { maxusers = $2; } |
196 	ENV ID { newenvvar($2, true); } |
197 	ENVVAR ENVLINE { newenvvar($2, false); } |
198 	HINTS ID {
199 		struct hint *hint;
200 
201 		hint = (struct hint *)calloc(1, sizeof (struct hint));
202 		if (hint == NULL)
203 			err(EXIT_FAILURE, "calloc");
204 		hint->hint_name = $2;
205 		STAILQ_INSERT_HEAD(&hints, hint, hint_next);
206 	        }
207 
208 System_id:
209 	Save_id { newopt(&mkopt, ns("KERNEL"), $1, 0, 0); };
210 
211 System_parameter_list:
212 	  System_parameter_list ID
213 	| ID
214 	;
215 
216 Opt_list:
217 	Opt_list COMMA Option
218 		|
219 	Option
220 		;
221 
222 NoOpt_list:
223 	NoOpt_list COMMA NoOption
224 	  	|
225 	NoOption
226 		;
227 Option:
228 	Save_id {
229 		newopt(&opt, $1, NULL, 0, 1);
230 		if (strchr($1, '=') != NULL)
231 			errx(1, "%s:%d: The `=' in options should not be "
232 			    "quoted", yyfile, yyline);
233 	      } |
234 	Save_id EQUALS Opt_value {
235 		newopt(&opt, $1, $3, 0, 1);
236 	      } ;
237 
238 NoOption:
239 	Save_id {
240 		rmopt_schedule(&opt, $1);
241 		};
242 
243 Opt_value:
244 	ID { $$ = $1; } |
245 	NUMBER {
246 			char buf[80];
247 
248 			(void) snprintf(buf, sizeof(buf), "%d", $1);
249 			$$ = ns(buf);
250 		} ;
251 
252 Save_id:
253 	ID { $$ = $1; }
254 	;
255 
256 Mkopt_list:
257 	Mkopt_list COMMA Mkoption
258 		|
259 	Mkoption
260 		;
261 
262 Mkoption:
263 	Save_id { newopt(&mkopt, $1, ns(""), 0, 0); } |
264 	Save_id EQUALS { newopt(&mkopt, $1, ns(""), 0, 0); } |
265 	Save_id EQUALS Opt_value { newopt(&mkopt, $1, $3, 0, 0); } |
266 	Save_id PLUSEQUALS Opt_value { newopt(&mkopt, $1, $3, 1, 0); } ;
267 
268 Dev:
269 	ID { $$ = $1; }
270 	;
271 
272 Device_spec:
273 	DEVICE Dev_list
274 		|
275 	NODEVICE NoDev_list
276 		;
277 
278 Dev_list:
279 	Dev_list COMMA Device
280 		|
281 	Device
282 		;
283 
284 NoDev_list:
285 	NoDev_list COMMA NoDevice
286 		|
287 	NoDevice
288 		;
289 
290 Device:
291 	Dev {
292 		newopt(&opt, devopt($1), ns("1"), 0, 0);
293 		/* and the device part */
294 		newdev($1);
295 		}
296 
297 NoDevice:
298 	Dev {
299 		char *s = devopt($1);
300 
301 		rmopt_schedule(&opt, s);
302 		free(s);
303 		/* and the device part */
304 		rmdev_schedule(&dtab, $1);
305 		} ;
306 
307 %%
308 
309 int
310 yyerror(const char *s)
311 {
312 
313 	errx(1, "%s:%d: %s", yyfile, yyline + 1, s);
314 }
315 
316 int
317 yywrap(void)
318 {
319 	if (found_defaults) {
320 		if (freopen(PREFIX, "r", stdin) == NULL)
321 			err(2, "%s", PREFIX);
322 		yyfile = PREFIX;
323 		yyline = 0;
324 		found_defaults = 0;
325 		return 0;
326 	}
327 	return 1;
328 }
329 
330 /*
331  * Add a new file to the list of files.
332  */
333 static void
334 newfile(char *name)
335 {
336 	struct files_name *nl;
337 
338 	nl = (struct files_name *) calloc(1, sizeof *nl);
339 	if (nl == NULL)
340 		err(EXIT_FAILURE, "calloc");
341 	nl->f_name = name;
342 	STAILQ_INSERT_TAIL(&fntab, nl, f_next);
343 }
344 
345 /*
346  * Add a new options file to the list of options files.
347  */
348 static void
349 newoptionsfile(char *name)
350 {
351 	struct files_name *nl;
352 
353 	nl = (struct files_name *) calloc(1, sizeof *nl);
354 	if (nl == NULL)
355 		err(EXIT_FAILURE, "calloc");
356 	nl->f_name = name;
357 	STAILQ_INSERT_TAIL(&optfntab, nl, f_next);
358 }
359 
360 static void
361 newenvvar(char *name, bool is_file)
362 {
363 	struct envvar *envvar;
364 
365 	envvar = (struct envvar *)calloc(1, sizeof (struct envvar));
366 	if (envvar == NULL)
367 		err(EXIT_FAILURE, "calloc");
368 	envvar->env_str = name;
369 	envvar->env_is_file = is_file;
370 	STAILQ_INSERT_HEAD(&envvars, envvar, envvar_next);
371 }
372 
373 /*
374  * Find a device in the list of devices.
375  */
376 static struct device *
377 finddev(struct device_head *dlist, char *name)
378 {
379 	struct device *dp;
380 
381 	STAILQ_FOREACH(dp, dlist, d_next)
382 		if (eq(dp->d_name, name))
383 			return (dp);
384 
385 	return (NULL);
386 }
387 
388 /*
389  * Add a device to the list of devices.
390  */
391 static void
392 newdev(char *name)
393 {
394 	struct device *np, *dp;
395 
396 	if ((dp = finddev(&dtab, name)) != NULL) {
397 		if (strcmp(dp->yyfile, yyfile) == 0)
398 			fprintf(stderr,
399 			    "WARNING: duplicate device `%s' encountered in %s\n",
400 			    name, yyfile);
401 		return;
402 	}
403 
404 	np = (struct device *) calloc(1, sizeof *np);
405 	if (np == NULL)
406 		err(EXIT_FAILURE, "calloc");
407 	np->d_name = name;
408 	np->yyfile = strdup(yyfile);
409 	STAILQ_INSERT_TAIL(&dtab, np, d_next);
410 }
411 
412 /*
413  * Schedule a device to removal.
414  */
415 static void
416 rmdev_schedule(struct device_head *dh, char *name)
417 {
418 	struct device *dp;
419 
420 	dp = finddev(dh, name);
421 	if (dp != NULL) {
422 		STAILQ_REMOVE(dh, dp, device, d_next);
423 		free(dp->yyfile);
424 		free(dp->d_name);
425 		free(dp);
426 	}
427 }
428 
429 /*
430  * Find an option in the list of options.
431  */
432 static struct opt *
433 findopt(struct opt_head *list, char *name)
434 {
435 	struct opt *op;
436 
437 	SLIST_FOREACH(op, list, op_next)
438 		if (eq(op->op_name, name))
439 			return (op);
440 
441 	return (NULL);
442 }
443 
444 /*
445  * Add an option to the list of options.
446  */
447 static void
448 newopt(struct opt_head *list, char *name, char *value, int append, int dupe)
449 {
450 	struct opt *op, *op2;
451 
452 	/*
453 	 * Ignore inclusions listed explicitly for configuration files.
454 	 */
455 	if (eq(name, OPT_AUTOGEN)) {
456 		incignore = 1;
457 		return;
458 	}
459 
460 	op2 = findopt(list, name);
461 	if (op2 != NULL && !append && !dupe) {
462 		if (strcmp(op2->yyfile, yyfile) == 0)
463 			fprintf(stderr,
464 			    "WARNING: duplicate option `%s' encountered.\n", name);
465 		return;
466 	}
467 
468 	op = (struct opt *)calloc(1, sizeof (struct opt));
469 	if (op == NULL)
470 		err(EXIT_FAILURE, "calloc");
471 	op->op_name = name;
472 	op->op_ownfile = 0;
473 	op->op_value = value;
474 	op->yyfile = strdup(yyfile);
475 	if (op2 != NULL) {
476 		if (append) {
477 			while (SLIST_NEXT(op2, op_append) != NULL)
478 				op2 = SLIST_NEXT(op2, op_append);
479 			SLIST_NEXT(op2, op_append) = op;
480 		} else {
481 			while (SLIST_NEXT(op2, op_next) != NULL)
482 				op2 = SLIST_NEXT(op2, op_next);
483 			SLIST_NEXT(op2, op_next) = op;
484 		}
485 	} else
486 		SLIST_INSERT_HEAD(list, op, op_next);
487 }
488 
489 /*
490  * Remove an option from the list of options.
491  */
492 static void
493 rmopt_schedule(struct opt_head *list, char *name)
494 {
495 	struct opt *op;
496 
497 	while ((op = findopt(list, name)) != NULL) {
498 		SLIST_REMOVE(list, op, opt, op_next);
499 		free(op->yyfile);
500 		free(op->op_name);
501 		free(op);
502 	}
503 }
504