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 * make(1)/src triad word-removal across += assignment fragments.
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 int
make_strike_p(const struct request * req)17*3fe5961aSDevin Teske make_strike_p(const struct request *req)
18*3fe5961aSDevin Teske {
19*3fe5961aSDevin Teske
20*3fe5961aSDevin Teske return ((bsdconf_format_processing(format) &
21*3fe5961aSDevin Teske BSDCONF_OPERATOR_EQUALS) != 0 &&
22*3fe5961aSDevin Teske req->listop == '-' && req->newvalue != NULL);
23*3fe5961aSDevin Teske }
24*3fe5961aSDevin Teske
25*3fe5961aSDevin Teske int
make_assign_push(struct request * req,enum bsdconf_op op,const char * piece,uint32_t line)26*3fe5961aSDevin Teske make_assign_push(struct request *req, enum bsdconf_op op,
27*3fe5961aSDevin Teske const char *piece, uint32_t line)
28*3fe5961aSDevin Teske {
29*3fe5961aSDevin Teske struct make_assign *na;
30*3fe5961aSDevin Teske char *pcopy;
31*3fe5961aSDevin Teske
32*3fe5961aSDevin Teske if (piece == NULL)
33*3fe5961aSDevin Teske piece = "";
34*3fe5961aSDevin Teske if ((pcopy = strdup(piece)) == NULL)
35*3fe5961aSDevin Teske return (-1);
36*3fe5961aSDevin Teske na = realloc(req->assigns, (req->nassigns + 1) * sizeof(*na));
37*3fe5961aSDevin Teske if (na == NULL) {
38*3fe5961aSDevin Teske free(pcopy);
39*3fe5961aSDevin Teske return (-1);
40*3fe5961aSDevin Teske }
41*3fe5961aSDevin Teske req->assigns = na;
42*3fe5961aSDevin Teske na[req->nassigns].fileidx = conf_scanidx;
43*3fe5961aSDevin Teske na[req->nassigns].line = line;
44*3fe5961aSDevin Teske na[req->nassigns].op = op;
45*3fe5961aSDevin Teske na[req->nassigns].piece = pcopy;
46*3fe5961aSDevin Teske req->nassigns++;
47*3fe5961aSDevin Teske return (0);
48*3fe5961aSDevin Teske }
49*3fe5961aSDevin Teske
50*3fe5961aSDevin Teske /*
51*3fe5961aSDevin Teske * Apply make/src `name-=word' strikes: for each whitespace-separated word,
52*3fe5961aSDevin Teske * find the last assignment whose piece contains it and remove the word from
53*3fe5961aSDevin Teske * that piece (delete the statement if the piece becomes empty). With
54*3fe5961aSDevin Teske * `check_only', report whether any change would be required without writing.
55*3fe5961aSDevin Teske * Echoes effective old/new (or `(unchanged)') unless -q. Neutralizes each
56*3fe5961aSDevin Teske * handled request so do_writes() skips it. Returns EXIT_SUCCESS or
57*3fe5961aSDevin Teske * EXIT_FAILURE.
58*3fe5961aSDevin Teske */
59*3fe5961aSDevin Teske int
do_make_strikes(int check_only)60*3fe5961aSDevin Teske do_make_strikes(int check_only)
61*3fe5961aSDevin Teske {
62*3fe5961aSDevin Teske char **work;
63*3fe5961aSDevin Teske char *new_eff;
64*3fe5961aSDevin Teske char *token;
65*3fe5961aSDevin Teske char *wordscopy;
66*3fe5961aSDevin Teske char *wp;
67*3fe5961aSDevin Teske const char *old_eff;
68*3fe5961aSDevin Teske int differs;
69*3fe5961aSDevin Teske int rv = EXIT_SUCCESS;
70*3fe5961aSDevin Teske size_t i;
71*3fe5961aSDevin Teske size_t j;
72*3fe5961aSDevin Teske size_t wlen;
73*3fe5961aSDevin Teske unsigned int n;
74*3fe5961aSDevin Teske
75*3fe5961aSDevin Teske for (n = 0; n < nreqs; n++) {
76*3fe5961aSDevin Teske if (!make_strike_p(&reqs[n]))
77*3fe5961aSDevin Teske continue;
78*3fe5961aSDevin Teske if (!reqs[n].found || reqs[n].nassigns == 0) {
79*3fe5961aSDevin Teske if (!ignore_unknown) {
80*3fe5961aSDevin Teske if (!quiet)
81*3fe5961aSDevin Teske warnx("unknown directive '%s'",
82*3fe5961aSDevin Teske reqs[n].name);
83*3fe5961aSDevin Teske rv = EXIT_FAILURE;
84*3fe5961aSDevin Teske }
85*3fe5961aSDevin Teske reqs[n].newvalue = NULL;
86*3fe5961aSDevin Teske reqs[n].name = "";
87*3fe5961aSDevin Teske continue;
88*3fe5961aSDevin Teske }
89*3fe5961aSDevin Teske
90*3fe5961aSDevin Teske if ((work = calloc(reqs[n].nassigns, sizeof(*work))) == NULL)
91*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
92*3fe5961aSDevin Teske for (i = 0; i < reqs[n].nassigns; i++) {
93*3fe5961aSDevin Teske work[i] = strdup(reqs[n].assigns[i].piece);
94*3fe5961aSDevin Teske if (work[i] == NULL)
95*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
96*3fe5961aSDevin Teske }
97*3fe5961aSDevin Teske
98*3fe5961aSDevin Teske differs = 0;
99*3fe5961aSDevin Teske if ((wordscopy = strdup(reqs[n].newvalue)) == NULL)
100*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
101*3fe5961aSDevin Teske wp = wordscopy;
102*3fe5961aSDevin Teske while ((token = strsep(&wp, " \t")) != NULL) {
103*3fe5961aSDevin Teske if (*token == '\0')
104*3fe5961aSDevin Teske continue;
105*3fe5961aSDevin Teske wlen = strlen(token);
106*3fe5961aSDevin Teske for (i = reqs[n].nassigns; i > 0; i--) {
107*3fe5961aSDevin Teske j = i - 1;
108*3fe5961aSDevin Teske if (!list_has(work[j], token, wlen))
109*3fe5961aSDevin Teske continue;
110*3fe5961aSDevin Teske {
111*3fe5961aSDevin Teske char *merged;
112*3fe5961aSDevin Teske
113*3fe5961aSDevin Teske merged = list_merge(work[j], token,
114*3fe5961aSDevin Teske '-');
115*3fe5961aSDevin Teske free(work[j]);
116*3fe5961aSDevin Teske work[j] = merged;
117*3fe5961aSDevin Teske differs = 1;
118*3fe5961aSDevin Teske }
119*3fe5961aSDevin Teske break;
120*3fe5961aSDevin Teske }
121*3fe5961aSDevin Teske }
122*3fe5961aSDevin Teske free(wordscopy);
123*3fe5961aSDevin Teske
124*3fe5961aSDevin Teske if (check_only) {
125*3fe5961aSDevin Teske if (differs) {
126*3fe5961aSDevin Teske if (!quiet)
127*3fe5961aSDevin Teske warnx("%s: value differs",
128*3fe5961aSDevin Teske reqs[n].name);
129*3fe5961aSDevin Teske rv = EXIT_FAILURE;
130*3fe5961aSDevin Teske }
131*3fe5961aSDevin Teske goto neutralize;
132*3fe5961aSDevin Teske }
133*3fe5961aSDevin Teske
134*3fe5961aSDevin Teske old_eff = reqs[n].value != NULL ? reqs[n].value : "";
135*3fe5961aSDevin Teske if (!differs) {
136*3fe5961aSDevin Teske if (!quiet)
137*3fe5961aSDevin Teske print_write_echo(
138*3fe5961aSDevin Teske reqs[n].srcidx >= 0 ?
139*3fe5961aSDevin Teske conf_files[reqs[n].srcidx] : NULL,
140*3fe5961aSDevin Teske 0, reqs[n].name, BSDCONF_OP_ASSIGN,
141*3fe5961aSDevin Teske old_eff, old_eff, 0, 0, 0);
142*3fe5961aSDevin Teske goto neutralize;
143*3fe5961aSDevin Teske }
144*3fe5961aSDevin Teske
145*3fe5961aSDevin Teske /* Effective value after applying the struck pieces */
146*3fe5961aSDevin Teske new_eff = NULL;
147*3fe5961aSDevin Teske for (i = 0; i < reqs[n].nassigns; i++) {
148*3fe5961aSDevin Teske char *merged;
149*3fe5961aSDevin Teske
150*3fe5961aSDevin Teske if (work[i][0] == '\0')
151*3fe5961aSDevin Teske continue;
152*3fe5961aSDevin Teske merged = make_apply(reqs[n].assigns[i].op, new_eff,
153*3fe5961aSDevin Teske work[i]);
154*3fe5961aSDevin Teske if (merged == NULL)
155*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
156*3fe5961aSDevin Teske free(new_eff);
157*3fe5961aSDevin Teske new_eff = merged;
158*3fe5961aSDevin Teske }
159*3fe5961aSDevin Teske if (new_eff == NULL) {
160*3fe5961aSDevin Teske if ((new_eff = strdup("")) == NULL)
161*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
162*3fe5961aSDevin Teske }
163*3fe5961aSDevin Teske
164*3fe5961aSDevin Teske /* Put each changed statement (line-targeted) */
165*3fe5961aSDevin Teske for (i = 0; i < nconf_files; i++) {
166*3fe5961aSDevin Teske struct bsdconf_option *options;
167*3fe5961aSDevin Teske unsigned int nopts = 0;
168*3fe5961aSDevin Teske size_t k;
169*3fe5961aSDevin Teske
170*3fe5961aSDevin Teske for (k = 0; k < reqs[n].nassigns; k++)
171*3fe5961aSDevin Teske if (reqs[n].assigns[k].fileidx == i &&
172*3fe5961aSDevin Teske strcmp(work[k],
173*3fe5961aSDevin Teske reqs[n].assigns[k].piece) != 0)
174*3fe5961aSDevin Teske nopts++;
175*3fe5961aSDevin Teske if (nopts == 0)
176*3fe5961aSDevin Teske continue;
177*3fe5961aSDevin Teske options = calloc(nopts + 1, sizeof(*options));
178*3fe5961aSDevin Teske if (options == NULL)
179*3fe5961aSDevin Teske err(EXIT_FAILURE, NULL);
180*3fe5961aSDevin Teske nopts = 0;
181*3fe5961aSDevin Teske for (k = 0; k < reqs[n].nassigns; k++) {
182*3fe5961aSDevin Teske struct bsdconf_option *opt;
183*3fe5961aSDevin Teske
184*3fe5961aSDevin Teske if (reqs[n].assigns[k].fileidx != i ||
185*3fe5961aSDevin Teske strcmp(work[k],
186*3fe5961aSDevin Teske reqs[n].assigns[k].piece) == 0)
187*3fe5961aSDevin Teske continue;
188*3fe5961aSDevin Teske opt = &options[nopts++];
189*3fe5961aSDevin Teske memset(opt, 0, sizeof(*opt));
190*3fe5961aSDevin Teske opt->type = BSDCONF_TYPE_STR;
191*3fe5961aSDevin Teske opt->directive = reqs[n].name;
192*3fe5961aSDevin Teske opt->match_line = reqs[n].assigns[k].line;
193*3fe5961aSDevin Teske opt->op = reqs[n].assigns[k].op;
194*3fe5961aSDevin Teske if (work[k][0] == '\0') {
195*3fe5961aSDevin Teske opt->action = BSDCONF_ACTION_REMOVE;
196*3fe5961aSDevin Teske opt->value.str = NULL;
197*3fe5961aSDevin Teske } else {
198*3fe5961aSDevin Teske opt->action = BSDCONF_ACTION_SET_VALUE;
199*3fe5961aSDevin Teske opt->value.str = work[k];
200*3fe5961aSDevin Teske }
201*3fe5961aSDevin Teske }
202*3fe5961aSDevin Teske {
203*3fe5961aSDevin Teske int fd;
204*3fe5961aSDevin Teske
205*3fe5961aSDevin Teske if ((fd = open(conf_files[i],
206*3fe5961aSDevin Teske O_WRONLY | O_CREAT | O_EXCL, 0666)) >= 0)
207*3fe5961aSDevin Teske close(fd);
208*3fe5961aSDevin Teske else if (errno != EEXIST)
209*3fe5961aSDevin Teske err(EXIT_FAILURE, "%s", conf_files[i]);
210*3fe5961aSDevin Teske }
211*3fe5961aSDevin Teske if (bsdconf_put(options, conf_files[i],
212*3fe5961aSDevin Teske bsdconf_format_processing(format),
213*3fe5961aSDevin Teske bsdconf_format_put(format)) != 0)
214*3fe5961aSDevin Teske err(EXIT_FAILURE, "%s", conf_files[i]);
215*3fe5961aSDevin Teske if (!quiet && verbose_trail) {
216*3fe5961aSDevin Teske for (k = 0; k < nopts; k++) {
217*3fe5961aSDevin Teske struct bsdconf_option *opt;
218*3fe5961aSDevin Teske const char *oval;
219*3fe5961aSDevin Teske const char *nval;
220*3fe5961aSDevin Teske
221*3fe5961aSDevin Teske opt = &options[k];
222*3fe5961aSDevin Teske /* Recover old piece from assigns */
223*3fe5961aSDevin Teske oval = "";
224*3fe5961aSDevin Teske nval = opt->value.str != NULL ?
225*3fe5961aSDevin Teske opt->value.str : "";
226*3fe5961aSDevin Teske for (j = 0; j < reqs[n].nassigns;
227*3fe5961aSDevin Teske j++) {
228*3fe5961aSDevin Teske if (reqs[n].assigns[j].fileidx
229*3fe5961aSDevin Teske != i ||
230*3fe5961aSDevin Teske reqs[n].assigns[j].line !=
231*3fe5961aSDevin Teske opt->match_line)
232*3fe5961aSDevin Teske continue;
233*3fe5961aSDevin Teske oval =
234*3fe5961aSDevin Teske reqs[n].assigns[j].piece;
235*3fe5961aSDevin Teske break;
236*3fe5961aSDevin Teske }
237*3fe5961aSDevin Teske if (opt->action ==
238*3fe5961aSDevin Teske BSDCONF_ACTION_REMOVE)
239*3fe5961aSDevin Teske printf("%s:%u: %s%s%s "
240*3fe5961aSDevin Teske "(removed)\n",
241*3fe5961aSDevin Teske conf_files[i],
242*3fe5961aSDevin Teske opt->match_line,
243*3fe5961aSDevin Teske reqs[n].name,
244*3fe5961aSDevin Teske op_str(opt->op), oval);
245*3fe5961aSDevin Teske else
246*3fe5961aSDevin Teske print_write_echo(conf_files[i],
247*3fe5961aSDevin Teske opt->match_line,
248*3fe5961aSDevin Teske reqs[n].name, opt->op,
249*3fe5961aSDevin Teske oval, nval, 1, 0, 0);
250*3fe5961aSDevin Teske }
251*3fe5961aSDevin Teske }
252*3fe5961aSDevin Teske free(options);
253*3fe5961aSDevin Teske }
254*3fe5961aSDevin Teske
255*3fe5961aSDevin Teske if (!quiet && !verbose_trail)
256*3fe5961aSDevin Teske print_write_echo(
257*3fe5961aSDevin Teske reqs[n].srcidx >= 0 ?
258*3fe5961aSDevin Teske conf_files[reqs[n].srcidx] : NULL,
259*3fe5961aSDevin Teske 0, reqs[n].name, BSDCONF_OP_ASSIGN, old_eff,
260*3fe5961aSDevin Teske new_eff, 1, 0, 0);
261*3fe5961aSDevin Teske free(reqs[n].value);
262*3fe5961aSDevin Teske reqs[n].value = new_eff;
263*3fe5961aSDevin Teske
264*3fe5961aSDevin Teske neutralize:
265*3fe5961aSDevin Teske for (i = 0; i < reqs[n].nassigns; i++)
266*3fe5961aSDevin Teske free(work[i]);
267*3fe5961aSDevin Teske free(work);
268*3fe5961aSDevin Teske reqs[n].newvalue = NULL;
269*3fe5961aSDevin Teske reqs[n].listop = '\0';
270*3fe5961aSDevin Teske /* Keep name for any trailing read of the same argv slot */
271*3fe5961aSDevin Teske }
272*3fe5961aSDevin Teske
273*3fe5961aSDevin Teske return (rv);
274*3fe5961aSDevin Teske }
275