xref: /freebsd/usr.sbin/sysconf/sysconf_print.c (revision 3fe5961a0b708da599d42cbb6b5e4f030c28ea45)
1*3fe5961aSDevin Teske /*
2*3fe5961aSDevin Teske  * Copyright (c) 2013-2026 Devin Teske <dteske@FreeBSD.org>
3*3fe5961aSDevin Teske  * Copyright (c) 2021-2026 Faraz Vahedi <kfv@FreeBSD.org>
4*3fe5961aSDevin Teske  *
5*3fe5961aSDevin Teske  * SPDX-License-Identifier: BSD-2-Clause
6*3fe5961aSDevin Teske  */
7*3fe5961aSDevin Teske 
8*3fe5961aSDevin Teske /*
9*3fe5961aSDevin Teske  * Display helpers: name/value pairs, `-V' trails, write echoes.
10*3fe5961aSDevin Teske  */
11*3fe5961aSDevin Teske 
12*3fe5961aSDevin Teske #include <ctype.h>
13*3fe5961aSDevin Teske 
14*3fe5961aSDevin Teske #include "sysconf_priv.h"
15*3fe5961aSDevin Teske 
16*3fe5961aSDevin Teske /*
17*3fe5961aSDevin Teske  * True when `name' is a presence-only WITH_/WITHOUT_ build knob
18*3fe5961aSDevin Teske  * (share/mk/bsd.mkopt.mk tests defined(), and src.conf(5) ignores the
19*3fe5961aSDevin Teske  * value). WITHOUT_MODULES is excluded: its value is a module reject list.
20*3fe5961aSDevin Teske  */
21*3fe5961aSDevin Teske int
make_knob_name_p(const char * name)22*3fe5961aSDevin Teske make_knob_name_p(const char *name)
23*3fe5961aSDevin Teske {
24*3fe5961aSDevin Teske 
25*3fe5961aSDevin Teske 	if (strcmp(name, "WITHOUT_MODULES") == 0)
26*3fe5961aSDevin Teske 		return (0);
27*3fe5961aSDevin Teske 	return (strncmp(name, "WITH_", 5) == 0 ||
28*3fe5961aSDevin Teske 	    strncmp(name, "WITHOUT_", 8) == 0);
29*3fe5961aSDevin Teske }
30*3fe5961aSDevin Teske 
31*3fe5961aSDevin Teske /*
32*3fe5961aSDevin Teske  * Precursor to share/mk/bsd.mkopt.mk (seen under /usr/src after
33*3fe5961aSDevin Teske  * bsd.opts.mk / src.opts.mk, not bare make or ports): WITH_FOO=no
34*3fe5961aSDevin Teske  * does not disable FOO (presence still counts). Warn only for WITH_*
35*3fe5961aSDevin Teske  * with the exact value "no" (not WITHOUT_*, not "NO"). On reads,
36*3fe5961aSDevin Teske  * `path'/`line' locate the authoritative assignment when known.
37*3fe5961aSDevin Teske  */
38*3fe5961aSDevin Teske void
warn_with_equals_no(const char * name,const char * value,const char * path,uint32_t line)39*3fe5961aSDevin Teske warn_with_equals_no(const char *name, const char *value, const char *path,
40*3fe5961aSDevin Teske     uint32_t line)
41*3fe5961aSDevin Teske {
42*3fe5961aSDevin Teske 	const char *opt;
43*3fe5961aSDevin Teske 
44*3fe5961aSDevin Teske 	if (format != BSDCONF_FORMAT_MAKE && format != BSDCONF_FORMAT_SRC)
45*3fe5961aSDevin Teske 		return;
46*3fe5961aSDevin Teske 	if (name == NULL || value == NULL || strcmp(value, "no") != 0)
47*3fe5961aSDevin Teske 		return;
48*3fe5961aSDevin Teske 	/* WITH_ but not WITHOUT_ */
49*3fe5961aSDevin Teske 	if (strncmp(name, "WITH_", 5) != 0 ||
50*3fe5961aSDevin Teske 	    strncmp(name, "WITHOUT_", 8) == 0)
51*3fe5961aSDevin Teske 		return;
52*3fe5961aSDevin Teske 	opt = name + 5;
53*3fe5961aSDevin Teske 	if (path != NULL && line != 0)
54*3fe5961aSDevin Teske 		warnx("%s:%u: Use WITHOUT_%s=1 instead of WITH_%s=no",
55*3fe5961aSDevin Teske 		    path, line, opt, opt);
56*3fe5961aSDevin Teske 	else
57*3fe5961aSDevin Teske 		warnx("Use WITHOUT_%s=1 instead of WITH_%s=no", opt, opt);
58*3fe5961aSDevin Teske }
59*3fe5961aSDevin Teske 
60*3fe5961aSDevin Teske /*
61*3fe5961aSDevin Teske  * True when `name' is a presence-only WITH_/WITHOUT_ knob on make/src
62*3fe5961aSDevin Teske  * (bsd.mkopt.mk tests defined(); src.conf(5) ignores the value).
63*3fe5961aSDevin Teske  */
64*3fe5961aSDevin Teske int
make_knob_p(const char * name)65*3fe5961aSDevin Teske make_knob_p(const char *name)
66*3fe5961aSDevin Teske {
67*3fe5961aSDevin Teske 
68*3fe5961aSDevin Teske 	if (format != BSDCONF_FORMAT_MAKE && format != BSDCONF_FORMAT_SRC)
69*3fe5961aSDevin Teske 		return (0);
70*3fe5961aSDevin Teske 	return (make_knob_name_p(name));
71*3fe5961aSDevin Teske }
72*3fe5961aSDevin Teske 
73*3fe5961aSDevin Teske /*
74*3fe5961aSDevin Teske  * Print a directive/value pair according to the display flags. `path' is
75*3fe5961aSDevin Teske  * the configuration file holding the authoritative definition (or NULL).
76*3fe5961aSDevin Teske  */
77*3fe5961aSDevin Teske void
print_pair(const char * path,const char * directive,const char * value)78*3fe5961aSDevin Teske print_pair(const char *path, const char *directive, const char *value)
79*3fe5961aSDevin Teske {
80*3fe5961aSDevin Teske 	int knob;
81*3fe5961aSDevin Teske 
82*3fe5961aSDevin Teske 	if (show_file)
83*3fe5961aSDevin Teske 		value = path != NULL ? path : "(none)";
84*3fe5961aSDevin Teske 	if (verbose && path != NULL && !show_file)
85*3fe5961aSDevin Teske 		printf("%s: ", path);
86*3fe5961aSDevin Teske 	if (name_only) {
87*3fe5961aSDevin Teske 		puts(directive);
88*3fe5961aSDevin Teske 		return;
89*3fe5961aSDevin Teske 	}
90*3fe5961aSDevin Teske 	/* Presence knobs: any assigned value means present (defined()). */
91*3fe5961aSDevin Teske 	knob = !show_file && make_knob_p(directive);
92*3fe5961aSDevin Teske 	if (value_only) {
93*3fe5961aSDevin Teske 		puts(value != NULL ? value : "");
94*3fe5961aSDevin Teske 		return;
95*3fe5961aSDevin Teske 	}
96*3fe5961aSDevin Teske 	/*
97*3fe5961aSDevin Teske 	 * Presence display is for the human-readable form only. With -e,
98*3fe5961aSDevin Teske 	 * emit the real assignment (`NAME=') so the spelling is valid make
99*3fe5961aSDevin Teske 	 * and matches what -s writes.
100*3fe5961aSDevin Teske 	 */
101*3fe5961aSDevin Teske 	if (knob && !show_equals) {
102*3fe5961aSDevin Teske 		printf("%s (present)\n", directive);
103*3fe5961aSDevin Teske 		return;
104*3fe5961aSDevin Teske 	}
105*3fe5961aSDevin Teske 	if (show_equals && !show_file) {
106*3fe5961aSDevin Teske 		if ((bsdconf_format_put(format) &
107*3fe5961aSDevin Teske 		    BSDCONF_PUT_QUOTE_ALWAYS) != 0)
108*3fe5961aSDevin Teske 			printf("%s=\"%s\"\n", directive,
109*3fe5961aSDevin Teske 			    value != NULL ? value : "");
110*3fe5961aSDevin Teske 		else
111*3fe5961aSDevin Teske 			printf("%s=%s\n", directive,
112*3fe5961aSDevin Teske 			    value != NULL ? value : "");
113*3fe5961aSDevin Teske 		return;
114*3fe5961aSDevin Teske 	}
115*3fe5961aSDevin Teske 	printf("%s: %s\n", directive, value != NULL ? value : "");
116*3fe5961aSDevin Teske }
117*3fe5961aSDevin Teske 
118*3fe5961aSDevin Teske /*
119*3fe5961aSDevin Teske  * Spelling of a make(1) (or plain) assignment operator for `-V' trails.
120*3fe5961aSDevin Teske  */
121*3fe5961aSDevin Teske const char *
op_str(enum bsdconf_op op)122*3fe5961aSDevin Teske op_str(enum bsdconf_op op)
123*3fe5961aSDevin Teske {
124*3fe5961aSDevin Teske 
125*3fe5961aSDevin Teske 	switch (op) {
126*3fe5961aSDevin Teske 	case BSDCONF_OP_APPEND:
127*3fe5961aSDevin Teske 		return ("+=");
128*3fe5961aSDevin Teske 	case BSDCONF_OP_COND:
129*3fe5961aSDevin Teske 		return ("?=");
130*3fe5961aSDevin Teske 	case BSDCONF_OP_EXPAND:
131*3fe5961aSDevin Teske 		return (":=");
132*3fe5961aSDevin Teske 	case BSDCONF_OP_SHELL:
133*3fe5961aSDevin Teske 		return ("!=");
134*3fe5961aSDevin Teske 	case BSDCONF_OP_ASSIGN:
135*3fe5961aSDevin Teske 	case BSDCONF_OP_DEFAULT:
136*3fe5961aSDevin Teske 	default:
137*3fe5961aSDevin Teske 		return ("=");
138*3fe5961aSDevin Teske 	}
139*3fe5961aSDevin Teske }
140*3fe5961aSDevin Teske 
141*3fe5961aSDevin Teske /*
142*3fe5961aSDevin Teske  * Append one assignment step to a `-V' trail. No-op when `-V' is unset.
143*3fe5961aSDevin Teske  * Returns zero on success; -1 (errno set) on allocation failure.
144*3fe5961aSDevin Teske  */
145*3fe5961aSDevin Teske int
trail_push(struct trail_step ** trailp,size_t * ntrailp,size_t fileidx,uint32_t line,enum bsdconf_op op,const char * piece,const char * effective)146*3fe5961aSDevin Teske trail_push(struct trail_step **trailp, size_t *ntrailp,
147*3fe5961aSDevin Teske     size_t fileidx, uint32_t line, enum bsdconf_op op,
148*3fe5961aSDevin Teske     const char *piece, const char *effective)
149*3fe5961aSDevin Teske {
150*3fe5961aSDevin Teske 	struct trail_step *nt;
151*3fe5961aSDevin Teske 	char *ecopy;
152*3fe5961aSDevin Teske 	char *pcopy;
153*3fe5961aSDevin Teske 
154*3fe5961aSDevin Teske 	if (!verbose_trail)
155*3fe5961aSDevin Teske 		return (0);
156*3fe5961aSDevin Teske 	if (piece == NULL)
157*3fe5961aSDevin Teske 		piece = "";
158*3fe5961aSDevin Teske 	if (effective == NULL)
159*3fe5961aSDevin Teske 		effective = "";
160*3fe5961aSDevin Teske 	if ((pcopy = strdup(piece)) == NULL)
161*3fe5961aSDevin Teske 		return (-1);
162*3fe5961aSDevin Teske 	if ((ecopy = strdup(effective)) == NULL) {
163*3fe5961aSDevin Teske 		free(pcopy);
164*3fe5961aSDevin Teske 		return (-1);
165*3fe5961aSDevin Teske 	}
166*3fe5961aSDevin Teske 	nt = realloc(*trailp, (*ntrailp + 1) * sizeof(*nt));
167*3fe5961aSDevin Teske 	if (nt == NULL) {
168*3fe5961aSDevin Teske 		free(pcopy);
169*3fe5961aSDevin Teske 		free(ecopy);
170*3fe5961aSDevin Teske 		return (-1);
171*3fe5961aSDevin Teske 	}
172*3fe5961aSDevin Teske 	*trailp = nt;
173*3fe5961aSDevin Teske 	nt[*ntrailp].fileidx = fileidx;
174*3fe5961aSDevin Teske 	nt[*ntrailp].line = line;
175*3fe5961aSDevin Teske 	nt[*ntrailp].op = op;
176*3fe5961aSDevin Teske 	nt[*ntrailp].piece = pcopy;
177*3fe5961aSDevin Teske 	nt[*ntrailp].effective = ecopy;
178*3fe5961aSDevin Teske 	(*ntrailp)++;
179*3fe5961aSDevin Teske 	return (0);
180*3fe5961aSDevin Teske }
181*3fe5961aSDevin Teske 
182*3fe5961aSDevin Teske void
trail_free(struct trail_step * trail,size_t ntrail)183*3fe5961aSDevin Teske trail_free(struct trail_step *trail, size_t ntrail)
184*3fe5961aSDevin Teske {
185*3fe5961aSDevin Teske 	size_t i;
186*3fe5961aSDevin Teske 
187*3fe5961aSDevin Teske 	for (i = 0; i < ntrail; i++) {
188*3fe5961aSDevin Teske 		free(trail[i].piece);
189*3fe5961aSDevin Teske 		free(trail[i].effective);
190*3fe5961aSDevin Teske 	}
191*3fe5961aSDevin Teske 	free(trail);
192*3fe5961aSDevin Teske }
193*3fe5961aSDevin Teske 
194*3fe5961aSDevin Teske /*
195*3fe5961aSDevin Teske  * Print every recorded assignment step for `name'. Each line is
196*3fe5961aSDevin Teske  * `file:line: nameoppiece' and, when the running effective value differs
197*3fe5961aSDevin Teske  * from the fragment, ` -> effective'.
198*3fe5961aSDevin Teske  */
199*3fe5961aSDevin Teske void
print_trail(const char * name,const struct trail_step * trail,size_t ntrail)200*3fe5961aSDevin Teske print_trail(const char *name, const struct trail_step *trail, size_t ntrail)
201*3fe5961aSDevin Teske {
202*3fe5961aSDevin Teske 	const char *path;
203*3fe5961aSDevin Teske 	size_t i;
204*3fe5961aSDevin Teske 
205*3fe5961aSDevin Teske 	for (i = 0; i < ntrail; i++) {
206*3fe5961aSDevin Teske 		path = conf_files[trail[i].fileidx];
207*3fe5961aSDevin Teske 		if (strcmp(trail[i].piece, trail[i].effective) != 0)
208*3fe5961aSDevin Teske 			printf("%s:%u: %s%s%s -> %s\n", path, trail[i].line,
209*3fe5961aSDevin Teske 			    name, op_str(trail[i].op), trail[i].piece,
210*3fe5961aSDevin Teske 			    trail[i].effective);
211*3fe5961aSDevin Teske 		else
212*3fe5961aSDevin Teske 			printf("%s:%u: %s%s%s\n", path, trail[i].line, name,
213*3fe5961aSDevin Teske 			    op_str(trail[i].op), trail[i].piece);
214*3fe5961aSDevin Teske 	}
215*3fe5961aSDevin Teske }
216*3fe5961aSDevin Teske 
217*3fe5961aSDevin Teske /*
218*3fe5961aSDevin Teske  * Echo a write result. With -V: `file:line: name=old -> name=new',
219*3fe5961aSDevin Teske  * `name=value (unchanged)', or `name=value (added)', matching read-trail
220*3fe5961aSDevin Teske  * shape. With -v alone: prefix the pathname. Otherwise: `name: old -> new'
221*3fe5961aSDevin Teske  * / `name: value (unchanged)' (additions still use old -> new with an
222*3fe5961aSDevin Teske  * empty old value). With `-e', the non-trail form uses `=' (and quotes
223*3fe5961aSDevin Teske  * for formats that always quote), matching print_pair() on reads --
224*3fe5961aSDevin Teske  * except make/src WITH_/WITHOUT_ presence knobs, which use
225*3fe5961aSDevin Teske  * `(not present) -> (present)' when newly defined,
226*3fe5961aSDevin Teske  * `(present) (unchanged)' when `-s' finds them already defined or
227*3fe5961aSDevin Teske  * an explicit write leaves the value unchanged, and a normal `old ->
228*3fe5961aSDevin Teske  * new' echo when the placeholder value changes while still present
229*3fe5961aSDevin Teske  * (also for `-e'). Removals are only echoed when the caller passes
230*3fe5961aSDevin Teske  * `-v'/`-V' (not bare `-x'), using the same `name (removed)' / trail
231*3fe5961aSDevin Teske  * form as other directives.
232*3fe5961aSDevin Teske  */
233*3fe5961aSDevin Teske void
print_write_echo(const char * path,uint32_t line,const char * name,enum bsdconf_op op,const char * oldv,const char * newv,int changed,int added,int removed)234*3fe5961aSDevin Teske print_write_echo(const char *path, uint32_t line, const char *name,
235*3fe5961aSDevin Teske     enum bsdconf_op op, const char *oldv, const char *newv, int changed,
236*3fe5961aSDevin Teske     int added, int removed)
237*3fe5961aSDevin Teske {
238*3fe5961aSDevin Teske 	const char *ops;
239*3fe5961aSDevin Teske 	const char *shown;
240*3fe5961aSDevin Teske 	int knob;
241*3fe5961aSDevin Teske 	int quote;
242*3fe5961aSDevin Teske 	int was_present;
243*3fe5961aSDevin Teske 
244*3fe5961aSDevin Teske 	/* NULL oldv: absent; "" : present with empty assignment */
245*3fe5961aSDevin Teske 	was_present = (oldv != NULL);
246*3fe5961aSDevin Teske 	if (oldv == NULL)
247*3fe5961aSDevin Teske 		oldv = "";
248*3fe5961aSDevin Teske 	if (newv == NULL)
249*3fe5961aSDevin Teske 		newv = "";
250*3fe5961aSDevin Teske 	ops = op_str(op);
251*3fe5961aSDevin Teske 	shown = oldv[0] != '\0' ? oldv : newv;
252*3fe5961aSDevin Teske 	knob = make_knob_p(name) && op != BSDCONF_OP_APPEND;
253*3fe5961aSDevin Teske 
254*3fe5961aSDevin Teske 	if (verbose_trail && path != NULL) {
255*3fe5961aSDevin Teske 		if (removed) {
256*3fe5961aSDevin Teske 			printf("%s:%u: %s (removed)\n", path, line, name);
257*3fe5961aSDevin Teske 			return;
258*3fe5961aSDevin Teske 		}
259*3fe5961aSDevin Teske 		if (knob) {
260*3fe5961aSDevin Teske 			if (!was_present || added) {
261*3fe5961aSDevin Teske 				printf("%s:%u: %s: (not present) -> "
262*3fe5961aSDevin Teske 				    "(present)\n", path, line, name);
263*3fe5961aSDevin Teske 				return;
264*3fe5961aSDevin Teske 			}
265*3fe5961aSDevin Teske 			if (strcmp(oldv, newv) == 0) {
266*3fe5961aSDevin Teske 				printf("%s:%u: %s: (present) (unchanged)\n",
267*3fe5961aSDevin Teske 				    path, line, name);
268*3fe5961aSDevin Teske 				return;
269*3fe5961aSDevin Teske 			}
270*3fe5961aSDevin Teske 			/* value change while present: fall through */
271*3fe5961aSDevin Teske 		}
272*3fe5961aSDevin Teske 		if (added)
273*3fe5961aSDevin Teske 			printf("%s:%u: %s%s%s (added)\n", path, line, name,
274*3fe5961aSDevin Teske 			    ops, newv);
275*3fe5961aSDevin Teske 		else if (changed)
276*3fe5961aSDevin Teske 			printf("%s:%u: %s%s%s -> %s%s%s\n", path, line, name,
277*3fe5961aSDevin Teske 			    ops, oldv, name, ops, newv);
278*3fe5961aSDevin Teske 		else
279*3fe5961aSDevin Teske 			printf("%s:%u: %s%s%s (unchanged)\n", path, line, name,
280*3fe5961aSDevin Teske 			    ops, shown);
281*3fe5961aSDevin Teske 		return;
282*3fe5961aSDevin Teske 	}
283*3fe5961aSDevin Teske 	if (verbose && path != NULL)
284*3fe5961aSDevin Teske 		printf("%s: ", path);
285*3fe5961aSDevin Teske 	if (removed) {
286*3fe5961aSDevin Teske 		printf("%s (removed)\n", name);
287*3fe5961aSDevin Teske 		return;
288*3fe5961aSDevin Teske 	}
289*3fe5961aSDevin Teske 	if (knob) {
290*3fe5961aSDevin Teske 		if (!was_present || added) {
291*3fe5961aSDevin Teske 			printf("%s: (not present) -> (present)\n", name);
292*3fe5961aSDevin Teske 			return;
293*3fe5961aSDevin Teske 		}
294*3fe5961aSDevin Teske 		if (strcmp(oldv, newv) == 0) {
295*3fe5961aSDevin Teske 			printf("%s: (present) (unchanged)\n", name);
296*3fe5961aSDevin Teske 			return;
297*3fe5961aSDevin Teske 		}
298*3fe5961aSDevin Teske 		/* value change while present: fall through */
299*3fe5961aSDevin Teske 	}
300*3fe5961aSDevin Teske 	quote = show_equals && !show_file &&
301*3fe5961aSDevin Teske 	    (bsdconf_format_put(format) & BSDCONF_PUT_QUOTE_ALWAYS) != 0;
302*3fe5961aSDevin Teske 	if (show_equals && !show_file) {
303*3fe5961aSDevin Teske 		if (changed || added) {
304*3fe5961aSDevin Teske 			/* sysrc(8)-style: `name=old # -> new' with `-e' */
305*3fe5961aSDevin Teske 			if (quote)
306*3fe5961aSDevin Teske 				printf("%s=\"%s\" # -> \"%s\"\n", name, oldv,
307*3fe5961aSDevin Teske 				    newv);
308*3fe5961aSDevin Teske 			else
309*3fe5961aSDevin Teske 				printf("%s=%s # -> %s\n", name, oldv, newv);
310*3fe5961aSDevin Teske 		} else if (quote)
311*3fe5961aSDevin Teske 			printf("%s=\"%s\" (unchanged)\n", name, shown);
312*3fe5961aSDevin Teske 		else
313*3fe5961aSDevin Teske 			printf("%s=%s (unchanged)\n", name, shown);
314*3fe5961aSDevin Teske 		return;
315*3fe5961aSDevin Teske 	}
316*3fe5961aSDevin Teske 	if (changed || added)
317*3fe5961aSDevin Teske 		printf("%s: %s -> %s\n", name, oldv, newv);
318*3fe5961aSDevin Teske 	else
319*3fe5961aSDevin Teske 		printf("%s: %s (unchanged)\n", name, shown);
320*3fe5961aSDevin Teske }
321