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