Coverage Report

Created: 2025-07-11 06:08

/src/proftpd/lib/pr_fnmatch_loop.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 1991-1993,1996-2001,2003-2005,2007
2
   Free Software Foundation, Inc.
3
   This file is part of the GNU C Library.
4
5
   The GNU C Library is free software; you can redistribute it and/or
6
   modify it under the terms of the GNU Lesser General Public
7
   License as published by the Free Software Foundation; either
8
   version 2.1 of the License, or (at your option) any later version.
9
10
   The GNU C Library is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
   Lesser General Public License for more details.
14
15
   You should have received a copy of the GNU Lesser General Public
16
   License along with the GNU C Library; if not, write to the Free
17
   Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA
18
   02110-1335, USA.  */
19
20
/* This file comes from the GNU C Library and has been modified for use in
21
 * ProFTPD.
22
 *
23
 * Changes are released under the GNU Public License, version 2.
24
 * Copyright (C) 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
25
 * Copyright (C) 2010-2013 The ProFTPD Project
26
 */
27
28
/* AIX requires this to be the first thing in the file.  */
29
#if defined _AIX && !defined __GNUC__
30
 #pragma alloca
31
#endif
32
33
#include <config.h>
34
35
struct STRUCT
36
{
37
  const CHAR *pattern;
38
  const CHAR *string;
39
  int no_leading_period;
40
};
41
42
/* Match STRING against the filename pattern PATTERN, returning zero if
43
   it matches, nonzero if not.  */
44
static int FCT (const CHAR *pattern, const CHAR *string,
45
    const CHAR *string_end, int no_leading_period, int flags,
46
    struct STRUCT *ends)
47
     internal_function;
48
static int EXT (INT opt, const CHAR *pattern, const CHAR *string,
49
    const CHAR *string_end, int no_leading_period, int flags)
50
     internal_function;
51
static const CHAR *END (const CHAR *patternp) internal_function;
52
53
#ifndef HAVE_MEMPCPY
54
/* Copy N bytes of SRC to DEST, return pointer to bytes after the
55
   last written byte.  */
56
static void *
57
__mempcpy (void *dest, const void *src, size_t n)
58
{
59
  return (char *) memcpy (dest, src, n) + n;
60
}
61
#endif
62
63
static int
64
internal_function
65
FCT (pattern, string, string_end, no_leading_period, flags, ends)
66
     const CHAR *pattern;
67
     const CHAR *string;
68
     const CHAR *string_end;
69
     int no_leading_period;
70
     int flags;
71
     struct STRUCT *ends;
72
0
{
73
0
  register const CHAR *p = pattern, *n = string;
74
0
  register UCHAR c;
75
0
  int is_seqval = 0;
76
#ifdef _LIBC
77
# if WIDE_CHAR_VERSION
78
  const char *collseq = (const char *)
79
    _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQWC);
80
# else
81
  const UCHAR *collseq = (const UCHAR *)
82
    _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQMB);
83
# endif
84
#endif
85
86
0
  while ((c = *p++) != L('\0'))
87
0
    {
88
0
      int new_no_leading_period = 0;
89
0
      c = FOLD (c);
90
91
0
      switch (c)
92
0
  {
93
0
  case L('?'):
94
0
    if (__builtin_expect (flags & PR_FNM_EXTMATCH, 0) && *p == '(')
95
0
      {
96
0
        int res;
97
98
0
        res = EXT (c, p, n, string_end, no_leading_period,
99
0
       flags);
100
0
        if (res != -1)
101
0
    return res;
102
0
      }
103
104
0
    if (n == string_end)
105
0
      return PR_FNM_NOMATCH;
106
0
    else if (*n == L('/') && (flags & PR_FNM_FILE_NAME))
107
0
      return PR_FNM_NOMATCH;
108
0
    else if (*n == L('.') && no_leading_period)
109
0
      return PR_FNM_NOMATCH;
110
0
    break;
111
112
0
  case L('\\'):
113
0
    if (!(flags & PR_FNM_NOESCAPE))
114
0
      {
115
0
        c = *p++;
116
0
        if (c == L('\0'))
117
    /* Trailing \ loses.  */
118
0
    return PR_FNM_NOMATCH;
119
0
        c = FOLD (c);
120
0
      }
121
0
    if (n == string_end || FOLD ((UCHAR) *n) != c)
122
0
      return PR_FNM_NOMATCH;
123
0
    break;
124
125
0
  case L('*'):
126
0
    if (__builtin_expect (flags & PR_FNM_EXTMATCH, 0) && *p == '(')
127
0
      {
128
0
        int res;
129
130
0
        res = EXT (c, p, n, string_end, no_leading_period,
131
0
       flags);
132
0
        if (res != -1)
133
0
    return res;
134
0
      }
135
0
    else if (ends != NULL)
136
0
      {
137
0
        ends->pattern = p - 1;
138
0
        ends->string = n;
139
0
        ends->no_leading_period = no_leading_period;
140
0
        return 0;
141
0
      }
142
143
0
    if (n != string_end && *n == L('.') && no_leading_period)
144
0
      return PR_FNM_NOMATCH;
145
146
0
    for (c = *p++; c == L('?') || c == L('*'); c = *p++)
147
0
      {
148
0
        if (*p == L('(') && (flags & PR_FNM_EXTMATCH) != 0)
149
0
    {
150
0
      const CHAR *endp = END (p);
151
0
      if (endp != p)
152
0
        {
153
          /* This is a pattern.  Skip over it.  */
154
0
          p = endp;
155
0
          continue;
156
0
        }
157
0
    }
158
159
0
        if (c == L('?'))
160
0
    {
161
      /* A ? needs to match one character.  */
162
0
      if (n == string_end)
163
        /* There isn't another character; no match.  */
164
0
        return PR_FNM_NOMATCH;
165
0
      else if (*n == L('/')
166
0
         && __builtin_expect (flags & PR_FNM_FILE_NAME, 0))
167
        /* A slash does not match a wildcard under
168
           PR_FNM_FILE_NAME.  */
169
0
        return PR_FNM_NOMATCH;
170
0
      else
171
        /* One character of the string is consumed in matching
172
           this ? wildcard, so *??? won't match if there are
173
           less than three characters.  */
174
0
        ++n;
175
0
    }
176
0
      }
177
178
0
    if (c == L('\0'))
179
      /* The wildcard(s) is/are the last element of the pattern.
180
         If the name is a file name and contains another slash
181
         this means it cannot match, unless the PR_FNM_LEADING_DIR
182
         flag is set.  */
183
0
      {
184
0
        int result = (flags & PR_FNM_FILE_NAME) == 0 ? 0 : PR_FNM_NOMATCH;
185
186
0
        if (flags & PR_FNM_FILE_NAME)
187
0
    {
188
0
      if (flags & PR_FNM_LEADING_DIR)
189
0
        result = 0;
190
0
      else
191
0
        {
192
0
          if (MEMCHR (n, L('/'), string_end - n) == NULL)
193
0
      result = 0;
194
0
        }
195
0
    }
196
197
0
        return result;
198
0
      }
199
0
    else
200
0
      {
201
0
        const CHAR *endp;
202
0
        struct STRUCT end;
203
204
0
        end.pattern = NULL;
205
0
        endp = MEMCHR (n, (flags & PR_FNM_FILE_NAME) ? L('/') : L('\0'),
206
0
           string_end - n);
207
0
        if (endp == NULL)
208
0
    endp = string_end;
209
210
0
        if (c == L('[')
211
0
      || (__builtin_expect (flags & PR_FNM_EXTMATCH, 0) != 0
212
0
          && (c == L('@') || c == L('+') || c == L('!'))
213
0
          && *p == L('(')))
214
0
    {
215
0
      int flags2 = ((flags & PR_FNM_FILE_NAME)
216
0
        ? flags : (flags & ~PR_FNM_PERIOD));
217
218
0
      for (--p; n < endp; ++n, no_leading_period = 0)
219
0
        if (FCT (p, n, string_end, no_leading_period, flags2,
220
0
           &end) == 0)
221
0
          goto found;
222
0
    }
223
0
        else if (c == L('/') && (flags & PR_FNM_FILE_NAME))
224
0
    {
225
0
      while (n < string_end && *n != L('/'))
226
0
        ++n;
227
0
      if (n < string_end && *n == L('/')
228
0
          && (FCT (p, n + 1, string_end, flags & PR_FNM_PERIOD, flags,
229
0
             NULL) == 0))
230
0
        return 0;
231
0
    }
232
0
        else
233
0
    {
234
0
      int flags2 = ((flags & PR_FNM_FILE_NAME)
235
0
        ? flags : (flags & ~PR_FNM_PERIOD));
236
237
0
      if (c == L('\\') && !(flags & PR_FNM_NOESCAPE))
238
0
        c = *p;
239
0
      c = FOLD (c);
240
0
      for (--p; n < endp; ++n, no_leading_period = 0)
241
0
        if (FOLD ((UCHAR) *n) == c
242
0
      && (FCT (p, n, string_end, no_leading_period, flags2,
243
0
         &end) == 0))
244
0
          {
245
0
          found:
246
0
      if (end.pattern == NULL)
247
0
        return 0;
248
0
      break;
249
0
          }
250
0
      if (end.pattern != NULL)
251
0
        {
252
0
          p = end.pattern;
253
0
          n = end.string;
254
0
          no_leading_period = end.no_leading_period;
255
0
          continue;
256
0
        }
257
0
    }
258
0
      }
259
260
    /* If we come here no match is possible with the wildcard.  */
261
0
    return PR_FNM_NOMATCH;
262
263
0
  case L('['):
264
0
    {
265
      /* Nonzero if the sense of the character class is inverted.  */
266
0
      register int not;
267
0
      CHAR cold;
268
0
      UCHAR fn;
269
270
0
      if (posixly_correct == 0)
271
0
        posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
272
273
0
      if (n == string_end)
274
0
        return PR_FNM_NOMATCH;
275
276
0
      if (*n == L('.') && no_leading_period)
277
0
        return PR_FNM_NOMATCH;
278
279
0
      if (*n == L('/') && (flags & PR_FNM_FILE_NAME))
280
        /* `/' cannot be matched.  */
281
0
        return PR_FNM_NOMATCH;
282
283
0
      not = (*p == L('!') || (posixly_correct < 0 && *p == L('^')));
284
0
      if (not)
285
0
        ++p;
286
287
0
      fn = FOLD ((UCHAR) *n);
288
289
0
      c = *p++;
290
0
      for (;;)
291
0
        {
292
0
    if (!(flags & PR_FNM_NOESCAPE) && c == L('\\'))
293
0
      {
294
0
        if (*p == L('\0'))
295
0
          return PR_FNM_NOMATCH;
296
0
        c = FOLD ((UCHAR) *p);
297
0
        ++p;
298
299
0
        goto normal_bracket;
300
0
      }
301
0
    else if (c == L('[') && *p == L(':'))
302
0
      {
303
        /* Leave room for the null.  */
304
0
        CHAR str[CHAR_CLASS_MAX_LENGTH + 1];
305
0
        size_t c1 = 0;
306
#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
307
        wctype_t wt;
308
#endif
309
0
        const CHAR *startp = p;
310
311
0
        for (;;)
312
0
          {
313
0
      if (c1 == CHAR_CLASS_MAX_LENGTH)
314
        /* The name is too long and therefore the pattern
315
           is ill-formed.  */
316
0
        return PR_FNM_NOMATCH;
317
318
0
      c = *++p;
319
0
      if (c == L(':') && p[1] == L(']'))
320
0
        {
321
0
          p += 2;
322
0
          break;
323
0
        }
324
0
      if (c < L('a') || c >= L('z'))
325
0
        {
326
          /* This cannot possibly be a character class name.
327
             Match it as a normal range.  */
328
0
          p = startp;
329
0
          c = L('[');
330
0
          goto normal_bracket;
331
0
        }
332
0
      str[c1++] = c;
333
0
          }
334
0
        str[c1] = L('\0');
335
336
#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
337
        wt = IS_CHAR_CLASS (str);
338
        if (wt == 0)
339
          /* Invalid character class name.  */
340
          return PR_FNM_NOMATCH;
341
342
# if defined _LIBC && ! WIDE_CHAR_VERSION
343
        /* The following code is glibc specific but does
344
           there a good job in speeding up the code since
345
           we can avoid the btowc() call.  */
346
        if (_ISCTYPE ((UCHAR) *n, wt))
347
          goto matched;
348
# else
349
        if (ISWCTYPE (BTOWC ((UCHAR) *n), wt))
350
          goto matched;
351
# endif
352
#else
353
0
        if ((STREQ (str, L("alnum")) && ISALNUM ((UCHAR) *n))
354
0
      || (STREQ (str, L("alpha")) && ISALPHA ((UCHAR) *n))
355
0
      || (STREQ (str, L("blank")) && ISBLANK ((UCHAR) *n))
356
0
      || (STREQ (str, L("cntrl")) && ISCNTRL ((UCHAR) *n))
357
0
      || (STREQ (str, L("digit")) && ISDIGIT ((UCHAR) *n))
358
0
      || (STREQ (str, L("graph")) && ISGRAPH ((UCHAR) *n))
359
0
      || (STREQ (str, L("lower")) && ISLOWER ((UCHAR) *n))
360
0
      || (STREQ (str, L("print")) && ISPRINT ((UCHAR) *n))
361
0
      || (STREQ (str, L("punct")) && ISPUNCT ((UCHAR) *n))
362
0
      || (STREQ (str, L("space")) && ISSPACE ((UCHAR) *n))
363
0
      || (STREQ (str, L("upper")) && ISUPPER ((UCHAR) *n))
364
0
      || (STREQ (str, L("xdigit")) && ISXDIGIT ((UCHAR) *n)))
365
0
          goto matched;
366
0
#endif
367
0
        c = *p++;
368
0
      }
369
#ifdef _LIBC
370
    else if (c == L('[') && *p == L('='))
371
      {
372
        UCHAR str[1];
373
        uint32_t nrules =
374
          _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
375
        const CHAR *startp = p;
376
377
        c = *++p;
378
        if (c == L('\0'))
379
          {
380
      p = startp;
381
      c = L('[');
382
      goto normal_bracket;
383
          }
384
        str[0] = c;
385
386
        c = *++p;
387
        if (c != L('=') || p[1] != L(']'))
388
          {
389
      p = startp;
390
      c = L('[');
391
      goto normal_bracket;
392
          }
393
        p += 2;
394
395
        if (nrules == 0)
396
          {
397
      if ((UCHAR) *n == str[0])
398
        goto matched;
399
          }
400
        else
401
          {
402
      const int32_t *table;
403
# if WIDE_CHAR_VERSION
404
      const int32_t *weights;
405
      const int32_t *extra;
406
# else
407
      const unsigned char *weights;
408
      const unsigned char *extra;
409
# endif
410
      const int32_t *indirect;
411
      int32_t idx;
412
      const UCHAR *cp = (const UCHAR *) str;
413
414
      /* This #include defines a local function!  */
415
# if WIDE_CHAR_VERSION
416
#  include <locale/weightwc.h>
417
# else
418
#  include <locale/weight.h>
419
# endif
420
421
# if WIDE_CHAR_VERSION
422
      table = (const int32_t *)
423
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEWC);
424
      weights = (const int32_t *)
425
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTWC);
426
      extra = (const int32_t *)
427
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAWC);
428
      indirect = (const int32_t *)
429
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTWC);
430
# else
431
      table = (const int32_t *)
432
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
433
      weights = (const unsigned char *)
434
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
435
      extra = (const unsigned char *)
436
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
437
      indirect = (const int32_t *)
438
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
439
# endif
440
441
      idx = findidx (&cp);
442
      if (idx != 0)
443
        {
444
          /* We found a table entry.  Now see whether the
445
             character we are currently at has the same
446
             equivalance class value.  */
447
          int len = weights[idx & 0xffffff];
448
          int32_t idx2;
449
          const UCHAR *np = (const UCHAR *) n;
450
451
          idx2 = findidx (&np);
452
          if (idx2 != 0
453
        && (idx >> 24) == (idx2 >> 24)
454
        && len == weights[idx2 & 0xffffff])
455
            {
456
        int cnt = 0;
457
458
        idx &= 0xffffff;
459
        idx2 &= 0xffffff;
460
461
        while (cnt < len
462
               && (weights[idx + 1 + cnt]
463
             == weights[idx2 + 1 + cnt]))
464
          ++cnt;
465
466
        if (cnt == len)
467
          goto matched;
468
            }
469
        }
470
          }
471
472
        c = *p++;
473
      }
474
#endif
475
0
    else if (c == L('\0'))
476
      /* [ (unterminated) loses.  */
477
0
      return PR_FNM_NOMATCH;
478
0
    else
479
0
      {
480
0
        int is_range = 0;
481
482
#ifdef _LIBC
483
        is_seqval = 0;
484
485
        if (c == L('[') && *p == L('.'))
486
          {
487
      uint32_t nrules =
488
        _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
489
      const CHAR *startp = p;
490
      size_t c1 = 0;
491
492
      while (1)
493
        {
494
          c = *++p;
495
          if (c == L('.') && p[1] == L(']'))
496
            {
497
        p += 2;
498
        break;
499
            }
500
          if (c == '\0')
501
            return PR_FNM_NOMATCH;
502
          ++c1;
503
        }
504
505
      /* We have to handling the symbols differently in
506
         ranges since then the collation sequence is
507
         important.  */
508
      is_range = *p == L('-') && p[1] != L('\0');
509
510
      if (nrules == 0)
511
        {
512
          /* There are no names defined in the collation
513
             data.  Therefore we only accept the trivial
514
             names consisting of the character itself.  */
515
          if (c1 != 1)
516
            return PR_FNM_NOMATCH;
517
518
          if (!is_range && *n == startp[1])
519
            goto matched;
520
521
          cold = startp[1];
522
          c = *p++;
523
        }
524
      else
525
        {
526
          int32_t table_size;
527
          const int32_t *symb_table;
528
# ifdef WIDE_CHAR_VERSION
529
          char str[c1];
530
          unsigned int strcnt;
531
# else
532
#  define str (startp + 1)
533
# endif
534
          const unsigned char *extra;
535
          int32_t idx;
536
          int32_t elem;
537
          int32_t second;
538
          int32_t hash;
539
540
# ifdef WIDE_CHAR_VERSION
541
          /* We have to convert the name to a single-byte
542
             string.  This is possible since the names
543
             consist of ASCII characters and the internal
544
             representation is UCS4.  */
545
          for (strcnt = 0; strcnt < c1; ++strcnt)
546
            str[strcnt] = startp[1 + strcnt];
547
#endif
548
549
          table_size =
550
            _NL_CURRENT_WORD (LC_COLLATE,
551
            _NL_COLLATE_SYMB_HASH_SIZEMB);
552
          symb_table = (const int32_t *)
553
            _NL_CURRENT (LC_COLLATE,
554
             _NL_COLLATE_SYMB_TABLEMB);
555
          extra = (const unsigned char *)
556
            _NL_CURRENT (LC_COLLATE,
557
             _NL_COLLATE_SYMB_EXTRAMB);
558
559
          /* Locate the character in the hashing table.  */
560
          hash = elem_hash (str, c1);
561
562
          idx = 0;
563
          elem = hash % table_size;
564
          if (symb_table[2 * elem] != 0)
565
            {
566
        second = hash % (table_size - 2) + 1;
567
568
        do
569
          {
570
            /* First compare the hashing value.  */
571
            if (symb_table[2 * elem] == hash
572
          && (c1
573
              == extra[symb_table[2 * elem + 1]])
574
          && memcmp (str,
575
               &extra[symb_table[2 * elem
576
                     + 1]
577
                + 1], c1) == 0)
578
              {
579
          /* Yep, this is the entry.  */
580
          idx = symb_table[2 * elem + 1];
581
          idx += 1 + extra[idx];
582
          break;
583
              }
584
585
            /* Next entry.  */
586
            elem += second;
587
          }
588
        while (symb_table[2 * elem] != 0);
589
            }
590
591
          if (symb_table[2 * elem] != 0)
592
            {
593
        /* Compare the byte sequence but only if
594
           this is not part of a range.  */
595
# ifdef WIDE_CHAR_VERSION
596
        int32_t *wextra;
597
598
        idx += 1 + extra[idx];
599
        /* Adjust for the alignment.  */
600
        idx = (idx + 3) & ~3;
601
602
        wextra = (int32_t *) &extra[idx + 4];
603
# endif
604
605
        if (! is_range)
606
          {
607
# ifdef WIDE_CHAR_VERSION
608
            for (c1 = 0;
609
           (int32_t) c1 < wextra[idx];
610
           ++c1)
611
              if (n[c1] != wextra[1 + c1])
612
          break;
613
614
            if ((int32_t) c1 == wextra[idx])
615
              goto matched;
616
# else
617
            for (c1 = 0; c1 < extra[idx]; ++c1)
618
              if (n[c1] != extra[1 + c1])
619
          break;
620
621
            if (c1 == extra[idx])
622
              goto matched;
623
# endif
624
          }
625
626
        /* Get the collation sequence value.  */
627
        is_seqval = 1;
628
# ifdef WIDE_CHAR_VERSION
629
        cold = wextra[1 + wextra[idx]];
630
# else
631
        /* Adjust for the alignment.  */
632
        idx += 1 + extra[idx];
633
        idx = (idx + 3) & ~4;
634
        cold = *((int32_t *) &extra[idx]);
635
# endif
636
637
        c = *p++;
638
            }
639
          else if (c1 == 1)
640
            {
641
        /* No valid character.  Match it as a
642
           single byte.  */
643
        if (!is_range && *n == str[0])
644
          goto matched;
645
646
        cold = str[0];
647
        c = *p++;
648
            }
649
          else
650
            return PR_FNM_NOMATCH;
651
        }
652
          }
653
        else
654
# undef str
655
#endif
656
0
          {
657
0
      c = FOLD (c);
658
0
          normal_bracket:
659
660
      /* We have to handling the symbols differently in
661
         ranges since then the collation sequence is
662
         important.  */
663
0
      is_range = (*p == L('-') && p[1] != L('\0')
664
0
            && p[1] != L(']'));
665
666
0
      if (!is_range && c == fn)
667
0
        goto matched;
668
669
      /* This is needed if we goto normal_bracket; from
670
         outside of is_seqval's scope.  */
671
0
      is_seqval = 0;
672
0
      cold = c;
673
0
      c = *p++;
674
0
          }
675
676
0
        if (c == L('-') && *p != L(']'))
677
0
          {
678
#ifdef _LIBC
679
      /* We have to find the collation sequence
680
         value for C.  Collation sequence is nothing
681
         we can regularly access.  The sequence
682
         value is defined by the order in which the
683
         definitions of the collation values for the
684
         various characters appear in the source
685
         file.  A strange concept, nowhere
686
         documented.  */
687
      uint32_t fcollseq;
688
      uint32_t lcollseq;
689
      UCHAR cend = *p++;
690
691
# ifdef WIDE_CHAR_VERSION
692
      /* Search in the `names' array for the characters.  */
693
      fcollseq = __collseq_table_lookup (collseq, fn);
694
      if (fcollseq == ~((uint32_t) 0))
695
        /* XXX We don't know anything about the character
696
           we are supposed to match.  This means we are
697
           failing.  */
698
        goto range_not_matched;
699
700
      if (is_seqval)
701
        lcollseq = cold;
702
      else
703
        lcollseq = __collseq_table_lookup (collseq, cold);
704
# else
705
      fcollseq = collseq[fn];
706
      lcollseq = is_seqval ? cold : collseq[(UCHAR) cold];
707
# endif
708
709
      is_seqval = 0;
710
      if (cend == L('[') && *p == L('.'))
711
        {
712
          uint32_t nrules =
713
            _NL_CURRENT_WORD (LC_COLLATE,
714
            _NL_COLLATE_NRULES);
715
          const CHAR *startp = p;
716
          size_t c1 = 0;
717
718
          while (1)
719
            {
720
        c = *++p;
721
        if (c == L('.') && p[1] == L(']'))
722
          {
723
            p += 2;
724
            break;
725
          }
726
        if (c == '\0')
727
          return PR_FNM_NOMATCH;
728
        ++c1;
729
            }
730
731
          if (nrules == 0)
732
            {
733
        /* There are no names defined in the
734
           collation data.  Therefore we only
735
           accept the trivial names consisting
736
           of the character itself.  */
737
        if (c1 != 1)
738
          return PR_FNM_NOMATCH;
739
740
        cend = startp[1];
741
            }
742
          else
743
            {
744
        int32_t table_size;
745
        const int32_t *symb_table;
746
# ifdef WIDE_CHAR_VERSION
747
        char str[c1];
748
        unsigned int strcnt;
749
# else
750
#  define str (startp + 1)
751
# endif
752
        const unsigned char *extra;
753
        int32_t idx;
754
        int32_t elem;
755
        int32_t second;
756
        int32_t hash;
757
758
# ifdef WIDE_CHAR_VERSION
759
        /* We have to convert the name to a single-byte
760
           string.  This is possible since the names
761
           consist of ASCII characters and the internal
762
           representation is UCS4.  */
763
        for (strcnt = 0; strcnt < c1; ++strcnt)
764
          str[strcnt] = startp[1 + strcnt];
765
# endif
766
767
        table_size =
768
          _NL_CURRENT_WORD (LC_COLLATE,
769
                _NL_COLLATE_SYMB_HASH_SIZEMB);
770
        symb_table = (const int32_t *)
771
          _NL_CURRENT (LC_COLLATE,
772
                 _NL_COLLATE_SYMB_TABLEMB);
773
        extra = (const unsigned char *)
774
          _NL_CURRENT (LC_COLLATE,
775
                 _NL_COLLATE_SYMB_EXTRAMB);
776
777
        /* Locate the character in the hashing
778
                                   table.  */
779
        hash = elem_hash (str, c1);
780
781
        idx = 0;
782
        elem = hash % table_size;
783
        if (symb_table[2 * elem] != 0)
784
          {
785
            second = hash % (table_size - 2) + 1;
786
787
            do
788
              {
789
          /* First compare the hashing value.  */
790
          if (symb_table[2 * elem] == hash
791
              && (c1
792
            == extra[symb_table[2 * elem + 1]])
793
              && memcmp (str,
794
                   &extra[symb_table[2 * elem + 1]
795
                    + 1], c1) == 0)
796
            {
797
              /* Yep, this is the entry.  */
798
              idx = symb_table[2 * elem + 1];
799
              idx += 1 + extra[idx];
800
              break;
801
            }
802
803
          /* Next entry.  */
804
          elem += second;
805
              }
806
            while (symb_table[2 * elem] != 0);
807
          }
808
809
        if (symb_table[2 * elem] != 0)
810
          {
811
            /* Compare the byte sequence but only if
812
               this is not part of a range.  */
813
# ifdef WIDE_CHAR_VERSION
814
            int32_t *wextra;
815
816
            idx += 1 + extra[idx];
817
            /* Adjust for the alignment.  */
818
            idx = (idx + 3) & ~4;
819
820
            wextra = (int32_t *) &extra[idx + 4];
821
# endif
822
            /* Get the collation sequence value.  */
823
            is_seqval = 1;
824
# ifdef WIDE_CHAR_VERSION
825
            cend = wextra[1 + wextra[idx]];
826
# else
827
            /* Adjust for the alignment.  */
828
            idx += 1 + extra[idx];
829
            idx = (idx + 3) & ~4;
830
            cend = *((int32_t *) &extra[idx]);
831
# endif
832
          }
833
        else if (symb_table[2 * elem] != 0 && c1 == 1)
834
          {
835
            cend = str[0];
836
            c = *p++;
837
          }
838
        else
839
          return PR_FNM_NOMATCH;
840
            }
841
# undef str
842
        }
843
      else
844
        {
845
          if (!(flags & PR_FNM_NOESCAPE) && cend == L('\\'))
846
            cend = *p++;
847
          if (cend == L('\0'))
848
            return PR_FNM_NOMATCH;
849
          cend = FOLD (cend);
850
        }
851
852
      /* XXX It is not entirely clear to me how to handle
853
         characters which are not mentioned in the
854
         collation specification.  */
855
      if (
856
# ifdef WIDE_CHAR_VERSION
857
          lcollseq == 0xffffffff ||
858
# endif
859
          lcollseq <= fcollseq)
860
        {
861
          /* We have to look at the upper bound.  */
862
          uint32_t hcollseq;
863
864
          if (is_seqval)
865
            hcollseq = cend;
866
          else
867
            {
868
# ifdef WIDE_CHAR_VERSION
869
        hcollseq =
870
          __collseq_table_lookup (collseq, cend);
871
        if (hcollseq == ~((uint32_t) 0))
872
          {
873
            /* Hum, no information about the upper
874
               bound.  The matching succeeds if the
875
               lower bound is matched exactly.  */
876
            if (lcollseq != fcollseq)
877
              goto range_not_matched;
878
879
            goto matched;
880
          }
881
# else
882
        hcollseq = collseq[cend];
883
# endif
884
            }
885
886
          if (lcollseq <= hcollseq && fcollseq <= hcollseq)
887
            goto matched;
888
        }
889
# ifdef WIDE_CHAR_VERSION
890
          range_not_matched:
891
# endif
892
#else
893
      /* We use a boring value comparison of the character
894
         values.  This is better than comparing using
895
         `strcoll' since the latter would have surprising
896
         and sometimes fatal consequences.  */
897
0
      UCHAR cend = *p++;
898
899
0
      if (!(flags & PR_FNM_NOESCAPE) && cend == L('\\'))
900
0
        cend = *p++;
901
0
      if (cend == L('\0'))
902
0
        return PR_FNM_NOMATCH;
903
904
      /* It is a range.  */
905
0
      if (cold <= fn && fn <= cend)
906
0
        goto matched;
907
0
#endif
908
909
0
      c = *p++;
910
0
          }
911
0
      }
912
913
0
    if (c == L(']'))
914
0
      break;
915
0
        }
916
917
0
      if (!not)
918
0
        return PR_FNM_NOMATCH;
919
0
      break;
920
921
0
    matched:
922
      /* Skip the rest of the [...] that already matched.  */
923
0
      do
924
0
        {
925
0
        ignore_next:
926
0
    c = *p++;
927
928
0
    if (c == L('\0'))
929
      /* [... (unterminated) loses.  */
930
0
      return PR_FNM_NOMATCH;
931
932
0
    if (!(flags & PR_FNM_NOESCAPE) && c == L('\\'))
933
0
      {
934
0
        if (*p == L('\0'))
935
0
          return PR_FNM_NOMATCH;
936
        /* XXX 1003.2d11 is unclear if this is right.  */
937
0
        ++p;
938
0
      }
939
0
    else if (c == L('[') && *p == L(':'))
940
0
      {
941
0
        int c1 = 0;
942
0
        const CHAR *startp = p;
943
944
0
        while (1)
945
0
          {
946
0
      c = *++p;
947
0
      if (++c1 == CHAR_CLASS_MAX_LENGTH)
948
0
        return PR_FNM_NOMATCH;
949
950
0
      if (*p == L(':') && p[1] == L(']'))
951
0
        break;
952
953
0
      if (c < L('a') || c >= L('z'))
954
0
        {
955
0
          p = startp;
956
0
          goto ignore_next;
957
0
        }
958
0
          }
959
0
        p += 2;
960
0
        c = *p++;
961
0
      }
962
0
    else if (c == L('[') && *p == L('='))
963
0
      {
964
0
        c = *++p;
965
0
        if (c == L('\0'))
966
0
          return PR_FNM_NOMATCH;
967
0
        c = *++p;
968
0
        if (c != L('=') || p[1] != L(']'))
969
0
          return PR_FNM_NOMATCH;
970
0
        p += 2;
971
0
        c = *p++;
972
0
      }
973
0
    else if (c == L('[') && *p == L('.'))
974
0
      {
975
0
        while (1)
976
0
          {
977
0
      c = *++p;
978
0
      if (c == L('\0'))
979
0
        return PR_FNM_NOMATCH;
980
981
0
      if (c == L('.') && p[1] == L(']'))
982
0
        break;
983
0
          }
984
0
        p += 2;
985
0
        c = *p++;
986
0
      }
987
0
        }
988
0
      while (c != L(']'));
989
0
      if (not)
990
0
        return PR_FNM_NOMATCH;
991
0
    }
992
0
    break;
993
994
0
  case L('+'):
995
0
  case L('@'):
996
0
  case L('!'):
997
0
    if (__builtin_expect (flags & PR_FNM_EXTMATCH, 0) && *p == '(')
998
0
      {
999
0
        int res;
1000
1001
0
        res = EXT (c, p, n, string_end, no_leading_period, flags);
1002
0
        if (res != -1)
1003
0
    return res;
1004
0
      }
1005
0
    goto normal_match;
1006
1007
0
  case L('/'):
1008
0
    if (NO_LEADING_PERIOD (flags))
1009
0
      {
1010
0
        if (n == string_end || c != (UCHAR) *n)
1011
0
    return PR_FNM_NOMATCH;
1012
1013
0
        new_no_leading_period = 1;
1014
0
        break;
1015
0
      }
1016
    /* FALLTHROUGH */
1017
0
  default:
1018
0
  normal_match:
1019
0
    if (n == string_end || c != FOLD ((UCHAR) *n))
1020
0
      return PR_FNM_NOMATCH;
1021
0
  }
1022
1023
0
      no_leading_period = new_no_leading_period;
1024
0
      ++n;
1025
0
    }
1026
1027
0
  if (n == string_end)
1028
0
    return 0;
1029
1030
0
  if ((flags & PR_FNM_LEADING_DIR) && n != string_end && *n == L('/'))
1031
    /* The PR_FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
1032
0
    return 0;
1033
1034
0
  return PR_FNM_NOMATCH;
1035
0
}
1036
1037
1038
static const CHAR *
1039
internal_function
1040
END (const CHAR *pattern)
1041
0
{
1042
0
  const CHAR *p = pattern;
1043
1044
0
  while (1)
1045
0
    if (*++p == L('\0'))
1046
      /* This is an invalid pattern.  */
1047
0
      return pattern;
1048
0
    else if (*p == L('['))
1049
0
      {
1050
  /* Handle brackets special.  */
1051
0
  if (posixly_correct == 0)
1052
0
    posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
1053
1054
  /* Skip the not sign.  We have to recognize it because of a possibly
1055
     following ']'.  */
1056
0
  if (*++p == L('!') || (posixly_correct < 0 && *p == L('^')))
1057
0
    ++p;
1058
  /* A leading ']' is recognized as such.  */
1059
0
  if (*p == L(']'))
1060
0
    ++p;
1061
  /* Skip over all characters of the list.  */
1062
0
  while (*p != L(']'))
1063
0
    if (*p++ == L('\0'))
1064
      /* This is no valid pattern.  */
1065
0
      return pattern;
1066
0
      }
1067
0
    else if ((*p == L('?') || *p == L('*') || *p == L('+') || *p == L('@')
1068
0
        || *p == L('!')) && p[1] == L('('))
1069
0
      p = END (p + 1);
1070
0
    else if (*p == L(')'))
1071
0
      break;
1072
1073
0
  return p + 1;
1074
0
}
1075
1076
1077
static int
1078
internal_function
1079
EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end,
1080
     int no_leading_period, int flags)
1081
0
{
1082
0
  const CHAR *startp;
1083
0
  int level;
1084
0
  struct patternlist
1085
0
  {
1086
0
    struct patternlist *next;
1087
0
    CHAR str[1];
1088
0
  } *list = NULL;
1089
0
  struct patternlist **lastp = &list;
1090
0
  size_t pattern_len = STRLEN (pattern);
1091
0
  const CHAR *p;
1092
0
  const CHAR *rs;
1093
1094
  /* Parse the pattern.  Store the individual parts in the list.  */
1095
0
  level = 0;
1096
0
  for (startp = p = pattern + 1; level >= 0; ++p)
1097
0
    if (*p == L('\0'))
1098
      /* This is an invalid pattern.  */
1099
0
      return -1;
1100
0
    else if (*p == L('['))
1101
0
      {
1102
  /* Handle brackets special.  */
1103
0
  if (posixly_correct == 0)
1104
0
    posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
1105
1106
  /* Skip the not sign.  We have to recognize it because of a possibly
1107
     following ']'.  */
1108
0
  if (*++p == L('!') || (posixly_correct < 0 && *p == L('^')))
1109
0
    ++p;
1110
  /* A leading ']' is recognized as such.  */
1111
0
  if (*p == L(']'))
1112
0
    ++p;
1113
  /* Skip over all characters of the list.  */
1114
0
  while (*p != L(']'))
1115
0
    if (*p++ == L('\0'))
1116
      /* This is no valid pattern.  */
1117
0
      return -1;
1118
0
      }
1119
0
    else if ((*p == L('?') || *p == L('*') || *p == L('+') || *p == L('@')
1120
0
        || *p == L('!')) && p[1] == L('('))
1121
      /* Remember the nesting level.  */
1122
0
      ++level;
1123
0
    else if (*p == L(')'))
1124
0
      {
1125
0
  if (level-- == 0)
1126
0
    {
1127
      /* This means we found the end of the pattern.  */
1128
0
#define NEW_PATTERN \
1129
0
      struct patternlist *newp;               \
1130
0
                        \
1131
0
      if (opt == L('?') || opt == L('@'))              \
1132
0
        newp = __alloca (sizeof (struct patternlist)         \
1133
0
           + (pattern_len * sizeof (CHAR)));          \
1134
0
      else                    \
1135
0
        newp = __alloca (sizeof (struct patternlist)         \
1136
0
           + ((p - startp + 1) * sizeof (CHAR)));       \
1137
0
      *((CHAR *) MEMPCPY (newp->str, startp, p - startp)) = L('\0');    \
1138
0
      newp->next = NULL;                  \
1139
0
      *lastp = newp;                  \
1140
0
      lastp = &newp->next
1141
0
      NEW_PATTERN;
1142
0
    }
1143
0
      }
1144
0
    else if (*p == L('|'))
1145
0
      {
1146
0
  if (level == 0)
1147
0
    {
1148
0
      NEW_PATTERN;
1149
0
      startp = p + 1;
1150
0
    }
1151
0
      }
1152
0
#undef NEW_PATTERN
1153
1154
0
  switch (opt)
1155
0
    {
1156
0
    case L('*'):
1157
0
      if (FCT (p, string, string_end, no_leading_period, flags, NULL) == 0)
1158
0
  return 0;
1159
      /* FALLTHROUGH */
1160
1161
0
    case L('+'):
1162
0
      do
1163
0
  {
1164
0
    for (rs = string; rs <= string_end; ++rs)
1165
      /* First match the prefix with the current pattern with the
1166
         current pattern.  */
1167
0
      if (FCT (list->str, string, rs, no_leading_period,
1168
0
         flags & PR_FNM_FILE_NAME ? flags : flags & ~PR_FNM_PERIOD,
1169
0
         NULL) == 0
1170
    /* This was successful.  Now match the rest with the rest
1171
       of the pattern.  */
1172
0
    && (FCT (p, rs, string_end,
1173
0
       rs == string
1174
0
       ? no_leading_period
1175
0
       : rs[-1] == '/' && NO_LEADING_PERIOD (flags) ? 1 : 0,
1176
0
       flags & PR_FNM_FILE_NAME
1177
0
       ? flags : flags & ~PR_FNM_PERIOD, NULL) == 0
1178
        /* This didn't work.  Try the whole pattern.  */
1179
0
        || (rs != string
1180
0
      && FCT (pattern - 1, rs, string_end,
1181
0
        rs == string
1182
0
        ? no_leading_period
1183
0
        : (rs[-1] == '/' && NO_LEADING_PERIOD (flags)
1184
0
           ? 1 : 0),
1185
0
        flags & PR_FNM_FILE_NAME
1186
0
        ? flags : flags & ~PR_FNM_PERIOD, NULL) == 0)))
1187
        /* It worked.  Signal success.  */
1188
0
        return 0;
1189
0
  }
1190
0
      while ((list = list->next) != NULL);
1191
1192
      /* None of the patterns lead to a match.  */
1193
0
      return PR_FNM_NOMATCH;
1194
1195
0
    case L('?'):
1196
0
      if (FCT (p, string, string_end, no_leading_period, flags, NULL) == 0)
1197
0
  return 0;
1198
      /* FALLTHROUGH */
1199
1200
0
    case L('@'):
1201
0
      do
1202
  /* I cannot believe it but `strcat' is actually acceptable
1203
     here.  Match the entire string with the prefix from the
1204
     pattern list and the rest of the pattern following the
1205
     pattern list.  */
1206
0
  if (FCT (STRCAT (list->str, p), string, string_end,
1207
0
     no_leading_period,
1208
0
     flags & PR_FNM_FILE_NAME ? flags : flags & ~PR_FNM_PERIOD,
1209
0
     NULL) == 0)
1210
    /* It worked.  Signal success.  */
1211
0
    return 0;
1212
0
      while ((list = list->next) != NULL);
1213
1214
      /* None of the patterns lead to a match.  */
1215
0
      return PR_FNM_NOMATCH;
1216
1217
0
    case L('!'):
1218
0
      for (rs = string; rs <= string_end; ++rs)
1219
0
  {
1220
0
    struct patternlist *runp;
1221
1222
0
    for (runp = list; runp != NULL; runp = runp->next)
1223
0
      if (FCT (runp->str, string, rs,  no_leading_period,
1224
0
         flags & PR_FNM_FILE_NAME ? flags : flags & ~PR_FNM_PERIOD,
1225
0
         NULL) == 0)
1226
0
        break;
1227
1228
    /* If none of the patterns matched see whether the rest does.  */
1229
0
    if (runp == NULL
1230
0
        && (FCT (p, rs, string_end,
1231
0
           rs == string
1232
0
           ? no_leading_period
1233
0
           : rs[-1] == '/' && NO_LEADING_PERIOD (flags) ? 1 : 0,
1234
0
           flags & PR_FNM_FILE_NAME ? flags : flags & ~PR_FNM_PERIOD,
1235
0
           NULL) == 0))
1236
      /* This is successful.  */
1237
0
      return 0;
1238
0
  }
1239
1240
      /* None of the patterns together with the rest of the pattern
1241
   lead to a match.  */
1242
0
      return PR_FNM_NOMATCH;
1243
1244
0
    default:
1245
0
      break;
1246
0
    }
1247
1248
0
  return -1;
1249
0
}
1250
1251
1252
#undef FOLD
1253
#undef CHAR
1254
#undef UCHAR
1255
#undef INT
1256
#undef FCT
1257
#undef EXT
1258
#undef END
1259
#undef STRUCT
1260
#undef MEMPCPY
1261
#undef MEMCHR
1262
#undef STRCOLL
1263
#undef STRLEN
1264
#undef STRCAT
1265
#undef L
1266
#undef BTOWC