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)
#
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)
#=======
-------------------------------------------------------------------
+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)
PathInfo
Pathname
PluginFrame
+ PoolQueryCC
PoolQuery
ProgressData
PtrTypes
--- /dev/null
+#include "TestSetup.h"
+#include <zypp/base/String.h>
+#include <zypp/base/LogTools.h>
+
+#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 <class TCont>
+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<sat::Solvable> 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 );
+}
+
+/////////////////////////////////////////////////////////////////////////////
q.addRepo("opensuse");
q.addKind(ResKind::patch);
q.setMatchRegex();
- q.setRequireAll();
q.setCaseSensitive();
q.setUninstalledOnly();
q.setEdition(Edition("0.8.3"),Rel::NE);
BOOST_CHECK( !m( "" ) );
BOOST_CHECK( !m( "a" ) );
BOOST_CHECK( m( "fau" ) );
+ BOOST_CHECK( !m( "fault" ) );
+ BOOST_CHECK( !m( "defau" ) );
BOOST_CHECK( !m( "default" ) );
}
BOOST_CHECK( !m( "a" ) );
BOOST_CHECK( m( "fau" ) );
BOOST_CHECK( m( "fault" ) );
+ BOOST_CHECK( !m( "defau" ) );
BOOST_CHECK( !m( "default" ) );
}
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() );
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";
repo: opensuse
type: patch
match_type: regex
-require_all: on
case_sensitive: on
install_status: not-installed
version: != 0.8.3
--- /dev/null
+<channel>
+ <subchannel>
+ <package><name>.</name></package>
+ <package><name>?</name></package>
+ <package><name>*</name></package>
+ <package><name>+</name></package>
+ <package><name>[</name></package>
+ <package><name>]</name></package>
+ <package><name>(</name></package>
+ <package><name>)</name></package>
+ <package><name>{</name></package>
+ <package><name>}</name></package>
+ <package><name>|</name></package>
+ <package><name>^</name></package>
+ <package><name>$</name></package>
+ <package><name>\</name></package>
+ <package><name>A</name></package>
+ <package><name>AB</name></package>
+ <package><name>BAB</name></package>
+ <package><name>.A.</name></package>
+</subchannel></channel>
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);
}
Impl()
: _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
, _match_word(false)
- , _require_all(false)
, _status_flags(ALL)
{}
/** Sat solver search flags */
Match _flags;
bool _match_word;
- bool _require_all;
/** Sat solver status flags */
StatusFilter _status_flags;
&& _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
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<Impl>( const Impl * rhs );
{
_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
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
{
// 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;
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 ) );
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 ) ) );
}
}
}
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
{
// 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.
//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
_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; }
{ _pimpl->_status_flags = flags; }
- void PoolQuery::setRequireAll(bool require_all)
- { _pimpl->_require_all = require_all; }
-
-
const PoolQuery::StrContainer &
PoolQuery::strings() const
{ return _pimpl->_strings; }
{ _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; }
{ invokeOnEach( begin(), end(), fnc); }
+ /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {}
+ /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const { return false; }
+
///////////////////////////////////////////////////////////////////
//
// CLASS NAME : PoolQuery::Attr
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;
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");
}
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 )
{
}
}
- if( requireAll() != q.requireAll() )
- {
- str << "require_all: ";
- if (requireAll())
- {
- str << "on" << delim;
- }
- else
- {
- str << "off" << delim;
- }
- }
-
if( statusFilterFlags() != q.statusFilterFlags() )
{
switch( statusFilterFlags() )
* 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 <b>any</b>
- * 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);
* case the query strings will be combined (together with strings added
* via addString()) into a regex. Searched attribute value will match
* this regex if <b>any</b> 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
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 */
//@{
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;
//@}
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 */
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 );
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()
//@}