|
//------------------------------------------------------------------------------
// <copyright file="Wildcard.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* Wildcard
*
* wildcard wrappers for Regex
*
* (1) Wildcard does straight string wildcarding with no path separator awareness
* (2) WildcardUrl recognizes that forward / slashes are special and can't match * or ?
* (3) WildcardDos recognizes that backward \ and : are special and can't match * or ?
*
* Copyright (c) 1999, Microsoft Corporation
*/
namespace System.Web.Util {
using System.Runtime.Serialization.Formatters;
using System.Text.RegularExpressions;
/*
* Wildcard
*
* Wildcard patterns have three metacharacters:
*
* A ? is equivalent to .
* A * is equivalent to .*
* A , is equivalent to |
*
* Note that by each alternative is surrounded by \A...\z to anchor
* at the edges of the string.
*/
internal class Wildcard {
#if NOT_USED
internal /*public*/ Wildcard(String pattern) : this (pattern, false) {
}
#endif
internal /*public*/ Wildcard(String pattern, bool caseInsensitive) {
_pattern = pattern;
_caseInsensitive = caseInsensitive;
}
internal String _pattern;
internal bool _caseInsensitive;
internal Regex _regex;
protected static Regex metaRegex = new Regex("[\\+\\{\\\\\\[\\|\\(\\)\\.\\^\\$]");
protected static Regex questRegex = new Regex("\\?");
protected static Regex starRegex = new Regex("\\*");
protected static Regex commaRegex = new Regex(",");
protected static Regex slashRegex = new Regex("(?=/)");
protected static Regex backslashRegex = new Regex("(?=[\\\\:])");
/*
* IsMatch returns true if the input is an exact match for the
* wildcard pattern.
*/
internal /*public*/ bool IsMatch(String input) {
EnsureRegex();
bool result = _regex.IsMatch(input);
return result;
}
#if DONT_COMPILE
internal /*public*/ String Pattern {
get {
return _pattern;
}
}
#endif
/*
* Builds the matching regex when needed
*/
protected void EnsureRegex() {
// threadsafe without protection because of gc
if (_regex != null)
return;
_regex = RegexFromWildcard(_pattern, _caseInsensitive);
}
/*
* Basic wildcard -> Regex conversion, no slashes
*/
protected virtual Regex RegexFromWildcard(String pattern, bool caseInsensitive) {
RegexOptions options = RegexOptions.None;
// match right-to-left (for speed) if the pattern starts with a *
if (pattern.Length > 0 && pattern[0] == '*')
options = RegexOptions.RightToLeft | RegexOptions.Singleline;
else
options = RegexOptions.Singleline;
// case insensitivity
if (caseInsensitive)
options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
// Remove regex metacharacters
pattern = metaRegex.Replace(pattern, "\\$0");
// Replace wildcard metacharacters with regex codes
pattern = questRegex.Replace(pattern, ".");
pattern = starRegex.Replace(pattern, ".*");
pattern = commaRegex.Replace(pattern, "\\z|\\A");
// anchor the pattern at beginning and end, and return the regex
return new Regex("\\A" + pattern + "\\z", options);
}
}
abstract internal class WildcardPath : Wildcard {
#if NOT_USED
internal /*public*/ WildcardPath(String pattern) : base(pattern) {
}
private Regex[][] _dirs;
#endif
internal /*public*/ WildcardPath(String pattern, bool caseInsensitive) : base(pattern, caseInsensitive) {
}
private Regex _suffix;
/*
* IsSuffix returns true if a suffix of the input is an exact
* match for the wildcard pattern.
*/
internal /*public*/ bool IsSuffix(String input) {
EnsureSuffix();
return _suffix.IsMatch(input);
}
#if NOT_USED
/*
* AllowPrefix returns true if the input is an exact match for
* a prefix-directory of the wildcard pattern (i.e., if it
* is possible to match the wildcard pattern by adding
* more subdirectories or a filename at the end of the path).
*/
internal /*public*/ bool AllowPrefix(String prefix) {
String[] dirs = SplitDirs(prefix);
EnsureDirs();
for (int i = 0; i < _dirs.Length; i++) {
// pattern is shorter than prefix: reject
if (_dirs[i].Length < dirs.Length)
goto NextAlt;
for (int j = 0; j < dirs.Length; j++) {
// the jth directory doesn't match; path is not a prefix
if (!_dirs[i][j].IsMatch(dirs[j]))
goto NextAlt;
}
// one alternative passed: we pass.
return true;
NextAlt:
;
}
return false;
}
/*
* Builds the matching regex array when needed
*/
protected void EnsureDirs() {
// threadsafe without protection because of gc
if (_dirs != null)
return;
_dirs = DirsFromWildcard(_pattern);
}
#endif
/*
* Builds the matching regex when needed
*/
protected void EnsureSuffix() {
// threadsafe without protection because of gc
if (_suffix != null)
return;
_suffix = SuffixFromWildcard(_pattern, _caseInsensitive);
}
/*
* Specialize for forward-slash and backward-slash cases
*/
protected abstract Regex SuffixFromWildcard(String pattern, bool caseInsensitive);
protected abstract Regex[][] DirsFromWildcard(String pattern);
protected abstract String[] SplitDirs(String input);
}
/*
* WildcardUrl
*
* The twist is that * and ? cannot match forward slashes,
* and we can do an exact suffix match that starts after
* any /, and we can also do a prefix prune.
*/
internal class WildcardUrl : WildcardPath {
#if NOT_USED
internal /*public*/ WildcardUrl(String pattern) : base(pattern) {
}
#endif
internal /*public*/ WildcardUrl(String pattern, bool caseInsensitive) : base(pattern, caseInsensitive) {
}
protected override String[] SplitDirs(String input) {
return slashRegex.Split(input);
}
protected override Regex RegexFromWildcard(String pattern, bool caseInsensitive) {
RegexOptions options;
// match right-to-left (for speed) if the pattern starts with a *
if (pattern.Length > 0 && pattern[0] == '*')
options = RegexOptions.RightToLeft;
else
options = RegexOptions.None;
// case insensitivity
if (caseInsensitive)
options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
// Remove regex metacharacters
pattern = metaRegex.Replace(pattern, "\\$0");
// Replace wildcard metacharacters with regex codes
pattern = questRegex.Replace(pattern, "[^/]");
pattern = starRegex.Replace(pattern, "[^/]*");
pattern = commaRegex.Replace(pattern, "\\z|\\A");
// anchor the pattern at beginning and end, and return the regex
return new Regex("\\A" + pattern + "\\z", options);
}
protected override Regex SuffixFromWildcard(String pattern, bool caseInsensitive) {
RegexOptions options;
// match right-to-left (for speed)
options = RegexOptions.RightToLeft;
// case insensitivity
if (caseInsensitive)
options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
// Remove regex metacharacters
pattern = metaRegex.Replace(pattern, "\\$0");
// Replace wildcard metacharacters with regex codes
pattern = questRegex.Replace(pattern, "[^/]");
pattern = starRegex.Replace(pattern, "[^/]*");
pattern = commaRegex.Replace(pattern, "\\z|(?:\\A|(?<=/))");
// anchor the pattern at beginning and end, and return the regex
return new Regex("(?:\\A|(?<=/))" + pattern + "\\z", options);
}
protected override Regex[][] DirsFromWildcard(String pattern) {
String[] alts = commaRegex.Split(pattern);
Regex[][] dirs = new Regex[alts.Length][];
for (int i = 0; i < alts.Length; i++) {
String[] dirpats = slashRegex.Split(alts[i]);
Regex[] dirregex = new Regex[dirpats.Length];
if (alts.Length == 1 && dirpats.Length == 1) {
// common case: no commas, no slashes: dir regex is same as top regex.
EnsureRegex();
dirregex[0] = _regex;
}
else {
for (int j = 0; j < dirpats.Length; j++) {
dirregex[j] = RegexFromWildcard(dirpats[j], _caseInsensitive);
}
}
dirs[i] = dirregex;
}
return dirs;
}
}
}
|