1 /* exclude.c -- exclude file names 2 3 Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003 Free 4 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 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 20 21 /* Written by Paul Eggert <eggert@twinsun.com> */ 22 23 #if HAVE_CONFIG_H 24 # include <config.h> 25 #endif 26 27 #include <stdbool.h> 28 29 #include <ctype.h> 30 #include <errno.h> 31 #ifndef errno 32 extern int errno; 33 #endif 34 #include <stddef.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include "exclude.h" 40 #include "fnmatch.h" 41 #include "unlocked-io.h" 42 #include "xalloc.h" 43 44 #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII) 45 # define IN_CTYPE_DOMAIN(c) true 46 #else 47 # define IN_CTYPE_DOMAIN(c) isascii (c) 48 #endif 49 50 static inline bool 51 is_space (unsigned char c) 52 { 53 return IN_CTYPE_DOMAIN (c) && isspace (c); 54 } 55 56 /* Verify a requirement at compile-time (unlike assert, which is runtime). */ 57 #define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } 58 59 /* Non-GNU systems lack these options, so we don't need to check them. */ 60 #ifndef FNM_CASEFOLD 61 # define FNM_CASEFOLD 0 62 #endif 63 #ifndef FNM_LEADING_DIR 64 # define FNM_LEADING_DIR 0 65 #endif 66 67 verify (EXCLUDE_macros_do_not_collide_with_FNM_macros, 68 (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS) 69 & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR 70 | FNM_CASEFOLD)) 71 == 0)); 72 73 /* An exclude pattern-options pair. The options are fnmatch options 74 ORed with EXCLUDE_* options. */ 75 76 struct patopts 77 { 78 char const *pattern; 79 int options; 80 }; 81 82 /* An exclude list, of pattern-options pairs. */ 83 84 struct exclude 85 { 86 struct patopts *exclude; 87 size_t exclude_alloc; 88 size_t exclude_count; 89 }; 90 91 /* Return a newly allocated and empty exclude list. */ 92 93 struct exclude * 94 new_exclude (void) 95 { 96 return xzalloc (sizeof *new_exclude ()); 97 } 98 99 /* Free the storage associated with an exclude list. */ 100 101 void 102 free_exclude (struct exclude *ex) 103 { 104 free (ex->exclude); 105 free (ex); 106 } 107 108 /* Return zero if PATTERN matches F, obeying OPTIONS, except that 109 (unlike fnmatch) wildcards are disabled in PATTERN. */ 110 111 static int 112 fnmatch_no_wildcards (char const *pattern, char const *f, int options) 113 { 114 if (! (options & FNM_LEADING_DIR)) 115 return ((options & FNM_CASEFOLD) 116 ? strcasecmp (pattern, f) 117 : strcmp (pattern, f)); 118 else 119 { 120 size_t patlen = strlen (pattern); 121 int r = ((options & FNM_CASEFOLD) 122 ? strncasecmp (pattern, f, patlen) 123 : strncmp (pattern, f, patlen)); 124 if (! r) 125 { 126 r = f[patlen]; 127 if (r == '/') 128 r = 0; 129 } 130 return r; 131 } 132 } 133 134 /* Return true if EX excludes F. */ 135 136 bool 137 excluded_filename (struct exclude const *ex, char const *f) 138 { 139 size_t exclude_count = ex->exclude_count; 140 141 /* If no options are given, the default is to include. */ 142 if (exclude_count == 0) 143 return false; 144 else 145 { 146 struct patopts const *exclude = ex->exclude; 147 size_t i; 148 149 /* Otherwise, the default is the opposite of the first option. */ 150 bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE); 151 152 /* Scan through the options, seeing whether they change F from 153 excluded to included or vice versa. */ 154 for (i = 0; i < exclude_count; i++) 155 { 156 char const *pattern = exclude[i].pattern; 157 int options = exclude[i].options; 158 if (excluded == !! (options & EXCLUDE_INCLUDE)) 159 { 160 int (*matcher) (char const *, char const *, int) = 161 (options & EXCLUDE_WILDCARDS 162 ? fnmatch 163 : fnmatch_no_wildcards); 164 bool matched = ((*matcher) (pattern, f, options) == 0); 165 char const *p; 166 167 if (! (options & EXCLUDE_ANCHORED)) 168 for (p = f; *p && ! matched; p++) 169 if (*p == '/' && p[1] != '/') 170 matched = ((*matcher) (pattern, p + 1, options) == 0); 171 172 excluded ^= matched; 173 } 174 } 175 176 return excluded; 177 } 178 } 179 180 /* Append to EX the exclusion PATTERN with OPTIONS. */ 181 182 void 183 add_exclude (struct exclude *ex, char const *pattern, int options) 184 { 185 struct patopts *patopts; 186 187 if (ex->exclude_count == ex->exclude_alloc) 188 ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc, 189 sizeof *ex->exclude); 190 191 patopts = &ex->exclude[ex->exclude_count++]; 192 patopts->pattern = pattern; 193 patopts->options = options; 194 } 195 196 /* Use ADD_FUNC to append to EX the patterns in FILENAME, each with 197 OPTIONS. LINE_END terminates each pattern in the file. If 198 LINE_END is a space character, ignore trailing spaces and empty 199 lines in FILE. Return -1 on failure, 0 on success. */ 200 201 int 202 add_exclude_file (void (*add_func) (struct exclude *, char const *, int), 203 struct exclude *ex, char const *filename, int options, 204 char line_end) 205 { 206 bool use_stdin = filename[0] == '-' && !filename[1]; 207 FILE *in; 208 char *buf = NULL; 209 char *p; 210 char const *pattern; 211 char const *lim; 212 size_t buf_alloc = 0; 213 size_t buf_count = 0; 214 int c; 215 int e = 0; 216 217 if (use_stdin) 218 in = stdin; 219 else if (! (in = fopen (filename, "r"))) 220 return -1; 221 222 while ((c = getc (in)) != EOF) 223 { 224 if (buf_count == buf_alloc) 225 buf = x2realloc (buf, &buf_alloc); 226 buf[buf_count++] = c; 227 } 228 229 if (ferror (in)) 230 e = errno; 231 232 if (!use_stdin && fclose (in) != 0) 233 e = errno; 234 235 buf = xrealloc (buf, buf_count + 1); 236 buf[buf_count] = line_end; 237 lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end); 238 pattern = buf; 239 240 for (p = buf; p < lim; p++) 241 if (*p == line_end) 242 { 243 char *pattern_end = p; 244 245 if (is_space (line_end)) 246 { 247 for (; ; pattern_end--) 248 if (pattern_end == pattern) 249 goto next_pattern; 250 else if (! is_space (pattern_end[-1])) 251 break; 252 } 253 254 *pattern_end = '\0'; 255 (*add_func) (ex, pattern, options); 256 257 next_pattern: 258 pattern = p + 1; 259 } 260 261 errno = e; 262 return e ? -1 : 0; 263 } 264