~ubuntu-branches/ubuntu/utopic/coreutils/utopic-proposed

« back to all changes in this revision

Viewing changes to lib/exclude.c

  • Committer: Package Import Robot
  • Author(s): Colin Watson
  • Date: 2012-11-28 03:03:42 UTC
  • mfrom: (8.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20121128030342-21zanj8354gas5gr
Tags: 8.20-3ubuntu1
* Resynchronise with Debian.  Remaining changes:
  - Make 'uname -i -p' return the real processor/hardware, instead of
    unknown.
  - Build-depend on gettext:any instead of on gettext, so that apt-get can
    properly resolve build-dependencies on the tool when cross-building.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* exclude.c -- exclude file names
2
2
 
3
 
   Copyright (C) 1992-1994, 1997, 1999-2007, 2009-2011 Free Software
 
3
   Copyright (C) 1992-1994, 1997, 1999-2007, 2009-2012 Free Software
4
4
   Foundation, Inc.
5
5
 
6
6
   This program is free software: you can redistribute it and/or modify
104
104
    } v;
105
105
  };
106
106
 
107
 
/* The exclude structure keeps a singly-linked list of exclude segments */
 
107
/* The exclude structure keeps a singly-linked list of exclude segments,
 
108
   maintained in reverse order.  */
108
109
struct exclude
109
110
  {
110
 
    struct exclude_segment *head, *tail;
 
111
    struct exclude_segment *head;
111
112
  };
112
113
 
113
 
/* Return true if str has wildcard characters */
 
114
/* Return true if STR has or may have wildcards, when matched with OPTIONS.
 
115
   Return false if STR definitely does not have wildcards.  */
114
116
bool
115
117
fnmatch_pattern_has_wildcards (const char *str, int options)
116
118
{
117
 
  const char *cset = "\\?*[]";
118
 
  if (options & FNM_NOESCAPE)
119
 
    cset++;
120
 
  while (*str)
 
119
  while (1)
121
120
    {
122
 
      size_t n = strcspn (str, cset);
123
 
      if (str[n] == 0)
124
 
        break;
125
 
      else if (str[n] == '\\')
 
121
      switch (*str++)
126
122
        {
127
 
          str += n + 1;
128
 
          if (*str)
129
 
            str++;
 
123
        case '\\':
 
124
          str += ! (options & FNM_NOESCAPE) && *str;
 
125
          break;
 
126
 
 
127
        case '+': case '@': case '!':
 
128
          if (options & FNM_EXTMATCH && *str == '(')
 
129
            return true;
 
130
          break;
 
131
 
 
132
        case '?': case '*': case '[':
 
133
          return true;
 
134
 
 
135
        case '\0':
 
136
          return false;
130
137
        }
131
 
      else
132
 
        return true;
133
138
    }
134
 
  return false;
135
139
}
136
140
 
137
141
static void
138
142
unescape_pattern (char *str)
139
143
{
140
 
  int inset = 0;
141
 
  char *q = str;
 
144
  char const *q = str;
142
145
  do
143
 
    {
144
 
      if (inset)
145
 
        {
146
 
          if (*q == ']')
147
 
            inset = 0;
148
 
        }
149
 
      else if (*q == '[')
150
 
        inset = 1;
151
 
      else if (*q == '\\')
152
 
        q++;
153
 
    }
 
146
    q += *q == '\\' && q[1];
154
147
  while ((*str++ = *q++));
155
148
}
156
149
 
219
212
}
220
213
 
221
214
/* Create new exclude segment of given TYPE and OPTIONS, and attach it
222
 
   to the tail of list in EX */
223
 
static struct exclude_segment *
 
215
   to the head of EX.  */
 
216
static void
224
217
new_exclude_segment (struct exclude *ex, enum exclude_type type, int options)
225
218
{
226
219
  struct exclude_segment *sp = xzalloc (sizeof (struct exclude_segment));
242
235
                                     string_free);
243
236
      break;
244
237
    }
245
 
  if (ex->tail)
246
 
    ex->tail->next = sp;
247
 
  else
248
 
    ex->head = sp;
249
 
  ex->tail = sp;
250
 
  return sp;
 
238
  sp->next = ex->head;
 
239
  ex->head = sp;
251
240
}
252
241
 
253
242
/* Free a single exclude segment */
347
336
  return matched;
348
337
}
349
338
 
350
 
/* Return true if the exclude_pattern segment SEG excludes F.  */
 
339
/* Return true if the exclude_pattern segment SEG matches F.  */
351
340
 
352
341
static bool
353
 
excluded_file_pattern_p (struct exclude_segment const *seg, char const *f)
 
342
file_pattern_matches (struct exclude_segment const *seg, char const *f)
354
343
{
355
344
  size_t exclude_count = seg->v.pat.exclude_count;
356
345
  struct patopts const *exclude = seg->v.pat.exclude;
357
346
  size_t i;
358
 
  bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);
359
347
 
360
 
  /* Scan through the options, until they change excluded */
361
348
  for (i = 0; i < exclude_count; i++)
362
349
    {
363
350
      char const *pattern = exclude[i].pattern;
364
351
      int options = exclude[i].options;
365
352
      if (exclude_fnmatch (pattern, f, options))
366
 
        return !excluded;
 
353
        return true;
367
354
    }
368
 
  return excluded;
 
355
  return false;
369
356
}
370
357
 
371
 
/* Return true if the exclude_hash segment SEG excludes F.
 
358
/* Return true if the exclude_hash segment SEG matches F.
372
359
   BUFFER is an auxiliary storage of the same length as F (with nul
373
360
   terminator included) */
374
361
static bool
375
 
excluded_file_name_p (struct exclude_segment const *seg, char const *f,
376
 
                      char *buffer)
 
362
file_name_matches (struct exclude_segment const *seg, char const *f,
 
363
                   char *buffer)
377
364
{
378
365
  int options = seg->options;
379
 
  bool excluded = !! (options & EXCLUDE_INCLUDE);
380
366
  Hash_table *table = seg->v.table;
381
367
 
382
368
  do
387
373
      while (1)
388
374
        {
389
375
          if (hash_lookup (table, buffer))
390
 
            return !excluded;
 
376
            return true;
391
377
          if (options & FNM_LEADING_DIR)
392
378
            {
393
379
              char *p = strrchr (buffer, '/');
410
396
        break;
411
397
    }
412
398
  while (f);
413
 
  return excluded;
 
399
 
 
400
  return false;
414
401
}
415
402
 
416
403
/* Return true if EX excludes F.  */
419
406
excluded_file_name (struct exclude const *ex, char const *f)
420
407
{
421
408
  struct exclude_segment *seg;
422
 
  bool excluded;
 
409
  bool invert = false;
423
410
  char *filename = NULL;
424
411
 
425
412
  /* If no patterns are given, the default is to include.  */
426
413
  if (!ex->head)
427
414
    return false;
428
415
 
429
 
  /* Otherwise, the default is the opposite of the first option.  */
430
 
  excluded = !! (ex->head->options & EXCLUDE_INCLUDE);
431
 
  /* Scan through the segments, seeing whether they change status from
432
 
     excluded to included or vice versa.  */
433
 
  for (seg = ex->head; seg; seg = seg->next)
 
416
  /* Scan through the segments, reporting the status of the first match.
 
417
     The segments are in reverse order, so this reports the status of
 
418
     the last match in the original option list.  */
 
419
  for (seg = ex->head; ; seg = seg->next)
434
420
    {
435
 
      bool rc;
436
 
 
437
 
      switch (seg->type)
 
421
      if (seg->type == exclude_hash)
438
422
        {
439
 
        case exclude_pattern:
440
 
          rc = excluded_file_pattern_p (seg, f);
441
 
          break;
442
 
 
443
 
        case exclude_hash:
444
423
          if (!filename)
445
424
            filename = xmalloc (strlen (f) + 1);
446
 
          rc = excluded_file_name_p (seg, f, filename);
447
 
          break;
 
425
          if (file_name_matches (seg, f, filename))
 
426
            break;
 
427
        }
 
428
      else
 
429
        {
 
430
          if (file_pattern_matches (seg, f))
 
431
            break;
 
432
        }
448
433
 
449
 
        default:
450
 
          abort ();
451
 
        }
452
 
      if (rc != excluded)
 
434
      if (! seg->next)
453
435
        {
454
 
          excluded = rc;
 
436
          /* If patterns are given but none match, the default is the
 
437
             opposite of the last segment (i.e., the first in the
 
438
             original option list).  For example, in the command
 
439
             'grep -r --exclude="a*" --include="*b" pat dir', the
 
440
             first option is --exclude so any file name matching
 
441
             neither a* nor *b is included.  */
 
442
          invert = true;
455
443
          break;
456
444
        }
457
445
    }
 
446
 
458
447
  free (filename);
459
 
  return excluded;
 
448
  return invert ^ ! (seg->options & EXCLUDE_INCLUDE);
460
449
}
461
450
 
462
451
/* Append to EX the exclusion PATTERN with OPTIONS.  */
472
461
      struct exclude_pattern *pat;
473
462
      struct patopts *patopts;
474
463
 
475
 
      if (ex->tail && ex->tail->type == exclude_pattern
476
 
          && ((ex->tail->options & EXCLUDE_INCLUDE) ==
477
 
              (options & EXCLUDE_INCLUDE)))
478
 
        seg = ex->tail;
479
 
      else
480
 
        seg = new_exclude_segment (ex, exclude_pattern, options);
 
464
      if (! (ex->head && ex->head->type == exclude_pattern
 
465
             && ((ex->head->options & EXCLUDE_INCLUDE)
 
466
                 == (options & EXCLUDE_INCLUDE))))
 
467
        new_exclude_segment (ex, exclude_pattern, options);
 
468
      seg = ex->head;
481
469
 
482
470
      pat = &seg->v.pat;
483
471
      if (pat->exclude_count == pat->exclude_alloc)
490
478
  else
491
479
    {
492
480
      char *str, *p;
493
 
#define EXCLUDE_HASH_FLAGS (EXCLUDE_INCLUDE|EXCLUDE_ANCHORED|\
494
 
                            FNM_LEADING_DIR|FNM_CASEFOLD)
495
 
      if (ex->tail && ex->tail->type == exclude_hash
496
 
          && ((ex->tail->options & EXCLUDE_HASH_FLAGS) ==
497
 
              (options & EXCLUDE_HASH_FLAGS)))
498
 
        seg = ex->tail;
499
 
      else
500
 
        seg = new_exclude_segment (ex, exclude_hash, options);
 
481
      int exclude_hash_flags = (EXCLUDE_INCLUDE | EXCLUDE_ANCHORED
 
482
                                | FNM_LEADING_DIR | FNM_CASEFOLD);
 
483
      if (! (ex->head && ex->head->type == exclude_hash
 
484
             && ((ex->head->options & exclude_hash_flags)
 
485
                 == (options & exclude_hash_flags))))
 
486
        new_exclude_segment (ex, exclude_hash, options);
 
487
      seg = ex->head;
501
488
 
502
489
      str = xstrdup (pattern);
503
 
      if (options & EXCLUDE_WILDCARDS)
 
490
      if ((options & (EXCLUDE_WILDCARDS | FNM_NOESCAPE)) == EXCLUDE_WILDCARDS)
504
491
        unescape_pattern (str);
505
492
      p = hash_insert (seg->v.table, str);
506
493
      if (p != str)