BOOLEAN PvfsWildcardMatch( IN PSTR pszPathname, IN PSTR pszPattern, IN BOOLEAN bCaseSensitive ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PSTR pszString = NULL; PSTR pszMatch = NULL; PSTR pszPathUpper = NULL; PSTR pszPatternUpper = NULL; BOOLEAN bMatched = FALSE; /* Quick check for an exact match */ if (!strchr(pszPattern, '?') && !strchr(pszPattern, '*')) { return RtlCStringIsEqual(pszPathname, pszPattern, bCaseSensitive); } /* If we have a case insensitive search, upper case the Pathname and Pattern for easier comparison */ pszString = pszPathname; pszMatch = pszPattern; if (!bCaseSensitive) { ntError = RtlCStringDuplicate(&pszPathUpper, pszPathname); BAIL_ON_NT_STATUS(ntError); ntError = RtlCStringDuplicate(&pszPatternUpper, pszPattern); BAIL_ON_NT_STATUS(ntError); PvfsCStringUpper(pszPathUpper); PvfsCStringUpper(pszPatternUpper); pszString = pszPathUpper; pszMatch = pszPatternUpper; } /* Enter state machine */ for (/* already performed init */; PVFS_CSTRING_NON_NULL(pszString) && PVFS_CSTRING_NON_NULL(pszMatch); pszString++) { PVFS_WILDCARD_TYPE eState = 0; CHAR cSrc = '\0'; CHAR cMatch = '\0'; DWORD dwCount = 0; /* Save the current CHAR */ cSrc = *pszString; cMatch = *pszMatch; /* Consumes the pattern from pszMatch */ eState = NextMatchState(&pszMatch, &dwCount); switch (eState) { case PVFS_WILDCARD_TYPE_NONE: if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } break; case PVFS_WILDCARD_TYPE_SPLAT: { PSTR pszCursor = NULL; /* We are done if this is the last character in the pattern */ if (!PVFS_CSTRING_NON_NULL(pszMatch)) { pszString = NULL; goto cleanup; } /* If we don't find a match for the next character in the pattern, then fail */ if ((pszCursor = strchr(pszString, *pszMatch)) == NULL) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } /* Have to consume at least one character here */ if (pszString == pszCursor) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } /* Set to the previous character so that pszString is incremented properly next pass of the loop */ pszString = pszCursor-1; break; } case PVFS_WILDCARD_TYPE_SINGLE: { DWORD i = 0; /* Consume dwCount characters */ for (i=0; i<dwCount && PVFS_CSTRING_NON_NULL(pszString); i++, pszString++) { /* no loop body */; } if (*pszString == '\0') { /* backup so pszString incrent in outer loop works out */ pszString--; } break; } case PVFS_WILDCARD_TYPE_DOT: /* For now deal with the '.' as just another character */ if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } break; case PVFS_WILDCARD_TYPE_SPLAT_DOT: { PSTR pszCursor = NULL; /* Similar to "A*B" except we search for the '.' from the end. */ if ((pszCursor = strrchr(pszString, '.')) == NULL) { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } pszString = pszCursor; break; } case PVFS_WILDCARD_TYPE_SINGLE_DOT: { DWORD i = 0; /* We can match 0 - dwCount characters up to the last '.' This is really a hold over from DOS 8.3 filenames */ for (i=0; i<dwCount && PVFS_CSTRING_NON_NULL(pszString) && (*pszString != '.'); i++, pszString++) { /* no loop body */; } /* If we any path lefft, it better be on '.' for a match */ if (pszString && *pszString != '.') { ntError = STATUS_NO_MATCH; BAIL_ON_NT_STATUS(ntError); } break; } } } cleanup: /* We matched if pszString is empty AND either pszMatch is empty OR only contains wildcard characters */ if (!PVFS_CSTRING_NON_NULL(pszString) && (!PVFS_CSTRING_NON_NULL(pszMatch) || (!strchr(pszPattern, '?') && !strchr(pszPattern, '*')))) { bMatched = TRUE; } if (!bCaseSensitive) { PVFS_FREE(&pszPathUpper); PVFS_FREE(&pszPatternUpper); } /* If we have any string left to parse, we don't have a match */ return bMatched; error: goto cleanup; }
BOOLEAN PvfsWildcardMatch( IN PSTR pszPathname, IN PSTR pszPattern, IN BOOLEAN bCaseSensitive ) { NTSTATUS ntError = STATUS_SUCCESS; PSTR pszString = NULL; PSTR pszMatch = NULL; PSTR pszPathUpper = NULL; PSTR pszPatternUpper = NULL; BOOLEAN bMatched = FALSE; LW_LIST_LINKS Stack; // Quick check for an exact match if (!strchr(pszPattern, '?') && !strchr(pszPattern, '*')) { return RtlCStringIsEqual(pszPathname, pszPattern, bCaseSensitive); } // If we have a case insensitive search, upper case the Pathname // and Pattern for easier comparison pszString = pszPathname; pszMatch = pszPattern; if (!bCaseSensitive) { ntError = RtlCStringDuplicate(&pszPathUpper, pszPathname); BAIL_ON_NT_STATUS(ntError); ntError = RtlCStringDuplicate(&pszPatternUpper, pszPattern); BAIL_ON_NT_STATUS(ntError); PvfsCStringUpper(pszPathUpper); PvfsCStringUpper(pszPatternUpper); pszString = pszPathUpper; pszMatch = pszPatternUpper; } // Enter state machine LwListInit(&Stack); reset_state: while (PVFS_CSTRING_NON_NULL(pszString) && PVFS_CSTRING_NON_NULL(pszMatch)) { PVFS_WILDCARD_TYPE eState = 0; CHAR cSrc = '\0'; CHAR cMatch = '\0'; DWORD dwCount = 0; // Save the current CHAR cSrc = *pszString; cMatch = *pszMatch; // Certain characters should never match and hence allow us to filter // certain file patterns if (cSrc == PVFS_STREAM_DELIMINATOR_C) { bMatched = FALSE; goto cleanup; } // Consumes the pattern from pszMatch eState = NextMatchState(&pszMatch, &dwCount); switch (eState) { case PVFS_WILDCARD_TYPE_NONE: if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; pszString = NULL; } else { pszString++; } break; case PVFS_WILDCARD_TYPE_SPLAT: { // We are done if this is the last character in the pattern if (!PVFS_CSTRING_NON_NULL(pszMatch)) { pszString = NULL; } else { // Be greedy - Consume as much of the string using the '*' // as possible. This will require a stack in order to backtrack // on a failure. pszString = PvfsWildcardEatString(pszString, pszMatch); if (!pszString) { ntError = STATUS_NO_MATCH; } else { // Add next character past "String" match and the previously // match "Pattern" state (so we pick up the '*' when we pop // the stack ntError = PvfsWildcardStackPush( &Stack, pszString+1, pszMatch-1); BAIL_ON_NT_STATUS(ntError); } } break; } case PVFS_WILDCARD_TYPE_SINGLE: { DWORD i = 0; // Consume dwCount characters for (i=0; (i<dwCount) && PVFS_CSTRING_NON_NULL(pszString); i++, pszString++) { // no loop body ; } break; } case PVFS_WILDCARD_TYPE_DOT: // For now deal with the '.' as just another character if (cSrc != cMatch) { ntError = STATUS_NO_MATCH; pszString = NULL; } else { pszString++; } break; case PVFS_WILDCARD_TYPE_SPLAT_DOT: { PSTR pszCursor = NULL; // Similar to "A*B" except we search for the '.' from the end if ((pszCursor = strrchr(pszString, '.')) == NULL) { ntError = STATUS_NO_MATCH; pszString = NULL; } else { pszString = pszCursor + 1; } break; } case PVFS_WILDCARD_TYPE_SINGLE_DOT: { DWORD i = 0; // We can match 0 - dwCount characters up to the last '.' // This is really a hold over from DOS 8.3 filenames for (i=0; i<dwCount && PVFS_CSTRING_NON_NULL(pszString) && (*pszString != '.'); i++, pszString++) { // no loop body ; } // If we any path left, it better be on '.' for a match if (*pszString == '.') { pszString++; } else { ntError = STATUS_NO_MATCH; pszString = NULL; } break; } } // end of switch {...} } // end of for {...} // We matched if pszString is empty AND either pszMatch is empty // OR only contains wildcard characters if ((ntError == STATUS_SUCCESS) && !PVFS_CSTRING_NON_NULL(pszString) && (!PVFS_CSTRING_NON_NULL(pszMatch) || PvfsCStringOnlyContainsChars(pszMatch, "?*"))) { bMatched = TRUE; } else if (!LwListIsEmpty(&Stack)) { // Pop back to the earlier state, consume one character (*from '*') // and try another path ntError = PvfsWildcardStackPop(&Stack, &pszString, &pszMatch); BAIL_ON_NT_STATUS(ntError); pszString++; goto reset_state; } cleanup: PvfsWildcardStackDestroy(&Stack); if (!bCaseSensitive) { PVFS_FREE(&pszPathUpper); PVFS_FREE(&pszPatternUpper); } // If we have any string left to parse, we don't have a match return bMatched; error: goto cleanup; }