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 28 #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H) 29 #include "bsdtar.h" 30 31 #include <errno.h> 32 #if defined(HAVE_PCREPOSIX_H) 33 #include <pcreposix.h> 34 #elif defined(HAVE_PCRE2POSIX_H) 35 #include <pcre2posix.h> 36 #else 37 #include <regex.h> 38 #endif 39 #include <stdlib.h> 40 #include <string.h> 41 42 #ifndef REG_BASIC 43 #define REG_BASIC 0 44 #endif 45 46 #include "err.h" 47 48 struct subst_rule { 49 struct subst_rule *next; 50 regex_t re; 51 char *result; 52 unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1, from_begin:1; 53 }; 54 55 struct substitution { 56 struct subst_rule *first_rule, *last_rule; 57 }; 58 59 static void 60 init_substitution(struct bsdtar *bsdtar) 61 { 62 struct substitution *subst; 63 64 bsdtar->substitution = subst = malloc(sizeof(*subst)); 65 if (subst == NULL) 66 lafe_errc(1, errno, "Out of memory"); 67 subst->first_rule = subst->last_rule = NULL; 68 } 69 70 void 71 add_substitution(struct bsdtar *bsdtar, const char *rule_text) 72 { 73 struct subst_rule *rule; 74 struct substitution *subst; 75 const char *end_pattern, *start_subst; 76 char *pattern; 77 int r; 78 79 if ((subst = bsdtar->substitution) == NULL) { 80 init_substitution(bsdtar); 81 subst = bsdtar->substitution; 82 } 83 84 rule = malloc(sizeof(*rule)); 85 if (rule == NULL) 86 lafe_errc(1, errno, "Out of memory"); 87 rule->next = NULL; 88 rule->result = NULL; 89 90 if (subst->last_rule == NULL) 91 subst->first_rule = rule; 92 else 93 subst->last_rule->next = rule; 94 subst->last_rule = rule; 95 96 if (*rule_text == '\0') 97 lafe_errc(1, 0, "Empty replacement string"); 98 end_pattern = strchr(rule_text + 1, *rule_text); 99 if (end_pattern == NULL) 100 lafe_errc(1, 0, "Invalid replacement string"); 101 102 pattern = malloc(end_pattern - rule_text); 103 if (pattern == NULL) 104 lafe_errc(1, errno, "Out of memory"); 105 memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1); 106 pattern[end_pattern - rule_text - 1] = '\0'; 107 108 if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) { 109 char buf[80]; 110 regerror(r, &rule->re, buf, sizeof(buf)); 111 lafe_errc(1, 0, "Invalid regular expression: %s", buf); 112 } 113 free(pattern); 114 115 start_subst = end_pattern + 1; 116 end_pattern = strchr(start_subst, *rule_text); 117 if (end_pattern == NULL) 118 lafe_errc(1, 0, "Invalid replacement string"); 119 120 rule->result = malloc(end_pattern - start_subst + 1); 121 if (rule->result == NULL) 122 lafe_errc(1, errno, "Out of memory"); 123 memcpy(rule->result, start_subst, end_pattern - start_subst); 124 rule->result[end_pattern - start_subst] = '\0'; 125 126 /* Defaults */ 127 rule->global = 0; /* Don't do multiple replacements. */ 128 rule->print = 0; /* Don't print. */ 129 rule->regular = 1; /* Rewrite regular filenames. */ 130 rule->symlink = 1; /* Rewrite symlink targets. */ 131 rule->hardlink = 1; /* Rewrite hardlink targets. */ 132 rule->from_begin = 0; /* Don't match from start. */ 133 134 while (*++end_pattern) { 135 switch (*end_pattern) { 136 case 'b': 137 case 'B': 138 rule->from_begin = 1; 139 break; 140 case 'g': 141 case 'G': 142 rule->global = 1; 143 break; 144 case 'h': 145 rule->hardlink = 1; 146 break; 147 case 'H': 148 rule->hardlink = 0; 149 break; 150 case 'p': 151 case 'P': 152 rule->print = 1; 153 break; 154 case 'r': 155 rule->regular = 1; 156 break; 157 case 'R': 158 rule->regular = 0; 159 break; 160 case 's': 161 rule->symlink = 1; 162 break; 163 case 'S': 164 rule->symlink = 0; 165 break; 166 default: 167 lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern); 168 /* NOTREACHED */ 169 } 170 } 171 } 172 173 static void 174 realloc_strncat(char **str, const char *append, size_t len) 175 { 176 char *new_str; 177 size_t old_len; 178 179 if (*str == NULL) 180 old_len = 0; 181 else 182 old_len = strlen(*str); 183 184 new_str = malloc(old_len + len + 1); 185 if (new_str == NULL) 186 lafe_errc(1, errno, "Out of memory"); 187 if (*str != NULL) 188 memcpy(new_str, *str, old_len); 189 memcpy(new_str + old_len, append, len); 190 new_str[old_len + len] = '\0'; 191 free(*str); 192 *str = new_str; 193 } 194 195 static void 196 realloc_strcat(char **str, const char *append) 197 { 198 char *new_str; 199 size_t old_len; 200 201 if (*str == NULL) 202 old_len = 0; 203 else 204 old_len = strlen(*str); 205 206 new_str = malloc(old_len + strlen(append) + 1); 207 if (new_str == NULL) 208 lafe_errc(1, errno, "Out of memory"); 209 if (*str != NULL) 210 memcpy(new_str, *str, old_len); 211 strcpy(new_str + old_len, append); 212 free(*str); 213 *str = new_str; 214 } 215 216 int 217 apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, 218 int symlink_target, int hardlink_target) 219 { 220 const char *path = name; 221 regmatch_t matches[10]; 222 char* buffer = NULL; 223 size_t i, j; 224 struct subst_rule *rule; 225 struct substitution *subst; 226 int c, got_match, print_match; 227 228 *result = NULL; 229 230 if ((subst = bsdtar->substitution) == NULL) 231 return 0; 232 233 got_match = 0; 234 print_match = 0; 235 236 for (rule = subst->first_rule; rule != NULL; rule = rule->next) { 237 if (symlink_target) { 238 if (!rule->symlink) 239 continue; 240 } else if (hardlink_target) { 241 if (!rule->hardlink) 242 continue; 243 } else { /* Regular filename. */ 244 if (!rule->regular) 245 continue; 246 } 247 248 if (rule->from_begin && *result) { 249 realloc_strcat(result, name); 250 realloc_strcat(&buffer, *result); 251 name = buffer; 252 (*result)[0] = 0; 253 } 254 255 while (1) { 256 if (regexec(&rule->re, name, 10, matches, 0)) 257 break; 258 259 got_match = 1; 260 print_match |= rule->print; 261 realloc_strncat(result, name, matches[0].rm_so); 262 263 for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { 264 if (rule->result[i] == '~') { 265 realloc_strncat(result, rule->result + j, i - j); 266 realloc_strncat(result, 267 name + matches[0].rm_so, 268 matches[0].rm_eo - matches[0].rm_so); 269 j = i + 1; 270 continue; 271 } 272 if (rule->result[i] != '\\') 273 continue; 274 275 ++i; 276 c = rule->result[i]; 277 switch (c) { 278 case '~': 279 case '\\': 280 realloc_strncat(result, rule->result + j, i - j - 1); 281 j = i; 282 break; 283 case '1': 284 case '2': 285 case '3': 286 case '4': 287 case '5': 288 case '6': 289 case '7': 290 case '8': 291 case '9': 292 realloc_strncat(result, rule->result + j, i - j - 1); 293 if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { 294 free(buffer); 295 free(*result); 296 *result = NULL; 297 return -1; 298 } 299 realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); 300 j = i + 1; 301 break; 302 default: 303 /* Just continue; */ 304 break; 305 } 306 307 } 308 309 realloc_strcat(result, rule->result + j); 310 311 name += matches[0].rm_eo; 312 313 if (!rule->global) 314 break; 315 } 316 } 317 318 if (got_match) 319 realloc_strcat(result, name); 320 321 free(buffer); 322 323 if (print_match) 324 fprintf(stderr, "%s >> %s\n", path, *result); 325 326 return got_match; 327 } 328 329 void 330 cleanup_substitution(struct bsdtar *bsdtar) 331 { 332 struct subst_rule *rule; 333 struct substitution *subst; 334 335 if ((subst = bsdtar->substitution) == NULL) 336 return; 337 338 while ((rule = subst->first_rule) != NULL) { 339 subst->first_rule = rule->next; 340 free(rule->result); 341 regfree(&rule->re); 342 free(rule); 343 } 344 free(subst); 345 } 346 #endif /* defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H) */ 347