* match, it's also suitable for sub-structure (flexarray) inspection
* (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
*
- * (bsc#1035729) If SolvAttr::name searches for an explicit \c kind:name,
- * this \c kind is stored in \ref kindPredicate and will overwrite any
- * 'global' kind restriction applied via \ref PoolQuery::addKind. This
- * task can't be passed off to a predicate, as \ref PoolQueryMatcher::isAMatch
- * must accept only explicit-kind-checking predicate matches, in case the
- * 'global' kind restriction woudl otherwise discard the match.
- *
* \note: \see \ref addPredicate for further constraints.
*/
struct AttrMatchData
AttrMatchData()
{}
- AttrMatchData( sat::SolvAttr attr_r )
- : attr( attr_r )
- {}
-
AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
: attr( attr_r )
, strMatcher( strMatcher_r )
* string representation instead. If you add new predicated, check the
* deserialization code in \ref deserialize.
*/
- template<class _Predicate>
- void addPredicate( const _Predicate & predicate_r )
+ template<class TPredicate>
+ void addPredicate( const TPredicate & predicate_r )
{
predicate = predicate_r;
predicateStr = predicate_r.serialize();
}
sat::SolvAttr attr;
- StrMatcher strMatcher;
+ StrMatcher strMatcher;
Predicate predicate;
std::string predicateStr;
- ResKind kindPredicate = ResKind::nokind; // holds the 'kind' part if SolvAttr:name looks for an explicit 'kind:name'
};
/** \relates AttrMatchData */
inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
{
str << obj.attr << ": " << obj.strMatcher;
- if ( obj.kindPredicate )
- str << " +(" << obj.kindPredicate << ")";
if ( obj.predicate )
str << " +(" << obj.predicateStr << ")";
return str;
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;
public:
- bool operator<( const PoolQuery::Impl & rhs ) const
- {
-#define OUTS(A) if ( A != rhs.A ) return A < rhs.A;
- OUTS( _strings );
- OUTS( _attrs );
- OUTS( _uncompiledPredicated );
- OUTS( _flags.get() );
- OUTS( _match_word );
- OUTS( _status_flags );
- OUTS( _edition );
- OUTS( _op.inSwitch() );
- OUTS( _repos );
- OUTS( _kinds );
-#undef OUTS
- return false;
- }
-
bool operator==( const PoolQuery::Impl & rhs ) const
{
if ( _flags == rhs._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:
- /** 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;
+ /** Pass flags from \ref compile, as they may have been changed. */
+ string createRegex( const StrContainer & container, const Match & flags ) const;
private:
friend Impl * rwcowClone<Impl>( const Impl * rhs );
{
_attrMatchList.clear();
- if ( _flags.mode() == Match::OTHER ) // this will never succeed...
- ZYPP_THROW( MatchUnknownModeException( _flags ) );
+ Match cflags( _flags );
+ if ( cflags.mode() == Match::OTHER ) // this will never succeed...
+ ZYPP_THROW( MatchUnknownModeException( cflags ) );
+
+ /** Compiled search strings. */
+ string rcstrings;
+
// '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));
-
- _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, joinedStrMatcher( joined, _flags ) ) );
+ 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 ) ) );
}
// // MULTIPLE ATTRIBUTES
{
// check whether there are any per-attribute strings
bool attrvals_empty = true;
- 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;
- }
+ 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:
// 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( joinedStrMatcher( joined, _flags ) );
+ StrMatcher matcher( rcstrings, cflags );
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));
-
- _attrMatchList.push_back( AttrMatchData( ai->first, joinedStrMatcher( joined, _flags ) ) );
+ 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 ) ) );
}
}
}
if ( ! mstr.empty() )
joined.insert( mstr );
- // copy and exchange the StrMatcher
- AttrMatchData nattr( *it );
- nattr.strMatcher = joinedStrMatcher( joined, _flags );
- _attrMatchList.push_back( std::move(nattr) );
+ 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 ) );
}
else
{
// If no attributes defined at all, then add 'query all'
if ( _attrMatchList.empty() )
{
- _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, joinedStrMatcher( _strings, _flags ) ) );
+ 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 ) ) );
}
// Finally check here, whether all involved regex compile.
//DBG << asString() << endl;
}
- ///////////////////////////////////////////////////////////////////
- namespace
+
+ /**
+ * Converts '*' and '?' wildcards within str into their regex equivalents.
+ */
+ static string wildcards2regex(const string & str)
{
- /** 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 )
+ 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
+ {
+//! 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)
{
- if ( str_r.empty() || flags_r.isModeRegex() )
- return str_r;
+ return WB + *container.begin() + WB;
+ }
+
+ // multiple strings
+
+ 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;
- return str::rxEscapeStr( std::move(str_r) );
+ if (_require_all)
+ {
+ if ( ! flags.isModeString() ) // not match exact
+ tmp += ".*" + WB + tmp;
+ rstr = "(?=" + tmp + ")";
+ }
+ else
+ {
+ if ( flags.isModeString() || flags.isModeGlob() )
+ rstr = "^";
+ rstr += WB + "(" + tmp;
}
- } // namespace
- ///////////////////////////////////////////////////////////////////
- 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 )
+ ++it;
+
+ for (; it != container.end(); ++it)
{
- ret << sep << rxEscape( s, flags_r );
- if ( sep == '(' )
- sep = '|';
+ 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 << ')';
- if ( flags_r.isModeString() || flags_r.isModeGlob() )
- ret << "$";
- else if ( _match_word )
- ret << "\\b";
+ if (_require_all)
+ {
+ if ( ! flags.isModeString() ) // not match exact
+ rstr += WB + ".*";
+ }
+ else
+ {
+ rstr += ")" + WB;
+ if ( flags.isModeString() || flags.isModeGlob() )
+ rstr += "$";
+ }
- return StrMatcher( ret, retflags );
+ return rstr;
+#undef WB
}
string PoolQuery::Impl::asString() const
void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
{
- // SolvAttr::name with explicit 'kind:name' will overwrite the default _kinds
- ResKind explicitKind;
- if ( attr == sat::SolvAttr::name ) explicitKind = ResKind::explicitBuiltin( name );
-
switch ( op.inSwitch() )
{
case Rel::ANY_e: // no additional constraint on edition.
- if ( arch.empty() && !explicitKind ) // no additional constraint on arch/kind
+ if ( arch.empty() ) // no additional constraint on arch.
{
addAttribute( attr, name );
return;
// Match::OTHER indicates need to compile
// (merge global search strings into name).
- AttrMatchData attrMatchData( attr );
- if ( !explicitKind )
- attrMatchData.strMatcher = StrMatcher( name, Match::OTHER );
- else
- {
- // ResKind::explicitBuiltin call above asserts the presence of the ':' in name
- attrMatchData.strMatcher = StrMatcher( strchr( name.c_str(), ':')+1, Match::OTHER );
- attrMatchData.kindPredicate = explicitKind;
- }
+ AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
if ( isDependencyAttribute( attr ) )
attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
_pimpl->_op = op;
}
- 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; }
+ 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();
+ }
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() && !_pimpl->_match_word; }
+ bool PoolQuery::matchSubstring() const { return _pimpl->_flags.isModeSubstring(); }
bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); }
bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); }
- bool PoolQuery::matchWord() const { return _pimpl->_flags.isModeSubstring() && _pimpl->_match_word; }
+
+ bool PoolQuery::matchWord() const
+ { return _pimpl->_match_word; }
+
+ bool PoolQuery::requireAll() const
+ { return _pimpl->_require_all; }
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; // LEAGACY: attribute was defined but never implemented.
+ static const PoolQueryAttr requireAllAttr;
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"); // LEAGACY: attribute was defined but never implemented.
+ const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
const PoolQueryAttr PoolQueryAttr::editionAttr("version");
}
else if ( attribute==PoolQueryAttr::requireAllAttr )
{
- // LEAGACY: attribute was defined but never implemented.
- // Actually it should not occur outside our testcases.
+ if ( str::strToTrue(attrValue) )
+ {
+ setRequireAll(true);
+ }
+ else if ( !str::strToFalse(attrValue) )
+ {
+ setRequireAll(false);
+ }
+ else
+ {
+ WAR << "unknown boolean value " << attrValue << endl;
+ }
}
else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
{
} while ( true );
- // OLD STYLE VERSIONED LOCKS:
- // solvable_name: kernel
- // version: > 1
- //
- // NEW STYLE VERSIONED LOCKS:
- // complex: AttrMatchData solvable:name kernel C SolvableRange\ >\ 1\ \"\"
- // or
- // solvable_name: kernel > 1
- //
- // Semantically equivalent as locks, but due to the different syntax
- // the complex lock is wrongly handled by zypper.
- //
- // bsc#1112911: Unfortunately all styles are found in real-life locks-files.
- // libzypp will try to make sure, when parsing the locks-file, that complex
- // locks are rewritten into to OLD STYLE queries zypper can handle.
- if ( !_pimpl->_attrs.count(SolvAttr::name) && _pimpl->_uncompiledPredicated.size() == 1 )
- {
- // No OLD STYLE lock for SolvAttr::name and exactly one complex lock...
- const AttrMatchData & attrmatch { *_pimpl->_uncompiledPredicated.begin() };
- if ( attrmatch.attr == SolvAttr::name && attrmatch.strMatcher.flags().mode() == Match::OTHER )
- {
- // ...for SolvAttr::name and following the global search flags.
- // A candidate for a rewrite?
-
- std::vector<std::string> words;
- str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
- if ( words.size() < 4 || words[3].empty() )
- {
- // We have _NO_ arch rule in the complex predicate, so we can simplify it.
- //
- // NOTE: AFAIK it's not possible to create (or have created) a complex lock
- // with arch rule with zypper means. Nevertheless, in case such a rule made it
- // into a locks file, it's better to have a strange looking 'zypper locks' list
- // than to lock the wrong packages.
- // (and remember that you can't use "addAttribute( SolvAttr::arch, ... )" because
- // attributes are `OR`ed)
-
- // kind
- if ( attrmatch.kindPredicate )
- {
- _pimpl->_kinds.clear(); // an explicit kind overwrites any global one
- addKind( attrmatch.kindPredicate );
- }
-
- // name
- addAttribute( SolvAttr::name, attrmatch.strMatcher.searchstring() );
-
- // edition
- std::vector<std::string> words;
- str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
- if ( ! words.empty() )
- {
- if ( words[0] == "EditionRange" || words[0] == "SolvableRange" )
- {
- setEdition( Edition(words[2]), Rel(words[1]) );
- }
- }
-
- // finally remove the complex lock
- _pimpl->_uncompiledPredicated.clear();
- }
- }
- }
-
return finded_something;
}
}
}
+ if( requireAll() != q.requireAll() )
+ {
+ str << "require_all: ";
+ if (requireAll())
+ {
+ str << "on" << delim;
+ }
+ else
+ {
+ str << "off" << delim;
+ }
+ }
+
if( statusFilterFlags() != q.statusFilterFlags() )
{
switch( statusFilterFlags() )
bool PoolQuery::operator==( const PoolQuery & rhs ) const
{ return *_pimpl == *rhs._pimpl; }
- bool PoolQuery::operator<( const PoolQuery & rhs ) const
- { return *_pimpl < *rhs._pimpl; }
-
///////////////////////////////////////////////////////////////////
namespace detail
{ /////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////
sat::Solvable inSolvable( base_r.inSolvable() );
+ // Kind restriction:
+ if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
+ {
+ base_r.nextSkipSolvable();
+ return false;
+ }
+
// Edition restriction:
if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
{
base_r.nextSkipSolvable();
return false;
}
-
- // Kind restriction:
- // Delay the decision to nextSkipSolvable and return false, as there may be
- // some explicit kind:name predicate which overrules the global kinds.
- bool globalKindOk =( _kinds.empty() || inSolvable.isKind( _kinds.begin(), _kinds.end() ) );
-
/////////////////////////////////////////////////////////////////////
// string and predicate matching:
{
// String matching was done by the base iterator.
// Now check any predicate:
- const AttrMatchData & matchData( _attrMatchList.front() );
-
- if ( matchData.kindPredicate )
- {
- if ( matchData.kindPredicate != inSolvable.kind() )
- {
- base_r.nextSkipSolvable(); // this matchData will never match in this solvable
- return false;
- }
- }
- else if ( !globalKindOk )
- return false; // only matching kindPredicate could overwrite this
-
- if ( !matchData.predicate || matchData.predicate( base_r ) )
+ const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
+ if ( ! predicate || predicate( base_r ) )
return true;
- return false; // no skip as there may be more occurrences in this solvable of this attr.
+ return false; // no skip as there may be more occurrences od this attr.
}
// Here: search all attributes ;(
for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
{
const AttrMatchData & matchData( *mi );
-
- if ( matchData.kindPredicate )
- {
- if ( matchData.kindPredicate != inSolvable.kind() )
- continue; // this matchData does not apply
- }
- else if ( !globalKindOk )
- continue; // only matching kindPredicate could overwrite this
-
sat::LookupAttr q( matchData.attr, inSolvable );
if ( matchData.strMatcher ) // an empty searchstring matches always
q.setStrMatcher( matchData.strMatcher );