# have all needed files, that a GNU package needs
AUTOMAKE_OPTIONS = foreign 1.4
-SUBDIRS = src test
+SUBDIRS = src configlib test
# Install the pkg-config file:
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libi2ncommon.pc
+pkgconfig_DATA = libi2ncommon.pc libi2ncommon_config.pc
if AUTOCHECK
all: config.h
--- /dev/null
+# set the include path found by configure
+INCLUDES = -I$(top_srcdir)/src $(all_includes) @LIBGETTEXT_CFLAGS@ \
+ @LIBICONV_CFLAGS@
+
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libi2ncommon_config.la
+
+include_HEADERS = i2n_global_config.hpp
+
+# Note: If you specify a:b:c as the version in the next line,
+# the library that is made has version (a-c).c.b. In this
+# example, the version is 2.1.2. (3:2:1)
+
+libi2ncommon_config_la_LDFLAGS = -version-info @LIBI2NCOMMON_LIB_VERSION@
+
+libi2ncommon_config_la_LIBADD =
+libi2ncommon_config_la_SOURCES = i2n_global_config.cpp
--- /dev/null
+/** @file
+ *
+ * @author Reinhard Pfau \<Reinhard.Pfau@intra2net.com\>
+ *
+ * @copyright © Copyright 2008 by Intra2net AG
+ * @license commercial
+ * @contact info@intra2net.com
+ */
+
+#include "i2n_global_config.hpp"
+
+
+#include <set>
+#include <map>
+
+#include <filefunc.hxx>
+#include <stringfunc.hxx>
+#include <i2n_configdata.hpp>
+#include <i2n_configfile.hpp>
+#include <logfunc.hpp>
+#include <tracefunc.hpp>
+
+//#define NOISEDEBUG
+#undef NOISEDEBUG
+
+#ifdef NOISEDEBUG
+#include <iostream>
+#include <iomanip>
+#define DOUT(msg) std::cout << msg << std::endl
+#else
+#define DOUT(msg) do {} while (0)
+#endif
+
+
+namespace I2n
+{
+
+namespace Config
+{
+
+
+namespace Detail
+{
+
+
+} // eo namespace Detail
+
+
+
+namespace
+{
+
+
+Logger::PartLogger& module_logger()
+{
+ static Logger::PartLogger _logger(HERE);
+ return _logger;
+} // eo module_logger()
+
+
+
+std::string config_file_name;
+
+ConfigData config_data;
+
+
+/*
+**
+*/
+
+
+struct HideOut
+: public Detail::Thing
+{
+ HideOut(
+ const std::string& section,
+ const std::string& key,
+ const Detail::FeedFunction& feed,
+ const Detail::FeedFunction2& feed2);
+
+ virtual ~HideOut();
+
+ Detail::FeedFunction m_feed;
+ Detail::FeedFunction2 m_feed2;
+ std::string m_section;
+ std::string m_key;
+}; // eo struct HideOut
+
+typedef boost::shared_ptr< HideOut > HideOutPtr;
+
+
+class HideOutMap
+: public GroupedKeyValueData<
+ std::string,
+ std::string,
+ HideOut*
+>
+{
+ public:
+ HideOutMap();
+ virtual ~HideOutMap();
+
+ public:
+
+ static int Instances;
+}; // eo class HideOutMap
+
+int HideOutMap::Instances= 0;
+
+HideOutMap::HideOutMap()
+{
+ DOUT("HideOutMap::HideOutMap()");
+ ++Instances;
+} // eo HideOutMap::HideOutMap()
+
+
+HideOutMap::~HideOutMap()
+{
+ DOUT("HideOutMap::~HideOutMap()");
+ --Instances;
+} // eo HideOutMap::~HideOutMap()
+
+
+HideOutMap& hideout_map()
+{
+ static HideOutMap the_hideout_map;
+ DOUT("hideout_map()");
+ return the_hideout_map;
+} // eo hideout_map()
+
+
+// implementation of HideOut:
+
+
+HideOut::HideOut(
+ const std::string& section,
+ const std::string& key,
+ const Detail::FeedFunction& feed,
+ const Detail::FeedFunction2& feed2)
+: m_section(section)
+, m_key(key)
+, m_feed(feed)
+, m_feed2(feed2)
+{
+ hideout_map()[section][key]+= this;
+} // eo HideOut::HideOut(const FeedFunction&)
+
+
+
+HideOut::~HideOut()
+{
+ if (HideOutMap::Instances)
+ {
+ hideout_map()[m_section].removeValue(m_key,this);
+ }
+} // eo HideOut::~HideOut()
+
+
+
+/*
+**
+*/
+
+
+/**
+ * @brief distributes (new) values to the active config variables.
+ * @param old_config_data reference to the old config data (for determining the deltas).
+ * @return @a true if new values could be converted without an error.
+ */
+bool feedEm( ConfigData& old_config_data )
+{
+ SCOPETRACKER();
+ bool result= true;
+ HideOutMap::KeyListType section_list;
+ hideout_map().getKeys(section_list);
+ for(HideOutMap::KeyListType::const_iterator section_it = section_list.begin();
+ section_it != section_list.end();
+ ++section_it)
+ {
+ std::string section( *section_it );
+ DOUT("process section \"" << section << "\"");
+ HideOutMap::GroupDataType::KeyListType key_list;
+ hideout_map()[section].getKeys(key_list);
+ for(HideOutMap::GroupDataType::KeyListType::const_iterator key_it= key_list.begin();
+ key_it != key_list.end();
+ ++key_it)
+ {
+ std::string key( *key_it );
+ DOUT("process key \"" << key << "\"");
+ bool has_new_data= config_data.hasKey(section, key);
+ bool has_old_data= old_config_data.hasKey(section, key);
+ if (not has_new_data and not has_old_data)
+ {
+ // we don't have data and we didn't had data... well, nothing to do:
+ DOUT("no data");
+ continue;
+ }
+ if (has_new_data)
+ {
+ DOUT("has (new) data");
+ StringList values;
+ config_data[section].getValues(key, values);
+ std::string value( config_data[section][key] );
+ if (has_old_data)
+ {
+ StringList old_values;
+ old_config_data[section].getValues(key, old_values);
+ if (values == old_values)
+ {
+ // value didn't change, so we can skip this case.
+ DOUT("data is same as before");
+ continue;
+ }
+ }
+ // at this point we need to publish (new or changed) value:
+ HideOutMap::GroupDataType::ValueListType func_list;
+ hideout_map()[section].getValues(key, func_list);
+ for(HideOutMap::GroupDataType::ValueListType::iterator func_it= func_list.begin();
+ func_it != func_list.end();
+ ++func_it)
+ {
+ DOUT(" feed iteration");
+ if (not (*func_it)) //paranoia
+ {
+ continue;
+ }
+ if ( (*func_it)->m_feed)
+ {
+ if ( not (*func_it)->m_feed( value ) )
+ {
+ module_logger().error() << "unable to publish [" <<section << "]"
+ << " \"" << key << "\" : \"" << value << "\""
+ ;
+ result= false;
+ }
+ continue;
+ }
+ if ( (*func_it)->m_feed2)
+ {
+ if ( not (*func_it)->m_feed2( values ) )
+ {
+ module_logger().error() << "unable to publish [" <<section << "]"
+ << " \"" << key << "\" : ...\"" << value << "\""
+ ;
+ result= false;
+ }
+ continue;
+ }
+ module_logger().error() << "no feeder";
+ }
+ continue;
+ }
+ // at this point we don't have (new) data, but we had old data...
+ //TODO how to handle this case? (reset to initial value??)
+ // for now, we ignore it.
+ }
+ }
+ return result;
+} // eo feedEm(ConfigData&,ConfigData&)
+
+
+/*
+**
+*/
+
+
+} // eo namespace <anonymous>
+
+
+/************************************************************************\
+\************************************************************************/
+
+
+namespace Detail
+{
+
+
+FlowerShop seymour(
+ const std::string& section,
+ const std::string& key,
+ const FeedFunction& feed)
+{
+ DOUT("seymor(\"" << section << "\", \"" << key << "\",FeedFunction)");
+ FlowerShop result( new HideOut( section, key, feed, 0 ) );
+ if (not config_data.empty())
+ {
+ if (config_data.hasKey(section, key))
+ {
+ feed( config_data[section][key] );
+ }
+ }
+ return result;
+} // eo seymour(const std::string&,const std::string&,const FeedFunction&)
+
+
+FlowerShop seymour(
+ const std::string& section,
+ const std::string& key,
+ const FeedFunction2& feed)
+{
+ DOUT("seymor(\"" << section << "\", \"" << key << "\",FeedFunction2)");
+ FlowerShop result( new HideOut( section, key, 0, feed ) );
+ if (not config_data.empty())
+ {
+ if (config_data.hasKey(section, key))
+ {
+ StringList values;
+ config_data[section].getValues(key, values);
+ feed( values );
+ }
+ }
+ return result;
+} // eo seymour(const std::string&,const std::string&,const FeedFunction2&)
+
+
+FlowerShop seymour(FlowerShop shop, const FeedFunction& feed)
+{
+ if (!shop)
+ {
+ // no shop; no coordinates; no useful stuff to do...
+ return shop;
+ }
+ HideOutPtr ptr = boost::shared_dynamic_cast< HideOut >(shop);
+ if (!ptr)
+ {
+ // again, no coordinates...
+ return ptr;
+ }
+ FlowerShop result( new HideOut( ptr->m_section, ptr->m_key, feed, 0 ) );
+ if (not config_data.empty())
+ {
+ if (config_data.hasKey(ptr->m_section, ptr->m_key))
+ {
+ feed( config_data[ptr->m_section][ptr->m_key] );
+ }
+ }
+ return result;
+} // eo seymour(FlowerShop,const FeedFunction&)
+
+
+FlowerShop seymour(FlowerShop shop, const FeedFunction2& feed)
+{
+ if (!shop)
+ {
+ // no shop; no coordinates; no useful stuff to do...
+ return shop;
+ }
+ HideOutPtr ptr = boost::shared_dynamic_cast< HideOut >(shop);
+ if (!ptr)
+ {
+ // again, no coordinates...
+ return ptr;
+ }
+ FlowerShop result( new HideOut( ptr->m_section, ptr->m_key, 0, feed ) );
+ if (not config_data.empty())
+ {
+ if (config_data.hasKey(ptr->m_section, ptr->m_key))
+ {
+ StringList values;
+ config_data[ptr->m_section].getValues(ptr->m_key, values);
+ feed( values );
+ }
+ }
+ return result;
+} // eo seymour(FlowerShop,const FeedFunction&)
+
+
+} // eo namespace Detail
+
+
+
+/**
+ * @brief reloads the configuration from the last (known) config file.
+ * @return @a true if config was succesfully reloaded.
+ */
+bool reload()
+{
+ ConfigData old_config_data;
+ ConfigData new_config_data;
+ if (config_file_name.empty())
+ {
+ return false;
+ }
+ bool loaded= load_ini_config_file(config_file_name, new_config_data);
+ bool publish_successful= true;
+ if (loaded)
+ {
+ module_logger().debug() << "new_config_data.empty(): " << new_config_data.empty();
+ // save old data
+ old_config_data.swap( config_data );
+ // and install the new data
+ new_config_data.swap( config_data );
+
+ // react on the config: publish the values:
+ publish_successful = feedEm( old_config_data );
+ }
+ return loaded and publish_successful;
+} // reload()
+
+
+
+
+/**
+ * @brief sets the path of the config file and loads it.
+ * @param path the path to the config file.
+ * @return @a true iff the config could be loaded and all values pushed into their config vars.
+ */
+bool set_config_file(const std::string& path)
+{
+ if (not config_file_name.empty())
+ {
+ // WTF?!...
+ // a new config file.. well... why not?
+ }
+ Stat stat(path);
+ if (not stat or not stat.is_regular_file())
+ {
+ return false;
+ }
+ config_file_name= path;
+ return reload();
+} // eo set_config_file(const std::string&)
+
+
+/**
+ * @brief returns the current config file path.
+ * @return the current path.
+ */
+std::string get_config_file()
+{
+ return config_file_name;
+} // eo get_config_file()
+
+
+} // eo namespace config
+
+} // eo namespace I2n
--- /dev/null
+/** @file
+ * @brief provides config file reading and automatic propagation of the values.
+ *
+ * @author Reinhard Pfau \<Reinhard.Pfau@intra2net.com\>
+ *
+ * @copyright © Copyright 2008 by Intra2net AG
+ * @license commercial
+ * @contact info@intra2net.com
+ *
+ * Basic idea of this module is that one need only to declare its config vars of
+ * the type Config::Var\< T \> and pass the desired section and key from the
+ * (INI style) config file where the value for this var should be searched.
+ * The string value from the config file is converted to the desired type using a
+ * converter class (a default is provided and uses a conversion based on string_to template
+ * from libi2ncommon).
+ *
+ * Populating the var is automagically done when the config is read or reread or when
+ * the variable is defined.
+ *
+ *
+ * @todo review. consolidate. beautify.
+ * @todo the converter should be better named as "validator" since they do not only convert...
+ * @todo support for std::set<> ?
+ */
+
+#ifndef __I2N_GLOBAL_CONFIG_HPP__
+#define __I2N_GLOBAL_CONFIG_HPP__
+
+#include <string>
+#include <functional>
+#include <vector>
+#include <list>
+#include <ostream>
+#include <sstream>
+#include <stringfunc.hxx>
+#include <boost/shared_ptr.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/type_traits.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+
+namespace I2n
+{
+
+namespace Config
+{
+
+template<
+ typename ValueType,
+ class Converter
+>
+class Var;
+
+
+typedef std::vector< std::string > StringList;
+
+/**
+ * @brief contains the "magic" details of the config module
+ * (i.e. all the stuff which must be declared here, but U shouldn't use it!)
+ */
+namespace Detail {
+
+
+typedef boost::function< bool(const std::string&) > FeedFunction;
+typedef boost::function< bool(const StringList&) > FeedFunction2;
+
+
+struct Thing
+{
+ virtual ~Thing() {}
+}; // eo struct Thing
+
+typedef boost::shared_ptr< Thing > FlowerShop;
+
+
+
+
+FlowerShop seymour(const std::string& section, const std::string& key, const FeedFunction& feed);
+FlowerShop seymour(const std::string& section, const std::string& key, const FeedFunction2& feed);
+FlowerShop seymour(FlowerShop shop, const FeedFunction& feed);
+FlowerShop seymour(FlowerShop shop, const FeedFunction2& feed);
+
+/*
+** type magics:
+*/
+
+/**
+ * @brief determines if a type is one of the supported list containers.
+ * @tparam Container the type to test for supported list type.
+ *
+ * currently we support std::list<> and std::vector<> (and derivates).
+ */
+template< class Container >
+struct is_list_container
+{
+ static Container gen();
+
+ template< typename T >
+ static boost::type_traits::yes_type c1( const std::vector< T >& );
+
+ template< typename T >
+ static boost::type_traits::yes_type c1( const std::list< T >& );
+
+ static boost::type_traits::no_type c1(...);
+
+ BOOST_STATIC_CONSTANT( bool, value= ( sizeof(c1(gen())) == sizeof(boost::type_traits::yes_type) ) );
+};
+
+
+template< class C, bool is_list >
+struct extract_type_impl
+{
+ typedef typename boost::remove_reference< typename boost::remove_const< C >::type>::type type;
+};
+
+template< class C >
+struct extract_type_impl< C, true >
+{
+ typedef typename boost::remove_reference<
+ typename boost::remove_const< typename C::value_type >::type
+ >::type type;
+};
+
+template< class C >
+struct extract_type
+: public extract_type_impl< C, is_list_container< C >::value >
+{
+};
+
+
+/*
+**
+*/
+
+template<
+ typename ValueType,
+ class Converter,
+ bool wantList
+>
+class OuterSpace
+{
+ public:
+ typedef ValueType result_type;
+ typedef typename boost::remove_reference< ValueType >::type unref_value_type;
+ typedef typename boost::remove_const< unref_value_type>::type base_value_type;
+ typedef typename boost::add_const<
+ typename boost::add_reference< base_value_type >::type
+ >::type const_ref_value_type;
+
+ typedef FeedFunction FeedFunctionType;
+
+ protected:
+ friend class Var<ValueType, Converter>;
+
+ OuterSpace(ValueType initial_value)
+ : Value(initial_value)
+ , InitialValue(initial_value)
+ {
+ }
+
+ OuterSpace(const OuterSpace& other)
+ : Value( other.Value)
+ , InitialValue( other.InitialValue)
+ {
+ }
+
+ bool audreyII(const std::string& v)
+ {
+ bool res= Converter()(v,Value);
+ if (!res) Value= InitialValue;
+ return res;
+ } // eo audreyII(const std::string&)
+
+ base_value_type Value;
+ base_value_type InitialValue;
+}; // eo class OuterSpace
+
+
+template<
+ typename ValueType,
+ class Converter
+>
+class OuterSpace< ValueType, Converter, true >
+{
+ public:
+ typedef ValueType result_type;
+ typedef typename boost::remove_reference< ValueType >::type unref_value_type;
+ typedef typename boost::remove_const< unref_value_type>::type base_value_type;
+ typedef typename boost::add_const<
+ typename boost::add_reference< base_value_type >::type
+ >::type const_ref_value_type;
+
+ typedef FeedFunction2 FeedFunctionType;
+
+ protected:
+ friend class Var<ValueType, Converter>;
+
+ OuterSpace(ValueType initial_value)
+ : Value(initial_value)
+ , InitialValue(initial_value)
+ {
+ }
+
+ bool audreyII(const StringList& v)
+ {
+ Converter convert;
+ Value.clear();
+ bool res= true;
+ BOOST_FOREACH(const std::string& s, v)
+ {
+ typename base_value_type::value_type value;
+ if (not convert(s,value))
+ {
+ res = false;
+ break;
+ }
+ Value.push_back( value );
+ }
+ if (not res) Value= InitialValue;
+ return res;
+ } // eo audreyII(const std::string&)
+
+ base_value_type Value;
+ base_value_type InitialValue;
+}; // eo class OuterSpace
+
+} // eo namespace Detail
+
+
+/**
+ * @brief default converter for the Var class.
+ * @tparam ValueType the type of the result value (which the input should be converted to)
+ *
+ * Uses stringTo to convert the values.
+ *
+ * @see i2n::stringTo
+ */
+template<
+ typename ValueType
+>
+struct DefaultConverter
+: public std::binary_function< std::string, ValueType&, bool >
+{
+
+ bool operator () ( const std::string& str, ValueType& v )
+ {
+ return string_to< ValueType >( str, v );
+ } // eo operator()(const std::string&,ValueType&)
+
+}; // eo struct DefaultConverter
+
+
+/**
+ * @brief specialized converter for std::string result type.
+ *
+ * just copies the input to the result var and returns true.
+ */
+template<> struct DefaultConverter< std::string >
+: public std::binary_function< std::string, std::string&, bool >
+{
+
+ bool operator () ( const std::string& str, std::string& v )
+ {
+ v= str;
+ return true;
+ } // eo operator()(const std::string&,std::string&)
+
+}; // eo struct DefaultConverter< std::string >
+
+
+
+/**
+ * @brief specialized converter for int types which accepts also octal and hex notation as input.
+ * @tparam ValueType the type of the result value (which the input should be converted to)
+ */
+template<
+ typename ValueType
+>
+struct AutoIntConverter
+: public std::binary_function< std::string, ValueType&, bool >
+{
+
+ bool operator () ( const std::string& str, ValueType& v )
+ {
+ std::istringstream istr(str);
+ if (has_prefix(str,"0x"))
+ {
+ istr.get(); istr.get();
+ istr.setf( std::ios::hex, std::ios::basefield );
+ }
+ else if (has_prefix(str,"0") or has_prefix(str,"-0"))
+ {
+ istr.setf( std::ios::oct, std::ios::basefield );
+ }
+ istr >> v;
+ return istr.eof();
+ } // eo operator()(const std::string&,ValueType&)
+
+}; // eo struct AutoIntConverter
+
+
+
+/**
+ * @brief represents a configuration variable with automatic update.
+ * @tparam ValueType type of the value this variable should hold.
+ * @tparam Converter a converter class which converts a string (from config file) to the desired
+ * type.
+ * Needs to be derived from std::binary_function (taking a string as first arg, a reference to the
+ * desired type as second and returning bool (@a true if conversion was succesful).
+ *
+ * Basic idea is to pass a point (section, key) in the config where the value for this
+ * variable is stored. The config value (a string) should be converted to the desired value (type).
+ * When the config is loaded (or reloaded) the var (content) is automatically updated.
+ *
+ * @see DefaultConverter
+ *
+ * @bug value accessing methods are @a const, but the result type may allow changing the internal value...
+ */
+template<
+ typename ValueType,
+ class Converter = DefaultConverter<
+ typename Detail::extract_type< ValueType >::type
+ >
+>
+class Var
+: public Detail::OuterSpace<
+ ValueType, Converter,
+ Detail::is_list_container< ValueType >::value >
+{
+ BOOST_STATIC_ASSERT(( boost::is_base_of<
+ std::binary_function<
+ std::string,
+ typename boost::add_reference<
+ typename Detail::extract_type< ValueType >::type
+ >::type,
+ bool >,
+ Converter >::value ));
+
+ typedef Detail::OuterSpace<
+ ValueType, Converter,
+ Detail::is_list_container< ValueType >::value
+ > MySpace;
+
+ public:
+
+ typedef typename MySpace::result_type result_type;
+ typedef typename MySpace::base_value_type base_value_type;
+
+ public:
+
+ /**
+ * @brief constructor with config location and initial value.
+ * @param section the section in the config where the value is located in.
+ * @param key the key (in the section) which identifies the value.
+ * @param initial_value the initial value.
+ */
+ Var(const std::string& section,
+ const std::string& key,
+ ValueType initial_value)
+ : MySpace(initial_value)
+ {
+ FlowerShop= Detail::seymour(
+ section, key,
+ typename MySpace::FeedFunctionType( boost::bind( &MySpace::audreyII, this, _1 ) ) );
+ }
+
+
+ /**
+ * @brief constructor with config location (key in global section) and initial value.
+ * @param key the key (in the global section) which identifies the value.
+ * @param initial_value the initial value.
+ */
+ Var(const std::string& key,
+ ValueType initial_value)
+ : MySpace(initial_value)
+ {
+ FlowerShop= Detail::seymour(
+ "", key,
+ typename MySpace::FeedFunctionType( boost::bind( &MySpace::audreyII, this, _1 ) ) );
+ }
+
+
+ /**
+ * @brief operator for accessing the value.
+ * @return the current value.
+ */
+ result_type operator()() const { return MySpace::Value; }
+
+
+ /**
+ * @brief method for accessing the value.
+ * @return the current value.
+ */
+ result_type get_value() const { return MySpace::Value; }
+
+
+ /**
+ * @brief copy constructor.
+ * @param other the instance to copy from.
+ */
+ Var(const Var& other)
+ : MySpace( other )
+ {
+ FlowerShop= Detail::seymour(
+ other.FlowerShop,
+ typename MySpace::FeedFunctionType( boost::bind( &MySpace::audreyII, this, _1 ) ) );
+ } // eo Var(const Var&)
+
+
+ /**
+ * @brief cast operator for the underlying value type. delivers the value.
+ * @return the current value.
+ *
+ * The cast operator allows the instance to be used whereever the underlying value type
+ * is expected.
+ */
+ operator result_type () const { return MySpace::Value; }
+
+
+ private:
+
+ Detail::FlowerShop FlowerShop;
+
+}; // eo class Var
+
+
+
+/**
+ * @brief convenience: \<\< operator which streams the current value of a config var.
+ */
+template <
+ typename ValueType,
+ class Converter
+>
+std::ostream& operator<<(std::ostream& o, const Var< ValueType, Converter >& v)
+{
+ return o << v();
+} // eo operator<<(std::ostream&,const Var< ValueType, Converter>&
+
+
+bool set_config_file(const std::string& path);
+
+std::string get_config_file();
+
+bool reload();
+
+
+} // eo namespace Config
+
+} // eo namespace I2n
+
+#endif
AM_PATH_CPPUNIT(1.8.0)
-AC_OUTPUT(Doxyfile Makefile src/Makefile libi2ncommon.pc test/Makefile)
+AC_OUTPUT(Doxyfile Makefile configlib/Makefile libi2ncommon.pc src/Makefile \
+ test/Makefile libi2ncommon_config.pc)
%description devel
library with functions common in Intra2net programs
+
+%package config
+Summary: library with a config module
+Group: Intranator
+Requires: libi2ncommon = %{version}
+
+%description config
+library with a config module.
+The config module provides a global configuration system with decentralized
+declaration of the config variables.
+
+
%prep
%setup -q
%{prefix}/lib/*.*a*
%{prefix}/lib/pkgconfig/*.pc
%{prefix}/include/
+
+%files config
+%defattr(-,root,root)
+%doc LICENSE
+%{prefix}/lib/libi2ncommon_config.so*
+
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libi2ncommon_config
+Description: library with config module common in some Intra2net programs
+Requires: libi2ncommon
+Version: @VERSION@
+Libs: -L${libdir} -li2ncommon_config
+Cflags: -I${includedir}