~thopiekar/zypper/libzypp-manual-import

« back to all changes in this revision

Viewing changes to zypp/repo/RepoVariables.cc

  • Committer: Thomas-Karl Pietrowski
  • Date: 2015-08-15 15:59:50 UTC
  • Revision ID: thopiekar@googlemail.com-20150815155950-j66qn38efmvn289t
syncing with "changes 15.13.0 (11)"  #9a0aca7e3a21d768491b141a8ae86ef0c3fbc227
* https://github.com/openSUSE/libzypp/commit/9a0aca7e3a21d768491b141a8ae86ef0c3fbc227

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
|                         /_____||_| |_| |_|                           |
7
7
|                                                                      |
8
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
9
19
 
10
20
#include "zypp/base/LogTools.h"
11
21
#include "zypp/base/String.h"
34
44
  namespace repo
35
45
  {
36
46
    ///////////////////////////////////////////////////////////////////
 
47
    // RepoVarExpand
 
48
    ///////////////////////////////////////////////////////////////////
 
49
    namespace
 
50
    {
 
51
      ///////////////////////////////////////////////////////////////////
 
52
      /// \class FindVar
 
53
      /// \brief Helper scanning for variable definitions in a string
 
54
      ///////////////////////////////////////////////////////////////////
 
55
      struct FindVar
 
56
      {
 
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)
 
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
 
 
75
        /** Nullptr in _send indicates we scanned the whole string. */
 
76
        bool done() const
 
77
        { return !_send; }
 
78
 
 
79
        /** Advance to first/next var if there is one */
 
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
 
 
97
        /** Valid _vend indicates valid var data in scan. */
 
98
        bool hasVar() const
 
99
        { return _vend; }
 
100
 
 
101
        //
 
102
        // Methods below are only valid if hasVar() == true
 
103
        //
 
104
 
 
105
        /** Return the full var text */
 
106
        std::string var() const
 
107
        { return std::string( _vbeg, _vend ); }
 
108
 
 
109
        /** Return the var name */
 
110
        std::string varName() const
 
111
        { return std::string( _nbeg, _nend ); }
 
112
 
 
113
        /** Whether this is a conditional var (${..:[+-]...}) */
 
114
        bool varIsConditional() const
 
115
        { return( *(_vbeg+1) == '{' && *_nend == ':' ); }
 
116
 
 
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
 
122
        */
 
123
        int varType() const
 
124
        { return( varIsConditional() ? *(_nend+1) : *_vbeg ); }
 
125
 
 
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() ); }
 
129
 
 
130
 
 
131
        /** Have unwritten data before var? */
 
132
        bool hasVarPrefix() const
 
133
        { return ( _sbeg != _vbeg ); }
 
134
 
 
135
        /** Return unwritten data before var */
 
136
        std::string varPrefix() const
 
137
        { return std::string( _sbeg, _vbeg ); }
 
138
 
 
139
        /** Indicate all data up to _vend were written */
 
140
        void wroteVar()
 
141
        { _sbeg = _vend; }
 
142
 
 
143
      private:
 
144
        /** Return next \c $ */
 
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
 
 
153
        /** Valid var name char */
 
154
        bool isnamech( int ch ) const
 
155
        { return ch == '_' || isalpha( ch ); }
 
156
 
 
157
        /** Scan for a valid variable starting at _vbeg (storing the values) */
 
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
 
 
167
        /** Skip over valid variable starting at vbeg (return end in \a vend). */
 
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
 
 
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
 
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
 
 
277
      /** Expand variables in \a value_r depending on \a level-r
 
278
      * <tt>level_r > 0</tt> may have escaped chars outside braces.
 
279
      */
 
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
 
379
    ///////////////////////////////////////////////////////////////////
 
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
 
 
387
    ///////////////////////////////////////////////////////////////////
 
388
    // RepoVariables*Replace
 
389
    ///////////////////////////////////////////////////////////////////
37
390
    namespace
38
391
    {
39
392
      /** \brief Provide lazy initialized repo variables
40
393
       */
41
 
      struct ReplacerData : private zypp::base::NonCopyable
 
394
      struct RepoVars : private zypp::base::NonCopyable
42
395
      {
43
 
        typedef const std::string & (ReplacerData::*Getter)() const;
 
396
        typedef const std::string & (RepoVars::*Getter)() const;
44
397
 
45
398
        const std::string & arch() const
46
399
        {
82
435
            _basearch = arch.baseArch().asString();
83
436
          }
84
437
        }
 
438
 
85
439
        void assertReleaseverStr() const
86
440
        {
87
441
          if ( _releasever.empty() )
114
468
        mutable std::string _releaseverMinor;
115
469
      };
116
470
 
117
 
     /** \brief Replace repo variables on demand
118
 
       *
119
 
       * Initialisation of repo variables is delayed until they actually occur in
120
 
       * a string.
121
 
       */
122
 
      std::string replacer( std::string value_r )
 
471
      /** \brief */
 
472
      const std::string * repoVarLookup( const std::string & name_r )
123
473
      {
124
 
        std::string ret;
125
 
        if ( ! value_r.empty() )
 
474
        RepoVars::Getter getter = nullptr;
 
475
        switch ( name_r.size() )
126
476
        {
127
 
          static const str::regex rxVAR( "^([^$]*)\\$(\\{[[:alnum:]_]+\\}|[[:alnum:]_]+)([^$]*)" );
128
 
          str::smatch what;
129
 
          while ( str::regex_match( value_r, what, rxVAR ) )
130
 
          {
131
 
            ReplacerData::Getter getter = nullptr;
132
 
 
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 {}
136
 
            {
137
 
              ++varStart;
138
 
              varSize -= 2;
139
 
            }
140
 
 
141
 
            switch ( varSize )
142
 
            {
143
 
#define ASSIGN_IF(NAME,GETTER) if ( ::strncmp( varStart, NAME, varSize ) == 0 ) getter = GETTER
144
 
 
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;
150
483
#undef ASSIGN_IF
151
 
            }
152
 
 
153
 
            if ( getter )       // known var?
154
 
            {
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
159
 
            }
160
 
            else
161
 
            {
162
 
              ret += what[0];   // unchanged
163
 
            }
164
 
 
165
 
            value_r.erase( 0, what.size( 0 ) );
166
 
            if ( value_r.empty() )
167
 
              break;
168
 
          }
169
 
          if ( ! value_r.empty() )
170
 
            ret += std::move(value_r);          // no match
 
484
        }
 
485
 
 
486
        const std::string * ret = nullptr;
 
487
        if ( getter )   // known var
 
488
        {
 
489
          static const RepoVars _repoVars;
 
490
          ret = &(_repoVars.*getter)();
171
491
        }
172
492
        return ret;
173
493
      }
176
496
 
177
497
    std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
178
498
    {
179
 
      return replacer( value );
 
499
      return RepoVarExpand()( value, repoVarLookup );
 
500
    }
 
501
    std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
 
502
    {
 
503
      return RepoVarExpand()( value, repoVarLookup );
180
504
    }
181
505
 
182
506
    Url RepoVariablesUrlReplacer::operator()( const Url & value ) const
183
507
    {
 
508
      RepoVarExpand expand;
184
509
      Url newurl( value );
185
 
      newurl.setPathData( replacer( value.getPathData() ) );
186
 
      newurl.setQueryString( replacer( value.getQueryString() ) );
 
510
      newurl.setPathData( expand( value.getPathData(), repoVarLookup ) );
 
511
      newurl.setQueryString( expand( value.getQueryString(), repoVarLookup ) );
187
512
      return newurl;
188
513
    }
189
514