1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/CpeId.cc
13 #include "zypp/base/String.h"
14 #include "zypp/base/LogTools.h"
15 #include "zypp/base/NonCopyable.h"
17 #include "zypp/CpeId.h"
21 /** Initializer list with all wfn attributes */
22 #define WFN_ATTRIBUTES {\
29 Attribute::language, \
30 Attribute::sw_edition,\
31 Attribute::target_sw, \
32 Attribute::target_hw, \
36 ///////////////////////////////////////////////////////////////////
39 ///////////////////////////////////////////////////////////////////
42 /** Hex-digit to number or -1. */
43 inline int heDecodeCh( char ch )
45 if ( '0' <= ch && ch <= '9' )
47 if ( 'A' <= ch && ch <= 'F' )
48 return( ch - 'A' + 10 );
49 if ( 'a' <= ch && ch <= 'f' )
50 return( ch - 'a' + 10 );
54 /** Printable non whitespace in [0x00,0x7f] valid in WFN */
55 inline bool chIsValidRange( char ch )
56 { return( '!' <= ch && ch <= '~' ); }
59 inline bool chIsAlpha( char ch )
60 { return( ( 'a' <= ch && ch <= 'z' ) || ( 'A' <= ch && ch <= 'Z' ) ); }
63 inline bool chIsNum( char ch )
64 { return( '0' <= ch && ch <= '9' ); }
67 inline bool chIsAlNum( char ch )
68 { return( chIsAlpha( ch ) || chIsNum( ch ) ); }
70 /** Alphanum or \c underscore are unescaped in WFN */
71 inline bool chIsWfnUnescaped( char ch )
72 { return( chIsAlNum( ch ) || ch == '_' ); }
75 ///////////////////////////////////////////////////////////////////
77 ///////////////////////////////////////////////////////////////////
78 /// \class CpeId::Impl
79 /// \brief CpeId implementation.
80 ///////////////////////////////////////////////////////////////////
81 class CpeId::Impl : private base::NonCopyable
83 typedef std::array<Value,Attribute::numAttributes> Wfn;
88 Impl( const std::string & cpe_r )
89 : _wfn( unbind( cpe_r ) )
93 explicit operator bool() const
94 { for ( const auto & val : _wfn ) if ( ! val.isANY() ) return true; return false; }
96 std::string asFs() const
100 for ( auto ai : WFN_ATTRIBUTES )
102 ret << ':' << _wfn[ai].asFs();
107 std::string asUri() const
112 unsigned colon = 0; // to remember trailing colons
113 for ( auto ai : WFN_ATTRIBUTES )
115 val = _wfn[ai].asUri();
117 if ( ai == Attribute::edition )
119 if ( ! ( _wfn[Attribute::sw_edition].isANY()
120 && _wfn[Attribute::target_sw].isANY()
121 && _wfn[Attribute::target_hw].isANY()
122 && _wfn[Attribute::other].isANY() ) )
126 << '~' << val//Attribute::edition
127 << '~' << _wfn[Attribute::sw_edition].asUri()
128 << '~' << _wfn[Attribute::target_sw].asUri()
129 << '~' << _wfn[Attribute::target_hw].asUri()
130 << '~' << _wfn[Attribute::other].asUri();
137 ret << std::string( colon, ':' );
144 if ( ai == Attribute::language )
145 break; // remaining attrs packaed in edition
150 std::string asWfn() const
154 for ( auto ai : WFN_ATTRIBUTES )
156 const Value & val( _wfn[ai] );
159 if ( ai ) ret << ',';
160 ret << Attribute::asString( ai ) << '=';
161 if ( val.isString() )
162 ret << '"' << val << '"';
164 ret << "NA"; // as ANY is omitted, it must be NA
171 SetCompare setRelationMixinCompare( const Impl & trg ) const
173 SetCompare ret = SetCompare::equal;
174 for ( auto ai : WFN_ATTRIBUTES )
176 switch ( _wfn[ai].compare( trg._wfn[ai] ).asEnum() )
178 case SetCompare::uncomparable:
179 ret = SetCompare::uncomparable;
182 case SetCompare::equal:
185 case SetCompare::properSubset:
186 if ( ret == SetCompare::equal )
187 ret = SetCompare::properSubset;
188 else if ( ret != SetCompare::properSubset )
189 ret = SetCompare::uncomparable;
192 case SetCompare::properSuperset:
193 if ( ret == SetCompare::equal )
194 ret = SetCompare::properSuperset;
195 else if ( ret != SetCompare::properSuperset )
196 ret = SetCompare::uncomparable;
199 case SetCompare::disjoint:
200 ret = SetCompare::disjoint;
203 if ( ret == SetCompare::uncomparable || ret == SetCompare::disjoint )
210 /** Assign \a val_r if it meets \a attr_r specific contraints.
211 * \throws std::invalid_argument if string is malformed
213 static void assignAttr( Wfn & wfn_r, Attribute attr_r, const Value & val_r )
215 if ( val_r.isString() )
217 switch ( attr_r.asEnum() )
219 case Attribute::part:
221 const std::string & wfn( val_r.asWfn() );
227 if ( wfn[1] == '\0' )
231 throw std::invalid_argument( str::Str() << "CpeId:Wfn:part: '" << wfn << "' illegal value; expected: 'h' | 'o' | 'a'" );
237 case Attribute::language:
239 const std::string & wfn( val_r.asWfn() );
240 std::string::size_type len = 0;
241 // (2*3ALPHA) ["-" (2ALPHA / 3DIGIT)]
242 if ( chIsAlpha( wfn[0] ) && chIsAlpha( wfn[1] ) )
244 len = chIsAlpha( wfn[2] ) ? 3 : 2;
245 if ( wfn[len] == '-' )
247 if ( chIsAlpha( wfn[len+1] ) && chIsAlpha( wfn[len+2] ) )
249 else if ( chIsNum( wfn[len+1] ) && chIsNum( wfn[len+2] ) && chIsNum( wfn[len+3] ) )
253 if ( wfn.size() != len )
254 throw std::invalid_argument( str::Str() << "CpeId:Wfn:language: '" << wfn << "' illegal value; expected RFC5646 conform: language ['-' region]" );
263 wfn_r[attr_r.asIntegral()] = val_r;
267 /** Parse magic and unbind accordingly
268 * \throws std::invalid_argument if string is malformed
270 static Wfn unbind( const std::string & cpe_r );
272 /** Parse Uri and unbind
273 * \throws std::invalid_argument if string is malformed
275 static Wfn unbindUri( const std::string & cpe_r );
277 /** Parse Fs and unbind
278 * \throws std::invalid_argument if string is malformed
280 static Wfn unbindFs( const std::string & cpe_r );
286 CpeId::Impl::Wfn CpeId::Impl::unbind( const std::string & cpe_r )
294 if ( cpe_r[4] == '/' )
296 ret = unbindUri( cpe_r );
298 else if ( cpe_r[4] == '2'
303 ret = unbindFs( cpe_r );
306 throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
308 else if ( cpe_r[0] != '\0' )
309 throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
313 CpeId::Impl::Wfn CpeId::Impl::unbindUri( const std::string & cpe_r )
317 static constexpr unsigned numUriAttr = 7u; // basic URI attibutes
318 std::vector<std::string> field;
319 field.reserve( Attribute::numAttributes ); // reserve 7 + 4 for packed extened attrs in edition
320 if ( str::splitFields( cpe_r.c_str()+5/* skip magic 'cpe:/' */, std::back_inserter(field), ":" ) > numUriAttr )
321 throw std::invalid_argument( str::Str() << "CpeId:Uri: too many fields (" << field.size() << "); expected " << numUriAttr );
322 field.resize( Attribute::numAttributes ); // fillup with ANY(""),
324 for ( auto ai : WFN_ATTRIBUTES )
326 if ( ai == Attribute::edition && field[ai][0] == '~' )
328 // unpacking is needed
329 static constexpr unsigned numPacks = 6u; // dummy_before_~ + edition + 4 extended attributes
330 std::vector<std::string> pack;
331 pack.reserve( numPacks );
332 if ( str::splitFields( field[ai], std::back_inserter(pack), "~" ) > numPacks )
333 throw std::invalid_argument( str::Str() << "CpeId:Uri:edition: too many packs (" << pack.size() << "); expected " << numPacks );
334 pack.resize( numPacks ); // fillup with ANY(""), should be noOP
336 pack[1].swap( field[Attribute::edition] );
337 pack[2].swap( field[Attribute::sw_edition] );
338 pack[3].swap( field[Attribute::target_sw] );
339 pack[4].swap( field[Attribute::target_hw] );
340 pack[5].swap( field[Attribute::other] );
342 assignAttr( ret, ai, Value( field[ai], Value::uriFormat ) );
347 CpeId::Impl::Wfn CpeId::Impl::unbindFs( const std::string & cpe_r )
351 std::vector<std::string> field;
352 field.reserve( Attribute::numAttributes );
353 if ( str::splitFields( cpe_r.c_str()+8/* skip magic 'cpe:2.3:' */, std::back_inserter(field), ":" ) > Attribute::numAttributes )
354 throw std::invalid_argument( str::Str() << "CpeId:Fs: too many fields (" << field.size() << "); expected 11" /*<< Attribute::numAttributes but g++ currently can't resoolve this as constexpr*/ );
355 if ( field.back().empty() ) // A trailing ':' leads to an empty (illegal) field, but we fillup missing fields with ANY|"*"
357 field.resize( Attribute::numAttributes, "*" ); // fillup with ANY|"*"
359 for ( auto ai : WFN_ATTRIBUTES )
361 assignAttr( ret, ai, Value( field[ai], Value::fsFormat ) );
367 ///////////////////////////////////////////////////////////////////
369 ///////////////////////////////////////////////////////////////////
371 std::string CpeId::NoThrowType::lastMalformed;
377 CpeId::CpeId( const std::string & cpe_r )
378 : _pimpl( new Impl( cpe_r ) )
381 CpeId::CpeId( const std::string & cpe_r, NoThrowType )
385 _pimpl.reset( new Impl( cpe_r ) );
386 NoThrowType::lastMalformed.clear();
390 _pimpl.reset( new Impl );
391 NoThrowType::lastMalformed = cpe_r;
398 CpeId::operator bool() const
399 { return bool(*_pimpl); }
401 std::string CpeId::asFs() const
402 { return _pimpl->asFs(); }
404 std::string CpeId::asUri() const
405 { return _pimpl->asUri(); }
407 std::string CpeId::asWfn() const
408 { return _pimpl->asWfn(); }
410 SetCompare CpeId::setRelationMixinCompare( const CpeId & trg ) const
411 { return _pimpl->setRelationMixinCompare( *trg._pimpl ); }
413 ///////////////////////////////////////////////////////////////////
414 // class CpeId::WfnAttribute
415 ///////////////////////////////////////////////////////////////////
417 const std::string & CpeId::_AttributeDef::asString( Enum val_r )
419 static std::map<Enum,std::string> _table = {
420 #define OUTS(N) { N, #N }
434 return _table[val_r];
437 ///////////////////////////////////////////////////////////////////
438 // class CpeId::Value
439 ///////////////////////////////////////////////////////////////////
441 const CpeId::Value CpeId::Value::ANY;
442 const CpeId::Value CpeId::Value::NA( "" );
444 CpeId::Value::Value( const std::string & value_r )
446 if ( value_r.empty() ) // NA
448 if ( ! CpeId::Value::NA._value ) // initialized by this ctor!
449 _value.reset( new std::string );
451 _value = CpeId::Value::NA._value;
453 else if ( value_r != "*" ) // ANY is default constructed
455 bool starting = true; // false after the 1st non-?
456 for_( chp, value_r.begin(), value_r.end() )
462 if ( ! chIsValidRange( *chp ) )
465 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal quoted character '\\" << reinterpret_cast<void*>(*chp) << "'" );
467 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
469 else if ( chIsWfnUnescaped( *chp ) )
470 throw std::invalid_argument( str::Str() << "CpeId:Wfn: unnecessarily quoted character '\\" << *chp << "'" );
471 else if ( starting && *chp == '-' && chp+1 == value_r.end() )
472 throw std::invalid_argument( str::Str() << "CpeId:Wfn: '\\-' is illegal value" );
475 case '?': // sequence at beginning or end of string
476 while ( *(chp+1) == '?' )
478 if ( ! ( starting || chp+1 == value_r.end() ) )
479 throw std::invalid_argument( "CpeId:Wfn: embedded ?" );
482 case '*': // single at beginning or end of string
483 if ( ! ( starting || chp+1 == value_r.end() ) )
484 throw std::invalid_argument( "CpeId:Wfn: embedded *" );
487 default: // everything else unquoted
488 if ( ! chIsWfnUnescaped( *chp ) )
490 if ( chIsValidRange( *chp ) )
491 throw std::invalid_argument( str::Str() << "CpeId:Wfn: missing quote before '" << *chp << "'" );
493 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
500 _value.reset( new std::string( value_r ) );
504 CpeId::Value::Value( const std::string & encoded_r, FsFormatType )
506 if ( encoded_r != "*" ) // ANY is default constructed
508 if ( encoded_r == "-" ) // NA
510 _value = CpeId::Value::NA._value;
515 bool starting = true; // false after the 1st non-?
516 for_( chp, encoded_r.begin(), encoded_r.end() )
520 case '\\': // may stay quoted
522 if ( chIsWfnUnescaped( *chp ) )
524 else if ( chIsValidRange( *chp ) )
525 result << '\\' << *chp;
527 throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal quoted character '\\" << *chp << "'" );
529 throw std::invalid_argument( "CpeId:Fs: Backslash escapes nothing" );
532 case '?': // sequence at beginning or end of string
534 while ( *(chp+1) == '?' )
539 if ( ! ( starting || chp+1 == encoded_r.end() ) )
540 throw std::invalid_argument( "CpeId:Fs: embedded ?" );
543 case '*': // single at beginning or end of string
544 if ( starting || chp+1 == encoded_r.end() )
547 throw std::invalid_argument( "CpeId:Fs: embedded *" );
551 if ( chIsWfnUnescaped( *chp ) )
553 else if ( chIsValidRange( *chp ) )
554 result << '\\' << *chp;
556 throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
563 throw std::invalid_argument( "CpeId:Fs: '' value is illegal" );
564 _value.reset( new std::string( result ) );
569 CpeId::Value::Value( const std::string & encoded_r, UriFormatType )
571 if ( ! encoded_r.empty() ) // ANY is default constructed
573 if ( encoded_r == "-" ) // NA
575 _value = CpeId::Value::NA._value;
580 bool starting = true; // false after the 1st non-? (%01)
581 for_( chp, encoded_r.begin(), encoded_r.end() )
585 if ( ch == '%' ) // legal '%xx' sequence first
587 int d1 = heDecodeCh( *(chp+1) );
590 int d2 = heDecodeCh( *(chp+2) );
593 chp += 2; // skip sequence
596 if ( d2 == 1 ) // %01 - ? valid sequence at begin or end
599 while ( *(chp+1) == '%' && *(chp+2) == '0' && *(chp+3) == '1' )
604 if ( starting || chp+1 == encoded_r.end() )
607 continue; // -> continue;
610 throw std::invalid_argument( "CpeId:Uri: embedded %01" );
612 else if ( d2 == 2 ) // %02 - * valid at begin or end
614 if ( starting || chp+1 == encoded_r.end() )
618 continue; // -> continue;
621 throw std::invalid_argument( "CpeId:Uri: embedded %02" );
625 if ( ! chIsValidRange( ch ) )
626 throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal % encoded character '" << reinterpret_cast<void*>(ch) << "'" );
630 else if ( ! chIsValidRange( ch ) )
631 throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal character '" << reinterpret_cast<void*>(ch) << "'" );
633 if ( chIsWfnUnescaped( ch ) )
636 result << '\\' << ch;
641 _value.reset( new std::string( result ) );
646 std::string CpeId::Value::asWfn() const
651 static const std::string any( "*" );
655 ret = *_value; // includes "" for NA
659 std::string CpeId::Value::asFs() const
664 static const std::string asterisk( "*" );
669 static const std::string dash( "-" );
675 for_( chp, _value->begin(), _value->end() )
687 result << *chp; // without escaping
691 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
695 result << '\\' << *chp;
705 std::string CpeId::Value::asUri() const
707 std::string ret; // ANY
712 static const std::string dash( "-" );
718 for_( chp, _value->begin(), _value->end() )
720 if ( chIsWfnUnescaped( *chp ) )
726 static const char *const hdig = "0123456789abcdef";
735 result << *chp; // without encodeing
739 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
743 result << '%' << hdig[(unsigned char)(*chp)/16] << hdig[(unsigned char)(*chp)%16];
757 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal char '" << *chp << "' in WFN" );
768 ///////////////////////////////////////////////////////////////////
771 /** Whether it's a wildcard character (<tt>[*?]</tt>). */
772 inline bool isWildchar( char ch_r )
773 { return( ch_r == '*' || ch_r == '?' ); }
775 /** Whether there is an even number of consecutive backslashes before and including \a rbegin_r
776 * An even number of backslashes means the character following is unescaped.
778 inline bool evenNumberOfBackslashes( std::string::const_reverse_iterator rbegin_r, std::string::const_reverse_iterator rend_r )
780 unsigned backslashes = 0;
781 for_( it, rbegin_r, rend_r )
788 return !(backslashes & 1U);
791 /** Number of chars (not counting escaping backslashes) in <tt>[begin_r,end_r[</tt> */
792 inline unsigned trueCharsIn( const std::string & str_r, std::string::size_type begin_r, std::string::size_type end_r )
795 for_( it, begin_r, end_r )
798 if ( str_r[it] == '\\' )
807 /** Match helper comparing 2 Wildcardfree string values (case insensitive). */
808 inline bool matchWildcardfreeString( const std::string & lhs, const std::string & rhs )
809 { return( str::compareCI( lhs, rhs ) == 0 ); }
811 /** Match helper matching Wildcarded source against Wildcardfree target.
813 * Constraints on usage of the unquoted question mark (zero or one char in \a trg):
814 * 1. An unquoted question mark MAY be used at the beginning and/or the end of an
815 * attribute-value string.
816 * 2. A contiguous sequence of unquoted question marks MAY appear at the beginning
817 * and/or the end of an attribute-value string.
818 * 3. An unquoted question mark SHALL NOT be used in any other place in an
819 * attribute-value string.
821 * Constraints on usage of the unquoted asterisk (zero or more chars in \a trg):
822 * 1. A single unquoted asterisk MAY be used as the entire attribute-value string.
823 * 2. A single unquoted asterisk MAY be used at the beginning and/or end of an
824 * attribute-value string.
825 * 3. An unquoted asterisk SHALL NOT be used in any other place in an attribute-value
828 * Unquoted question marks and asterisks MAY appear in the same attribute-value string
829 * as long as they meet the constraints above.
831 * Example of illegal usage: "foo?bar", "bar??baz", "q??x",
832 * "foo*bar", "**foo", "bar***",
833 * "*?foobar", "foobar*?"
835 * \note Relies on \a src and \a trg being wellformed.
837 inline bool matchWildcardedString( std::string src, std::string trg )
839 // std::string::npos remembers an asterisk
840 // unescaped wildcard prefix
841 std::string::size_type prefx = 0;
842 switch ( *src.begin() ) // wellformed implies not empty
845 if ( src.size() == 1 )
846 return true; // "*" matches always: superset
848 prefx = std::string::npos;
853 for_( it, ++src.begin(), src.end() )
854 { if ( *it == '?' ) ++prefx; else break; }
855 if ( src.size() == prefx )
856 return( trg.size() <= prefx ); // "??..?": superset if at most #prefx chars
858 src.erase( 0, prefx );
863 // unescaped wildcard suffix
864 std::string::size_type suffx = 0;
867 switch ( *src.rbegin() )
870 if ( evenNumberOfBackslashes( ++src.rbegin(), src.rend() ) )
872 suffx = std::string::npos;
873 src.erase( src.size()-1 );
878 for_( it, ++src.rbegin(), src.rend() )
879 { if ( *it == '?' ) ++suffx; else break; }
880 if ( ! evenNumberOfBackslashes( src.rbegin()+suffx, src.rend() ) )
881 --suffx; // last '?' was escaped.
882 src.erase( src.size()-suffx );
888 // now match; find src in trg an check surrounding wildcards
889 src = str::toLower( src );
890 trg = str::toLower( trg );
891 for ( std::string::size_type match = trg.find( src, 0 );
892 match != std::string::npos;
893 match = trg.find( src, match+1 ) )
895 if ( prefx != std::string::npos && trueCharsIn( trg, 0, match ) > prefx )
896 break; // not "*", and already more chars than "?"s before match: disjoint
897 std::string::size_type frontSize = match + src.size();
898 if ( suffx != std::string::npos && trueCharsIn( trg, frontSize, trg.size() ) > suffx )
899 continue; // not "*", and still more chars than "?"s after match: check next match
900 return true; // match: superset
902 return false; // disjoint
905 ///////////////////////////////////////////////////////////////////
907 bool CpeId::Value::containsWildcard() const
909 const std::string & value( *_value );
910 return ( isWildchar( *value.begin() )
911 || ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
914 ///////////////////////////////////////////////////////////////////
915 /// Symmetric attribute compare if wildcards are involved!
916 /// The specs define any comarison with a wildcarded attribute as
917 /// target to return \c uncomparable:
919 /// wildcardfree <=> wildcarded ==> uncomparable,
920 /// wildcarded <=> wildcardfree ==> superset or disjoint
922 /// But a symmetric result is much more intuitive:
924 /// wildcardfree <=> wildcarded ==> subset or disjoint
925 /// wildcarded <=> wildcardfree ==> superset or disjoint
927 ///////////////////////////////////////////////////////////////////
928 #define WFN_STRICT_SPEC 0
930 //SetCompare CpeId::Value::setRelationMixinCompare( const CpeId::Value & trg ) const
932 static const SetCompare _NeedsCloserLook( SetCompare::Enum(-1) ); // artificial Compare value
933 static const SetCompare matchTabel[4][4] = {{
934 /* ANY, ANY */ SetCompare::equal,
935 /* ANY, NA */ SetCompare::properSuperset,
936 /* ANY, wildcardfree */ SetCompare::properSuperset,
937 /* ANY, wildcarded */ SetCompare::uncomparable,
939 /* NA, ANY */ SetCompare::properSubset,
940 /* NA, NA */ SetCompare::equal,
941 /* NA, wildcardfree */ SetCompare::disjoint,
942 /* NA, wildcarded */ SetCompare::uncomparable,
944 /* wildcardfree, ANY */ SetCompare::properSubset,
945 /* wildcardfree, NA */ SetCompare::disjoint,
946 /* wildcardfree, wildcardfree */ _NeedsCloserLook, // equal or disjoint
947 /* wildcardfree, wildcarded */ SetCompare::uncomparable,
949 /* wildcarded, ANY */ SetCompare::properSubset,
950 /* wildcarded, NA */ SetCompare::disjoint,
951 /* wildcarded, wildcardfree */ _NeedsCloserLook, // superset or disjoint
952 /* wildcarded, wildcarded */ SetCompare::uncomparable,
955 Type srcType = type();
956 Type trgType = trg.type();
957 SetCompare ret = matchTabel[srcType.asIntegral()][trgType.asIntegral()];
958 if ( ret == _NeedsCloserLook )
960 if ( srcType == Type::wildcardfree ) // trgType == Type::wildcardfree
962 // simple string compare
963 ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::disjoint;
965 else if ( srcType == Type::wildcarded ) // trgType == Type::wildcardfree
967 // Needs wildcard compare
968 ret = matchWildcardedString( *_value, *trg._value ) ? SetCompare::properSuperset : SetCompare::disjoint;
974 SetCompare CpeId::Value::setRelationMixinCompare( const CpeId::Value & trg ) const
976 ///////////////////////////////////////////////////////////////////
978 // ANY, NA => properSuperset
979 // ANY, wildcardfree => properSuperset
980 // ANY, wildcarded => properSuperset
982 // NA, ANY => properSubset
984 // NA, wildcardfree => disjoint
985 // NA, wildcarded => disjoint
987 // wildcardfree, ANY => properSubset
988 // wildcardfree, NA => disjoint
989 // wildcardfree, wildcardfree => NeedsCloserLook: equal or disjoint
990 // wildcardfree, wildcarded => NeedsCloserLook: subset or disjoint
992 // wildcarded, ANY => properSubset
993 // wildcarded, NA => disjoint
994 // wildcarded, wildcardfree => NeedsCloserLook: superset or disjoint
995 // wildcarded, wildcarded => NeedsCloserLook" equal or uncomparable
996 ///////////////////////////////////////////////////////////////////
998 SetCompare ret = SetCompare::disjoint;
1002 ret = trg.isANY() ? SetCompare::equal : SetCompare::properSuperset;
1004 else if ( trg.isANY() )
1006 ret = SetCompare::properSubset;
1010 if ( trg.isNA() ) ret = SetCompare::equal; // else: SetCompare::disjoint;
1012 else if ( ! trg.isNA() ) // else: SetCompare::disjoint;
1015 if ( isWildcarded() )
1017 if ( trg.isWildcarded() )
1019 // simple string compare just to detect 'equal'
1020 ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::uncomparable;
1024 // Needs wildcard compare (src,trg)
1025 if ( matchWildcardedString( *_value, *trg._value ) ) ret = SetCompare::properSuperset; // else: SetCompare::disjoint;
1030 if ( trg.isWildcarded() )
1032 // Needs wildcard compare (trg,src)
1033 if ( matchWildcardedString( *trg._value, *_value ) ) ret = SetCompare::properSubset; // else: SetCompare::disjoint;
1037 // simple string compare
1038 if ( matchWildcardfreeString( *_value, *trg._value ) ) ret = SetCompare::equal; // else: SetCompare::disjoint;
1044 #endif // WFN_STRICT_SPEC
1046 std::ostream & operator<<( std::ostream & str, const CpeId::Value & obj )
1047 { return str << obj.asString(); }
1050 ///////////////////////////////////////////////////////////////////