1228753Smm/*-
2228753Smm * Copyright (c) 2008 Joerg Sonnenberger
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24228753Smm */
25228753Smm
26228753Smm#include "bsdtar_platform.h"
27229592Smm__FBSDID("$FreeBSD$");
28228753Smm
29228753Smm#if HAVE_REGEX_H
30228753Smm#include "bsdtar.h"
31228753Smm
32228753Smm#include <errno.h>
33228753Smm#include <regex.h>
34228753Smm#include <stdlib.h>
35228753Smm#include <string.h>
36228753Smm
37228753Smm#ifndef REG_BASIC
38228753Smm#define	REG_BASIC 0
39228753Smm#endif
40228753Smm
41228753Smm#include "err.h"
42228753Smm
43228753Smmstruct subst_rule {
44228753Smm	struct subst_rule *next;
45228753Smm	regex_t re;
46228753Smm	char *result;
47228753Smm	unsigned int global:1, print:1, symlink:1;
48228753Smm};
49228753Smm
50228753Smmstruct substitution {
51228753Smm	struct subst_rule *first_rule, *last_rule;
52228753Smm};
53228753Smm
54228753Smmstatic void
55228753Smminit_substitution(struct bsdtar *bsdtar)
56228753Smm{
57228753Smm	struct substitution *subst;
58228753Smm
59228753Smm	bsdtar->substitution = subst = malloc(sizeof(*subst));
60228753Smm	if (subst == NULL)
61228753Smm		lafe_errc(1, errno, "Out of memory");
62228753Smm	subst->first_rule = subst->last_rule = NULL;
63228753Smm}
64228753Smm
65228753Smmvoid
66228753Smmadd_substitution(struct bsdtar *bsdtar, const char *rule_text)
67228753Smm{
68228753Smm	struct subst_rule *rule;
69228753Smm	struct substitution *subst;
70228753Smm	const char *end_pattern, *start_subst;
71228753Smm	char *pattern;
72228753Smm	int r;
73228753Smm
74228753Smm	if ((subst = bsdtar->substitution) == NULL) {
75228753Smm		init_substitution(bsdtar);
76228753Smm		subst = bsdtar->substitution;
77228753Smm	}
78228753Smm
79228753Smm	rule = malloc(sizeof(*rule));
80228753Smm	if (rule == NULL)
81228753Smm		lafe_errc(1, errno, "Out of memory");
82228753Smm	rule->next = NULL;
83228753Smm
84228753Smm	if (subst->last_rule == NULL)
85228753Smm		subst->first_rule = rule;
86228753Smm	else
87228753Smm		subst->last_rule->next = rule;
88228753Smm	subst->last_rule = rule;
89228753Smm
90228753Smm	if (*rule_text == '\0')
91228753Smm		lafe_errc(1, 0, "Empty replacement string");
92228753Smm	end_pattern = strchr(rule_text + 1, *rule_text);
93228753Smm	if (end_pattern == NULL)
94228753Smm		lafe_errc(1, 0, "Invalid replacement string");
95228753Smm
96228753Smm	pattern = malloc(end_pattern - rule_text);
97228753Smm	if (pattern == NULL)
98228753Smm		lafe_errc(1, errno, "Out of memory");
99228753Smm	memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
100228753Smm	pattern[end_pattern - rule_text - 1] = '\0';
101228753Smm
102228753Smm	if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
103228753Smm		char buf[80];
104228753Smm		regerror(r, &rule->re, buf, sizeof(buf));
105228753Smm		lafe_errc(1, 0, "Invalid regular expression: %s", buf);
106228753Smm	}
107228753Smm	free(pattern);
108228753Smm
109228753Smm	start_subst = end_pattern + 1;
110228753Smm	end_pattern = strchr(start_subst, *rule_text);
111228753Smm	if (end_pattern == NULL)
112228753Smm		lafe_errc(1, 0, "Invalid replacement string");
113228753Smm
114228753Smm	rule->result = malloc(end_pattern - start_subst + 1);
115228753Smm	if (rule->result == NULL)
116228753Smm		lafe_errc(1, errno, "Out of memory");
117228753Smm	memcpy(rule->result, start_subst, end_pattern - start_subst);
118228753Smm	rule->result[end_pattern - start_subst] = '\0';
119228753Smm
120228753Smm	rule->global = 0;
121228753Smm	rule->print = 0;
122228753Smm	rule->symlink = 0;
123228753Smm
124228753Smm	while (*++end_pattern) {
125228753Smm		switch (*end_pattern) {
126228753Smm		case 'g':
127228753Smm		case 'G':
128228753Smm			rule->global = 1;
129228753Smm			break;
130228753Smm		case 'p':
131228753Smm		case 'P':
132228753Smm			rule->print = 1;
133228753Smm			break;
134228753Smm		case 's':
135228753Smm		case 'S':
136228753Smm			rule->symlink = 1;
137228753Smm			break;
138228753Smm		default:
139228753Smm			lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
140228753Smm		}
141228753Smm	}
142228753Smm}
143228753Smm
144228753Smmstatic void
145228753Smmrealloc_strncat(char **str, const char *append, size_t len)
146228753Smm{
147228753Smm	char *new_str;
148228753Smm	size_t old_len;
149228753Smm
150228753Smm	if (*str == NULL)
151228753Smm		old_len = 0;
152228753Smm	else
153228753Smm		old_len = strlen(*str);
154228753Smm
155228753Smm	new_str = malloc(old_len + len + 1);
156228753Smm	if (new_str == NULL)
157228753Smm		lafe_errc(1, errno, "Out of memory");
158228753Smm	memcpy(new_str, *str, old_len);
159228753Smm	memcpy(new_str + old_len, append, len);
160228753Smm	new_str[old_len + len] = '\0';
161228753Smm	free(*str);
162228753Smm	*str = new_str;
163228753Smm}
164228753Smm
165228753Smmstatic void
166228753Smmrealloc_strcat(char **str, const char *append)
167228753Smm{
168228753Smm	char *new_str;
169228753Smm	size_t old_len;
170228753Smm
171228753Smm	if (*str == NULL)
172228753Smm		old_len = 0;
173228753Smm	else
174228753Smm		old_len = strlen(*str);
175228753Smm
176228753Smm	new_str = malloc(old_len + strlen(append) + 1);
177228753Smm	if (new_str == NULL)
178228753Smm		lafe_errc(1, errno, "Out of memory");
179228753Smm	memcpy(new_str, *str, old_len);
180228753Smm	strcpy(new_str + old_len, append);
181228753Smm	free(*str);
182228753Smm	*str = new_str;
183228753Smm}
184228753Smm
185228753Smmint
186228753Smmapply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int symlink_only)
187228753Smm{
188228753Smm	const char *path = name;
189228753Smm	regmatch_t matches[10];
190228753Smm	size_t i, j;
191228753Smm	struct subst_rule *rule;
192228753Smm	struct substitution *subst;
193228753Smm	int c, got_match, print_match;
194228753Smm
195228753Smm	*result = NULL;
196228753Smm
197228753Smm	if ((subst = bsdtar->substitution) == NULL)
198228753Smm		return 0;
199228753Smm
200228753Smm	got_match = 0;
201228753Smm	print_match = 0;
202228753Smm
203228753Smm	for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
204228753Smm		if (symlink_only && !rule->symlink)
205228753Smm			continue;
206228753Smm		if (regexec(&rule->re, name, 10, matches, 0))
207228753Smm			continue;
208228753Smm
209228753Smm		got_match = 1;
210228753Smm		print_match |= rule->print;
211228753Smm		realloc_strncat(result, name, matches[0].rm_so);
212228753Smm
213228753Smm		for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
214228753Smm			if (rule->result[i] == '~') {
215228753Smm				realloc_strncat(result, rule->result + j, i - j);
216228753Smm				realloc_strncat(result, name, matches[0].rm_eo);
217228753Smm				j = i + 1;
218228753Smm				continue;
219228753Smm			}
220228753Smm			if (rule->result[i] != '\\')
221228753Smm				continue;
222228753Smm
223228753Smm			++i;
224228753Smm			c = rule->result[i];
225228753Smm			switch (c) {
226228753Smm			case '~':
227228753Smm			case '\\':
228228753Smm				realloc_strncat(result, rule->result + j, i - j - 1);
229228753Smm				j = i;
230228753Smm				break;
231228753Smm			case '1':
232228753Smm			case '2':
233228753Smm			case '3':
234228753Smm			case '4':
235228753Smm			case '5':
236228753Smm			case '6':
237228753Smm			case '7':
238228753Smm			case '8':
239228753Smm			case '9':
240228753Smm				realloc_strncat(result, rule->result + j, i - j - 1);
241228753Smm				if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
242228753Smm					free(*result);
243228753Smm					*result = NULL;
244228753Smm					return -1;
245228753Smm				}
246228753Smm				realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
247228753Smm				j = i + 1;
248228753Smm				break;
249228753Smm			default:
250228753Smm				/* Just continue; */
251228753Smm				break;
252228753Smm			}
253228753Smm
254228753Smm		}
255228753Smm
256228753Smm		realloc_strcat(result, rule->result + j);
257228753Smm
258228753Smm		name += matches[0].rm_eo;
259228753Smm
260228753Smm		if (!rule->global)
261228753Smm			break;
262228753Smm	}
263228753Smm
264228753Smm	if (got_match)
265228753Smm		realloc_strcat(result, name);
266228753Smm
267228753Smm	if (print_match)
268228753Smm		fprintf(stderr, "%s >> %s\n", path, *result);
269228753Smm
270228753Smm	return got_match;
271228753Smm}
272228753Smm
273228753Smmvoid
274228753Smmcleanup_substitution(struct bsdtar *bsdtar)
275228753Smm{
276228753Smm	struct subst_rule *rule;
277228753Smm	struct substitution *subst;
278228753Smm
279228753Smm	if ((subst = bsdtar->substitution) == NULL)
280228753Smm		return;
281228753Smm
282228753Smm	while ((rule = subst->first_rule) != NULL) {
283228753Smm		subst->first_rule = rule->next;
284228753Smm		free(rule->result);
285228753Smm		free(rule);
286228753Smm	}
287228753Smm	free(subst);
288228753Smm}
289228753Smm#endif /* HAVE_REGEX_H */
290