1170754Sdelphij/* exclude.c -- exclude file names 2170754Sdelphij 3170754Sdelphij Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003 Free 4170754Sdelphij Software Foundation, Inc. 5170754Sdelphij 6170754Sdelphij This program is free software; you can redistribute it and/or modify 7170754Sdelphij it under the terms of the GNU General Public License as published by 8170754Sdelphij the Free Software Foundation; either version 2, or (at your option) 9170754Sdelphij any later version. 10170754Sdelphij 11170754Sdelphij This program is distributed in the hope that it will be useful, 12170754Sdelphij but WITHOUT ANY WARRANTY; without even the implied warranty of 13170754Sdelphij MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14170754Sdelphij GNU General Public License for more details. 15170754Sdelphij 16170754Sdelphij You should have received a copy of the GNU General Public License 17170754Sdelphij along with this program; see the file COPYING. 18170754Sdelphij If not, write to the Free Software Foundation, 19170754Sdelphij 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 20170754Sdelphij 21170754Sdelphij/* Written by Paul Eggert <eggert@twinsun.com> */ 22170754Sdelphij 23170754Sdelphij#if HAVE_CONFIG_H 24170754Sdelphij# include <config.h> 25170754Sdelphij#endif 26170754Sdelphij 27170754Sdelphij#include <stdbool.h> 28170754Sdelphij 29170754Sdelphij#include <ctype.h> 30170754Sdelphij#include <errno.h> 31170754Sdelphij#ifndef errno 32170754Sdelphijextern int errno; 33170754Sdelphij#endif 34170754Sdelphij#include <stddef.h> 35170754Sdelphij#include <stdio.h> 36170754Sdelphij#include <stdlib.h> 37170754Sdelphij#include <string.h> 38170754Sdelphij 39170754Sdelphij#include "exclude.h" 40170754Sdelphij#include "fnmatch.h" 41170754Sdelphij#include "unlocked-io.h" 42170754Sdelphij#include "xalloc.h" 43170754Sdelphij 44170754Sdelphij#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII) 45170754Sdelphij# define IN_CTYPE_DOMAIN(c) true 46170754Sdelphij#else 47170754Sdelphij# define IN_CTYPE_DOMAIN(c) isascii (c) 48170754Sdelphij#endif 49170754Sdelphij 50170754Sdelphijstatic inline bool 51170754Sdelphijis_space (unsigned char c) 52170754Sdelphij{ 53170754Sdelphij return IN_CTYPE_DOMAIN (c) && isspace (c); 54170754Sdelphij} 55170754Sdelphij 56170754Sdelphij/* Verify a requirement at compile-time (unlike assert, which is runtime). */ 57170754Sdelphij#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } 58170754Sdelphij 59170754Sdelphij/* Non-GNU systems lack these options, so we don't need to check them. */ 60170754Sdelphij#ifndef FNM_CASEFOLD 61170754Sdelphij# define FNM_CASEFOLD 0 62170754Sdelphij#endif 63170754Sdelphij#ifndef FNM_LEADING_DIR 64170754Sdelphij# define FNM_LEADING_DIR 0 65170754Sdelphij#endif 66170754Sdelphij 67170754Sdelphijverify (EXCLUDE_macros_do_not_collide_with_FNM_macros, 68170754Sdelphij (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS) 69170754Sdelphij & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR 70170754Sdelphij | FNM_CASEFOLD)) 71170754Sdelphij == 0)); 72170754Sdelphij 73170754Sdelphij/* An exclude pattern-options pair. The options are fnmatch options 74170754Sdelphij ORed with EXCLUDE_* options. */ 75170754Sdelphij 76170754Sdelphijstruct patopts 77170754Sdelphij { 78170754Sdelphij char const *pattern; 79170754Sdelphij int options; 80170754Sdelphij }; 81170754Sdelphij 82170754Sdelphij/* An exclude list, of pattern-options pairs. */ 83170754Sdelphij 84170754Sdelphijstruct exclude 85170754Sdelphij { 86170754Sdelphij struct patopts *exclude; 87170754Sdelphij size_t exclude_alloc; 88170754Sdelphij size_t exclude_count; 89170754Sdelphij }; 90170754Sdelphij 91170754Sdelphij/* Return a newly allocated and empty exclude list. */ 92170754Sdelphij 93170754Sdelphijstruct exclude * 94170754Sdelphijnew_exclude (void) 95170754Sdelphij{ 96170754Sdelphij return xzalloc (sizeof *new_exclude ()); 97170754Sdelphij} 98170754Sdelphij 99170754Sdelphij/* Free the storage associated with an exclude list. */ 100170754Sdelphij 101170754Sdelphijvoid 102170754Sdelphijfree_exclude (struct exclude *ex) 103170754Sdelphij{ 104170754Sdelphij free (ex->exclude); 105170754Sdelphij free (ex); 106170754Sdelphij} 107170754Sdelphij 108170754Sdelphij/* Return zero if PATTERN matches F, obeying OPTIONS, except that 109170754Sdelphij (unlike fnmatch) wildcards are disabled in PATTERN. */ 110170754Sdelphij 111170754Sdelphijstatic int 112170754Sdelphijfnmatch_no_wildcards (char const *pattern, char const *f, int options) 113170754Sdelphij{ 114170754Sdelphij if (! (options & FNM_LEADING_DIR)) 115170754Sdelphij return ((options & FNM_CASEFOLD) 116170754Sdelphij ? strcasecmp (pattern, f) 117170754Sdelphij : strcmp (pattern, f)); 118170754Sdelphij else 119170754Sdelphij { 120170754Sdelphij size_t patlen = strlen (pattern); 121170754Sdelphij int r = ((options & FNM_CASEFOLD) 122170754Sdelphij ? strncasecmp (pattern, f, patlen) 123170754Sdelphij : strncmp (pattern, f, patlen)); 124170754Sdelphij if (! r) 125170754Sdelphij { 126170754Sdelphij r = f[patlen]; 127170754Sdelphij if (r == '/') 128170754Sdelphij r = 0; 129170754Sdelphij } 130170754Sdelphij return r; 131170754Sdelphij } 132170754Sdelphij} 133170754Sdelphij 134170754Sdelphij/* Return true if EX excludes F. */ 135170754Sdelphij 136170754Sdelphijbool 137170754Sdelphijexcluded_filename (struct exclude const *ex, char const *f) 138170754Sdelphij{ 139170754Sdelphij size_t exclude_count = ex->exclude_count; 140170754Sdelphij 141170754Sdelphij /* If no options are given, the default is to include. */ 142170754Sdelphij if (exclude_count == 0) 143170754Sdelphij return false; 144170754Sdelphij else 145170754Sdelphij { 146170754Sdelphij struct patopts const *exclude = ex->exclude; 147170754Sdelphij size_t i; 148170754Sdelphij 149170754Sdelphij /* Otherwise, the default is the opposite of the first option. */ 150170754Sdelphij bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE); 151170754Sdelphij 152170754Sdelphij /* Scan through the options, seeing whether they change F from 153170754Sdelphij excluded to included or vice versa. */ 154170754Sdelphij for (i = 0; i < exclude_count; i++) 155170754Sdelphij { 156170754Sdelphij char const *pattern = exclude[i].pattern; 157170754Sdelphij int options = exclude[i].options; 158170754Sdelphij if (excluded == !! (options & EXCLUDE_INCLUDE)) 159170754Sdelphij { 160170754Sdelphij int (*matcher) (char const *, char const *, int) = 161170754Sdelphij (options & EXCLUDE_WILDCARDS 162170754Sdelphij ? fnmatch 163170754Sdelphij : fnmatch_no_wildcards); 164170754Sdelphij bool matched = ((*matcher) (pattern, f, options) == 0); 165170754Sdelphij char const *p; 166170754Sdelphij 167170754Sdelphij if (! (options & EXCLUDE_ANCHORED)) 168170754Sdelphij for (p = f; *p && ! matched; p++) 169170754Sdelphij if (*p == '/' && p[1] != '/') 170170754Sdelphij matched = ((*matcher) (pattern, p + 1, options) == 0); 171170754Sdelphij 172170754Sdelphij excluded ^= matched; 173170754Sdelphij } 174170754Sdelphij } 175170754Sdelphij 176170754Sdelphij return excluded; 177170754Sdelphij } 178170754Sdelphij} 179170754Sdelphij 180170754Sdelphij/* Append to EX the exclusion PATTERN with OPTIONS. */ 181170754Sdelphij 182170754Sdelphijvoid 183170754Sdelphijadd_exclude (struct exclude *ex, char const *pattern, int options) 184170754Sdelphij{ 185170754Sdelphij struct patopts *patopts; 186170754Sdelphij 187170754Sdelphij if (ex->exclude_count == ex->exclude_alloc) 188170754Sdelphij ex->exclude = x2nrealloc (ex->exclude, &ex->exclude_alloc, 189170754Sdelphij sizeof *ex->exclude); 190170754Sdelphij 191170754Sdelphij patopts = &ex->exclude[ex->exclude_count++]; 192170754Sdelphij patopts->pattern = pattern; 193170754Sdelphij patopts->options = options; 194170754Sdelphij} 195170754Sdelphij 196170754Sdelphij/* Use ADD_FUNC to append to EX the patterns in FILENAME, each with 197170754Sdelphij OPTIONS. LINE_END terminates each pattern in the file. If 198170754Sdelphij LINE_END is a space character, ignore trailing spaces and empty 199170754Sdelphij lines in FILE. Return -1 on failure, 0 on success. */ 200170754Sdelphij 201170754Sdelphijint 202170754Sdelphijadd_exclude_file (void (*add_func) (struct exclude *, char const *, int), 203170754Sdelphij struct exclude *ex, char const *filename, int options, 204170754Sdelphij char line_end) 205170754Sdelphij{ 206170754Sdelphij bool use_stdin = filename[0] == '-' && !filename[1]; 207170754Sdelphij FILE *in; 208170754Sdelphij char *buf = NULL; 209170754Sdelphij char *p; 210170754Sdelphij char const *pattern; 211170754Sdelphij char const *lim; 212170754Sdelphij size_t buf_alloc = 0; 213170754Sdelphij size_t buf_count = 0; 214170754Sdelphij int c; 215170754Sdelphij int e = 0; 216170754Sdelphij 217170754Sdelphij if (use_stdin) 218170754Sdelphij in = stdin; 219170754Sdelphij else if (! (in = fopen (filename, "r"))) 220170754Sdelphij return -1; 221170754Sdelphij 222170754Sdelphij while ((c = getc (in)) != EOF) 223170754Sdelphij { 224170754Sdelphij if (buf_count == buf_alloc) 225170754Sdelphij buf = x2realloc (buf, &buf_alloc); 226170754Sdelphij buf[buf_count++] = c; 227170754Sdelphij } 228170754Sdelphij 229170754Sdelphij if (ferror (in)) 230170754Sdelphij e = errno; 231170754Sdelphij 232170754Sdelphij if (!use_stdin && fclose (in) != 0) 233170754Sdelphij e = errno; 234170754Sdelphij 235170754Sdelphij buf = xrealloc (buf, buf_count + 1); 236170754Sdelphij buf[buf_count] = line_end; 237170754Sdelphij lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end); 238170754Sdelphij pattern = buf; 239170754Sdelphij 240170754Sdelphij for (p = buf; p < lim; p++) 241170754Sdelphij if (*p == line_end) 242170754Sdelphij { 243170754Sdelphij char *pattern_end = p; 244170754Sdelphij 245170754Sdelphij if (is_space (line_end)) 246170754Sdelphij { 247170754Sdelphij for (; ; pattern_end--) 248170754Sdelphij if (pattern_end == pattern) 249170754Sdelphij goto next_pattern; 250170754Sdelphij else if (! is_space (pattern_end[-1])) 251170754Sdelphij break; 252170754Sdelphij } 253170754Sdelphij 254170754Sdelphij *pattern_end = '\0'; 255170754Sdelphij (*add_func) (ex, pattern, options); 256170754Sdelphij 257170754Sdelphij next_pattern: 258170754Sdelphij pattern = p + 1; 259170754Sdelphij } 260170754Sdelphij 261170754Sdelphij errno = e; 262170754Sdelphij return e ? -1 : 0; 263170754Sdelphij} 264