libzypp 17.31.23
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
26using std::cout;
27#endif // ZYPP_DBG_VAREXPAND
28
30namespace 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 // Safe guard in case the caller does not own a zypp instance. In this case
404 // getZYpp()->getTarget() in checkOverride would create a zypp instance which
405 // would clear the variables parsed so far.
406 auto guard { getZYpp() };
407
408 if ( empty() ) // at init / after reset
409 {
410 // load user definitions from vars.d
411 filesystem::dirForEach( ZConfig::instance().repoManagerRoot() / ZConfig::instance().varsPath(),
412 filesystem::matchNoDots(), bind( &RepoVarsMap::parse, this, _1, _2 ) );
413 // releasever_major/_minor are per default derived from releasever.
414 // If releasever is userdefined, inject missing _major/_minor too.
415 deriveFromReleasever( "releasever", /*dont't overwrite user defined values*/false );
416
417 dumpOn( DBG );
418 // add builtin vars except for releasever{,_major,_minor} (see checkOverride)
419 {
420 const Arch & arch( ZConfig::instance().systemArchitecture() );
421 {
422 std::string & var( operator[]( "arch" ) );
423 if ( var.empty() ) var = arch.asString();
424 }
425 {
426 std::string & var( operator[]( "basearch" ) );
427 if ( var.empty() ) var = arch.baseArch().asString();
428 }
429 }
430 }
431
432 const std::string * ret = checkOverride( name_r );
433 if ( !ret )
434 {
435 // get value from map
436 iterator it = find( name_r );
437 if ( it != end() )
438 ret = &(it->second);
439 }
440
441 return ret;
442 }
443
444 std::ostream & dumpOn( std::ostream & str ) const
445 {
446 for ( auto && kv : *this )
447 {
448 str << '{' << kv.first << '=' << kv.second << '}' << endl;
449 }
450 return str;
451 }
452
453 private:
455 bool parse( const Pathname & dir_r, const std::string & str_r )
456 {
457 std::ifstream file( (dir_r/str_r).c_str() );
458 operator[]( str_r ) = str::getline( file, /*trim*/false );
459 return true;
460 }
461
463 void deriveFromReleasever( const std::string & stem_r, bool overwrite_r )
464 {
465 if ( count( stem_r ) ) // releasever is defined..
466 {
467 const std::string & stem_major( stem_r+"_major" );
468 const std::string & stem_minor( stem_r+"_minor" );
469 if ( overwrite_r )
470 splitReleaseverTo( operator[]( stem_r ), &operator[]( stem_major ), &operator[]( stem_minor ) );
471 else
472 splitReleaseverTo( operator[]( stem_r ),
473 count( stem_major ) ? nullptr : &operator[]( stem_major ),
474 count( stem_minor ) ? nullptr : &operator[]( stem_minor ) );
475 }
476 }
477
479 void splitReleaseverTo( const std::string & releasever_r, std::string * major_r, std::string * minor_r ) const
480 {
481 if ( major_r || minor_r )
482 {
483 std::string::size_type pos = releasever_r.find( "." );
484 if ( pos == std::string::npos )
485 {
486 if ( major_r ) *major_r = releasever_r;
487 if ( minor_r ) minor_r->clear();
488 }
489 else
490 {
491 if ( major_r ) *major_r = releasever_r.substr( 0, pos );
492 if ( minor_r ) *minor_r = releasever_r.substr( pos+1 ) ;
493 }
494 }
495 }
496
498 const std::string * checkOverride( const std::string & name_r )
499 {
501 // Always check for changing releasever{,_major,_minor} (bnc#943563)
502 if ( str::startsWith( name_r, "releasever" )
503 && ( name_r.size() == 10
504 || strcmp( name_r.c_str()+10, "_minor" ) == 0
505 || strcmp( name_r.c_str()+10, "_major" ) == 0 ) )
506 {
507 std::string val( env::ZYPP_REPO_RELEASEVER() );
508 if ( !val.empty() )
509 {
510 // $ZYPP_REPO_RELEASEVER always overwrites any defined value
511 if ( val != operator[]( "$releasever" ) )
512 {
513 operator[]( "$releasever" ) = std::move(val);
514 deriveFromReleasever( "$releasever", /*overwrite previous values*/true );
515 }
516 return &operator[]( "$"+name_r );
517 }
518 else if ( !count( name_r ) )
519 {
520 // No user defined value, so we follow the target
521 Target_Ptr trg( getZYpp()->getTarget() );
522 if ( trg )
523 val = trg->distributionVersion();
524 else
525 val = Target::distributionVersion( Pathname()/*guess*/ );
526
527 if ( val != operator[]( "$_releasever" ) )
528 {
529 operator[]( "$_releasever" ) = std::move(val);
530 deriveFromReleasever( "$_releasever", /*overwrite previous values*/true );
531 }
532 return &operator[]( "$_"+name_r );
533 }
534 // else:
535 return nullptr; // get user value from map
536 }
538
539 return nullptr; // get user value from map
540 }
541 };
542 } // namespace
544
545 std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
546 {
547 return RepoVarExpand()( value, RepoVarsMap::lookup );
548 }
549 std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
550 {
551 return RepoVarExpand()( value, RepoVarsMap::lookup );
552 }
553
555 {
557 // Legacy: Not 100% correct because it substitutes inside the 'proxypass=' value,
558 // but this was done before as well. The final fix will have to keep the proxypasswd
559 // out side the url in a cedential file.
560 Url tmpurl { value };
561 tmpurl.setViewOptions( toReplace );
562 const std::string & replaced( RepoVarExpand()( hotfix1050625::asString( tmpurl ), RepoVarsMap::lookup ) );
563
564 Url newurl;
565 if ( !replaced.empty() )
566 {
567 newurl = replaced;
570 newurl.setViewOptions( value.getViewOptions() );
571 }
572 return newurl;
573 }
574 } // namespace repo
576} // namespace zypp
579namespace zyppintern
580{
581 using namespace zypp;
582 // internal helper called when re-acquiring the lock
584 { repo::RepoVarsMap::instance().clear(); }
585
586} // namespace zyppintern
const char * _nend
${variable[:]-word} / ${variable[}]
const char * _sbeg
start of string to scan
const char * _nbeg
${[v]ariable:-word} / ${[v]ariable}
const char * _vend
${variable:-word}[] / ${variable}[]
bool _embedded
A (formerly) embedded string may have esacped $, closebrace and backslash.
const char * _send
end of scan (next $ or nullptr if done)
const char * _vbeg
[$]{variable:-word} / [$]{variable} / if embedded also on [\]
std::string distributionVersion() const
This is version attribute of the installed base product.
Definition: Target.cc:122
Url manipulation class.
Definition: Url.h:92
void setViewOptions(const ViewOptions &vopts)
Change the view options of the current object.
Definition: Url.cc:859
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:572
ViewOptions getViewOptions() const
Return the view options of the current object.
Definition: Url.cc:852
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition: Url.cc:739
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
Definition: Url.cc:730
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition: Url.cc:580
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:922
String related utilities and Regular expression matching.
std::string ZYPP_REPO_RELEASEVER()
Use faked releasever (e.g.
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition: PathInfo.cc:26
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition: PathInfo.cc:32
std::string asString(const Url &url_r)
Definition: Url.cc:886
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Definition: String.cc:478
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1085
@ E_ENCODED
Flag to request encoded string(s).
Definition: UrlUtils.h:53
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
std::ostream & dumpOn(std::ostream &str, const Capability &obj)
Definition: Capability.cc:580
void repoVariablesReset()
Functor expanding repo variables in a string.
Definition: RepoVariables.h:58
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
std::string operator()(const std::string &value_r, VarRetriever varRetriever_r) const
Return a copy of value_r with embedded variables expanded.
std::string operator()(const std::string &value_r) const
Url operator()(const Url &url_r) const
Url::asString() view options.
Definition: UrlBase.h:40
static const ViewOption WITH_PASSWORD
Option to include password in the URL string.
Definition: UrlBase.h:67
static const ViewOption WITH_USERNAME
Option to include username in the URL string.
Definition: UrlBase.h:58
#define nullptr
Definition: Easy.h:55
#define DBG
Definition: Logger.h:95