forgot 1 place that used slprintf
[rsync.git] / lib / fnmatch.c
1 #include "../rsync.h"
2 #ifndef HAVE_FNMATCH
3
4 /* ----- THE FOLLOWING UP TO 'END' is glibc-2.1.2 posix/fnmatch.c
5      except for the parts with '#if 0' */
6
7 /* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
8    This file is part of the GNU C Library.
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Library General Public License as
12    published by the Free Software Foundation; either version 2 of the
13    License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Library General Public License for more details.
19
20    You should have received a copy of the GNU Library General Public
21    License along with this library; see the file COPYING.LIB.  If not,
22    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23    Boston, MA 02111-1307, USA.  */
24
25 #if 0   /* header files included better by ../rsync.h */
26
27 #if HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 /* Enable GNU extensions in fnmatch.h.  */
32 #ifndef _GNU_SOURCE
33 # define _GNU_SOURCE    1
34 #endif
35
36 #include <errno.h>
37 #include <fnmatch.h>
38 #include <ctype.h>
39
40 #if HAVE_STRING_H || defined _LIBC
41 # include <string.h>
42 #else
43 # include <strings.h>
44 #endif
45
46 #if defined STDC_HEADERS || defined _LIBC
47 # include <stdlib.h>
48 #endif
49
50 #endif /* 0 */
51 /* For platform which support the ISO C amendement 1 functionality we
52    support user defined character classes.  */
53 #if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
54 /* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
55 # include <wchar.h>
56 # include <wctype.h>
57 #endif
58
59 /* Comment out all this code if we are using the GNU C Library, and are not
60    actually compiling the library itself.  This code is part of the GNU C
61    Library, but also included in many other GNU distributions.  Compiling
62    and linking in this code is a waste when using the GNU C library
63    (especially if it is a shared library).  Rather than having every GNU
64    program understand `configure --with-gnu-libc' and omit the object files,
65    it is simpler to just do this in the source for each such file.  */
66
67 #if 1
68
69 # if defined STDC_HEADERS || !defined isascii
70 #  define ISASCII(c) 1
71 # else
72 #  define ISASCII(c) isascii(c)
73 # endif
74
75 #ifdef isblank
76 # define ISBLANK(c) (ISASCII (c) && isblank (c))
77 #else
78 # define ISBLANK(c) ((c) == ' ' || (c) == '\t')
79 #endif
80 #ifdef isgraph
81 # define ISGRAPH(c) (ISASCII (c) && isgraph (c))
82 #else
83 # define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
84 #endif
85
86 #define ISPRINT(c) (ISASCII (c) && isprint (c))
87 #define ISDIGIT(c) (ISASCII (c) && isdigit (c))
88 #define ISALNUM(c) (ISASCII (c) && isalnum (c))
89 #define ISALPHA(c) (ISASCII (c) && isalpha (c))
90 #define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
91 #define ISLOWER(c) (ISASCII (c) && islower (c))
92 #define ISPUNCT(c) (ISASCII (c) && ispunct (c))
93 #define ISSPACE(c) (ISASCII (c) && isspace (c))
94 #define ISUPPER(c) (ISASCII (c) && isupper (c))
95 #define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
96
97 # define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
98
99 # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
100 /* The GNU C library provides support for user-defined character classes
101    and the functions from ISO C amendement 1.  */
102 #  ifdef CHARCLASS_NAME_MAX
103 #   define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
104 #  else
105 /* This shouldn't happen but some implementation might still have this
106    problem.  Use a reasonable default value.  */
107 #   define CHAR_CLASS_MAX_LENGTH 256
108 #  endif
109
110 #  ifdef _LIBC
111 #   define IS_CHAR_CLASS(string) __wctype (string)
112 #  else
113 #   define IS_CHAR_CLASS(string) wctype (string)
114 #  endif
115 # else
116 #  define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
117
118 #  define IS_CHAR_CLASS(string)                                               \
119    (STREQ (string, "alpha") || STREQ (string, "upper")                        \
120     || STREQ (string, "lower") || STREQ (string, "digit")                     \
121     || STREQ (string, "alnum") || STREQ (string, "xdigit")                    \
122     || STREQ (string, "space") || STREQ (string, "print")                     \
123     || STREQ (string, "punct") || STREQ (string, "graph")                     \
124     || STREQ (string, "cntrl") || STREQ (string, "blank"))
125 # endif
126
127 /* Avoid depending on library functions or files
128    whose names are inconsistent.  */
129
130 # if !defined _LIBC && !defined getenv
131 extern char *getenv ();
132 # endif
133
134 # ifndef errno
135 extern int errno;
136 # endif
137
138 /* Match STRING against the filename pattern PATTERN, returning zero if
139    it matches, nonzero if not.  */
140 static int
141 #ifdef _LIBC
142 internal_function
143 #endif
144 internal_fnmatch (const char *pattern, const char *string,
145                   int no_leading_period, int flags)
146 {
147   register const char *p = pattern, *n = string;
148   register unsigned char c;
149
150 /* Note that this evaluates C many times.  */
151 # ifdef _LIBC
152 #  define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
153 # else
154 #  define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
155 # endif
156
157   while ((c = *p++) != '\0')
158     {
159       c = FOLD (c);
160
161       switch (c)
162         {
163         case '?':
164           if (*n == '\0')
165             return FNM_NOMATCH;
166           else if (*n == '/' && (flags & FNM_FILE_NAME))
167             return FNM_NOMATCH;
168           else if (*n == '.' && no_leading_period
169                    && (n == string
170                        || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
171             return FNM_NOMATCH;
172           break;
173
174         case '\\':
175           if (!(flags & FNM_NOESCAPE))
176             {
177               c = *p++;
178               if (c == '\0')
179                 /* Trailing \ loses.  */
180                 return FNM_NOMATCH;
181               c = FOLD (c);
182             }
183           if (FOLD ((unsigned char) *n) != c)
184             return FNM_NOMATCH;
185           break;
186
187         case '*':
188           if (*n == '.' && no_leading_period
189               && (n == string
190                   || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
191             return FNM_NOMATCH;
192
193           for (c = *p++; c == '?' || c == '*'; c = *p++)
194             {
195               if (*n == '/' && (flags & FNM_FILE_NAME))
196                 /* A slash does not match a wildcard under FNM_FILE_NAME.  */
197                 return FNM_NOMATCH;
198               else if (c == '?')
199                 {
200                   /* A ? needs to match one character.  */
201                   if (*n == '\0')
202                     /* There isn't another character; no match.  */
203                     return FNM_NOMATCH;
204                   else
205                     /* One character of the string is consumed in matching
206                        this ? wildcard, so *??? won't match if there are
207                        less than three characters.  */
208                     ++n;
209                 }
210             }
211
212           if (c == '\0')
213             /* The wildcard(s) is/are the last element of the pattern.
214                If the name is a file name and contains another slash
215                this does mean it cannot match.  */
216             return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL
217                     ? FNM_NOMATCH : 0);
218           else
219             {
220               const char *endp;
221
222 #if 0
223               endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');
224 #else
225 /* replace call to internal glibc function with equivalent */
226               if (!(flags & FNM_FILE_NAME) || ((endp = strchr(n, '/')) == NULL))
227                 endp = n + strlen(n);
228 #endif
229
230               if (c == '[')
231                 {
232                   int flags2 = ((flags & FNM_FILE_NAME)
233                                 ? flags : (flags & ~FNM_PERIOD));
234
235                   for (--p; n < endp; ++n)
236                     if (internal_fnmatch (p, n,
237                                           (no_leading_period
238                                            && (n == string
239                                                || (n[-1] == '/'
240                                                    && (flags
241                                                        & FNM_FILE_NAME)))),
242                                           flags2)
243                         == 0)
244                       return 0;
245                 }
246               else if (c == '/' && (flags & FNM_FILE_NAME))
247                 {
248                   while (*n != '\0' && *n != '/')
249                     ++n;
250                   if (*n == '/'
251                       && (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
252                                             flags) == 0))
253                     return 0;
254                 }
255               else
256                 {
257                   int flags2 = ((flags & FNM_FILE_NAME)
258                                 ? flags : (flags & ~FNM_PERIOD));
259
260                   if (c == '\\' && !(flags & FNM_NOESCAPE))
261                     c = *p;
262                   c = FOLD (c);
263                   for (--p; n < endp; ++n)
264                     if (FOLD ((unsigned char) *n) == c
265                         && (internal_fnmatch (p, n,
266                                               (no_leading_period
267                                                && (n == string
268                                                    || (n[-1] == '/'
269                                                        && (flags
270                                                            & FNM_FILE_NAME)))),
271                                               flags2) == 0))
272                       return 0;
273                 }
274             }
275
276           /* If we come here no match is possible with the wildcard.  */
277           return FNM_NOMATCH;
278
279         case '[':
280           {
281             /* Nonzero if the sense of the character class is inverted.  */
282             static int posixly_correct;
283             register int not;
284             char cold;
285
286             if (posixly_correct == 0)
287               posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
288
289             if (*n == '\0')
290               return FNM_NOMATCH;
291
292             if (*n == '.' && no_leading_period && (n == string
293                                                    || (n[-1] == '/'
294                                                        && (flags
295                                                            & FNM_FILE_NAME))))
296               return FNM_NOMATCH;
297
298             if (*n == '/' && (flags & FNM_FILE_NAME))
299               /* `/' cannot be matched.  */
300               return FNM_NOMATCH;
301
302             not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
303             if (not)
304               ++p;
305
306             c = *p++;
307             for (;;)
308               {
309                 unsigned char fn = FOLD ((unsigned char) *n);
310
311                 if (!(flags & FNM_NOESCAPE) && c == '\\')
312                   {
313                     if (*p == '\0')
314                       return FNM_NOMATCH;
315                     c = FOLD ((unsigned char) *p);
316                     ++p;
317
318                     if (c == fn)
319                       goto matched;
320                   }
321                 else if (c == '[' && *p == ':')
322                   {
323                     /* Leave room for the null.  */
324                     char str[CHAR_CLASS_MAX_LENGTH + 1];
325                     size_t c1 = 0;
326 # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
327                     wctype_t wt;
328 # endif
329                     const char *startp = p;
330
331                     for (;;)
332                       {
333                         if (c1 == CHAR_CLASS_MAX_LENGTH)
334                           /* The name is too long and therefore the pattern
335                              is ill-formed.  */
336                           return FNM_NOMATCH;
337
338                         c = *++p;
339                         if (c == ':' && p[1] == ']')
340                           {
341                             p += 2;
342                             break;
343                           }
344                         if (c < 'a' || c >= 'z')
345                           {
346                             /* This cannot possibly be a character class name.
347                                Match it as a normal range.  */
348                             p = startp;
349                             c = '[';
350                             goto normal_bracket;
351                           }
352                         str[c1++] = c;
353                       }
354                     str[c1] = '\0';
355
356 # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
357                     wt = IS_CHAR_CLASS (str);
358                     if (wt == 0)
359                       /* Invalid character class name.  */
360                       return FNM_NOMATCH;
361
362                     if (__iswctype (__btowc ((unsigned char) *n), wt))
363                       goto matched;
364 # else
365                     if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n))
366                         || (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n))
367                         || (STREQ (str, "blank") && ISBLANK ((unsigned char) *n))
368                         || (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n))
369                         || (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n))
370                         || (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n))
371                         || (STREQ (str, "lower") && ISLOWER ((unsigned char) *n))
372                         || (STREQ (str, "print") && ISPRINT ((unsigned char) *n))
373                         || (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n))
374                         || (STREQ (str, "space") && ISSPACE ((unsigned char) *n))
375                         || (STREQ (str, "upper") && ISUPPER ((unsigned char) *n))
376                         || (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n)))
377                       goto matched;
378 # endif
379                   }
380                 else if (c == '\0')
381                   /* [ (unterminated) loses.  */
382                   return FNM_NOMATCH;
383                 else
384                   {
385                   normal_bracket:
386                     if (FOLD (c) == fn)
387                       goto matched;
388
389                     cold = c;
390                     c = *p++;
391
392                     if (c == '-' && *p != ']')
393                       {
394                         /* It is a range.  */
395                         unsigned char cend = *p++;
396                         if (!(flags & FNM_NOESCAPE) && cend == '\\')
397                           cend = *p++;
398                         if (cend == '\0')
399                           return FNM_NOMATCH;
400
401                         if (cold <= fn && fn <= FOLD (cend))
402                           goto matched;
403
404                         c = *p++;
405                       }
406                   }
407
408                 if (c == ']')
409                   break;
410               }
411
412             if (!not)
413               return FNM_NOMATCH;
414             break;
415
416           matched:
417             /* Skip the rest of the [...] that already matched.  */
418             while (c != ']')
419               {
420                 if (c == '\0')
421                   /* [... (unterminated) loses.  */
422                   return FNM_NOMATCH;
423
424                 c = *p++;
425                 if (!(flags & FNM_NOESCAPE) && c == '\\')
426                   {
427                     if (*p == '\0')
428                       return FNM_NOMATCH;
429                     /* XXX 1003.2d11 is unclear if this is right.  */
430                     ++p;
431                   }
432                 else if (c == '[' && *p == ':')
433                   {
434                     do
435                       if (*++p == '\0')
436                         return FNM_NOMATCH;
437                     while (*p != ':' || p[1] == ']');
438                     p += 2;
439                     c = *p;
440                   }
441               }
442             if (not)
443               return FNM_NOMATCH;
444           }
445           break;
446
447         default:
448           if (c != FOLD ((unsigned char) *n))
449             return FNM_NOMATCH;
450         }
451
452       ++n;
453     }
454
455   if (*n == '\0')
456     return 0;
457
458   if ((flags & FNM_LEADING_DIR) && *n == '/')
459     /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
460     return 0;
461
462   return FNM_NOMATCH;
463
464 # undef FOLD
465 }
466
467
468 int
469 fnmatch (pattern, string, flags)
470      const char *pattern;
471      const char *string;
472      int flags;
473 {
474   return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
475 }
476
477 #endif  /* _LIBC or not __GNU_LIBRARY__.  */
478 /* ----- END glibc-2.1.2 posix/fnmatch.c */
479
480 #else   /* HAVE_FNMATCH */
481 void fnmatch_dummy(void) {}
482 #endif