1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
11 #define ZYPP_DBG_VAREXPAND 0
12 #if ( ZYPP_DBG_VAREXPAND )
13 #warning ZYPP_DBG_VAREXPAND is on
18 #endif // ZYPP_DBG_VAREXPAND
20 #include "zypp/base/LogTools.h"
21 #include "zypp/base/String.h"
22 #include "zypp/base/Regex.h"
24 #include "zypp/ZConfig.h"
25 #include "zypp/Target.h"
26 #include "zypp/Arch.h"
27 #include "zypp/repo/RepoVariables.h"
28 #include "zypp/base/NonCopyable.h"
30 ///////////////////////////////////////////////////////////////////
35 /** Use faked releasever (e.g. for 'zupper dup' to next distro version */
36 inline std::string ZYPP_REPO_RELEASEVER()
38 const char * env = getenv("ZYPP_REPO_RELEASEVER");
39 return( env ? env : "" );
43 ///////////////////////////////////////////////////////////////////
46 ///////////////////////////////////////////////////////////////////
48 ///////////////////////////////////////////////////////////////////
51 ///////////////////////////////////////////////////////////////////
53 /// \brief Helper scanning for variable definitions in a string
54 ///////////////////////////////////////////////////////////////////
57 bool _embedded; ///< A (formerly) embedded string may have esacped \c $, \c closebrace and \c backslash
58 const char * _sbeg; ///< start of string to scan
59 const char * _vbeg; ///< [$]{variable:-word} / [$]{variable} / if embedded also on [\\]
60 const char * _nbeg; ///< ${[v]ariable:-word} / ${[v]ariable}
61 const char * _nend; ///< ${variable[:]-word} / ${variable[}]
62 const char * _vend; ///< ${variable:-word}[] / ${variable}[]
63 const char * _send; ///< end of scan (next $ or nullptr if done)
65 FindVar( const std::string & str_r, bool embedded_r )
66 : _embedded( embedded_r )
67 , _sbeg( str_r.c_str() )
72 , _send( findVarStart( _sbeg ) )
75 /** Nullptr in _send indicates we scanned the whole string. */
79 /** Advance to first/next var if there is one */
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!
92 } while( ! findVarEnd() );
97 /** Valid _vend indicates valid var data in scan. */
102 // Methods below are only valid if hasVar() == true
105 /** Return the full var text */
106 std::string var() const
107 { return std::string( _vbeg, _vend ); }
109 /** Return the var name */
110 std::string varName() const
111 { return std::string( _nbeg, _nend ); }
113 /** Whether this is a conditional var (${..:[+-]...}) */
114 bool varIsConditional() const
115 { return( *(_vbeg+1) == '{' && *_nend == ':' ); }
117 /** The var type: \c \, \c $, \c - , \c +
118 * \li \c \ backslash escaped literal
119 * \li \c $ plain variable
120 * \li \c - conditional: default value
121 * \li \c + conditional: alternate value
124 { return( varIsConditional() ? *(_nend+1) : *_vbeg ); }
126 /** Return embedded value in conditional vars or empty string */
127 std::string varEmbedded() const
128 { return( varIsConditional() ? std::string( _nend+2, _vend-1 ) : std::string() ); }
131 /** Have unwritten data before var? */
132 bool hasVarPrefix() const
133 { return ( _sbeg != _vbeg ); }
135 /** Return unwritten data before var */
136 std::string varPrefix() const
137 { return std::string( _sbeg, _vbeg ); }
139 /** Indicate all data up to _vend were written */
144 /** Return next \c $ */
145 const char * findVarStart( const char * sbeg_r ) const
147 for ( ; *sbeg_r; ++sbeg_r )
148 if ( *sbeg_r == '$' || ( _embedded && *sbeg_r == '\\' ) )
153 /** Valid var name char */
154 bool isnamech( int ch ) const
155 { return ch == '_' || isalpha( ch ); }
157 /** Scan for a valid variable starting at _vbeg (storing the values) */
160 // asserted: *_vbeg == '$' || '\\'
161 if ( ! findVarEnd( _vbeg, _nbeg, _nend, _vend ) )
163 _send = findVarStart( _vend );
167 /** Skip over valid variable starting at vbeg (return end in \a vend). */
168 const char * findVarEnd( const char * vbeg ) const
170 // asserted: *_vbeg == '$'
171 const char * nbeg = nullptr;
172 const char * nend = nullptr;
173 const char * vend = nullptr;
174 findVarEnd( vbeg, nbeg, nend, vend );
178 /** Scan for a valid variable starting at vbeg (const version returning the values). */
179 bool findVarEnd( const char * vbeg, const char *& nbeg, const char *& nend, const char *& vend ) const
181 // embedded only: handle backslash escaped chars
182 if ( *_vbeg == '\\' )
189 nend = vend = vbeg+2;
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
202 for ( nend = nbeg+1; isnamech( *nend ); ++nend )
203 {;} // skip over var name
204 // nend: ${variable[:]-word} / ${variable[}]
206 // vend: ${variable:-word}[] / ${variable}[]
207 // stay with ( vend == nullptr ) until you know it's valid
214 else if ( *nend == ':' )
216 const char * scan = nend+1;
217 if ( *scan == '+' || *scan == '-' )
220 // find first not escaped '}'
225 ++scan; // next char is skipped
229 else if ( *scan == '$' )
232 if ( ! (scan = findVarEnd( scan )) )
235 else if ( *scan == '}' )
237 vend = scan+1; // ==> unesacped '}', we're done!
243 // ( ! *scan ) => end of string while looking for unesacped '}'
246 ; // err: ':' not followed by '+' or '-'
249 ; // err: braced name must end with '}' or ':'
253 vend = nend; // un-braced
255 return( vend != nullptr );
259 bool _expand( std::string &, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r );
261 inline std::string expand( const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
264 if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
269 inline std::string expand( std::string && value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
272 if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
273 ret = std::move(value_r);
277 /** Expand variables in \a value_r depending on \a level-r
278 * <tt>level_r > 0</tt> may have escaped chars outside braces.
280 inline bool _expand( std::string & result_r, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
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
290 bool expanded = false;
292 if ( ! value_r.empty() )
294 FindVar scan( value_r, level_r ); // level_r > 0 is embedded
295 while ( scan.nextVar() )
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 );
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;
306 #endif // ZYPP_DBG_VAREXPAND
308 bool mustSubstitute = false; // keep original text per default
309 std::string substitutionValue;
311 int varType = scan.varType();
312 if ( varType == '$' ) // plain var
316 mustSubstitute = true;
317 substitutionValue = varValue;
320 ; // keep original text per default
322 else if ( varType == '-' ) // ':-' default value
324 mustSubstitute = true;
325 if ( varValue.empty() )
326 substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
328 substitutionValue = varValue;
330 else if ( varType == '+' ) // ':+' alternate value
332 mustSubstitute = true;
333 if ( ! varValue.empty() )
334 substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
336 ; // empty substitutionValue
338 else if ( varType == '\\' ) // backslash escaped literal (in varName)
340 mustSubstitute = true;
341 substitutionValue = scan.varName();
344 ; // keep original text per default
346 if ( mustSubstitute )
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
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
360 // handle unwritten data:
361 if ( scan._sbeg != value_r.c_str() )
365 result_r += std::string( scan._sbeg );
368 ; // no replacements at all
371 #if ( ZYPP_DBG_VAREXPAND )
373 cout << dbg.str() << endl;
374 cout << std::string( 2*level_r, ' ' ) << "\033[36m->" << result_r << "<-\033[0m" << endl;
375 #endif // ZYPP_DBG_VAREXPAND
379 ///////////////////////////////////////////////////////////////////
381 std::string RepoVarExpand::operator()( const std::string & value_r, VarRetriever varRetriever_r ) const
382 { return expand( value_r, 0, varRetriever_r ); }
384 std::string RepoVarExpand::operator()( std::string && value_r, VarRetriever varRetriever_r ) const
385 { return expand( std::move(value_r), 0, varRetriever_r ); }
387 ///////////////////////////////////////////////////////////////////
388 // RepoVariables*Replace
389 ///////////////////////////////////////////////////////////////////
392 /** \brief Provide lazy initialized repo variables
394 struct RepoVars : private zypp::base::NonCopyable
396 typedef const std::string & (RepoVars::*Getter)() const;
398 const std::string & arch() const
404 const std::string & basearch() const
410 const std::string & releasever() const
412 assertReleaseverStr();
416 const std::string & releaseverMajor() const
418 assertReleaseverStr();
419 return _releaseverMajor;
422 const std::string & releaseverMinor() const
424 assertReleaseverStr();
425 return _releaseverMinor;
429 void assertArchStr() const
433 Arch arch( ZConfig::instance().systemArchitecture() );
434 _arch = arch.asString();
435 _basearch = arch.baseArch().asString();
439 void assertReleaseverStr() const
441 if ( _releasever.empty() )
443 _releasever = env::ZYPP_REPO_RELEASEVER();
444 if( _releasever.empty() )
445 _releasever = Target::distributionVersion( Pathname()/*guess*/ );
447 WAR << "ENV overwrites $releasever=" << _releasever << endl;
449 // split major/minor for SLE
450 std::string::size_type pos = _releasever.find( "." );
451 if ( pos == std::string::npos )
453 _releaseverMajor = _releasever;
454 _releaseverMinor.clear();
458 _releaseverMajor = _releasever.substr( 0, pos );
459 _releaseverMinor = _releasever.substr( pos+1 ) ;
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;
472 const std::string * repoVarLookup( const std::string & name_r )
474 RepoVars::Getter getter = nullptr;
475 switch ( name_r.size() )
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;
486 const std::string * ret = nullptr;
487 if ( getter ) // known var
489 static const RepoVars _repoVars;
490 ret = &(_repoVars.*getter)();
495 ///////////////////////////////////////////////////////////////////
497 std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
499 return RepoVarExpand()( value, repoVarLookup );
501 std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
503 return RepoVarExpand()( value, repoVarLookup );
506 Url RepoVariablesUrlReplacer::operator()( const Url & value ) const
508 RepoVarExpand expand;
510 newurl.setPathData( expand( value.getPathData(), repoVarLookup ) );
511 newurl.setQueryString( expand( value.getQueryString(), repoVarLookup ) );
516 ///////////////////////////////////////////////////////////////////
518 ///////////////////////////////////////////////////////////////////