MESSAGE( FATAL_ERROR "readline not found" )
ENDIF( READLINE_FOUND )
+FIND_PACKAGE( Augeas REQUIRED )
+IF( AUGEAS_FOUND )
+ INCLUDE_DIRECTORIES(${AUGEAS_INCLUDE_DIR})
+ELSE( AUGEAS_FOUND )
+ MESSAGE( FATAL_ERROR "augeas not found" )
+ENDIF( AUGEAS_FOUND )
+
+
ADD_SUBDIRECTORY( src )
ADD_SUBDIRECTORY( po )
ADD_SUBDIRECTORY( doc )
--- /dev/null
+# Find augeas library and tool
+#
+
+if(AUGEAS_INCLUDE_DIR AND AUGEAS_LIBRARY)
+ # Already in cache, be silent
+ set(AUGEAS_FIND_QUIETLY TRUE)
+endif(AUGEAS_INCLUDE_DIR AND AUGEAS_LIBRARY)
+
+set(AUGEAS_LIBRARY)
+set(AUGEAS_INCLUDE_DIR)
+
+FIND_PATH(AUGEAS_INCLUDE_DIR augeas.h
+ /usr/include
+ /usr/local/include
+)
+
+FIND_LIBRARY(AUGEAS_LIBRARY NAMES augeas
+ PATHS
+ /usr/lib
+ /usr/lib64
+ /usr/local/lib
+ /usr/local/lib64
+)
+
+if(AUGEAS_INCLUDE_DIR AND AUGEAS_LIBRARY)
+ MESSAGE( STATUS "augeas found: includes in ${AUGEAS_INCLUDE_DIR}, library in ${AUGEAS_LIBRARY}")
+ set(AUGEAS_FOUND TRUE)
+else(AUGEAS_INCLUDE_DIR AND AUGEAS_LIBRARY)
+ MESSAGE( STATUS "augeas not found")
+endif(AUGEAS_INCLUDE_DIR AND AUGEAS_LIBRARY)
+
+MARK_AS_ADVANCED(AUGEAS_INCLUDE_DIR AUGEAS_LIBRARY)
)
SET( zypper_utils_HEADERS
+ utils/Augeas.h
utils/colors.h
utils/getopt.h
utils/messages.h
)
SET( zypper_utils_SRCS
+ utils/Augeas.cc
utils/colors.cc
utils/getopt.cc
utils/messages.cc
)
ADD_EXECUTABLE( zypper ${zypper_SRCS} ${zypper_out_SRCS} ${zypper_utils_SRCS} )
-TARGET_LINK_LIBRARIES( zypper ${ZYPP_LIBRARY} ${READLINE_LIBRARY} )
+TARGET_LINK_LIBRARIES( zypper ${ZYPP_LIBRARY} ${READLINE_LIBRARY} -laugeas ${AUGEAS_LIBRARY} )
INSTALL(
TARGETS zypper
DESTINATION ${SYSCONFDIR}/logrotate.d
)
+# augeas lens
+INSTALL(
+ FILES utils/zypper.aug
+ DESTINATION ${INSTALL_PREFIX}/share/zypper
+)
--- /dev/null
+/*---------------------------------------------------------------------------*\
+ ____ _ _ __ _ __ ___ _ _
+ |_ / || | '_ \ '_ \/ -_) '_|
+ /__|\_, | .__/ .__/\___|_|
+ |__/|_| |_|
+\*---------------------------------------------------------------------------*/
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "Zypper.h"
+#include "utils/Augeas.h"
+
+using namespace zypp;
+using namespace std;
+
+Augeas::Augeas()
+ : _augeas(NULL), _got_global_zypper_conf(false), _got_user_zypper_conf(false)
+{
+ MIL << "Going to read zypper config using Augeas..." << endl;
+
+ //! \todo use specified root dir
+ _augeas = ::aug_init(NULL, "/usr/share/zypper", AUG_NO_STDINC);
+
+ if (_augeas == NULL)
+ ZYPP_THROW(Exception(_("Cannot initialize configuration file parser.")));
+
+
+ _got_global_zypper_conf =
+ ::aug_get(_augeas, "/files/etc/zypp/zypper.conf", NULL) != 0;
+
+ const char *value[1] = {};
+ string error;
+ if (::aug_get(_augeas, "/augeas/files/etc/zypp/zypper.conf/error/message", value))
+ error = value[0];
+
+ _homedir = ::getenv("HOME");
+ if (_homedir.empty())
+ WAR << "Cannot figure out user's home directory." << endl;
+ else
+ {
+ //! \todo cherry-pick this file as soon as the API allows it
+ string user_zypper_conf = "/files" + _homedir + "/.zypper.conf";
+ _got_user_zypper_conf =
+ ::aug_get(_augeas, user_zypper_conf.c_str(), NULL) != 0;
+ }
+
+ if (!_got_global_zypper_conf && !_got_user_zypper_conf)
+ {
+ if (error.empty())
+ ZYPP_THROW(Exception(
+ _("No configuration file exists or could be parsed.")));
+ else
+ {
+ string msg = _("Error parsing zypper.conf:") + string("\n") + error;
+ ZYPP_THROW(Exception(msg));
+ }
+ }
+
+ MIL << "Done reading conf files:" << endl;
+ MIL << "user conf read: " << (_got_user_zypper_conf ? "yes" : "no") << endl;
+ MIL << "global conf read: " << (_got_global_zypper_conf ? "yes" : "no") << endl;
+}
+
+// ---------------------------------------------------------------------------
+
+Augeas::~Augeas()
+{
+ if (_augeas != NULL)
+ ::aug_close(_augeas);
+}
+
+// ---------------------------------------------------------------------------
+
+static string global_option_path(
+ const string & section, const string & option)
+{
+ return "/files/etc/zypp/zypper.conf/" + section + "/*/" + option;
+}
+
+static string user_option_path(
+ const string & section, const string & option, const string & homedir)
+{
+ return "/files" + homedir + "/.zypper.conf/" + section + "/*/" + option;
+}
+
+// ---------------------------------------------------------------------------
+
+string Augeas::get(const string & augpath) const
+{
+ const char *value[1] = {};
+ _last_get_result = ::aug_get(_augeas, augpath.c_str(), value);
+ if (_last_get_result)
+ {
+ MIL << "Got " << augpath << " = " << value[0] << endl;
+ return value[0];
+ }
+ else if (_last_get_result == 0)
+ DBG << "No match for " << augpath << endl;
+ else
+ DBG << "Multiple matches for " << augpath << endl;
+
+ return string();
+}
+
+// ---------------------------------------------------------------------------
+
+string Augeas::getOption(const string & option) const
+{
+ vector<string> opt;
+ str::split(option, back_inserter(opt), "/");
+
+ if (opt.size() != 2 || opt[0].empty() || opt[1].empty())
+ {
+ ERR << "invalid option " << option << endl;
+ return string();
+ }
+
+ string augpath_u = user_option_path(opt[0], opt[1], _homedir);
+ string result = get(augpath_u);
+ if (_last_get_result && !isCommented(opt[0], opt[1], false))
+ return result;
+
+ string augpath_g = global_option_path(opt[0], opt[1]);
+ result = get(augpath_g);
+ if (_last_get_result && !isCommented(opt[0], opt[1], true))
+ return result;
+
+ return string();
+}
+
+// ---------------------------------------------------------------------------
+
+bool Augeas::isCommented(
+ const string & section, const string & option, bool global) const
+{
+ Pathname path(global ?
+ global_option_path(section, option) :
+ user_option_path(section, option, _homedir));
+
+ path = path.dirname() + "/commented";
+ if (::aug_get(_augeas, path.c_str(), NULL))
+ return true;
+
+ return false;
+}
+
+// ---------------------------------------------------------------------------
+
+bool Augeas::isCommented(const string & option, bool global) const
+{
+ vector<string> opt;
+ str::split(option, back_inserter(opt), "/");
+
+ if (opt.size() != 2 || opt[0].empty() || opt[1].empty())
+ {
+ ERR << "invalid option " << option << endl;
+ return false;
+ }
+
+ return isCommented(opt[0], opt[1], global);
+}
--- /dev/null
+/*---------------------------------------------------------------------------*\
+ ____ _ _ __ _ __ ___ _ _
+ |_ / || | '_ \ '_ \/ -_) '_|
+ /__|\_, | .__/ .__/\___|_|
+ |__/|_| |_|
+\*---------------------------------------------------------------------------*/
+
+#ifndef ZYPPER_UTIL_AUGEAS_H_
+#define ZYPPER_UTIL_AUGEAS_H_
+
+#include <string>
+
+extern "C"
+{
+ #include <augeas.h>
+}
+
+#include "zypp/base/NonCopyable.h"
+
+/**
+ * Zypper's wrapper around Augeas.
+ */
+class Augeas : private zypp::base::NonCopyable
+{
+public:
+ Augeas();
+ ~Augeas();
+
+ std::string get(const std::string & augpath) const;
+
+ std::string getOption(const std::string & option) const;
+ bool isCommented(const std::string & option, bool global) const;
+ void comment(const std::string & option);
+ void uncomment(const std::string & option);
+
+ ::augeas * getAugPtr()
+ { return _augeas; }
+
+private:
+ bool isCommented(const std::string & section, const std::string & option,
+ bool global) const;
+
+private:
+ ::augeas * _augeas;
+ std::string _homedir;
+ bool _got_global_zypper_conf;
+ bool _got_user_zypper_conf;
+
+ mutable bool _last_get_result;
+};
+
+#endif /* ZYPPER_UTIL_AUGEAS_H_ */
--- /dev/null
+(* Lens for parsing Zypper configuration file.
+
+About: License
+ This file is licensed under the GPLv2+, like the rest of Zypper.
+*)
+
+module ZYpper =
+ autoload xfm
+
+ (* ****************( primitives )*************** *)
+ (* These are taken from the official util.aug *)
+
+ (*
+ Variable: eol
+ Delete end of line, including optional trailing whitespace
+ *)
+ let eol = del /[ \t]*\n/ "\n"
+
+ (*
+ Variable: del_str
+ Delete a string and default to it
+
+ Parameters:
+ s:string - the string to delete and default to
+ *)
+ let del_str (s:string) = del s s
+
+ (*
+ Variable: del_opt_ws
+ Delete optional whitespace
+ *)
+ let del_opt_ws = del /[ \t]*/
+
+ (* Matches an empty line and creates a node for it *)
+ let empty = [ eol ]
+
+ (* Deletes optional whitespace and stores the rest 'till the end of line *)
+ let store_to_eol = del_opt_ws " " . store /([^ \t\n].*[^ \t\n])/
+
+ (*
+ Keyword regex.
+ Allows alphanumericals and '.' and '_'. Must start with a letter
+ and end with a letter or number.
+ *)
+ let kw_re = /[a-zA-Z][a-zA-Z0-9\._]*[a-zA-Z0-9]/
+
+ (* ****************( section )*************** *)
+
+ (* Matches one line of ## description and creates a node for it *)
+ let description = [ label "description" . del /##/ "##" . store_to_eol? . eol ]
+
+ (*
+ Matches '#' and whitespace, creates a 'commented' note for it.
+ Used in 'option' to mark the option as commented.
+ *)
+ let commented = [ label "commented" . del /#[ \t]*/ "# " ]
+
+ (* Matches key=value, creates a new node out of key and stores the value *)
+ let kv = [ key kw_re . del /[ \t]*=[ \t]*/ " = " . store /[^# \t\n][^#\n]*/ ]
+
+ (*
+ An option consists of ## description, and an (optionally commented)
+ key=value pair.
+
+ This could be relaxed a bit in the future to allow more commented
+ keys, arbitrary comments, etc.
+ *)
+ let option = [ seq "option" . description* . commented? . kv . del_str "\n" ]
+
+ (* ****************( section )*************** *)
+
+ (* Matches section [title] and creates a new tree node out of it *)
+ let section_title = del_str "[" . key /[^] \t\n\/]+/ . del_str "]" . eol
+
+ (* Section with it's contests *)
+ let section = [ section_title . (option | empty)* ]
+
+ (* Optional comments in the anonymous section (start of the file). *)
+ let section_anon = [ label "anon" . ( description | empty )+ ]
+
+
+ (* The lens matching and mapping the whole file *)
+ let lns = section_anon . section+
+
+ (*
+ Filter for the xfm transformation.
+ This is for system-wide zypper.conf only. ~/.zypper.conf must be
+ read on-demand only.
+ *)
+ let filter = (incl "/etc/zypp/zypper.conf")
+
+ (* Transfrom files matching 'filter' using lens 'lns' *)
+ let xfm = transform lns filter