Coverage Report

Created: 2025-10-13 07:05

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