(linenum→info "unix/slp.c:2238")

coreutils/6.9/lib/exclude.c

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