From ba0353ce3c35bf7c940ad8be489e9b66aafc1ac7 Mon Sep 17 00:00:00 2001 From: Michael Andres Date: Wed, 24 Oct 2012 10:39:56 +0200 Subject: [PATCH] Add simple sysconfig::write (bnc#766598) --- tests/zypp/base/Sysconfig_test.cc | 44 ++++++++++++++++++- tests/zypp/base/data/Sysconfig/proxy | 4 +- zypp/base/Sysconfig.cc | 84 ++++++++++++++++++++++++++++++++++-- zypp/base/Sysconfig.h | 43 ++++++++++++++++++ 4 files changed, 169 insertions(+), 6 deletions(-) diff --git a/tests/zypp/base/Sysconfig_test.cc b/tests/zypp/base/Sysconfig_test.cc index 610555a..dfbd98f 100644 --- a/tests/zypp/base/Sysconfig_test.cc +++ b/tests/zypp/base/Sysconfig_test.cc @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -10,6 +11,7 @@ #include "zypp/base/Exception.h" #include "zypp/TmpPath.h" #include "zypp/PathInfo.h" +#include "zypp/ExternalProgram.h" #include "zypp/base/Sysconfig.h" @@ -24,7 +26,7 @@ using namespace zypp; BOOST_AUTO_TEST_CASE(Sysconfig) { - Pathname file = DATADIR + "proxy"; + Pathname file = DATADIR / "proxy"; map values = zypp::base::sysconfig::read(file); BOOST_CHECK_EQUAL( values.size(), 6 ); BOOST_CHECK_EQUAL( values["PROXY_ENABLED"], "no"); @@ -32,3 +34,43 @@ BOOST_AUTO_TEST_CASE(Sysconfig) BOOST_CHECK_EQUAL( values["NO_PROXY"], "localhost, 127.0.0.1"); } +BOOST_AUTO_TEST_CASE(SysconfigWrite) +{ + Pathname file = DATADIR / "proxy"; + filesystem::TmpFile tmpf( filesystem::TmpFile::makeSibling( file ) ); + filesystem::copy( file, tmpf.path() ); + + BOOST_REQUIRE_THROW( zypp::base::sysconfig::writeStringVal( "/tmp/wrzlprmpf", "PROXY_ENABLED", "yes", "# fifi\n fofo\n" ), + zypp::Exception ); + BOOST_CHECK( zypp::base::sysconfig::writeStringVal( tmpf.path(), "PROXY_ENABLED", "yes", "# fifi\n fofo\n" ) ); + BOOST_CHECK( !zypp::base::sysconfig::writeStringVal( tmpf.path(), "NEW1","12" ) ); + BOOST_CHECK( zypp::base::sysconfig::writeStringVal( tmpf.path(), "NEW2","13", "# fifi\n# fofo" ) ); + BOOST_CHECK( zypp::base::sysconfig::writeStringVal( tmpf.path(), "NEW3","13\"str\"", "fifi\nffofo" ) ); + + std::ostringstream s; + ExternalProgram( "diff -u " + file.asString() + " " + tmpf.path().asString() + " | tail -n +3" ) >> s; + BOOST_CHECK_EQUAL( s.str(), + "@@ -8,7 +8,7 @@\n" + " # This setting allows to turn the proxy on and off while\n" + " # preserving the particular proxy setup.\n" + " #\n" + "-PROXY_ENABLED=\"no\"\n" + "+PROXY_ENABLED=\"yes\"\n" + " \n" + " ## Type:\tstring\n" + " ## Default:\t\"\"\n" + "@@ -49,3 +49,11 @@\n" + " # Example: NO_PROXY=\"www.me.de, do.main, localhost\"\n" + " #\n" + " NO_PROXY=\"localhost, 127.0.0.1\"\n" + "+\n" + "+# fifi\n" + "+# fofo\n" + "+NEW2=\"13\"\n" + "+\n" + "+# fifi\n" + "+# ffofo\n" + "+NEW3=\"13\\\"str\\\"\"\n" + ); +} + diff --git a/tests/zypp/base/data/Sysconfig/proxy b/tests/zypp/base/data/Sysconfig/proxy index 0924e45..f3a6b9c 100644 --- a/tests/zypp/base/data/Sysconfig/proxy +++ b/tests/zypp/base/data/Sysconfig/proxy @@ -1,5 +1,5 @@ ## Path: Network/Proxy -## Description: +## Description: ## Type: yesno ## Default: no ## Config: kde,profiles @@ -7,7 +7,7 @@ # Enable a generation of the proxy settings to the profile. # This setting allows to turn the proxy on and off while # preserving the particular proxy setup. -# +# PROXY_ENABLED="no" ## Type: string diff --git a/zypp/base/Sysconfig.cc b/zypp/base/Sysconfig.cc index 253c44d..0d11ec4 100644 --- a/zypp/base/Sysconfig.cc +++ b/zypp/base/Sysconfig.cc @@ -15,7 +15,12 @@ #include "zypp/base/Logger.h" #include "zypp/base/String.h" +#include "zypp/base/StrMatcher.h" +#include "zypp/base/IOStream.h" +#include "zypp/base/InputStream.h" #include "zypp/Pathname.h" +#include "zypp/PathInfo.h" +#include "zypp/TmpPath.h" #include "zypp/base/Sysconfig.h" @@ -24,13 +29,13 @@ using namespace zypp::base; namespace zypp { namespace base { - namespace sysconfig { + map read( const Pathname & _path ) { DBG << "Load '" << _path << "'" << endl; map ret; - + string line; ifstream in( _path.asString().c_str() ); if ( in.fail() ) { @@ -68,10 +73,83 @@ namespace zypp { } // not comment } // while getline - MIL << "done reading '" << _path << "'" << endl; + MIL << "done reading '" << _path << "'" << endl; return ret; } + bool write( const Pathname & path_r, const std::string & key_r, const std::string & val_r, const std::string & newcomment_r ) + { + if ( key_r.empty() ) + { + WAR << "Empty key in write " << path_r << endl; + return false; + } + + PathInfo pi( path_r ); + if ( ! pi.isFile() ) + ZYPP_THROW( Exception( str::Str() << path_r << ": " << Errno(ENOENT) ) ); + if ( ! pi.userMayRW() ) + ZYPP_THROW( Exception( str::Str() << path_r << ": " << Errno(EACCES) ) ); + + bool found = false; + filesystem::TmpFile tmpf( filesystem::TmpFile::makeSibling( path_r ) ); + { + StrMatcher matches( "^[ \t]*"+key_r+"[ \t]*=", Match::REGEX ); + std::ofstream o( tmpf.path().c_str() ); + iostr::forEachLine( InputStream( path_r ), + [&]( int num_r, std::string line_r )->bool + { + if ( !found && matches( line_r ) ) + { + o << key_r << '=' << val_r << endl; + found = true; + MIL << path_r << ": " << key_r << '=' << val_r << " changed on line " << num_r << endl; + } + else + o << line_r << endl; + return true; + } ); + if ( !found ) + { + if ( newcomment_r.empty() ) + { + WAR << path_r << ": " << key_r << '=' << val_r << " can not be added (no comment provided)." << endl; + } + else + { + std::vector lines; + str::split( newcomment_r, std::back_inserter(lines), "\r\n" ); + o << endl; + for ( auto line : lines ) + { + if ( line[0] != '#' ) + o << "# "; + o << line << endl; + } + o << key_r << '=' << val_r << endl; + found = true; + MIL << path_r << ": " << key_r << '=' << val_r << " appended. " << endl; + } + } + + if ( ! o ) + ZYPP_THROW( Exception( str::Str() << tmpf.path() << ": " << Errno(EIO) ) ); + } + + // If everything is fine, exchange the files: + int res = exchange( tmpf.path(), path_r ); + if ( res ) + { + ZYPP_THROW( Exception( str::Str() << tmpf.path() << ": " << Errno(res) ) ); + } + return found; + } + + bool writeStringVal( const Pathname & path_r, const std::string & key_r, const std::string & val_r, const std::string & newcomment_r ) + { + return write( path_r, key_r, str::Str() << '"' << str::escape( val_r, '"' )<< '"', newcomment_r ); + } + } // namespace sysconfig } // namespace base } // namespace zypp diff --git a/zypp/base/Sysconfig.h b/zypp/base/Sysconfig.h index 5d511dc..c9ae7cb 100644 --- a/zypp/base/Sysconfig.h +++ b/zypp/base/Sysconfig.h @@ -20,8 +20,51 @@ namespace zypp { namespace base { namespace sysconfig { + /** Read sysconfig file \a path_r and return (key,valye) pairs. */ std::map read( const Pathname & _path ); + /** Add or change a value in sysconfig file \a path_r. + * + * If \a key_r already exists, only the \a val_r is changed accordingly. + * + * In case \a key_r is not yet present in the file, a new entry may be created + * at the end of the file, using the lines in \a newcomment_r as comment + * block. If \a newcomment_r is not provided or empty, a new value is not + * created and \c false is returned. + * + * \returns \c TRUE if an entry was changed or created. + * + * \throws Exception if \a path_r can not be read or written. + * + * \note \a val_r is written as it is. The caller is responsible for escaping and + * enclosing in '"', in case this is needed (\see \ref writeStringVal and \ref str::escape). + * + * \note Lines in \a newcomment_r which do not already start with a '#', + * are prefixes with "# ". + * + * \code + * ## Type: string + * ## Default: "" + * # + * # A multiline description of + * # the options purpose. + * # + * KEY="value" + * \endcode + */ + bool write( const Pathname & path_r, const std::string & key_r, const std::string & val_r, + const std::string & newcomment_r = std::string() ); + + /** Convenience to add or change a string-value in sysconfig file \a path_r. + * + * \a val_r is expected to be a plain string value, so it is propery escaped and enclosed in + * double quotes before it is written to the sysconfig file \a path_r. + * + * \see \ref write + */ + bool writeStringVal( const Pathname & path_r, const std::string & key_r, const std::string & val_r, + const std::string & newcomment_r = std::string() ); + } // namespace sysconfig } // namespace base } // namespace zypp -- 2.7.4