#
SET(LIBZYPP_MAJOR "14")
SET(LIBZYPP_COMPATMINOR "30")
-SET(LIBZYPP_MINOR "36")
+SET(LIBZYPP_MINOR "37")
SET(LIBZYPP_PATCH "0")
#
-# LAST RELEASED: 14.36.0 (30)
+# LAST RELEASED: 14.37.0 (30)
# (The number in parenthesis is LIBZYPP_COMPATMINOR)
#=======
-------------------------------------------------------------------
+Wed Mar 11 09:22:46 CET 2015 - ma@suse.de
+
+- New RepoVarExpand: Functor expanding repo variables in a
+ string . Supports bash style default ${v:-w}' and alternate ${v:+w}
+ values (FATE#318354)
+- Easy.h: Use __typeof__ rather than typeof in header
+- Support repo variable replacement in service url
+- Support repo variable replacement in gpg url
+- Gettext.h: Fallback to ::gettext if accidentally included
+ outside libzypp
+- version 14.37.0 (30)
+
+-------------------------------------------------------------------
+Sun Feb 22 01:13:12 CET 2015 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Feb 19 01:13:13 CET 2015 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Sun Feb 15 01:13:28 CET 2015 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Feb 12 01:13:34 CET 2015 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
Mon Feb 9 16:05:38 CET 2015 - ma@suse.de
- Don't execute scripts in /tmp or /var/tmp, as they could be
-#include <stdio.h>
+#include <boost/test/auto_unit_test.hpp>
#include <iostream>
-#include <fstream>
-#include <vector>
#include <list>
-#include <boost/test/auto_unit_test.hpp>
+#include <map>
-#include "zypp/ZYppFactory.h"
-#include "zypp/Url.h"
-#include "zypp/PathInfo.h"
-#include "zypp/TmpPath.h"
#include "zypp/ZConfig.h"
+#include "zypp/Pathname.h"
+#include "zypp/Url.h"
#include "zypp/base/ValueTransform.h"
#include "zypp/repo/RepoVariables.h"
using std::cout;
using std::endl;
-using std::string;
using namespace zypp;
using namespace boost::unit_test;
-using namespace zypp::repo;
#define DATADIR (Pathname(TESTS_SRC_DIR) + "/repo/yum/data")
}
}
-// plain functor
+// A plain functor
struct PlainTransformator
{
std::string operator()( const std::string & value_r ) const
{ return "{"+value_r+"}"; }
};
-// plain functor + std::unary_function typedefs
+// plain functor + required std::unary_function typedefs
struct FncTransformator : public PlainTransformator, public std::unary_function<const std::string &, std::string>
{};
BOOST_CHECK_EQUAL( rl.transformed( rl.rawBegin() ), "{a}" );
}
+void helperGenRepVarExpandResults()
+{
+ // Generate test result strings for RepVarExpand:
+ // ( STRING, REPLACED_all_vars_undef, REPLACED_all_vars_defined )
+ // Crefully check whether new stings are correct before
+ // adding them to the testccse.
+ std::map<std::string,std::string> vartable;
+ std::map<std::string,std::pair<std::string,std::string>> result;
+ bool varsoff = true;
+
+ auto varLookup = [&vartable,&varsoff]( const std::string & name_r )->const std::string *
+ {
+ if ( varsoff )
+ return nullptr;
+ std::string & val( vartable[name_r] );
+ if ( val.empty() )
+ { val = "["+name_r+"]"; }
+ return &val;
+ };
+
+ for ( auto && value : {
+ ""
+ , "$"
+ , "$${}"
+ , "$_:"
+ , "$_A:"
+ , "$_A_:"
+ , "$_A_B:"
+ , "${_A_B}"
+ , "\\${_A_B}" // no escape on level 0
+ , "${_A_B\\}" // no close brace
+ , "${C:-a$Bba}"
+ , "${C:+a$Bba}"
+ , "${C:+a${B}ba}"
+ , "${C:+a\\$Bba}" // escape on level > 0; no var $Bba
+ , "${C:+a$Bba\\}" // escape on level > 0; no close brace C
+ , "${C:+a${B}ba}"
+ , "${C:+a\\${B}ba}" // escape on level > 0; no var ${B}
+ , "${C:+a${B\\}ba}" // escape on level > 0; no close brace B
+ , "${C:+a\\${B\\}ba}"
+ , "__${D:+\\$X--{${E:-==\\$X{o\\}==} }--}__\\${B}${}__"
+ , "__${D:+\\$X--{${E:-==\\$X{o\\}==}\\}--}__\\${B}${}__"
+ } ) {
+ varsoff = true;
+ result[value].first = repo::RepoVarExpand()( value, varLookup );
+ varsoff = false;
+ result[value].second = repo::RepoVarExpand()( value, varLookup );
+ }
+
+ for ( const auto & el : result )
+ {
+#define CSTR(STR) str::form( "%-40s", str::gsub( "\""+STR+"\"", "\\", "\\\\" ).c_str() )
+ cout << "RepVarExpandTest( " << CSTR(el.first) << ", " << CSTR(el.second.first) << ", " << CSTR(el.second.second) << " );" << endl;
+ }
+}
+
+void RepVarExpandTest( const std::string & string_r, const std::string & allUndef_r, const std::string & allDef_r )
+{
+ std::map<std::string,std::string> vartable;
+ bool varsoff = true;
+
+ auto varLookup = [&vartable,&varsoff]( const std::string & name_r )->const std::string *
+ {
+ if ( varsoff )
+ return nullptr;
+ std::string & val( vartable[name_r] );
+ if ( val.empty() )
+ { val = "["+name_r+"]"; }
+ return &val;
+ };
+
+ varsoff = true;
+ BOOST_CHECK_EQUAL( repo::RepoVarExpand()( string_r, varLookup ), allUndef_r );
+ varsoff = false;
+ BOOST_CHECK_EQUAL( repo::RepoVarExpand()( string_r, varLookup ), allDef_r );
+}
+
+BOOST_AUTO_TEST_CASE(RepVarExpand)
+{ // ( STRING , REPLACED_all_vars_undef , REPLACED_all_vars_defined )
+ RepVarExpandTest( "" , "" , "" );
+ RepVarExpandTest( "$" , "$" , "$" );
+ RepVarExpandTest( "$${}" , "$${}" , "$${}" );
+ RepVarExpandTest( "$_:" , "$_:" , "[_]:" );
+ RepVarExpandTest( "$_A:" , "$_A:" , "[_A]:" );
+ RepVarExpandTest( "$_A_:" , "$_A_:" , "[_A_]:" );
+ RepVarExpandTest( "$_A_B:" , "$_A_B:" , "[_A_B]:" );
+ RepVarExpandTest( "${C:+a$Bba\\}" , "${C:+a$Bba\\}" , "${C:+a[Bba]\\}" );
+ RepVarExpandTest( "${C:+a$Bba}" , "" , "a[Bba]" );
+ RepVarExpandTest( "${C:+a${B\\}ba}" , "${C:+a${B\\}ba}" , "${C:+a${B\\}ba}" );
+ RepVarExpandTest( "${C:+a${B}ba}" , "" , "a[B]ba" );
+ RepVarExpandTest( "${C:+a\\$Bba}" , "" , "a$Bba" );
+ RepVarExpandTest( "${C:+a\\${B\\}ba}" , "" , "a${B}ba" );
+ RepVarExpandTest( "${C:+a\\${B}ba}" , "ba}" , "a${Bba}" );
+ RepVarExpandTest( "${C:-a$Bba}" , "a$Bba" , "[C]" );
+ RepVarExpandTest( "${_A_B\\}" , "${_A_B\\}" , "${_A_B\\}" );
+ RepVarExpandTest( "${_A_B}" , "${_A_B}" , "[_A_B]" );
+ RepVarExpandTest( "\\${_A_B}" , "\\${_A_B}" , "\\[_A_B]" );
+ RepVarExpandTest( "__${D:+\\$X--{${E:-==\\$X{o\\}==} }--}__\\${B}${}__", "__--}__\\${B}${}__" , "__$X--{[E] --}__\\[B]${}__" );
+ RepVarExpandTest( "__${D:+\\$X--{${E:-==\\$X{o\\}==}\\}--}__\\${B}${}__", "____\\${B}${}__" , "__$X--{[E]}--__\\[B]${}__" );
+}
+
BOOST_AUTO_TEST_CASE(replace_text)
{
/* check RepoVariablesStringReplacer */
ZConfig::instance().setSystemArchitecture(Arch("i686"));
::setenv( "ZYPP_REPO_RELEASEVER", "13.2", 1 );
- RepoVariablesStringReplacer replacer1;
+ repo::RepoVariablesStringReplacer replacer1;
BOOST_CHECK_EQUAL( replacer1(""), "" );
BOOST_CHECK_EQUAL( replacer1("$"), "$" );
BOOST_CHECK_EQUAL( replacer1("$arc"), "$arc" );
BOOST_CHECK_EQUAL(replacer1("http://foo/$arch/bar"), "http://foo/i686/bar");
/* check RepoVariablesUrlReplacer */
- RepoVariablesUrlReplacer replacer2;
+ repo::RepoVariablesUrlReplacer replacer2;
BOOST_CHECK_EQUAL(replacer2(Url("ftp://user:secret@site.org/$arch/")).asCompleteString(),
"ftp://user:secret@site.org/i686/");
Pathname licenseTgz() const
{ return metadatapath.empty() ? Pathname() : metadatapath / path / "license.tar.gz"; }
- Url mirrorListUrl() const
- { return replacer(mirrorlist_url); }
-
- void mirrorListUrl( const Url & url_r )
- { mirrorlist_url = url_r; }
-
- const Url & rawMirrorListUrl() const
- { return mirrorlist_url; }
-
- const url_set & baseUrls() const
+ const RepoVariablesReplacedUrlList & baseUrls() const
{
- if ( _baseUrls.empty() && ! mirrorListUrl().asString().empty() )
+ const Url & mlurl( _mirrorListUrl.transformed() ); // Variables replaced!
+ if ( _baseUrls.empty() && ! mlurl.asString().empty() )
{
emptybaseurls = true;
DBG << "MetadataPath: " << metadatapath << endl;
- repo::RepoMirrorList rmurls( mirrorListUrl(), metadatapath );
- _baseUrls.insert( _baseUrls.end(), rmurls.getUrls().begin(), rmurls.getUrls().end() );
+ repo::RepoMirrorList rmurls( mlurl, metadatapath );
+ _baseUrls.raw().insert( _baseUrls.raw().end(), rmurls.getUrls().begin(), rmurls.getUrls().end() );
}
return _baseUrls;
}
- url_set & baseUrls()
+ RepoVariablesReplacedUrlList & baseUrls()
{ return _baseUrls; }
bool baseurl2dump() const
/////////////////////////////////////////////////////////////////
}
return( _keywords.find( keyword_r ) != _keywords.end() );
-
}
public:
TriBool gpgcheck;
TriBool keeppackages;
- Url gpgkey_url;
+ RepoVariablesReplacedUrl _gpgKeyUrl;
+ RepoVariablesReplacedUrl _mirrorListUrl;
repo::RepoType type;
Pathname path;
std::string service;
repo::RepoVariablesUrlReplacer replacer;
private:
- Url mirrorlist_url;
- mutable url_set _baseUrls;
+ mutable RepoVariablesReplacedUrlList _baseUrls;
mutable std::set<std::string> _keywords;
friend Impl * rwcowClone<Impl>( const Impl * rhs );
void RepoInfo::setGpgCheck( bool check )
{ _pimpl->gpgcheck = check; }
- void RepoInfo::setMirrorListUrl( const Url & url_r )
- { _pimpl->mirrorListUrl( url_r ); }
+ void RepoInfo::setMirrorListUrl( const Url & url_r ) // Raw
+ { _pimpl->_mirrorListUrl.raw() = url_r; }
void RepoInfo::setGpgKeyUrl( const Url & url_r )
- { _pimpl->gpgkey_url = url_r; }
+ { _pimpl->_gpgKeyUrl.raw() = url_r; }
void RepoInfo::addBaseUrl( const Url & url_r )
{
- for ( const auto & url : _pimpl->baseUrls() ) // unique!
+ for ( const auto & url : _pimpl->baseUrls().raw() ) // Raw unique!
if ( url == url_r )
return;
- _pimpl->baseUrls().push_back( url_r );
+ _pimpl->baseUrls().raw().push_back( url_r );
}
void RepoInfo::setBaseUrl( const Url & url_r )
{
- _pimpl->baseUrls().clear();
- _pimpl->baseUrls().push_back( url_r );
+ _pimpl->baseUrls().raw().clear();
+ _pimpl->baseUrls().raw().push_back( url_r );
}
void RepoInfo::setPath( const Pathname &path )
repo::RepoType RepoInfo::type() const
{ return _pimpl->type; }
- Url RepoInfo::mirrorListUrl() const
- { return _pimpl->mirrorListUrl(); }
+ Url RepoInfo::mirrorListUrl() const // Variables replaced!
+ { return _pimpl->_mirrorListUrl.transformed(); }
+
+ Url RepoInfo::rawMirrorListUrl() const // Raw
+ { return _pimpl->_mirrorListUrl.raw(); }
+
+ Url RepoInfo::gpgKeyUrl() const // Variables replaced!
+ { return _pimpl->_gpgKeyUrl.transformed(); }
- Url RepoInfo::rawMirrorListUrl() const
- { return _pimpl->rawMirrorListUrl(); }
+ Url RepoInfo::rawGpgKeyUrl() const // Raw
+ { return _pimpl->_gpgKeyUrl.raw(); }
- Url RepoInfo::gpgKeyUrl() const
- { return _pimpl->gpgkey_url; }
+ RepoInfo::url_set RepoInfo::baseUrls() const // Variables replaced!
+ { return _pimpl->baseUrls().transformed(); }
- RepoInfo::url_set RepoInfo::baseUrls() const
- { return url_set( baseUrlsBegin(), baseUrlsEnd() ); } // Variables replaced!
+ RepoInfo::url_set RepoInfo::rawBaseUrls() const // Raw
+ { return _pimpl->baseUrls().raw(); }
Pathname RepoInfo::path() const
{ return _pimpl->path; }
{ return _pimpl->targetDistro; }
Url RepoInfo::rawUrl() const
- { return( _pimpl->baseUrls().empty() ? Url() : *_pimpl->baseUrls().begin() ); }
+ { return( _pimpl->baseUrls().empty() ? Url() : *_pimpl->baseUrls().rawBegin() ); }
RepoInfo::urls_const_iterator RepoInfo::baseUrlsBegin() const
- {
- return make_transform_iterator( _pimpl->baseUrls().begin(),
- _pimpl->replacer );
- }
+ { return _pimpl->baseUrls().transformedBegin(); }
RepoInfo::urls_const_iterator RepoInfo::baseUrlsEnd() const
- {
- return make_transform_iterator( _pimpl->baseUrls().end(),
- _pimpl->replacer );
- }
+ { return _pimpl->baseUrls().transformedEnd(); }
RepoInfo::urls_size_type RepoInfo::baseUrlsSize() const
{ return _pimpl->baseUrls().size(); }
RepoInfoBase::dumpOn(str);
if ( _pimpl->baseurl2dump() )
{
- for ( const auto & url : _pimpl->baseUrls() )
+ for ( const auto & url : _pimpl->baseUrls().raw() )
{
str << "- url : " << url << std::endl;
}
str << tag_r << value_r << std::endl;
});
- strif( "- mirrorlist : ", _pimpl->rawMirrorListUrl().asString() );
+ strif( "- mirrorlist : ", rawMirrorListUrl().asString() );
strif( "- path : ", path().asString() );
str << "- type : " << type() << std::endl;
str << "- priority : " << priority() << std::endl;
str << "- gpgcheck : " << gpgCheck() << std::endl;
- strif( "- gpgkey : ", gpgKeyUrl().asString() );
+ strif( "- gpgkey : ", rawGpgKeyUrl().asString() );
if ( ! indeterminate(_pimpl->keeppackages) )
str << "- keeppackages: " << keepPackages() << std::endl;
{
str << "baseurl=";
std::string indent;
- for ( const auto & url : _pimpl->baseUrls() )
+ for ( const auto & url : _pimpl->baseUrls().raw() )
{
str << indent << url << endl;
if ( indent.empty() ) indent = " "; // "baseurl="
if ( ! _pimpl->path.empty() )
str << "path="<< path() << endl;
- if ( ! (_pimpl->rawMirrorListUrl().asString().empty()) )
- str << "mirrorlist=" << _pimpl->rawMirrorListUrl() << endl;
+ if ( ! (rawMirrorListUrl().asString().empty()) )
+ str << "mirrorlist=" << rawMirrorListUrl() << endl;
str << "type=" << type().asString() << endl;
if (!indeterminate(_pimpl->gpgcheck))
str << "gpgcheck=" << (gpgCheck() ? "1" : "0") << endl;
- if ( ! (gpgKeyUrl().asString().empty()) )
- str << "gpgkey=" <<gpgKeyUrl() << endl;
+
+ if ( ! (rawGpgKeyUrl().asString().empty()) )
+ str << "gpgkey=" << rawGpgKeyUrl() << endl;
if (!indeterminate(_pimpl->keeppackages))
str << "keeppackages=" << keepPackages() << endl;
* iterator that points at end of repository urls
*/
urls_const_iterator baseUrlsEnd() const;
+
/**
* Pars pro toto: The first repository url
*/
Url url() const
{ return( baseUrlsEmpty() ? Url() : *baseUrlsBegin()); }
/**
- * Pars pro toto: The first repository url (no variables replaced)
+ * Pars pro toto: The first repository raw url (no variables replaced)
*/
Url rawUrl() const;
+
/**
- * A Url under which the metadata are located, or a set of mirrors.
+ * The complete set of repository urls
*
- * This can't be empty in order the repository to be valid
- * unless the download of the mirror list succeeds and it
- * contains a valid url.
+ * These are either the configured baseurls, or if empty, the downloaded
+ * mirror list (\see \ref mirrorListUrl)
*/
url_set baseUrls() const;
/**
+ * The complete set of raw repository urls (no variables replaced)
+ */
+ url_set rawBaseUrls() const;
+
+ /**
* Add a base url. \see baseUrls
* \param url The base url for the repository.
*
void setPath( const Pathname &path );
/**
- * Url of a file which contains a list of Urls
- * If empty, the base url will be used.
+ * Url of a file which contains a list of repository urls
*/
Url mirrorListUrl() const;
/**
*/
Url gpgKeyUrl() const;
/**
+ * The raw gpgKeyUrl (no variables replaced).
+ */
+ Url rawGpgKeyUrl() const;
+ /**
* \short Key to use for gpg checking of this repository
*
* \param url Url to the key in ASCII armored format
// if the repo url was not set by the repoindex parser, set service's url
Url url;
if ( it->baseUrlsEmpty() )
- url = service.url();
+ url = service.rawUrl();
else
{
// service repo can contain only one URL now, so no need to iterate.
typedef ServiceInfo::ReposToDisable ReposToDisable;
public:
- Url url;
+ RepoVariablesReplacedUrl url;
repo::ServiceType type;
ReposToEnable reposToEnable;
ReposToDisable reposToDisable;
ServiceInfo::~ServiceInfo()
{}
- Url ServiceInfo::url() const { return _pimpl->url; }
- void ServiceInfo::setUrl( const Url& url ) { _pimpl->url = url; }
+ Url ServiceInfo::url() const // Variables replaced
+ { return _pimpl->url.transformed(); }
+
+ Url ServiceInfo::rawUrl() const // Raw
+ { return _pimpl->url.raw(); }
+
+ void ServiceInfo::setUrl( const Url& url ) // Raw
+ { _pimpl->url.raw() = url; }
repo::ServiceType ServiceInfo::type() const
{ return _pimpl->type; }
std::ostream & ServiceInfo::dumpAsIniOn( std::ostream & str ) const
{
RepoInfoBase::dumpAsIniOn(str)
- << "url = " << url() << endl
+ << "url = " << rawUrl() << endl
<< "type = " << type() << endl;
if ( ! repoStates().empty() )
{ /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : ServiceInfo
- //
- /** */
+ /// \class ServiceInfo
+ /// \brief Service data
+ ///
+ /// \note Name and Url are subject to repo variable replacement
+ /// (\see \ref RepoVariablesStringReplacer).
+ ///
class ServiceInfo : public repo::RepoInfoBase
{
public:
public:
- /**
- * Gets url to service
- *
- * \return url to service
- */
+ /** The service url */
Url url() const;
- /**
- * Sets url for this service
- *
- * \param url url to this service
- */
+ /** The service raw url (no variables replaced) */
+ Url rawUrl() const;
+
+ /** Set the service url (raw value) */
void setUrl( const Url& url );
- /**
- *
- */
+
+ /** Service type */
repo::ServiceType type() const;
- /**
- * Set service type.
- *
- * \param type the new type
- */
+ /** Set service type */
void setType( const repo::ServiceType & type );
+ /** Lazy init service type */
void setProbedType( const repo::ServiceType & t ) const;
* \endcode
*/
#ifndef __GXX_EXPERIMENTAL_CXX0X__
-#define for_(IT,BEG,END) for ( typeof(BEG) IT = BEG, _for_end = END; IT != _for_end; ++IT )
+#define for_(IT,BEG,END) for ( __typeof__(BEG) IT = BEG, _for_end = END; IT != _for_end; ++IT )
#else
#define for_(IT,BEG,END) for ( auto IT = BEG, _for_end = END; IT != _for_end; ++IT )
#endif
/** Just tag text for translation. */
#define N_(MSG) MSG
+#ifdef ZYPP_DLL //defined if zypp is compiled as DLL
+
/** Return translated text. */
#define _(MSG) ::zypp::gettext::dgettext( MSG )
/** Return translated text (plural form). */
#define _PL(MSG1,MSG2,N) ::zypp::gettext::dngettext( MSG1, MSG2, N )
+#else
+#define _(MSG) ::gettext( MSG )
+#define _PL(MSG1,MSG2,N) ::ngettext( MSG1, MSG2, N )
+#endif
+
///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////
TriBool _autorefresh;
std::string _alias;
std::string _escaped_alias;
- std::string _name;
+ RepoVariablesReplacedString _name;
Pathname _filepath;
public:
{ _pimpl->setAlias(alias); }
void RepoInfoBase::setName( const std::string &name )
- { _pimpl->_name = name; }
+ { _pimpl->_name.raw() = name; }
void RepoInfoBase::setFilepath( const Pathname &filepath )
{ _pimpl->_filepath = filepath; }
}
std::string RepoInfoBase::rawName() const
- { return _pimpl->_name; }
+ { return _pimpl->_name.raw(); }
std::string RepoInfoBase::label() const
{
| /_____||_| |_| |_| |
| |
\---------------------------------------------------------------------*/
+#include <cstring>
+
+#define ZYPP_DBG_VAREXPAND 0
+#if ( ZYPP_DBG_VAREXPAND )
+#warning ZYPP_DBG_VAREXPAND is on
+#include <iostream>
+#include <sstream>
+using std::cout;
+using std::endl;
+#endif // ZYPP_DBG_VAREXPAND
#include "zypp/base/LogTools.h"
#include "zypp/base/String.h"
namespace repo
{
///////////////////////////////////////////////////////////////////
+ // RepoVarExpand
+ ///////////////////////////////////////////////////////////////////
+ namespace
+ {
+ ///////////////////////////////////////////////////////////////////
+ /// \class FindVar
+ /// \brief Helper scanning for variable definitions in a string
+ ///////////////////////////////////////////////////////////////////
+ struct FindVar
+ {
+ bool _embedded; ///< A (formerly) embedded string may have esacped \c $, \c closebrace and \c backslash
+ const char * _sbeg; ///< start of string to scan
+ const char * _vbeg; ///< [$]{variable:-word} / [$]{variable} / if embedded also on [\\]
+ const char * _nbeg; ///< ${[v]ariable:-word} / ${[v]ariable}
+ const char * _nend; ///< ${variable[:]-word} / ${variable[}]
+ const char * _vend; ///< ${variable:-word}[] / ${variable}[]
+ const char * _send; ///< end of scan (next $ or nullptr if done)
+
+ FindVar( const std::string & str_r, bool embedded_r )
+ : _embedded( embedded_r )
+ , _sbeg( str_r.c_str() )
+ , _vbeg( nullptr )
+ , _nbeg( nullptr )
+ , _nend( nullptr )
+ , _vend( nullptr )
+ , _send( findVarStart( _sbeg ) )
+ {}
+
+ /** Nullptr in _send indicates we scanned the whole string. */
+ bool done() const
+ { return !_send; }
+
+ /** Advance to first/next var if there is one */
+ bool nextVar()
+ {
+ if ( done() )
+ return false;
+
+ do {
+ if ( _vbeg && !_vend ) // loop internal: no findVarEnd at current $; skip it
+ _send = findVarStart( _vbeg+1 );
+ _vbeg = _send; // next $ or null if string end
+ _nbeg = _nend = _vend = _send = nullptr;
+ if ( ! _vbeg ) // done!
+ return false;
+ } while( ! findVarEnd() );
+
+ return true;
+ }
+
+ /** Valid _vend indicates valid var data in scan. */
+ bool hasVar() const
+ { return _vend; }
+
+ //
+ // Methods below are only valid if hasVar() == true
+ //
+
+ /** Return the full var text */
+ std::string var() const
+ { return std::string( _vbeg, _vend ); }
+
+ /** Return the var name */
+ std::string varName() const
+ { return std::string( _nbeg, _nend ); }
+
+ /** Whether this is a conditional var (${..:[+-]...}) */
+ bool varIsConditional() const
+ { return( *(_vbeg+1) == '{' && *_nend == ':' ); }
+
+ /** The var type: \c \, \c $, \c - , \c +
+ * \li \c \ backslash escaped literal
+ * \li \c $ plain variable
+ * \li \c - conditional: default value
+ * \li \c + conditional: alternate value
+ */
+ int varType() const
+ { return( varIsConditional() ? *(_nend+1) : *_vbeg ); }
+
+ /** Return embedded value in conditional vars or empty string */
+ std::string varEmbedded() const
+ { return( varIsConditional() ? std::string( _nend+2, _vend-1 ) : std::string() ); }
+
+
+ /** Have unwritten data before var? */
+ bool hasVarPrefix() const
+ { return ( _sbeg != _vbeg ); }
+
+ /** Return unwritten data before var */
+ std::string varPrefix() const
+ { return std::string( _sbeg, _vbeg ); }
+
+ /** Indicate all data up to _vend were written */
+ void wroteVar()
+ { _sbeg = _vend; }
+
+ private:
+ /** Return next \c $ */
+ const char * findVarStart( const char * sbeg_r ) const
+ {
+ for ( ; *sbeg_r; ++sbeg_r )
+ if ( *sbeg_r == '$' || ( _embedded && *sbeg_r == '\\' ) )
+ return sbeg_r;
+ return nullptr;
+ }
+
+ /** Valid var name char */
+ bool isnamech( int ch ) const
+ { return ch == '_' || isalpha( ch ); }
+
+ /** Scan for a valid variable starting at _vbeg (storing the values) */
+ bool findVarEnd()
+ {
+ // asserted: *_vbeg == '$' || '\\'
+ if ( ! findVarEnd( _vbeg, _nbeg, _nend, _vend ) )
+ return false;
+ _send = findVarStart( _vend );
+ return true;
+ }
+
+ /** Skip over valid variable starting at vbeg (return end in \a vend). */
+ const char * findVarEnd( const char * vbeg ) const
+ {
+ // asserted: *_vbeg == '$'
+ const char * nbeg = nullptr;
+ const char * nend = nullptr;
+ const char * vend = nullptr;
+ findVarEnd( vbeg, nbeg, nend, vend );
+ return vend;
+ }
+
+ /** Scan for a valid variable starting at vbeg (const version returning the values). */
+ bool findVarEnd( const char * vbeg, const char *& nbeg, const char *& nend, const char *& vend ) const
+ {
+ // embedded only: handle backslash escaped chars
+ if ( *_vbeg == '\\' )
+ {
+ nbeg = vbeg+1;
+ if ( *nbeg == '$'
+ || *nbeg == '}'
+ || *nbeg == '\\' )
+ {
+ nend = vend = vbeg+2;
+ return true;
+ }
+ return false;
+ }
+
+ // asserted: *vbeg == '$'
+ // vbeg: [$]{variable:-word} / [$]{variable}
+ // nbeg: ${[v]ariable:-word} / ${[v]ariable}
+ bool braced = ( *(vbeg+1) == '{' ); //}
+ nbeg = vbeg+( braced ? 2 : 1 );
+ if ( !isnamech( *nbeg ) ) // don't allow empty var name
+ return false;
+ for ( nend = nbeg+1; isnamech( *nend ); ++nend )
+ {;} // skip over var name
+ // nend: ${variable[:]-word} / ${variable[}]
+
+ // vend: ${variable:-word}[] / ${variable}[]
+ // stay with ( vend == nullptr ) until you know it's valid
+ if ( braced )
+ {
+ if ( *nend == '}' )
+ {
+ vend = nend+1;
+ }
+ else if ( *nend == ':' )
+ {
+ const char * scan = nend+1;
+ if ( *scan == '+' || *scan == '-' )
+ {
+ ++scan;
+ // find first not escaped '}'
+ while ( *scan )
+ {
+ if ( *scan == '\\' )
+ {
+ ++scan; // next char is skipped
+ if ( *scan )
+ ++scan;
+ }
+ else if ( *scan == '$' )
+ {
+ // an embedded var?
+ if ( ! (scan = findVarEnd( scan )) )
+ return false;
+ }
+ else if ( *scan == '}' )
+ {
+ vend = scan+1; // ==> unesacped '}', we're done!
+ break;
+ }
+ else
+ ++scan; // literal
+ }
+ // ( ! *scan ) => end of string while looking for unesacped '}'
+ }
+ else
+ ; // err: ':' not followed by '+' or '-'
+ }
+ else
+ ; // err: braced name must end with '}' or ':'
+ }
+ else
+ {
+ vend = nend; // un-braced
+ }
+ return( vend != nullptr );
+ }
+ };
+
+ bool _expand( std::string &, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r );
+
+ inline std::string expand( const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
+ {
+ std::string ret;
+ if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
+ ret = value_r;
+ return ret;
+ }
+
+ inline std::string expand( std::string && value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
+ {
+ std::string ret;
+ if ( ! _expand( ret, value_r, level_r, varRetriever_r ) )
+ ret = std::move(value_r);
+ return ret;
+ }
+
+ /** Expand variables in \a value_r depending on \a level-r
+ * <tt>level_r > 0</tt> may have escaped chars outside braces.
+ */
+ inline bool _expand( std::string & result_r, const std::string & value_r, unsigned level_r, RepoVarExpand::VarRetriever & varRetriever_r )
+ {
+#if ( ZYPP_DBG_VAREXPAND )
+ cout << std::string( 2*level_r, ' ' ) << "\033[7m>>" << value_r << "<<\033[27m" << endl;
+ std::ostringstream dbg;
+ const char * dbgsbeg = value_r.c_str(); // track vars we already added to dbg
+ unsigned dbgi = 0; // color 1-5 var / 6 moved value_r
+ dbg << std::string( 2*level_r, ' ' ) << ">>";
+#endif // ZYPP_DBG_VAREXPAND
+
+ bool expanded = false;
+
+ if ( ! value_r.empty() )
+ {
+ FindVar scan( value_r, level_r ); // level_r > 0 is embedded
+ while ( scan.nextVar() )
+ {
+ static const std::string _emptyValue;
+ const std::string *const knownVar = ( varRetriever_r ? varRetriever_r( scan.varName() ) : nullptr );
+ const std::string & varValue( knownVar ? *knownVar : _emptyValue );
+
+#if ( ZYPP_DBG_VAREXPAND )
+ dbg << std::string(dbgsbeg,scan._vbeg) << "\033[3" << ((dbgi%5)+1) << "m" << scan.var() << "\033[0m";
+ cout << dbg.str() << "|<< " << scan.varName() << " " << (knownVar?"("+varValue+")":"-") << " {" << scan.varEmbedded() << "}" << endl;
+ dbgsbeg = scan._vend;
+ dbgi++;
+#endif // ZYPP_DBG_VAREXPAND
+
+ bool mustSubstitute = false; // keep original text per default
+ std::string substitutionValue;
+
+ int varType = scan.varType();
+ if ( varType == '$' ) // plain var
+ {
+ if ( knownVar )
+ {
+ mustSubstitute = true;
+ substitutionValue = varValue;
+ }
+ else
+ ; // keep original text per default
+ }
+ else if ( varType == '-' ) // ':-' default value
+ {
+ mustSubstitute = true;
+ if ( varValue.empty() )
+ substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
+ else
+ substitutionValue = varValue;
+ }
+ else if ( varType == '+' ) // ':+' alternate value
+ {
+ mustSubstitute = true;
+ if ( ! varValue.empty() )
+ substitutionValue = expand( scan.varEmbedded(), level_r+1, varRetriever_r );
+ else
+ ; // empty substitutionValue
+ }
+ else if ( varType == '\\' ) // backslash escaped literal (in varName)
+ {
+ mustSubstitute = true;
+ substitutionValue = scan.varName();
+ }
+ else
+ ; // keep original text per default
+
+ if ( mustSubstitute )
+ {
+ if ( scan.hasVarPrefix() )
+ result_r += scan.varPrefix();
+ if ( ! substitutionValue.empty() )
+ result_r += substitutionValue;
+ scan.wroteVar(); // this moves scan._sbeg so we can later see what's already written
+ }
+ }
+
+#if ( ZYPP_DBG_VAREXPAND )
+ dbg << std::string( dbgsbeg ) << (scan._sbeg == value_r.c_str() ? "<<\033[36m(moved)\033[0m" : "");
+#endif // ZYPP_DBG_VAREXPAND
+
+ // handle unwritten data:
+ if ( scan._sbeg != value_r.c_str() )
+ {
+ expanded = true;
+ if ( *scan._sbeg )
+ result_r += std::string( scan._sbeg );
+ }
+ else
+ ; // no replacements at all
+ }
+
+#if ( ZYPP_DBG_VAREXPAND )
+ dbg << "<<";
+ cout << dbg.str() << endl;
+ cout << std::string( 2*level_r, ' ' ) << "\033[36m->" << result_r << "<-\033[0m" << endl;
+#endif // ZYPP_DBG_VAREXPAND
+ return expanded;
+ }
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ std::string RepoVarExpand::operator()( const std::string & value_r, VarRetriever varRetriever_r ) const
+ { return expand( value_r, 0, varRetriever_r ); }
+
+ std::string RepoVarExpand::operator()( std::string && value_r, VarRetriever varRetriever_r ) const
+ { return expand( std::move(value_r), 0, varRetriever_r ); }
+
+ ///////////////////////////////////////////////////////////////////
+ // RepoVariables*Replace
+ ///////////////////////////////////////////////////////////////////
namespace
{
/** \brief Provide lazy initialized repo variables
*/
- struct ReplacerData : private zypp::base::NonCopyable
+ struct RepoVars : private zypp::base::NonCopyable
{
- typedef const std::string & (ReplacerData::*Getter)() const;
+ typedef const std::string & (RepoVars::*Getter)() const;
const std::string & arch() const
{
_basearch = arch.baseArch().asString();
}
}
+
void assertReleaseverStr() const
{
if ( _releasever.empty() )
mutable std::string _releaseverMinor;
};
- /** \brief Replace repo variables on demand
- *
- * Initialisation of repo variables is delayed until they actually occur in
- * a string.
- */
- std::string replacer( std::string value_r )
+ /** \brief */
+ const std::string * repoVarLookup( const std::string & name_r )
{
- std::string ret;
- if ( ! value_r.empty() )
+ RepoVars::Getter getter = nullptr;
+ switch ( name_r.size() )
{
- static const str::regex rxVAR( "^([^$]*)\\$(\\{[[:alnum:]_]+\\}|[[:alnum:]_]+)([^$]*)" );
- str::smatch what;
- while ( str::regex_match( value_r, what, rxVAR ) )
- {
- ReplacerData::Getter getter = nullptr;
-
- const char * varStart = value_r.c_str() + what.begin( 2 );
- std::string::size_type varSize = what.size( 2 );
- if ( *varStart == '{' ) // enclosed in {}
- {
- ++varStart;
- varSize -= 2;
- }
-
- switch ( varSize )
- {
-#define ASSIGN_IF(NAME,GETTER) if ( ::strncmp( varStart, NAME, varSize ) == 0 ) getter = GETTER
-
- case 4: ASSIGN_IF( "arch", &ReplacerData::arch ); break;
- case 8: ASSIGN_IF( "basearch", &ReplacerData::basearch ); break;
- case 10: ASSIGN_IF( "releasever", &ReplacerData::releasever ); break;
- case 16: ASSIGN_IF( "releasever_major", &ReplacerData::releaseverMajor );
- else ASSIGN_IF( "releasever_minor", &ReplacerData::releaseverMinor ); break;
+#define ASSIGN_IF(NAME,GETTER) if ( name_r == NAME ) getter = GETTER
+ case 4: ASSIGN_IF( "arch", &RepoVars::arch ); break;
+ case 8: ASSIGN_IF( "basearch", &RepoVars::basearch ); break;
+ case 10: ASSIGN_IF( "releasever", &RepoVars::releasever ); break;
+ case 16: ASSIGN_IF( "releasever_major", &RepoVars::releaseverMajor );
+ else ASSIGN_IF( "releasever_minor", &RepoVars::releaseverMinor ); break;
#undef ASSIGN_IF
- }
-
- if ( getter ) // known var?
- {
- static const ReplacerData _data;
- if ( what.size( 1 ) > 0 ) ret += what[1]; // pre
- ret += (_data.*getter)(); // var
- if ( what.size( 3 ) > 0 ) ret += what[3]; // post
- }
- else
- {
- ret += what[0]; // unchanged
- }
+ }
- value_r.erase( 0, what.size( 0 ) );
- if ( value_r.empty() )
- break;
- }
- if ( ! value_r.empty() )
- ret += std::move(value_r); // no match
+ const std::string * ret = nullptr;
+ if ( getter ) // known var
+ {
+ static const RepoVars _repoVars;
+ ret = &(_repoVars.*getter)();
}
return ret;
}
std::string RepoVariablesStringReplacer::operator()( const std::string & value ) const
{
- return replacer( value );
+ return RepoVarExpand()( value, repoVarLookup );
+ }
+ std::string RepoVariablesStringReplacer::operator()( std::string && value ) const
+ {
+ return RepoVarExpand()( value, repoVarLookup );
}
Url RepoVariablesUrlReplacer::operator()( const Url & value ) const
{
+ RepoVarExpand expand;
Url newurl( value );
- newurl.setPathData( replacer( value.getPathData() ) );
- newurl.setQueryString( replacer( value.getQueryString() ) );
+ newurl.setPathData( expand( value.getPathData(), repoVarLookup ) );
+ newurl.setQueryString( expand( value.getQueryString(), repoVarLookup ) );
return newurl;
}
| /_____||_| |_| |_| |
| |
\---------------------------------------------------------------------*/
-
+/** \file zypp/repo/RepoVariables.h
+ */
#ifndef ZYPP_REPO_VARIABLES_H_
#define ZYPP_REPO_VARIABLES_H_
#include <string>
+#include "zypp/base/Function.h"
+#include "zypp/base/ValueTransform.h"
#include "zypp/Url.h"
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
namespace repo
{
+ ///////////////////////////////////////////////////////////////////
+ /// \class RepoVarExpand
+ /// \brief Functor expanding repo variables in a string
+ ///
+ /// Known variables are determined by a callback function taking a variable
+ /// name and returning a pointer to the variable value or \c nullptr if unset.
+ ///
+ /// The \c $ character introduces variable expansion. A valid variable name
+ /// is any non-empty case-insensitive sequence of <tt>[[:alnum:]_]</tt>.
+ /// The variable name to be expanded may be enclosed in braces, which are
+ /// optional but serve to protect the variable to be expanded from characters
+ /// immediately following it which could be interpreted as part of the name.
+ ///
+ /// When braces are used, the matching ending brace is the first \c } not
+ /// escaped by a backslash and not within an embedded variable expansion.
+ /// Within braces only \c $, \c } and \c backslash are escaped by a
+ /// backslash. There is no escaping outside braces, to stay comaptible
+ /// with \c YUM (which does not support braces).
+ ///
+ /// <ul>
+ /// <li> \c ${variable}
+ /// If \c variable is unset the original is preserved like in \c YUM.
+ /// Otherwise, the value of \c variable is substituted.</li>
+ ///
+ /// <li> \c ${variable:-word} (default value)
+ /// If \c variable is unset or empty, the expansion of \c word is substituted.
+ /// Otherwise, the value of \c variable is substituted.</li>
+ ///
+ /// <li> \c ${variable:+word} (alternate value)
+ /// If variable is unset or empty nothing is substituted.
+ /// Otherwise, the expansion of \c word is substituted.</li>
+ /// </ul>
+ struct RepoVarExpand
+ {
+ /** Function taking a variable name and returning a pointer to the variable value or \c nullptr if unset. */
+ typedef function<const std::string * ( const std::string & )> VarRetriever;
+
+ /** Return a copy of \a value_r with embedded variables expanded. */
+ std::string operator()( const std::string & value_r, VarRetriever varRetriever_r ) const;
+#ifndef SWIG // Swig treats it as syntax error
+ /** \overload moving */
+ std::string operator()( std::string && value_r, VarRetriever varRetriever_r ) const;
+#endif
+ };
/**
* \short Functor replacing repository variables
* ftp://user:secret@site.net/$arch/ -> ftp://user:secret@site.net/i686/
* http://site.net/?basearch=$basearch -> http://site.net/?basearch=i386
* \endcode
+ *
+ * \see \ref RepoVarExpand for supported variable syntax.
*/
struct RepoVariablesStringReplacer : public std::unary_function<const std::string &, std::string>
{
std::string operator()( const std::string & value_r ) const;
+#ifndef SWIG // Swig treats it as syntax error
+ /** \overload moving */
+ std::string operator()( std::string && value_r ) const;
+#endif
};
/**
{
Url operator()( const Url & url_r ) const;
};
-
} // namespace repo
///////////////////////////////////////////////////////////////////
+
+ /** \relates RepoVariablesStringReplacer Helper managing repo variables replaced strings */
+ typedef base::ValueTransform<std::string, repo::RepoVariablesStringReplacer> RepoVariablesReplacedString;
+
+ /** \relates RepoVariablesStringReplacer Helper managing repo variables replaced string lists */
+ typedef base::ContainerTransform<std::list<std::string>, repo::RepoVariablesStringReplacer> RepoVariablesReplacedStringList;
+
+ /** \relates RepoVariablesUrlReplacer Helper managing repo variables replaced urls */
+ typedef base::ValueTransform<Url, repo::RepoVariablesUrlReplacer> RepoVariablesReplacedUrl;
+
+ /** \relates RepoVariablesUrlReplacer Helper managing repo variables replaced url lists */
+ typedef base::ContainerTransform<std::list<Url>, repo::RepoVariablesUrlReplacer> RepoVariablesReplacedUrlList;
+
} // namespace zypp
///////////////////////////////////////////////////////////////////
, _allowuninstall(false)
, _updatesystem(false)
, _noupdateprovide(false)
- , _dosplitprovides(false)
+ , _dosplitprovides(true)
, _onlyRequires(ZConfig::instance().solver_onlyRequires())
, _ignorealreadyrecommended(true)
, _distupgrade(false)
#include "zypp/ZYppCallbacks.h"
#include "zypp/ExternalProgram.h"
#include "zypp/target/rpm/RpmHeader.h"
-
+#include "zypp/ZConfig.h"
using std::endl;
#undef ZYPP_BASE_LOGGER_LOGGROUP