#if defined(LIBC_SCCS) && !defined(lint)
static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $";
#endif
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif
#include "compat.h"
#include "emul/fnmatch.h"
#undef EOS
#define EOS '\0'
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
static int rangematch __P((const char *, char, int, char **));
int
fnmatch(pattern, string, flags)
const char *pattern, *string;
int flags;
{
const char *stringstart;
char *newp;
char c, test;
for (stringstart = string;;)
switch (c = *pattern++) {
case EOS:
if (ISSET(flags, FNM_LEADING_DIR) && *string == '/')
return (0);
return (*string == EOS ? 0 : FNM_NOMATCH);
case '?':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && ISSET(flags, FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
(string == stringstart ||
(ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
++string;
break;
case '*':
c = *pattern;
while (c == '*')
c = *++pattern;
if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
(string == stringstart ||
(ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
if (c == EOS) {
if (ISSET(flags, FNM_PATHNAME))
return (ISSET(flags, FNM_LEADING_DIR) ||
strchr(string, '/') == NULL ?
0 : FNM_NOMATCH);
else
return (0);
} else if (c == '/' && ISSET(flags, FNM_PATHNAME)) {
if ((string = strchr(string, '/')) == NULL)
return (FNM_NOMATCH);
break;
}
while ((test = *string) != EOS) {
if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
return (0);
if (test == '/' && ISSET(flags, FNM_PATHNAME))
break;
++string;
}
return (FNM_NOMATCH);
case '[':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && ISSET(flags, FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
(string == stringstart ||
(ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
switch (rangematch(pattern, *string, flags, &newp)) {
case RANGE_ERROR:
goto normal;
case RANGE_MATCH:
pattern = newp;
break;
case RANGE_NOMATCH:
return (FNM_NOMATCH);
}
++string;
break;
case '\\':
if (!ISSET(flags, FNM_NOESCAPE)) {
if ((c = *pattern++) == EOS) {
c = '\\';
--pattern;
}
}
default:
normal:
if (c != *string && !(ISSET(flags, FNM_CASEFOLD) &&
(tolower((unsigned char)c) ==
tolower((unsigned char)*string))))
return (FNM_NOMATCH);
++string;
break;
}
}
static int
#ifdef __STDC__
rangematch(const char *pattern, char test, int flags, char **newp)
#else
rangematch(pattern, test, flags, newp)
const char *pattern;
char test;
int flags;
char **newp;
#endif
{
int negate, ok;
char c, c2;
if ((negate = (*pattern == '!' || *pattern == '^')))
++pattern;
if (ISSET(flags, FNM_CASEFOLD))
test = tolower((unsigned char)test);
ok = 0;
c = *pattern++;
do {
if (c == '\\' && !ISSET(flags, FNM_NOESCAPE))
c = *pattern++;
if (c == EOS)
return (RANGE_ERROR);
if (c == '/' && ISSET(flags, FNM_PATHNAME))
return (RANGE_NOMATCH);
if (ISSET(flags, FNM_CASEFOLD))
c = tolower((unsigned char)c);
if (*pattern == '-'
&& (c2 = *(pattern+1)) != EOS && c2 != ']') {
pattern += 2;
if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE))
c2 = *pattern++;
if (c2 == EOS)
return (RANGE_ERROR);
if (ISSET(flags, FNM_CASEFOLD))
c2 = tolower((unsigned char)c2);
if (c <= test && test <= c2)
ok = 1;
} else if (c == test)
ok = 1;
} while ((c = *pattern++) != ']');
*newp = (char *)pattern;
return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
}