1/*- 2 * Copyright (c) 2003-2007 Tim Kientzle 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 "lafe_platform.h" 27__FBSDID("$FreeBSD: src/usr.bin/cpio/matching.c,v 1.2 2008/06/21 02:20:20 kientzle Exp $"); 28 29#ifdef HAVE_ERRNO_H 30#include <errno.h> 31#endif 32#ifdef HAVE_STDLIB_H 33#include <stdlib.h> 34#endif 35#ifdef HAVE_STRING_H 36#include <string.h> 37#endif 38 39#include "err.h" 40#include "line_reader.h" 41#include "matching.h" 42#include "pathmatch.h" 43 44struct match { 45 struct match *next; 46 int matches; 47 char pattern[1]; 48}; 49 50struct lafe_matching { 51 struct match *exclusions; 52 int exclusions_count; 53 struct match *inclusions; 54 int inclusions_count; 55 int inclusions_unmatched_count; 56}; 57 58static void add_pattern(struct match **list, const char *pattern); 59static void initialize_matching(struct lafe_matching **); 60static int match_exclusion(struct match *, const char *pathname); 61static int match_inclusion(struct match *, const char *pathname); 62 63/* 64 * The matching logic here needs to be re-thought. I started out to 65 * try to mimic gtar's matching logic, but it's not entirely 66 * consistent. In particular 'tar -t' and 'tar -x' interpret patterns 67 * on the command line as anchored, but --exclude doesn't. 68 */ 69 70/* 71 * Utility functions to manage exclusion/inclusion patterns 72 */ 73 74int 75lafe_exclude(struct lafe_matching **matching, const char *pattern) 76{ 77 78 if (*matching == NULL) 79 initialize_matching(matching); 80 add_pattern(&((*matching)->exclusions), pattern); 81 (*matching)->exclusions_count++; 82 return (0); 83} 84 85int 86lafe_exclude_from_file(struct lafe_matching **matching, const char *pathname) 87{ 88 struct lafe_line_reader *lr; 89 const char *p; 90 int ret = 0; 91 92 lr = lafe_line_reader(pathname, 0); 93 while ((p = lafe_line_reader_next(lr)) != NULL) { 94 if (lafe_exclude(matching, p) != 0) 95 ret = -1; 96 } 97 lafe_line_reader_free(lr); 98 return (ret); 99} 100 101int 102lafe_include(struct lafe_matching **matching, const char *pattern) 103{ 104 105 if (*matching == NULL) 106 initialize_matching(matching); 107 add_pattern(&((*matching)->inclusions), pattern); 108 (*matching)->inclusions_count++; 109 (*matching)->inclusions_unmatched_count++; 110 return (0); 111} 112 113int 114lafe_include_from_file(struct lafe_matching **matching, const char *pathname, 115 int nullSeparator) 116{ 117 struct lafe_line_reader *lr; 118 const char *p; 119 int ret = 0; 120 121 lr = lafe_line_reader(pathname, nullSeparator); 122 while ((p = lafe_line_reader_next(lr)) != NULL) { 123 if (lafe_include(matching, p) != 0) 124 ret = -1; 125 } 126 lafe_line_reader_free(lr); 127 return (ret); 128} 129 130static void 131add_pattern(struct match **list, const char *pattern) 132{ 133 struct match *match; 134 size_t len; 135 136 len = strlen(pattern); 137 match = malloc(sizeof(*match) + len + 1); 138 if (match == NULL) 139 lafe_errc(1, errno, "Out of memory"); 140 strcpy(match->pattern, pattern); 141 /* Both "foo/" and "foo" should match "foo/bar". */ 142 if (len && match->pattern[len - 1] == '/') 143 match->pattern[strlen(match->pattern)-1] = '\0'; 144 match->next = *list; 145 *list = match; 146 match->matches = 0; 147} 148 149 150int 151lafe_excluded(struct lafe_matching *matching, const char *pathname) 152{ 153 struct match *match; 154 struct match *matched; 155 156 if (matching == NULL) 157 return (0); 158 159 /* Exclusions take priority */ 160 for (match = matching->exclusions; match != NULL; match = match->next){ 161 if (match_exclusion(match, pathname)) 162 return (1); 163 } 164 165 /* Then check for inclusions */ 166 matched = NULL; 167 for (match = matching->inclusions; match != NULL; match = match->next){ 168 if (match_inclusion(match, pathname)) { 169 /* 170 * If this pattern has never been matched, 171 * then we're done. 172 */ 173 if (match->matches == 0) { 174 match->matches++; 175 matching->inclusions_unmatched_count--; 176 return (0); 177 } 178 /* 179 * Otherwise, remember the match but keep checking 180 * in case we can tick off an unmatched pattern. 181 */ 182 matched = match; 183 } 184 } 185 /* 186 * We didn't find a pattern that had never been matched, but 187 * we did find a match, so count it and exit. 188 */ 189 if (matched != NULL) { 190 matched->matches++; 191 return (0); 192 } 193 194 /* If there were inclusions, default is to exclude. */ 195 if (matching->inclusions != NULL) 196 return (1); 197 198 /* No explicit inclusions, default is to match. */ 199 return (0); 200} 201 202/* 203 * This is a little odd, but it matches the default behavior of 204 * gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar' 205 * 206 */ 207static int 208match_exclusion(struct match *match, const char *pathname) 209{ 210 return (lafe_pathmatch(match->pattern, 211 pathname, 212 PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END)); 213} 214 215/* 216 * Again, mimic gtar: inclusions are always anchored (have to match 217 * the beginning of the path) even though exclusions are not anchored. 218 */ 219static int 220match_inclusion(struct match *match, const char *pathname) 221{ 222#if 0 223 return (lafe_pathmatch(match->pattern, pathname, 0)); 224#else 225 return (lafe_pathmatch(match->pattern, pathname, PATHMATCH_NO_ANCHOR_END)); 226#endif 227} 228 229void 230lafe_cleanup_exclusions(struct lafe_matching **matching) 231{ 232 struct match *p, *q; 233 234 if (*matching == NULL) 235 return; 236 237 for (p = (*matching)->inclusions; p != NULL; ) { 238 q = p; 239 p = p->next; 240 free(q); 241 } 242 243 for (p = (*matching)->exclusions; p != NULL; ) { 244 q = p; 245 p = p->next; 246 free(q); 247 } 248 249 free(*matching); 250 *matching = NULL; 251} 252 253static void 254initialize_matching(struct lafe_matching **matching) 255{ 256 *matching = calloc(sizeof(**matching), 1); 257 if (*matching == NULL) 258 lafe_errc(1, errno, "No memory"); 259} 260 261int 262lafe_unmatched_inclusions(struct lafe_matching *matching) 263{ 264 265 if (matching == NULL) 266 return (0); 267 return (matching->inclusions_unmatched_count); 268} 269 270int 271lafe_unmatched_inclusions_warn(struct lafe_matching *matching, const char *msg) 272{ 273 struct match *p; 274 275 if (matching == NULL) 276 return (0); 277 278 for (p = matching->inclusions; p != NULL; p = p->next) { 279 if (p->matches == 0) 280 lafe_warnc(0, "%s: %s", p->pattern, msg); 281 } 282 283 return (matching->inclusions_unmatched_count); 284} 285