This is an automated email from the git hooks/post-receive script. henrich pushed a commit to branch debian/sid in repository jruby-joni.
commit aa32be522b78da9895e35e32d3e7d26b76ce9542 Author: Hideki Yamane <[email protected]> Date: Tue Jun 4 19:31:10 2013 +0900 Imported Upstream version 2.0.0 --- pom.xml | 41 ++++++++++- src/org/joni/ByteCodeMachine.java | 13 +++- src/org/joni/Matcher.java | 43 +++++++---- test/org/joni/test/Test.java | 122 +++++++++++++++++++++++++++++--- test/org/joni/test/TestA.java | 2 +- test/org/joni/test/TestC.java | 2 +- test/org/joni/test/TestCornerCases.java | 2 +- test/org/joni/test/TestCrnl.java | 2 +- test/org/joni/test/TestInterrupt.java | 100 ++++++++++++++++++++++++++ test/org/joni/test/TestJoni.java | 6 ++ test/org/joni/test/TestLookBehind.java | 2 +- test/org/joni/test/TestNSU8.java | 2 +- test/org/joni/test/TestU.java | 2 +- test/org/joni/test/TestU8.java | 2 +- 14 files changed, 310 insertions(+), 31 deletions(-) diff --git a/pom.xml b/pom.xml index e7f4799..a1f09f3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,13 +5,17 @@ <groupId>org.jruby.joni</groupId> <artifactId>joni</artifactId> <packaging>jar</packaging> - <version>1.1.9</version> + <version>2.0.0</version> <name>Joni</name> <description> Java port of Oniguruma: http://www.geocities.jp/kosako3/oniguruma that uses byte arrays directly instead of java Strings and chars </description> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <issueManagement> <system>JIRA</system> <url>http://jira.codehaus.org/browse/JRUBY</url> @@ -104,6 +108,7 @@ <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> + <version>2.3.2</version> <configuration> <source>1.5</source> <target>1.5</target> @@ -112,6 +117,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> + <version>2.4.3</version> <configuration> <includes> <include>**/TestJoni.java</include> @@ -120,12 +126,45 @@ </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> + <version>2.2</version> <configuration> <archive> <manifestFile>MANIFEST.MF</manifestFile> </archive> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version>2.1.2</version> + <executions> + <execution> + <id>attach-sources</id> + <phase>verify</phase> + <goals> + <goal>jar-no-fork</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.8.1</version> + <configuration> + <show>private</show> + <nohelp>true</nohelp> + </configuration> + <executions> + <execution> + <id>attach-sources</id> + <phase>verify</phase> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> </project> diff --git a/src/org/joni/ByteCodeMachine.java b/src/org/joni/ByteCodeMachine.java index 9056166..d5b318c 100644 --- a/src/org/joni/ByteCodeMachine.java +++ b/src/org/joni/ByteCodeMachine.java @@ -37,6 +37,9 @@ import org.joni.exception.ErrorMessages; import org.joni.exception.InternalException; class ByteCodeMachine extends StackMachine { + private static final int INTERRUPT_CHECK_EVERY = 30000; + int interruptCheckCounter = 0; // we modulos this to occasionally check for interrupts + private int bestLen; // return value private int s = 0; // current char @@ -169,7 +172,7 @@ class ByteCodeMachine extends StackMachine { } } - protected final int matchAt(int range, int sstart, int sprev) { + protected final int matchAt(int range, int sstart, int sprev) throws InterruptedException { this.range = range; this.sstart = sstart; this.sprev = sprev; @@ -183,9 +186,15 @@ class ByteCodeMachine extends StackMachine { bestLen = -1; s = sstart; + Thread currentThread = Thread.currentThread(); final int[]code = this.code; while (true) { + if (interruptCheckCounter++ % INTERRUPT_CHECK_EVERY == 0 && currentThread.isInterrupted()) { + currentThread.interrupted(); + throw new InterruptedException(); + } + if (Config.DEBUG_MATCH) debugMatchLoop(); sbegin = s; @@ -1744,4 +1753,4 @@ class ByteCodeMachine extends StackMachine { private int finish() { return bestLen; } -} \ No newline at end of file +} diff --git a/src/org/joni/Matcher.java b/src/org/joni/Matcher.java index 4fa923d..a2f777e 100644 --- a/src/org/joni/Matcher.java +++ b/src/org/joni/Matcher.java @@ -27,6 +27,9 @@ import org.jcodings.IntHolder; import org.joni.constants.AnchorType; public abstract class Matcher extends IntHolder { + public static final int FAILED = -1; + public static final int INTERRUPTED = -2; + protected final Regex regex; protected final Encoding enc; @@ -59,7 +62,7 @@ public abstract class Matcher extends IntHolder { } // main matching method - protected abstract int matchAt(int range, int sstart, int sprev); + protected abstract int matchAt(int range, int sstart, int sprev) throws InterruptedException; protected abstract void stateCheckBuffInit(int strLength, int offset, int stateNum); protected abstract void stateCheckBuffClear(); @@ -85,8 +88,16 @@ public abstract class Matcher extends IntHolder { msaStart = start; if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) msaBestLen = -1; } - + public final int match(int at, int range, int option) { + try { + return matchInterruptible(at, range, option); + } catch (InterruptedException ex) { + return INTERRUPTED; + } + } + + public final int matchInterruptible(int at, int range, int option) throws InterruptedException { msaInit(option, at); if (Config.USE_COMBINATION_EXPLOSION_CHECK) { @@ -277,7 +288,7 @@ public abstract class Matcher extends IntHolder { } // MATCH_AND_RETURN_CHECK - private boolean matchCheck(int upperRange, int s, int prev) { + private boolean matchCheck(int upperRange, int s, int prev) throws InterruptedException { if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) { if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) { //range = upperRange; @@ -301,8 +312,16 @@ public abstract class Matcher extends IntHolder { } return false; } - + public final int search(int start, int range, int option) { + try { + return searchInterruptible(start, range, option); + } catch (InterruptedException ex) { + return INTERRUPTED; + } + } + + public final int searchInterruptible(int start, int range, int option) throws InterruptedException { int s, prev; int origStart = start; int origRange = range; @@ -315,7 +334,7 @@ public abstract class Matcher extends IntHolder { ", range " + (range - str)); } - if (start > end || start < str) return -1; + if (start > end || start < str) return FAILED; /* anchor optimize: resume search range */ if (regex.anchor != 0 && str < end) { @@ -332,20 +351,20 @@ public abstract class Matcher extends IntHolder { } else if ((regex.anchor & AnchorType.BEGIN_BUF) != 0) { /* search str-position only */ if (range > start) { - if (start != str) return -1; // mismatch_no_msa; + if (start != str) return FAILED; // mismatch_no_msa; range = str + 1; } else { if (range <= str) { start = str; range = str; } else { - return -1; // mismatch_no_msa; + return FAILED; // mismatch_no_msa; } } } else if ((regex.anchor & AnchorType.END_BUF) != 0) { minSemiEnd = maxSemiEnd = end; // !end_buf:! - if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa; + if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return FAILED; // mismatch_no_msa; } else if ((regex.anchor & AnchorType.SEMI_END_BUF) != 0) { int preEnd = enc.stepBack(bytes, str, end, end, 1); maxSemiEnd = end; @@ -359,12 +378,12 @@ public abstract class Matcher extends IntHolder { } if (minSemiEnd > str && start <= minSemiEnd) { // !goto end_buf;! - if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa; + if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return FAILED; // mismatch_no_msa; } } else { minSemiEnd = end; // !goto end_buf;! - if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa; + if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return FAILED; // mismatch_no_msa; } } else if ((regex.anchor & AnchorType.ANYCHAR_STAR_ML) != 0) { // goto !begin_position;! @@ -391,7 +410,7 @@ public abstract class Matcher extends IntHolder { if (matchCheck(end, s, prev)) return match(s); return mismatch(); } - return -1; // goto mismatch_no_msa; + return FAILED; // goto mismatch_no_msa; } if (Config.DEBUG_SEARCH) { @@ -564,6 +583,6 @@ public abstract class Matcher extends IntHolder { } } // falls through finish: - return -1; + return FAILED; } } diff --git a/test/org/joni/test/Test.java b/test/org/joni/test/Test.java index dda5c3e..b343f20 100644 --- a/test/org/joni/test/Test.java +++ b/test/org/joni/test/Test.java @@ -49,12 +49,89 @@ public abstract class Test { protected int length(byte[]bytes) { return bytes.length; } + + protected void assertTrue(boolean expression, String... failMessage) { + if (expression) { + nsucc++; + } else { + Config.err.println(failMessage); + nfail++; + } + } public void xx(byte[]pattern, byte[]str, int from, int to, int mem, boolean not) { xx(pattern, str, from, to, mem, not, option()); } + + public int xx(byte[]pattern, byte[]str, int from, int to, int mem, boolean not, int option) { + Regex reg; + + try { + reg = new Regex(pattern, 0, length(pattern), option, encoding(), syntax()); + } catch (JOniException je) { + Config.err.println("Pattern: " + repr(pattern) + " Str: " + repr(str)); + je.printStackTrace(Config.err); + Config.err.println("ERROR: " + je.getMessage()); + nerror++; + return Matcher.FAILED; + } catch (Exception e) { + Config.err.println("Pattern: " + repr(pattern) + " Str: " + repr(str)); + e.printStackTrace(Config.err); + Config.err.println("SEVERE ERROR: " + e.getMessage()); + nerror++; + return Matcher.FAILED; + } + + Matcher m = reg.matcher(str, 0, length(str)); + Region region; - public void xx(byte[]pattern, byte[]str, int from, int to, int mem, boolean not, int option) { + int r = 0; + try { + r = m.search(0, length(str), Option.NONE); + region = m.getEagerRegion(); + } catch (JOniException je) { + Config.err.println("Pattern: " + repr(pattern) + " Str: " + repr(str)); + je.printStackTrace(Config.err); + Config.err.println("ERROR: " + je.getMessage()); + nerror++; + return Matcher.FAILED; + } catch (Exception e) { + Config.err.println("Pattern: " + repr(pattern) + " Str: " + repr(str)); + e.printStackTrace(Config.err); + Config.err.println("SEVERE ERROR: " + e.getMessage()); + nerror++; + return Matcher.FAILED; + } + + if (r == -1) { + if (not) { + if (VERBOSE) Config.log.println("OK(N): /" + repr(pattern) + "/ '" + repr(str) + "'"); + nsucc++; + } else { + Config.log.println("FAIL: /" + repr(pattern) + "/ '" + repr(str) + "'"); + nfail++; + } + } else { + if (not) { + Config.log.println("FAIL(N): /" + repr(pattern) + "/ '" + repr(str) + "'"); + nfail++; + } else { + if (region.beg[mem] == from && region.end[mem] == to) { + if (VERBOSE) Config.log.println("OK: /" + repr(pattern) + "/ '" +repr(str) + "'"); + nsucc++; + } else { + Config.log.println("FAIL: /" + repr(pattern) + "/ '" + repr(str) + "' " + + from + "-" + to + " : " + region.beg[mem] + "-" + region.end[mem] + ); + nfail++; + } + } + } + + return r; + } + + public void xxi(byte[]pattern, byte[]str, int from, int to, int mem, boolean not, int option) throws InterruptedException { Regex reg; try { @@ -78,7 +155,7 @@ public abstract class Test { int r = 0; try { - r = m.search(0, length(str), Option.NONE); + r = m.searchInterruptible(0, length(str), Option.NONE); region = m.getEagerRegion(); } catch (JOniException je) { Config.err.println("Pattern: " + repr(pattern) + " Str: " + repr(str)); @@ -86,6 +163,8 @@ public abstract class Test { Config.err.println("ERROR: " + je.getMessage()); nerror++; return; + } catch (InterruptedException e) { + throw e; } catch (Exception e) { Config.err.println("Pattern: " + repr(pattern) + " Str: " + repr(str)); e.printStackTrace(Config.err); @@ -151,18 +230,43 @@ public abstract class Test { uee.printStackTrace(); } } + + public void xxsi(String pattern, String str, int from, int to, int mem, boolean not) throws InterruptedException { + xxsi(pattern, str, from, to, mem, not, option()); + } - public void x2s(String pattern, String str, int from, int to) { - x2s(pattern, str, from, to, option()); + public void xxsi(String pattern, String str, int from, int to, int mem, boolean not, int option) throws InterruptedException { + try{ + xxi(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), from, to, mem, not, option); + } catch (UnsupportedEncodingException uee) { + uee.printStackTrace(); + } + } + + public int x2s(String pattern, String str, int from, int to) { + return x2s(pattern, str, from, to, option()); } - public void x2s(String pattern, String str, int from, int to, int option) { + public int x2s(String pattern, String str, int from, int to, int option) { try{ - xx(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), from, to, 0, false, option); + return xx(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), from, to, 0, false, option); } catch (UnsupportedEncodingException uee) { uee.printStackTrace(); + return Matcher.FAILED; } } + + public void x2si(String pattern, String str, int from, int to) throws InterruptedException { + x2si(pattern, str, from, to, option()); + } + + public void x2si(String pattern, String str, int from, int to, int option) throws InterruptedException { + try{ + xxi(pattern.getBytes(testEncoding()), str.getBytes(testEncoding()), from, to, 0, false, option); + } catch (UnsupportedEncodingException uee) { + uee.printStackTrace(); + } + } public void x3s(String pattern, String str, int from, int to, int mem) { x3s(pattern, str, from, to, mem, option()); @@ -192,10 +296,12 @@ public abstract class Test { Config.log.println("RESULT SUCC: " + nsucc + ", FAIL: " + nfail + ", ERROR: " + nerror + " Test: " + getClass().getSimpleName() + ", Encoding: " + encoding()); } - public abstract void test(); + public abstract void test() throws InterruptedException; public final void run() { - test(); + try { + test(); + } catch (InterruptedException ie) { } printResults(); } diff --git a/test/org/joni/test/TestA.java b/test/org/joni/test/TestA.java index f73153a..aee59ae 100644 --- a/test/org/joni/test/TestA.java +++ b/test/org/joni/test/TestA.java @@ -42,7 +42,7 @@ public class TestA extends Test { return Syntax.DEFAULT; } - public void test() { + public void test() throws InterruptedException { x2s("", "", 0, 0); x2s("^", "", 0, 0); x2s("$", "", 0, 0); diff --git a/test/org/joni/test/TestC.java b/test/org/joni/test/TestC.java index 9af3941..619ef30 100644 --- a/test/org/joni/test/TestC.java +++ b/test/org/joni/test/TestC.java @@ -43,7 +43,7 @@ public class TestC extends Test { return Syntax.DEFAULT; } - public void test() { + public void test() throws InterruptedException { x2s("", "", 0, 0); x2s("^", "", 0, 0); x2s("$", "", 0, 0); diff --git a/test/org/joni/test/TestCornerCases.java b/test/org/joni/test/TestCornerCases.java index 717766d..d8099d5 100644 --- a/test/org/joni/test/TestCornerCases.java +++ b/test/org/joni/test/TestCornerCases.java @@ -44,7 +44,7 @@ public class TestCornerCases extends Test { return Syntax.DEFAULT; } - public void test() { + public void test() throws InterruptedException { byte[] reg = "l.".getBytes(); byte[] str = "hello,lo".getBytes(); diff --git a/test/org/joni/test/TestCrnl.java b/test/org/joni/test/TestCrnl.java index 9a8c65c..de8b984 100644 --- a/test/org/joni/test/TestCrnl.java +++ b/test/org/joni/test/TestCrnl.java @@ -43,7 +43,7 @@ public class TestCrnl extends Test { return Syntax.DEFAULT; } - public void test() { + public void test() throws InterruptedException { x2s("", "\r\n", 0, 0); x2s(".", "\r\n", 0, 1); ns("..", "\r\n"); diff --git a/test/org/joni/test/TestInterrupt.java b/test/org/joni/test/TestInterrupt.java new file mode 100644 index 0000000..dd1547f --- /dev/null +++ b/test/org/joni/test/TestInterrupt.java @@ -0,0 +1,100 @@ +/* + * The MIT License + * + * Copyright 2013 enebo. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.joni.test; + +import java.util.Timer; +import java.util.TimerTask; +import org.jcodings.Encoding; +import org.jcodings.specific.ASCIIEncoding; +import org.joni.Matcher; +import org.joni.Option; +import org.joni.Syntax; + +/** + * These are fairly long-running tests but we want a large time slice to reduce misfires + * on slow ci boxes. + */ +public class TestInterrupt extends Test { + interface InterruptibleRunnable { + public void run() throws InterruptedException; + } + public void test() throws InterruptedException { + interruptAfter(new InterruptibleRunnable() { + public void run() throws InterruptedException { + x2si("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 0); + } + }, 1000, 15000); + + final int status[] = new int[1]; + + interruptAfter(new InterruptibleRunnable() { + public void run() throws InterruptedException { + status[0] = x2s("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 0); + } + }, 1000, 15000); + + assertTrue(status[0] == Matcher.INTERRUPTED, "Status was not INTERRUPTED: " + status[0]); + } + + private void interruptAfter(InterruptibleRunnable block, int delayBeforeInterrupt, int acceptableMaximumTime) { + final long start[] = new long[1]; + + final Thread currentThread = Thread.currentThread(); + + new Timer().schedule(new TimerTask() { + @Override public void run() { + start[0] = System.currentTimeMillis(); + System.out.println("INTERRUPTING at " + start[0]); + currentThread.interrupt(); + } + }, delayBeforeInterrupt); + + try { + block.run(); + } catch (InterruptedException e) { + + } + long total = System.currentTimeMillis() - start[0]; + System.out.println("Time taken: " + total); + assertTrue(total < acceptableMaximumTime, "Took too long to interrupt: " + total + " > " + acceptableMaximumTime); + } + + public int option() { + return Option.DEFAULT; + } + + public Encoding encoding() { + return ASCIIEncoding.INSTANCE; + } + + public String testEncoding() { + return "iso-8859-2"; + } + + public Syntax syntax() { + return Syntax.DEFAULT; + } +} diff --git a/test/org/joni/test/TestJoni.java b/test/org/joni/test/TestJoni.java index a94ed56..b7d5744 100644 --- a/test/org/joni/test/TestJoni.java +++ b/test/org/joni/test/TestJoni.java @@ -29,6 +29,7 @@ public class TestJoni extends TestCase { private Test testnsu8; private Test testLookBehind; private Test testu8; + private Test testInterrupt; protected void setUp() { testa = new TestA(); @@ -37,6 +38,7 @@ public class TestJoni extends TestCase { testnsu8 = new TestNSU8(); testu8 = new TestU8(); testLookBehind = new TestLookBehind(); + testInterrupt = new TestInterrupt(); } protected void tearDown() { @@ -65,4 +67,8 @@ public class TestJoni extends TestCase { public void testLookBehind() { testJoniTest(testLookBehind); } + + public void testInterrupt() { + testJoniTest(testInterrupt); + } } diff --git a/test/org/joni/test/TestLookBehind.java b/test/org/joni/test/TestLookBehind.java index 9ae4379..fa2d279 100644 --- a/test/org/joni/test/TestLookBehind.java +++ b/test/org/joni/test/TestLookBehind.java @@ -43,7 +43,7 @@ public class TestLookBehind extends Test { } @Override - public void test() { + public void test() throws InterruptedException { x2s("(?<=a).*b", "aab", 1, 3); } diff --git a/test/org/joni/test/TestNSU8.java b/test/org/joni/test/TestNSU8.java index ec4ed2d..1189776 100644 --- a/test/org/joni/test/TestNSU8.java +++ b/test/org/joni/test/TestNSU8.java @@ -42,7 +42,7 @@ public class TestNSU8 extends Test { return Syntax.DEFAULT; } - public void test() { + public void test() throws InterruptedException { xx("([^\\[\\]]+)".getBytes(), new byte[]{(byte)240, (byte)32, (byte)32, (byte)32, (byte)32}, 0, 5, 1, false); xx("([^\\[\\]]+)".getBytes(), new byte[]{(byte)240, (byte)32, (byte)32, (byte)32}, 0, 4, 1, false); xx("([^\\[\\]]+)".getBytes(), new byte[]{(byte)240, (byte)32, (byte)32}, 0, 3, 1, false); diff --git a/test/org/joni/test/TestU.java b/test/org/joni/test/TestU.java index eb4a2a7..ee1ed76 100644 --- a/test/org/joni/test/TestU.java +++ b/test/org/joni/test/TestU.java @@ -77,7 +77,7 @@ public class TestU extends Test { return ulen(bytes); } - public void test() { + public void test() throws InterruptedException { x2s("\000\000", "\000\000", 0, 0); x2s("\000^\000\000", "\000\000", 0, 0); x2s("\000$\000\000", "\000\000", 0, 0); diff --git a/test/org/joni/test/TestU8.java b/test/org/joni/test/TestU8.java index c922401..2479e74 100644 --- a/test/org/joni/test/TestU8.java +++ b/test/org/joni/test/TestU8.java @@ -42,7 +42,7 @@ public class TestU8 extends Test { return Syntax.DEFAULT; } - public void test() { + public void test() throws InterruptedException { xx("^\\d\\d\\d-".getBytes(), new byte []{-30, -126, -84, 48, 45}, 0, 0, 0, true); x2s("x{2}", "xx", 0, 2, Option.IGNORECASE); x2s("x{2}", "XX", 0, 2, Option.IGNORECASE); -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jruby-joni.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

