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