libzypp  15.3.0
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/ZConfig.h"
25 #include "zypp/Target.h"
26 #include "zypp/Arch.h"
28 #include "zypp/base/NonCopyable.h"
29 
31 namespace zypp
32 {
33  namespace env
34  {
36  inline std::string ZYPP_REPO_RELEASEVER()
37  {
38  const char * env = getenv("ZYPP_REPO_RELEASEVER");
39  return( env ? env : "" );
40  }
41  }
42 
44  namespace repo
45  {
47  // RepoVarExpand
49  namespace
50  {
55  struct FindVar
56  {
57  bool _embedded;
58  const char * _sbeg;
59  const char * _vbeg;
60  const char * _nbeg;
61  const char * _nend;
62  const char * _vend;
63  const char * _send;
64 
65  FindVar( const std::string & str_r, bool embedded_r )
66  : _embedded( embedded_r )
67  , _sbeg( str_r.c_str() )
68  , _vbeg( nullptr )
69  , _nbeg( nullptr )
70  , _nend( nullptr )
71  , _vend( nullptr )
72  , _send( findVarStart( _sbeg ) )
73  {}
74 
76  bool done() const
77  { return !_send; }
78 
80  bool nextVar()
81  {
82  if ( done() )
83  return false;
84 
85  do {
86  if ( _vbeg && !_vend ) // loop internal: no findVarEnd at current $; skip it
87  _send = findVarStart( _vbeg+1 );
88  _vbeg = _send; // next $ or null if string end
89  _nbeg = _nend = _vend = _send = nullptr;
90  if ( ! _vbeg ) // done!
91  return false;
92  } while( ! findVarEnd() );
93 
94  return true;
95  }
96 
98  bool hasVar() const
99  { return _vend; }
100 
101  //
102  // Methods below are only valid if hasVar() == true
103  //
104 
106  std::string var() const
107  { return std::string( _vbeg, _vend ); }
108 
110  std::string varName() const
111  { return std::string( _nbeg, _nend ); }
112 
114  bool varIsConditional() const
115  { return( *(_vbeg+1) == '{' && *_nend == ':' ); }
116 
123  int varType() const
124  { return( varIsConditional() ? *(_nend+1) : *_vbeg ); }
125 
127  std::string varEmbedded() const
128  { return( varIsConditional() ? std::string( _nend+2, _vend-1 ) : std::string() ); }
129 
130 
132  bool hasVarPrefix() const
133  { return ( _sbeg != _vbeg ); }
134 
136  std::string varPrefix() const
137  { return std::string( _sbeg, _vbeg ); }
138 
140  void wroteVar()
141  { _sbeg = _vend; }
142 
143  private:
145  const char * findVarStart( const char * sbeg_r ) const
146  {
147  for ( ; *sbeg_r; ++sbeg_r )
148  if ( *sbeg_r == '$' || ( _embedded && *sbeg_r == '\\' ) )
149  return sbeg_r;
150  return nullptr;
151  }
152 
154  bool isnamech( int ch ) const
155  { return ch == '_' || isalpha( ch ); }
156 
158  bool findVarEnd()
159  {
160  // asserted: *_vbeg == '$' || '\\'
161  if ( ! findVarEnd( _vbeg, _nbeg, _nend, _vend ) )
162  return false;
163  _send = findVarStart( _vend );
164  return true;
165  }
166 
168  const char * findVarEnd( const char * vbeg ) const
169  {
170  // asserted: *_vbeg == '$'
171  const char * nbeg = nullptr;
172  const char * nend = nullptr;
173  const char * vend = nullptr;
174  findVarEnd( vbeg, nbeg, nend, vend );
175  return vend;
176  }
177 
179  bool findVarEnd( const char * vbeg, const char *& nbeg, const char *& nend, const char *& vend ) const
180  {
181  // embedded only: handle backslash escaped chars
182  if ( *_vbeg == '\\' )
183  {
184  nbeg = vbeg+1;
185  if ( *nbeg == '$'
186  || *nbeg == '}'
187  || *nbeg == '\\' )
188  {
189  nend = vend = vbeg+2;
190  return true;
191  }
192  return false;
193  }
194 
195  // asserted: *vbeg == '$'
196  // vbeg: [$]{variable:-word} / [$]{variable}
197  // nbeg: ${[v]ariable:-word} / ${[v]ariable}
198  bool braced = ( *(vbeg+1) == '{' ); //}
199  nbeg = vbeg+( braced ? 2 : 1 );
200  if ( !isnamech( *nbeg ) ) // don't allow empty var name
201  return false;
202  for ( nend = nbeg+1; isnamech( *nend ); ++nend )
203  {;} // skip over var name
204  // nend: ${variable[:]-word} / ${variable[}]
205 
206  // vend: ${variable:-word}[] / ${variable}[]
207  // stay with ( vend == nullptr ) until you know it's valid
208  if ( braced )
209  {
210  if ( *nend == '}' )
211  {
212  vend = nend+1;
213  }
214  else if ( *nend == ':' )
215  {
216  const char * scan = nend+1;
217  if ( *scan == '+' || *scan == '-' )
218  {
219  ++scan;
220  // find first not escaped '}'
221  while ( *scan )
222  {
223  if ( *scan == '\\' )
224  {
225  ++scan; // next char is skipped
226  if ( *scan )
227  ++scan;
228  }
229  else if ( *scan == '$' )
230  {
231  // an embedded var?
232  if ( ! (scan = findVarEnd( scan )) )
233  return false;
234  }
235  else if ( *scan == '}' )
236  {
237  vend = scan+1; // ==> unesacped '}', we're done!
238  break;
239  }
240  else
241  ++scan; // literal
242  }
243  // ( ! *scan ) => end of string while looking for unesacped '}'
244  }
245  else
246  ; // err: ':' not followed by '+' or '-'
247  }
248  else
249  ; // err: braced name must end with '}' or ':'
250  }
251  else
252  {
253  vend = nend; // un-braced
254  }
255  return( vend != nullptr );
256  }
257  };
258 
259  bool _expand( std::string &, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r );
260 
261  inline std::string expand( const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
262  {
263  std::string ret;
264  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
265  ret = value_r;
266  return ret;
267  }
268 
269  inline std::string expand( std::string && value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
270  {
271  std::string ret;
272  if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
273  ret = std::move(value_r);
274  return ret;
275  }
276 
280  inline bool _expand( std::string & result_r, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
281  {
282 #if ( ZYPP_DBG_VAREXPAND )
283  cout << std::string( 2*level_r, ' ' ) << "\033[7m>>" << value_r << "<<\033[27m" << endl;
284  std::ostringstream dbg;
285  const char * dbgsbeg = value_r.c_str(); // track vars we already added to dbg
286  unsigned dbgi = 0; // color 1-5 var / 6 moved value_r
287  dbg << std::string( 2*level_r, ' ' ) << ">>";
288 #endif // ZYPP_DBG_VAREXPAND
289 
290  bool expanded = false;
291 
292  if ( ! value_r.empty() )
293  {
294  FindVar scan( value_r, level_r ); // level_r > 0 is embedded
295  while ( scan.nextVar() )
296  {
297  static const std::string _emptyValue;
298  const std::string *const knownVar = ( varRetriever_r ? varRetriever_r( scan.varName() ) : nullptr );
299  const std::string & varValue( knownVar ? *knownVar : _emptyValue );
300 
301 #if ( ZYPP_DBG_VAREXPAND )
302  dbg << std::string(dbgsbeg,scan._vbeg) << "\033[3" << ((dbgi%5)+1) << "m" << scan.var() << "\033[0m";
303  cout << dbg.str() << "|<< " << scan.varName() << " " << (knownVar?"("+varValue+")":"-") << " {" << scan.varEmbedded() << "}" << endl;
304  dbgsbeg = scan._vend;
305  dbgi++;
306 #endif // ZYPP_DBG_VAREXPAND
307 
308  bool mustSubstitute = false; // keep original text per default
309  std::string substitutionValue;
310 
311  int varType = scan.varType();
312  if ( varType == '$' ) // plain var
313  {
314  if ( knownVar )
315  {
316  mustSubstitute = true;
317  substitutionValue = varValue;
318  }
319  else
320  ; // keep original text per default
321  }
322  else if ( varType == '-' ) // ':-' default value
323  {
324  mustSubstitute = true;
325  if ( varValue.empty() )
326  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
327  else
328  substitutionValue = varValue;
329  }
330  else if ( varType == '+' ) // ':+' alternate value
331  {
332  mustSubstitute = true;
333  if ( ! varValue.empty() )
334  substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
335  else
336  ; // empty substitutionValue
337  }
338  else if ( varType == '\\' ) // backslash escaped literal (in varName)
339  {
340  mustSubstitute = true;
341  substitutionValue = scan.varName();
342  }
343  else
344  ; // keep original text per default
345 
346  if ( mustSubstitute )
347  {
348  if ( scan.hasVarPrefix() )
349  result_r += scan.varPrefix();
350  if ( ! substitutionValue.empty() )
351  result_r += substitutionValue;
352  scan.wroteVar(); // this moves scan._sbeg so we can later see what's already written
353  }
354  }
355 
356 #if ( ZYPP_DBG_VAREXPAND )
357  dbg << std::string( dbgsbeg ) << (scan._sbeg == value_r.c_str() ? "<<\033[36m(moved)\033[0m" : "");
358 #endif // ZYPP_DBG_VAREXPAND
359 
360  // handle unwritten data:
361  if ( scan._sbeg != value_r.c_str() )
362  {
363  expanded = true;
364  if ( *scan._sbeg )
365  result_r += std::string( scan._sbeg );
366  }
367  else
368  ; // no replacements at all
369  }
370 
371 #if ( ZYPP_DBG_VAREXPAND )
372  dbg << "<<";
373  cout << dbg.str() << endl;
374  cout << std::string( 2*level_r, ' ' ) << "\033[36m->" << result_r << "<-\033[0m" << endl;
375 #endif // ZYPP_DBG_VAREXPAND
376  return expanded;
377  }
378  } // namespace
380 
381  std::string RepoVarExpand::operator()( const std::string & value_r, VarRetriever varRetriever_r ) const
382  { return expand( value_r, 0, varRetriever_r ); }
383 
384  std::string RepoVarExpand::operator()( std::string && value_r, VarRetriever varRetriever_r ) const
385  { return expand( std::move(value_r), 0, varRetriever_r ); }
386 
388  // RepoVariables*Replace
390  namespace
391  {
394  struct RepoVars : private zypp::base::NonCopyable
395  {
396  typedef const std::string & (RepoVars::*Getter)() const;
397 
398  const std::string & arch() const
399  {
400  assertArchStr();
401  return _arch;
402  }
403 
404  const std::string & basearch() const
405  {
406  assertArchStr();
407  return _basearch;
408  }
409 
410  const std::string & releasever() const
411  {
412  assertReleaseverStr();
413  return _releasever;
414  }
415 
416  const std::string & releaseverMajor() const
417  {
418  assertReleaseverStr();
419  return _releaseverMajor;
420  }
421 
422  const std::string & releaseverMinor() const
423  {
424  assertReleaseverStr();
425  return _releaseverMinor;
426  }
427 
428  private:
429  void assertArchStr() const
430  {
431  if ( _arch.empty() )
432  {
433  Arch arch( ZConfig::instance().systemArchitecture() );
434  _arch = arch.asString();
435  _basearch = arch.baseArch().asString();
436  }
437  }
438 
439  void assertReleaseverStr() const
440  {
441  if ( _releasever.empty() )
442  {
444  if( _releasever.empty() )
445  _releasever = Target::distributionVersion( Pathname()/*guess*/ );
446  else
447  WAR << "ENV overwrites $releasever=" << _releasever << endl;
448 
449  // split major/minor for SLE
450  std::string::size_type pos = _releasever.find( "." );
451  if ( pos == std::string::npos )
452  {
454  _releaseverMinor.clear();
455  }
456  else
457  {
458  _releaseverMajor = _releasever.substr( 0, pos );
459  _releaseverMinor = _releasever.substr( pos+1 ) ;
460  }
461  }
462  }
463  private:
464  mutable std::string _arch;
465  mutable std::string _basearch;
466  mutable std::string _releasever;
467  mutable std::string _releaseverMajor;
468  mutable std::string _releaseverMinor;
469  };
470 
472  const std::string * repoVarLookup( const std::string & name_r )
473  {
474  RepoVars::Getter getter = nullptr;
475  switch ( name_r.size() )
476  {
477 #define ASSIGN_IF(NAME,GETTER) if ( name_r == NAME ) getter = GETTER
478  case 4: ASSIGN_IF( "arch", &RepoVars::arch ); break;
479  case 8: ASSIGN_IF( "basearch", &RepoVars::basearch ); break;
480  case 10: ASSIGN_IF( "releasever", &RepoVars::releasever ); break;
481  case 16: ASSIGN_IF( "releasever_major", &RepoVars::releaseverMajor );
482  else ASSIGN_IF( "releasever_minor", &RepoVars::releaseverMinor ); break;
483 #undef ASSIGN_IF
484  }
485 
486  const std::string * ret = nullptr;
487  if ( getter ) // known var
488  {
489  static const RepoVars _repoVars;
490  ret = &(_repoVars.*getter)();
491  }
492  return ret;
493  }
494  } // namespace
496 
497  std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
498  {
499  return RepoVarExpand()( value, repoVarLookup );
500  }
501  std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
502  {
503  return RepoVarExpand()( value, repoVarLookup );
504  }
505 
507  {
508  RepoVarExpand expand;
509  Url newurl( value );
510  newurl.setPathData( expand( value.getPathData(), repoVarLookup ) );
511  newurl.setQueryString( expand( value.getQueryString(), repoVarLookup ) );
512  return newurl;
513  }
514 
515  } // namespace repo
517 } // 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: ZConfig.cc:679
std::string _basearch
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition: Target.cc:134
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:99
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
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
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
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