1/* exclude.c -- exclude file names 2 3 Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003, 4 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; see the file COPYING. 18 If not, write to the Free Software Foundation, 19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 20 21/* Written by Paul Eggert <eggert@twinsun.com> */ 22 23#include <config.h> 24 25#include <stdbool.h> 26 27#include <ctype.h> 28#include <errno.h> 29#include <stddef.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33 34#include "exclude.h" 35#include "fnmatch.h" 36#include "xalloc.h" 37#include "verify.h" 38 39#if USE_UNLOCKED_IO 40# include "unlocked-io.h" 41#endif 42 43/* Non-GNU systems lack these options, so we don't need to check them. */ 44#ifndef FNM_CASEFOLD 45# define FNM_CASEFOLD 0 46#endif 47#ifndef FNM_EXTMATCH 48# define FNM_EXTMATCH 0 49#endif 50#ifndef FNM_LEADING_DIR 51# define FNM_LEADING_DIR 0 52#endif 53 54verify (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS) 55 & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR 56 | FNM_CASEFOLD | FNM_EXTMATCH)) 57 == 0); 58 59/* An exclude pattern-options pair. The options are fnmatch options 60 ORed with EXCLUDE_* options. */ 61 62struct patopts 63 { 64 char const *pattern; 65 int options; 66 }; 67 68/* An exclude list, of pattern-options pairs. */ 69 70struct exclude 71 { 72 struct patopts *exclude; 73 size_t exclude_alloc; 74 size_t exclude_count; 75 }; 76 77/* Return a newly allocated and empty exclude list. */ 78 79struct exclude * 80new_exclude (void) 81{ 82 return xzalloc (sizeof *new_exclude ()); 83} 84 85/* Free the storage associated with an exclude list. */ 86 87void 88free_exclude (struct exclude *ex) 89{ 90 free (ex->exclude); 91 free (ex); 92} 93 94/* Return zero if PATTERN matches F, obeying OPTIONS, except that 95 (unlike fnmatch) wildcards are disabled in PATTERN. */ 96 97static int 98fnmatch_no_wildcards (char const *pattern, char const *f, int options) 99{ 100 if (! (options & FNM_LEADING_DIR)) 101 return ((options & FNM_CASEFOLD) 102 ? mbscasecmp (pattern, f) 103 : strcmp (pattern, f)); 104 else if (! (options & FNM_CASEFOLD)) 105 { 106 size_t patlen = strlen (pattern); 107 int r = strncmp (pattern, f, patlen); 108 if (! r) 109 { 110 r = f[patlen]; 111 if (r == '/') 112 r = 0; 113 } 114 return r; 115 } 116 else 117 { 118 /* Walk through a copy of F, seeing whether P matches any prefix 119 of F. 120 121 FIXME: This is an O(N**2) algorithm; it should be O(N). 122 Also, the copy should not be necessary. However, fixing this 123 will probably involve a change to the mbs* API. */ 124 125 char *fcopy = xstrdup (f); 126 char *p; 127 int r; 128 for (p = fcopy; ; *p++ = '/') 129 { 130 p = strchr (p, '/'); 131 if (p) 132 *p = '\0'; 133 r = mbscasecmp (pattern, fcopy); 134 if (!p || r <= 0) 135 break; 136 } 137 free (fcopy); 138 return r; 139 } 140} 141 142bool 143exclude_fnmatch (char const *pattern, char const *f, int options) 144{ 145 int (*matcher) (char const *, char const *, int) = 146 (options & EXCLUDE_WILDCARDS 147 ? fnmatch 148 : fnmatch_no_wildcards); 149 bool matched = ((*matcher) (pattern, f, options) == 0); 150 char const *p; 151 152 if (! (options & EXCLUDE_ANCHORED)) 153 for (p = f; *p && ! matched; p++) 154 if (*p == '/' && p[1] != '/') 155 matched = ((*matcher) (pattern, p + 1, options) == 0); 156 157 return matched; 158} 159 160/* Return true if EX excludes F. */ 161 162bool 163excluded_file_name (struct exclude const *ex, char const *f) 164{ 165 size_t exclude_count = ex->exclude_count; 166 167 /* If no options are given, the default is to include. */ 168 if (exclude_count == 0) 169 return false; 170 else 171 { 172 struct patopts const *exclude = ex->exclude; 173 size_t i; 174 175 /* Otherwise, the default is the opposite of the first option. */ 176 bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE); 177 178 /* Scan through the options, seeing whether they change F from 179 excluded to included or vice versa. */ 180 for (i = 0; i < exclude_count; i++) 181 { 182 char const *pattern = exclude[i].pattern; 183 int options = exclude[i].options; 184 if (excluded == !! (options & EXCLUDE_INCLUDE)) 185 excluded ^= exclude_fnmatch (pattern, f, options); 186 } 187 188 return excluded; 189 } 190} 191 192/* Append to EX the exclusion PATTERN with OPTIONS. */ 193 194void 195add_exclude (struct exclude *ex, char const *pattern, int options) 196{ 197 struct patopts *patopts; 198 199 if (ex->exclude_count == ex->exclude_alloc) 200 ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc, 201 sizeof *ex->exclude); 202 203 patopts = &ex->exclude[ex->exclude_count++]; 204 patopts->pattern = pattern; 205 patopts->options = options; 206} 207 208/* Use ADD_FUNC to append to EX the patterns in FILE_NAME, each with 209 OPTIONS. LINE_END terminates each pattern in the file. If 210 LINE_END is a space character, ignore trailing spaces and empty 211 lines in FILE. Return -1 on failure, 0 on success. */ 212 213int 214add_exclude_file (void (*add_func) (struct exclude *, char const *, int), 215 struct exclude *ex, char const *file_name, int options, 216 char line_end) 217{ 218 bool use_stdin = file_name[0] == '-' && !file_name[1]; 219 FILE *in; 220 char *buf = NULL; 221 char *p; 222 char const *pattern; 223 char const *lim; 224 size_t buf_alloc = 0; 225 size_t buf_count = 0; 226 int c; 227 int e = 0; 228 229 if (use_stdin) 230 in = stdin; 231 else if (! (in = fopen (file_name, "r"))) 232 return -1; 233 234 while ((c = getc (in)) != EOF) 235 { 236 if (buf_count == buf_alloc) 237 buf = x2realloc (buf, &buf_alloc); 238 buf[buf_count++] = c; 239 } 240 241 if (ferror (in)) 242 e = errno; 243 244 if (!use_stdin && fclose (in) != 0) 245 e = errno; 246 247 buf = xrealloc (buf, buf_count + 1); 248 buf[buf_count] = line_end; 249 lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end); 250 pattern = buf; 251 252 for (p = buf; p < lim; p++) 253 if (*p == line_end) 254 { 255 char *pattern_end = p; 256 257 if (isspace ((unsigned char) line_end)) 258 { 259 for (; ; pattern_end--) 260 if (pattern_end == pattern) 261 goto next_pattern; 262 else if (! isspace ((unsigned char) pattern_end[-1])) 263 break; 264 } 265 266 *pattern_end = '\0'; 267 (*add_func) (ex, pattern, options); 268 269 next_pattern: 270 pattern = p + 1; 271 } 272 273 errno = e; 274 return e ? -1 : 0; 275} 276