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