diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/fnmatch/FileNameMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/fnmatch/FileNameMatcherTest.java
index ec0724406..1db6c8030 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/fnmatch/FileNameMatcherTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/fnmatch/FileNameMatcherTest.java
@@ -801,6 +801,46 @@ public class FileNameMatcherTest {
}
}
+ @Test
+ public void testEscapedBracket1() throws Exception {
+ assertMatch("\\[", "[", true, false);
+ }
+
+ @Test
+ public void testEscapedBracket2() throws Exception {
+ assertMatch("\\[[a]", "[", false, true);
+ }
+
+ @Test
+ public void testEscapedBracket3() throws Exception {
+ assertMatch("\\[[a]", "a", false, false);
+ }
+
+ @Test
+ public void testEscapedBracket4() throws Exception {
+ assertMatch("\\[[a]", "[a", true, false);
+ }
+
+ @Test
+ public void testEscapedBracket5() throws Exception {
+ assertMatch("[a\\]]", "]", true, false);
+ }
+
+ @Test
+ public void testEscapedBracket6() throws Exception {
+ assertMatch("[a\\]]", "a", true, false);
+ }
+
+ @Test
+ public void testEscapedBackslash() throws Exception {
+ assertMatch("a\\\\b", "a\\b", true, false);
+ }
+
+ @Test
+ public void testMultipleEscapedCharacters1() throws Exception {
+ assertMatch("\\]a?c\\*\\[d\\?\\]", "]abc*[d?]", true, false);
+ }
+
@Test
public void testFilePathSimpleCase() throws Exception {
assertFileNameMatch("a/b", "a/b", '/', true, false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
index 22840fb71..02e4235fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
@@ -60,9 +60,9 @@ import org.eclipse.jgit.errors.NoClosingBracketException;
*
* Supported are the wildcard characters * and ? and groups with:
*
- * - characters e.g. [abc]
- * - ranges e.g. [a-z]
- * - the following character classes
+ *
- characters e.g. [abc]
+ * - ranges e.g. [a-z]
+ * - the following character classes
*
* - [:alnum:]
* - [:alpha:]
@@ -78,9 +78,10 @@ import org.eclipse.jgit.errors.NoClosingBracketException;
* - [:word:]
* - [:xdigit:]
*
- * e. g. [[:xdigit:]]
+ * e. g. [[:xdigit:]]
*
*
+ * Any character can be escaped by prepending it with a \
*/
public class FileNameMatcher {
static final List EMPTY_HEAD_LIST = Collections.emptyList();
@@ -199,7 +200,7 @@ public class FileNameMatcher {
int groupEnd = -1;
while (groupEnd == -1) {
- final int possibleGroupEnd = pattern.indexOf(']',
+ final int possibleGroupEnd = indexOfUnescaped(pattern, ']',
firstValidEndBracketIndex);
if (possibleGroupEnd == -1)
throw new NoClosingBracketException(indexOfStartBracket, "[", //$NON-NLS-1$
@@ -238,7 +239,7 @@ public class FileNameMatcher {
int currentIndex = 0;
List heads = new ArrayList();
while (currentIndex < pattern.length()) {
- final int groupStart = pattern.indexOf('[', currentIndex);
+ final int groupStart = indexOfUnescaped(pattern, '[', currentIndex);
if (groupStart == -1) {
final String patternPart = pattern.substring(currentIndex);
heads.addAll(createSimpleHeads(patternPart,
@@ -264,24 +265,35 @@ public class FileNameMatcher {
final String patternPart, final Character invalidWildgetCharacter) {
final List heads = new ArrayList(
patternPart.length());
+
+ boolean escaped = false;
for (int i = 0; i < patternPart.length(); i++) {
final char c = patternPart.charAt(i);
- switch (c) {
- case '*': {
- final AbstractHead head = createWildCardHead(
- invalidWildgetCharacter, true);
- heads.add(head);
- break;
- }
- case '?': {
- final AbstractHead head = createWildCardHead(
- invalidWildgetCharacter, false);
- heads.add(head);
- break;
- }
- default:
+ if (escaped) {
final CharacterHead head = new CharacterHead(c);
heads.add(head);
+ escaped = false;
+ } else {
+ switch (c) {
+ case '*': {
+ final AbstractHead head = createWildCardHead(
+ invalidWildgetCharacter, true);
+ heads.add(head);
+ break;
+ }
+ case '?': {
+ final AbstractHead head = createWildCardHead(
+ invalidWildgetCharacter, false);
+ heads.add(head);
+ break;
+ }
+ case '\\':
+ escaped = true;
+ break;
+ default:
+ final CharacterHead head = new CharacterHead(c);
+ heads.add(head);
+ }
}
}
return heads;
@@ -317,6 +329,18 @@ public class FileNameMatcher {
heads = newHeads;
}
+ private static int indexOfUnescaped(final String searchString,
+ final char ch, final int fromIndex) {
+ for (int i = fromIndex; i < searchString.length(); i++) {
+ char current = searchString.charAt(i);
+ if (current == ch)
+ return i;
+ if (current == '\\')
+ i++; // Skip the next char as it is escaped }
+ }
+ return -1;
+ }
+
/**
*
* @param stringToMatch