libzypp  16.22.5
RepoVariables.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 #include <iostream>
10 #include <fstream>
11 
12 #include "zypp/base/LogTools.h"
13 #include "zypp/base/String.h"
14 #include "zypp/base/Regex.h"
15 
16 #include "zypp/ZYppFactory.h"
17 #include "zypp/ZConfig.h"
18 #include "zypp/Target.h"
19 #include "zypp/Arch.h"
21 #include "zypp/base/NonCopyable.h"
22 
23 #define ZYPP_DBG_VAREXPAND 0
24 #if ( ZYPP_DBG_VAREXPAND )
25 #warning ZYPP_DBG_VAREXPAND is on
26 using std::cout;
27 #endif // ZYPP_DBG_VAREXPAND
28 
30 namespace zypp
31 {
32  namespace env
33  {
35  inline std::string ZYPP_REPO_RELEASEVER()
36  {
37  const char * env = getenv("ZYPP_REPO_RELEASEVER");
38  return( env ? env : "" );
39  }
40  }
41 
43  namespace repo
44  {
46  // RepoVarExpand
48  namespace
49  {
54  struct FindVar
55  {
56  bool _embedded;
57  const char * _sbeg;
58  const char * _vbeg;
59  const char * _nbeg;
60  const char * _nend;
61  const char * _vend;
62  const char * _send;
63 
64  FindVar( const std::string & str_r, bool embedded_r )
65  : _embedded( embedded_r )
66  , _sbeg( str_r.c_str() )
67  , _vbeg( nullptr )
68  , _nbeg( nullptr )
69  , _nend( nullptr )
70  , _vend( nullptr )
71  , _send( findVarStart( _sbeg ) )
72  {}
73 
75  bool done() const
76  { return !_send; }
77 
79  bool nextVar()
80  {
81  if ( done() )
82  return false;
83 
84  do {
85  if ( _vbeg && !_vend ) // loop internal: no findVarEnd at current $; skip it
86  _send = findVarStart( _vbeg+1 );
87  _vbeg = _send; // next $ or null if string end
88  _nbeg = _nend = _vend = _send = nullptr;
89  if ( ! _vbeg ) // done!
90  return false;
91  } while( ! findVarEnd() );
92 
93  return true;
94  }
95 
97  bool hasVar() const
98  { return _vend; }
99 
100  //
101  // Methods below are only valid if hasVar() == true
102  //
103 
105  std::string var() const
106  { return std::string( _vbeg, _vend ); }
107 
109  std::string varName() const
110  { return std::string( _nbeg, _nend ); }
111 
113  bool varIsConditional() const
114  { return( *(_vbeg+1) == '{' && *_nend == ':' ); }
115 
122  int varType() const
123  { return( varIsConditional() ? *(_nend+1) : *_vbeg ); }
124 
126  std::string varEmbedded() const
127  { return( varIsConditional() ? std::string( _nend+2, _vend-1 ) : std::string() ); }
128 
129 
131  bool hasVarPrefix() const
132  { return ( _sbeg != _vbeg ); }
133 
135  std::string varPrefix() const
136  { return std::string( _sbeg, _vbeg ); }
137 
139  void wroteVar()
140  { _sbeg = _vend; }
141 
142  private:
144  const char * findVarStart( const char * sbeg_r ) const
145  {
146  for ( ; *sbeg_r; ++sbeg_r )
147  if ( *sbeg_r == '$' || ( _embedded && *sbeg_r == '\\' ) )
148  return sbeg_r;
149  return nullptr;
150  }
151 
153  bool isnamech( int ch ) const
154  { return ch == '_' || isalnum( ch ); }
155 
157  bool findVarEnd()
158  {
159  // asserted: *_vbeg == '$' || '\\'
160  if ( ! findVarEnd( _vbeg, _nbeg, _nend, _vend ) )
161  return false;
162  _send = findVarStart( _vend );
163  return true;
164  }
165 
167  const char * findVarEnd( const char * vbeg ) const
168  {
169  // asserted: *_vbeg == '$'
170  const char * nbeg = nullptr;
171  const char * nend = nullptr;
172  const char * vend = nullptr;
173  findVarEnd( vbeg, nbeg, nend, vend );
174  return vend;
175  }
176 
178  bool findVarEnd( const char * vbeg, const char *& nbeg, const char *& nend, const char *& vend ) const
179  {
180  // embedded only: handle backslash escaped chars
181  if ( *_vbeg == '\\' )
182  {
183  nbeg = vbeg+1;
184  if ( *nbeg == '$'
185  || *nbeg == '}'
186  || *nbeg == '\\' )
187  {
188  nend = vend = vbeg+2;
189  return true;
190  }
191  return false;
192  }
193 
194  // asserted: *vbeg == '$'
195  // vbeg: [$]{variable:-word} / [$]{variable}
196  // nbeg: ${[v]ariable:-word} / ${[v]ariable}
197  bool braced = ( *(vbeg+1) == '{' ); //}
198  nbeg = vbeg+( braced ? 2 : 1 );
199  if ( !isnamech( *nbeg ) ) // don't allow empty var name
200  return false;
201  for ( nend = nbeg+1; isnamech( *nend ); ++nend )
202  {;} // skip over var name
203  // nend: ${variable[:]-word} / ${variable[}]
204 
205  // vend: ${variable:-word}[] / ${variable}[]
206  // stay with ( vend == nullptr ) until you know it's valid
207  if ( braced )
208  {
209  if ( *nend == '}' )
210  {
211  vend = nend+1;
212  }
213  else if ( *nend == ':' )
214  {
215  const char * scan = nend+1;
216  if ( *scan == '+' || *scan == '-' )
217  {
218  ++scan;
219  // find first not escaped '}'
220  while ( *scan )
221  {
222  if ( *scan == '\\' )
223  {
224  ++scan; // next char is skipped
225  if ( *scan )
226  ++scan;
227  }
228  else if ( *scan == '$' )
229  {
230  // an embedded var?
231  if ( ! (scan = findVarEnd( scan )) )
232  return false;
233  }
234  else if ( *scan == '}' )
235  {
236  vend = scan+1; // ==> unesacped '}', we're done!
237  break;
238  }
239  else
240  ++scan; // literal
241  }
242  // ( ! *scan ) => end of string while looking for unesacped '}'
243  }
244  else
245  ; // err: ':' not followed by '+' or '-'
246  }
247  else
248  ; // err: braced name must end with '}' or ':'
249  }
250  else
251  {
252  vend = nend; // un-braced
253  }
254  return( vend != nullptr );
255  }
256  };
257 
258  bool _expand( std::string &, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r );
259 
260  inline std::string expand( const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
261  {
262  std::string ret;
263  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
264  ret = value_r;
265  return ret;
266  }
267 
268  inline std::string expand( std::string && value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
269  {
270  std::string ret;
271  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
272  ret = std::move(value_r);
273  return ret;
274  }
275 
279  inline bool _expand( std::string & result_r, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
280  {
281 #if ( ZYPP_DBG_VAREXPAND )
282  cout << std::string( 2*level_r, ' ' ) << "\033[7m>>" << value_r << "<<\033[27m" << endl;
283  std::ostringstream dbg;
284  const char * dbgsbeg = value_r.c_str(); // track vars we already added to dbg
285  unsigned dbgi = 0; // color 1-5 var / 6 moved value_r
286  dbg << std::string( 2*level_r, ' ' ) << ">>";
287 #endif // ZYPP_DBG_VAREXPAND
288 
289  bool expanded = false;
290 
291  if ( ! value_r.empty() )
292  {
293  FindVar scan( value_r, level_r ); // level_r > 0 is embedded
294  while ( scan.nextVar() )
295  {
296  static const std::string _emptyValue;
297  const std::string *const knownVar = ( varRetriever_r ? varRetriever_r( scan.varName() ) : nullptr );
298  const std::string & varValue( knownVar ? *knownVar : _emptyValue );
299 
300 #if ( ZYPP_DBG_VAREXPAND )
301  dbg << std::string(dbgsbeg,scan._vbeg) << "\033[3" << ((dbgi%5)+1) << "m" << scan.var() << "\033[0m";
302  cout << dbg.str() << "|<< " << scan.varName() << " " << (knownVar?"("+varValue+")":"-") << " {" << scan.varEmbedded() << "}" << endl;
303  dbgsbeg = scan._vend;
304  dbgi++;
305 #endif // ZYPP_DBG_VAREXPAND
306 
307  bool mustSubstitute = false; // keep original text per default
308  std::string substitutionValue;
309 
310  int varType = scan.varType();
311  if ( varType == '$' ) // plain var
312  {
313  if ( knownVar )
314  {
315  mustSubstitute = true;
316  substitutionValue = varValue;
317  }
318  else
319  ; // keep original text per default
320  }
321  else if ( varType == '-' ) // ':-' default value
322  {
323  mustSubstitute = true;
324  if ( varValue.empty() )
325  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
326  else
327  substitutionValue = varValue;
328  }
329  else if ( varType == '+' ) // ':+' alternate value
330  {
331  mustSubstitute = true;
332  if ( ! varValue.empty() )
333  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
334  else
335  ; // empty substitutionValue
336  }
337  else if ( varType == '\\' ) // backslash escaped literal (in varName)
338  {
339  mustSubstitute = true;
340  substitutionValue = scan.varName();
341  }
342  else
343  ; // keep original text per default
344 
345  if ( mustSubstitute )
346  {
347  if ( scan.hasVarPrefix() )
348  result_r += scan.varPrefix();
349  if ( ! substitutionValue.empty() )
350  result_r += substitutionValue;
351  scan.wroteVar(); // this moves scan._sbeg so we can later see what's already written
352  }
353  }
354 
355 #if ( ZYPP_DBG_VAREXPAND )
356  dbg << std::string( dbgsbeg ) << (scan._sbeg == value_r.c_str() ? "<<\033[36m(moved)\033[0m" : "");
357 #endif // ZYPP_DBG_VAREXPAND
358 
359  // handle unwritten data:
360  if ( scan._sbeg != value_r.c_str() )
361  {
362  expanded = true;
363  if ( *scan._sbeg )
364  result_r += std::string( scan._sbeg );
365  }
366  else
367  ; // no replacements at all
368  }
369 
370 #if ( ZYPP_DBG_VAREXPAND )
371  dbg << "<<";
372  cout << dbg.str() << endl;
373  cout << std::string( 2*level_r, ' ' ) << "\033[36m->" << result_r << "<-\033[0m" << endl;
374 #endif // ZYPP_DBG_VAREXPAND
375  return expanded;
376  }
377  } // namespace
379 
380  std::string RepoVarExpand::operator()( const std::string & value_r, VarRetriever varRetriever_r ) const
381  { return expand( value_r, 0, varRetriever_r ); }
382 
383  std::string RepoVarExpand::operator()( std::string && value_r, VarRetriever varRetriever_r ) const
384  { return expand( std::move(value_r), 0, varRetriever_r ); }
385 
387  // RepoVariables*Replace
389  namespace
390  {
391  class RepoVarsMap : public std::map<std::string,std::string>
392  {
393  public:
394  static RepoVarsMap & instance()
395  { static RepoVarsMap _instance; return _instance; }
396 
397  static const std::string * lookup( const std::string & name_r )
398  { return instance()._lookup( name_r ); }
399 
400  private:
401  const std::string * _lookup( const std::string & name_r )
402  {
403  if ( empty() ) // at init / after reset
404  {
405  // load user definitions from vars.d
406  filesystem::dirForEach( ZConfig::instance().systemRoot() / ZConfig::instance().varsPath(),
407  filesystem::matchNoDots(), bind( &RepoVarsMap::parse, this, _1, _2 ) );
408  // releasever_major/_minor are per default derived from releasever.
409  // If releasever is userdefined, inject missing _major/_minor too.
410  deriveFromReleasever( "releasever", /*dont't overwrite user defined values*/false );
411 
412  dumpOn( DBG );
413  // add builtin vars except for releasever{,_major,_minor} (see checkOverride)
414  {
415  const Arch & arch( ZConfig::instance().systemArchitecture() );
416  {
417  std::string & var( operator[]( "arch" ) );
418  if ( var.empty() ) var = arch.asString();
419  }
420  {
421  std::string & var( operator[]( "basearch" ) );
422  if ( var.empty() ) var = arch.baseArch().asString();
423  }
424  }
425  }
426 
427  const std::string * ret = checkOverride( name_r );
428  if ( !ret )
429  {
430  // get value from map
431  iterator it = find( name_r );
432  if ( it != end() )
433  ret = &(it->second);
434  }
435 
436  return ret;
437  }
438 
439  std::ostream & dumpOn( std::ostream & str ) const
440  {
441  for ( auto && kv : *this )
442  {
443  str << '{' << kv.first << '=' << kv.second << '}' << endl;
444  }
445  return str;
446  }
447 
448  private:
450  bool parse( const Pathname & dir_r, const std::string & str_r )
451  {
452  std::ifstream file( (dir_r/str_r).c_str() );
453  operator[]( str_r ) = str::getline( file, /*trim*/false );
454  return true;
455  }
456 
458  void deriveFromReleasever( const std::string & stem_r, bool overwrite_r )
459  {
460  if ( count( stem_r ) ) // releasever is defined..
461  {
462  const std::string & stem_major( stem_r+"_major" );
463  const std::string & stem_minor( stem_r+"_minor" );
464  if ( overwrite_r )
465  splitReleaseverTo( operator[]( stem_r ), &operator[]( stem_major ), &operator[]( stem_minor ) );
466  else
467  splitReleaseverTo( operator[]( stem_r ),
468  count( stem_major ) ? nullptr : &operator[]( stem_major ),
469  count( stem_minor ) ? nullptr : &operator[]( stem_minor ) );
470  }
471  }
472 
474  void splitReleaseverTo( const std::string & releasever_r, std::string * major_r, std::string * minor_r ) const
475  {
476  if ( major_r || minor_r )
477  {
478  std::string::size_type pos = releasever_r.find( "." );
479  if ( pos == std::string::npos )
480  {
481  if ( major_r ) *major_r = releasever_r;
482  if ( minor_r ) minor_r->clear();
483  }
484  else
485  {
486  if ( major_r ) *major_r = releasever_r.substr( 0, pos );
487  if ( minor_r ) *minor_r = releasever_r.substr( pos+1 ) ;
488  }
489  }
490  }
491 
493  const std::string * checkOverride( const std::string & name_r )
494  {
496  // Always check for changing releasever{,_major,_minor} (bnc#943563)
497  if ( str::startsWith( name_r, "releasever" )
498  && ( name_r.size() == 10
499  || strcmp( name_r.c_str()+10, "_minor" ) == 0
500  || strcmp( name_r.c_str()+10, "_major" ) == 0 ) )
501  {
502  std::string val( env::ZYPP_REPO_RELEASEVER() );
503  if ( !val.empty() )
504  {
505  // $ZYPP_REPO_RELEASEVER always overwrites any defined value
506  if ( val != operator[]( "$releasever" ) )
507  {
508  operator[]( "$releasever" ) = std::move(val);
509  deriveFromReleasever( "$releasever", /*overwrite previous values*/true );
510  }
511  return &operator[]( "$"+name_r );
512  }
513  else if ( !count( name_r ) )
514  {
515  // No user defined value, so we follow the target
516  Target_Ptr trg( getZYpp()->getTarget() );
517  if ( trg )
518  val = trg->distributionVersion();
519  else
520  val = Target::distributionVersion( Pathname()/*guess*/ );
521 
522  if ( val != operator[]( "$_releasever" ) )
523  {
524  operator[]( "$_releasever" ) = std::move(val);
525  deriveFromReleasever( "$_releasever", /*overwrite previous values*/true );
526  }
527  return &operator[]( "$_"+name_r );
528  }
529  // else:
530  return nullptr; // get user value from map
531  }
533 
534  return nullptr; // get user value from map
535  }
536  };
537  } // namespace
539 
540  std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
541  {
542  return RepoVarExpand()( value, RepoVarsMap::lookup );
543  }
544  std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
545  {
546  return RepoVarExpand()( value, RepoVarsMap::lookup );
547  }
548 
550  {
552  const std::string & replaced( RepoVarExpand()( value.asString( toReplace ), RepoVarsMap::lookup ) );
553  Url newurl;
554  if ( !replaced.empty() )
555  {
556  newurl = replaced;
559  }
560  return newurl;
561  }
562  } // namespace repo
564 } // namespace zypp
567 namespace zyppintern
568 {
569  using namespace zypp;
570  // internal helper called when re-acquiring the lock
572  { repo::RepoVarsMap::instance().clear(); }
573 
574 } // namespace zyppintern
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition: Url.cc:733
static const ViewOption WITH_USERNAME
Option to include username in the URL string.
Definition: UrlBase.h:58
const char * _nend
${variable[:]-word} / ${variable[}]
const char * _vend
${variable:-word}[] / ${variable}[]
static ZConfig & instance()
Singleton ctor.
Definition: Resolver.cc:122
Flag to request encoded string(s).
Definition: UrlUtils.h:53
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition: Target.cc:125
const char * _vbeg
[$]{variable:-word} / [$]{variable} / if embedded also on [\]
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:566
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
Definition: Url.cc:724
Url::asString() view options.
Definition: UrlBase.h:39
bool _embedded
A (formerly) embedded string may have esacped $, closebrace and backslash.
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:491
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition: PathInfo.cc:545
std::string operator()(const std::string &value_r) const
Functor expanding repo variables in a string.
Definition: RepoVariables.h:57
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1095
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Definition: String.cc:476
#define nullptr
Definition: Easy.h:54
std::string operator()(const std::string &value_r, VarRetriever varRetriever_r) const
Return a copy of value_r with embedded variables expanded.
std::ostream & dumpOn(std::ostream &str, const Capability &obj)
Definition: Capability.cc:444
SolvableIdType size_type
Definition: PoolMember.h:152
const char * _send
end of scan (next $ or nullptr if done)
static const ViewOption WITH_PASSWORD
Option to include password in the URL string.
Definition: UrlBase.h:67
const char * _sbeg
start of string to scan
int dirForEach(const Pathname &dir_r, function< bool(const Pathname &, const char *const)> fnc_r)
Invoke callback function fnc_r for each entry in directory dir_r.
Definition: PathInfo.cc:551
static const ViewOption DEFAULTS
Default combination of view options.
Definition: UrlBase.h:177
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
#define DBG
Definition: Logger.h:63
void repoVariablesReset()
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition: Url.cc:574