36
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
///////////////////////////////////////////////////////////////////
39
392
/** \brief Provide lazy initialized repo variables
41
struct ReplacerData : private zypp::base::NonCopyable
394
struct RepoVars : private zypp::base::NonCopyable
43
typedef const std::string & (ReplacerData::*Getter)() const;
396
typedef const std::string & (RepoVars::*Getter)() const;
45
398
const std::string & arch() const
114
468
mutable std::string _releaseverMinor;
117
/** \brief Replace repo variables on demand
119
* Initialisation of repo variables is delayed until they actually occur in
122
std::string replacer( std::string value_r )
472
const std::string * repoVarLookup( const std::string & name_r )
125
if ( ! value_r.empty() )
474
RepoVars::Getter getter = nullptr;
475
switch ( name_r.size() )
127
static const str::regex rxVAR( "^([^$]*)\\$(\\{[[:alnum:]_]+\\}|[[:alnum:]_]+)([^$]*)" );
129
while ( str::regex_match( value_r, what, rxVAR ) )
131
ReplacerData::Getter getter = nullptr;
133
const char * varStart = value_r.c_str() + what.begin( 2 );
134
std::string::size_type varSize = what.size( 2 );
135
if ( *varStart == '{' ) // enclosed in {}
143
#define ASSIGN_IF(NAME,GETTER) if ( ::strncmp( varStart, NAME, varSize ) == 0 ) getter = GETTER
145
case 4: ASSIGN_IF( "arch", &ReplacerData::arch ); break;
146
case 8: ASSIGN_IF( "basearch", &ReplacerData::basearch ); break;
147
case 10: ASSIGN_IF( "releasever", &ReplacerData::releasever ); break;
148
case 16: ASSIGN_IF( "releasever_major", &ReplacerData::releaseverMajor );
149
else ASSIGN_IF( "releasever_minor", &ReplacerData::releaseverMinor ); break;
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;
153
if ( getter ) // known var?
155
static const ReplacerData _data;
156
if ( what.size( 1 ) > 0 ) ret += what[1]; // pre
157
ret += (_data.*getter)(); // var
158
if ( what.size( 3 ) > 0 ) ret += what[3]; // post
162
ret += what[0]; // unchanged
165
value_r.erase( 0, what.size( 0 ) );
166
if ( value_r.empty() )
169
if ( ! value_r.empty() )
170
ret += std::move(value_r); // no match
486
const std::string * ret = nullptr;
487
if ( getter ) // known var
489
static const RepoVars _repoVars;
490
ret = &(_repoVars.*getter)();