/*****************************************************************************\ * * * Filename: fnmatch.c * * * * Description: DOS/WIN32 port of standard C library's fnmatch() * * * * Notes: TO DO: Manage FNM_PATHNAME, FNM_PERIOD, FNM_LEADING_DIR. * * * * History: * * 2012-01-17 JFL Created this file. * * 2013-03-10 JFL In DOS/Windows, the pattern "*.*" actually means "*". * * 2014-02-13 JFL Removed warnings. * * 2014-02-14 JFL In DOS/Windows, the pattern "*." means no extension. * * 2014-02-17 JFL Wildcards match the empty string. * * 2014-02-20 JFL Fixed "*." pattern matching. * * 2014-02-28 JFL Added support for UTF-8 pathnames. * * 2014-03-05 JFL In debug mode, hide recursive calls. * * * * Copyright 2016 Hewlett Packard Enterprise Development LP * * Licensed under the Apache 2.0 license - www.apache.org/licenses/LICENSE-2.0 * \*****************************************************************************/ #define _UTF8_SOURCE /* Generate the UTF-8 version of routines */ #include #include #include #include "msvcFnmatch.h" /* Include our associated .h, in the same dir as this .c. Do not use <>. */ #include "msvcDebugm.h" #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #pragma warning(disable:4706) /* Ignore the "assignment within conditional expression" warning */ int fnmatch(const char *pszPattern, const char *pszName, int iFlags) { char cp, cn; size_t l; const char *pc; int iErr; DEBUG_CODE( int iHideRecursions = FALSE; ) DEBUG_ENTER(("fnmatch(\"%s\", \"%s\", 0x%x);\n", pszPattern, pszName, iFlags)); DEBUG_CODE(if(DEBUG_IS_ON() && !XDEBUG_IS_ON()) iHideRecursions = TRUE;) /* Special case in DOS/Windows: "*.*" will actually match any name, even without a dot */ if (!strcmp(pszPattern, "*.*")) pszPattern = "*"; /* Special case in DOS/Windows: "anything." means anything without extension */ l = strlen(pszPattern); if (l && (pszPattern[l-1] == '.')) { int i; int lName = (int)strlen(pszName); /* First eliminate the case of forbidden extensions <==> A '.' anywhere before the last character */ for (i=0; i<(lName-1); i++) { /* Search for a '.' in the name */ if (pszName[i] == '.') RETURN_CONST_COMMENT(FNM_NOMATCH, ("Forbidden extension found\n")); } /* If the name doesn't end with a dot, Remove the pattern's trailing '.' */ if (lName && (pszName[lName-1] != '.')) { char *pszPattern2; pszPattern2 = _strdup(pszPattern); pszPattern2[--l] = '\0'; /* Recursively do the pattern matching with the new pattern */ DEBUG_CODE(if (iHideRecursions) iDebug -= 1;) iErr = fnmatch(pszPattern2, pszName, iFlags); DEBUG_CODE(if (iHideRecursions) iDebug += 1;) free(pszPattern2); RETURN_INT(iErr); } } for ( ; (cp = *pszPattern) && (cn = *pszName); pszPattern++, pszName++) { XDEBUG_PRINTF(("// cp='%c' cn='%c'\n", cp, cn)); switch (cp) { case '?': if (cn == '.') RETURN_CONST_COMMENT(FNM_NOMATCH, ("? does not match a .\n")); break; /* Anything else matches. Continue analysing the pattern. */ case '*': cp = *(++pszPattern); if (!cp) RETURN_CONST_COMMENT(FNM_MATCH, ("'*' matches whatever remains in the string\n")); for ( ; cn = *pszName; pszName++) { DEBUG_CODE(if (iHideRecursions) iDebug -= 1;) iErr = fnmatch(pszPattern, pszName, iFlags); DEBUG_CODE(if (iHideRecursions) iDebug += 1;) if (iErr == FNM_MATCH) RETURN_CONST(FNM_MATCH); } RETURN_CONST_COMMENT(FNM_NOMATCH, ("No tail string matches the remainder of the pattern\n")); default: if (iFlags & FNM_CASEFOLD) { cp = (char)toupper(cp); cn = (char)toupper(cn); } if (cp != cn) RETURN_CONST_COMMENT(FNM_NOMATCH, ("Character mismatch\n")); break; /* The character matches. Continue analysing the pattern. */ } } /* '*' and '?' match the empty string */ if (*pszPattern && !*pszName) { int bOnlyWildCards = TRUE; for (pc=pszPattern; cp=*pc; pc++) { if ((cp != '*') && (cp != '?')) { bOnlyWildCards = FALSE; break; } } if (bOnlyWildCards) { RETURN_CONST_COMMENT(FNM_MATCH, ("WildCards match the empty string\n")); } } /* Special case in DOS/Windows: trailing dot allowed in pattern */ if ((*pszPattern == '.') && (!*(pszPattern+1)) && (!*pszName)) RETURN_CONST_COMMENT(FNM_MATCH, ("trailing dot matches empty string\n")); if (*pszPattern || *pszName) RETURN_CONST_COMMENT(FNM_NOMATCH, ("Something remains that did not match\n")); RETURN_CONST_COMMENT(FNM_MATCH, ("Complete match\n")); } #pragma warning(default:4706)