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