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