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" 27228763Smm__FBSDID("$FreeBSD$"); 28228753Smm 29248616Smm#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) 30228753Smm#include "bsdtar.h" 31228753Smm 32228753Smm#include <errno.h> 33248616Smm#ifdef HAVE_PCREPOSIX_H 34248616Smm#include <pcreposix.h> 35248616Smm#else 36228753Smm#include <regex.h> 37248616Smm#endif 38228753Smm#include <stdlib.h> 39228753Smm#include <string.h> 40228753Smm 41228753Smm#ifndef REG_BASIC 42228753Smm#define REG_BASIC 0 43228753Smm#endif 44228753Smm 45228753Smm#include "err.h" 46228753Smm 47228753Smmstruct subst_rule { 48228753Smm struct subst_rule *next; 49228753Smm regex_t re; 50228753Smm char *result; 51232153Smm unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1; 52228753Smm}; 53228753Smm 54228753Smmstruct substitution { 55228753Smm struct subst_rule *first_rule, *last_rule; 56228753Smm}; 57228753Smm 58228753Smmstatic void 59228753Smminit_substitution(struct bsdtar *bsdtar) 60228753Smm{ 61228753Smm struct substitution *subst; 62228753Smm 63228753Smm bsdtar->substitution = subst = malloc(sizeof(*subst)); 64228753Smm if (subst == NULL) 65228753Smm lafe_errc(1, errno, "Out of memory"); 66228753Smm subst->first_rule = subst->last_rule = NULL; 67228753Smm} 68228753Smm 69228753Smmvoid 70228753Smmadd_substitution(struct bsdtar *bsdtar, const char *rule_text) 71228753Smm{ 72228753Smm struct subst_rule *rule; 73228753Smm struct substitution *subst; 74228753Smm const char *end_pattern, *start_subst; 75228753Smm char *pattern; 76228753Smm int r; 77228753Smm 78228753Smm if ((subst = bsdtar->substitution) == NULL) { 79228753Smm init_substitution(bsdtar); 80228753Smm subst = bsdtar->substitution; 81228753Smm } 82228753Smm 83228753Smm rule = malloc(sizeof(*rule)); 84228753Smm if (rule == NULL) 85228753Smm lafe_errc(1, errno, "Out of memory"); 86228753Smm rule->next = NULL; 87228753Smm 88228753Smm if (subst->last_rule == NULL) 89228753Smm subst->first_rule = rule; 90228753Smm else 91228753Smm subst->last_rule->next = rule; 92228753Smm subst->last_rule = rule; 93228753Smm 94228753Smm if (*rule_text == '\0') 95228753Smm lafe_errc(1, 0, "Empty replacement string"); 96228753Smm end_pattern = strchr(rule_text + 1, *rule_text); 97228753Smm if (end_pattern == NULL) 98228753Smm lafe_errc(1, 0, "Invalid replacement string"); 99228753Smm 100228753Smm pattern = malloc(end_pattern - rule_text); 101228753Smm if (pattern == NULL) 102228753Smm lafe_errc(1, errno, "Out of memory"); 103228753Smm memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1); 104228753Smm pattern[end_pattern - rule_text - 1] = '\0'; 105228753Smm 106228753Smm if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) { 107228753Smm char buf[80]; 108228753Smm regerror(r, &rule->re, buf, sizeof(buf)); 109228753Smm lafe_errc(1, 0, "Invalid regular expression: %s", buf); 110228753Smm } 111228753Smm free(pattern); 112228753Smm 113228753Smm start_subst = end_pattern + 1; 114228753Smm end_pattern = strchr(start_subst, *rule_text); 115228753Smm if (end_pattern == NULL) 116228753Smm lafe_errc(1, 0, "Invalid replacement string"); 117228753Smm 118228753Smm rule->result = malloc(end_pattern - start_subst + 1); 119228753Smm if (rule->result == NULL) 120228753Smm lafe_errc(1, errno, "Out of memory"); 121228753Smm memcpy(rule->result, start_subst, end_pattern - start_subst); 122228753Smm rule->result[end_pattern - start_subst] = '\0'; 123228753Smm 124232153Smm /* Defaults */ 125232153Smm rule->global = 0; /* Don't do multiple replacements. */ 126232153Smm rule->print = 0; /* Don't print. */ 127232153Smm rule->regular = 1; /* Rewrite regular filenames. */ 128232153Smm rule->symlink = 1; /* Rewrite symlink targets. */ 129232153Smm rule->hardlink = 1; /* Rewrite hardlink targets. */ 130228753Smm 131228753Smm while (*++end_pattern) { 132228753Smm switch (*end_pattern) { 133228753Smm case 'g': 134228753Smm case 'G': 135228753Smm rule->global = 1; 136228753Smm break; 137232153Smm case 'h': 138232153Smm rule->hardlink = 1; 139232153Smm break; 140232153Smm case 'H': 141232153Smm rule->hardlink = 0; 142232153Smm break; 143228753Smm case 'p': 144228753Smm case 'P': 145228753Smm rule->print = 1; 146228753Smm break; 147232153Smm case 'r': 148232153Smm rule->regular = 1; 149232153Smm break; 150232153Smm case 'R': 151232153Smm rule->regular = 0; 152232153Smm break; 153228753Smm case 's': 154228753Smm rule->symlink = 1; 155228753Smm break; 156232153Smm case 'S': 157232153Smm rule->symlink = 0; 158232153Smm break; 159228753Smm default: 160228753Smm lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern); 161228753Smm } 162228753Smm } 163228753Smm} 164228753Smm 165228753Smmstatic void 166228753Smmrealloc_strncat(char **str, const char *append, size_t len) 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 + len + 1); 177228753Smm if (new_str == NULL) 178228753Smm lafe_errc(1, errno, "Out of memory"); 179232153Smm if (*str != NULL) 180232153Smm memcpy(new_str, *str, old_len); 181228753Smm memcpy(new_str + old_len, append, len); 182228753Smm new_str[old_len + len] = '\0'; 183228753Smm free(*str); 184228753Smm *str = new_str; 185228753Smm} 186228753Smm 187228753Smmstatic void 188228753Smmrealloc_strcat(char **str, const char *append) 189228753Smm{ 190228753Smm char *new_str; 191228753Smm size_t old_len; 192228753Smm 193228753Smm if (*str == NULL) 194228753Smm old_len = 0; 195228753Smm else 196228753Smm old_len = strlen(*str); 197228753Smm 198228753Smm new_str = malloc(old_len + strlen(append) + 1); 199228753Smm if (new_str == NULL) 200228753Smm lafe_errc(1, errno, "Out of memory"); 201232153Smm if (*str != NULL) 202232153Smm memcpy(new_str, *str, old_len); 203228753Smm strcpy(new_str + old_len, append); 204228753Smm free(*str); 205228753Smm *str = new_str; 206228753Smm} 207228753Smm 208228753Smmint 209232153Smmapply_substitution(struct bsdtar *bsdtar, const char *name, char **result, 210232153Smm int symlink_target, int hardlink_target) 211228753Smm{ 212228753Smm const char *path = name; 213228753Smm regmatch_t matches[10]; 214228753Smm size_t i, j; 215228753Smm struct subst_rule *rule; 216228753Smm struct substitution *subst; 217228753Smm int c, got_match, print_match; 218228753Smm 219228753Smm *result = NULL; 220228753Smm 221228753Smm if ((subst = bsdtar->substitution) == NULL) 222228753Smm return 0; 223228753Smm 224228753Smm got_match = 0; 225228753Smm print_match = 0; 226228753Smm 227228753Smm for (rule = subst->first_rule; rule != NULL; rule = rule->next) { 228232153Smm if (symlink_target) { 229232153Smm if (!rule->symlink) 230232153Smm continue; 231232153Smm } else if (hardlink_target) { 232232153Smm if (!rule->hardlink) 233232153Smm continue; 234232153Smm } else { /* Regular filename. */ 235232153Smm if (!rule->regular) 236232153Smm continue; 237232153Smm } 238232153Smm 239228753Smm if (regexec(&rule->re, name, 10, matches, 0)) 240228753Smm continue; 241228753Smm 242228753Smm got_match = 1; 243228753Smm print_match |= rule->print; 244228753Smm realloc_strncat(result, name, matches[0].rm_so); 245228753Smm 246228753Smm for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { 247228753Smm if (rule->result[i] == '~') { 248228753Smm realloc_strncat(result, rule->result + j, i - j); 249232153Smm realloc_strncat(result, 250232153Smm name + matches[0].rm_so, 251232153Smm matches[0].rm_eo - matches[0].rm_so); 252228753Smm j = i + 1; 253228753Smm continue; 254228753Smm } 255228753Smm if (rule->result[i] != '\\') 256228753Smm continue; 257228753Smm 258228753Smm ++i; 259228753Smm c = rule->result[i]; 260228753Smm switch (c) { 261228753Smm case '~': 262228753Smm case '\\': 263228753Smm realloc_strncat(result, rule->result + j, i - j - 1); 264228753Smm j = i; 265228753Smm break; 266228753Smm case '1': 267228753Smm case '2': 268228753Smm case '3': 269228753Smm case '4': 270228753Smm case '5': 271228753Smm case '6': 272228753Smm case '7': 273228753Smm case '8': 274228753Smm case '9': 275228753Smm realloc_strncat(result, rule->result + j, i - j - 1); 276228753Smm if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { 277228753Smm free(*result); 278228753Smm *result = NULL; 279228753Smm return -1; 280228753Smm } 281228753Smm realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); 282228753Smm j = i + 1; 283228753Smm break; 284228753Smm default: 285228753Smm /* Just continue; */ 286228753Smm break; 287228753Smm } 288228753Smm 289228753Smm } 290228753Smm 291228753Smm realloc_strcat(result, rule->result + j); 292228753Smm 293228753Smm name += matches[0].rm_eo; 294228753Smm 295228753Smm if (!rule->global) 296228753Smm break; 297228753Smm } 298228753Smm 299228753Smm if (got_match) 300228753Smm realloc_strcat(result, name); 301228753Smm 302228753Smm if (print_match) 303228753Smm fprintf(stderr, "%s >> %s\n", path, *result); 304228753Smm 305228753Smm return got_match; 306228753Smm} 307228753Smm 308228753Smmvoid 309228753Smmcleanup_substitution(struct bsdtar *bsdtar) 310228753Smm{ 311228753Smm struct subst_rule *rule; 312228753Smm struct substitution *subst; 313228753Smm 314228753Smm if ((subst = bsdtar->substitution) == NULL) 315228753Smm return; 316228753Smm 317228753Smm while ((rule = subst->first_rule) != NULL) { 318228753Smm subst->first_rule = rule->next; 319228753Smm free(rule->result); 320228753Smm free(rule); 321228753Smm } 322228753Smm free(subst); 323228753Smm} 324248616Smm#endif /* defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) */ 325