xref: /freebsd-src/contrib/libarchive/tar/subst.c (revision bd66c1b43e33540205dbc1187c2f2a15c58b57ba)
1caf54c4fSMartin Matuska /*-
2*bd66c1b4SMartin Matuska  * SPDX-License-Identifier: BSD-2-Clause
3*bd66c1b4SMartin Matuska  *
4caf54c4fSMartin Matuska  * Copyright (c) 2008 Joerg Sonnenberger
5caf54c4fSMartin Matuska  * All rights reserved.
6caf54c4fSMartin Matuska  */
7caf54c4fSMartin Matuska 
8caf54c4fSMartin Matuska #include "bsdtar_platform.h"
9caf54c4fSMartin Matuska 
10b9128a37SMartin Matuska #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H)
11caf54c4fSMartin Matuska #include "bsdtar.h"
12caf54c4fSMartin Matuska 
13caf54c4fSMartin Matuska #include <errno.h>
14b9128a37SMartin Matuska #if defined(HAVE_PCREPOSIX_H)
15acc60b03SMartin Matuska #include <pcreposix.h>
16b9128a37SMartin Matuska #elif defined(HAVE_PCRE2POSIX_H)
17b9128a37SMartin Matuska #include <pcre2posix.h>
18acc60b03SMartin Matuska #else
19caf54c4fSMartin Matuska #include <regex.h>
20acc60b03SMartin Matuska #endif
21caf54c4fSMartin Matuska #include <stdlib.h>
22caf54c4fSMartin Matuska #include <string.h>
23caf54c4fSMartin Matuska 
24caf54c4fSMartin Matuska #ifndef REG_BASIC
25caf54c4fSMartin Matuska #define	REG_BASIC 0
26caf54c4fSMartin Matuska #endif
27caf54c4fSMartin Matuska 
28caf54c4fSMartin Matuska #include "err.h"
29caf54c4fSMartin Matuska 
30caf54c4fSMartin Matuska struct subst_rule {
31caf54c4fSMartin Matuska 	struct subst_rule *next;
32caf54c4fSMartin Matuska 	regex_t re;
33caf54c4fSMartin Matuska 	char *result;
34b9128a37SMartin Matuska 	unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1, from_begin:1;
35caf54c4fSMartin Matuska };
36caf54c4fSMartin Matuska 
37caf54c4fSMartin Matuska struct substitution {
38caf54c4fSMartin Matuska 	struct subst_rule *first_rule, *last_rule;
39caf54c4fSMartin Matuska };
40caf54c4fSMartin Matuska 
41caf54c4fSMartin Matuska static void
42caf54c4fSMartin Matuska init_substitution(struct bsdtar *bsdtar)
43caf54c4fSMartin Matuska {
44caf54c4fSMartin Matuska 	struct substitution *subst;
45caf54c4fSMartin Matuska 
46caf54c4fSMartin Matuska 	bsdtar->substitution = subst = malloc(sizeof(*subst));
47caf54c4fSMartin Matuska 	if (subst == NULL)
48caf54c4fSMartin Matuska 		lafe_errc(1, errno, "Out of memory");
49caf54c4fSMartin Matuska 	subst->first_rule = subst->last_rule = NULL;
50caf54c4fSMartin Matuska }
51caf54c4fSMartin Matuska 
52caf54c4fSMartin Matuska void
53caf54c4fSMartin Matuska add_substitution(struct bsdtar *bsdtar, const char *rule_text)
54caf54c4fSMartin Matuska {
55caf54c4fSMartin Matuska 	struct subst_rule *rule;
56caf54c4fSMartin Matuska 	struct substitution *subst;
57caf54c4fSMartin Matuska 	const char *end_pattern, *start_subst;
58caf54c4fSMartin Matuska 	char *pattern;
59caf54c4fSMartin Matuska 	int r;
60caf54c4fSMartin Matuska 
61caf54c4fSMartin Matuska 	if ((subst = bsdtar->substitution) == NULL) {
62caf54c4fSMartin Matuska 		init_substitution(bsdtar);
63caf54c4fSMartin Matuska 		subst = bsdtar->substitution;
64caf54c4fSMartin Matuska 	}
65caf54c4fSMartin Matuska 
66caf54c4fSMartin Matuska 	rule = malloc(sizeof(*rule));
67caf54c4fSMartin Matuska 	if (rule == NULL)
68caf54c4fSMartin Matuska 		lafe_errc(1, errno, "Out of memory");
69caf54c4fSMartin Matuska 	rule->next = NULL;
70f3e9b21aSMartin Matuska 	rule->result = NULL;
71caf54c4fSMartin Matuska 
72caf54c4fSMartin Matuska 	if (subst->last_rule == NULL)
73caf54c4fSMartin Matuska 		subst->first_rule = rule;
74caf54c4fSMartin Matuska 	else
75caf54c4fSMartin Matuska 		subst->last_rule->next = rule;
76caf54c4fSMartin Matuska 	subst->last_rule = rule;
77caf54c4fSMartin Matuska 
78caf54c4fSMartin Matuska 	if (*rule_text == '\0')
79caf54c4fSMartin Matuska 		lafe_errc(1, 0, "Empty replacement string");
80caf54c4fSMartin Matuska 	end_pattern = strchr(rule_text + 1, *rule_text);
81caf54c4fSMartin Matuska 	if (end_pattern == NULL)
82caf54c4fSMartin Matuska 		lafe_errc(1, 0, "Invalid replacement string");
83caf54c4fSMartin Matuska 
84caf54c4fSMartin Matuska 	pattern = malloc(end_pattern - rule_text);
85caf54c4fSMartin Matuska 	if (pattern == NULL)
86caf54c4fSMartin Matuska 		lafe_errc(1, errno, "Out of memory");
87caf54c4fSMartin Matuska 	memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
88caf54c4fSMartin Matuska 	pattern[end_pattern - rule_text - 1] = '\0';
89caf54c4fSMartin Matuska 
90caf54c4fSMartin Matuska 	if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
91caf54c4fSMartin Matuska 		char buf[80];
92caf54c4fSMartin Matuska 		regerror(r, &rule->re, buf, sizeof(buf));
93caf54c4fSMartin Matuska 		lafe_errc(1, 0, "Invalid regular expression: %s", buf);
94caf54c4fSMartin Matuska 	}
95caf54c4fSMartin Matuska 	free(pattern);
96caf54c4fSMartin Matuska 
97caf54c4fSMartin Matuska 	start_subst = end_pattern + 1;
98caf54c4fSMartin Matuska 	end_pattern = strchr(start_subst, *rule_text);
99caf54c4fSMartin Matuska 	if (end_pattern == NULL)
100caf54c4fSMartin Matuska 		lafe_errc(1, 0, "Invalid replacement string");
101caf54c4fSMartin Matuska 
102caf54c4fSMartin Matuska 	rule->result = malloc(end_pattern - start_subst + 1);
103caf54c4fSMartin Matuska 	if (rule->result == NULL)
104caf54c4fSMartin Matuska 		lafe_errc(1, errno, "Out of memory");
105caf54c4fSMartin Matuska 	memcpy(rule->result, start_subst, end_pattern - start_subst);
106caf54c4fSMartin Matuska 	rule->result[end_pattern - start_subst] = '\0';
107caf54c4fSMartin Matuska 
1086c95142eSMartin Matuska 	/* Defaults */
1096c95142eSMartin Matuska 	rule->global = 0; /* Don't do multiple replacements. */
1106c95142eSMartin Matuska 	rule->print = 0; /* Don't print. */
1116c95142eSMartin Matuska 	rule->regular = 1; /* Rewrite regular filenames. */
1126c95142eSMartin Matuska 	rule->symlink = 1; /* Rewrite symlink targets. */
1136c95142eSMartin Matuska 	rule->hardlink = 1; /* Rewrite hardlink targets. */
114b9128a37SMartin Matuska 	rule->from_begin = 0; /* Don't match from start. */
115caf54c4fSMartin Matuska 
116caf54c4fSMartin Matuska 	while (*++end_pattern) {
117caf54c4fSMartin Matuska 		switch (*end_pattern) {
118b9128a37SMartin Matuska 		case 'b':
119b9128a37SMartin Matuska 		case 'B':
120b9128a37SMartin Matuska 			rule->from_begin = 1;
121b9128a37SMartin Matuska 			break;
122caf54c4fSMartin Matuska 		case 'g':
123caf54c4fSMartin Matuska 		case 'G':
124caf54c4fSMartin Matuska 			rule->global = 1;
125caf54c4fSMartin Matuska 			break;
1266c95142eSMartin Matuska 		case 'h':
1276c95142eSMartin Matuska 			rule->hardlink = 1;
1286c95142eSMartin Matuska 			break;
1296c95142eSMartin Matuska 		case 'H':
1306c95142eSMartin Matuska 			rule->hardlink = 0;
1316c95142eSMartin Matuska 			break;
132caf54c4fSMartin Matuska 		case 'p':
133caf54c4fSMartin Matuska 		case 'P':
134caf54c4fSMartin Matuska 			rule->print = 1;
135caf54c4fSMartin Matuska 			break;
1366c95142eSMartin Matuska 		case 'r':
1376c95142eSMartin Matuska 			rule->regular = 1;
1386c95142eSMartin Matuska 			break;
1396c95142eSMartin Matuska 		case 'R':
1406c95142eSMartin Matuska 			rule->regular = 0;
1416c95142eSMartin Matuska 			break;
142caf54c4fSMartin Matuska 		case 's':
143caf54c4fSMartin Matuska 			rule->symlink = 1;
144caf54c4fSMartin Matuska 			break;
1456c95142eSMartin Matuska 		case 'S':
1466c95142eSMartin Matuska 			rule->symlink = 0;
1476c95142eSMartin Matuska 			break;
148caf54c4fSMartin Matuska 		default:
149caf54c4fSMartin Matuska 			lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
150b9128a37SMartin Matuska 			/* NOTREACHED */
151caf54c4fSMartin Matuska 		}
152caf54c4fSMartin Matuska 	}
153caf54c4fSMartin Matuska }
154caf54c4fSMartin Matuska 
155caf54c4fSMartin Matuska static void
156caf54c4fSMartin Matuska realloc_strncat(char **str, const char *append, size_t len)
157caf54c4fSMartin Matuska {
158caf54c4fSMartin Matuska 	char *new_str;
159caf54c4fSMartin Matuska 	size_t old_len;
160caf54c4fSMartin Matuska 
161caf54c4fSMartin Matuska 	if (*str == NULL)
162caf54c4fSMartin Matuska 		old_len = 0;
163caf54c4fSMartin Matuska 	else
164caf54c4fSMartin Matuska 		old_len = strlen(*str);
165caf54c4fSMartin Matuska 
166caf54c4fSMartin Matuska 	new_str = malloc(old_len + len + 1);
167caf54c4fSMartin Matuska 	if (new_str == NULL)
168caf54c4fSMartin Matuska 		lafe_errc(1, errno, "Out of memory");
1696c95142eSMartin Matuska 	if (*str != NULL)
170caf54c4fSMartin Matuska 		memcpy(new_str, *str, old_len);
171caf54c4fSMartin Matuska 	memcpy(new_str + old_len, append, len);
172caf54c4fSMartin Matuska 	new_str[old_len + len] = '\0';
173caf54c4fSMartin Matuska 	free(*str);
174caf54c4fSMartin Matuska 	*str = new_str;
175caf54c4fSMartin Matuska }
176caf54c4fSMartin Matuska 
177caf54c4fSMartin Matuska static void
178caf54c4fSMartin Matuska realloc_strcat(char **str, const char *append)
179caf54c4fSMartin Matuska {
180caf54c4fSMartin Matuska 	char *new_str;
181caf54c4fSMartin Matuska 	size_t old_len;
182caf54c4fSMartin Matuska 
183caf54c4fSMartin Matuska 	if (*str == NULL)
184caf54c4fSMartin Matuska 		old_len = 0;
185caf54c4fSMartin Matuska 	else
186caf54c4fSMartin Matuska 		old_len = strlen(*str);
187caf54c4fSMartin Matuska 
188caf54c4fSMartin Matuska 	new_str = malloc(old_len + strlen(append) + 1);
189caf54c4fSMartin Matuska 	if (new_str == NULL)
190caf54c4fSMartin Matuska 		lafe_errc(1, errno, "Out of memory");
1916c95142eSMartin Matuska 	if (*str != NULL)
192caf54c4fSMartin Matuska 		memcpy(new_str, *str, old_len);
193caf54c4fSMartin Matuska 	strcpy(new_str + old_len, append);
194caf54c4fSMartin Matuska 	free(*str);
195caf54c4fSMartin Matuska 	*str = new_str;
196caf54c4fSMartin Matuska }
197caf54c4fSMartin Matuska 
198caf54c4fSMartin Matuska int
1996c95142eSMartin Matuska apply_substitution(struct bsdtar *bsdtar, const char *name, char **result,
2006c95142eSMartin Matuska     int symlink_target, int hardlink_target)
201caf54c4fSMartin Matuska {
202caf54c4fSMartin Matuska 	const char *path = name;
203caf54c4fSMartin Matuska 	regmatch_t matches[10];
204b9128a37SMartin Matuska 	char* buffer = NULL;
205caf54c4fSMartin Matuska 	size_t i, j;
206caf54c4fSMartin Matuska 	struct subst_rule *rule;
207caf54c4fSMartin Matuska 	struct substitution *subst;
208caf54c4fSMartin Matuska 	int c, got_match, print_match;
209caf54c4fSMartin Matuska 
210caf54c4fSMartin Matuska 	*result = NULL;
211caf54c4fSMartin Matuska 
212caf54c4fSMartin Matuska 	if ((subst = bsdtar->substitution) == NULL)
213caf54c4fSMartin Matuska 		return 0;
214caf54c4fSMartin Matuska 
215caf54c4fSMartin Matuska 	got_match = 0;
216caf54c4fSMartin Matuska 	print_match = 0;
217caf54c4fSMartin Matuska 
218caf54c4fSMartin Matuska 	for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
2196c95142eSMartin Matuska 		if (symlink_target) {
2206c95142eSMartin Matuska 			if (!rule->symlink)
221caf54c4fSMartin Matuska 				continue;
2226c95142eSMartin Matuska 		} else if (hardlink_target) {
2236c95142eSMartin Matuska 			if (!rule->hardlink)
2246c95142eSMartin Matuska 				continue;
2256c95142eSMartin Matuska 		} else { /* Regular filename. */
2266c95142eSMartin Matuska 			if (!rule->regular)
2276c95142eSMartin Matuska 				continue;
2286c95142eSMartin Matuska 		}
2296c95142eSMartin Matuska 
230b9128a37SMartin Matuska 		if (rule->from_begin && *result) {
231b9128a37SMartin Matuska 			realloc_strcat(result, name);
232b9128a37SMartin Matuska 			realloc_strcat(&buffer, *result);
233b9128a37SMartin Matuska 			name = buffer;
234b9128a37SMartin Matuska 			(*result)[0] = 0;
235b9128a37SMartin Matuska 		}
236b9128a37SMartin Matuska 
237cdf63a70SMartin Matuska 		while (1) {
238caf54c4fSMartin Matuska 			if (regexec(&rule->re, name, 10, matches, 0))
239cdf63a70SMartin Matuska 				break;
240caf54c4fSMartin Matuska 
241caf54c4fSMartin Matuska 			got_match = 1;
242caf54c4fSMartin Matuska 			print_match |= rule->print;
243caf54c4fSMartin Matuska 			realloc_strncat(result, name, matches[0].rm_so);
244caf54c4fSMartin Matuska 
245caf54c4fSMartin Matuska 			for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
246caf54c4fSMartin Matuska 				if (rule->result[i] == '~') {
247caf54c4fSMartin Matuska 					realloc_strncat(result, rule->result + j, i - j);
2486c95142eSMartin Matuska 					realloc_strncat(result,
2496c95142eSMartin Matuska 					    name + matches[0].rm_so,
2506c95142eSMartin Matuska 					    matches[0].rm_eo - matches[0].rm_so);
251caf54c4fSMartin Matuska 					j = i + 1;
252caf54c4fSMartin Matuska 					continue;
253caf54c4fSMartin Matuska 				}
254caf54c4fSMartin Matuska 				if (rule->result[i] != '\\')
255caf54c4fSMartin Matuska 					continue;
256caf54c4fSMartin Matuska 
257caf54c4fSMartin Matuska 				++i;
258caf54c4fSMartin Matuska 				c = rule->result[i];
259caf54c4fSMartin Matuska 				switch (c) {
260caf54c4fSMartin Matuska 				case '~':
261caf54c4fSMartin Matuska 				case '\\':
262caf54c4fSMartin Matuska 					realloc_strncat(result, rule->result + j, i - j - 1);
263caf54c4fSMartin Matuska 					j = i;
264caf54c4fSMartin Matuska 					break;
265caf54c4fSMartin Matuska 				case '1':
266caf54c4fSMartin Matuska 				case '2':
267caf54c4fSMartin Matuska 				case '3':
268caf54c4fSMartin Matuska 				case '4':
269caf54c4fSMartin Matuska 				case '5':
270caf54c4fSMartin Matuska 				case '6':
271caf54c4fSMartin Matuska 				case '7':
272caf54c4fSMartin Matuska 				case '8':
273caf54c4fSMartin Matuska 				case '9':
274caf54c4fSMartin Matuska 					realloc_strncat(result, rule->result + j, i - j - 1);
275caf54c4fSMartin Matuska 					if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
276b9128a37SMartin Matuska 						free(buffer);
277caf54c4fSMartin Matuska 						free(*result);
278caf54c4fSMartin Matuska 						*result = NULL;
279caf54c4fSMartin Matuska 						return -1;
280caf54c4fSMartin Matuska 					}
281caf54c4fSMartin Matuska 					realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
282caf54c4fSMartin Matuska 					j = i + 1;
283caf54c4fSMartin Matuska 					break;
284caf54c4fSMartin Matuska 				default:
285caf54c4fSMartin Matuska 					/* Just continue; */
286caf54c4fSMartin Matuska 					break;
287caf54c4fSMartin Matuska 				}
288caf54c4fSMartin Matuska 
289caf54c4fSMartin Matuska 			}
290caf54c4fSMartin Matuska 
291caf54c4fSMartin Matuska 			realloc_strcat(result, rule->result + j);
292caf54c4fSMartin Matuska 
293caf54c4fSMartin Matuska 			name += matches[0].rm_eo;
294caf54c4fSMartin Matuska 
295caf54c4fSMartin Matuska 			if (!rule->global)
296caf54c4fSMartin Matuska 				break;
297caf54c4fSMartin Matuska 		}
298cdf63a70SMartin Matuska 	}
299caf54c4fSMartin Matuska 
300caf54c4fSMartin Matuska 	if (got_match)
301caf54c4fSMartin Matuska 		realloc_strcat(result, name);
302caf54c4fSMartin Matuska 
303b9128a37SMartin Matuska 	free(buffer);
304b9128a37SMartin Matuska 
305caf54c4fSMartin Matuska 	if (print_match)
306caf54c4fSMartin Matuska 		fprintf(stderr, "%s >> %s\n", path, *result);
307caf54c4fSMartin Matuska 
308caf54c4fSMartin Matuska 	return got_match;
309caf54c4fSMartin Matuska }
310caf54c4fSMartin Matuska 
311caf54c4fSMartin Matuska void
312caf54c4fSMartin Matuska cleanup_substitution(struct bsdtar *bsdtar)
313caf54c4fSMartin Matuska {
314caf54c4fSMartin Matuska 	struct subst_rule *rule;
315caf54c4fSMartin Matuska 	struct substitution *subst;
316caf54c4fSMartin Matuska 
317caf54c4fSMartin Matuska 	if ((subst = bsdtar->substitution) == NULL)
318caf54c4fSMartin Matuska 		return;
319caf54c4fSMartin Matuska 
320caf54c4fSMartin Matuska 	while ((rule = subst->first_rule) != NULL) {
321caf54c4fSMartin Matuska 		subst->first_rule = rule->next;
322caf54c4fSMartin Matuska 		free(rule->result);
323bd5e624aSMartin Matuska 		regfree(&rule->re);
324caf54c4fSMartin Matuska 		free(rule);
325caf54c4fSMartin Matuska 	}
326caf54c4fSMartin Matuska 	free(subst);
327caf54c4fSMartin Matuska }
328b9128a37SMartin Matuska #endif /* defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H) */
329