Import libarchive-2.5.4b.
[dragonfly.git] / contrib / libarchive-2 / tar / subst.c
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$");
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 struct subst_rule {
38         struct subst_rule *next;
39         regex_t re;
40         char *result;
41         int global:1, print:1, symlink:1;
42 };
43
44 struct substitution {
45         struct subst_rule *first_rule, *last_rule;
46 };
47
48 static void
49 init_substitution(struct bsdtar *bsdtar)
50 {
51         struct substitution *subst;
52
53         bsdtar->substitution = subst = malloc(sizeof(*subst));
54         if (subst == NULL)
55                 bsdtar_errc(bsdtar, 1, errno, "Out of memory");
56         subst->first_rule = subst->last_rule = NULL;
57 }
58
59 void
60 add_substitution(struct bsdtar *bsdtar, const char *rule_text)
61 {
62         struct subst_rule *rule;
63         struct substitution *subst;
64         const char *end_pattern, *start_subst;
65         char *pattern;
66         int r;
67
68         if ((subst = bsdtar->substitution) == NULL) {
69                 init_substitution(bsdtar);
70                 subst = bsdtar->substitution;
71         }
72
73         rule = malloc(sizeof(*rule));
74         if (rule == NULL)
75                 bsdtar_errc(bsdtar, 1, errno, "Out of memory");
76         rule->next = NULL;
77
78         if (subst->last_rule == NULL)
79                 subst->first_rule = rule;
80         else
81                 subst->last_rule->next = rule;
82         subst->last_rule = rule;
83
84         if (*rule_text == '\0')
85                 bsdtar_errc(bsdtar, 1, 0, "Empty replacement string");
86         end_pattern = strchr(rule_text + 1, *rule_text);
87         if (end_pattern == NULL)
88                 bsdtar_errc(bsdtar, 1, 0, "Invalid replacement string");
89
90         pattern = malloc(end_pattern - rule_text);
91         if (pattern == NULL)
92                 bsdtar_errc(bsdtar, 1, errno, "Out of memory");
93         memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1);
94         pattern[end_pattern - rule_text - 1] = '\0';
95
96         if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) {
97                 char buf[80];
98                 regerror(r, &rule->re, buf, sizeof(buf));
99                 bsdtar_errc(bsdtar, 1, 0, "Invalid regular expression: %s", buf);
100         }
101         free(pattern);
102
103         start_subst = end_pattern + 1;
104         end_pattern = strchr(start_subst, *rule_text);
105         if (end_pattern == NULL)
106                 bsdtar_errc(bsdtar, 1, 0, "Invalid replacement string");
107
108         rule->result = malloc(end_pattern - start_subst + 1);
109         if (rule->result == NULL)
110                 bsdtar_errc(bsdtar, 1, errno, "Out of memory");
111         memcpy(rule->result, start_subst, end_pattern - start_subst);
112         rule->result[end_pattern - start_subst] = '\0';
113
114         rule->global = 0;
115         rule->print = 0;
116         rule->symlink = 0;
117
118         while (*++end_pattern) {
119                 switch (*end_pattern) {
120                 case 'g':
121                 case 'G':
122                         rule->global = 1;
123                         break;
124                 case 'p':
125                 case 'P':
126                         rule->print = 1;
127                         break;
128                 case 's':
129                 case 'S':
130                         rule->symlink = 1;
131                         break;
132                 default:
133                         bsdtar_errc(bsdtar, 1, 0, "Invalid replacement flag %c", *end_pattern);
134                 }
135         }
136 }
137
138 static void
139 realloc_strncat(struct bsdtar *bsdtar, char **str, const char *append, size_t len)
140 {
141         char *new_str;
142         size_t old_len;
143
144         if (*str == NULL)
145                 old_len = 0;
146         else
147                 old_len = strlen(*str);
148
149         new_str = malloc(old_len + len + 1);
150         if (new_str == NULL)
151                 bsdtar_errc(bsdtar, 1, errno, "Out of memory");
152         memcpy(new_str, *str, old_len);
153         memcpy(new_str + old_len, append, len);
154         new_str[old_len + len] = '\0';
155         free(*str);
156         *str = new_str;
157 }
158
159 static void
160 realloc_strcat(struct bsdtar *bsdtar, char **str, const char *append)
161 {
162         char *new_str;
163         size_t old_len;
164
165         if (*str == NULL)
166                 old_len = 0;
167         else
168                 old_len = strlen(*str);
169
170         new_str = malloc(old_len + strlen(append) + 1);
171         if (new_str == NULL)
172                 bsdtar_errc(bsdtar, 1, errno, "Out of memory");
173         memcpy(new_str, *str, old_len);
174         strcpy(new_str + old_len, append);
175         free(*str);
176         *str = new_str;
177 }
178
179 int
180 apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int symlink_only)
181 {
182         const char *path = name;
183         regmatch_t matches[10];
184         size_t i, j;
185         struct subst_rule *rule;
186         struct substitution *subst;
187         int c, got_match, print_match;
188
189         *result = NULL;
190
191         if ((subst = bsdtar->substitution) == NULL)
192                 return 0;
193
194         got_match = 0;
195         print_match = 0;
196
197         for (rule = subst->first_rule; rule != NULL; rule = rule->next) {
198                 if (symlink_only && !rule->symlink)
199                         continue;
200                 if (regexec(&rule->re, name, 10, matches, 0))
201                         break;
202
203                 got_match = 1;
204                 print_match |= rule->print;
205                 realloc_strncat(bsdtar, result, name, matches[0].rm_so);
206
207                 for (i = 0, j = 0; rule->result[i] != '\0'; ++i) {
208                         if (rule->result[i] == '~') {
209                                 realloc_strncat(bsdtar, result, rule->result + j, i - j);
210                                 realloc_strncat(bsdtar, result, name, matches[0].rm_eo);
211                                 j = i + 1;
212                                 continue;
213                         }
214                         if (rule->result[i] != '\\')
215                                 continue;
216
217                         ++i;
218                         c = rule->result[i];
219                         switch (c) {
220                         case '~':
221                         case '\\':
222                                 realloc_strncat(bsdtar, result, rule->result + j, i - j - 1);
223                                 j = i;
224                                 break;
225                         case '1' ... '9':
226                                 realloc_strncat(bsdtar, result, rule->result + j, i - j - 1);
227                                 if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) {
228                                         free(*result);
229                                         *result = NULL;
230                                         return -1;
231                                 }
232                                 realloc_strncat(bsdtar, result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so);
233                                 j = i + 1;
234                                 break;
235                         default:
236                                 /* Just continue; */
237                                 break;
238                         }
239
240                 }
241
242                 realloc_strcat(bsdtar, result, rule->result + j);
243
244                 name += matches[0].rm_eo;
245
246                 if (!rule->global)
247                         break;
248         }
249
250         if (got_match)
251                 realloc_strcat(bsdtar, result, name);
252
253         if (print_match)
254                 fprintf(stderr, "%s >> %s\n", path, *result);
255
256         return got_match;
257 }
258
259 void
260 cleanup_substitution(struct bsdtar *bsdtar)
261 {
262         struct subst_rule *rule;
263         struct substitution *subst;
264
265         if ((subst = bsdtar->substitution) == NULL)
266                 return;
267
268         while ((rule = subst->first_rule) != NULL) {
269                 subst->first_rule = rule->next;
270                 free(rule->result);
271                 free(rule);
272         }
273         free(subst);
274 }
275 #endif /* HAVE_REGEX_H */