From: DongHun Kwak Date: Mon, 2 Sep 2019 07:09:30 +0000 (+0900) Subject: Imported Upstream version 14.46.0 X-Git-Tag: upstream/14.46.0^0 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fupstream%2Flibzypp.git;a=commitdiff_plain;h=48046fb4ac6b1d63d5031ddc51b9dab8c6b6fd8a Imported Upstream version 14.46.0 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 47786d4..3a476d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,8 +91,6 @@ MACRO(ADD_TESTS) ENDMACRO(ADD_TESTS) #################################################################### -# prefer packages using the same install prefix as we do -SET(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX} usr/localX /usr/local /usr) FIND_PACKAGE(Rpm REQUIRED) IF ( NOT RPM_FOUND) diff --git a/VERSION.cmake b/VERSION.cmake index 496bf85..b75578d 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -60,9 +60,9 @@ # SET(LIBZYPP_MAJOR "14") SET(LIBZYPP_COMPATMINOR "39") -SET(LIBZYPP_MINOR "45") -SET(LIBZYPP_PATCH "18") +SET(LIBZYPP_MINOR "46") +SET(LIBZYPP_PATCH "0") # -# LAST RELEASED: 14.45.18 (39) +# LAST RELEASED: 14.46.0 (39) # (The number in parenthesis is LIBZYPP_COMPATMINOR) #======= diff --git a/package/libzypp.changes b/package/libzypp.changes index 7181c36..ce915aa 100644 --- a/package/libzypp.changes +++ b/package/libzypp.changes @@ -1,4 +1,11 @@ ------------------------------------------------------------------- +Tue Oct 2 12:36:18 CEST 2018 - ma@suse.de + +- Fix conversion of string and glob to regex when compiling queries + (bsc#1099982, bsc#939392, bsc#556664) +- version 14.46.0 (39) + +------------------------------------------------------------------- Mon Oct 1 14:37:39 CEST 2018 - ma@suse.de - Fix blocking wait for finished child process (bsc#1109877) diff --git a/tests/zypp/CMakeLists.txt b/tests/zypp/CMakeLists.txt index a2d5d9c..cba941b 100644 --- a/tests/zypp/CMakeLists.txt +++ b/tests/zypp/CMakeLists.txt @@ -26,6 +26,7 @@ ADD_TESTS( PathInfo Pathname PluginFrame + PoolQueryCC PoolQuery ProgressData PtrTypes diff --git a/tests/zypp/PoolQueryCC_test.cc b/tests/zypp/PoolQueryCC_test.cc new file mode 100644 index 0000000..6c50db4 --- /dev/null +++ b/tests/zypp/PoolQueryCC_test.cc @@ -0,0 +1,121 @@ +#include "TestSetup.h" +#include +#include + +#include "zypp/PoolQuery.h" +#include "zypp/PoolQueryUtil.tcc" + +#define BOOST_TEST_MODULE PoolQuery_CC + +using boost::unit_test::test_case; +using std::cin; +using std::cout; +using std::cerr; +using std::endl; +using namespace zypp; + +static TestSetup test; + +///////////////////////////////////////////////////////////////////////////// +template +std::ostream & nlist( std::ostream & str, const TCont & set_r ) +{ + str << "[" << set_r.size() << "]: "; + for ( const auto & solv : set_r ) + str << " \"" << solv.name() << "\""; + return str << endl; +} + +BOOST_AUTO_TEST_CASE(init) +{ + test.loadTargetHelix( TESTS_SRC_DIR "/zypp/data/PoolQueryCC/rxnames.xml" ); + nlist( cout << "repo ", ResPool::instance() ); +} + +///////////////////////////////////////////////////////////////////////////// +// Basic issue: Multiple match strings are compiled into a singe regex. The +// semantic of the individual match strings must be preserved. I.e. a literal +// "." must become "\.". Globbing patterns must match the whole string, so they +// need to be anchored within the regex. Etc. +///////////////////////////////////////////////////////////////////////////// +static const unsigned qtestSIZEMISS = unsigned(-1); +static const unsigned qtestRXFAIL = unsigned(-2); +static const unsigned qtestRXFAILCOMB = unsigned(-3); + +unsigned qtest( const std::string & pattern_r, Match::Mode mode_r, bool verbose_r = false ) +{ + static constexpr const bool noMatchInvalidRegexException = false; + + typedef std::set Result; + PoolQuery q; + q.addAttribute(sat::SolvAttr::name); + switch ( mode_r ) + { + case Match::STRING: q.setMatchExact(); break; + case Match::SUBSTRING: q.setMatchSubstring(); break; + case Match::OTHER: q.setMatchWord(); break; // OTHER missused for matchWord() + case Match::GLOB: q.setMatchGlob(); break; + case Match::REGEX: q.setMatchRegex(); break; + default: + throw( "unhandled match mode" ); + break; + } + q.addString( pattern_r ); + Result o; + try { + o = Result( q.begin(), q.end() ); // original query + } + catch ( const zypp::MatchInvalidRegexException & excpt ) + { + cout << "Caught: " << excpt << endl; + return qtestRXFAIL; + } + + q.addString( "more" ); + try { + Result r( q.begin(), q.end() ); // compiles into RX (o|more) + + BOOST_CHECK( o == r ); + if ( o != r || verbose_r ) + { + cout << '"' << pattern_r << "\" " << mode_r << endl; + nlist( cout << " o", o ); + nlist( cout << " r", r ); + if ( ! verbose_r ) + return qtestSIZEMISS; + } + } + catch ( const zypp::MatchInvalidRegexException & excpt ) + { + BOOST_CHECK( noMatchInvalidRegexException ); + cout << "Caught: " << excpt << endl; + return qtestRXFAILCOMB; + } + + return o.size(); +} + +inline unsigned qtest( const std::string & pattern_r, bool verbose_r = false ) +{ return qtest( pattern_r, Match::SUBSTRING, verbose_r ); } + +///////////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(pool_query_init) +{ + // NOTE: qtest( , Match::OTHER ) is missused for matchWord() + BOOST_CHECK_EQUAL( qtest( "?", Match::SUBSTRING ), 1 ); + BOOST_CHECK_EQUAL( qtest( "?", Match::STRING ), 1 ); + BOOST_CHECK_EQUAL( qtest( "?", Match::OTHER ), 0 ); // not word boundary + BOOST_CHECK_EQUAL( qtest( "?", Match::GLOB ), 15 ); + BOOST_CHECK_EQUAL( qtest( "\\?", Match::GLOB ), 1 ); + BOOST_CHECK_EQUAL( qtest( "?", Match::REGEX ), qtestRXFAIL ); + BOOST_CHECK_EQUAL( qtest( "\\?", Match::REGEX ), 1 ); + + BOOST_CHECK_EQUAL( qtest( "A", Match::SUBSTRING ), 4 ); + BOOST_CHECK_EQUAL( qtest( "A", Match::OTHER ), 2 ); + BOOST_CHECK_EQUAL( qtest( "A*", Match::OTHER ), 0 ); + BOOST_CHECK_EQUAL( qtest( "*A", Match::OTHER ), 0 ); + BOOST_CHECK_EQUAL( qtest( "A*", Match::GLOB ), 2 ); + BOOST_CHECK_EQUAL( qtest( "*A", Match::GLOB ), 1 ); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/tests/zypp/PoolQuery_test.cc b/tests/zypp/PoolQuery_test.cc index 1f9e3a3..544a21d 100644 --- a/tests/zypp/PoolQuery_test.cc +++ b/tests/zypp/PoolQuery_test.cc @@ -606,7 +606,6 @@ BOOST_AUTO_TEST_CASE(pool_query_recovery) q.addRepo("opensuse"); q.addKind(ResKind::patch); q.setMatchRegex(); - q.setRequireAll(); q.setCaseSensitive(); q.setUninstalledOnly(); q.setEdition(Edition("0.8.3"),Rel::NE); diff --git a/tests/zypp/StrMatcher_test.cc b/tests/zypp/StrMatcher_test.cc index ec2e157..3dddf0c 100644 --- a/tests/zypp/StrMatcher_test.cc +++ b/tests/zypp/StrMatcher_test.cc @@ -103,6 +103,8 @@ BOOST_AUTO_TEST_CASE(StrMatcher_STRING) BOOST_CHECK( !m( "" ) ); BOOST_CHECK( !m( "a" ) ); BOOST_CHECK( m( "fau" ) ); + BOOST_CHECK( !m( "fault" ) ); + BOOST_CHECK( !m( "defau" ) ); BOOST_CHECK( !m( "default" ) ); } @@ -113,6 +115,7 @@ BOOST_AUTO_TEST_CASE(StrMatcher_STRINGSTART) BOOST_CHECK( !m( "a" ) ); BOOST_CHECK( m( "fau" ) ); BOOST_CHECK( m( "fault" ) ); + BOOST_CHECK( !m( "defau" ) ); BOOST_CHECK( !m( "default" ) ); } @@ -122,12 +125,61 @@ BOOST_AUTO_TEST_CASE(StrMatcher_STRINGEND) BOOST_CHECK( !m( "" ) ); BOOST_CHECK( !m( "a" ) ); BOOST_CHECK( m( "fau" ) ); + BOOST_CHECK( !m( "fault" ) ); BOOST_CHECK( m( "defau" ) ); BOOST_CHECK( !m( "default" ) ); } +BOOST_AUTO_TEST_CASE(StrMatcher_GLOB) +{ + // GLOB must match whole word + StrMatcher m( "f[a]u", Match::GLOB ); + BOOST_CHECK( !m( "" ) ); + BOOST_CHECK( !m( "a" ) ); + BOOST_CHECK( m( "fau" ) ); + BOOST_CHECK( !m( "fault" ) ); + BOOST_CHECK( !m( "defau" ) ); + BOOST_CHECK( !m( "default" ) ); +} + BOOST_AUTO_TEST_CASE(StrMatcher_REGEX) { + // REGEX matches substring (unless anchored) + StrMatcher m( "f[a]u", Match::REGEX ); + BOOST_CHECK( !m( "" ) ); + BOOST_CHECK( !m( "a" ) ); + BOOST_CHECK( m( "fau" ) ); + BOOST_CHECK( m( "fault" ) ); + BOOST_CHECK( m( "defau" ) ); + BOOST_CHECK( m( "default" ) ); + + m.setSearchstring( "^f[a]u" ); + BOOST_CHECK( !m( "" ) ); + BOOST_CHECK( !m( "a" ) ); + BOOST_CHECK( m( "fau" ) ); + BOOST_CHECK( m( "fault" ) ); + BOOST_CHECK( !m( "defau" ) ); + BOOST_CHECK( !m( "default" ) ); + + m.setSearchstring( "f[a]u$" ); + BOOST_CHECK( !m( "" ) ); + BOOST_CHECK( !m( "a" ) ); + BOOST_CHECK( m( "fau" ) ); + BOOST_CHECK( !m( "fault" ) ); + BOOST_CHECK( m( "defau" ) ); + BOOST_CHECK( !m( "default" ) ); + + m.setSearchstring( "^f[a]u$" ); + BOOST_CHECK( !m( "" ) ); + BOOST_CHECK( !m( "a" ) ); + BOOST_CHECK( m( "fau" ) ); + BOOST_CHECK( !m( "fault" ) ); + BOOST_CHECK( !m( "defau" ) ); + BOOST_CHECK( !m( "default" ) ); +} + +BOOST_AUTO_TEST_CASE(StrMatcher_RX) +{ StrMatcher m( "fau" ); BOOST_CHECK( !m.isCompiled() ); diff --git a/tests/zypp/base/String_test.cc b/tests/zypp/base/String_test.cc index 152a229..2e82ab8 100644 --- a/tests/zypp/base/String_test.cc +++ b/tests/zypp/base/String_test.cc @@ -11,6 +11,56 @@ using namespace std; using namespace zypp; using namespace zypp::str; +#define RXspecial "\\^.[$()|*+?{" + +BOOST_AUTO_TEST_CASE(str2rx) +{ + char s[] = "c"; + char x[] = "\\c"; + for ( const char * ch = RXspecial; *ch; ++ch ) + { + s[0] = x[1] = *ch; + BOOST_CHECK_EQUAL( str::rxEscapeStr( s ), x ); + } +} + +BOOST_AUTO_TEST_CASE(glob2rx) +{ + { + char s[] = "c"; + char x[] = "\\c"; + for ( const char * ch = RXspecial; *ch; ++ch ) + { + s[0] = x[1] = *ch; + if ( *ch == '?' ) + BOOST_CHECK_EQUAL( str::rxEscapeGlob( s ), "." ); + else if ( *ch == '*' ) + BOOST_CHECK_EQUAL( str::rxEscapeGlob( s ), ".*" ); + else if ( *ch == '[' ) + BOOST_CHECK_EQUAL( str::rxEscapeGlob( s ), "\\[" ); // no closing ] so it is literal + else if ( *ch == '\\' ) + BOOST_CHECK_EQUAL( str::rxEscapeGlob( s ), "\\" ); // actually an input error as "\" is not a valid GLOB + else + { + s[0] = x[1] = *ch; + BOOST_CHECK_EQUAL( str::rxEscapeGlob( s ), x ); + } + } + std::string a( str::rxEscapeStr( RXspecial ) ); // all rx/glob special chars are literally (\-escaped) + BOOST_CHECK_EQUAL( str::rxEscapeGlob( a ), a ); // nothing more to escape. + + // character class: contains "][" + BOOST_CHECK_EQUAL( str::rxEscapeGlob( "[][]" ), "[][]" ); + BOOST_CHECK_EQUAL( str::rxEscapeGlob( "[^][]" ), "[^][]" ); + BOOST_CHECK_EQUAL( str::rxEscapeGlob( "[!][]" ), "[^][]" ); // glob allows ! and ^ to negate a cclass + + // no character class: no closing ']' so take it literally (the ] would be member of the cclass, not the closing ]) + BOOST_CHECK_EQUAL( str::rxEscapeGlob( "[]" ), "\\[]" ); + BOOST_CHECK_EQUAL( str::rxEscapeGlob( "[!]" ), "\\[!]" ); + BOOST_CHECK_EQUAL( str::rxEscapeGlob( "[^]" ), "\\[\\^]" ); + } +} + BOOST_AUTO_TEST_CASE(gsubTest) { string olds = "olds"; diff --git a/tests/zypp/data/PoolQuery/savedqueries b/tests/zypp/data/PoolQuery/savedqueries index 79c689b..26d56e6 100644 --- a/tests/zypp/data/PoolQuery/savedqueries +++ b/tests/zypp/data/PoolQuery/savedqueries @@ -6,7 +6,6 @@ query_string: ma* repo: opensuse type: patch match_type: regex -require_all: on case_sensitive: on install_status: not-installed version: != 0.8.3 diff --git a/tests/zypp/data/PoolQueryCC/rxnames.xml b/tests/zypp/data/PoolQueryCC/rxnames.xml new file mode 100644 index 0000000..d3be84b --- /dev/null +++ b/tests/zypp/data/PoolQueryCC/rxnames.xml @@ -0,0 +1,21 @@ + + + . + ? + * + + + [ + ] + ( + ) + { + } + | + ^ + $ + \ + A + AB + BAB + .A. + diff --git a/zypp/Locks.cc b/zypp/Locks.cc index 721a1cc..fff8b9d 100644 --- a/zypp/Locks.cc +++ b/zypp/Locks.cc @@ -223,8 +223,7 @@ void Locks::removeLock( const ResKind &kind_r, const IdString &name_r ) q.addKind( kind_r ); q.setMatchExact(); q.setCaseSensitive(true); - q.requireAll(); - DBG << "remove lock by selectactable" << endl; + DBG << "remove lock by Selectable" << endl; removeLock(q); } diff --git a/zypp/PoolQuery.cc b/zypp/PoolQuery.cc index 4e286b2..94489ff 100644 --- a/zypp/PoolQuery.cc +++ b/zypp/PoolQuery.cc @@ -357,7 +357,6 @@ namespace zypp Impl() : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND ) , _match_word(false) - , _require_all(false) , _status_flags(ALL) {} @@ -380,7 +379,6 @@ namespace zypp /** Sat solver search flags */ Match _flags; bool _match_word; - bool _require_all; /** Sat solver status flags */ StatusFilter _status_flags; @@ -417,7 +415,6 @@ namespace zypp && _attrs == rhs._attrs && _uncompiledPredicated == rhs._uncompiledPredicated && _match_word == rhs._match_word - && _require_all == rhs._require_all && _status_flags == rhs._status_flags && _edition == rhs._edition && _op == rhs._op @@ -441,8 +438,10 @@ namespace zypp mutable AttrMatchList _attrMatchList; private: - /** Pass flags from \ref compile, as they may have been changed. */ - string createRegex( const StrContainer & container, const Match & flags ) const; + /** Join patterns in \a container_r according to \a flags_r into a single \ref StrMatcher. + * The \ref StrMatcher returned will be a REGEX if more than one pattern was passed. + */ + StrMatcher joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const; private: friend Impl * rwcowClone( const Impl * rhs ); @@ -479,13 +478,8 @@ namespace zypp { _attrMatchList.clear(); - Match cflags( _flags ); - if ( cflags.mode() == Match::OTHER ) // this will never succeed... - ZYPP_THROW( MatchUnknownModeException( cflags ) ); - - /** Compiled search strings. */ - string rcstrings; - + if ( _flags.mode() == Match::OTHER ) // this will never succeed... + ZYPP_THROW( MatchUnknownModeException( _flags ) ); // 'different' - will have to iterate through all and match by ourselves (slow) // 'same' - will pass the compiled string to dataiterator_init @@ -510,11 +504,8 @@ namespace zypp StrContainer joined; invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined)); invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined)); - rcstrings = createRegex(joined, cflags); - if (joined.size() > 1) // switch to regex for multiple strings - cflags.setModeRegex(); - _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, - StrMatcher( rcstrings, cflags ) ) ); + + _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, joinedStrMatcher( joined, _flags ) ) ); } // // MULTIPLE ATTRIBUTES @@ -522,16 +513,21 @@ namespace zypp { // check whether there are any per-attribute strings bool attrvals_empty = true; - for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai) - if (!ai->second.empty()) - for(StrContainer::const_iterator it = ai->second.begin(); - it != ai->second.end(); it++) - if (!it->empty()) - { - attrvals_empty = false; - goto attremptycheckend; - } -attremptycheckend: + for_( ai, _attrs.begin(), _attrs.end() ) + { + if ( ai->second.empty() ) + continue; + for_( it, ai->second.begin(), ai->second.end() ) + { + if ( !it->empty() ) + { + attrvals_empty = false; + break; + } + } + if ( ! attrvals_empty ) + break; + } // chceck whether the per-attribute strings are all the same bool attrvals_thesame = true; @@ -562,18 +558,15 @@ attremptycheckend: if (attrvals_empty) { invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined)); - rcstrings = createRegex(joined, cflags); } else { invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined)); invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined)); - rcstrings = createRegex(joined, cflags); } - if (joined.size() > 1) // switch to regex for multiple strings - cflags.setModeRegex(); + // May use the same StrMatcher for all - StrMatcher matcher( rcstrings, cflags ); + StrMatcher matcher( joinedStrMatcher( joined, _flags ) ); for_( ai, _attrs.begin(), _attrs.end() ) { _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) ); @@ -591,11 +584,8 @@ attremptycheckend: StrContainer joined; invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined)); invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined)); - string s = createRegex(joined, cflags); - if (joined.size() > 1) // switch to regex for multiple strings - cflags.setModeRegex(); - _attrMatchList.push_back( AttrMatchData( ai->first, - StrMatcher( s, cflags ) ) ); + + _attrMatchList.push_back( AttrMatchData( ai->first, joinedStrMatcher( joined, _flags ) ) ); } } } @@ -615,14 +605,10 @@ attremptycheckend: if ( ! mstr.empty() ) joined.insert( mstr ); - cflags = _flags; - rcstrings = createRegex( joined, cflags ); - if ( joined.size() > 1 ) // switch to regex for multiple strings - cflags.setModeRegex(); - - _attrMatchList.push_back( AttrMatchData( it->attr, - StrMatcher( rcstrings, cflags ), - it->predicate, it->predicateStr ) ); + // copy and exchange the StrMatcher + AttrMatchData nattr( *it ); + nattr.strMatcher = joinedStrMatcher( joined, _flags ); + _attrMatchList.push_back( std::move(nattr) ); } else { @@ -635,12 +621,7 @@ attremptycheckend: // If no attributes defined at all, then add 'query all' if ( _attrMatchList.empty() ) { - cflags = _flags; - rcstrings = createRegex( _strings, cflags ); - if ( _strings.size() > 1 ) // switch to regex for multiple strings - cflags.setModeRegex(); - _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, - StrMatcher( rcstrings, cflags ) ) ); + _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, joinedStrMatcher( _strings, _flags ) ) ); } // Finally check here, whether all involved regex compile. @@ -651,102 +632,62 @@ attremptycheckend: //DBG << asString() << endl; } - - /** - * Converts '*' and '?' wildcards within str into their regex equivalents. - */ - static string wildcards2regex(const string & str) - { - string regexed = str; - - string r_all(".*"); // regex equivalent of '*' - string r_one("."); // regex equivalent of '?' - string::size_type pos; - - // replace all "*" in input with ".*" - for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2) - regexed = regexed.replace(pos, 1, r_all); - - // replace all "?" in input with "." - for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos) - regexed = regexed.replace(pos, 1, r_one); - - return regexed; - } - - string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const + /////////////////////////////////////////////////////////////////// + namespace { -//! macro for word boundary tags for regexes -#define WB (_match_word ? string("\\b") : string()) - string rstr; - - if (container.empty()) - return rstr; - - if (container.size() == 1) + /** Escape \a str_r for use in a regex. + * \a flags_r determines whether the input string is interpreted + * as regex, glob or plain string. + */ + std::string rxEscape( std::string str_r, const Match & flags_r ) { - return WB + *container.begin() + WB; - } - - // multiple strings + if ( str_r.empty() || flags_r.isModeRegex() ) + return str_r; - bool use_wildcards = flags.isModeGlob(); - StrContainer::const_iterator it = container.begin(); - string tmp; + if ( flags_r.isModeGlob() ) + return str::rxEscapeGlob( std::move(str_r) ); - if (use_wildcards) - tmp = wildcards2regex(*it); - else - tmp = *it; - - if (_require_all) - { - if ( ! flags.isModeString() ) // not match exact - tmp += ".*" + WB + tmp; - rstr = "(?=" + tmp + ")"; + return str::rxEscapeStr( std::move(str_r) ); } - else - { - if ( flags.isModeString() || flags.isModeGlob() ) - rstr = "^"; - rstr += WB + "(" + tmp; - } - - ++it; + } // namespace + /////////////////////////////////////////////////////////////////// - for (; it != container.end(); ++it) + StrMatcher PoolQuery::Impl::joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const + { + if ( container_r.empty() ) + return StrMatcher( std::string(), flags_r ); + + if ( container_r.size() == 1 && !_match_word ) // use RX to match words + return StrMatcher( *container_r.begin(), flags_r ); + + // Convert to a regex. + // Note: Modes STRING and GLOB match whole strings (anchored ^ $) + // SUBSTRING and REGEX match substrings (match_word anchores SUBSTRING \b) + Match retflags( flags_r ); + retflags.setModeRegex(); + str::Str ret; + + if ( flags_r.isModeString() || flags_r.isModeGlob() ) + ret << "^"; + else if ( _match_word ) + ret << "\\b"; + + // (..|..|..) + char sep = '('; + for ( const::std::string & s : container_r ) { - if (use_wildcards) - tmp = wildcards2regex(*it); - else - tmp = *it; - - if (_require_all) - { - if ( ! flags.isModeString() ) // not match exact - tmp += ".*" + WB + tmp; - rstr += "(?=" + tmp + ")"; - } - else - { - rstr += "|" + tmp; - } + ret << sep << rxEscape( s, flags_r ); + if ( sep == '(' ) + sep = '|'; } + ret << ')'; - if (_require_all) - { - if ( ! flags.isModeString() ) // not match exact - rstr += WB + ".*"; - } - else - { - rstr += ")" + WB; - if ( flags.isModeString() || flags.isModeGlob() ) - rstr += "$"; - } + if ( flags_r.isModeString() || flags_r.isModeGlob() ) + ret << "$"; + else if ( _match_word ) + ret << "\\b"; - return rstr; -#undef WB + return StrMatcher( ret, retflags ); } string PoolQuery::Impl::asString() const @@ -911,15 +852,11 @@ attremptycheckend: _pimpl->_op = op; } - void PoolQuery::setMatchSubstring() { _pimpl->_flags.setModeSubstring(); } - void PoolQuery::setMatchExact() { _pimpl->_flags.setModeString(); } - void PoolQuery::setMatchRegex() { _pimpl->_flags.setModeRegex(); } - void PoolQuery::setMatchGlob() { _pimpl->_flags.setModeGlob(); } - void PoolQuery::setMatchWord() - { - _pimpl->_match_word = true; - _pimpl->_flags.setModeRegex(); - } + void PoolQuery::setMatchSubstring() { _pimpl->_flags.setModeSubstring(); _pimpl->_match_word = false; } + void PoolQuery::setMatchExact() { _pimpl->_flags.setModeString(); _pimpl->_match_word = false; } + void PoolQuery::setMatchRegex() { _pimpl->_flags.setModeRegex(); _pimpl->_match_word = false; } + void PoolQuery::setMatchGlob() { _pimpl->_flags.setModeGlob(); _pimpl->_match_word = false; } + void PoolQuery::setMatchWord() { _pimpl->_flags.setModeSubstring(); _pimpl->_match_word = true; } Match PoolQuery::flags() const { return _pimpl->_flags; } @@ -935,10 +872,6 @@ attremptycheckend: { _pimpl->_status_flags = flags; } - void PoolQuery::setRequireAll(bool require_all) - { _pimpl->_require_all = require_all; } - - const PoolQuery::StrContainer & PoolQuery::strings() const { return _pimpl->_strings; } @@ -981,15 +914,10 @@ attremptycheckend: { _pimpl->_flags.turn( Match::FILES, value ); } bool PoolQuery::matchExact() const { return _pimpl->_flags.isModeString(); } - bool PoolQuery::matchSubstring() const { return _pimpl->_flags.isModeSubstring(); } + bool PoolQuery::matchSubstring() const { return _pimpl->_flags.isModeSubstring() && !_pimpl->_match_word; } bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); } bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); } - - bool PoolQuery::matchWord() const - { return _pimpl->_match_word; } - - bool PoolQuery::requireAll() const - { return _pimpl->_require_all; } + bool PoolQuery::matchWord() const { return _pimpl->_flags.isModeSubstring() && _pimpl->_match_word; } PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const { return _pimpl->_status_flags; } @@ -1018,6 +946,9 @@ attremptycheckend: { invokeOnEach( begin(), end(), fnc); } + /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {} + /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const { return false; } + /////////////////////////////////////////////////////////////////// // // CLASS NAME : PoolQuery::Attr @@ -1052,7 +983,7 @@ attremptycheckend: static const PoolQueryAttr kindAttr; static const PoolQueryAttr stringAttr; static const PoolQueryAttr stringTypeAttr; - static const PoolQueryAttr requireAllAttr; + static const PoolQueryAttr requireAllAttr; // LEAGACY: attribute was defined but never implemented. static const PoolQueryAttr caseSensitiveAttr; static const PoolQueryAttr installStatusAttr; static const PoolQueryAttr editionAttr; @@ -1065,7 +996,7 @@ attremptycheckend: const PoolQueryAttr PoolQueryAttr::kindAttr( "type" ); const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" ); const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type"); - const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all"); + const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all"); // LEAGACY: attribute was defined but never implemented. const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive"); const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status"); const PoolQueryAttr PoolQueryAttr::editionAttr("version"); @@ -1189,18 +1120,8 @@ attremptycheckend: } else if ( attribute==PoolQueryAttr::requireAllAttr ) { - if ( str::strToTrue(attrValue) ) - { - setRequireAll(true); - } - else if ( !str::strToFalse(attrValue) ) - { - setRequireAll(false); - } - else - { - WAR << "unknown boolean value " << attrValue << endl; - } + // LEAGACY: attribute was defined but never implemented. + // Actually it should not occur outside our testcases. } else if ( attribute==PoolQueryAttr::caseSensitiveAttr ) { @@ -1344,19 +1265,6 @@ attremptycheckend: } } - if( requireAll() != q.requireAll() ) - { - str << "require_all: "; - if (requireAll()) - { - str << "on" << delim; - } - else - { - str << "off" << delim; - } - } - if( statusFilterFlags() != q.statusFilterFlags() ) { switch( statusFilterFlags() ) diff --git a/zypp/PoolQuery.h b/zypp/PoolQuery.h index aeb3b4b..6897674 100644 --- a/zypp/PoolQuery.h +++ b/zypp/PoolQuery.h @@ -182,8 +182,7 @@ namespace zypp * This method can be used multiple times in which case the query strings * will be combined (together with strings added via addAttribute()) into * a regex. Searched attribute value will match this regex if any - * of these strings will match the value. This can be changed by - * (not yet implemented) \ref setRequireAll() method. + * of these strings will match the value. */ void addString(const std::string & value); @@ -195,8 +194,6 @@ namespace zypp * case the query strings will be combined (together with strings added * via addString()) into a regex. Searched attribute value will match * this regex if any of these strings will match the value. - * This can be changed by (not yet implemented) \ref setRequireAll() - * method. * * \note Though it is possible to use dependency attributes like * \ref Solv::Attr::provides here, note that the query string is @@ -365,20 +362,11 @@ namespace zypp void setMatchGlob(); /** Set to use the query strings as regexes */ void setMatchRegex(); - /** Set to match words (uses regex) */ + /** Set substring to match words */ void setMatchWord(); //void setLocale(const Locale & locale); //@} - /** - * Require that all of the values set by addString or addAttribute - * match the values of respective attributes. - * - * \todo doesn't work yet, don't use this function - */ - void setRequireAll( bool require_all = true ); - - /** \name getters */ //@{ @@ -421,12 +409,6 @@ namespace zypp Match::Mode matchMode() const { return flags().mode(); } - /** - * Whether all values added via addString() or addAttribute() are required - * to match the values of the respective attributes. - */ - bool requireAll() const; - StatusFilter statusFilterFlags() const; //@} @@ -478,6 +460,12 @@ namespace zypp void setFlags( const Match & flags ); public: + /** \deprecated Attribute was defined but never implemented/used. Will be removed in future versions. */ + void setRequireAll( bool require_all = true ) ZYPP_DEPRECATED; + /** \deprecated Attribute was defined but never implemented/used. Will be removed in future versions. */ + bool requireAll() const ZYPP_DEPRECATED; + + public: class Impl; private: /** Pointer to implementation */ diff --git a/zypp/base/String.cc b/zypp/base/String.cc index fb7bd35..47ef8b1 100644 --- a/zypp/base/String.cc +++ b/zypp/base/String.cc @@ -373,6 +373,91 @@ namespace zypp return std::string( buf.begin(), buf.end() ); } + + std::string bEscape( std::string str_r, const C_Str & special_r ) + { + if ( str_r.empty() ) + return str_r; + + if ( str_r.find_first_of( special_r ) == std::string::npos + && ( ::strchr( special_r.c_str(), '\\' ) || !::strchr( str_r.c_str(), '\\' ) ) ) + return str_r; + + Str buf; + for_( s, str_r.c_str(), s+str_r.size() ) + { + if ( *s == '\\' || ::strchr( special_r.c_str(), *s ) ) + buf << '\\'; + buf << *s; + } + return buf; + } + + #define RXSPECIALCHARS "\\.*+?^$[()|{" + + std::string rxEscapeStr( std::string str_r ) + { + return bEscape( std::move(str_r), RXSPECIALCHARS ); + } + + std::string rxEscapeGlob( std::string str_r ) + { + if ( str_r.empty() ) + return str_r; + + if ( str_r.find_first_of( RXSPECIALCHARS ) == std::string::npos ) + return str_r; + + Str buf; + for_( s, str_r.c_str(), s+str_r.size() ) + { + if ( *s == '\\' ) // + next char literally + { + buf << '\\'; + if ( *(s+1) ) { ++s; buf << *s; } + } + else if ( *s == '?' ) // translate + { + buf << '.'; + } + else if ( *s == '*' ) // translate + { + buf << ".*"; + } + else if ( *s == '[' ) // character class if closing ] is found, else literally + { + const char * e = s+1; + if ( *e == '^' || *e == '!' ) // negated cclass + ++e; + if ( *e == ']' ) // ] in cclass + ++e; + while ( *e && *e != ']' ) // ...to ] or \0 + ++e; + if ( *e ) // on closing ']' + { + ++s; buf << '[' << (*s == '!' ? '^' : *s ); + while ( ++s != e ) + buf << *s; + buf << ']'; + } + else + { + buf << "\\["; + } + } + else if ( ::strchr( RXSPECIALCHARS, *s ) ) // escape + { + buf << '\\' << *s; + } + else + { + buf << *s; + } + } + return buf; + } + + std::string getline( std::istream & str, const Trim trim_r ) { return trim( receiveUpTo( str, '\n' ), trim_r ); diff --git a/zypp/base/String.h b/zypp/base/String.h index acff23a..bb59e93 100644 --- a/zypp/base/String.h +++ b/zypp/base/String.h @@ -917,6 +917,15 @@ namespace zypp str_r += escape( next_r, sep_r ); } + /** Return \a str_r with '\'-escaped chars occurring in \a special_r (and '\'). */ + std::string bEscape( std::string str_r, const C_Str & special_r ); + + /** Escape plain STRING \a str_r for use in a regex (not anchored by "^" or "$"). */ + std::string rxEscapeStr( std::string str_r ); + + /** Escape GLOB \a str_r for use in a regex (not anchored by "^" or "$"). */ + std::string rxEscapeGlob( std::string str_r ); + //! \todo unsecape() //@}