xref: /dflybsd-src/contrib/libarchive/tar/subst.c (revision e95abc476b80ab0f6041c0123580ca7eee767083)
160b4ad09SPeter Avalos /*-
260b4ad09SPeter Avalos  * Copyright (c) 2008 Joerg Sonnenberger
360b4ad09SPeter Avalos  * All rights reserved.
460b4ad09SPeter Avalos  *
560b4ad09SPeter Avalos  * Redistribution and use in source and binary forms, with or without
660b4ad09SPeter Avalos  * modification, are permitted provided that the following conditions
760b4ad09SPeter Avalos  * are met:
860b4ad09SPeter Avalos  * 1. Redistributions of source code must retain the above copyright
960b4ad09SPeter Avalos  *    notice, this list of conditions and the following disclaimer.
1060b4ad09SPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
1160b4ad09SPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
1260b4ad09SPeter Avalos  *    documentation and/or other materials provided with the distribution.
1360b4ad09SPeter Avalos  *
1460b4ad09SPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
1560b4ad09SPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1660b4ad09SPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1760b4ad09SPeter Avalos  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
1860b4ad09SPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1960b4ad09SPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2060b4ad09SPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2160b4ad09SPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2260b4ad09SPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2360b4ad09SPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2460b4ad09SPeter Avalos  */
2560b4ad09SPeter Avalos 
2660b4ad09SPeter Avalos #include "bsdtar_platform.h"
2760b4ad09SPeter Avalos __FBSDID("$FreeBSD: src/usr.bin/tar/subst.c,v 1.4 2008/06/15 10:08:16 kientzle Exp $");
2860b4ad09SPeter Avalos 
29d4d8193eSPeter Avalos #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
3060b4ad09SPeter Avalos #include "bsdtar.h"
3160b4ad09SPeter Avalos 
3260b4ad09SPeter Avalos #include <errno.h>
33d4d8193eSPeter Avalos #ifdef HAVE_PCREPOSIX_H
34d4d8193eSPeter Avalos #include <pcreposix.h>
35d4d8193eSPeter Avalos #else
3660b4ad09SPeter Avalos #include <regex.h>
37d4d8193eSPeter Avalos #endif
3860b4ad09SPeter Avalos #include <stdlib.h>
3960b4ad09SPeter Avalos #include <string.h>
4060b4ad09SPeter Avalos 
4160b4ad09SPeter Avalos #ifndef REG_BASIC
4260b4ad09SPeter Avalos #define	REG_BASIC 0
4360b4ad09SPeter Avalos #endif
4460b4ad09SPeter Avalos 
459c82a63eSPeter Avalos #include "err.h"
469c82a63eSPeter Avalos 
4760b4ad09SPeter Avalos struct subst_rule {
4860b4ad09SPeter Avalos 	struct subst_rule *next;
4960b4ad09SPeter Avalos 	regex_t re;
5060b4ad09SPeter Avalos 	char *result;
51c09f92d2SPeter Avalos 	unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1;
5260b4ad09SPeter Avalos };
5360b4ad09SPeter Avalos 
5460b4ad09SPeter Avalos struct substitution {
5560b4ad09SPeter Avalos 	struct subst_rule *first_rule, *last_rule;
5660b4ad09SPeter Avalos };
5760b4ad09SPeter Avalos 
5860b4ad09SPeter Avalos static void
init_substitution(struct bsdtar * bsdtar)5960b4ad09SPeter Avalos init_substitution(struct bsdtar *bsdtar)
6060b4ad09SPeter Avalos {
6160b4ad09SPeter Avalos 	struct substitution *subst;
6260b4ad09SPeter Avalos 
6360b4ad09SPeter Avalos 	bsdtar->substitution = subst = malloc(sizeof(*subst));
6460b4ad09SPeter Avalos 	if (subst == NULL)
659c82a63eSPeter Avalos 		lafe_errc(1, errno, "Out of memory");
6660b4ad09SPeter Avalos 	subst->first_rule = subst->last_rule = NULL;
6760b4ad09SPeter Avalos }
6860b4ad09SPeter Avalos 
6960b4ad09SPeter Avalos void
add_substitution(struct bsdtar * bsdtar,const char * rule_text)7060b4ad09SPeter Avalos add_substitution(struct bsdtar *bsdtar, const char *rule_text)
7160b4ad09SPeter Avalos {
7260b4ad09SPeter Avalos 	struct subst_rule *rule;
7360b4ad09SPeter Avalos 	struct substitution *subst;
7460b4ad09SPeter Avalos 	const char *end_pattern, *start_subst;
7560b4ad09SPeter Avalos 	char *pattern;
7660b4ad09SPeter Avalos 	int r;
7760b4ad09SPeter Avalos 
7860b4ad09SPeter Avalos 	if ((subst = bsdtar->substitution) == NULL) {
7960b4ad09SPeter Avalos 		init_substitution(bsdtar);
8060b4ad09SPeter Avalos 		subst = bsdtar->substitution;
8160b4ad09SPeter Avalos 	}
8260b4ad09SPeter Avalos 
8360b4ad09SPeter Avalos 	rule = malloc(sizeof(*rule));
8460b4ad09SPeter Avalos 	if (rule == NULL)
859c82a63eSPeter Avalos 		lafe_errc(1, errno, "Out of memory");
8660b4ad09SPeter Avalos 	rule->next = NULL;
87*e95abc47Szrj 	rule->result = NULL;
8860b4ad09SPeter Avalos 
8960b4ad09SPeter Avalos 	if (subst->last_rule == NULL)
9060b4ad09SPeter Avalos 		subst->first_rule = rule;
9160b4ad09SPeter Avalos 	else
9260b4ad09SPeter Avalos 		subst->last_rule->next = rule;
9360b4ad09SPeter Avalos 	subst->last_rule = rule;
9460b4ad09SPeter Avalos 
9560b4ad09SPeter Avalos 	if (*rule_text == '\0')
969c82a63eSPeter Avalos 		lafe_errc(1, 0, "Empty replacement string");
9760b4ad09SPeter Avalos 	end_pattern = strchr(rule_text + 1, *rule_text);
9860b4ad09SPeter Avalos 	if (end_pattern == NULL)
999c82a63eSPeter Avalos 		lafe_errc(1, 0, "Invalid replacement string");
10060b4ad09SPeter Avalos 
10160b4ad09SPeter Avalos 	pattern = malloc(end_pattern - rule_text);
10260b4ad09SPeter Avalos 	if (pattern == NULL)
1039c82a63eSPeter Avalos 		lafe_errc(1, errno, "Out of memory");
10460b4ad09SPeter Avalos 	memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
10560b4ad09SPeter Avalos 	pattern[end_pattern - rule_text - 1] = '\0';
10660b4ad09SPeter Avalos 
10760b4ad09SPeter Avalos 	if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
10860b4ad09SPeter Avalos 		char buf[80];
10960b4ad09SPeter Avalos 		regerror(r, &rule->re, buf, sizeof(buf));
1109c82a63eSPeter Avalos 		lafe_errc(1, 0, "Invalid regular expression: %s", buf);
11160b4ad09SPeter Avalos 	}
11260b4ad09SPeter Avalos 	free(pattern);
11360b4ad09SPeter Avalos 
11460b4ad09SPeter Avalos 	start_subst = end_pattern + 1;
11560b4ad09SPeter Avalos 	end_pattern = strchr(start_subst, *rule_text);
11660b4ad09SPeter Avalos 	if (end_pattern == NULL)
1179c82a63eSPeter Avalos 		lafe_errc(1, 0, "Invalid replacement string");
11860b4ad09SPeter Avalos 
11960b4ad09SPeter Avalos 	rule->result = malloc(end_pattern - start_subst + 1);
12060b4ad09SPeter Avalos 	if (rule->result == NULL)
1219c82a63eSPeter Avalos 		lafe_errc(1, errno, "Out of memory");
12260b4ad09SPeter Avalos 	memcpy(rule->result, start_subst, end_pattern - start_subst);
12360b4ad09SPeter Avalos 	rule->result[end_pattern - start_subst] = '\0';
12460b4ad09SPeter Avalos 
125c09f92d2SPeter Avalos 	/* Defaults */
126c09f92d2SPeter Avalos 	rule->global = 0; /* Don't do multiple replacements. */
127c09f92d2SPeter Avalos 	rule->print = 0; /* Don't print. */
128c09f92d2SPeter Avalos 	rule->regular = 1; /* Rewrite regular filenames. */
129c09f92d2SPeter Avalos 	rule->symlink = 1; /* Rewrite symlink targets. */
130c09f92d2SPeter Avalos 	rule->hardlink = 1; /* Rewrite hardlink targets. */
13160b4ad09SPeter Avalos 
13260b4ad09SPeter Avalos 	while (*++end_pattern) {
13360b4ad09SPeter Avalos 		switch (*end_pattern) {
13460b4ad09SPeter Avalos 		case 'g':
13560b4ad09SPeter Avalos 		case 'G':
13660b4ad09SPeter Avalos 			rule->global = 1;
13760b4ad09SPeter Avalos 			break;
138c09f92d2SPeter Avalos 		case 'h':
139c09f92d2SPeter Avalos 			rule->hardlink = 1;
140c09f92d2SPeter Avalos 			break;
141c09f92d2SPeter Avalos 		case 'H':
142c09f92d2SPeter Avalos 			rule->hardlink = 0;
143c09f92d2SPeter Avalos 			break;
14460b4ad09SPeter Avalos 		case 'p':
14560b4ad09SPeter Avalos 		case 'P':
14660b4ad09SPeter Avalos 			rule->print = 1;
14760b4ad09SPeter Avalos 			break;
148c09f92d2SPeter Avalos 		case 'r':
149c09f92d2SPeter Avalos 			rule->regular = 1;
150c09f92d2SPeter Avalos 			break;
151c09f92d2SPeter Avalos 		case 'R':
152c09f92d2SPeter Avalos 			rule->regular = 0;
153c09f92d2SPeter Avalos 			break;
15460b4ad09SPeter Avalos 		case 's':
15560b4ad09SPeter Avalos 			rule->symlink = 1;
15660b4ad09SPeter Avalos 			break;
157c09f92d2SPeter Avalos 		case 'S':
158c09f92d2SPeter Avalos 			rule->symlink = 0;
159c09f92d2SPeter Avalos 			break;
16060b4ad09SPeter Avalos 		default:
1619c82a63eSPeter Avalos 			lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
16260b4ad09SPeter Avalos 		}
16360b4ad09SPeter Avalos 	}
16460b4ad09SPeter Avalos }
16560b4ad09SPeter Avalos 
16660b4ad09SPeter Avalos static void
realloc_strncat(char ** str,const char * append,size_t len)1679c82a63eSPeter Avalos realloc_strncat(char **str, const char *append, size_t len)
16860b4ad09SPeter Avalos {
16960b4ad09SPeter Avalos 	char *new_str;
17060b4ad09SPeter Avalos 	size_t old_len;
17160b4ad09SPeter Avalos 
17260b4ad09SPeter Avalos 	if (*str == NULL)
17360b4ad09SPeter Avalos 		old_len = 0;
17460b4ad09SPeter Avalos 	else
17560b4ad09SPeter Avalos 		old_len = strlen(*str);
17660b4ad09SPeter Avalos 
17760b4ad09SPeter Avalos 	new_str = malloc(old_len + len + 1);
17860b4ad09SPeter Avalos 	if (new_str == NULL)
1799c82a63eSPeter Avalos 		lafe_errc(1, errno, "Out of memory");
180c09f92d2SPeter Avalos 	if (*str != NULL)
18160b4ad09SPeter Avalos 		memcpy(new_str, *str, old_len);
18260b4ad09SPeter Avalos 	memcpy(new_str + old_len, append, len);
18360b4ad09SPeter Avalos 	new_str[old_len + len] = '\0';
18460b4ad09SPeter Avalos 	free(*str);
18560b4ad09SPeter Avalos 	*str = new_str;
18660b4ad09SPeter Avalos }
18760b4ad09SPeter Avalos 
18860b4ad09SPeter Avalos static void
realloc_strcat(char ** str,const char * append)1899c82a63eSPeter Avalos realloc_strcat(char **str, const char *append)
19060b4ad09SPeter Avalos {
19160b4ad09SPeter Avalos 	char *new_str;
19260b4ad09SPeter Avalos 	size_t old_len;
19360b4ad09SPeter Avalos 
19460b4ad09SPeter Avalos 	if (*str == NULL)
19560b4ad09SPeter Avalos 		old_len = 0;
19660b4ad09SPeter Avalos 	else
19760b4ad09SPeter Avalos 		old_len = strlen(*str);
19860b4ad09SPeter Avalos 
19960b4ad09SPeter Avalos 	new_str = malloc(old_len + strlen(append) + 1);
20060b4ad09SPeter Avalos 	if (new_str == NULL)
2019c82a63eSPeter Avalos 		lafe_errc(1, errno, "Out of memory");
202c09f92d2SPeter Avalos 	if (*str != NULL)
20360b4ad09SPeter Avalos 		memcpy(new_str, *str, old_len);
20460b4ad09SPeter Avalos 	strcpy(new_str + old_len, append);
20560b4ad09SPeter Avalos 	free(*str);
20660b4ad09SPeter Avalos 	*str = new_str;
20760b4ad09SPeter Avalos }
20860b4ad09SPeter Avalos 
20960b4ad09SPeter Avalos int
apply_substitution(struct bsdtar * bsdtar,const char * name,char ** result,int symlink_target,int hardlink_target)210c09f92d2SPeter Avalos apply_substitution(struct bsdtar *bsdtar, const char *name, char **result,
211c09f92d2SPeter Avalos     int symlink_target, int hardlink_target)
21260b4ad09SPeter Avalos {
21360b4ad09SPeter Avalos 	const char *path = name;
21460b4ad09SPeter Avalos 	regmatch_t matches[10];
21560b4ad09SPeter Avalos 	size_t i, j;
21660b4ad09SPeter Avalos 	struct subst_rule *rule;
21760b4ad09SPeter Avalos 	struct substitution *subst;
21860b4ad09SPeter Avalos 	int c, got_match, print_match;
21960b4ad09SPeter Avalos 
22060b4ad09SPeter Avalos 	*result = NULL;
22160b4ad09SPeter Avalos 
22260b4ad09SPeter Avalos 	if ((subst = bsdtar->substitution) == NULL)
22360b4ad09SPeter Avalos 		return 0;
22460b4ad09SPeter Avalos 
22560b4ad09SPeter Avalos 	got_match = 0;
22660b4ad09SPeter Avalos 	print_match = 0;
22760b4ad09SPeter Avalos 
22860b4ad09SPeter Avalos 	for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
229c09f92d2SPeter Avalos 		if (symlink_target) {
230c09f92d2SPeter Avalos 			if (!rule->symlink)
23160b4ad09SPeter Avalos 				continue;
232c09f92d2SPeter Avalos 		} else if (hardlink_target) {
233c09f92d2SPeter Avalos 			if (!rule->hardlink)
234c09f92d2SPeter Avalos 				continue;
235c09f92d2SPeter Avalos 		} else { /* Regular filename. */
236c09f92d2SPeter Avalos 			if (!rule->regular)
237c09f92d2SPeter Avalos 				continue;
238c09f92d2SPeter Avalos 		}
239c09f92d2SPeter Avalos 
2406b384f39SPeter Avalos 		while (1) {
24160b4ad09SPeter Avalos 			if (regexec(&rule->re, name, 10, matches, 0))
2426b384f39SPeter Avalos 				break;
24360b4ad09SPeter Avalos 
24460b4ad09SPeter Avalos 			got_match = 1;
24560b4ad09SPeter Avalos 			print_match |= rule->print;
2469c82a63eSPeter Avalos 			realloc_strncat(result, name, matches[0].rm_so);
24760b4ad09SPeter Avalos 
24860b4ad09SPeter Avalos 			for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
24960b4ad09SPeter Avalos 				if (rule->result[i] == '~') {
2509c82a63eSPeter Avalos 					realloc_strncat(result, rule->result + j, i - j);
251c09f92d2SPeter Avalos 					realloc_strncat(result,
252c09f92d2SPeter Avalos 					    name + matches[0].rm_so,
253c09f92d2SPeter Avalos 					    matches[0].rm_eo - matches[0].rm_so);
25460b4ad09SPeter Avalos 					j = i + 1;
25560b4ad09SPeter Avalos 					continue;
25660b4ad09SPeter Avalos 				}
25760b4ad09SPeter Avalos 				if (rule->result[i] != '\\')
25860b4ad09SPeter Avalos 					continue;
25960b4ad09SPeter Avalos 
26060b4ad09SPeter Avalos 				++i;
26160b4ad09SPeter Avalos 				c = rule->result[i];
26260b4ad09SPeter Avalos 				switch (c) {
26360b4ad09SPeter Avalos 				case '~':
26460b4ad09SPeter Avalos 				case '\\':
2659c82a63eSPeter Avalos 					realloc_strncat(result, rule->result + j, i - j - 1);
26660b4ad09SPeter Avalos 					j = i;
26760b4ad09SPeter Avalos 					break;
26860b4ad09SPeter Avalos 				case '1':
26960b4ad09SPeter Avalos 				case '2':
27060b4ad09SPeter Avalos 				case '3':
27160b4ad09SPeter Avalos 				case '4':
27260b4ad09SPeter Avalos 				case '5':
27360b4ad09SPeter Avalos 				case '6':
27460b4ad09SPeter Avalos 				case '7':
27560b4ad09SPeter Avalos 				case '8':
27660b4ad09SPeter Avalos 				case '9':
2779c82a63eSPeter Avalos 					realloc_strncat(result, rule->result + j, i - j - 1);
27860b4ad09SPeter Avalos 					if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
27960b4ad09SPeter Avalos 						free(*result);
28060b4ad09SPeter Avalos 						*result = NULL;
28160b4ad09SPeter Avalos 						return -1;
28260b4ad09SPeter Avalos 					}
2839c82a63eSPeter Avalos 					realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
28460b4ad09SPeter Avalos 					j = i + 1;
28560b4ad09SPeter Avalos 					break;
28660b4ad09SPeter Avalos 				default:
28760b4ad09SPeter Avalos 					/* Just continue; */
28860b4ad09SPeter Avalos 					break;
28960b4ad09SPeter Avalos 				}
29060b4ad09SPeter Avalos 
29160b4ad09SPeter Avalos 			}
29260b4ad09SPeter Avalos 
2939c82a63eSPeter Avalos 			realloc_strcat(result, rule->result + j);
29460b4ad09SPeter Avalos 
29560b4ad09SPeter Avalos 			name += matches[0].rm_eo;
29660b4ad09SPeter Avalos 
29760b4ad09SPeter Avalos 			if (!rule->global)
29860b4ad09SPeter Avalos 				break;
29960b4ad09SPeter Avalos 		}
3006b384f39SPeter Avalos 	}
30160b4ad09SPeter Avalos 
30260b4ad09SPeter Avalos 	if (got_match)
3039c82a63eSPeter Avalos 		realloc_strcat(result, name);
30460b4ad09SPeter Avalos 
30560b4ad09SPeter Avalos 	if (print_match)
30660b4ad09SPeter Avalos 		fprintf(stderr, "%s >> %s\n", path, *result);
30760b4ad09SPeter Avalos 
30860b4ad09SPeter Avalos 	return got_match;
30960b4ad09SPeter Avalos }
31060b4ad09SPeter Avalos 
31160b4ad09SPeter Avalos void
cleanup_substitution(struct bsdtar * bsdtar)31260b4ad09SPeter Avalos cleanup_substitution(struct bsdtar *bsdtar)
31360b4ad09SPeter Avalos {
31460b4ad09SPeter Avalos 	struct subst_rule *rule;
31560b4ad09SPeter Avalos 	struct substitution *subst;
31660b4ad09SPeter Avalos 
31760b4ad09SPeter Avalos 	if ((subst = bsdtar->substitution) == NULL)
31860b4ad09SPeter Avalos 		return;
31960b4ad09SPeter Avalos 
32060b4ad09SPeter Avalos 	while ((rule = subst->first_rule) != NULL) {
32160b4ad09SPeter Avalos 		subst->first_rule = rule->next;
32260b4ad09SPeter Avalos 		free(rule->result);
32360b4ad09SPeter Avalos 		free(rule);
32460b4ad09SPeter Avalos 	}
32560b4ad09SPeter Avalos 	free(subst);
32660b4ad09SPeter Avalos }
327d4d8193eSPeter Avalos #endif /* defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) */
328