xref: /dragonfly/contrib/libarchive/tar/subst.c (revision 650094e1)
1 /*-
2  * Copyright (c) 2008 Joerg Sonnenberger
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "bsdtar_platform.h"
27 __FBSDID("$FreeBSD: src/usr.bin/tar/subst.c,v 1.4 2008/06/15 10:08:16 kientzle Exp $");
28 
29 #if HAVE_REGEX_H
30 #include "bsdtar.h"
31 
32 #include <errno.h>
33 #include <regex.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #ifndef REG_BASIC
38 #define	REG_BASIC 0
39 #endif
40 
41 #include "err.h"
42 
43 struct subst_rule {
44 	struct subst_rule *next;
45 	regex_t re;
46 	char *result;
47 	unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1;
48 };
49 
50 struct substitution {
51 	struct subst_rule *first_rule, *last_rule;
52 };
53 
54 static void
55 init_substitution(struct bsdtar *bsdtar)
56 {
57 	struct substitution *subst;
58 
59 	bsdtar->substitution = subst = malloc(sizeof(*subst));
60 	if (subst == NULL)
61 		lafe_errc(1, errno, "Out of memory");
62 	subst->first_rule = subst->last_rule = NULL;
63 }
64 
65 void
66 add_substitution(struct bsdtar *bsdtar, const char *rule_text)
67 {
68 	struct subst_rule *rule;
69 	struct substitution *subst;
70 	const char *end_pattern, *start_subst;
71 	char *pattern;
72 	int r;
73 
74 	if ((subst = bsdtar->substitution) == NULL) {
75 		init_substitution(bsdtar);
76 		subst = bsdtar->substitution;
77 	}
78 
79 	rule = malloc(sizeof(*rule));
80 	if (rule == NULL)
81 		lafe_errc(1, errno, "Out of memory");
82 	rule->next = NULL;
83 
84 	if (subst->last_rule == NULL)
85 		subst->first_rule = rule;
86 	else
87 		subst->last_rule->next = rule;
88 	subst->last_rule = rule;
89 
90 	if (*rule_text == '\0')
91 		lafe_errc(1, 0, "Empty replacement string");
92 	end_pattern = strchr(rule_text + 1, *rule_text);
93 	if (end_pattern == NULL)
94 		lafe_errc(1, 0, "Invalid replacement string");
95 
96 	pattern = malloc(end_pattern - rule_text);
97 	if (pattern == NULL)
98 		lafe_errc(1, errno, "Out of memory");
99 	memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
100 	pattern[end_pattern - rule_text - 1] = '\0';
101 
102 	if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
103 		char buf[80];
104 		regerror(r, &rule->re, buf, sizeof(buf));
105 		lafe_errc(1, 0, "Invalid regular expression: %s", buf);
106 	}
107 	free(pattern);
108 
109 	start_subst = end_pattern + 1;
110 	end_pattern = strchr(start_subst, *rule_text);
111 	if (end_pattern == NULL)
112 		lafe_errc(1, 0, "Invalid replacement string");
113 
114 	rule->result = malloc(end_pattern - start_subst + 1);
115 	if (rule->result == NULL)
116 		lafe_errc(1, errno, "Out of memory");
117 	memcpy(rule->result, start_subst, end_pattern - start_subst);
118 	rule->result[end_pattern - start_subst] = '\0';
119 
120 	/* Defaults */
121 	rule->global = 0; /* Don't do multiple replacements. */
122 	rule->print = 0; /* Don't print. */
123 	rule->regular = 1; /* Rewrite regular filenames. */
124 	rule->symlink = 1; /* Rewrite symlink targets. */
125 	rule->hardlink = 1; /* Rewrite hardlink targets. */
126 
127 	while (*++end_pattern) {
128 		switch (*end_pattern) {
129 		case 'g':
130 		case 'G':
131 			rule->global = 1;
132 			break;
133 		case 'h':
134 			rule->hardlink = 1;
135 			break;
136 		case 'H':
137 			rule->hardlink = 0;
138 			break;
139 		case 'p':
140 		case 'P':
141 			rule->print = 1;
142 			break;
143 		case 'r':
144 			rule->regular = 1;
145 			break;
146 		case 'R':
147 			rule->regular = 0;
148 			break;
149 		case 's':
150 			rule->symlink = 1;
151 			break;
152 		case 'S':
153 			rule->symlink = 0;
154 			break;
155 		default:
156 			lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
157 		}
158 	}
159 }
160 
161 static void
162 realloc_strncat(char **str, const char *append, size_t len)
163 {
164 	char *new_str;
165 	size_t old_len;
166 
167 	if (*str == NULL)
168 		old_len = 0;
169 	else
170 		old_len = strlen(*str);
171 
172 	new_str = malloc(old_len + len + 1);
173 	if (new_str == NULL)
174 		lafe_errc(1, errno, "Out of memory");
175 	if (*str != NULL)
176 		memcpy(new_str, *str, old_len);
177 	memcpy(new_str + old_len, append, len);
178 	new_str[old_len + len] = '\0';
179 	free(*str);
180 	*str = new_str;
181 }
182 
183 static void
184 realloc_strcat(char **str, const char *append)
185 {
186 	char *new_str;
187 	size_t old_len;
188 
189 	if (*str == NULL)
190 		old_len = 0;
191 	else
192 		old_len = strlen(*str);
193 
194 	new_str = malloc(old_len + strlen(append) + 1);
195 	if (new_str == NULL)
196 		lafe_errc(1, errno, "Out of memory");
197 	if (*str != NULL)
198 		memcpy(new_str, *str, old_len);
199 	strcpy(new_str + old_len, append);
200 	free(*str);
201 	*str = new_str;
202 }
203 
204 int
205 apply_substitution(struct bsdtar *bsdtar, const char *name, char **result,
206     int symlink_target, int hardlink_target)
207 {
208 	const char *path = name;
209 	regmatch_t matches[10];
210 	size_t i, j;
211 	struct subst_rule *rule;
212 	struct substitution *subst;
213 	int c, got_match, print_match;
214 
215 	*result = NULL;
216 
217 	if ((subst = bsdtar->substitution) == NULL)
218 		return 0;
219 
220 	got_match = 0;
221 	print_match = 0;
222 
223 	for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
224 		if (symlink_target) {
225 			if (!rule->symlink)
226 				continue;
227 		} else if (hardlink_target) {
228 			if (!rule->hardlink)
229 				continue;
230 		} else { /* Regular filename. */
231 			if (!rule->regular)
232 				continue;
233 		}
234 
235 		if (regexec(&rule->re, name, 10, matches, 0))
236 			continue;
237 
238 		got_match = 1;
239 		print_match |= rule->print;
240 		realloc_strncat(result, name, matches[0].rm_so);
241 
242 		for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
243 			if (rule->result[i] == '~') {
244 				realloc_strncat(result, rule->result + j, i - j);
245 				realloc_strncat(result,
246 				    name + matches[0].rm_so,
247 				    matches[0].rm_eo - matches[0].rm_so);
248 				j = i + 1;
249 				continue;
250 			}
251 			if (rule->result[i] != '\\')
252 				continue;
253 
254 			++i;
255 			c = rule->result[i];
256 			switch (c) {
257 			case '~':
258 			case '\\':
259 				realloc_strncat(result, rule->result + j, i - j - 1);
260 				j = i;
261 				break;
262 			case '1':
263 			case '2':
264 			case '3':
265 			case '4':
266 			case '5':
267 			case '6':
268 			case '7':
269 			case '8':
270 			case '9':
271 				realloc_strncat(result, rule->result + j, i - j - 1);
272 				if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
273 					free(*result);
274 					*result = NULL;
275 					return -1;
276 				}
277 				realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
278 				j = i + 1;
279 				break;
280 			default:
281 				/* Just continue; */
282 				break;
283 			}
284 
285 		}
286 
287 		realloc_strcat(result, rule->result + j);
288 
289 		name += matches[0].rm_eo;
290 
291 		if (!rule->global)
292 			break;
293 	}
294 
295 	if (got_match)
296 		realloc_strcat(result, name);
297 
298 	if (print_match)
299 		fprintf(stderr, "%s >> %s\n", path, *result);
300 
301 	return got_match;
302 }
303 
304 void
305 cleanup_substitution(struct bsdtar *bsdtar)
306 {
307 	struct subst_rule *rule;
308 	struct substitution *subst;
309 
310 	if ((subst = bsdtar->substitution) == NULL)
311 		return;
312 
313 	while ((rule = subst->first_rule) != NULL) {
314 		subst->first_rule = rule->next;
315 		free(rule->result);
316 		free(rule);
317 	}
318 	free(subst);
319 }
320 #endif /* HAVE_REGEX_H */
321