subst.c revision 299529
1126596Sbms/*-
2126596Sbms * Copyright (c) 2008 Joerg Sonnenberger
3126596Sbms * All rights reserved.
4126596Sbms *
5126596Sbms * Redistribution and use in source and binary forms, with or without
6126596Sbms * modification, are permitted provided that the following conditions
7126596Sbms * are met:
8126596Sbms * 1. Redistributions of source code must retain the above copyright
9126596Sbms *    notice, this list of conditions and the following disclaimer.
10126596Sbms * 2. Redistributions in binary form must reproduce the above copyright
11126596Sbms *    notice, this list of conditions and the following disclaimer in the
12126596Sbms *    documentation and/or other materials provided with the distribution.
13126596Sbms *
14126596Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15126596Sbms * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16126596Sbms * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17126596Sbms * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18126596Sbms * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19126596Sbms * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20126596Sbms * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21126596Sbms * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22126596Sbms * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23126596Sbms * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24126596Sbms */
25126596Sbms
26126596Sbms#include "bsdtar_platform.h"
27126596Sbms__FBSDID("$FreeBSD: head/contrib/libarchive/tar/subst.c 299529 2016-05-12 10:16:16Z mm $");
28126596Sbms
29207325Srpaulo#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
30131738Sru#include "bsdtar.h"
31126596Sbms
32126596Sbms#include <errno.h>
33196155Ssam#ifdef HAVE_PCREPOSIX_H
34126596Sbms#include <pcreposix.h>
35126596Sbms#else
36126596Sbms#include <regex.h>
37196155Ssam#endif
38196155Ssam#include <stdlib.h>
39196155Ssam#include <string.h>
40196155Ssam
41196155Ssam#ifndef REG_BASIC
42131738Sru#define	REG_BASIC 0
43196155Ssam#endif
44126596Sbms
45196155Ssam#include "err.h"
46196155Ssam
47196155Ssamstruct subst_rule {
48196155Ssam	struct subst_rule *next;
49196155Ssam	regex_t re;
50196155Ssam	char *result;
51126596Sbms	unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1;
52196155Ssam};
53196155Ssam
54126596Sbmsstruct substitution {
55196155Ssam	struct subst_rule *first_rule, *last_rule;
56196155Ssam};
57126596Sbms
58196155Ssamstatic void
59196155Ssaminit_substitution(struct bsdtar *bsdtar)
60126596Sbms{
61131738Sru	struct substitution *subst;
62196155Ssam
63196155Ssam	bsdtar->substitution = subst = malloc(sizeof(*subst));
64196155Ssam	if (subst == NULL)
65131738Sru		lafe_errc(1, errno, "Out of memory");
66131583Sbms	subst->first_rule = subst->last_rule = NULL;
67196155Ssam}
68196155Ssam
69196155Ssamvoid
70233648Seadleradd_substitution(struct bsdtar *bsdtar, const char *rule_text)
71131583Sbms{
72196155Ssam	struct subst_rule *rule;
73196155Ssam	struct substitution *subst;
74196155Ssam	const char *end_pattern, *start_subst;
75233648Seadler	char *pattern;
76196155Ssam	int r;
77131583Sbms
78196155Ssam	if ((subst = bsdtar->substitution) == NULL) {
79196155Ssam		init_substitution(bsdtar);
80196155Ssam		subst = bsdtar->substitution;
81233648Seadler	}
82196155Ssam
83196155Ssam	rule = malloc(sizeof(*rule));
84196155Ssam	if (rule == NULL)
85196155Ssam		lafe_errc(1, errno, "Out of memory");
86196155Ssam	rule->next = NULL;
87196155Ssam
88196155Ssam	if (subst->last_rule == NULL)
89196155Ssam		subst->first_rule = rule;
90196155Ssam	else
91196155Ssam		subst->last_rule->next = rule;
92196155Ssam	subst->last_rule = rule;
93196155Ssam
94196155Ssam	if (*rule_text == '\0')
95196155Ssam		lafe_errc(1, 0, "Empty replacement string");
96196155Ssam	end_pattern = strchr(rule_text + 1, *rule_text);
97196155Ssam	if (end_pattern == NULL)
98196155Ssam		lafe_errc(1, 0, "Invalid replacement string");
99131583Sbms
100196155Ssam	pattern = malloc(end_pattern - rule_text);
101196155Ssam	if (pattern == NULL)
102196155Ssam		lafe_errc(1, errno, "Out of memory");
103196155Ssam	memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
104211936Sbrucec	pattern[end_pattern - rule_text - 1] = '\0';
105196155Ssam
106196155Ssam	if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
107196155Ssam		char buf[80];
108196155Ssam		regerror(r, &rule->re, buf, sizeof(buf));
109131583Sbms		lafe_errc(1, 0, "Invalid regular expression: %s", buf);
110196155Ssam	}
111196155Ssam	free(pattern);
112196155Ssam
113196155Ssam	start_subst = end_pattern + 1;
114196155Ssam	end_pattern = strchr(start_subst, *rule_text);
115196155Ssam	if (end_pattern == NULL)
116196155Ssam		lafe_errc(1, 0, "Invalid replacement string");
117196155Ssam
118196155Ssam	rule->result = malloc(end_pattern - start_subst + 1);
119131583Sbms	if (rule->result == NULL)
120196155Ssam		lafe_errc(1, errno, "Out of memory");
121196155Ssam	memcpy(rule->result, start_subst, end_pattern - start_subst);
122196155Ssam	rule->result[end_pattern - start_subst] = '\0';
123196155Ssam
124196155Ssam	/* Defaults */
125196155Ssam	rule->global = 0; /* Don't do multiple replacements. */
126196155Ssam	rule->print = 0; /* Don't print. */
127196155Ssam	rule->regular = 1; /* Rewrite regular filenames. */
128233648Seadler	rule->symlink = 1; /* Rewrite symlink targets. */
129196155Ssam	rule->hardlink = 1; /* Rewrite hardlink targets. */
130196155Ssam
131196155Ssam	while (*++end_pattern) {
132196155Ssam		switch (*end_pattern) {
133196155Ssam		case 'g':
134196155Ssam		case 'G':
135196155Ssam			rule->global = 1;
136131583Sbms			break;
137196155Ssam		case 'h':
138196155Ssam			rule->hardlink = 1;
139196155Ssam			break;
140196155Ssam		case 'H':
141196155Ssam			rule->hardlink = 0;
142233648Seadler			break;
143196155Ssam		case 'p':
144196155Ssam		case 'P':
145196155Ssam			rule->print = 1;
146196155Ssam			break;
147131583Sbms		case 'r':
148196155Ssam			rule->regular = 1;
149196155Ssam			break;
150196155Ssam		case 'R':
151196155Ssam			rule->regular = 0;
152196155Ssam			break;
153196155Ssam		case 's':
154196155Ssam			rule->symlink = 1;
155207325Srpaulo			break;
156196155Ssam		case 'S':
157196155Ssam			rule->symlink = 0;
158196155Ssam			break;
159196155Ssam		default:
160196155Ssam			lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
161196155Ssam		}
162196155Ssam	}
163196155Ssam}
164196155Ssam
165196155Ssamstatic void
166196155Ssamrealloc_strncat(char **str, const char *append, size_t len)
167196155Ssam{
168196155Ssam	char *new_str;
169196155Ssam	size_t old_len;
170196155Ssam
171196155Ssam	if (*str == NULL)
172196155Ssam		old_len = 0;
173196155Ssam	else
174196155Ssam		old_len = strlen(*str);
175196155Ssam
176196155Ssam	new_str = malloc(old_len + len + 1);
177196155Ssam	if (new_str == NULL)
178196155Ssam		lafe_errc(1, errno, "Out of memory");
179131583Sbms	if (*str != NULL)
180196155Ssam		memcpy(new_str, *str, old_len);
181196155Ssam	memcpy(new_str + old_len, append, len);
182196155Ssam	new_str[old_len + len] = '\0';
183196155Ssam	free(*str);
184196155Ssam	*str = new_str;
185196155Ssam}
186196155Ssam
187196155Ssamstatic void
188238542Skevlorealloc_strcat(char **str, const char *append)
189196155Ssam{
190196155Ssam	char *new_str;
191196155Ssam	size_t old_len;
192196155Ssam
193196155Ssam	if (*str == NULL)
194196155Ssam		old_len = 0;
195196155Ssam	else
196131583Sbms		old_len = strlen(*str);
197196155Ssam
198196155Ssam	new_str = malloc(old_len + strlen(append) + 1);
199196155Ssam	if (new_str == NULL)
200196155Ssam		lafe_errc(1, errno, "Out of memory");
201196155Ssam	if (*str != NULL)
202196155Ssam		memcpy(new_str, *str, old_len);
203196155Ssam	strcpy(new_str + old_len, append);
204196155Ssam	free(*str);
205196155Ssam	*str = new_str;
206196155Ssam}
207196155Ssam
208196155Ssamint
209233648Seadlerapply_substitution(struct bsdtar *bsdtar, const char *name, char **result,
210196155Ssam    int symlink_target, int hardlink_target)
211196155Ssam{
212196155Ssam	const char *path = name;
213196155Ssam	regmatch_t matches[10];
214196155Ssam	size_t i, j;
215131583Sbms	struct subst_rule *rule;
216196155Ssam	struct substitution *subst;
217196155Ssam	int c, got_match, print_match;
218196155Ssam
219196155Ssam	*result = NULL;
220196155Ssam
221196155Ssam	if ((subst = bsdtar->substitution) == NULL)
222196155Ssam		return 0;
223196155Ssam
224196155Ssam	got_match = 0;
225196155Ssam	print_match = 0;
226196155Ssam
227196155Ssam	for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
228196155Ssam		if (symlink_target) {
229196155Ssam			if (!rule->symlink)
230196155Ssam				continue;
231196155Ssam		} else if (hardlink_target) {
232196155Ssam			if (!rule->hardlink)
233196155Ssam				continue;
234196155Ssam		} else { /* Regular filename. */
235196155Ssam			if (!rule->regular)
236196155Ssam				continue;
237196155Ssam		}
238196155Ssam
239196155Ssam		while (1) {
240196155Ssam			if (regexec(&rule->re, name, 10, matches, 0))
241196155Ssam				break;
242196155Ssam
243196155Ssam			got_match = 1;
244196155Ssam			print_match |= rule->print;
245196155Ssam			realloc_strncat(result, name, matches[0].rm_so);
246196155Ssam
247196155Ssam			for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
248126596Sbms				if (rule->result[i] == '~') {
249205846Strasz					realloc_strncat(result, rule->result + j, i - j);
250126596Sbms					realloc_strncat(result,
251197300Sbrueffer					    name + matches[0].rm_so,
252					    matches[0].rm_eo - matches[0].rm_so);
253					j = i + 1;
254					continue;
255				}
256				if (rule->result[i] != '\\')
257					continue;
258
259				++i;
260				c = rule->result[i];
261				switch (c) {
262				case '~':
263				case '\\':
264					realloc_strncat(result, rule->result + j, i - j - 1);
265					j = i;
266					break;
267				case '1':
268				case '2':
269				case '3':
270				case '4':
271				case '5':
272				case '6':
273				case '7':
274				case '8':
275				case '9':
276					realloc_strncat(result, rule->result + j, i - j - 1);
277					if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
278						free(*result);
279						*result = NULL;
280						return -1;
281					}
282					realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
283					j = i + 1;
284					break;
285				default:
286					/* Just continue; */
287					break;
288				}
289
290			}
291
292			realloc_strcat(result, rule->result + j);
293
294			name += matches[0].rm_eo;
295
296			if (!rule->global)
297				break;
298		}
299	}
300
301	if (got_match)
302		realloc_strcat(result, name);
303
304	if (print_match)
305		fprintf(stderr, "%s >> %s\n", path, *result);
306
307	return got_match;
308}
309
310void
311cleanup_substitution(struct bsdtar *bsdtar)
312{
313	struct subst_rule *rule;
314	struct substitution *subst;
315
316	if ((subst = bsdtar->substitution) == NULL)
317		return;
318
319	while ((rule = subst->first_rule) != NULL) {
320		subst->first_rule = rule->next;
321		free(rule->result);
322		free(rule);
323	}
324	free(subst);
325}
326#endif /* defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) */
327