Blame lib/exclude.c

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