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, symlink: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 rule->global = 0; 121 rule->print = 0; 122 rule->symlink = 0; 123 124 while (*++end_pattern) { 125 switch (*end_pattern) { 126 case 'g': 127 case 'G': 128 rule->global = 1; 129 break; 130 case 'p': 131 case 'P': 132 rule->print = 1; 133 break; 134 case 's': 135 case 'S': 136 rule->symlink = 1; 137 break; 138 default: 139 lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern); 140 } 141 } 142 } 143 144 static void 145 realloc_strncat(char **str, const char *append, size_t len) 146 { 147 char *new_str; 148 size_t old_len; 149 150 if (*str == NULL) 151 old_len = 0; 152 else 153 old_len = strlen(*str); 154 155 new_str = malloc(old_len + len + 1); 156 if (new_str == NULL) 157 lafe_errc(1, errno, "Out of memory"); 158 memcpy(new_str, *str, old_len); 159 memcpy(new_str + old_len, append, len); 160 new_str[old_len + len] = '\0'; 161 free(*str); 162 *str = new_str; 163 } 164 165 static void 166 realloc_strcat(char **str, const char *append) 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 + strlen(append) + 1); 177 if (new_str == NULL) 178 lafe_errc(1, errno, "Out of memory"); 179 memcpy(new_str, *str, old_len); 180 strcpy(new_str + old_len, append); 181 free(*str); 182 *str = new_str; 183 } 184 185 int 186 apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int symlink_only) 187 { 188 const char *path = name; 189 regmatch_t matches[10]; 190 size_t i, j; 191 struct subst_rule *rule; 192 struct substitution *subst; 193 int c, got_match, print_match; 194 195 *result = NULL; 196 197 if ((subst = bsdtar->substitution) == NULL) 198 return 0; 199 200 got_match = 0; 201 print_match = 0; 202 203 for (rule = subst->first_rule; rule != NULL; rule = rule->next) { 204 if (symlink_only && !rule->symlink) 205 continue; 206 if (regexec(&rule->re, name, 10, matches, 0)) 207 continue; 208 209 got_match = 1; 210 print_match |= rule->print; 211 realloc_strncat(result, name, matches[0].rm_so); 212 213 for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { 214 if (rule->result[i] == '~') { 215 realloc_strncat(result, rule->result + j, i - j); 216 realloc_strncat(result, name, matches[0].rm_eo); 217 j = i + 1; 218 continue; 219 } 220 if (rule->result[i] != '\\') 221 continue; 222 223 ++i; 224 c = rule->result[i]; 225 switch (c) { 226 case '~': 227 case '\\': 228 realloc_strncat(result, rule->result + j, i - j - 1); 229 j = i; 230 break; 231 case '1': 232 case '2': 233 case '3': 234 case '4': 235 case '5': 236 case '6': 237 case '7': 238 case '8': 239 case '9': 240 realloc_strncat(result, rule->result + j, i - j - 1); 241 if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { 242 free(*result); 243 *result = NULL; 244 return -1; 245 } 246 realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); 247 j = i + 1; 248 break; 249 default: 250 /* Just continue; */ 251 break; 252 } 253 254 } 255 256 realloc_strcat(result, rule->result + j); 257 258 name += matches[0].rm_eo; 259 260 if (!rule->global) 261 break; 262 } 263 264 if (got_match) 265 realloc_strcat(result, name); 266 267 if (print_match) 268 fprintf(stderr, "%s >> %s\n", path, *result); 269 270 return got_match; 271 } 272 273 void 274 cleanup_substitution(struct bsdtar *bsdtar) 275 { 276 struct subst_rule *rule; 277 struct substitution *subst; 278 279 if ((subst = bsdtar->substitution) == NULL) 280 return; 281 282 while ((rule = subst->first_rule) != NULL) { 283 subst->first_rule = rule->next; 284 free(rule->result); 285 free(rule); 286 } 287 free(subst); 288 } 289 #endif /* HAVE_REGEX_H */ 290