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