From 923908e62c40dfd68ed22ea42ecb17b98226a5d8 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 3 Sep 2021 09:23:58 +0200 Subject: [PATCH 01/10] #65: fixed reading of multibyte characters in ClassRegexIterator --- src/librexgen/iterator/classregexiterator.h | 18 ++++++------------ src/librexgen/parser/regex_lexer.l | 2 +- src/librexgen/string/simplestring.cpp | 4 ++-- src/librexgen/string/simplestring.h | 2 +- src/rexgen/rexgen.1 | 2 +- src/unittest/testcases/test_simpleregex.cpp | 3 ++- 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/librexgen/iterator/classregexiterator.h b/src/librexgen/iterator/classregexiterator.h index 5fa8d13..f2883ef 100644 --- a/src/librexgen/iterator/classregexiterator.h +++ b/src/librexgen/iterator/classregexiterator.h @@ -38,19 +38,13 @@ namespace rexgen { template ClassRegexIterator(Iter begin, Iter end) : Iterator(), current(-1), characters() { - std::for_each(begin, end, [this](const wchar_t& ch) {characters.append_widechar(ch);}); std::string::size_type index = 0; - for (size_t n = 0; n < characters.length(); ++n) { - - /* - * TODO(jasa): - * the call to character_length is very slow and should be removed - */ - lengths.push_back(characters.character_length(n)); - + std::for_each(begin, end, [this, &index](const wchar_t& ch) { + size_t mb_length = characters.append_widechar(ch); + lengths.push_back(mb_length); indices.push_back(index); - index += characters.character_length(n); - } + index += mb_length; + }); characters_count = static_cast(characters.length()); state = usable; } @@ -113,7 +107,7 @@ namespace rexgen { /* * we must use this because multibyte characters - * cannot be counted effectively + * cannot be counted efficiently */ int characters_count; SimpleString characters; diff --git a/src/librexgen/parser/regex_lexer.l b/src/librexgen/parser/regex_lexer.l index beacef3..5db8b7b 100644 --- a/src/librexgen/parser/regex_lexer.l +++ b/src/librexgen/parser/regex_lexer.l @@ -57,7 +57,7 @@ CARRIAGERETURN \\r CLASS_DIGIT \\d CLASS_WORD \\w CLASS_SPACE \\s -SPECIAL [ \t\r\n,;:=/%&<>-] +SPECIAL [ \t\r\n.,;:=/%&<>-] NORMAL [^ \t\r\n.,;:=/%&?<>-] GROUPID \\[1-9] STREAM \\0 diff --git a/src/librexgen/string/simplestring.cpp b/src/librexgen/string/simplestring.cpp index 61a845f..1818f73 100644 --- a/src/librexgen/string/simplestring.cpp +++ b/src/librexgen/string/simplestring.cpp @@ -54,7 +54,7 @@ void SimpleString::toggle_case(size_t idx) { } } -SimpleString& SimpleString::append_widechar(const wchar_t &widechar) { +size_t SimpleString::append_widechar(const wchar_t &widechar) { char buffer[MB_LEN_MAX]; int ch_size = std::wctomb(&buffer[0], widechar); @@ -64,7 +64,7 @@ SimpleString& SimpleString::append_widechar(const wchar_t &widechar) { } this->append(&buffer[0], ch_size); - return *this; + return static_cast(ch_size); } wchar_t SimpleString::widechar_at(size_t index) const { diff --git a/src/librexgen/string/simplestring.h b/src/librexgen/string/simplestring.h index 7538b57..1d397c5 100644 --- a/src/librexgen/string/simplestring.h +++ b/src/librexgen/string/simplestring.h @@ -62,7 +62,7 @@ class SimpleString : public std::string { bool isupper(unsigned int n) const; wchar_t widechar_at(size_t index) const; - SimpleString& append_widechar(const wchar_t& codepoint); + size_t append_widechar(const wchar_t& codepoint); }; #endif /* __cplusplus */ diff --git a/src/rexgen/rexgen.1 b/src/rexgen/rexgen.1 index 69e5c4a..67514ef 100644 --- a/src/rexgen/rexgen.1 +++ b/src/rexgen/rexgen.1 @@ -80,7 +80,7 @@ T} .TE .SH Examples -.IP "rexgen index.php?id=[1-5]" +.IP "rexgen index\\.php\\?id=[1-5]" Would create the results .nf index.php?id=1 diff --git a/src/unittest/testcases/test_simpleregex.cpp b/src/unittest/testcases/test_simpleregex.cpp index cac9b75..f1dc131 100644 --- a/src/unittest/testcases/test_simpleregex.cpp +++ b/src/unittest/testcases/test_simpleregex.cpp @@ -31,4 +31,5 @@ TEST_CASE("TestSimple2", "TestSimple2") {validateRegex("test[\\d]", 10);} TEST_CASE("TestSimple3", "TestSimple3") {validateRegex("test[a\\d]", 11);} TEST_CASE("TestSimple4", "TestSimple4") {validateRegex("test[\\da]", 11);} TEST_CASE("TestSimple5", "TestSimple5") {validateRegex("test\\da", 10);} -TEST_CASE("TestSimple6", "TestSimple6") {validateRegex("a\\dtest", 10);} \ No newline at end of file +TEST_CASE("TestSimple6", "TestSimple6") {validateRegex("a\\dtest", 10);} +TEST_CASE("TestSimple7", "TestSimple7") {validateRegex("index\\.php_id=[1-5]", 5);} \ No newline at end of file From 346a21be6a8b08cdcc10e7ad4c0523aef53a04eb Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 3 Sep 2021 09:29:50 +0200 Subject: [PATCH 02/10] #65: fixed calculation of character_count in ClassRegexIterator --- src/librexgen/iterator/classregexiterator.h | 2 +- src/librexgen/parser/regex_lexer.l | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librexgen/iterator/classregexiterator.h b/src/librexgen/iterator/classregexiterator.h index f2883ef..78df5e0 100644 --- a/src/librexgen/iterator/classregexiterator.h +++ b/src/librexgen/iterator/classregexiterator.h @@ -44,8 +44,8 @@ namespace rexgen { lengths.push_back(mb_length); indices.push_back(index); index += mb_length; + ++characters_count; }); - characters_count = static_cast(characters.length()); state = usable; } diff --git a/src/librexgen/parser/regex_lexer.l b/src/librexgen/parser/regex_lexer.l index 5db8b7b..c45743b 100644 --- a/src/librexgen/parser/regex_lexer.l +++ b/src/librexgen/parser/regex_lexer.l @@ -57,7 +57,7 @@ CARRIAGERETURN \\r CLASS_DIGIT \\d CLASS_WORD \\w CLASS_SPACE \\s -SPECIAL [ \t\r\n.,;:=/%&<>-] +SPECIAL [ \t\r\n.,;:=/%&?<>-] NORMAL [^ \t\r\n.,;:=/%&?<>-] GROUPID \\[1-9] STREAM \\0 From d5bbeeb1349002ecbf13bb3832441bd2061dc505 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 3 Sep 2021 09:37:09 +0200 Subject: [PATCH 03/10] #65: clean initialization of character_count --- src/librexgen/iterator/classregexiterator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librexgen/iterator/classregexiterator.h b/src/librexgen/iterator/classregexiterator.h index 78df5e0..4e34953 100644 --- a/src/librexgen/iterator/classregexiterator.h +++ b/src/librexgen/iterator/classregexiterator.h @@ -37,7 +37,7 @@ namespace rexgen { template ClassRegexIterator(Iter begin, Iter end) - : Iterator(), current(-1), characters() { + : Iterator(), current(-1), characters_count(0), characters() { std::string::size_type index = 0; std::for_each(begin, end, [this, &index](const wchar_t& ch) { size_t mb_length = characters.append_widechar(ch); From 4590aa203e8b5188ebb7ab3048826a5f6325f112 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 3 Sep 2021 10:20:44 +0200 Subject: [PATCH 04/10] #65: removed support for empty class regex --- src/librexgen/parser/regex_parser.y | 4 ++-- src/unittest/testcases/test_classregex.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librexgen/parser/regex_parser.y b/src/librexgen/parser/regex_parser.y index 620fd75..cdc8c17 100644 --- a/src/librexgen/parser/regex_parser.y +++ b/src/librexgen/parser/regex_parser.y @@ -167,8 +167,8 @@ ClassRegex: | CharacterClassWord { $$ = std::move($1); } | CharacterClassSpace { $$ = std::move($1); } | T_BEGIN_CLASS T_HYPHEN ClassContent T_END_CLASS { $$ = std::move($3); $$->addCharacter(btowc('-')); } - | T_BEGIN_CLASS ClassContent T_END_CLASS { $$ = std::move($2); }; - | T_BEGIN_CLASS T_END_CLASS { $$ = std::make_shared(); } + | T_BEGIN_CLASS ClassContent T_END_CLASS { $$ = std::move($2); } + ClassContent: SimpleClassContent { $$ = std::move($1); } | SimpleClassContent ClassContent { diff --git a/src/unittest/testcases/test_classregex.cpp b/src/unittest/testcases/test_classregex.cpp index d3badb6..42c55f2 100644 --- a/src/unittest/testcases/test_classregex.cpp +++ b/src/unittest/testcases/test_classregex.cpp @@ -59,7 +59,8 @@ TEST_CASE("TestIteratorWithOneElement", "TestIteratorWithOneElement") { delete iter; } -TEST_CASE("TestSingle1", "TestSingle1") {validateRegex("[]", 0);} +/* not supported anymore since #65 */ +// TEST_CASE("TestSingle1", "TestSingle1") {validateRegex("[]", 0);} TEST_CASE("TestSingle2", "TestSingle2") {validateRegex("[a]", 1);} TEST_CASE("TestDigits1", "TestDigits1") {validateRegex("\\d", 10);} TEST_CASE("TestDigits2", "TestDigits2") {validateRegex("[\\d]", 10);} From a92266f64cf70a548c38f26ed829f061dca7cdee Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 3 Sep 2021 10:31:39 +0200 Subject: [PATCH 05/10] #65: correct handling of empty character classes --- src/librexgen/iterator/classregexiterator.h | 22 +++++++++++---------- src/librexgen/parser/regex_parser.y | 4 ++-- src/unittest/testcases/test_classregex.cpp | 3 +-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/librexgen/iterator/classregexiterator.h b/src/librexgen/iterator/classregexiterator.h index 4e34953..64b36c4 100644 --- a/src/librexgen/iterator/classregexiterator.h +++ b/src/librexgen/iterator/classregexiterator.h @@ -56,16 +56,18 @@ namespace rexgen { virtual void updateAttributes(IteratorState& /* iterState */) {} inline void value(SimpleString *dst) const { - /** - * FIXME(jasa): - * this condition may be expensive and should be unnecessary - */ - if (current >= 0) { - const std::string::size_type &length = lengths[current]; - const std::string::size_type &index = indices[current]; - - for (std::string::size_type n = 0; n < length; ++n) { - dst->push_back(characters[index + n]); + if (characters_count > 0) { + /** + * FIXME(jasa): + * this condition may be expensive and should be unnecessary + */ + if (current >= 0) { + const std::string::size_type &length = lengths[current]; + const std::string::size_type &index = indices[current]; + + for (std::string::size_type n = 0; n < length; ++n) { + dst->push_back(characters[index + n]); + } } } } diff --git a/src/librexgen/parser/regex_parser.y b/src/librexgen/parser/regex_parser.y index cdc8c17..620fd75 100644 --- a/src/librexgen/parser/regex_parser.y +++ b/src/librexgen/parser/regex_parser.y @@ -167,8 +167,8 @@ ClassRegex: | CharacterClassWord { $$ = std::move($1); } | CharacterClassSpace { $$ = std::move($1); } | T_BEGIN_CLASS T_HYPHEN ClassContent T_END_CLASS { $$ = std::move($3); $$->addCharacter(btowc('-')); } - | T_BEGIN_CLASS ClassContent T_END_CLASS { $$ = std::move($2); } - + | T_BEGIN_CLASS ClassContent T_END_CLASS { $$ = std::move($2); }; + | T_BEGIN_CLASS T_END_CLASS { $$ = std::make_shared(); } ClassContent: SimpleClassContent { $$ = std::move($1); } | SimpleClassContent ClassContent { diff --git a/src/unittest/testcases/test_classregex.cpp b/src/unittest/testcases/test_classregex.cpp index 42c55f2..d3badb6 100644 --- a/src/unittest/testcases/test_classregex.cpp +++ b/src/unittest/testcases/test_classregex.cpp @@ -59,8 +59,7 @@ TEST_CASE("TestIteratorWithOneElement", "TestIteratorWithOneElement") { delete iter; } -/* not supported anymore since #65 */ -// TEST_CASE("TestSingle1", "TestSingle1") {validateRegex("[]", 0);} +TEST_CASE("TestSingle1", "TestSingle1") {validateRegex("[]", 0);} TEST_CASE("TestSingle2", "TestSingle2") {validateRegex("[a]", 1);} TEST_CASE("TestDigits1", "TestDigits1") {validateRegex("\\d", 10);} TEST_CASE("TestDigits2", "TestDigits2") {validateRegex("[\\d]", 10);} From 085876edb04c069c4696957adf87603068835563 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 3 Sep 2021 12:52:58 +0200 Subject: [PATCH 06/10] #65 testing for invalid back references --- src/librexgen/librexgen.cpp | 4 ++-- src/librexgen/parser/rexgenparsingdriver.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librexgen/librexgen.cpp b/src/librexgen/librexgen.cpp index d288130..ca8471f 100644 --- a/src/librexgen/librexgen.cpp +++ b/src/librexgen/librexgen.cpp @@ -31,12 +31,12 @@ std::shared_ptr parse_regex(const char* regex, const rexgen::Rexg try { auto result = driver.parse(regex); -/* + if (driver.hasInvalidGroupReferences()) { driver.handleParserError("This regular expression has an invalid back reference"); return nullptr; } -*/ + return result; } catch (SyntaxError &exc) { driver.handleParserError(exc.getMessage()); diff --git a/src/librexgen/parser/rexgenparsingdriver.cpp b/src/librexgen/parser/rexgenparsingdriver.cpp index 6676094..a39f9d7 100644 --- a/src/librexgen/parser/rexgenparsingdriver.cpp +++ b/src/librexgen/parser/rexgenparsingdriver.cpp @@ -74,7 +74,7 @@ namespace rexgen { bool invalids = false; for (auto ref : groupRefs) { for (auto gr : *(ref.second)) { - invalids |= (gr->getRegex().expired() == false); + invalids |= (gr->getRegex().expired() == true); } } return invalids; From 3d3d22642dd22aa5a7459d3e7918ef4293317c88 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Fri, 3 Sep 2021 13:04:57 +0200 Subject: [PATCH 07/10] #65: no need to escape question marks in character classes --- src/librexgen/parser/regex_lexer.l | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/librexgen/parser/regex_lexer.l b/src/librexgen/parser/regex_lexer.l index c45743b..5aef4d8 100644 --- a/src/librexgen/parser/regex_lexer.l +++ b/src/librexgen/parser/regex_lexer.l @@ -57,8 +57,6 @@ CARRIAGERETURN \\r CLASS_DIGIT \\d CLASS_WORD \\w CLASS_SPACE \\s -SPECIAL [ \t\r\n.,;:=/%&?<>-] -NORMAL [^ \t\r\n.,;:=/%&?<>-] GROUPID \\[1-9] STREAM \\0 ESCAPED \\[^xnurdw0-9] @@ -75,7 +73,7 @@ MULTIBYTE_CHARACTER \xFE {ANSICHAR} { return RexgenParser::make_T_ANY_CHAR(parseAnsiChar(yytext)); } {UNICODECHAR} { return RexgenParser::make_T_ANY_CHAR(parseUnicodeChar(yytext)); } {MULTIBYTE_CHARACTER} { return RexgenParser::make_T_ANY_CHAR(*wcontent_ptr++); } -{CLASS_DIGIT} { return RexgenParser::make_T_CLASS_DIGIT(); } +{CLASS_DIGIT} { return RexgenParser::make_T_CLASS_DIGIT(); } {CLASS_WORD} { return RexgenParser::make_T_CLASS_WORD(); } {CLASS_SPACE} { return RexgenParser::make_T_CLASS_SPACE(); } {LINEFEED} { return RexgenParser::make_T_ANY_CHAR(btowc('\n')); } @@ -87,6 +85,7 @@ MULTIBYTE_CHARACTER \xFE {BEGIN_GROUP_WITH_OPTIONS} { return beginGroupWithOptions(driver); } {BEGIN_GROUP} { return beginGroup(driver); } {END_GROUP} { return RexgenParser::make_T_END_GROUP(); } +"?" { return RexgenParser::make_T_ANY_CHAR(btowc(yytext[0])); } "?" { return RexgenParser::make_T_OPTIONAL_QUANTIFIER(); } "{" { BEGIN(IN_QUANTIFIER); return RexgenParser::make_T_BEGIN_QUANTIFIER(); } "}" { BEGIN(INITIAL); return RexgenParser::make_T_END_QUANTIFIER(); } @@ -95,8 +94,6 @@ MULTIBYTE_CHARACTER \xFE "," { return RexgenParser::make_T_COMMA(); } "-" { return RexgenParser::make_T_HYPHEN(); } {DIGIT}+ { return RexgenParser::make_T_NUMBER(atoi(yytext)); } -{NORMAL} { return RexgenParser::make_T_ANY_CHAR(btowc(yytext[0])); } -{SPECIAL} { return RexgenParser::make_T_ANY_CHAR(btowc(yytext[0])); } -. { } +. { return RexgenParser::make_T_ANY_CHAR(btowc(yytext[0])); } %% \ No newline at end of file From 968b7ad44a72924efe30620354565fcede8f3d72 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Wed, 8 Sep 2021 17:32:36 +0200 Subject: [PATCH 08/10] added c_regex_cb_mb2 --- src/librexgen/c/librexgen.cpp | 7 +++++++ src/librexgen/c/librexgen.h | 5 +++++ src/rexgen/rexgen.c | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/librexgen/c/librexgen.cpp b/src/librexgen/c/librexgen.cpp index 894d899..07aa8f3 100644 --- a/src/librexgen/c/librexgen.cpp +++ b/src/librexgen/c/librexgen.cpp @@ -48,6 +48,13 @@ static size_t callback_wc_wrapper(char* dst, const size_t buffer_size) { EXPORT c_regex_ptr c_regex_cb_mb( + const char* regex_str, + callback_fp_mb cb) { + return c_regex_cb_mb2(regex_str, cb, NULL); +} + +EXPORT +c_regex_ptr c_regex_cb_mb2( const char* regex_str, callback_fp_mb cb, void (*parser_error)(const char* msg)) { diff --git a/src/librexgen/c/librexgen.h b/src/librexgen/c/librexgen.h index a8790d0..9dd9c39 100644 --- a/src/librexgen/c/librexgen.h +++ b/src/librexgen/c/librexgen.h @@ -45,6 +45,11 @@ c_regex_ptr c_regex_cb( EXPORT c_regex_ptr c_regex_cb_mb( + const char* regex_str, + callback_fp_mb cb); + +EXPORT +c_regex_ptr c_regex_cb_mb2( const char* regex_str, callback_fp_mb cb, void (*parser_error)(const char* msg)); diff --git a/src/rexgen/rexgen.c b/src/rexgen/rexgen.c index f3e643c..cdcac22 100644 --- a/src/rexgen/rexgen.c +++ b/src/rexgen/rexgen.c @@ -256,7 +256,7 @@ int _tmain(int argc, _TCHAR* argv[]) { } } - regex = c_regex_cb_mb(regex_str, callback, parser_error); + regex = c_regex_cb_mb2(regex_str, callback, parser_error); if (regex == c_regex_none) { fprintf(stderr, "Syntax Error:\n%s\n", c_rexgen_get_last_error()); retval = 1; From 436b943889eb477271e5331370ad5178255ff125 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Wed, 8 Sep 2021 17:35:55 +0200 Subject: [PATCH 09/10] fixed lua interface --- src/librexgen/lua/librexgen_lua.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librexgen/lua/librexgen_lua.cpp b/src/librexgen/lua/librexgen_lua.cpp index c8a9474..3bf8b73 100644 --- a/src/librexgen/lua/librexgen_lua.cpp +++ b/src/librexgen/lua/librexgen_lua.cpp @@ -94,7 +94,7 @@ int rexgen_value(lua_State* L, c_iterator_ptr iter) { EXPORT int rexgen_parse_regex(lua_State* L) { - auto regex = c_regex_cb_mb(luaL_checklstring(L, 1, NULL), callback, parser_error); + auto regex = c_regex_cb_mb2(luaL_checklstring(L, 1, NULL), callback, parser_error); if (regex == c_regex_none) { lua_pushliteral(L, "parsing error"); From 814fc8c43c4b0e4c8b82356829294465e2c88285 Mon Sep 17 00:00:00 2001 From: Jan Starke Date: Tue, 30 Nov 2021 11:44:06 +0100 Subject: [PATCH 10/10] renamed two functions to match older names --- src/librexgen/c/librexgen.cpp | 6 +++--- src/librexgen/c/librexgen.h | 4 ++-- src/rexgen/rexgen.c | 2 +- src/version.txt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librexgen/c/librexgen.cpp b/src/librexgen/c/librexgen.cpp index 07aa8f3..80e39ab 100644 --- a/src/librexgen/c/librexgen.cpp +++ b/src/librexgen/c/librexgen.cpp @@ -47,14 +47,14 @@ static size_t callback_wc_wrapper(char* dst, const size_t buffer_size) { } EXPORT -c_regex_ptr c_regex_cb_mb( +c_regex_ptr __c_regex_cb_mb( const char* regex_str, callback_fp_mb cb) { - return c_regex_cb_mb2(regex_str, cb, NULL); + return c_regex_cb_mb(regex_str, cb, NULL); } EXPORT -c_regex_ptr c_regex_cb_mb2( +c_regex_ptr c_regex_cb_mb( const char* regex_str, callback_fp_mb cb, void (*parser_error)(const char* msg)) { diff --git a/src/librexgen/c/librexgen.h b/src/librexgen/c/librexgen.h index 9dd9c39..e685901 100644 --- a/src/librexgen/c/librexgen.h +++ b/src/librexgen/c/librexgen.h @@ -44,12 +44,12 @@ c_regex_ptr c_regex_cb( callback_fp cb)); EXPORT -c_regex_ptr c_regex_cb_mb( +c_regex_ptr __c_regex_cb_mb( const char* regex_str, callback_fp_mb cb); EXPORT -c_regex_ptr c_regex_cb_mb2( +c_regex_ptr c_regex_cb_mb( const char* regex_str, callback_fp_mb cb, void (*parser_error)(const char* msg)); diff --git a/src/rexgen/rexgen.c b/src/rexgen/rexgen.c index cdcac22..f3e643c 100644 --- a/src/rexgen/rexgen.c +++ b/src/rexgen/rexgen.c @@ -256,7 +256,7 @@ int _tmain(int argc, _TCHAR* argv[]) { } } - regex = c_regex_cb_mb2(regex_str, callback, parser_error); + regex = c_regex_cb_mb(regex_str, callback, parser_error); if (regex == c_regex_none) { fprintf(stderr, "Syntax Error:\n%s\n", c_rexgen_get_last_error()); retval = 1; diff --git a/src/version.txt b/src/version.txt index ac2cdeb..7d2ed7c 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -2.1.3 +2.1.4