diff options
-rw-r--r-- | CMakeLists.txt | 18 | ||||
-rw-r--r-- | src/config/config.cpp | 129 | ||||
-rw-r--r-- | src/config/config.hpp | 111 | ||||
-rw-r--r-- | src/main.cpp | 27 | ||||
-rw-r--r-- | src/test.cpp | 26 |
5 files changed, 304 insertions, 7 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a7ff1d4..b0fae9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,14 @@ add_library(utils STATIC ${source_utils}) target_link_libraries(utils ${ICONV_LIBRARIES}) # +## config +# +file(GLOB source_config + src/config/*.[hc]pp) +add_library(config STATIC ${source_config}) +target_link_libraries(config utils) + +# ## network # file(GLOB source_network @@ -64,11 +72,16 @@ file(GLOB source_bridge add_library(bridge STATIC ${source_bridge}) target_link_libraries(bridge xmpp irc) +# +## Main executable +# add_executable(${PROJECT_NAME} src/main.cpp) target_link_libraries(${PROJECT_NAME} xmpp irc - bridge) + bridge + utils + config) # ## Tests @@ -79,6 +92,7 @@ target_link_libraries(test xmpp irc bridge - utils) + utils + config) CONFIGURE_FILE(config.h.cmake src/config.h @ONLY) diff --git a/src/config/config.cpp b/src/config/config.cpp new file mode 100644 index 0000000..cad8216 --- /dev/null +++ b/src/config/config.cpp @@ -0,0 +1,129 @@ +#include <utils/make_unique.hpp> +#include <config/config.hpp> + +#include <iostream> +#include <sstream> + +std::string Config::filename = "./biboumi.cfg"; +bool Config::file_must_exist = false; + +std::string Config::get(const std::string& option, const std::string& def) +{ + Config* self = Config::instance().get(); + auto it = self->values.find(option); + + if (it == self->values.end()) + return def; + return it->second; +} + +int Config::get_int(const std::string& option, const int& def) +{ + Config* self = Config::instance().get(); + std::string res = self->get(option, ""); + if (!res.empty()) + return atoi(res.c_str()); + else + return def; +} + +void Config::set_int(const std::string& option, const int& value, bool save) +{ + std::ostringstream os; + os << value; + Config::set(option, os.str(), save); +} + +void Config::set(const std::string& option, const std::string& value, bool save) +{ + Config* self = Config::instance().get(); + self->values[option] = value; + if (save) + { + self->save_to_file(); + self->trigger_configuration_change(); + } +} + +void Config::connect(t_config_changed_callback callback) +{ + Config* self = Config::instance().get(); + self->callbacks.push_back(callback); +} + +void Config::close() +{ + Config* self = Config::instance().get(); + self->save_to_file(); + self->values.clear(); + Config::instance().reset(); +} + +/** + * Private methods + */ + +void Config::trigger_configuration_change() +{ + std::vector<t_config_changed_callback>::iterator it; + for (it = this->callbacks.begin(); it < this->callbacks.end(); ++it) + (*it)(); +} + +std::unique_ptr<Config>& Config::instance() +{ + static std::unique_ptr<Config> instance; + + if (!instance) + { + instance = std::make_unique<Config>(); + instance->read_conf(); + } + return instance; +} + +bool Config::read_conf() +{ + std::ifstream file; + file.open(filename.data()); + if (!file.is_open()) + { + if (Config::file_must_exist) + { + perror(("Error while opening file " + filename + " for reading.").c_str()); + file.exceptions(std::ifstream::failbit); + } + return false; + } + + std::string line; + size_t pos; + std::string option; + std::string value; + while (file.good()) + { + std::getline(file, line); + if (line == "" || line[0] == '#') + continue ; + pos = line.find('='); + if (pos == std::string::npos) + continue ; + option = line.substr(0, pos); + value = line.substr(pos+1); + this->values[option] = value; + } + return true; +} + +void Config::save_to_file() const +{ + std::ofstream file(this->filename.data()); + if (file.fail()) + { + std::cerr << "Could not save config file." << std::endl; + return ; + } + for (auto& it: this->values) + file << it.first << "=" << it.second << std::endl; + file.close(); +} diff --git a/src/config/config.hpp b/src/config/config.hpp new file mode 100644 index 0000000..ea28388 --- /dev/null +++ b/src/config/config.hpp @@ -0,0 +1,111 @@ +/** + * Read the config file and save all the values in a map. + * Also, a singleton. + * + * Use Config::filename = "bla" to set the filename you want to use. + * + * If you want to exit if the file does not exist when it is open for + * reading, set Config::file_must_exist = true. + * + * Config::get() can the be used to access the values in the conf. + * + * Use Config::close() when you're done getting/setting value. This will + * save the config into the file. + */ + +#ifndef CONFIG_INCLUDED +# define CONFIG_INCLUDED + +#include <functional> +#include <fstream> +#include <memory> +#include <vector> +#include <string> +#include <map> + +typedef std::function<void()> t_config_changed_callback; + +class Config +{ +public: + Config(){}; + ~Config(){}; + /** + * returns a value from the config. If it doesn’t exist, use + * the second argument as the default. + * @param option The option we want + * @param def The default value in case the option does not exist + */ + static std::string get(const std::string&, const std::string&); + /** + * returns a value from the config. If it doesn’t exist, use + * the second argument as the default. + * @param option The option we want + * @param def The default value in case the option does not exist + */ + static int get_int(const std::string&, const int&); + /** + * Set a value for the given option. And write all the config + * in the file from which it was read if boolean is set. + * @param option The option to set + * @param value The value to use + * @param save if true, save the config file + */ + static void set(const std::string&, const std::string&, bool save = false); + /** + * Set a value for the given option. And write all the config + * in the file from which it was read if boolean is set. + * @param option The option to set + * @param value The value to use + * @param save if true, save the config file + */ + static void set_int(const std::string&, const int&, bool save = false); + /** + * Adds a function to a list. This function will be called whenever a + * configuration change occurs. + */ + static void connect(t_config_changed_callback); + /** + * Close the config file, saving it to the file is save == true. + */ + static void close(); + + /** + * Set the value of the filename to use, before calling any method. + */ + static std::string filename; + /** + * Set to true if you want an exception to be raised if the file does not + * exist when reading it. + */ + static bool file_must_exist; + +private: + /** + * Get the singleton instance + */ + static std::unique_ptr<Config>& instance(); + /** + * Read the configuration file at the given path. + */ + bool read_conf(); + /** + * Write all the config values into the configuration file + */ + void save_to_file() const; + /** + * Call all the callbacks previously registered using connect(). + * This is used to notify any class that a configuration change occured. + */ + void trigger_configuration_change(); + + std::map<std::string, std::string> values; + std::vector<t_config_changed_callback> callbacks; + + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; + Config(Config&&) = delete; + Config& operator=(Config&&) = delete; +}; + +#endif // CONFIG_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index b7fa01e..80c8436 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,13 +1,32 @@ -#include <network/poller.hpp> #include <xmpp/xmpp_component.hpp> +#include <network/poller.hpp> +#include <config/config.hpp> +#include <iostream> #include <memory> -int main() +int main(int ac, char** av) { - Poller p; + if (ac > 1) + Config::filename = av[1]; + Config::file_must_exist = true; + + std::string password; + try { // The file must exist + password = Config::get("password", ""); + } + catch (const std::ios::failure& e) { + return 1; + } + if (password.empty()) + { + std::cerr << "No password provided." << std::endl; + return 1; + } std::shared_ptr<XmppComponent> xmpp_component = - std::make_shared<XmppComponent>("irc.localhost", "secret"); + std::make_shared<XmppComponent>("irc.abricot", password); + + Poller p; p.add_socket_handler(xmpp_component); xmpp_component->start(); while (p.poll()) diff --git a/src/test.cpp b/src/test.cpp index 674be98..99454a5 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -10,6 +10,8 @@ #include <utils/encoding.hpp> #include <string.h> +#include <config/config.hpp> + #include <xmpp/xmpp_parser.hpp> int main() @@ -49,7 +51,7 @@ int main() * XML parsing */ XmppParser xml; - const std::string doc = "<stream xmlns='stream_ns'><stanza b='c'>inner<child1/><child2 xmlns='child2_ns'/>tail</stanza></stream>"; + const std::string doc = "<stream xmlns='stream_ns'><stanza b='c'>inner<child1><grandchild/></child1><child2 xmlns='child2_ns'/>tail</stanza></stream>"; xml.add_stanza_callback([](const Stanza& stanza) { assert(stanza.get_name() == "stream_ns:stanza"); @@ -62,5 +64,27 @@ int main() assert(stanza.get_child("child2_ns:child2")->get_tail() == "tail"); }); xml.feed(doc.data(), doc.size(), true); + + /** + * Config + */ + Config::filename = "test.cfg"; + Config::file_must_exist = false; + Config::set("coucou", "bonjour"); + Config::close(); + + bool error = false; + try + { + Config::file_must_exist = true; + assert(Config::get("coucou", "") == "bonjour"); + assert(Config::get("does not exist", "default") == "default"); + Config::close(); + } + catch (const std::ios::failure& e) + { + error = true; + } + assert(error == false); return 0; } |