1caf54c4fSMartin Matuska /*- 2*bd66c1b4SMartin Matuska * SPDX-License-Identifier: BSD-2-Clause 3*bd66c1b4SMartin Matuska * 4caf54c4fSMartin Matuska * Copyright (c) 2008 Joerg Sonnenberger 5caf54c4fSMartin Matuska * All rights reserved. 6caf54c4fSMartin Matuska */ 7caf54c4fSMartin Matuska 8caf54c4fSMartin Matuska #include "bsdtar_platform.h" 9caf54c4fSMartin Matuska 10b9128a37SMartin Matuska #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H) 11caf54c4fSMartin Matuska #include "bsdtar.h" 12caf54c4fSMartin Matuska 13caf54c4fSMartin Matuska #include <errno.h> 14b9128a37SMartin Matuska #if defined(HAVE_PCREPOSIX_H) 15acc60b03SMartin Matuska #include <pcreposix.h> 16b9128a37SMartin Matuska #elif defined(HAVE_PCRE2POSIX_H) 17b9128a37SMartin Matuska #include <pcre2posix.h> 18acc60b03SMartin Matuska #else 19caf54c4fSMartin Matuska #include <regex.h> 20acc60b03SMartin Matuska #endif 21caf54c4fSMartin Matuska #include <stdlib.h> 22caf54c4fSMartin Matuska #include <string.h> 23caf54c4fSMartin Matuska 24caf54c4fSMartin Matuska #ifndef REG_BASIC 25caf54c4fSMartin Matuska #define REG_BASIC 0 26caf54c4fSMartin Matuska #endif 27caf54c4fSMartin Matuska 28caf54c4fSMartin Matuska #include "err.h" 29caf54c4fSMartin Matuska 30caf54c4fSMartin Matuska struct subst_rule { 31caf54c4fSMartin Matuska struct subst_rule *next; 32caf54c4fSMartin Matuska regex_t re; 33caf54c4fSMartin Matuska char *result; 34b9128a37SMartin Matuska unsigned int global:1, print:1, regular:1, symlink:1, hardlink:1, from_begin:1; 35caf54c4fSMartin Matuska }; 36caf54c4fSMartin Matuska 37caf54c4fSMartin Matuska struct substitution { 38caf54c4fSMartin Matuska struct subst_rule *first_rule, *last_rule; 39caf54c4fSMartin Matuska }; 40caf54c4fSMartin Matuska 41caf54c4fSMartin Matuska static void 42caf54c4fSMartin Matuska init_substitution(struct bsdtar *bsdtar) 43caf54c4fSMartin Matuska { 44caf54c4fSMartin Matuska struct substitution *subst; 45caf54c4fSMartin Matuska 46caf54c4fSMartin Matuska bsdtar->substitution = subst = malloc(sizeof(*subst)); 47caf54c4fSMartin Matuska if (subst == NULL) 48caf54c4fSMartin Matuska lafe_errc(1, errno, "Out of memory"); 49caf54c4fSMartin Matuska subst->first_rule = subst->last_rule = NULL; 50caf54c4fSMartin Matuska } 51caf54c4fSMartin Matuska 52caf54c4fSMartin Matuska void 53caf54c4fSMartin Matuska add_substitution(struct bsdtar *bsdtar, const char *rule_text) 54caf54c4fSMartin Matuska { 55caf54c4fSMartin Matuska struct subst_rule *rule; 56caf54c4fSMartin Matuska struct substitution *subst; 57caf54c4fSMartin Matuska const char *end_pattern, *start_subst; 58caf54c4fSMartin Matuska char *pattern; 59caf54c4fSMartin Matuska int r; 60caf54c4fSMartin Matuska 61caf54c4fSMartin Matuska if ((subst = bsdtar->substitution) == NULL) { 62caf54c4fSMartin Matuska init_substitution(bsdtar); 63caf54c4fSMartin Matuska subst = bsdtar->substitution; 64caf54c4fSMartin Matuska } 65caf54c4fSMartin Matuska 66caf54c4fSMartin Matuska rule = malloc(sizeof(*rule)); 67caf54c4fSMartin Matuska if (rule == NULL) 68caf54c4fSMartin Matuska lafe_errc(1, errno, "Out of memory"); 69caf54c4fSMartin Matuska rule->next = NULL; 70f3e9b21aSMartin Matuska rule->result = NULL; 71caf54c4fSMartin Matuska 72caf54c4fSMartin Matuska if (subst->last_rule == NULL) 73caf54c4fSMartin Matuska subst->first_rule = rule; 74caf54c4fSMartin Matuska else 75caf54c4fSMartin Matuska subst->last_rule->next = rule; 76caf54c4fSMartin Matuska subst->last_rule = rule; 77caf54c4fSMartin Matuska 78caf54c4fSMartin Matuska if (*rule_text == '\0') 79caf54c4fSMartin Matuska lafe_errc(1, 0, "Empty replacement string"); 80caf54c4fSMartin Matuska end_pattern = strchr(rule_text + 1, *rule_text); 81caf54c4fSMartin Matuska if (end_pattern == NULL) 82caf54c4fSMartin Matuska lafe_errc(1, 0, "Invalid replacement string"); 83caf54c4fSMartin Matuska 84caf54c4fSMartin Matuska pattern = malloc(end_pattern - rule_text); 85caf54c4fSMartin Matuska if (pattern == NULL) 86caf54c4fSMartin Matuska lafe_errc(1, errno, "Out of memory"); 87caf54c4fSMartin Matuska memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1); 88caf54c4fSMartin Matuska pattern[end_pattern - rule_text - 1] = '\0'; 89caf54c4fSMartin Matuska 90caf54c4fSMartin Matuska if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) { 91caf54c4fSMartin Matuska char buf[80]; 92caf54c4fSMartin Matuska regerror(r, &rule->re, buf, sizeof(buf)); 93caf54c4fSMartin Matuska lafe_errc(1, 0, "Invalid regular expression: %s", buf); 94caf54c4fSMartin Matuska } 95caf54c4fSMartin Matuska free(pattern); 96caf54c4fSMartin Matuska 97caf54c4fSMartin Matuska start_subst = end_pattern + 1; 98caf54c4fSMartin Matuska end_pattern = strchr(start_subst, *rule_text); 99caf54c4fSMartin Matuska if (end_pattern == NULL) 100caf54c4fSMartin Matuska lafe_errc(1, 0, "Invalid replacement string"); 101caf54c4fSMartin Matuska 102caf54c4fSMartin Matuska rule->result = malloc(end_pattern - start_subst + 1); 103caf54c4fSMartin Matuska if (rule->result == NULL) 104caf54c4fSMartin Matuska lafe_errc(1, errno, "Out of memory"); 105caf54c4fSMartin Matuska memcpy(rule->result, start_subst, end_pattern - start_subst); 106caf54c4fSMartin Matuska rule->result[end_pattern - start_subst] = '\0'; 107caf54c4fSMartin Matuska 1086c95142eSMartin Matuska /* Defaults */ 1096c95142eSMartin Matuska rule->global = 0; /* Don't do multiple replacements. */ 1106c95142eSMartin Matuska rule->print = 0; /* Don't print. */ 1116c95142eSMartin Matuska rule->regular = 1; /* Rewrite regular filenames. */ 1126c95142eSMartin Matuska rule->symlink = 1; /* Rewrite symlink targets. */ 1136c95142eSMartin Matuska rule->hardlink = 1; /* Rewrite hardlink targets. */ 114b9128a37SMartin Matuska rule->from_begin = 0; /* Don't match from start. */ 115caf54c4fSMartin Matuska 116caf54c4fSMartin Matuska while (*++end_pattern) { 117caf54c4fSMartin Matuska switch (*end_pattern) { 118b9128a37SMartin Matuska case 'b': 119b9128a37SMartin Matuska case 'B': 120b9128a37SMartin Matuska rule->from_begin = 1; 121b9128a37SMartin Matuska break; 122caf54c4fSMartin Matuska case 'g': 123caf54c4fSMartin Matuska case 'G': 124caf54c4fSMartin Matuska rule->global = 1; 125caf54c4fSMartin Matuska break; 1266c95142eSMartin Matuska case 'h': 1276c95142eSMartin Matuska rule->hardlink = 1; 1286c95142eSMartin Matuska break; 1296c95142eSMartin Matuska case 'H': 1306c95142eSMartin Matuska rule->hardlink = 0; 1316c95142eSMartin Matuska break; 132caf54c4fSMartin Matuska case 'p': 133caf54c4fSMartin Matuska case 'P': 134caf54c4fSMartin Matuska rule->print = 1; 135caf54c4fSMartin Matuska break; 1366c95142eSMartin Matuska case 'r': 1376c95142eSMartin Matuska rule->regular = 1; 1386c95142eSMartin Matuska break; 1396c95142eSMartin Matuska case 'R': 1406c95142eSMartin Matuska rule->regular = 0; 1416c95142eSMartin Matuska break; 142caf54c4fSMartin Matuska case 's': 143caf54c4fSMartin Matuska rule->symlink = 1; 144caf54c4fSMartin Matuska break; 1456c95142eSMartin Matuska case 'S': 1466c95142eSMartin Matuska rule->symlink = 0; 1476c95142eSMartin Matuska break; 148caf54c4fSMartin Matuska default: 149caf54c4fSMartin Matuska lafe_errc(1, 0, "Invalid replacement flag %c", *end_pattern); 150b9128a37SMartin Matuska /* NOTREACHED */ 151caf54c4fSMartin Matuska } 152caf54c4fSMartin Matuska } 153caf54c4fSMartin Matuska } 154caf54c4fSMartin Matuska 155caf54c4fSMartin Matuska static void 156caf54c4fSMartin Matuska realloc_strncat(char **str, const char *append, size_t len) 157caf54c4fSMartin Matuska { 158caf54c4fSMartin Matuska char *new_str; 159caf54c4fSMartin Matuska size_t old_len; 160caf54c4fSMartin Matuska 161caf54c4fSMartin Matuska if (*str == NULL) 162caf54c4fSMartin Matuska old_len = 0; 163caf54c4fSMartin Matuska else 164caf54c4fSMartin Matuska old_len = strlen(*str); 165caf54c4fSMartin Matuska 166caf54c4fSMartin Matuska new_str = malloc(old_len + len + 1); 167caf54c4fSMartin Matuska if (new_str == NULL) 168caf54c4fSMartin Matuska lafe_errc(1, errno, "Out of memory"); 1696c95142eSMartin Matuska if (*str != NULL) 170caf54c4fSMartin Matuska memcpy(new_str, *str, old_len); 171caf54c4fSMartin Matuska memcpy(new_str + old_len, append, len); 172caf54c4fSMartin Matuska new_str[old_len + len] = '\0'; 173caf54c4fSMartin Matuska free(*str); 174caf54c4fSMartin Matuska *str = new_str; 175caf54c4fSMartin Matuska } 176caf54c4fSMartin Matuska 177caf54c4fSMartin Matuska static void 178caf54c4fSMartin Matuska realloc_strcat(char **str, const char *append) 179caf54c4fSMartin Matuska { 180caf54c4fSMartin Matuska char *new_str; 181caf54c4fSMartin Matuska size_t old_len; 182caf54c4fSMartin Matuska 183caf54c4fSMartin Matuska if (*str == NULL) 184caf54c4fSMartin Matuska old_len = 0; 185caf54c4fSMartin Matuska else 186caf54c4fSMartin Matuska old_len = strlen(*str); 187caf54c4fSMartin Matuska 188caf54c4fSMartin Matuska new_str = malloc(old_len + strlen(append) + 1); 189caf54c4fSMartin Matuska if (new_str == NULL) 190caf54c4fSMartin Matuska lafe_errc(1, errno, "Out of memory"); 1916c95142eSMartin Matuska if (*str != NULL) 192caf54c4fSMartin Matuska memcpy(new_str, *str, old_len); 193caf54c4fSMartin Matuska strcpy(new_str + old_len, append); 194caf54c4fSMartin Matuska free(*str); 195caf54c4fSMartin Matuska *str = new_str; 196caf54c4fSMartin Matuska } 197caf54c4fSMartin Matuska 198caf54c4fSMartin Matuska int 1996c95142eSMartin Matuska apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, 2006c95142eSMartin Matuska int symlink_target, int hardlink_target) 201caf54c4fSMartin Matuska { 202caf54c4fSMartin Matuska const char *path = name; 203caf54c4fSMartin Matuska regmatch_t matches[10]; 204b9128a37SMartin Matuska char* buffer = NULL; 205caf54c4fSMartin Matuska size_t i, j; 206caf54c4fSMartin Matuska struct subst_rule *rule; 207caf54c4fSMartin Matuska struct substitution *subst; 208caf54c4fSMartin Matuska int c, got_match, print_match; 209caf54c4fSMartin Matuska 210caf54c4fSMartin Matuska *result = NULL; 211caf54c4fSMartin Matuska 212caf54c4fSMartin Matuska if ((subst = bsdtar->substitution) == NULL) 213caf54c4fSMartin Matuska return 0; 214caf54c4fSMartin Matuska 215caf54c4fSMartin Matuska got_match = 0; 216caf54c4fSMartin Matuska print_match = 0; 217caf54c4fSMartin Matuska 218caf54c4fSMartin Matuska for (rule = subst->first_rule; rule != NULL; rule = rule->next) { 2196c95142eSMartin Matuska if (symlink_target) { 2206c95142eSMartin Matuska if (!rule->symlink) 221caf54c4fSMartin Matuska continue; 2226c95142eSMartin Matuska } else if (hardlink_target) { 2236c95142eSMartin Matuska if (!rule->hardlink) 2246c95142eSMartin Matuska continue; 2256c95142eSMartin Matuska } else { /* Regular filename. */ 2266c95142eSMartin Matuska if (!rule->regular) 2276c95142eSMartin Matuska continue; 2286c95142eSMartin Matuska } 2296c95142eSMartin Matuska 230b9128a37SMartin Matuska if (rule->from_begin && *result) { 231b9128a37SMartin Matuska realloc_strcat(result, name); 232b9128a37SMartin Matuska realloc_strcat(&buffer, *result); 233b9128a37SMartin Matuska name = buffer; 234b9128a37SMartin Matuska (*result)[0] = 0; 235b9128a37SMartin Matuska } 236b9128a37SMartin Matuska 237cdf63a70SMartin Matuska while (1) { 238caf54c4fSMartin Matuska if (regexec(&rule->re, name, 10, matches, 0)) 239cdf63a70SMartin Matuska break; 240caf54c4fSMartin Matuska 241caf54c4fSMartin Matuska got_match = 1; 242caf54c4fSMartin Matuska print_match |= rule->print; 243caf54c4fSMartin Matuska realloc_strncat(result, name, matches[0].rm_so); 244caf54c4fSMartin Matuska 245caf54c4fSMartin Matuska for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { 246caf54c4fSMartin Matuska if (rule->result[i] == '~') { 247caf54c4fSMartin Matuska realloc_strncat(result, rule->result + j, i - j); 2486c95142eSMartin Matuska realloc_strncat(result, 2496c95142eSMartin Matuska name + matches[0].rm_so, 2506c95142eSMartin Matuska matches[0].rm_eo - matches[0].rm_so); 251caf54c4fSMartin Matuska j = i + 1; 252caf54c4fSMartin Matuska continue; 253caf54c4fSMartin Matuska } 254caf54c4fSMartin Matuska if (rule->result[i] != '\\') 255caf54c4fSMartin Matuska continue; 256caf54c4fSMartin Matuska 257caf54c4fSMartin Matuska ++i; 258caf54c4fSMartin Matuska c = rule->result[i]; 259caf54c4fSMartin Matuska switch (c) { 260caf54c4fSMartin Matuska case '~': 261caf54c4fSMartin Matuska case '\\': 262caf54c4fSMartin Matuska realloc_strncat(result, rule->result + j, i - j - 1); 263caf54c4fSMartin Matuska j = i; 264caf54c4fSMartin Matuska break; 265caf54c4fSMartin Matuska case '1': 266caf54c4fSMartin Matuska case '2': 267caf54c4fSMartin Matuska case '3': 268caf54c4fSMartin Matuska case '4': 269caf54c4fSMartin Matuska case '5': 270caf54c4fSMartin Matuska case '6': 271caf54c4fSMartin Matuska case '7': 272caf54c4fSMartin Matuska case '8': 273caf54c4fSMartin Matuska case '9': 274caf54c4fSMartin Matuska realloc_strncat(result, rule->result + j, i - j - 1); 275caf54c4fSMartin Matuska if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { 276b9128a37SMartin Matuska free(buffer); 277caf54c4fSMartin Matuska free(*result); 278caf54c4fSMartin Matuska *result = NULL; 279caf54c4fSMartin Matuska return -1; 280caf54c4fSMartin Matuska } 281caf54c4fSMartin Matuska realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); 282caf54c4fSMartin Matuska j = i + 1; 283caf54c4fSMartin Matuska break; 284caf54c4fSMartin Matuska default: 285caf54c4fSMartin Matuska /* Just continue; */ 286caf54c4fSMartin Matuska break; 287caf54c4fSMartin Matuska } 288caf54c4fSMartin Matuska 289caf54c4fSMartin Matuska } 290caf54c4fSMartin Matuska 291caf54c4fSMartin Matuska realloc_strcat(result, rule->result + j); 292caf54c4fSMartin Matuska 293caf54c4fSMartin Matuska name += matches[0].rm_eo; 294caf54c4fSMartin Matuska 295caf54c4fSMartin Matuska if (!rule->global) 296caf54c4fSMartin Matuska break; 297caf54c4fSMartin Matuska } 298cdf63a70SMartin Matuska } 299caf54c4fSMartin Matuska 300caf54c4fSMartin Matuska if (got_match) 301caf54c4fSMartin Matuska realloc_strcat(result, name); 302caf54c4fSMartin Matuska 303b9128a37SMartin Matuska free(buffer); 304b9128a37SMartin Matuska 305caf54c4fSMartin Matuska if (print_match) 306caf54c4fSMartin Matuska fprintf(stderr, "%s >> %s\n", path, *result); 307caf54c4fSMartin Matuska 308caf54c4fSMartin Matuska return got_match; 309caf54c4fSMartin Matuska } 310caf54c4fSMartin Matuska 311caf54c4fSMartin Matuska void 312caf54c4fSMartin Matuska cleanup_substitution(struct bsdtar *bsdtar) 313caf54c4fSMartin Matuska { 314caf54c4fSMartin Matuska struct subst_rule *rule; 315caf54c4fSMartin Matuska struct substitution *subst; 316caf54c4fSMartin Matuska 317caf54c4fSMartin Matuska if ((subst = bsdtar->substitution) == NULL) 318caf54c4fSMartin Matuska return; 319caf54c4fSMartin Matuska 320caf54c4fSMartin Matuska while ((rule = subst->first_rule) != NULL) { 321caf54c4fSMartin Matuska subst->first_rule = rule->next; 322caf54c4fSMartin Matuska free(rule->result); 323bd5e624aSMartin Matuska regfree(&rule->re); 324caf54c4fSMartin Matuska free(rule); 325caf54c4fSMartin Matuska } 326caf54c4fSMartin Matuska free(subst); 327caf54c4fSMartin Matuska } 328b9128a37SMartin Matuska #endif /* defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) || defined(HAVE_PCRE2POSIX_H) */ 329