xref: /freebsd/usr.sbin/sysconf/sysconf_edit_make.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  * 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