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 void testUnsupportedGroupCase1() throws Exception { } } + @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 @@ *

* Supported are the wildcard characters * and ? and groups with: *

*

+ * 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 @@ private static int findGroupEnd(final int indexOfStartBracket, 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 @@ private static List parseHeads(final String pattern, 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 @@ private static List createSimpleHeads( 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 @@ private void extendStringToMatchByOneCharacter(final char c) { 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