libzypp 17.31.23
CpeId.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
11#include <iostream>
12#include <array>
13
14#include <zypp/base/String.h>
15#include <zypp/base/LogTools.h>
16#include <zypp/base/NonCopyable.h>
17
18#include <zypp/CpeId.h>
19
20using std::endl;
21
23#define WFN_ATTRIBUTES {\
24 Attribute::part, \
25 Attribute::vendor, \
26 Attribute::product, \
27 Attribute::version, \
28 Attribute::update, \
29 Attribute::edition, \
30 Attribute::language, \
31 Attribute::sw_edition,\
32 Attribute::target_sw, \
33 Attribute::target_hw, \
34 Attribute::other, \
35}
36
38namespace zypp
39{
41 namespace
42 {
44 inline int heDecodeCh( char ch )
45 {
46 if ( '0' <= ch && ch <= '9' )
47 return( ch - '0' );
48 if ( 'A' <= ch && ch <= 'F' )
49 return( ch - 'A' + 10 );
50 if ( 'a' <= ch && ch <= 'f' )
51 return( ch - 'a' + 10 );
52 return -1;
53 }
54
56 inline bool chIsValidRange( char ch )
57 { return( '!' <= ch && ch <= '~' ); }
58
60 inline bool chIsAlpha( char ch )
61 { return( ( 'a' <= ch && ch <= 'z' ) || ( 'A' <= ch && ch <= 'Z' ) ); }
62
64 inline bool chIsNum( char ch )
65 { return( '0' <= ch && ch <= '9' ); }
66
68 inline bool chIsAlNum( char ch )
69 { return( chIsAlpha( ch ) || chIsNum( ch ) ); }
70
72 inline bool chIsWfnUnescaped( char ch )
73 { return( chIsAlNum( ch ) || ch == '_' ); }
74
75 } // namespace
77
78 constexpr CpeId::NoThrowType CpeId::noThrow;
79
85 {
86 typedef std::array<Value,Attribute::numAttributes> Wfn;
87
88 public:
89 Impl() {}
90
91 Impl( const std::string & cpe_r )
92 : _wfn( unbind( cpe_r ) )
93 {}
94
95 public:
96 explicit operator bool() const
97 { for ( const auto & val : _wfn ) if ( ! val.isANY() ) return true; return false; }
98
99 std::string asFs() const
100 {
101 str::Str ret;
102 ret << "cpe:2.3";
103 for ( auto ai : WFN_ATTRIBUTES )
104 {
105 ret << ':' << _wfn[ai].asFs();
106 }
107 return ret;
108 }
109
110 std::string asUri() const
111 {
112 str::Str ret;
113 ret << "cpe:/";
114 std::string val;
115 unsigned colon = 0; // to remember trailing colons
116 for ( auto ai : WFN_ATTRIBUTES )
117 {
118 val = _wfn[ai].asUri();
119
120 if ( ai == Attribute::edition )
121 {
122 if ( ! ( _wfn[Attribute::sw_edition].isANY()
123 && _wfn[Attribute::target_sw].isANY()
124 && _wfn[Attribute::target_hw].isANY()
125 && _wfn[Attribute::other].isANY() ) )
126 {
127 // packing is needed
128 val = str::Str()
129 << '~' << val//Attribute::edition
130 << '~' << _wfn[Attribute::sw_edition].asUri()
131 << '~' << _wfn[Attribute::target_sw].asUri()
132 << '~' << _wfn[Attribute::target_hw].asUri()
133 << '~' << _wfn[Attribute::other].asUri();
134 }
135 }
136
137 if ( ! val.empty() )
138 {
139 if ( colon )
140 ret << std::string( colon, ':' );
141 ret << val;
142 colon = 1;
143 }
144 else
145 ++colon;
146
147 if ( ai == Attribute::language )
148 break; // remaining attrs packaed in edition
149 }
150 return ret;
151 }
152
153 std::string asWfn() const
154 {
155 str::Str ret;
156 ret << "wfn:[";
157 for ( auto ai : WFN_ATTRIBUTES )
158 {
159 const Value & val( _wfn[ai] );
160 if ( ! val.isANY() )
161 {
162 if ( ai ) ret << ',';
163 ret << Attribute::asString( ai ) << '=';
164 if ( val.isString() )
165 ret << '"' << val << '"';
166 else
167 ret << "NA"; // as ANY is omitted, it must be NA
168 }
169 }
170 return ret << "]";
171 }
172
173 public:
174 SetCompare setRelationMixinCompare( const Impl & trg ) const
175 {
176 SetCompare ret = SetCompare::equal;
177 for ( auto ai : WFN_ATTRIBUTES )
178 {
179 switch ( _wfn[ai].compare( trg._wfn[ai] ).asEnum() )
180 {
181 case SetCompare::uncomparable:
182 ret = SetCompare::uncomparable;
183 break;
184
185 case SetCompare::equal:
186 break;
187
188 case SetCompare::properSubset:
189 if ( ret == SetCompare::equal )
190 ret = SetCompare::properSubset;
191 else if ( ret != SetCompare::properSubset )
192 ret = SetCompare::uncomparable;
193 break;
194
195 case SetCompare::properSuperset:
196 if ( ret == SetCompare::equal )
197 ret = SetCompare::properSuperset;
198 else if ( ret != SetCompare::properSuperset )
199 ret = SetCompare::uncomparable;
200 break;
201
202 case SetCompare::disjoint:
203 ret = SetCompare::disjoint;
204 break;
205 }
206 if ( ret == SetCompare::uncomparable || ret == SetCompare::disjoint )
207 break;
208 }
209 return ret;
210 }
211
212 private:
216 static void assignAttr( Wfn & wfn_r, Attribute attr_r, const Value & val_r )
217 {
218 if ( val_r.isString() )
219 {
220 switch ( attr_r.asEnum() )
221 {
222 case Attribute::part:
223 {
224 const std::string & wfn( val_r.asWfn() );
225 switch ( wfn[0] )
226 {
227 case 'h':
228 case 'o':
229 case 'a':
230 if ( wfn[1] == '\0' )
231 break;
232 // else: fallthrough
233 default:
234 throw std::invalid_argument( str::Str() << "CpeId:Wfn:part: '" << wfn << "' illegal value; expected: 'h' | 'o' | 'a'" );
235 break;
236 }
237 }
238 break;
239
240 case Attribute::language:
241 {
242 const std::string & wfn( val_r.asWfn() );
243 std::string::size_type len = 0;
244 // (2*3ALPHA) ["-" (2ALPHA / 3DIGIT)]
245 if ( chIsAlpha( wfn[0] ) && chIsAlpha( wfn[1] ) )
246 {
247 len = chIsAlpha( wfn[2] ) ? 3 : 2;
248 if ( wfn[len] == '-' )
249 {
250 if ( chIsAlpha( wfn[len+1] ) && chIsAlpha( wfn[len+2] ) )
251 len += 3;
252 else if ( chIsNum( wfn[len+1] ) && chIsNum( wfn[len+2] ) && chIsNum( wfn[len+3] ) )
253 len += 4;
254 }
255 }
256 if ( wfn.size() != len )
257 throw std::invalid_argument( str::Str() << "CpeId:Wfn:language: '" << wfn << "' illegal value; expected RFC5646 conform: language ['-' region]" );
258 }
259 break;
260
261 default:
262 // no contraints
263 break;
264 }
265 }
266 wfn_r[attr_r.asIntegral()] = val_r;
267 }
268
269 private:
273 static Wfn unbind( const std::string & cpe_r );
274
278 static Wfn unbindUri( const std::string & cpe_r );
279
283 static Wfn unbindFs( const std::string & cpe_r );
284
285 private:
287 };
288
289 CpeId::Impl::Wfn CpeId::Impl::unbind( const std::string & cpe_r )
290 {
291 Wfn ret;
292 if ( cpe_r[0] == 'c'
293 && cpe_r[1] == 'p'
294 && cpe_r[2] == 'e'
295 && cpe_r[3] == ':' )
296 {
297 if ( cpe_r[4] == '/' )
298 {
299 ret = unbindUri( cpe_r );
300 }
301 else if ( cpe_r[4] == '2'
302 && cpe_r[5] == '.'
303 && cpe_r[6] == '3'
304 && cpe_r[7] == ':' )
305 {
306 ret = unbindFs( cpe_r );
307 }
308 else
309 throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
310 }
311 else if ( cpe_r[0] != '\0' )
312 throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
313 return ret;
314 }
315
316 CpeId::Impl::Wfn CpeId::Impl::unbindUri( const std::string & cpe_r )
317 {
318 Wfn ret;
319
320 static constexpr unsigned numUriAttr = 7u; // basic URI attibutes
321 std::vector<std::string> field;
322 field.reserve( Attribute::numAttributes ); // reserve 7 + 4 for packed extened attrs in edition
323 if ( str::splitFields( cpe_r.c_str()+5/* skip magic 'cpe:/' */, 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 ); // fillup with ANY(""),
326
327 for ( auto ai : WFN_ATTRIBUTES )
328 {
329 if ( ai == Attribute::edition && field[ai][0] == '~' )
330 {
331 // unpacking is needed
332 static constexpr unsigned numPacks = 6u; // dummy_before_~ + edition + 4 extended attributes
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 ); // fillup with ANY(""), should be noOP
338
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] );
344 }
345 assignAttr( ret, ai, Value( field[ai], Value::uriFormat ) );
346 }
347 return ret;
348 }
349
350 CpeId::Impl::Wfn CpeId::Impl::unbindFs( const std::string & cpe_r )
351 {
352 Wfn ret;
353
354 std::vector<std::string> field;
355 field.reserve( Attribute::numAttributes );
356 if ( str::splitFields( cpe_r.c_str()+8/* skip magic 'cpe:2.3:' */, std::back_inserter(field), ":" ) > Attribute::numAttributes )
357 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*/ );
358 if ( !field.empty() && field.back().empty() ) // A trailing ':' leads to an empty (illegal) field, but we fillup missing fields with ANY|"*"
359 field.back() = "*";
360 field.resize( Attribute::numAttributes, "*" ); // fillup with ANY|"*"
361
362 for ( auto ai : WFN_ATTRIBUTES )
363 {
364 assignAttr( ret, ai, Value( field[ai], Value::fsFormat ) );
365 }
366 return ret;
367 }
368
369
371 // class CpeId
373
375
377 : _pimpl( new Impl )
378 {}
379
380 CpeId::CpeId( const std::string & cpe_r )
381 : _pimpl( new Impl( cpe_r ) )
382 {}
383
384 CpeId::CpeId( const std::string & cpe_r, NoThrowType )
385 {
386 try
387 {
388 _pimpl.reset( new Impl( cpe_r ) );
390 }
391 catch(...)
392 {
393 _pimpl.reset( new Impl );
395 }
396 }
397
399 {}
400
401 CpeId::operator bool() const
402 { return bool(*_pimpl); }
403
404 std::string CpeId::asFs() const
405 { return _pimpl->asFs(); }
406
407 std::string CpeId::asUri() const
408 { return _pimpl->asUri(); }
409
410 std::string CpeId::asWfn() const
411 { return _pimpl->asWfn(); }
412
413 SetCompare CpeId::setRelationMixinCompare( const CpeId & trg ) const
414 { return _pimpl->setRelationMixinCompare( *trg._pimpl ); }
415
417 // class CpeId::WfnAttribute
419
420 const std::string & CpeId::EAttributeDef::asString( Enum val_r )
421 {
422 static std::map<Enum,std::string> _table = {
423#define OUTS(N) { N, #N }
424 OUTS( part ),
425 OUTS( vendor ),
426 OUTS( product ),
427 OUTS( version ),
428 OUTS( update ),
429 OUTS( edition ),
430 OUTS( language ),
431 OUTS( sw_edition ),
432 OUTS( target_sw ),
433 OUTS( target_hw ),
434 OUTS( other ),
435#undef OUTS
436 };
437 return _table[val_r];
438 }
439
441 // class CpeId::Value
443
445 const CpeId::Value CpeId::Value::NA( "" );
446
449
450 CpeId::Value::Value( const std::string & value_r )
451 {
452 if ( value_r.empty() ) // NA
453 {
454 if ( ! CpeId::Value::NA._value ) // initialized by this ctor!
455 _value.reset( new std::string );
456 else
458 }
459 else if ( value_r != "*" ) // ANY is default constructed
460 {
461 bool starting = true; // false after the 1st non-?
462 for_( chp, value_r.begin(), value_r.end() )
463 {
464 switch ( *chp )
465 {
466 case '\\': // quoted
467 ++chp;
468 if ( ! chIsValidRange( *chp ) )
469 {
470 if ( *chp )
471 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal quoted character '\\" << reinterpret_cast<void*>(*chp) << "'" );
472 else
473 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
474 }
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" );
479 break;
480
481 case '?': // sequence at beginning or end of string
482 while ( *(chp+1) == '?' )
483 ++chp;
484 if ( ! ( starting || chp+1 == value_r.end() ) )
485 throw std::invalid_argument( "CpeId:Wfn: embedded ?" );
486 break;
487
488 case '*': // single at beginning or end of string
489 if ( ! ( starting || chp+1 == value_r.end() ) )
490 throw std::invalid_argument( "CpeId:Wfn: embedded *" );
491 break;
492
493 default: // everything else unquoted
494 if ( ! chIsWfnUnescaped( *chp ) )
495 {
496 if ( chIsValidRange( *chp ) )
497 throw std::invalid_argument( str::Str() << "CpeId:Wfn: missing quote before '" << *chp << "'" );
498 else
499 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
500 }
501 break;
502 }
503 if ( starting )
504 starting = false;
505 }
506 _value.reset( new std::string( value_r ) );
507 }
508 }
509
510 CpeId::Value::Value( const std::string & encoded_r, FsFormatType )
511 {
512 if ( encoded_r != "*" ) // ANY is default constructed
513 {
514 if ( encoded_r == "-" ) // NA
515 {
517 }
518 else
519 {
520 str::Str result;
521 bool starting = true; // false after the 1st non-?
522 for_( chp, encoded_r.begin(), encoded_r.end() )
523 {
524 switch ( *chp )
525 {
526 case '\\': // may stay quoted
527 ++chp;
528 if ( chIsWfnUnescaped( *chp ) )
529 result << *chp;
530 else if ( chIsValidRange( *chp ) )
531 result << '\\' << *chp;
532 else if ( *chp )
533 throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal quoted character '\\" << *chp << "'" );
534 else
535 throw std::invalid_argument( "CpeId:Fs: Backslash escapes nothing" );
536 break;
537
538 case '?': // sequence at beginning or end of string
539 result << '?';
540 while ( *(chp+1) == '?' )
541 {
542 ++chp;
543 result << '?';
544 }
545 if ( ! ( starting || chp+1 == encoded_r.end() ) )
546 throw std::invalid_argument( "CpeId:Fs: embedded ?" );
547 break;
548
549 case '*': // single at beginning or end of string
550 if ( starting || chp+1 == encoded_r.end() )
551 result << '*';
552 else
553 throw std::invalid_argument( "CpeId:Fs: embedded *" );
554 break;
555
556 default:
557 if ( chIsWfnUnescaped( *chp ) )
558 result << *chp;
559 else if ( chIsValidRange( *chp ) )
560 result << '\\' << *chp;
561 else
562 throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
563 break;
564 }
565 if ( starting )
566 starting = false;
567 }
568 if ( starting )
569 throw std::invalid_argument( "CpeId:Fs: '' value is illegal" );
570 _value.reset( new std::string( result ) );
571 }
572 }
573 }
574
575 CpeId::Value::Value( const std::string & encoded_r, UriFormatType )
576 {
577 if ( ! encoded_r.empty() ) // ANY is default constructed
578 {
579 if ( encoded_r == "-" ) // NA
580 {
582 }
583 else
584 {
585 str::Str result;
586 bool starting = true; // false after the 1st non-? (%01)
587 for_( chp, encoded_r.begin(), encoded_r.end() )
588 {
589 char ch = *chp;
590
591 if ( ch == '%' ) // legal '%xx' sequence first
592 {
593 int d1 = heDecodeCh( *(chp+1) );
594 if ( d1 != -1 )
595 {
596 int d2 = heDecodeCh( *(chp+2) );
597 if ( d2 != -1 )
598 {
599 chp += 2; // skip sequence
600 if ( d1 == 0 )
601 {
602 if ( d2 == 1 ) // %01 - ? valid sequence at begin or end
603 {
604 result << '?';
605 while ( *(chp+1) == '%' && *(chp+2) == '0' && *(chp+3) == '1' )
606 {
607 chp += 3;
608 result << '?';
609 }
610 if ( starting || chp+1 == encoded_r.end() )
611 {
612 starting = false;
613 continue; // -> continue;
614 }
615 else
616 throw std::invalid_argument( "CpeId:Uri: embedded %01" );
617 }
618 else if ( d2 == 2 ) // %02 - * valid at begin or end
619 {
620 if ( starting || chp+1 == encoded_r.end() )
621 {
622 result << '*';
623 starting = false;
624 continue; // -> continue;
625 }
626 else
627 throw std::invalid_argument( "CpeId:Uri: embedded %02" );
628 }
629 }
630 ch = (d1<<4)|d2;
631 if ( ! chIsValidRange( ch ) )
632 throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal % encoded character '" << reinterpret_cast<void*>(ch) << "'" );
633 }
634 }
635 }
636 else if ( ! chIsValidRange( ch ) )
637 throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal character '" << reinterpret_cast<void*>(ch) << "'" );
638
639 if ( chIsWfnUnescaped( ch ) )
640 result << ch;
641 else
642 result << '\\' << ch;
643
644 if ( starting )
645 starting = false;
646 }
647 _value.reset( new std::string( result ) );
648 }
649 }
650 }
651
652 std::string CpeId::Value::asWfn() const
653 {
654 std::string ret;
655 if ( ! _value )
656 {
657 static const std::string any( "*" );
658 ret = any;
659 }
660 else
661 ret = *_value; // includes "" for NA
662 return ret;
663 }
664
665 std::string CpeId::Value::asFs() const
666 {
667 std::string ret;
668 if ( isANY() )
669 {
670 static const std::string asterisk( "*" );
671 ret = asterisk;
672 }
673 else if ( isNA() )
674 {
675 static const std::string dash( "-" );
676 ret = dash;
677 }
678 else
679 {
680 str::Str result;
681 for_( chp, _value->begin(), _value->end() )
682 {
683 if ( *chp != '\\' )
684 result << *chp;
685 else
686 {
687 ++chp;
688 switch ( *chp )
689 {
690 case '-':
691 case '.':
692 case '_':
693 result << *chp; // without escaping
694 break;
695
696 case '\0':
697 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
698 break;
699
700 default:
701 result << '\\' << *chp;
702 break;
703 }
704 }
705 }
706 ret = result;
707 }
708 return ret;
709 }
710
711 std::string CpeId::Value::asUri() const
712 {
713 std::string ret; // ANY
714 if ( ! isANY() )
715 {
716 if ( isNA() )
717 {
718 static const std::string dash( "-" );
719 ret = dash;
720 }
721 else
722 {
723 str::Str result;
724 for_( chp, _value->begin(), _value->end() )
725 {
726 if ( chIsWfnUnescaped( *chp ) )
727 {
728 result << *chp;
729 }
730 else
731 {
732 static const char *const hdig = "0123456789abcdef";
733 switch ( *chp )
734 {
735 case '\\':
736 ++chp;
737 switch ( *chp )
738 {
739 case '-':
740 case '.':
741 result << *chp; // without encodeing
742 break;
743
744 case '\0':
745 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
746 break;
747
748 default:
749 result << '%' << hdig[(unsigned char)(*chp)/16] << hdig[(unsigned char)(*chp)%16];
750 break;
751 }
752 break;
753
754 case '?':
755 result << "%01";
756 break;
757
758 case '*':
759 result << "%02";
760 break;
761
762 default:
763 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal char '" << *chp << "' in WFN" );
764 break;
765 }
766 }
767 }
768 ret = result;
769 }
770 }
771 return ret;
772 }
773
775 namespace
776 {
778 inline bool isWildchar( char ch_r )
779 { return( ch_r == '*' || ch_r == '?' ); }
780
784 inline bool evenNumberOfBackslashes( std::string::const_reverse_iterator rbegin_r, std::string::const_reverse_iterator rend_r )
785 {
786 unsigned backslashes = 0;
787 for_( it, rbegin_r, rend_r )
788 {
789 if ( *it == '\\' )
790 ++backslashes;
791 else
792 break;
793 }
794 return !(backslashes & 1U);
795 }
796
798 inline unsigned trueCharsIn( const std::string & str_r, std::string::size_type begin_r, std::string::size_type end_r )
799 {
800 unsigned chars = 0;
801 for_( it, begin_r, end_r )
802 {
803 ++chars;
804 if ( str_r[it] == '\\' )
805 {
806 if ( ++it == end_r )
807 break;
808 }
809 }
810 return chars;
811 }
812
814 inline bool matchWildcardfreeString( const std::string & lhs, const std::string & rhs )
815 { return( str::compareCI( lhs, rhs ) == 0 ); }
816
843 inline bool matchWildcardedString( std::string src, std::string trg )
844 {
845 // std::string::npos remembers an asterisk
846 // unescaped wildcard prefix
847 std::string::size_type prefx = 0;
848 switch ( *src.begin() ) // wellformed implies not empty
849 {
850 case '*':
851 if ( src.size() == 1 )
852 return true; // "*" matches always: superset
853 // else
854 prefx = std::string::npos;
855 src.erase( 0, 1 );
856 break;
857 case '?':
858 ++prefx;
859 for_( it, ++src.begin(), src.end() )
860 { if ( *it == '?' ) ++prefx; else break; }
861 if ( src.size() == prefx )
862 return( trg.size() <= prefx ); // "??..?": superset if at most #prefx chars
863 // else
864 src.erase( 0, prefx );
865 break;
866 default:
867 break;
868 }
869 // unescaped wildcard suffix
870 std::string::size_type suffx = 0;
871 if ( ! src.empty() )
872 {
873 switch ( *src.rbegin() )
874 {
875 case '*':
876 if ( evenNumberOfBackslashes( ++src.rbegin(), src.rend() ) )
877 {
878 suffx = std::string::npos;
879 src.erase( src.size()-1 );
880 }
881 break;
882 case '?':
883 ++suffx;
884 for_( it, ++src.rbegin(), src.rend() )
885 { if ( *it == '?' ) ++suffx; else break; }
886 if ( ! evenNumberOfBackslashes( src.rbegin()+suffx, src.rend() ) )
887 --suffx; // last '?' was escaped.
888 src.erase( src.size()-suffx );
889 break;
890 default:
891 break;
892 }
893 }
894 // now match; find src in trg an check surrounding wildcards
895 src = str::toLower( src );
896 trg = str::toLower( trg );
897 for ( std::string::size_type match = trg.find( src, 0 );
898 match != std::string::npos;
899 match = trg.find( src, match+1 ) )
900 {
901 if ( prefx != std::string::npos && trueCharsIn( trg, 0, match ) > prefx )
902 break; // not "*", and already more chars than "?"s before match: disjoint
903 std::string::size_type frontSize = match + src.size();
904 if ( suffx != std::string::npos && trueCharsIn( trg, frontSize, trg.size() ) > suffx )
905 continue; // not "*", and still more chars than "?"s after match: check next match
906 return true; // match: superset
907 }
908 return false; // disjoint
909 }
910 } // namespace
912
914 {
915 const std::string & value( *_value );
916 return ( isWildchar( *value.begin() )
917 || ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
918 }
919
934#define WFN_STRICT_SPEC 0
935#if WFN_STRICT_SPEC
936 //SetCompare CpeId::Value::setRelationMixinCompare( const CpeId::Value & trg ) const
937 {
938 static const SetCompare kNeedsCloserLook( SetCompare::Enum(-1) ); // artificial Compare value
939 static const SetCompare matchTabel[4][4] = {{
940 /* ANY, ANY */ SetCompare::equal,
941 /* ANY, NA */ SetCompare::properSuperset,
942 /* ANY, wildcardfree */ SetCompare::properSuperset,
943 /* ANY, wildcarded */ SetCompare::uncomparable,
944 },{
945 /* NA, ANY */ SetCompare::properSubset,
946 /* NA, NA */ SetCompare::equal,
947 /* NA, wildcardfree */ SetCompare::disjoint,
948 /* NA, wildcarded */ SetCompare::uncomparable,
949 },{
950 /* wildcardfree, ANY */ SetCompare::properSubset,
951 /* wildcardfree, NA */ SetCompare::disjoint,
952 /* wildcardfree, wildcardfree */ kNeedsCloserLook, // equal or disjoint
953 /* wildcardfree, wildcarded */ SetCompare::uncomparable,
954 },{
955 /* wildcarded, ANY */ SetCompare::properSubset,
956 /* wildcarded, NA */ SetCompare::disjoint,
957 /* wildcarded, wildcardfree */ kNeedsCloserLook, // superset or disjoint
958 /* wildcarded, wildcarded */ SetCompare::uncomparable,
959 }};
960
961 Type srcType = type();
962 Type trgType = trg.type();
963 SetCompare ret = matchTabel[srcType.asIntegral()][trgType.asIntegral()];
964 if ( ret == kNeedsCloserLook )
965 {
966 if ( srcType == Type::wildcardfree ) // trgType == Type::wildcardfree
967 {
968 // simple string compare
969 ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::disjoint;
970 }
971 else if ( srcType == Type::wildcarded ) // trgType == Type::wildcardfree
972 {
973 // Needs wildcard compare
974 ret = matchWildcardedString( *_value, *trg._value ) ? SetCompare::properSuperset : SetCompare::disjoint;
975 }
976 }
977 return ret;
978 }
979#else
981 {
983 // ANY, ANY => equal
984 // ANY, NA => properSuperset
985 // ANY, wildcardfree => properSuperset
986 // ANY, wildcarded => properSuperset
987 //
988 // NA, ANY => properSubset
989 // NA, NA => equal
990 // NA, wildcardfree => disjoint
991 // NA, wildcarded => disjoint
992 //
993 // wildcardfree, ANY => properSubset
994 // wildcardfree, NA => disjoint
995 // wildcardfree, wildcardfree => NeedsCloserLook: equal or disjoint
996 // wildcardfree, wildcarded => NeedsCloserLook: subset or disjoint
997 //
998 // wildcarded, ANY => properSubset
999 // wildcarded, NA => disjoint
1000 // wildcarded, wildcardfree => NeedsCloserLook: superset or disjoint
1001 // wildcarded, wildcarded => NeedsCloserLook" equal or uncomparable
1003
1004 SetCompare ret = SetCompare::disjoint;
1005
1006 if ( isANY() )
1007 {
1008 ret = trg.isANY() ? SetCompare::equal : SetCompare::properSuperset;
1009 }
1010 else if ( trg.isANY() )
1011 {
1012 ret = SetCompare::properSubset;
1013 }
1014 else if ( isNA() )
1015 {
1016 if ( trg.isNA() ) ret = SetCompare::equal; // else: SetCompare::disjoint;
1017 }
1018 else if ( ! trg.isNA() ) // else: SetCompare::disjoint;
1019 {
1020 // NeedsCloserLook:
1021 if ( isWildcarded() )
1022 {
1023 if ( trg.isWildcarded() )
1024 {
1025 // simple string compare just to detect 'equal'
1026 ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::uncomparable;
1027 }
1028 else
1029 {
1030 // Needs wildcard compare (src,trg)
1031 if ( matchWildcardedString( *_value, *trg._value ) ) ret = SetCompare::properSuperset; // else: SetCompare::disjoint;
1032 }
1033 }
1034 else
1035 {
1036 if ( trg.isWildcarded() )
1037 {
1038 // Needs wildcard compare (trg,src)
1039 if ( matchWildcardedString( *trg._value, *_value ) ) ret = SetCompare::properSubset; // else: SetCompare::disjoint;
1040 }
1041 else
1042 {
1043 // simple string compare
1044 if ( matchWildcardfreeString( *_value, *trg._value ) ) ret = SetCompare::equal; // else: SetCompare::disjoint;
1045 }
1046 }
1047 }
1048 return ret;
1049 }
1050#endif // WFN_STRICT_SPEC
1051
1052 std::ostream & operator<<( std::ostream & str, const CpeId::Value & obj )
1053 { return str << obj.asString(); }
1054
1055} // namespace zypp
#define WFN_ATTRIBUTES
Initializer list with all wfn attributes.
Definition: CpeId.cc:23
#define OUTS(V)
Edition * _value
Definition: SysContent.cc:311
CpeId implementation.
Definition: CpeId.cc:85
std::string asUri() const
Definition: CpeId.cc:110
Impl(const std::string &cpe_r)
Definition: CpeId.cc:91
SetCompare setRelationMixinCompare(const Impl &trg) const
Definition: CpeId.cc:174
static void assignAttr(Wfn &wfn_r, Attribute attr_r, const Value &val_r)
Assign val_r if it meets attr_r specific contraints.
Definition: CpeId.cc:216
static Wfn unbindUri(const std::string &cpe_r)
Parse Uri and unbind.
Definition: CpeId.cc:316
std::string asFs() const
Definition: CpeId.cc:99
static Wfn unbind(const std::string &cpe_r)
Parse magic and unbind accordingly.
Definition: CpeId.cc:289
static Wfn unbindFs(const std::string &cpe_r)
Parse Fs and unbind.
Definition: CpeId.cc:350
std::array< Value, Attribute::numAttributes > Wfn
Definition: CpeId.cc:86
std::string asWfn() const
Definition: CpeId.cc:153
WFN attribute value.
Definition: CpeId.h:159
static const Value ANY
Logical value matching ANY value.
Definition: CpeId.h:162
bool isNA() const
Whether value is NA.
Definition: CpeId.h:230
static const Value NA
Logical value indicating “not applicable/not used".
Definition: CpeId.h:165
bool isWildcarded() const
An attribute value string with wildcards ([*?] at begin and/or end)
Definition: CpeId.h:252
RWCOW_pointer< std::string > _value
Definition: CpeId.h:292
bool isANY() const
Whether value is ANY.
Definition: CpeId.h:226
static constexpr UriFormatType uriFormat
Indicator argument for ctor arg in URI format.
Definition: CpeId.h:176
static constexpr FsFormatType fsFormat
Indicator argument for ctor arg in FS format.
Definition: CpeId.h:171
bool isString() const
Whether it's an attribute value string (not logical value).
Definition: CpeId.h:241
std::string asString() const
Default string representation [asWfn].
Definition: CpeId.h:257
std::string asFs() const
String representation as in Formated-String (ANY:"*", NA:"-")
Definition: CpeId.cc:665
SetCompare setRelationMixinCompare(const Value &trg) const
CPE name matching hook for SetRelationMixin.
Definition: CpeId.cc:980
Value()
Default ctor: ANY.
Definition: CpeId.h:180
bool containsWildcard() const
HAs unquoted [*?] at begin and/or end of value.
Definition: CpeId.cc:913
std::string asUri() const
String representation as in URI (ANY:"", NA:"-")
Definition: CpeId.cc:711
std::string asWfn() const
String representation as in Well-Formed-Name (ANY:"*", NA:"").
Definition: CpeId.cc:652
Common Platform Enumearation (2.3) See http://cpe.mitre.org/ for more information on the Common Platf...
Definition: CpeId.h:32
std::string asUri() const
String representation as URI (in/out).
Definition: CpeId.cc:407
base::EnumClass< EAttributeDef > Attribute
'enum class Attribute'
Definition: CpeId.h:56
~CpeId()
Dtor.
Definition: CpeId.cc:398
std::ostream & operator<<(std::ostream &str, const CpeId &obj)
Stream output.
Definition: CpeId.h:132
static constexpr NoThrowType noThrow
Indicator argument for non-trowing ctor.
Definition: CpeId.h:62
std::string asWfn() const
String representation as Well-Formed-Name (internal format, out only).
Definition: CpeId.cc:410
std::string asFs() const
String representation as Formated-String (in/out).
Definition: CpeId.cc:404
SetCompare setRelationMixinCompare(const CpeId &trg) const
CPE name matching hook for SetRelationMixin.
Definition: CpeId.cc:413
CpeId()
Default ctor: ANY-Cpeid, all attribute values are ANY.
Definition: CpeId.cc:376
RWCOW_pointer< Impl > _pimpl
Implementation class.
Definition: CpeId.h:125
SetCompare compare(const CpeId &trg) const
Compare sets.
String related utilities and Regular expression matching.
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition: NonCopyable.h:26
unsigned splitFields(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=":")
Split line_r into fields.
Definition: String.h:725
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition: String.cc:177
int compareCI(const C_Str &lhs, const C_Str &rhs)
Definition: String.h:984
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
static const std::string & asString(Enum val_r)
string representantion
Definition: CpeId.cc:420
Indicator type for non-trowing ctor.
Definition: CpeId.h:60
static std::string lastMalformed
Definition: CpeId.h:60
Indicator type for ctor arg in FS format.
Definition: CpeId.h:169
Indicator type for ctor arg in URI format.
Definition: CpeId.h:174
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:212
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28