subst.c revision 228763
1139790Simp/*-
243412Snewton * Copyright (c) 2008 Joerg Sonnenberger
343412Snewton * All rights reserved.
443412Snewton *
543412Snewton * Redistribution and use in source and binary forms, with or without
643412Snewton * modification, are permitted provided that the following conditions
743412Snewton * are met:
843412Snewton * 1. Redistributions of source code must retain the above copyright
943412Snewton *    notice, this list of conditions and the following disclaimer.
1043412Snewton * 2. Redistributions in binary form must reproduce the above copyright
1143412Snewton *    notice, this list of conditions and the following disclaimer in the
1243412Snewton *    documentation and/or other materials provided with the distribution.
1343412Snewton *
1443412Snewton * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
1543412Snewton * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1643412Snewton * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1743412Snewton * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
1843412Snewton * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1943412Snewton * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2043412Snewton * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2143412Snewton * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2243412Snewton * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2343412Snewton * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2443412Snewton */
2543412Snewton
2643412Snewton#include "bsdtar_platform.h"
2743412Snewton__FBSDID("$FreeBSD: head/contrib/libarchive/tar/subst.c 228763 2011-12-21 11:13:29Z mm $");
2843412Snewton
29116145Sobrien#if HAVE_REGEX_H
30116145Sobrien#include "bsdtar.h"
31116145Sobrien
3243412Snewton#include <errno.h>
3343412Snewton#include <regex.h>
3443412Snewton#include <stdlib.h>
3543412Snewton#include <string.h>
3676166Smarkm
3743412Snewton#ifndef REG_BASIC
3876166Smarkm#define	REG_BASIC 0
3976166Smarkm#endif
4043412Snewton
4143412Snewton#include "err.h"
4243412Snewton
4343412Snewtonstruct subst_rule {
4443412Snewton	struct subst_rule *next;
4543412Snewton	regex_t re;
4643412Snewton	char *result;
4743412Snewton	unsigned int global:1, print:1, symlink:1;
4843412Snewton};
4943412Snewton
5043412Snewtonstruct substitution {
5143412Snewton	struct subst_rule *first_rule, *last_rule;
5276166Smarkm};
5376166Smarkm
5476166Smarkmstatic void
5565302Sobrieninit_substitution(struct bsdtar *bsdtar)
5665302Sobrien{
5765302Sobrien	struct substitution *subst;
5843412Snewton
5965302Sobrien	bsdtar->substitution = subst = malloc(sizeof(*subst));
6065302Sobrien	if (subst == NULL)
6165302Sobrien		lafe_errc(1, errno, "Out of memory");
6243412Snewton	subst->first_rule = subst->last_rule = NULL;
6343412Snewton}
6443412Snewton
6543412Snewtonvoid
6643412Snewtonadd_substitution(struct bsdtar *bsdtar, const char *rule_text)
6743412Snewton{
6843412Snewton	struct subst_rule *rule;
6992765Salfred	struct substitution *subst;
7043412Snewton	const char *end_pattern, *start_subst;
7143412Snewton	char *pattern;
7243412Snewton	int r;
7343412Snewton
7443412Snewton	if ((subst = bsdtar->substitution) == NULL) {
7543412Snewton		init_substitution(bsdtar);
7643412Snewton		subst = bsdtar->substitution;
7743412Snewton	}
7843412Snewton
7943412Snewton	rule = malloc(sizeof(*rule));
8083366Sjulian	if (rule == NULL)
8183366Sjulian		lafe_errc(1, errno, "Out of memory");
8243412Snewton	rule->next = NULL;
8343412Snewton
8443412Snewton	if (subst->last_rule == NULL)
8583366Sjulian		subst->first_rule = rule;
8643412Snewton	else
8743412Snewton		subst->last_rule->next = rule;
8883366Sjulian	subst->last_rule = rule;
8943412Snewton
9043412Snewton	if (*rule_text == '\0')
9143412Snewton		lafe_errc(1, 0, "Empty replacement string");
9243412Snewton	end_pattern = strchr(rule_text + 1, *rule_text);
9383366Sjulian	if (end_pattern == NULL)
9483366Sjulian		lafe_errc(1, 0, "Invalid replacement string");
9543412Snewton
9651793Smarcel	pattern = malloc(end_pattern - rule_text);
9751793Smarcel	if (pattern == NULL)
9843412Snewton		lafe_errc(1, errno, "Out of memory");
9983366Sjulian	memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
10083366Sjulian	pattern[end_pattern - rule_text - 1] = '\0';
10143412Snewton
10243412Snewton	if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
10356044Snewton		char buf[80];
10471495Sjhb		regerror(r, &rule->re, buf, sizeof(buf));
10571495Sjhb		lafe_errc(1, 0, "Invalid regular expression: %s", buf);
10649270Snewton	}
10743412Snewton	free(pattern);
10883641Sjhb
10971495Sjhb	start_subst = end_pattern + 1;
11071495Sjhb	end_pattern = strchr(start_subst, *rule_text);
11171495Sjhb	if (end_pattern == NULL)
11271495Sjhb		lafe_errc(1, 0, "Invalid replacement string");
11371495Sjhb
11443412Snewton	rule->result = malloc(end_pattern - start_subst + 1);
11543412Snewton	if (rule->result == NULL)
11644270Snewton		lafe_errc(1, errno, "Out of memory");
11743412Snewton	memcpy(rule->result, start_subst, end_pattern - start_subst);
11843412Snewton	rule->result[end_pattern - start_subst] = '\0';
11943412Snewton
12047694Snewton	rule->global = 0;
12143412Snewton	rule->print = 0;
12243412Snewton	rule->symlink = 0;
12343412Snewton
12443412Snewton	while (*++end_pattern) {
12543412Snewton		switch (*end_pattern) {
12683366Sjulian		case 'g':
12743412Snewton		case 'G':
12847694Snewton			rule->global = 1;
12943412Snewton			break;
13046129Sluoqi		case 'p':
13143412Snewton		case 'P':
13243412Snewton			rule->print = 1;
13346129Sluoqi			break;
13446129Sluoqi		case 's':
13546129Sluoqi		case 'S':
13646129Sluoqi			rule->symlink = 1;
13743412Snewton			break;
13843412Snewton		default:
13943412Snewton			lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern);
14043412Snewton		}
14143412Snewton	}
14243412Snewton}
14343412Snewton
14443412Snewtonstatic void
14543412Snewtonrealloc_strncat(char **str, const char *append, size_t len)
14643412Snewton{
14743412Snewton	char *new_str;
14843412Snewton	size_t old_len;
14944270Snewton
15044270Snewton	if (*str == NULL)
15143412Snewton		old_len = 0;
15243412Snewton	else
15343412Snewton		old_len = strlen(*str);
15443412Snewton
15543412Snewton	new_str = malloc(old_len + len + 1);
15643412Snewton	if (new_str == NULL)
15743412Snewton		lafe_errc(1, errno, "Out of memory");
15843412Snewton	memcpy(new_str, *str, old_len);
15956044Snewton	memcpy(new_str + old_len, append, len);
16043412Snewton	new_str[old_len + len] = '\0';
16144270Snewton	free(*str);
162298433Spfg	*str = new_str;
16344270Snewton}
16444270Snewton
16544270Snewtonstatic void
16683641Sjhbrealloc_strcat(char **str, const char *append)
16743412Snewton{
16843412Snewton	char *new_str;
16943412Snewton	size_t old_len;
17043412Snewton
17151793Smarcel	if (*str == NULL)
17243412Snewton		old_len = 0;
17343412Snewton	else
17443412Snewton		old_len = strlen(*str);
17543412Snewton
17656044Snewton	new_str = malloc(old_len + strlen(append) + 1);
17743412Snewton	if (new_str == NULL)
17843412Snewton		lafe_errc(1, errno, "Out of memory");
17943412Snewton	memcpy(new_str, *str, old_len);
18043412Snewton	strcpy(new_str + old_len, append);
18156044Snewton	free(*str);
18243412Snewton	*str = new_str;
18343412Snewton}
18443412Snewton
18543412Snewtonint
18643412Snewtonapply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int symlink_only)
18743412Snewton{
18843412Snewton	const char *path = name;
18943412Snewton	regmatch_t matches[10];
19083366Sjulian	size_t i, j;
19183366Sjulian	struct subst_rule *rule;
19243412Snewton	struct substitution *subst;
19343412Snewton	int c, got_match, print_match;
19456044Snewton
19571495Sjhb	*result = NULL;
19656044Snewton
19783366Sjulian	if ((subst = bsdtar->substitution) == NULL)
19843412Snewton		return 0;
19943412Snewton
20043412Snewton	got_match = 0;
20171495Sjhb	print_match = 0;
20251793Smarcel
20343412Snewton	for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
20483641Sjhb		if (symlink_only && !rule->symlink)
20571495Sjhb			continue;
20683641Sjhb		if (regexec(&rule->re, name, 10, matches, 0))
20771495Sjhb			continue;
208124141Sobrien
20971495Sjhb		got_match = 1;
21043412Snewton		print_match |= rule->print;
21143412Snewton		realloc_strncat(result, name, matches[0].rm_so);
21243412Snewton
21343412Snewton		for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
21443412Snewton			if (rule->result[i] == '~') {
21543412Snewton				realloc_strncat(result, rule->result + j, i - j);
21643412Snewton				realloc_strncat(result, name, matches[0].rm_eo);
21743412Snewton				j = i + 1;
21843412Snewton				continue;
219113623Sjhb			}
220113623Sjhb			if (rule->result[i] != '\\')
22144270Snewton				continue;
222113623Sjhb
22344270Snewton			++i;
22456044Snewton			c = rule->result[i];
22556044Snewton			switch (c) {
22683366Sjulian			case '~':
22743412Snewton			case '\\':
22843412Snewton				realloc_strncat(result, rule->result + j, i - j - 1);
22943412Snewton				j = i;
23043412Snewton				break;
23147694Snewton			case '1':
23256044Snewton			case '2':
23343412Snewton			case '3':
23443412Snewton			case '4':
23543412Snewton			case '5':
23643412Snewton			case '6':
23743412Snewton			case '7':
23883366Sjulian			case '8':
23943412Snewton			case '9':
24047694Snewton				realloc_strncat(result, rule->result + j, i - j - 1);
24143412Snewton				if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
24243412Snewton					free(*result);
24343412Snewton					*result = NULL;
24443412Snewton					return -1;
24543412Snewton				}
24643412Snewton				realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
24743412Snewton				j = i + 1;
24843412Snewton				break;
249113623Sjhb			default:
250113623Sjhb				/* Just continue; */
25143412Snewton				break;
252113623Sjhb			}
25343412Snewton
25446129Sluoqi		}
25543412Snewton
25646129Sluoqi		realloc_strcat(result, rule->result + j);
25746129Sluoqi
25846129Sluoqi		name += matches[0].rm_eo;
25946129Sluoqi
26043412Snewton		if (!rule->global)
26143412Snewton			break;
26243412Snewton	}
26343412Snewton
26443412Snewton	if (got_match)
26543412Snewton		realloc_strcat(result, name);
26643412Snewton
26743412Snewton	if (print_match)
26843412Snewton		fprintf(stderr, "%s >> %s\n", path, *result);
26943412Snewton
27043412Snewton	return got_match;
27144270Snewton}
27244270Snewton
27343412Snewtonvoid
27443412Snewtoncleanup_substitution(struct bsdtar *bsdtar)
27543412Snewton{
27643412Snewton	struct subst_rule *rule;
27743412Snewton	struct substitution *subst;
27844270Snewton
27943412Snewton	if ((subst = bsdtar->substitution) == NULL)
28043412Snewton		return;
28143412Snewton
28244270Snewton	while ((rule = subst->first_rule) != NULL) {
28344270Snewton		subst->first_rule = rule->next;
28444270Snewton		free(rule->result);
28543412Snewton		free(rule);
28643412Snewton	}
28743412Snewton	free(subst);
28843412Snewton}
28944270Snewton#endif /* HAVE_REGEX_H */
29056044Snewton