1228753Smm/*- 2228753Smm * Copyright (c) 2008 Joerg Sonnenberger 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm 26228753Smm#include "bsdtar_platform.h" 27229592Smm__FBSDID("$FreeBSD$"); 28228753Smm 29228753Smm#if HAVE_REGEX_H 30228753Smm#include "bsdtar.h" 31228753Smm 32228753Smm#include <errno.h> 33228753Smm#include <regex.h> 34228753Smm#include <stdlib.h> 35228753Smm#include <string.h> 36228753Smm 37228753Smm#ifndef REG_BASIC 38228753Smm#define REG_BASIC 0 39228753Smm#endif 40228753Smm 41228753Smm#include "err.h" 42228753Smm 43228753Smmstruct subst_rule { 44228753Smm struct subst_rule *next; 45228753Smm regex_t re; 46228753Smm char *result; 47228753Smm unsigned int global:1, print:1, symlink:1; 48228753Smm}; 49228753Smm 50228753Smmstruct substitution { 51228753Smm struct subst_rule *first_rule, *last_rule; 52228753Smm}; 53228753Smm 54228753Smmstatic void 55228753Smminit_substitution(struct bsdtar *bsdtar) 56228753Smm{ 57228753Smm struct substitution *subst; 58228753Smm 59228753Smm bsdtar->substitution = subst = malloc(sizeof(*subst)); 60228753Smm if (subst == NULL) 61228753Smm lafe_errc(1, errno, "Out of memory"); 62228753Smm subst->first_rule = subst->last_rule = NULL; 63228753Smm} 64228753Smm 65228753Smmvoid 66228753Smmadd_substitution(struct bsdtar *bsdtar, const char *rule_text) 67228753Smm{ 68228753Smm struct subst_rule *rule; 69228753Smm struct substitution *subst; 70228753Smm const char *end_pattern, *start_subst; 71228753Smm char *pattern; 72228753Smm int r; 73228753Smm 74228753Smm if ((subst = bsdtar->substitution) == NULL) { 75228753Smm init_substitution(bsdtar); 76228753Smm subst = bsdtar->substitution; 77228753Smm } 78228753Smm 79228753Smm rule = malloc(sizeof(*rule)); 80228753Smm if (rule == NULL) 81228753Smm lafe_errc(1, errno, "Out of memory"); 82228753Smm rule->next = NULL; 83228753Smm 84228753Smm if (subst->last_rule == NULL) 85228753Smm subst->first_rule = rule; 86228753Smm else 87228753Smm subst->last_rule->next = rule; 88228753Smm subst->last_rule = rule; 89228753Smm 90228753Smm if (*rule_text == '\0') 91228753Smm lafe_errc(1, 0, "Empty replacement string"); 92228753Smm end_pattern = strchr(rule_text + 1, *rule_text); 93228753Smm if (end_pattern == NULL) 94228753Smm lafe_errc(1, 0, "Invalid replacement string"); 95228753Smm 96228753Smm pattern = malloc(end_pattern - rule_text); 97228753Smm if (pattern == NULL) 98228753Smm lafe_errc(1, errno, "Out of memory"); 99228753Smm memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1); 100228753Smm pattern[end_pattern - rule_text - 1] = '\0'; 101228753Smm 102228753Smm if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) { 103228753Smm char buf[80]; 104228753Smm regerror(r, &rule->re, buf, sizeof(buf)); 105228753Smm lafe_errc(1, 0, "Invalid regular expression: %s", buf); 106228753Smm } 107228753Smm free(pattern); 108228753Smm 109228753Smm start_subst = end_pattern + 1; 110228753Smm end_pattern = strchr(start_subst, *rule_text); 111228753Smm if (end_pattern == NULL) 112228753Smm lafe_errc(1, 0, "Invalid replacement string"); 113228753Smm 114228753Smm rule->result = malloc(end_pattern - start_subst + 1); 115228753Smm if (rule->result == NULL) 116228753Smm lafe_errc(1, errno, "Out of memory"); 117228753Smm memcpy(rule->result, start_subst, end_pattern - start_subst); 118228753Smm rule->result[end_pattern - start_subst] = '\0'; 119228753Smm 120228753Smm rule->global = 0; 121228753Smm rule->print = 0; 122228753Smm rule->symlink = 0; 123228753Smm 124228753Smm while (*++end_pattern) { 125228753Smm switch (*end_pattern) { 126228753Smm case 'g': 127228753Smm case 'G': 128228753Smm rule->global = 1; 129228753Smm break; 130228753Smm case 'p': 131228753Smm case 'P': 132228753Smm rule->print = 1; 133228753Smm break; 134228753Smm case 's': 135228753Smm case 'S': 136228753Smm rule->symlink = 1; 137228753Smm break; 138228753Smm default: 139228753Smm lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern); 140228753Smm } 141228753Smm } 142228753Smm} 143228753Smm 144228753Smmstatic void 145228753Smmrealloc_strncat(char **str, const char *append, size_t len) 146228753Smm{ 147228753Smm char *new_str; 148228753Smm size_t old_len; 149228753Smm 150228753Smm if (*str == NULL) 151228753Smm old_len = 0; 152228753Smm else 153228753Smm old_len = strlen(*str); 154228753Smm 155228753Smm new_str = malloc(old_len + len + 1); 156228753Smm if (new_str == NULL) 157228753Smm lafe_errc(1, errno, "Out of memory"); 158228753Smm memcpy(new_str, *str, old_len); 159228753Smm memcpy(new_str + old_len, append, len); 160228753Smm new_str[old_len + len] = '\0'; 161228753Smm free(*str); 162228753Smm *str = new_str; 163228753Smm} 164228753Smm 165228753Smmstatic void 166228753Smmrealloc_strcat(char **str, const char *append) 167228753Smm{ 168228753Smm char *new_str; 169228753Smm size_t old_len; 170228753Smm 171228753Smm if (*str == NULL) 172228753Smm old_len = 0; 173228753Smm else 174228753Smm old_len = strlen(*str); 175228753Smm 176228753Smm new_str = malloc(old_len + strlen(append) + 1); 177228753Smm if (new_str == NULL) 178228753Smm lafe_errc(1, errno, "Out of memory"); 179228753Smm memcpy(new_str, *str, old_len); 180228753Smm strcpy(new_str + old_len, append); 181228753Smm free(*str); 182228753Smm *str = new_str; 183228753Smm} 184228753Smm 185228753Smmint 186228753Smmapply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int symlink_only) 187228753Smm{ 188228753Smm const char *path = name; 189228753Smm regmatch_t matches[10]; 190228753Smm size_t i, j; 191228753Smm struct subst_rule *rule; 192228753Smm struct substitution *subst; 193228753Smm int c, got_match, print_match; 194228753Smm 195228753Smm *result = NULL; 196228753Smm 197228753Smm if ((subst = bsdtar->substitution) == NULL) 198228753Smm return 0; 199228753Smm 200228753Smm got_match = 0; 201228753Smm print_match = 0; 202228753Smm 203228753Smm for (rule = subst->first_rule; rule != NULL; rule = rule->next) { 204228753Smm if (symlink_only && !rule->symlink) 205228753Smm continue; 206228753Smm if (regexec(&rule->re, name, 10, matches, 0)) 207228753Smm continue; 208228753Smm 209228753Smm got_match = 1; 210228753Smm print_match |= rule->print; 211228753Smm realloc_strncat(result, name, matches[0].rm_so); 212228753Smm 213228753Smm for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { 214228753Smm if (rule->result[i] == '~') { 215228753Smm realloc_strncat(result, rule->result + j, i - j); 216228753Smm realloc_strncat(result, name, matches[0].rm_eo); 217228753Smm j = i + 1; 218228753Smm continue; 219228753Smm } 220228753Smm if (rule->result[i] != '\\') 221228753Smm continue; 222228753Smm 223228753Smm ++i; 224228753Smm c = rule->result[i]; 225228753Smm switch (c) { 226228753Smm case '~': 227228753Smm case '\\': 228228753Smm realloc_strncat(result, rule->result + j, i - j - 1); 229228753Smm j = i; 230228753Smm break; 231228753Smm case '1': 232228753Smm case '2': 233228753Smm case '3': 234228753Smm case '4': 235228753Smm case '5': 236228753Smm case '6': 237228753Smm case '7': 238228753Smm case '8': 239228753Smm case '9': 240228753Smm realloc_strncat(result, rule->result + j, i - j - 1); 241228753Smm if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { 242228753Smm free(*result); 243228753Smm *result = NULL; 244228753Smm return -1; 245228753Smm } 246228753Smm realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); 247228753Smm j = i + 1; 248228753Smm break; 249228753Smm default: 250228753Smm /* Just continue; */ 251228753Smm break; 252228753Smm } 253228753Smm 254228753Smm } 255228753Smm 256228753Smm realloc_strcat(result, rule->result + j); 257228753Smm 258228753Smm name += matches[0].rm_eo; 259228753Smm 260228753Smm if (!rule->global) 261228753Smm break; 262228753Smm } 263228753Smm 264228753Smm if (got_match) 265228753Smm realloc_strcat(result, name); 266228753Smm 267228753Smm if (print_match) 268228753Smm fprintf(stderr, "%s >> %s\n", path, *result); 269228753Smm 270228753Smm return got_match; 271228753Smm} 272228753Smm 273228753Smmvoid 274228753Smmcleanup_substitution(struct bsdtar *bsdtar) 275228753Smm{ 276228753Smm struct subst_rule *rule; 277228753Smm struct substitution *subst; 278228753Smm 279228753Smm if ((subst = bsdtar->substitution) == NULL) 280228753Smm return; 281228753Smm 282228753Smm while ((rule = subst->first_rule) != NULL) { 283228753Smm subst->first_rule = rule->next; 284228753Smm free(rule->result); 285228753Smm free(rule); 286228753Smm } 287228753Smm free(subst); 288228753Smm} 289228753Smm#endif /* HAVE_REGEX_H */ 290