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