23 #define WFN_ATTRIBUTES {\
30 Attribute::language, \
31 Attribute::sw_edition,\
32 Attribute::target_sw, \
33 Attribute::target_hw, \
44 inline int heDecodeCh(
char ch )
46 if (
'0' <= ch && ch <=
'9' )
48 if (
'A' <= ch && ch <=
'F' )
49 return( ch -
'A' + 10 );
50 if (
'a' <= ch && ch <=
'f' )
51 return( ch -
'a' + 10 );
56 inline bool chIsValidRange(
char ch )
57 {
return(
'!' <= ch && ch <=
'~' ); }
60 inline bool chIsAlpha(
char ch )
61 {
return( (
'a' <= ch && ch <=
'z' ) || (
'A' <= ch && ch <=
'Z' ) ); }
64 inline bool chIsNum(
char ch )
65 {
return(
'0' <= ch && ch <=
'9' ); }
68 inline bool chIsAlNum(
char ch )
69 {
return( chIsAlpha( ch ) || chIsNum( ch ) ); }
72 inline bool chIsWfnUnescaped(
char ch )
73 {
return( chIsAlNum( ch ) || ch ==
'_' ); }
86 typedef std::array<Value,Attribute::numAttributes>
Wfn;
91 Impl(
const std::string & cpe_r )
96 explicit operator bool()
const
97 {
for (
const auto & val :
_wfn )
if ( ! val.isANY() )
return true;
return false; }
105 ret <<
':' <<
_wfn[ai].asFs();
118 val =
_wfn[ai].asUri();
120 if ( ai == Attribute::edition )
122 if ( ! (
_wfn[Attribute::sw_edition].isANY()
123 &&
_wfn[Attribute::target_sw].isANY()
124 &&
_wfn[Attribute::target_hw].isANY()
125 &&
_wfn[Attribute::other].isANY() ) )
130 <<
'~' <<
_wfn[Attribute::sw_edition].asUri()
131 <<
'~' <<
_wfn[Attribute::target_sw].asUri()
132 <<
'~' <<
_wfn[Attribute::target_hw].asUri()
133 <<
'~' <<
_wfn[Attribute::other].asUri();
140 ret << std::string( colon,
':' );
147 if ( ai == Attribute::language )
162 if ( ai ) ret <<
',';
165 ret <<
'"' << val <<
'"';
176 SetCompare ret = SetCompare::equal;
181 case SetCompare::uncomparable:
182 ret = SetCompare::uncomparable;
185 case SetCompare::equal:
188 case SetCompare::properSubset:
189 if ( ret == SetCompare::equal )
190 ret = SetCompare::properSubset;
191 else if ( ret != SetCompare::properSubset )
192 ret = SetCompare::uncomparable;
195 case SetCompare::properSuperset:
196 if ( ret == SetCompare::equal )
197 ret = SetCompare::properSuperset;
198 else if ( ret != SetCompare::properSuperset )
199 ret = SetCompare::uncomparable;
202 case SetCompare::disjoint:
203 ret = SetCompare::disjoint;
206 if ( ret == SetCompare::uncomparable || ret == SetCompare::disjoint )
220 switch ( attr_r.asEnum() )
222 case Attribute::part:
224 const std::string & wfn( val_r.
asWfn() );
230 if ( wfn[1] ==
'\0' )
234 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:part: '" << wfn <<
"' illegal value; expected: 'h' | 'o' | 'a'" );
240 case Attribute::language:
242 const std::string & wfn( val_r.
asWfn() );
245 if ( chIsAlpha( wfn[0] ) && chIsAlpha( wfn[1] ) )
247 len = chIsAlpha( wfn[2] ) ? 3 : 2;
248 if ( wfn[len] ==
'-' )
250 if ( chIsAlpha( wfn[len+1] ) && chIsAlpha( wfn[len+2] ) )
252 else if ( chIsNum( wfn[len+1] ) && chIsNum( wfn[len+2] ) && chIsNum( wfn[len+3] ) )
256 if ( wfn.size() != len )
257 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:language: '" << wfn <<
"' illegal value; expected RFC5646 conform: language ['-' region]" );
266 wfn_r[attr_r.asIntegral()] = val_r;
273 static Wfn unbind(
const std::string & cpe_r );
297 if ( cpe_r[4] ==
'/' )
301 else if ( cpe_r[4] ==
'2'
309 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
311 else if ( cpe_r[0] !=
'\0' )
312 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
320 static constexpr
unsigned numUriAttr = 7u;
321 std::vector<std::string> field;
322 field.reserve( Attribute::numAttributes );
323 if (
str::splitFields( cpe_r.c_str()+5, std::back_inserter(field),
":" ) > numUriAttr )
324 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: too many fields (" << field.size() <<
"); expected " << numUriAttr );
325 field.resize( Attribute::numAttributes );
329 if ( ai == Attribute::edition && field[ai][0] ==
'~' )
332 static constexpr
unsigned numPacks = 6u;
333 std::vector<std::string> pack;
334 pack.reserve( numPacks );
335 if (
str::splitFields( field[ai], std::back_inserter(pack),
"~" ) > numPacks )
336 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri:edition: too many packs (" << pack.size() <<
"); expected " << numPacks );
337 pack.resize( numPacks );
339 pack[1].swap( field[Attribute::edition] );
340 pack[2].swap( field[Attribute::sw_edition] );
341 pack[3].swap( field[Attribute::target_sw] );
342 pack[4].swap( field[Attribute::target_hw] );
343 pack[5].swap( field[Attribute::other] );
354 std::vector<std::string> field;
355 field.reserve( Attribute::numAttributes );
356 if (
str::splitFields( cpe_r.c_str()+8, std::back_inserter(field),
":" ) > Attribute::numAttributes )
357 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: too many fields (" << field.size() <<
"); expected 11" );
358 if ( !field.empty() && field.back().empty() )
360 field.resize( Attribute::numAttributes,
"*" );
381 : _pimpl( new
Impl( cpe_r ) )
402 {
return bool(*_pimpl); }
422 static std::map<Enum,std::string>
_table = {
423 #define OUTS(N) { N, #N }
452 if ( value_r.empty() )
455 _value.reset(
new std::string );
459 else if ( value_r !=
"*" )
461 bool starting =
true;
462 for_( chp, value_r.begin(), value_r.end() )
468 if ( ! chIsValidRange( *chp ) )
471 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal quoted character '\\" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
473 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
475 else if ( chIsWfnUnescaped( *chp ) )
476 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: unnecessarily quoted character '\\" << *chp <<
"'" );
477 else if ( starting && *chp ==
'-' && chp+1 == value_r.end() )
478 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: '\\-' is illegal value" );
482 while ( *(chp+1) ==
'?' )
484 if ( ! ( starting || chp+1 == value_r.end() ) )
485 throw std::invalid_argument(
"CpeId:Wfn: embedded ?" );
489 if ( ! ( starting || chp+1 == value_r.end() ) )
490 throw std::invalid_argument(
"CpeId:Wfn: embedded *" );
494 if ( ! chIsWfnUnescaped( *chp ) )
496 if ( chIsValidRange( *chp ) )
497 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: missing quote before '" << *chp <<
"'" );
499 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal character '" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
506 _value.reset(
new std::string( value_r ) );
512 if ( encoded_r !=
"*" )
514 if ( encoded_r ==
"-" )
521 bool starting =
true;
522 for_( chp, encoded_r.begin(), encoded_r.end() )
528 if ( chIsWfnUnescaped( *chp ) )
530 else if ( chIsValidRange( *chp ) )
531 result <<
'\\' << *chp;
533 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal quoted character '\\" << *chp <<
"'" );
535 throw std::invalid_argument(
"CpeId:Fs: Backslash escapes nothing" );
540 while ( *(chp+1) ==
'?' )
545 if ( ! ( starting || chp+1 == encoded_r.end() ) )
546 throw std::invalid_argument(
"CpeId:Fs: embedded ?" );
550 if ( starting || chp+1 == encoded_r.end() )
553 throw std::invalid_argument(
"CpeId:Fs: embedded *" );
557 if ( chIsWfnUnescaped( *chp ) )
559 else if ( chIsValidRange( *chp ) )
560 result <<
'\\' << *chp;
562 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal character '" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
569 throw std::invalid_argument(
"CpeId:Fs: '' value is illegal" );
570 _value.reset(
new std::string( result ) );
577 if ( ! encoded_r.empty() )
579 if ( encoded_r ==
"-" )
586 bool starting =
true;
587 for_( chp, encoded_r.begin(), encoded_r.end() )
593 int d1 = heDecodeCh( *(chp+1) );
596 int d2 = heDecodeCh( *(chp+2) );
605 while ( *(chp+1) ==
'%' && *(chp+2) ==
'0' && *(chp+3) ==
'1' )
610 if ( starting || chp+1 == encoded_r.end() )
616 throw std::invalid_argument(
"CpeId:Uri: embedded %01" );
620 if ( starting || chp+1 == encoded_r.end() )
627 throw std::invalid_argument(
"CpeId:Uri: embedded %02" );
631 if ( ! chIsValidRange( ch ) )
632 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal % encoded character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
636 else if ( ! chIsValidRange( ch ) )
637 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
639 if ( chIsWfnUnescaped( ch ) )
642 result <<
'\\' << ch;
647 _value.reset(
new std::string( result ) );
657 static const std::string any(
"*" );
670 static const std::string asterisk(
"*" );
675 static const std::string dash(
"-" );
697 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
701 result <<
'\\' << *chp;
718 static const std::string dash(
"-" );
726 if ( chIsWfnUnescaped( *chp ) )
732 static const char *
const hdig =
"0123456789abcdef";
745 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
749 result <<
'%' << hdig[(
unsigned char)(*chp)/16] << hdig[(
unsigned char)(*chp)%16];
763 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal char '" << *chp <<
"' in WFN" );
778 inline bool isWildchar(
char ch_r )
779 {
return( ch_r ==
'*' || ch_r ==
'?' ); }
784 inline bool evenNumberOfBackslashes( std::string::const_reverse_iterator rbegin_r, std::string::const_reverse_iterator rend_r )
786 unsigned backslashes = 0;
787 for_( it, rbegin_r, rend_r )
794 return !(backslashes & 1U);
801 for_( it, begin_r, end_r )
804 if ( str_r[it] ==
'\\' )
814 inline bool matchWildcardfreeString(
const std::string & lhs,
const std::string & rhs )
843 inline bool matchWildcardedString( std::string src, std::string trg )
848 switch ( *src.begin() )
851 if ( src.size() == 1 )
854 prefx = std::string::npos;
859 for_( it, ++src.begin(), src.end() )
860 {
if ( *it ==
'?' ) ++prefx;
else break; }
861 if ( src.size() == prefx )
862 return( trg.size() <= prefx );
864 src.erase( 0, prefx );
873 switch ( *src.rbegin() )
876 if ( evenNumberOfBackslashes( ++src.rbegin(), src.rend() ) )
878 suffx = std::string::npos;
879 src.erase( src.size()-1 );
884 for_( it, ++src.rbegin(), src.rend() )
885 {
if ( *it ==
'?' ) ++suffx;
else break; }
886 if ( ! evenNumberOfBackslashes( src.rbegin()+suffx, src.rend() ) )
888 src.erase( src.size()-suffx );
898 match != std::string::npos;
899 match = trg.find( src, match+1 ) )
901 if ( prefx != std::string::npos && trueCharsIn( trg, 0, match ) > prefx )
904 if ( suffx != std::string::npos && trueCharsIn( trg, frontSize, trg.size() ) > suffx )
915 const std::string & value( *
_value );
916 return ( isWildchar( *value.begin() )
917 || ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
934 #define WFN_STRICT_SPEC 0
938 static const SetCompare kNeedsCloserLook( SetCompare::Enum(-1) );
939 static const SetCompare matchTabel[4][4] = {{
941 SetCompare::properSuperset,
942 SetCompare::properSuperset,
943 SetCompare::uncomparable,
945 SetCompare::properSubset,
947 SetCompare::disjoint,
948 SetCompare::uncomparable,
950 SetCompare::properSubset,
951 SetCompare::disjoint,
953 SetCompare::uncomparable,
955 SetCompare::properSubset,
956 SetCompare::disjoint,
958 SetCompare::uncomparable,
961 Type srcType = type();
962 Type trgType = trg.type();
963 SetCompare ret = matchTabel[srcType.asIntegral()][trgType.asIntegral()];
964 if ( ret == kNeedsCloserLook )
966 if ( srcType == Type::wildcardfree )
969 ret = matchWildcardfreeString( *
_value, *trg._value ) ? SetCompare::equal : SetCompare::disjoint;
971 else if ( srcType == Type::wildcarded )
974 ret = matchWildcardedString( *
_value, *trg._value ) ? SetCompare::properSuperset : SetCompare::disjoint;
1004 SetCompare ret = SetCompare::disjoint;
1008 ret = trg.
isANY() ? SetCompare::equal : SetCompare::properSuperset;
1010 else if ( trg.
isANY() )
1012 ret = SetCompare::properSubset;
1016 if ( trg.
isNA() ) ret = SetCompare::equal;
1018 else if ( ! trg.
isNA() )
1021 if ( isWildcarded() )
1026 ret = matchWildcardfreeString( *
_value, *trg.
_value ) ? SetCompare::equal : SetCompare::uncomparable;
1031 if ( matchWildcardedString( *
_value, *trg.
_value ) ) ret = SetCompare::properSuperset;
1039 if ( matchWildcardedString( *trg.
_value, *
_value ) ) ret = SetCompare::properSubset;
1044 if ( matchWildcardfreeString( *
_value, *trg.
_value ) ) ret = SetCompare::equal;
1050 #endif // WFN_STRICT_SPEC