Blame lib/exclude.c

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