libzypp  15.28.6
RepoVariables.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 #include <cstring>
10 
11 #define ZYPP_DBG_VAREXPAND 0
12 #if ( ZYPP_DBG_VAREXPAND )
13 #warning ZYPP_DBG_VAREXPAND is on
14 #include <iostream>
15 #include <sstream>
16 using std::cout;
17 using std::endl;
18 #endif // ZYPP_DBG_VAREXPAND
19 
20 #include "zypp/base/LogTools.h"
21 #include "zypp/base/String.h"
22 #include "zypp/base/Regex.h"
23 
24 #include "zypp/ZYppFactory.h"
25 #include "zypp/ZConfig.h"
26 #include "zypp/Target.h"
27 #include "zypp/Arch.h"
29 #include "zypp/base/NonCopyable.h"
30 
32 namespace zypp
33 {
34  namespace env
35  {
37  inline std::string ZYPP_REPO_RELEASEVER()
38  {
39  const char * env = getenv("ZYPP_REPO_RELEASEVER");
40  return( env ? env : "" );
41  }
42  }
43 
45  namespace repo
46  {
48  // RepoVarExpand
50  namespace
51  {
56  struct FindVar
57  {
58  bool _embedded;
59  const char * _sbeg;
60  const char * _vbeg;
61  const char * _nbeg;
62  const char * _nend;
63  const char * _vend;
64  const char * _send;
65 
66  FindVar( const std::string & str_r, bool embedded_r )
67  : _embedded( embedded_r )
68  , _sbeg( str_r.c_str() )
69  , _vbeg( nullptr )
70  , _nbeg( nullptr )
71  , _nend( nullptr )
72  , _vend( nullptr )
73  , _send( findVarStart( _sbeg ) )
74  {}
75 
77  bool done() const
78  { return !_send; }
79 
81  bool nextVar()
82  {
83  if ( done() )
84  return false;
85 
86  do {
87  if ( _vbeg && !_vend ) // loop internal: no findVarEnd at current $; skip it
88  _send = findVarStart( _vbeg+1 );
89  _vbeg = _send; // next $ or null if string end
90  _nbeg = _nend = _vend = _send = nullptr;
91  if ( ! _vbeg ) // done!
92  return false;
93  } while( ! findVarEnd() );
94 
95  return true;
96  }
97 
99  bool hasVar() const
100  { return _vend; }
101 
102  //
103  // Methods below are only valid if hasVar() == true
104  //
105 
107  std::string var() const
108  { return std::string( _vbeg, _vend ); }
109 
111  std::string varName() const
112  { return std::string( _nbeg, _nend ); }
113 
115  bool varIsConditional() const
116  { return( *(_vbeg+1) == '{' && *_nend == ':' ); }
117 
124  int varType() const
125  { return( varIsConditional() ? *(_nend+1) : *_vbeg ); }
126 
128  std::string varEmbedded() const
129  { return( varIsConditional() ? std::string( _nend+2, _vend-1 ) : std::string() ); }
130 
131 
133  bool hasVarPrefix() const
134  { return ( _sbeg != _vbeg ); }
135 
137  std::string varPrefix() const
138  { return std::string( _sbeg, _vbeg ); }
139 
141  void wroteVar()
142  { _sbeg = _vend; }
143 
144  private:
146  const char * findVarStart( const char * sbeg_r ) const
147  {
148  for ( ; *sbeg_r; ++sbeg_r )
149  if ( *sbeg_r == '$' || ( _embedded && *sbeg_r == '\\' ) )
150  return sbeg_r;
151  return nullptr;
152  }
153 
155  bool isnamech( int ch ) const
156  { return ch == '_' || isalpha( ch ); }
157 
159  bool findVarEnd()
160  {
161  // asserted: *_vbeg == '$' || '\\'
162  if ( ! findVarEnd( _vbeg, _nbeg, _nend, _vend ) )
163  return false;
164  _send = findVarStart( _vend );
165  return true;
166  }
167 
169  const char * findVarEnd( const char * vbeg ) const
170  {
171  // asserted: *_vbeg == '$'
172  const char * nbeg = nullptr;
173  const char * nend = nullptr;
174  const char * vend = nullptr;
175  findVarEnd( vbeg, nbeg, nend, vend );
176  return vend;
177  }
178 
180  bool findVarEnd( const char * vbeg, const char *& nbeg, const char *& nend, const char *& vend ) const
181  {
182  // embedded only: handle backslash escaped chars
183  if ( *_vbeg == '\\' )
184  {
185  nbeg = vbeg+1;
186  if ( *nbeg == '$'
187  || *nbeg == '}'
188  || *nbeg == '\\' )
189  {
190  nend = vend = vbeg+2;
191  return true;
192  }
193  return false;
194  }
195 
196  // asserted: *vbeg == '$'
197  // vbeg: [$]{variable:-word} / [$]{variable}
198  // nbeg: ${[v]ariable:-word} / ${[v]ariable}
199  bool braced = ( *(vbeg+1) == '{' ); //}
200  nbeg = vbeg+( braced ? 2 : 1 );
201  if ( !isnamech( *nbeg ) ) // don't allow empty var name
202  return false;
203  for ( nend = nbeg+1; isnamech( *nend ); ++nend )
204  {;} // skip over var name
205  // nend: ${variable[:]-word} / ${variable[}]
206 
207  // vend: ${variable:-word}[] / ${variable}[]
208  // stay with ( vend == nullptr ) until you know it's valid
209  if ( braced )
210  {
211  if ( *nend == '}' )
212  {
213  vend = nend+1;
214  }
215  else if ( *nend == ':' )
216  {
217  const char * scan = nend+1;
218  if ( *scan == '+' || *scan == '-' )
219  {
220  ++scan;
221  // find first not escaped '}'
222  while ( *scan )
223  {
224  if ( *scan == '\\' )
225  {
226  ++scan; // next char is skipped
227  if ( *scan )
228  ++scan;
229  }
230  else if ( *scan == '$' )
231  {
232  // an embedded var?
233  if ( ! (scan = findVarEnd( scan )) )
234  return false;
235  }
236  else if ( *scan == '}' )
237  {
238  vend = scan+1; // ==> unesacped '}', we're done!
239  break;
240  }
241  else
242  ++scan; // literal
243  }
244  // ( ! *scan ) => end of string while looking for unesacped '}'
245  }
246  else
247  ; // err: ':' not followed by '+' or '-'
248  }
249  else
250  ; // err: braced name must end with '}' or ':'
251  }
252  else
253  {
254  vend = nend; // un-braced
255  }
256  return( vend != nullptr );
257  }
258  };
259 
260  bool _expand( std::string &, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r );
261 
262  inline std::string expand( const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
263  {
264  std::string ret;
265  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
266  ret = value_r;
267  return ret;
268  }
269 
270  inline std::string expand( std::string && value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
271  {
272  std::string ret;
273  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
274  ret = std::move(value_r);
275  return ret;
276  }
277 
281  inline bool _expand( std::string & result_r, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
282  {
283 #if ( ZYPP_DBG_VAREXPAND )
284  cout << std::string( 2*level_r, ' ' ) << "\033[7m>>" << value_r << "<<\033[27m" << endl;
285  std::ostringstream dbg;
286  const char * dbgsbeg = value_r.c_str(); // track vars we already added to dbg
287  unsigned dbgi = 0; // color 1-5 var / 6 moved value_r
288  dbg << std::string( 2*level_r, ' ' ) << ">>";
289 #endif // ZYPP_DBG_VAREXPAND
290 
291  bool expanded = false;
292 
293  if ( ! value_r.empty() )
294  {
295  FindVar scan( value_r, level_r ); // level_r > 0 is embedded
296  while ( scan.nextVar() )
297  {
298  static const std::string _emptyValue;
299  const std::string *const knownVar = ( varRetriever_r ? varRetriever_r( scan.varName() ) : nullptr );
300  const std::string & varValue( knownVar ? *knownVar : _emptyValue );
301 
302 #if ( ZYPP_DBG_VAREXPAND )
303  dbg << std::string(dbgsbeg,scan._vbeg) << "\033[3" << ((dbgi%5)+1) << "m" << scan.var() << "\033[0m";
304  cout << dbg.str() << "|<< " << scan.varName() << " " << (knownVar?"("+varValue+")":"-") << " {" << scan.varEmbedded() << "}" << endl;
305  dbgsbeg = scan._vend;
306  dbgi++;
307 #endif // ZYPP_DBG_VAREXPAND
308 
309  bool mustSubstitute = false; // keep original text per default
310  std::string substitutionValue;
311 
312  int varType = scan.varType();
313  if ( varType == '$' ) // plain var
314  {
315  if ( knownVar )
316  {
317  mustSubstitute = true;
318  substitutionValue = varValue;
319  }
320  else
321  ; // keep original text per default
322  }
323  else if ( varType == '-' ) // ':-' default value
324  {
325  mustSubstitute = true;
326  if ( varValue.empty() )
327  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
328  else
329  substitutionValue = varValue;
330  }
331  else if ( varType == '+' ) // ':+' alternate value
332  {
333  mustSubstitute = true;
334  if ( ! varValue.empty() )
335  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
336  else
337  ; // empty substitutionValue
338  }
339  else if ( varType == '\\' ) // backslash escaped literal (in varName)
340  {
341  mustSubstitute = true;
342  substitutionValue = scan.varName();
343  }
344  else
345  ; // keep original text per default
346 
347  if ( mustSubstitute )
348  {
349  if ( scan.hasVarPrefix() )
350  result_r += scan.varPrefix();
351  if ( ! substitutionValue.empty() )
352  result_r += substitutionValue;
353  scan.wroteVar(); // this moves scan._sbeg so we can later see what's already written
354  }
355  }
356 
357 #if ( ZYPP_DBG_VAREXPAND )
358  dbg << std::string( dbgsbeg ) << (scan._sbeg == value_r.c_str() ? "<<\033[36m(moved)\033[0m" : "");
359 #endif // ZYPP_DBG_VAREXPAND
360 
361  // handle unwritten data:
362  if ( scan._sbeg != value_r.c_str() )
363  {
364  expanded = true;
365  if ( *scan._sbeg )
366  result_r += std::string( scan._sbeg );
367  }
368  else
369  ; // no replacements at all
370  }
371 
372 #if ( ZYPP_DBG_VAREXPAND )
373  dbg << "<<";
374  cout << dbg.str() << endl;
375  cout << std::string( 2*level_r, ' ' ) << "\033[36m->" << result_r << "<-\033[0m" << endl;
376 #endif // ZYPP_DBG_VAREXPAND
377  return expanded;
378  }
379  } // namespace
381 
382  std::string RepoVarExpand::operator()( const std::string & value_r, VarRetriever varRetriever_r ) const
383  { return expand( value_r, 0, varRetriever_r ); }
384 
385  std::string RepoVarExpand::operator()( std::string && value_r, VarRetriever varRetriever_r ) const
386  { return expand( std::move(value_r), 0, varRetriever_r ); }
387 
389  // RepoVariables*Replace
391  namespace
392  {
393  inline std::string getReleaseverString()
394  {
395  std::string ret( env::ZYPP_REPO_RELEASEVER() );
396  if( ret.empty() )
397  {
398  Target_Ptr trg( getZYpp()->getTarget() );
399  if ( trg )
400  ret = trg->distributionVersion();
401  else
402  ret = Target::distributionVersion( Pathname()/*guess*/ );
403  }
404  else
405  WAR << "ENV overwrites $releasever=" << ret << endl;
406 
407  return ret;
408  }
409 
412  struct RepoVars : private zypp::base::NonCopyable
413  {
414  typedef const std::string & (RepoVars::*Getter)() const;
415 
416  const std::string & arch() const
417  {
418  assertArchStr();
419  return _arch;
420  }
421 
422  const std::string & basearch() const
423  {
424  assertArchStr();
425  return _basearch;
426  }
427 
428  const std::string & releasever() const
429  {
430  assertReleaseverStr();
431  return _releasever;
432  }
433 
434  const std::string & releaseverMajor() const
435  {
436  assertReleaseverStr();
437  return _releaseverMajor;
438  }
439 
440  const std::string & releaseverMinor() const
441  {
442  assertReleaseverStr();
443  return _releaseverMinor;
444  }
445 
446  private:
447  void assertArchStr() const
448  {
449  if ( _arch.empty() )
450  {
451  Arch arch( ZConfig::instance().systemArchitecture() );
452  _arch = arch.asString();
453  _basearch = arch.baseArch().asString();
454  }
455  }
456 
457  void assertReleaseverStr() const
458  {
459  // check for changing releasever (bnc#943563)
460  std::string check( getReleaseverString() );
461  if ( check != _releasever )
462  {
463  _releasever = std::move(check);
464  // split major/minor for SLE
465  std::string::size_type pos = _releasever.find( "." );
466  if ( pos == std::string::npos )
467  {
469  _releaseverMinor.clear();
470  }
471  else
472  {
473  _releaseverMajor = _releasever.substr( 0, pos );
474  _releaseverMinor = _releasever.substr( pos+1 ) ;
475  }
476  }
477  }
478  private:
479  mutable std::string _arch;
480  mutable std::string _basearch;
481  mutable std::string _releasever;
482  mutable std::string _releaseverMajor;
483  mutable std::string _releaseverMinor;
484  };
485 
487  const std::string * repoVarLookup( const std::string & name_r )
488  {
489  RepoVars::Getter getter = nullptr;
490  switch ( name_r.size() )
491  {
492 #define ASSIGN_IF(NAME,GETTER) if ( name_r == NAME ) getter = GETTER
493  case 4: ASSIGN_IF( "arch", &RepoVars::arch ); break;
494  case 8: ASSIGN_IF( "basearch", &RepoVars::basearch ); break;
495  case 10: ASSIGN_IF( "releasever", &RepoVars::releasever ); break;
496  case 16: ASSIGN_IF( "releasever_major", &RepoVars::releaseverMajor );
497  else ASSIGN_IF( "releasever_minor", &RepoVars::releaseverMinor ); break;
498 #undef ASSIGN_IF
499  }
500 
501  const std::string * ret = nullptr;
502  if ( getter ) // known var
503  {
504  static const RepoVars _repoVars;
505  ret = &(_repoVars.*getter)();
506  }
507  return ret;
508  }
509  } // namespace
511 
512  std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
513  {
514  return RepoVarExpand()( value, repoVarLookup );
515  }
516  std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
517  {
518  return RepoVarExpand()( value, repoVarLookup );
519  }
520 
522  {
523  RepoVarExpand expand;
524  Url newurl( value );
525  newurl.setPathData( expand( value.getPathData(), repoVarLookup ) );
526  newurl.setQueryString( expand( value.getQueryString(), repoVarLookup ) );
527  return newurl;
528  }
529 
530  } // namespace repo
532 } // namespace zypp
std::string _releaseverMajor
const char * _nend
${variable[:]-word} / ${variable[}]
const char * _vend
${variable:-word}[] / ${variable}[]
std::string getPathData() const
Returns the encoded path component of the URL.
Definition: Url.cc:542
static ZConfig & instance()
Singleton ctor.
Definition: Resolver.cc:125
std::string _basearch
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition: Target.cc:122
const char * _vbeg
[$]{variable:-word} / [$]{variable} / if embedded also on [\]
std::string _arch
#define ASSIGN_IF(NAME, GETTER)
bool _embedded
A (formerly) embedded string may have esacped $, closebrace and backslash.
std::string _releasever
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition: NonCopyable.h:26
std::string operator()(const std::string &value_r) const
Functor expanding repo variables in a string.
Definition: RepoVariables.h:57
#define WAR
Definition: Logger.h:65
void setPathData(const std::string &pathdata)
Set the path data component in the URL.
Definition: Url.cc:700
std::string _releaseverMinor
#define nullptr
Definition: Easy.h:54
std::string getQueryString() const
Returns the encoded query string component of the URL.
Definition: Url.cc:550
std::string operator()(const std::string &value_r, VarRetriever varRetriever_r) const
Return a copy of value_r with embedded variables expanded.
SolvableIdType size_type
Definition: PoolMember.h:152
const char * _send
end of scan (next $ or nullptr if done)
void setQueryString(const std::string &querystr)
Set the query string in the URL.
Definition: Url.cc:708
const char * _sbeg
start of string to scan
bool check(const std::string &sequenceinfo_r, bool quick_r)
Check via sequence info.
function< const std::string *(const std::string &)> VarRetriever
Function taking a variable name and returning a pointer to the variable value or nullptr if unset...
Definition: RepoVariables.h:60
Url operator()(const Url &url_r) const
const char * _nbeg
${[v]ariable:-word} / ${[v]ariable}
std::string ZYPP_REPO_RELEASEVER()
Use faked releasever (e.g.
Url manipulation class.
Definition: Url.h:87