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