Blame lib/exclude.c

Packit Service fdd496
/* exclude.c -- exclude file names
Packit Service fdd496
Packit Service fdd496
   Copyright (C) 1992-1994, 1997, 1999-2007, 2009-2017 Free Software
Packit Service fdd496
   Foundation, Inc.
Packit Service fdd496
Packit Service fdd496
   This program is free software: you can redistribute it and/or modify
Packit Service fdd496
   it under the terms of the GNU General Public License as published by
Packit Service fdd496
   the Free Software Foundation; either version 3 of the License, or
Packit Service fdd496
   (at your option) any later version.
Packit Service fdd496
Packit Service fdd496
   This program is distributed in the hope that it will be useful,
Packit Service fdd496
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service fdd496
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service fdd496
   GNU General Public License for more details.
Packit Service fdd496
Packit Service fdd496
   You should have received a copy of the GNU General Public License
Packit Service fdd496
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service fdd496
Packit Service fdd496
/* Written by Paul Eggert <eggert@twinsun.com>
Packit Service fdd496
   and Sergey Poznyakoff <gray@gnu.org>.
Packit Service fdd496
   Thanks to Phil Proudman <phil@proudman51.freeserve.co.uk>
Packit Service fdd496
   for improvement suggestions. */
Packit Service fdd496
Packit Service fdd496
#include <config.h>
Packit Service fdd496
Packit Service fdd496
#include <stdbool.h>
Packit Service fdd496
Packit Service fdd496
#include <ctype.h>
Packit Service fdd496
#include <errno.h>
Packit Service fdd496
#include <stddef.h>
Packit Service fdd496
#include <stdio.h>
Packit Service fdd496
#include <stdlib.h>
Packit Service fdd496
#include <string.h>
Packit Service fdd496
#include <wctype.h>
Packit Service fdd496
#include <regex.h>
Packit Service fdd496
Packit Service fdd496
#include "exclude.h"
Packit Service fdd496
#include "hash.h"
Packit Service fdd496
#include "mbuiter.h"
Packit Service fdd496
#include "fnmatch.h"
Packit Service fdd496
#include "xalloc.h"
Packit Service fdd496
#include "verify.h"
Packit Service fdd496
#include "filename.h"
Packit Service fdd496
Packit Service fdd496
#if USE_UNLOCKED_IO
Packit Service fdd496
# include "unlocked-io.h"
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
/* Non-GNU systems lack these options, so we don't need to check them.  */
Packit Service fdd496
#ifndef FNM_CASEFOLD
Packit Service fdd496
# define FNM_CASEFOLD 0
Packit Service fdd496
#endif
Packit Service fdd496
#ifndef FNM_EXTMATCH
Packit Service fdd496
# define FNM_EXTMATCH 0
Packit Service fdd496
#endif
Packit Service fdd496
#ifndef FNM_LEADING_DIR
Packit Service fdd496
# define FNM_LEADING_DIR 0
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
verify (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
Packit Service fdd496
         & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
Packit Service fdd496
            | FNM_CASEFOLD | FNM_EXTMATCH))
Packit Service fdd496
        == 0);
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
/* Exclusion patterns are grouped into a singly-linked list of
Packit Service fdd496
   "exclusion segments".  Each segment represents a set of patterns
Packit Service fdd496
   that can be matches using the same algorithm.  Non-wildcard
Packit Service fdd496
   patterns are kept in hash tables, to speed up searches.  Wildcard
Packit Service fdd496
   patterns are stored as arrays of patterns. */
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
/* An exclude pattern-options pair.  The options are fnmatch options
Packit Service fdd496
   ORed with EXCLUDE_* options.  */
Packit Service fdd496
Packit Service fdd496
struct patopts
Packit Service fdd496
  {
Packit Service fdd496
    int options;
Packit Service fdd496
    union
Packit Service fdd496
    {
Packit Service fdd496
      char const *pattern;
Packit Service fdd496
      regex_t re;
Packit Service fdd496
    } v;
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
/* An array of pattern-options pairs.  */
Packit Service fdd496
Packit Service fdd496
struct exclude_pattern
Packit Service fdd496
  {
Packit Service fdd496
    struct patopts *exclude;
Packit Service fdd496
    size_t exclude_alloc;
Packit Service fdd496
    size_t exclude_count;
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
enum exclude_type
Packit Service fdd496
  {
Packit Service fdd496
    exclude_hash,                    /* a hash table of excluded names */
Packit Service fdd496
    exclude_pattern                  /* an array of exclude patterns */
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
struct exclude_segment
Packit Service fdd496
  {
Packit Service fdd496
    struct exclude_segment *next;    /* next segment in list */
Packit Service fdd496
    enum exclude_type type;          /* type of this segment */
Packit Service fdd496
    int options;                     /* common options for this segment */
Packit Service fdd496
    union
Packit Service fdd496
    {
Packit Service fdd496
      Hash_table *table;             /* for type == exclude_hash */
Packit Service fdd496
      struct exclude_pattern pat;    /* for type == exclude_pattern */
Packit Service fdd496
    } v;
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
struct pattern_buffer
Packit Service fdd496
  {
Packit Service fdd496
    struct pattern_buffer *next;
Packit Service fdd496
    char *base;
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
/* The exclude structure keeps a singly-linked list of exclude segments,
Packit Service fdd496
   maintained in reverse order.  */
Packit Service fdd496
struct exclude
Packit Service fdd496
  {
Packit Service fdd496
    struct exclude_segment *head;
Packit Service fdd496
    struct pattern_buffer *patbuf;
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
/* Register BUF in the pattern buffer list of EX.  ADD_FUNC (see
Packit Service fdd496
   add_exclude_file and add_exclude_fp below) can use this function
Packit Service fdd496
   if it modifies the pattern, to ensure the allocated memory will be
Packit Service fdd496
   properly reclaimed upon calling free_exclude. */
Packit Service fdd496
void
Packit Service fdd496
exclude_add_pattern_buffer (struct exclude *ex, char *buf)
Packit Service fdd496
{
Packit Service fdd496
  struct pattern_buffer *pbuf = xmalloc (sizeof *pbuf);
Packit Service fdd496
  pbuf->base = buf;
Packit Service fdd496
  pbuf->next = ex->patbuf;
Packit Service fdd496
  ex->patbuf = pbuf;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return true if STR has or may have wildcards, when matched with OPTIONS.
Packit Service fdd496
   Return false if STR definitely does not have wildcards.  */
Packit Service fdd496
bool
Packit Service fdd496
fnmatch_pattern_has_wildcards (const char *str, int options)
Packit Service fdd496
{
Packit Service fdd496
  while (1)
Packit Service fdd496
    {
Packit Service fdd496
      switch (*str++)
Packit Service fdd496
        {
Packit Service fdd496
	case '.':
Packit Service fdd496
	case '{':
Packit Service fdd496
	case '}':
Packit Service fdd496
	case '(':
Packit Service fdd496
	case ')':
Packit Service fdd496
	  if (options & EXCLUDE_REGEX)
Packit Service fdd496
	    return true;
Packit Service fdd496
	  break;
Packit Service fdd496
Packit Service fdd496
        case '\\':
Packit Service fdd496
	  if (options & EXCLUDE_REGEX)
Packit Service fdd496
	    continue;
Packit Service fdd496
	  else
Packit Service fdd496
	    str += ! (options & FNM_NOESCAPE) && *str;
Packit Service fdd496
          break;
Packit Service fdd496
Packit Service fdd496
        case '+': case '@': case '!':
Packit Service fdd496
          if (options & FNM_EXTMATCH && *str == '(')
Packit Service fdd496
            return true;
Packit Service fdd496
          break;
Packit Service fdd496
Packit Service fdd496
        case '?': case '*': case '[':
Packit Service fdd496
          return true;
Packit Service fdd496
Packit Service fdd496
        case '\0':
Packit Service fdd496
          return false;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
unescape_pattern (char *str)
Packit Service fdd496
{
Packit Service fdd496
  char const *q = str;
Packit Service fdd496
  do
Packit Service fdd496
    q += *q == '\\' && q[1];
Packit Service fdd496
  while ((*str++ = *q++));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return a newly allocated and empty exclude list.  */
Packit Service fdd496
Packit Service fdd496
struct exclude *
Packit Service fdd496
new_exclude (void)
Packit Service fdd496
{
Packit Service fdd496
  return xzalloc (sizeof *new_exclude ());
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Calculate the hash of string.  */
Packit Service fdd496
static size_t
Packit Service fdd496
string_hasher (void const *data, size_t n_buckets)
Packit Service fdd496
{
Packit Service fdd496
  char const *p = data;
Packit Service fdd496
  return hash_string (p, n_buckets);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Ditto, for case-insensitive hashes */
Packit Service fdd496
static size_t
Packit Service fdd496
string_hasher_ci (void const *data, size_t n_buckets)
Packit Service fdd496
{
Packit Service fdd496
  char const *p = data;
Packit Service fdd496
  mbui_iterator_t iter;
Packit Service fdd496
  size_t value = 0;
Packit Service fdd496
Packit Service fdd496
  for (mbui_init (iter, p); mbui_avail (iter); mbui_advance (iter))
Packit Service fdd496
    {
Packit Service fdd496
      mbchar_t m = mbui_cur (iter);
Packit Service fdd496
      wchar_t wc;
Packit Service fdd496
Packit Service fdd496
      if (m.wc_valid)
Packit Service fdd496
        wc = towlower (m.wc);
Packit Service fdd496
      else
Packit Service fdd496
        wc = *m.ptr;
Packit Service fdd496
Packit Service fdd496
      value = (value * 31 + wc) % n_buckets;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return value;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* compare two strings for equality */
Packit Service fdd496
static bool
Packit Service fdd496
string_compare (void const *data1, void const *data2)
Packit Service fdd496
{
Packit Service fdd496
  char const *p1 = data1;
Packit Service fdd496
  char const *p2 = data2;
Packit Service fdd496
  return strcmp (p1, p2) == 0;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* compare two strings for equality, case-insensitive */
Packit Service fdd496
static bool
Packit Service fdd496
string_compare_ci (void const *data1, void const *data2)
Packit Service fdd496
{
Packit Service fdd496
  char const *p1 = data1;
Packit Service fdd496
  char const *p2 = data2;
Packit Service fdd496
  return mbscasecmp (p1, p2) == 0;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
string_free (void *data)
Packit Service fdd496
{
Packit Service fdd496
  free (data);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Create new exclude segment of given TYPE and OPTIONS, and attach it
Packit Service fdd496
   to the head of EX.  */
Packit Service fdd496
static void
Packit Service fdd496
new_exclude_segment (struct exclude *ex, enum exclude_type type, int options)
Packit Service fdd496
{
Packit Service fdd496
  struct exclude_segment *sp = xzalloc (sizeof (struct exclude_segment));
Packit Service fdd496
  sp->type = type;
Packit Service fdd496
  sp->options = options;
Packit Service fdd496
  switch (type)
Packit Service fdd496
    {
Packit Service fdd496
    case exclude_pattern:
Packit Service fdd496
      break;
Packit Service fdd496
Packit Service fdd496
    case exclude_hash:
Packit Service fdd496
      sp->v.table = hash_initialize (0, NULL,
Packit Service fdd496
                                     (options & FNM_CASEFOLD) ?
Packit Service fdd496
                                       string_hasher_ci
Packit Service fdd496
                                       : string_hasher,
Packit Service fdd496
                                     (options & FNM_CASEFOLD) ?
Packit Service fdd496
                                       string_compare_ci
Packit Service fdd496
                                       : string_compare,
Packit Service fdd496
                                     string_free);
Packit Service fdd496
      break;
Packit Service fdd496
    }
Packit Service fdd496
  sp->next = ex->head;
Packit Service fdd496
  ex->head = sp;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Free a single exclude segment */
Packit Service fdd496
static void
Packit Service fdd496
free_exclude_segment (struct exclude_segment *seg)
Packit Service fdd496
{
Packit Service fdd496
  size_t i;
Packit Service fdd496
Packit Service fdd496
  switch (seg->type)
Packit Service fdd496
    {
Packit Service fdd496
    case exclude_pattern:
Packit Service fdd496
      for (i = 0; i < seg->v.pat.exclude_count; i++)
Packit Service fdd496
	{
Packit Service fdd496
	  if (seg->v.pat.exclude[i].options & EXCLUDE_REGEX)
Packit Service fdd496
	    regfree (&seg->v.pat.exclude[i].v.re);
Packit Service fdd496
	}
Packit Service fdd496
      free (seg->v.pat.exclude);
Packit Service fdd496
      break;
Packit Service fdd496
Packit Service fdd496
    case exclude_hash:
Packit Service fdd496
      hash_free (seg->v.table);
Packit Service fdd496
      break;
Packit Service fdd496
    }
Packit Service fdd496
  free (seg);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Free the storage associated with an exclude list.  */
Packit Service fdd496
void
Packit Service fdd496
free_exclude (struct exclude *ex)
Packit Service fdd496
{
Packit Service fdd496
  struct exclude_segment *seg;
Packit Service fdd496
  struct pattern_buffer *pbuf;
Packit Service fdd496
Packit Service fdd496
  for (seg = ex->head; seg; )
Packit Service fdd496
    {
Packit Service fdd496
      struct exclude_segment *next = seg->next;
Packit Service fdd496
      free_exclude_segment (seg);
Packit Service fdd496
      seg = next;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  for (pbuf = ex->patbuf; pbuf; )
Packit Service fdd496
    {
Packit Service fdd496
      struct pattern_buffer *next = pbuf->next;
Packit Service fdd496
      free (pbuf->base);
Packit Service fdd496
      free (pbuf);
Packit Service fdd496
      pbuf = next;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  free (ex);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return zero if PATTERN matches F, obeying OPTIONS, except that
Packit Service fdd496
   (unlike fnmatch) wildcards are disabled in PATTERN.  */
Packit Service fdd496
Packit Service fdd496
static int
Packit Service fdd496
fnmatch_no_wildcards (char const *pattern, char const *f, int options)
Packit Service fdd496
{
Packit Service fdd496
  if (! (options & FNM_LEADING_DIR))
Packit Service fdd496
    return ((options & FNM_CASEFOLD)
Packit Service fdd496
            ? mbscasecmp (pattern, f)
Packit Service fdd496
            : strcmp (pattern, f));
Packit Service fdd496
  else if (! (options & FNM_CASEFOLD))
Packit Service fdd496
    {
Packit Service fdd496
      size_t patlen = strlen (pattern);
Packit Service fdd496
      int r = strncmp (pattern, f, patlen);
Packit Service fdd496
      if (! r)
Packit Service fdd496
        {
Packit Service fdd496
          r = f[patlen];
Packit Service fdd496
          if (r == '/')
Packit Service fdd496
            r = 0;
Packit Service fdd496
        }
Packit Service fdd496
      return r;
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      /* Walk through a copy of F, seeing whether P matches any prefix
Packit Service fdd496
         of F.
Packit Service fdd496
Packit Service fdd496
         FIXME: This is an O(N**2) algorithm; it should be O(N).
Packit Service fdd496
         Also, the copy should not be necessary.  However, fixing this
Packit Service fdd496
         will probably involve a change to the mbs* API.  */
Packit Service fdd496
Packit Service fdd496
      char *fcopy = xstrdup (f);
Packit Service fdd496
      char *p;
Packit Service fdd496
      int r;
Packit Service fdd496
      for (p = fcopy; ; *p++ = '/')
Packit Service fdd496
        {
Packit Service fdd496
          p = strchr (p, '/');
Packit Service fdd496
          if (p)
Packit Service fdd496
            *p = '\0';
Packit Service fdd496
          r = mbscasecmp (pattern, fcopy);
Packit Service fdd496
          if (!p || r <= 0)
Packit Service fdd496
            break;
Packit Service fdd496
        }
Packit Service fdd496
      free (fcopy);
Packit Service fdd496
      return r;
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
bool
Packit Service fdd496
exclude_fnmatch (char const *pattern, char const *f, int options)
Packit Service fdd496
{
Packit Service fdd496
  int (*matcher) (char const *, char const *, int) =
Packit Service fdd496
    (options & EXCLUDE_WILDCARDS
Packit Service fdd496
     ? fnmatch
Packit Service fdd496
     : fnmatch_no_wildcards);
Packit Service fdd496
  bool matched = ((*matcher) (pattern, f, options) == 0);
Packit Service fdd496
  char const *p;
Packit Service fdd496
Packit Service fdd496
  if (! (options & EXCLUDE_ANCHORED))
Packit Service fdd496
    for (p = f; *p && ! matched; p++)
Packit Service fdd496
      if (*p == '/' && p[1] != '/')
Packit Service fdd496
	matched = ((*matcher) (pattern, p + 1, options) == 0);
Packit Service fdd496
Packit Service fdd496
  return matched;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
exclude_patopts (struct patopts const *opts, char const *f)
Packit Service fdd496
{
Packit Service fdd496
  int options = opts->options;
Packit Service fdd496
Packit Service fdd496
  return (options & EXCLUDE_REGEX)
Packit Service fdd496
          ? regexec (&opts->v.re, f, 0, NULL, 0) == 0
Packit Service fdd496
          : exclude_fnmatch (opts->v.pattern, f, options);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return true if the exclude_pattern segment SEG matches F.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
file_pattern_matches (struct exclude_segment const *seg, char const *f)
Packit Service fdd496
{
Packit Service fdd496
  size_t exclude_count = seg->v.pat.exclude_count;
Packit Service fdd496
  struct patopts const *exclude = seg->v.pat.exclude;
Packit Service fdd496
  size_t i;
Packit Service fdd496
Packit Service fdd496
  for (i = 0; i < exclude_count; i++)
Packit Service fdd496
    {
Packit Service fdd496
      if (exclude_patopts (exclude + i, f))
Packit Service fdd496
        return true;
Packit Service fdd496
    }
Packit Service fdd496
  return false;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return true if the exclude_hash segment SEG matches F.
Packit Service fdd496
   BUFFER is an auxiliary storage of the same length as F (with nul
Packit Service fdd496
   terminator included) */
Packit Service fdd496
static bool
Packit Service fdd496
file_name_matches (struct exclude_segment const *seg, char const *f,
Packit Service fdd496
                   char *buffer)
Packit Service fdd496
{
Packit Service fdd496
  int options = seg->options;
Packit Service fdd496
  Hash_table *table = seg->v.table;
Packit Service fdd496
Packit Service fdd496
  do
Packit Service fdd496
    {
Packit Service fdd496
      /* initialize the pattern */
Packit Service fdd496
      strcpy (buffer, f);
Packit Service fdd496
Packit Service fdd496
      while (1)
Packit Service fdd496
        {
Packit Service fdd496
          if (hash_lookup (table, buffer))
Packit Service fdd496
            return true;
Packit Service fdd496
          if (options & FNM_LEADING_DIR)
Packit Service fdd496
            {
Packit Service fdd496
              char *p = strrchr (buffer, '/');
Packit Service fdd496
              if (p)
Packit Service fdd496
                {
Packit Service fdd496
                  *p = 0;
Packit Service fdd496
                  continue;
Packit Service fdd496
                }
Packit Service fdd496
            }
Packit Service fdd496
          break;
Packit Service fdd496
        }
Packit Service fdd496
Packit Service fdd496
      if (!(options & EXCLUDE_ANCHORED))
Packit Service fdd496
        {
Packit Service fdd496
          f = strchr (f, '/');
Packit Service fdd496
          if (f)
Packit Service fdd496
            f++;
Packit Service fdd496
        }
Packit Service fdd496
      else
Packit Service fdd496
        break;
Packit Service fdd496
    }
Packit Service fdd496
  while (f);
Packit Service fdd496
Packit Service fdd496
  return false;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return true if EX excludes F.  */
Packit Service fdd496
Packit Service fdd496
bool
Packit Service fdd496
excluded_file_name (struct exclude const *ex, char const *f)
Packit Service fdd496
{
Packit Service fdd496
  struct exclude_segment *seg;
Packit Service fdd496
  bool invert = false;
Packit Service fdd496
  char *filename = NULL;
Packit Service fdd496
Packit Service fdd496
  /* If no patterns are given, the default is to include.  */
Packit Service fdd496
  if (!ex->head)
Packit Service fdd496
    return false;
Packit Service fdd496
Packit Service fdd496
  /* Scan through the segments, reporting the status of the first match.
Packit Service fdd496
     The segments are in reverse order, so this reports the status of
Packit Service fdd496
     the last match in the original option list.  */
Packit Service fdd496
  for (seg = ex->head; ; seg = seg->next)
Packit Service fdd496
    {
Packit Service fdd496
      if (seg->type == exclude_hash)
Packit Service fdd496
        {
Packit Service fdd496
          if (!filename)
Packit Service fdd496
            filename = xmalloc (strlen (f) + 1);
Packit Service fdd496
          if (file_name_matches (seg, f, filename))
Packit Service fdd496
            break;
Packit Service fdd496
        }
Packit Service fdd496
      else
Packit Service fdd496
        {
Packit Service fdd496
          if (file_pattern_matches (seg, f))
Packit Service fdd496
            break;
Packit Service fdd496
        }
Packit Service fdd496
Packit Service fdd496
      if (! seg->next)
Packit Service fdd496
        {
Packit Service fdd496
          /* If patterns are given but none match, the default is the
Packit Service fdd496
             opposite of the last segment (i.e., the first in the
Packit Service fdd496
             original option list).  For example, in the command
Packit Service fdd496
             'grep -r --exclude="a*" --include="*b" pat dir', the
Packit Service fdd496
             first option is --exclude so any file name matching
Packit Service fdd496
             neither a* nor *b is included.  */
Packit Service fdd496
          invert = true;
Packit Service fdd496
          break;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  free (filename);
Packit Service fdd496
  return invert ^ ! (seg->options & EXCLUDE_INCLUDE);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Append to EX the exclusion PATTERN with OPTIONS.  */
Packit Service fdd496
Packit Service fdd496
void
Packit Service fdd496
add_exclude (struct exclude *ex, char const *pattern, int options)
Packit Service fdd496
{
Packit Service fdd496
  struct exclude_segment *seg;
Packit Service fdd496
  struct exclude_pattern *pat;
Packit Service fdd496
  struct patopts *patopts;
Packit Service fdd496
Packit Service fdd496
  if ((options & (EXCLUDE_REGEX|EXCLUDE_WILDCARDS))
Packit Service fdd496
      && fnmatch_pattern_has_wildcards (pattern, options))
Packit Service fdd496
    {
Packit Service fdd496
      if (! (ex->head && ex->head->type == exclude_pattern
Packit Service fdd496
	     && ((ex->head->options & EXCLUDE_INCLUDE)
Packit Service fdd496
		 == (options & EXCLUDE_INCLUDE))))
Packit Service fdd496
	new_exclude_segment (ex, exclude_pattern, options);
Packit Service fdd496
Packit Service fdd496
      seg = ex->head;
Packit Service fdd496
Packit Service fdd496
      pat = &seg->v.pat;
Packit Service fdd496
      if (pat->exclude_count == pat->exclude_alloc)
Packit Service fdd496
        pat->exclude = x2nrealloc (pat->exclude, &pat->exclude_alloc,
Packit Service fdd496
                                   sizeof *pat->exclude);
Packit Service fdd496
      patopts = &pat->exclude[pat->exclude_count++];
Packit Service fdd496
Packit Service fdd496
      patopts->options = options;
Packit Service fdd496
      if (options & EXCLUDE_REGEX)
Packit Service fdd496
	{
Packit Service fdd496
	  int rc;
Packit Service fdd496
	  int cflags = REG_NOSUB|REG_EXTENDED|
Packit Service fdd496
	               ((options & FNM_CASEFOLD) ? REG_ICASE : 0);
Packit Service fdd496
Packit Service fdd496
	  if (options & FNM_LEADING_DIR)
Packit Service fdd496
	    {
Packit Service fdd496
	      char *tmp;
Packit Service fdd496
	      size_t len = strlen (pattern);
Packit Service fdd496
Packit Service fdd496
	      while (len > 0 && ISSLASH (pattern[len-1]))
Packit Service fdd496
		--len;
Packit Service fdd496
Packit Service fdd496
	      if (len == 0)
Packit Service fdd496
		rc = 1;
Packit Service fdd496
	      else
Packit Service fdd496
		{
Packit Service fdd496
		  tmp = xmalloc (len + 7);
Packit Service fdd496
		  memcpy (tmp, pattern, len);
Packit Service fdd496
		  strcpy (tmp + len, "(/.*)?");
Packit Service fdd496
		  rc = regcomp (&patopts->v.re, tmp, cflags);
Packit Service fdd496
		  free (tmp);
Packit Service fdd496
		}
Packit Service fdd496
	    }
Packit Service fdd496
	  else
Packit Service fdd496
	    rc = regcomp (&patopts->v.re, pattern, cflags);
Packit Service fdd496
Packit Service fdd496
	  if (rc)
Packit Service fdd496
	    {
Packit Service fdd496
	      pat->exclude_count--;
Packit Service fdd496
	      return;
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
      else
Packit Service fdd496
	{
Packit Service fdd496
	  if (options & EXCLUDE_ALLOC)
Packit Service fdd496
	    {
Packit Service fdd496
	      pattern = xstrdup (pattern);
Packit Service fdd496
	      exclude_add_pattern_buffer (ex, (char*) pattern);
Packit Service fdd496
	    }
Packit Service fdd496
	  patopts->v.pattern = pattern;
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      char *str, *p;
Packit Service fdd496
      int exclude_hash_flags = (EXCLUDE_INCLUDE | EXCLUDE_ANCHORED
Packit Service fdd496
                                | FNM_LEADING_DIR | FNM_CASEFOLD);
Packit Service fdd496
      if (! (ex->head && ex->head->type == exclude_hash
Packit Service fdd496
             && ((ex->head->options & exclude_hash_flags)
Packit Service fdd496
                 == (options & exclude_hash_flags))))
Packit Service fdd496
        new_exclude_segment (ex, exclude_hash, options);
Packit Service fdd496
      seg = ex->head;
Packit Service fdd496
Packit Service fdd496
      str = xstrdup (pattern);
Packit Service fdd496
      if ((options & (EXCLUDE_WILDCARDS | FNM_NOESCAPE)) == EXCLUDE_WILDCARDS)
Packit Service fdd496
        unescape_pattern (str);
Packit Service fdd496
      p = hash_insert (seg->v.table, str);
Packit Service fdd496
      if (p != str)
Packit Service fdd496
        free (str);
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Use ADD_FUNC to append to EX the patterns in FILE_NAME, each with
Packit Service fdd496
   OPTIONS.  LINE_END terminates each pattern in the file.  If
Packit Service fdd496
   LINE_END is a space character, ignore trailing spaces and empty
Packit Service fdd496
   lines in FP.  Return -1 on failure, 0 on success.  */
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
add_exclude_fp (void (*add_func) (struct exclude *, char const *, int, void *),
Packit Service fdd496
		struct exclude *ex, FILE *fp, int options,
Packit Service fdd496
		char line_end,
Packit Service fdd496
		void *data)
Packit Service fdd496
{
Packit Service fdd496
  char *buf = NULL;
Packit Service fdd496
  char *p;
Packit Service fdd496
  char *pattern;
Packit Service fdd496
  char const *lim;
Packit Service fdd496
  size_t buf_alloc = 0;
Packit Service fdd496
  size_t buf_count = 0;
Packit Service fdd496
  int c;
Packit Service fdd496
  int e = 0;
Packit Service fdd496
Packit Service fdd496
  while ((c = getc (fp)) != EOF)
Packit Service fdd496
    {
Packit Service fdd496
      if (buf_count == buf_alloc)
Packit Service fdd496
        buf = x2realloc (buf, &buf_alloc);
Packit Service fdd496
      buf[buf_count++] = c;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (ferror (fp))
Packit Service fdd496
    e = errno;
Packit Service fdd496
Packit Service fdd496
  buf = xrealloc (buf, buf_count + 1);
Packit Service fdd496
  buf[buf_count] = line_end;
Packit Service fdd496
  lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
Packit Service fdd496
Packit Service fdd496
  exclude_add_pattern_buffer (ex, buf);
Packit Service fdd496
Packit Service fdd496
  pattern = buf;
Packit Service fdd496
Packit Service fdd496
  for (p = buf; p < lim; p++)
Packit Service fdd496
    if (*p == line_end)
Packit Service fdd496
      {
Packit Service fdd496
        char *pattern_end = p;
Packit Service fdd496
Packit Service fdd496
        if (isspace ((unsigned char) line_end))
Packit Service fdd496
          {
Packit Service fdd496
            for (; ; pattern_end--)
Packit Service fdd496
              if (pattern_end == pattern)
Packit Service fdd496
                goto next_pattern;
Packit Service fdd496
              else if (! isspace ((unsigned char) pattern_end[-1]))
Packit Service fdd496
                break;
Packit Service fdd496
          }
Packit Service fdd496
Packit Service fdd496
        *pattern_end = '\0';
Packit Service fdd496
        (*add_func) (ex, pattern, options, data);
Packit Service fdd496
Packit Service fdd496
      next_pattern:
Packit Service fdd496
        pattern = p + 1;
Packit Service fdd496
      }
Packit Service fdd496
Packit Service fdd496
  errno = e;
Packit Service fdd496
  return e ? -1 : 0;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
call_addfn (struct exclude *ex, char const *pattern, int options, void *data)
Packit Service fdd496
{
Packit Service fdd496
  void (**addfnptr) (struct exclude *, char const *, int) = data;
Packit Service fdd496
  (*addfnptr) (ex, pattern, options);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
Packit Service fdd496
		  struct exclude *ex, char const *file_name, int options,
Packit Service fdd496
		  char line_end)
Packit Service fdd496
{
Packit Service fdd496
  bool use_stdin = file_name[0] == '-' && !file_name[1];
Packit Service fdd496
  FILE *in;
Packit Service fdd496
  int rc = 0;
Packit Service fdd496
Packit Service fdd496
  if (use_stdin)
Packit Service fdd496
    in = stdin;
Packit Service fdd496
  else if (! (in = fopen (file_name, "r")))
Packit Service fdd496
    return -1;
Packit Service fdd496
Packit Service fdd496
  rc = add_exclude_fp (call_addfn, ex, in, options, line_end, &add_func);
Packit Service fdd496
Packit Service fdd496
  if (!use_stdin && fclose (in) != 0)
Packit Service fdd496
    rc = -1;
Packit Service fdd496
Packit Service fdd496
  return rc;
Packit Service fdd496
}