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 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 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 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 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 * 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 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 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 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 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