Coverage Report

Created: 2026-05-23 07:02

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