xref: /netbsd-src/usr.bin/config/util.c (revision 93b25323436f832f83862b693a2b44c057a19e45)
1 /*	$NetBSD: util.c,v 1.23 2025/01/07 14:21:11 joe Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *	This product includes software developed by the University of
14  *	California, Lawrence Berkeley Laboratories.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	from: @(#)util.c	8.1 (Berkeley) 6/6/93
41  */
42 
43 #if HAVE_NBTOOL_CONFIG_H
44 #include "nbtool_config.h"
45 #endif
46 
47 #include <sys/cdefs.h>
48 __RCSID("$NetBSD: util.c,v 1.23 2025/01/07 14:21:11 joe Exp $");
49 
50 #include <sys/types.h>
51 #include <assert.h>
52 #include <ctype.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <stdarg.h>
57 #include <util.h>
58 #include <err.h>
59 #include "defs.h"
60 
61 extern const char *yyfile;
62 
63 static void cfgvxerror(const char *, int, const char *, va_list)
64 	     __printflike(3, 0);
65 static void cfgvxdbg(const char *, int, const char *, va_list)
66 	     __printflike(3, 0);
67 static void cfgvxwarn(const char *, int, const char *, va_list)
68 	     __printflike(3, 0);
69 static void cfgvxmsg(const char *, int, const char *, const char *, va_list)
70      __printflike(4, 0);
71 
72 /************************************************************/
73 
74 /*
75  * Prefix stack
76  */
77 
78 static void
79 prefixlist_push(struct prefixlist *pl, const char *path)
80 {
81 	struct prefix *prevpf = SLIST_FIRST(pl);
82 	struct prefix *pf;
83 	char *cp;
84 
85 	pf = ecalloc(1, sizeof(struct prefix));
86 
87 	if (prevpf != NULL) {
88 		cp = emalloc(strlen(prevpf->pf_prefix) + 1 +
89 		    strlen(path) + 1);
90 		(void) sprintf(cp, "%s/%s", prevpf->pf_prefix, path);
91 		pf->pf_prefix = intern(cp);
92 		free(cp);
93 	} else
94 		pf->pf_prefix = intern(path);
95 
96 	SLIST_INSERT_HEAD(pl, pf, pf_next);
97 }
98 
99 static void
100 prefixlist_pop(struct prefixlist *allpl, struct prefixlist *pl)
101 {
102 	struct prefix *pf;
103 
104 	if ((pf = SLIST_FIRST(pl)) == NULL) {
105 		cfgerror("no prefixes on the stack to pop");
106 		return;
107 	}
108 
109 	SLIST_REMOVE_HEAD(pl, pf_next);
110 	/* Remember this prefix for emitting -I... directives later. */
111 	SLIST_INSERT_HEAD(allpl, pf, pf_next);
112 }
113 
114 /*
115  * Push a prefix onto the prefix stack.
116  */
117 void
118 prefix_push(const char *path)
119 {
120 	prefixlist_push(&prefixes, path);
121 }
122 
123 /*
124  * Pop a prefix off the prefix stack.
125  */
126 void
127 prefix_pop(void)
128 {
129 	prefixlist_pop(&allprefixes, &prefixes);
130 }
131 
132 /*
133  * Push a buildprefix onto the buildprefix stack.
134  */
135 void
136 buildprefix_push(const char *path)
137 {
138 	prefixlist_push(&buildprefixes, path);
139 }
140 
141 /*
142  * Pop a buildprefix off the buildprefix stack.
143  */
144 void
145 buildprefix_pop(void)
146 {
147 	prefixlist_pop(&allbuildprefixes, &buildprefixes);
148 }
149 
150 /*
151  * Prepend the source path to a file name.
152  */
153 char *
154 sourcepath(const char *file)
155 {
156 	size_t len;
157 	char *cp;
158 	struct prefix *pf;
159 
160 	pf = SLIST_EMPTY(&prefixes) ? NULL : SLIST_FIRST(&prefixes);
161 	if (pf != NULL && *pf->pf_prefix == '/')
162 		len = strlen(pf->pf_prefix) + 1 + strlen(file) + 1;
163 	else {
164 		len = strlen(srcdir) + 1 + strlen(file) + 1;
165 		if (pf != NULL)
166 			len += strlen(pf->pf_prefix) + 1;
167 	}
168 
169 	cp = emalloc(len);
170 
171 	if (pf != NULL) {
172 		if (*pf->pf_prefix == '/')
173 			(void) sprintf(cp, "%s/%s", pf->pf_prefix, file);
174 		else
175 			(void) sprintf(cp, "%s/%s/%s", srcdir,
176 			    pf->pf_prefix, file);
177 	} else
178 		(void) sprintf(cp, "%s/%s", srcdir, file);
179 	return (cp);
180 }
181 
182 /************************************************************/
183 
184 /*
185  * Data structures
186  */
187 
188 /*
189  * nvlist
190  */
191 
192 struct nvlist *
193 newnv(const char *name, const char *str, void *ptr, long long i, struct nvlist *next)
194 {
195 	struct nvlist *nv;
196 
197 	nv = ecalloc(1, sizeof(*nv));
198 	nv->nv_next = next;
199 	nv->nv_name = name;
200 	nv->nv_str = str;
201 	nv->nv_ptr = ptr;
202 	nv->nv_num = i;
203 	nv->nv_where.w_srcfile = yyfile;
204 	nv->nv_where.w_srcline = currentline();
205 	return nv;
206 }
207 
208 /*
209  * Free an nvlist structure (just one).
210  */
211 void
212 nvfree(struct nvlist *nv)
213 {
214 
215 	free(nv);
216 }
217 
218 /*
219  * Free an nvlist (the whole list).
220  */
221 void
222 nvfreel(struct nvlist *nv)
223 {
224 	struct nvlist *next;
225 
226 	for (; nv != NULL; nv = next) {
227 		next = nv->nv_next;
228 		free(nv);
229 	}
230 }
231 
232 struct nvlist *
233 nvcat(struct nvlist *nv1, struct nvlist *nv2)
234 {
235 	struct nvlist *nv;
236 
237 	if (nv1 == NULL)
238 		return nv2;
239 
240 	for (nv = nv1; nv->nv_next != NULL; nv = nv->nv_next);
241 
242 	nv->nv_next = nv2;
243 	return nv1;
244 }
245 
246 /*
247  * Option definition lists
248  */
249 
250 struct defoptlist *
251 defoptlist_create(const char *name, const char *val, const char *lintval)
252 {
253 	struct defoptlist *dl;
254 
255 	dl = emalloc(sizeof(*dl));
256 	dl->dl_next = NULL;
257 	dl->dl_name = name;
258 	dl->dl_value = val;
259 	dl->dl_lintvalue = lintval;
260 	dl->dl_obsolete = 0;
261 	dl->dl_mkvar = 0;
262 	dl->dl_depends = NULL;
263 	dl->dl_where.w_srcfile = yyfile;
264 	dl->dl_where.w_srcline = currentline();
265 	return dl;
266 }
267 
268 void
269 defoptlist_destroy(struct defoptlist *dl)
270 {
271 	struct defoptlist *next;
272 
273 	while (dl != NULL) {
274 		next = dl->dl_next;
275 		dl->dl_next = NULL;
276 
277 		// XXX should we assert that dl->dl_deps is null to
278 		// be sure the deps have already been destroyed?
279 		free(dl);
280 
281 		dl = next;
282 	}
283 }
284 
285 struct defoptlist *
286 defoptlist_append(struct defoptlist *dla, struct defoptlist *dlb)
287 {
288 	struct defoptlist *dl;
289 
290 	if (dla == NULL)
291 		return dlb;
292 
293 	for (dl = dla; dl->dl_next != NULL; dl = dl->dl_next)
294 		;
295 
296 	dl->dl_next = dlb;
297 	return dla;
298 }
299 
300 /*
301  * Locator lists
302  */
303 
304 struct loclist *
305 loclist_create(const char *name, const char *string, long long num)
306 {
307 	struct loclist *ll;
308 
309 	ll = emalloc(sizeof(*ll));
310 	ll->ll_name = name;
311 	ll->ll_string = string;
312 	ll->ll_num = num;
313 	ll->ll_next = NULL;
314 	return ll;
315 }
316 
317 void
318 loclist_destroy(struct loclist *ll)
319 {
320 	struct loclist *next;
321 
322 	while (ll != NULL) {
323 		next = ll->ll_next;
324 		ll->ll_next = NULL;
325 		free(ll);
326 		ll = next;
327 	}
328 }
329 
330 /*
331  * Attribute lists
332  */
333 
334 struct attrlist *
335 attrlist_create(void)
336 {
337 	struct attrlist *al;
338 
339 	al = emalloc(sizeof(*al));
340 	al->al_next = NULL;
341 	al->al_this = NULL;
342 	return al;
343 }
344 
345 struct attrlist *
346 attrlist_cons(struct attrlist *next, struct attr *a)
347 {
348 	struct attrlist *al;
349 
350 	al = attrlist_create();
351 	al->al_next = next;
352 	al->al_this = a;
353 	return al;
354 }
355 
356 void
357 attrlist_destroy(struct attrlist *al)
358 {
359 	assert(al->al_next == NULL);
360 	assert(al->al_this == NULL);
361 	free(al);
362 }
363 
364 void
365 attrlist_destroyall(struct attrlist *al)
366 {
367 	struct attrlist *next;
368 
369 	while (al != NULL) {
370 		next = al->al_next;
371 		al->al_next = NULL;
372 		/* XXX should we make the caller guarantee this? */
373 		al->al_this = NULL;
374 		attrlist_destroy(al);
375 		al = next;
376 	}
377 }
378 
379 /*
380  * Condition expressions
381  */
382 
383 /*
384  * Create an expression node.
385  */
386 struct condexpr *
387 condexpr_create(enum condexpr_types type)
388 {
389 	struct condexpr *cx;
390 
391 	cx = emalloc(sizeof(*cx));
392 	cx->cx_type = type;
393 	switch (type) {
394 
395 	    case CX_ATOM:
396 		cx->cx_atom = NULL;
397 		break;
398 
399 	    case CX_NOT:
400 		cx->cx_not = NULL;
401 		break;
402 
403 	    case CX_AND:
404 		cx->cx_and.left = NULL;
405 		cx->cx_and.right = NULL;
406 		break;
407 
408 	    case CX_OR:
409 		cx->cx_or.left = NULL;
410 		cx->cx_or.right = NULL;
411 		break;
412 
413 	    default:
414 		panic("condexpr_create: invalid expr type %d", (int)type);
415 	}
416 	return cx;
417 }
418 
419 /*
420  * Free an expression tree.
421  */
422 void
423 condexpr_destroy(struct condexpr *expr)
424 {
425 	switch (expr->cx_type) {
426 
427 	    case CX_ATOM:
428 		/* nothing */
429 		break;
430 
431 	    case CX_NOT:
432 		condexpr_destroy(expr->cx_not);
433 		break;
434 
435 	    case CX_AND:
436 		condexpr_destroy(expr->cx_and.left);
437 		condexpr_destroy(expr->cx_and.right);
438 		break;
439 
440 	    case CX_OR:
441 		condexpr_destroy(expr->cx_or.left);
442 		condexpr_destroy(expr->cx_or.right);
443 		break;
444 
445 	    default:
446 		panic("condexpr_destroy: invalid expr type %d",
447 		      (int)expr->cx_type);
448 	}
449 	free(expr);
450 }
451 
452 /************************************************************/
453 
454 /*
455  * Diagnostic messages
456  */
457 
458 void
459 cfgdbg(const char *fmt, ...)
460 {
461 	va_list ap;
462 	extern const char *yyfile;
463 
464 	va_start(ap, fmt);
465 	cfgvxdbg(yyfile, currentline(), fmt, ap);
466 	va_end(ap);
467 }
468 
469 void
470 cfgwarn(const char *fmt, ...)
471 {
472 	va_list ap;
473 	extern const char *yyfile;
474 
475 	va_start(ap, fmt);
476 	cfgvxwarn(yyfile, currentline(), fmt, ap);
477 	va_end(ap);
478 }
479 
480 void
481 cfgxwarn(const char *file, int line, const char *fmt, ...)
482 {
483 	va_list ap;
484 
485 	va_start(ap, fmt);
486 	cfgvxwarn(file, line, fmt, ap);
487 	va_end(ap);
488 }
489 
490 static void
491 cfgvxdbg(const char *file, int line, const char *fmt, va_list ap)
492 {
493 	cfgvxmsg(file, line, "debug: ", fmt, ap);
494 }
495 
496 static void
497 cfgvxwarn(const char *file, int line, const char *fmt, va_list ap)
498 {
499 	cfgvxmsg(file, line, "warning: ", fmt, ap);
500 }
501 
502 /*
503  * External (config file) error.  Complain, using current file
504  * and line number.
505  */
506 void
507 cfgerror(const char *fmt, ...)
508 {
509 	va_list ap;
510 	extern const char *yyfile;
511 
512 	va_start(ap, fmt);
513 	cfgvxerror(yyfile, currentline(), fmt, ap);
514 	va_end(ap);
515 }
516 
517 /*
518  * Delayed config file error (i.e., something was wrong but we could not
519  * find out about it until later).
520  */
521 void
522 cfgxerror(const char *file, int line, const char *fmt, ...)
523 {
524 	va_list ap;
525 
526 	va_start(ap, fmt);
527 	cfgvxerror(file, line, fmt, ap);
528 	va_end(ap);
529 }
530 
531 /*
532  * Internal form of error() and xerror().
533  */
534 static void
535 cfgvxerror(const char *file, int line, const char *fmt, va_list ap)
536 {
537 	cfgvxmsg(file, line, "", fmt, ap);
538 	errors++;
539 }
540 
541 
542 /*
543  * Internal error, abort.
544  */
545 __dead void
546 panic(const char *fmt, ...)
547 {
548 	va_list ap;
549 
550 	va_start(ap, fmt);
551 	(void)fprintf(stderr, "%s: panic: ", getprogname());
552 	(void)vfprintf(stderr, fmt, ap);
553 	(void)putc('\n', stderr);
554 	va_end(ap);
555 	exit(2);
556 }
557 
558 /*
559  * Internal form of error() and xerror().
560  */
561 static void
562 cfgvxmsg(const char *file, int line, const char *msgclass, const char *fmt,
563       va_list ap)
564 {
565 
566 	(void)fprintf(stderr, "%s:%d: %s", file, line, msgclass);
567 	(void)vfprintf(stderr, fmt, ap);
568 	(void)putc('\n', stderr);
569 }
570 
571 void
572 autogen_comment(FILE *fp, const char *targetfile)
573 {
574 
575 	(void)fprintf(fp,
576 	    "/*\n"
577 	    " * MACHINE GENERATED: DO NOT EDIT\n"
578 	    " *\n"
579 	    " * %s, from \"%s\"\n"
580 	    " */\n\n",
581 	    targetfile, conffile);
582 }
583