#include <iostream>
+#include <sstream>
#include <fstream>
#include <map>
#include <string>
#include "zypp/base/Exception.h"
#include "zypp/TmpPath.h"
#include "zypp/PathInfo.h"
+#include "zypp/ExternalProgram.h"
#include "zypp/base/Sysconfig.h"
BOOST_AUTO_TEST_CASE(Sysconfig)
{
- Pathname file = DATADIR + "proxy";
+ Pathname file = DATADIR / "proxy";
map<string,string> values = zypp::base::sysconfig::read(file);
BOOST_CHECK_EQUAL( values.size(), 6 );
BOOST_CHECK_EQUAL( values["PROXY_ENABLED"], "no");
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"
+ );
+}
+
## Path: Network/Proxy
-## Description:
+## Description:
## Type: yesno
## Default: no
## Config: kde,profiles
# 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
#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"
namespace zypp {
namespace base {
-
namespace sysconfig {
+
map<string,string> read( const Pathname & _path )
{
DBG << "Load '" << _path << "'" << endl;
map<string,string> ret;
-
+
string line;
ifstream in( _path.asString().c_str() );
if ( in.fail() ) {
} // 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<std::string> 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
namespace base {
namespace sysconfig {
+ /** Read sysconfig file \a path_r and return <tt>(key,valye)</tt> pairs. */
std::map<std::string,std::string> 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