diff --git a/component/wildcard/wildcard.go b/component/wildcard/wildcard.go index 7fcf15ba..c22eedf9 100644 --- a/component/wildcard/wildcard.go +++ b/component/wildcard/wildcard.go @@ -1,3 +1,12 @@ +// Package wildcard modified IGLOU-EU/go-wildcard to support: +// +// `*` matches zero or more characters +// `?` matches exactly one character +// +// The original go-wildcard library used `.` to match exactly one character, and `?` to match zero or one character. +// `.` is a valid delimiter in domain name matching and should not be used as a wildcard. +// The `?` matching logic strictly matches only one character in most scenarios. +// So, the `?` matching logic in the original go-wildcard library has been removed and its wildcard `.` has been replaced with `?`. package wildcard // copy and modified from https://github.com/IGLOU-EU/go-wildcard/tree/ce22b7af48e487517a492d3727d9386492043e21 @@ -16,12 +25,10 @@ func Match(pattern, s string) bool { } func matchByString(pattern, s string) bool { - var lastErotemeCluster byte - var patternIndex, sIndex, lastStar, lastEroteme int + var patternIndex, sIndex, lastStar int patternLen := len(pattern) sLen := len(s) star := -1 - eroteme := -1 Loop: if sIndex >= sLen { @@ -38,14 +45,8 @@ Loop: return false } switch pattern[patternIndex] { - // Removed dot matching as it conflicts with dot in domains. - // case '.': - // It matches any single character. So, we don't need to check anything. case '?': - // '?' matches one character. Store its position and match exactly one character in the string. - eroteme = patternIndex - lastEroteme = sIndex - lastErotemeCluster = byte(s[sIndex]) + // It matches any single character. So, we don't need to check anything. case '*': // '*' matches zero or more characters. Store its position and increment the pattern index. star = patternIndex @@ -53,15 +54,8 @@ Loop: patternIndex++ goto Loop default: - // If the characters don't match, check if there was a previous '?' or '*' to backtrack. + // If the characters don't match, check if there was a previous '*' to backtrack. if pattern[patternIndex] != s[sIndex] { - if eroteme != -1 { - patternIndex = eroteme + 1 - sIndex = lastEroteme - eroteme = -1 - goto Loop - } - if star != -1 { patternIndex = star + 1 lastStar++ @@ -71,29 +65,18 @@ Loop: return false } - - // If the characters match, check if it was not the same to validate the eroteme. - if eroteme != -1 && lastErotemeCluster != byte(s[sIndex]) { - eroteme = -1 - } } patternIndex++ sIndex++ goto Loop - // Check if the remaining pattern characters are '*' or '?', which can match the end of the string. + // Check if the remaining pattern characters are '*', which can match the end of the string. checkPattern: if patternIndex < patternLen { if pattern[patternIndex] == '*' { patternIndex++ goto checkPattern - } else if pattern[patternIndex] == '?' { - if sIndex >= sLen { - sIndex-- - } - patternIndex++ - goto checkPattern } } diff --git a/component/wildcard/wildcard_test.go b/component/wildcard/wildcard_test.go index 719a4979..be17e9f6 100644 --- a/component/wildcard/wildcard_test.go +++ b/component/wildcard/wildcard_test.go @@ -25,31 +25,17 @@ func TestMatch(t *testing.T) { {"", "", true}, {"", "*", true}, {"", "**", true}, - {"", "?", true}, - {"", "??", true}, - {"", "?*", true}, - {"", "*?", true}, - {"", ".", false}, - {"", ".?", false}, - {"", "?.", false}, - {"", ".*", false}, - {"", "*.", false}, - {"", "*.?", false}, - {"", "?.*", false}, + {"", "?", false}, + {"", "?*", false}, + {"", "*?", false}, {"a", "", false}, {"a", "a", true}, {"a", "*", true}, {"a", "**", true}, {"a", "?", true}, - {"a", "??", true}, - {"a", ".", false}, - {"a", ".?", false}, - {"a", "?.", false}, - {"a", ".*", false}, - {"a", "*.", false}, - {"a", "*.?", false}, - {"a", "?.*", false}, + {"a", "?*", true}, + {"a", "*?", true}, {"match the exact string", "match the exact string", true}, {"do not match a different string", "this is a different string", false}, @@ -68,22 +54,27 @@ func TestMatch(t *testing.T) { {"match a string with a ?", "match ? string with a ?", true}, {"match a string with a ? at the beginning", "?atch a string with a ? at the beginning", true}, - {"match a string with two ?", "match a string with two ??", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"match a optional char with a ?", "match a optional? char with a ?", true}, - {"do not match a string with extra and a ?", "do not match ? string with extra and a ? like this", false}, + {"match a string with two ?", "match a ??ring with two ?", true}, + {"do not match a string with extra ?", "do not match a string with extra ??", false}, - {"do not match a string with a .", "do not match . string with a .", false}, - {"do not match a string with a . at the beginning", "do not .atch a string with a . at the beginning", false}, - {"do not match a string with two .", "do not match a ..ring with two .", false}, - {"do not match a string with extra .", "do not match a string with extra ..", false}, + {"abc.edf.hjg", "abc.edf.hjg", true}, + {"abc.edf.hjg", "ab.cedf.hjg", false}, + {"abc.edf.hjg", "abc.edfh.jg", false}, + {"abc.edf.hjg", "abc.edf.hjq", false}, - {"A big brown fox jumps over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", false}, - {"A big brown fox fails to jump over the lazy dog, with all there wildcards friends", ". big?brown fox jumps over * wildcard. friend??", false}, + {"abc.edf.hjg", "abc.*.hjg", true}, + {"abc.edf.hjg", "abc.*.hjq", false}, + {"abc.edf.hjg", "abc*hjg", true}, + {"abc.edf.hjg", "abc*hjq", false}, + {"abc.edf.hjg", "a*g", true}, + {"abc.edf.hjg", "a*q", false}, - {"domain a.b.c", "domain a.b.c", true}, - {"domain adb.c", "domain a.b.c", false}, - {"aaaa", "a*a", true}, + {"abc.edf.hjg", "ab?.edf.hjg", true}, + {"abc.edf.hjg", "?b?.edf.hjg", true}, + {"abc.edf.hjg", "??c.edf.hjg", true}, + {"abc.edf.hjg", "a??.edf.hjg", true}, + {"abc.edf.hjg", "ab??.edf.hjg", false}, + {"abc.edf.hjg", "??.edf.hjg", false}, } for i, c := range cases {