changeset 160:c1acfacc46bd

Irccd: dll export and style
author David Demelier <markand@malikania.fr>
date Tue, 24 May 2016 13:00:35 +0200
parents 70ed0753ce0d
children 49095643ae67
files cmake/IrccdSystem.cmake cmake/internal/sysconfig.hpp.in lib/CMakeLists.txt lib/irccd/alias.cpp lib/irccd/alias.hpp lib/irccd/application.cpp lib/irccd/application.hpp lib/irccd/cmd-help.cpp lib/irccd/cmd-help.hpp lib/irccd/cmd-plugin-info.cpp lib/irccd/cmd-plugin-info.hpp lib/irccd/cmd-plugin-list.cpp lib/irccd/cmd-plugin-list.hpp lib/irccd/cmd-plugin-load.hpp lib/irccd/cmd-plugin-reload.hpp lib/irccd/cmd-plugin-unload.hpp lib/irccd/cmd-server-cmode.hpp lib/irccd/cmd-server-cnotice.hpp lib/irccd/cmd-server-connect.cpp lib/irccd/cmd-server-connect.hpp lib/irccd/cmd-server-disconnect.cpp lib/irccd/cmd-server-disconnect.hpp lib/irccd/cmd-server-info.cpp lib/irccd/cmd-server-info.hpp lib/irccd/cmd-server-invite.hpp lib/irccd/cmd-server-join.cpp lib/irccd/cmd-server-join.hpp lib/irccd/cmd-server-kick.cpp lib/irccd/cmd-server-kick.hpp lib/irccd/cmd-server-list.cpp lib/irccd/cmd-server-list.hpp lib/irccd/cmd-server-me.hpp lib/irccd/cmd-server-message.hpp lib/irccd/cmd-server-mode.hpp lib/irccd/cmd-server-nick.hpp lib/irccd/cmd-server-notice.hpp lib/irccd/cmd-server-part.cpp lib/irccd/cmd-server-part.hpp lib/irccd/cmd-server-reconnect.cpp lib/irccd/cmd-server-reconnect.hpp lib/irccd/cmd-server-topic.hpp lib/irccd/cmd-watch.cpp lib/irccd/cmd-watch.hpp lib/irccd/command.cpp lib/irccd/command.hpp lib/irccd/config.cpp lib/irccd/config.hpp lib/irccd/connection.cpp lib/irccd/connection.hpp lib/irccd/elapsed-timer.hpp lib/irccd/fs.cpp lib/irccd/fs.hpp lib/irccd/ini.hpp lib/irccd/irccd.cpp lib/irccd/irccd.hpp lib/irccd/irccdctl.cpp lib/irccd/irccdctl.hpp lib/irccd/json.hpp lib/irccd/logger.hpp lib/irccd/mod-directory.cpp lib/irccd/mod-directory.hpp lib/irccd/mod-elapsed-timer.hpp lib/irccd/mod-file.cpp lib/irccd/mod-file.hpp lib/irccd/mod-irccd.hpp lib/irccd/mod-logger.hpp lib/irccd/mod-plugin.cpp lib/irccd/mod-plugin.hpp lib/irccd/mod-server.cpp lib/irccd/mod-server.hpp lib/irccd/mod-system.cpp lib/irccd/mod-system.hpp lib/irccd/mod-timer.cpp lib/irccd/mod-timer.hpp lib/irccd/mod-unicode.hpp lib/irccd/mod-util.hpp lib/irccd/module.hpp lib/irccd/options.hpp lib/irccd/path.hpp lib/irccd/plugin-dynlib.hpp lib/irccd/plugin-js.hpp lib/irccd/plugin.hpp lib/irccd/rule.hpp lib/irccd/server-event.cpp lib/irccd/server-event.hpp lib/irccd/server-state-connected.hpp lib/irccd/server-state-connecting.hpp lib/irccd/server-state-disconnected.hpp lib/irccd/server.hpp lib/irccd/service-interrupt.cpp lib/irccd/service-interrupt.hpp lib/irccd/service-module.hpp lib/irccd/service-plugin.hpp lib/irccd/service-rule.cpp lib/irccd/service-rule.hpp lib/irccd/service-server.cpp lib/irccd/service-server.hpp lib/irccd/service-transport.cpp lib/irccd/service-transport.hpp lib/irccd/service.hpp lib/irccd/sockets.hpp lib/irccd/system.cpp lib/irccd/system.hpp lib/irccd/timer.hpp lib/irccd/transport-client.cpp lib/irccd/transport-client.hpp lib/irccd/transport-server.cpp lib/irccd/transport-server.hpp lib/irccd/unicode.hpp lib/irccd/util.cpp lib/irccd/util.hpp
diffstat 111 files changed, 1503 insertions(+), 2495 deletions(-) [+]
line wrap: on
line diff
--- a/cmake/IrccdSystem.cmake	Mon May 23 14:05:41 2016 +0200
+++ b/cmake/IrccdSystem.cmake	Tue May 24 13:00:35 2016 +0200
@@ -55,7 +55,7 @@
 		set(CMAKE_CXX_FLAGS "-Wall -Wextra -std=c++14 ${CMAKE_CXX_FLAGS}")
 	endif ()
 elseif (MSVC14)
-	set(CMAKE_C_FLAGS "/DWIN32_LEAN_AND_MEAN /DNOMINMAX /wd4267 /wd48000 /D_CRT_SECURE_NO_WARNINGS ${CMAKE_C_FLAGS}")
+	set(CMAKE_C_FLAGS "/DWIN32_LEAN_AND_MEAN /DNOMINMAX /wd4267 /wd4800 /D_CRT_SECURE_NO_WARNINGS ${CMAKE_C_FLAGS}")
 	set(CMAKE_CXX_FLAGS "/DWIN32_LEAN_AND_MEAN /DNOMINMAX /wd4267 /wd4800 /D_CRT_SECURE_NO_WARNINGS /EHsc ${CMAKE_CXX_FLAGS}")
 else ()
 	message(WARNING "Unsupported ${CMAKE_CXX_COMPILER_ID}, may not build correctly.")
--- a/cmake/internal/sysconfig.hpp.in	Mon May 23 14:05:41 2016 +0200
+++ b/cmake/internal/sysconfig.hpp.in	Tue May 24 13:00:35 2016 +0200
@@ -94,4 +94,19 @@
 #cmakedefine HAVE_STAT_ST_UID
 #cmakedefine HAVE_SYSLOG
 
+/*
+ * Export stuff.
+ * ------------------------------------------------------------------
+ */
+
+#if defined(_WIN32)
+#  if defined(IRCCD_BUILDING_DLL)
+#    define IRCCD_EXPORT __declspec(dllexport)
+#  else
+#    define IRCCD_EXPORT __declspec(dllimport)
+#  endif
+#else
+#  define IRCCD_EXPORT
+#endif
+
 #endif // !IRCCD_SYSCONFIG_H
--- a/lib/CMakeLists.txt	Mon May 23 14:05:41 2016 +0200
+++ b/lib/CMakeLists.txt	Tue May 24 13:00:35 2016 +0200
@@ -34,8 +34,14 @@
 		${OPENSSL_INCLUDE_DIR}
 )
 
+source_group(irccd FILES ${HEADERS} ${SOURCES})
+
 if (IRCCD_SYSTEM_WINDOWS)
 	list(APPEND LIBRARIES ws2_32 shlwapi)
+
+	if (BUILD_SHARED_LIBS)
+		list(APPEND FLAGS IRCCD_BUILDING_DLL)
+	endif ()
 elseif (IRCCD_SYSTEM_MAC)
 	list(APPEND LIBRARIES resolv)
 elseif (IRCCD_SYSTEM_LINUX)
@@ -43,12 +49,18 @@
 endif ()
 
 target_link_libraries(libirccd extern-duktape extern-ircclient extern-jansson extern-cppformat ${LIBRARIES})
+target_compile_definitions(libirccd PRIVATE ${FLAGS})
 
 set_target_properties(
 	libirccd
 	PROPERTIES
 		PREFIX ""
 		OUTPUT_NAME_DEBUG libirccd2d
+		RUNTIME_OUTPUT_DIRECTORY ${IRCCD_FAKEROOTDIR}/${WITH_BINDIR}
+		RUNTIME_OUTPUT_DIRECTORY_DEBUG ${IRCCD_FAKEROOTDIR}/${WITH_BINDIR}
+		RUNTIME_OUTPUT_DIRECTORY_RELEASE ${IRCCD_FAKEROOTDIR}/${WITH_BINDIR}
+		RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${IRCCD_FAKEROOTDIR}/${WITH_BINDIR}
+		RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${IRCCD_FAKEROOTDIR}/${WITH_BINDIR}
 		VERSION ${IRCCD_VERSION}
 		SOVERSION ${IRCCD_VERSION_SHLIB}
 )
--- a/lib/irccd/alias.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/alias.cpp	Tue May 24 13:00:35 2016 +0200
@@ -27,11 +27,10 @@
 {
 	assert(!value.empty());
 
-	if ((m_isPlaceholder = std::regex_match(value, std::regex("^%\\d+$")))) {
+	if ((m_isPlaceholder = std::regex_match(value, std::regex("^%\\d+$")))) 
 		m_value = value.substr(1);
-	} else {
+	else
 		m_value = std::move(value);
-	}
 }
 
 unsigned AliasArg::index() const noexcept
@@ -50,11 +49,10 @@
 
 std::ostream &operator<<(std::ostream &out, const AliasArg &arg)
 {
-	if (arg.m_isPlaceholder) {
+	if (arg.m_isPlaceholder)
 		out << "%" << arg.m_value;
-	} else {
+	else
 		out << arg.m_value;
-	}
 
 	return out;
 }
--- a/lib/irccd/alias.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/alias.hpp	Tue May 24 13:00:35 2016 +0200
@@ -28,6 +28,8 @@
 #include <string>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 /**
@@ -51,7 +53,7 @@
 	 * \pre value must not be empty
 	 * \param value the value
 	 */
-	AliasArg(std::string value);
+	IRCCD_EXPORT AliasArg(std::string value);
 
 	/**
 	 * Check if the argument is a placeholder.
@@ -69,7 +71,7 @@
 	 * \pre isPlaceholder() must return true
 	 * \return the position
 	 */
-	unsigned index() const noexcept;
+	IRCCD_EXPORT unsigned index() const noexcept;
 
 	/**
 	 * Get the real value.
@@ -77,7 +79,7 @@
 	 * \pre isPlaceholder() must return false
 	 * \return the value
 	 */
-	const std::string &value() const noexcept;
+	IRCCD_EXPORT const std::string &value() const noexcept;
 
 	/**
 	 * Output the alias to the stream.
@@ -85,7 +87,7 @@
 	 * \param out the output stream
 	 * \return out
 	 */
-	friend std::ostream &operator<<(std::ostream &out, const AliasArg &);
+	IRCCD_EXPORT friend std::ostream &operator<<(std::ostream &out, const AliasArg &);
 };
 
 /**
--- a/lib/irccd/application.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/application.cpp	Tue May 24 13:00:35 2016 +0200
@@ -1,76 +1,76 @@
-/*
- * application.cpp -- super base class to create irccd front ends
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "application.hpp"
-#include "cmd-help.hpp"
-#include "cmd-plugin-info.hpp"
-#include "cmd-plugin-list.hpp"
-#include "cmd-plugin-load.hpp"
-#include "cmd-plugin-reload.hpp"
-#include "cmd-plugin-unload.hpp"
-#include "cmd-server-cmode.hpp"
-#include "cmd-server-cnotice.hpp"
-#include "cmd-server-connect.hpp"
-#include "cmd-server-disconnect.hpp"
-#include "cmd-server-info.hpp"
-#include "cmd-server-invite.hpp"
-#include "cmd-server-join.hpp"
-#include "cmd-server-kick.hpp"
-#include "cmd-server-list.hpp"
-#include "cmd-server-me.hpp"
-#include "cmd-server-message.hpp"
-#include "cmd-server-mode.hpp"
-#include "cmd-server-nick.hpp"
-#include "cmd-server-notice.hpp"
-#include "cmd-server-part.hpp"
-#include "cmd-server-reconnect.hpp"
-#include "cmd-server-topic.hpp"
-#include "cmd-watch.hpp"
-
-namespace irccd {
-
-Application::Application()
-{
-	/* Register all commands */
-	addCommand(std::make_unique<command::Help>());
-	addCommand(std::make_unique<command::PluginInfo>());
-	addCommand(std::make_unique<command::PluginList>());
-	addCommand(std::make_unique<command::PluginLoad>());
-	addCommand(std::make_unique<command::PluginReload>());
-	addCommand(std::make_unique<command::PluginUnload>());
-	addCommand(std::make_unique<command::ServerChannelMode>());
-	addCommand(std::make_unique<command::ServerChannelNotice>());
-	addCommand(std::make_unique<command::ServerConnect>());
-	addCommand(std::make_unique<command::ServerDisconnect>());
-	addCommand(std::make_unique<command::ServerInfo>());
-	addCommand(std::make_unique<command::ServerInvite>());
-	addCommand(std::make_unique<command::ServerJoin>());
-	addCommand(std::make_unique<command::ServerKick>());
-	addCommand(std::make_unique<command::ServerList>());
-	addCommand(std::make_unique<command::ServerMe>());
-	addCommand(std::make_unique<command::ServerMessage>());
-	addCommand(std::make_unique<command::ServerMode>());
-	addCommand(std::make_unique<command::ServerNick>());
-	addCommand(std::make_unique<command::ServerNotice>());
-	addCommand(std::make_unique<command::ServerPart>());
-	addCommand(std::make_unique<command::ServerReconnect>());
-	addCommand(std::make_unique<command::ServerTopic>());
-	addCommand(std::make_unique<command::Watch>());
-}
-
-} // !irccd
+/*
+ * application.cpp -- super base class to create irccd front ends
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "application.hpp"
+#include "cmd-help.hpp"
+#include "cmd-plugin-info.hpp"
+#include "cmd-plugin-list.hpp"
+#include "cmd-plugin-load.hpp"
+#include "cmd-plugin-reload.hpp"
+#include "cmd-plugin-unload.hpp"
+#include "cmd-server-cmode.hpp"
+#include "cmd-server-cnotice.hpp"
+#include "cmd-server-connect.hpp"
+#include "cmd-server-disconnect.hpp"
+#include "cmd-server-info.hpp"
+#include "cmd-server-invite.hpp"
+#include "cmd-server-join.hpp"
+#include "cmd-server-kick.hpp"
+#include "cmd-server-list.hpp"
+#include "cmd-server-me.hpp"
+#include "cmd-server-message.hpp"
+#include "cmd-server-mode.hpp"
+#include "cmd-server-nick.hpp"
+#include "cmd-server-notice.hpp"
+#include "cmd-server-part.hpp"
+#include "cmd-server-reconnect.hpp"
+#include "cmd-server-topic.hpp"
+#include "cmd-watch.hpp"
+
+namespace irccd {
+
+Application::Application()
+{
+	// Register all commands.
+	addCommand(std::make_unique<command::Help>());
+	addCommand(std::make_unique<command::PluginInfo>());
+	addCommand(std::make_unique<command::PluginList>());
+	addCommand(std::make_unique<command::PluginLoad>());
+	addCommand(std::make_unique<command::PluginReload>());
+	addCommand(std::make_unique<command::PluginUnload>());
+	addCommand(std::make_unique<command::ServerChannelMode>());
+	addCommand(std::make_unique<command::ServerChannelNotice>());
+	addCommand(std::make_unique<command::ServerConnect>());
+	addCommand(std::make_unique<command::ServerDisconnect>());
+	addCommand(std::make_unique<command::ServerInfo>());
+	addCommand(std::make_unique<command::ServerInvite>());
+	addCommand(std::make_unique<command::ServerJoin>());
+	addCommand(std::make_unique<command::ServerKick>());
+	addCommand(std::make_unique<command::ServerList>());
+	addCommand(std::make_unique<command::ServerMe>());
+	addCommand(std::make_unique<command::ServerMessage>());
+	addCommand(std::make_unique<command::ServerMode>());
+	addCommand(std::make_unique<command::ServerNick>());
+	addCommand(std::make_unique<command::ServerNotice>());
+	addCommand(std::make_unique<command::ServerPart>());
+	addCommand(std::make_unique<command::ServerReconnect>());
+	addCommand(std::make_unique<command::ServerTopic>());
+	addCommand(std::make_unique<command::Watch>());
+}
+
+} // !irccd
--- a/lib/irccd/application.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/application.hpp	Tue May 24 13:00:35 2016 +0200
@@ -29,6 +29,7 @@
 #include <unordered_map>
 
 #include "command.hpp"
+#include "sysconfig.hpp"
 
 namespace irccd {
 
@@ -51,7 +52,7 @@
 	/**
 	 * Create the application and fill the commands with predefined commands.
 	 */
-	Application();
+	IRCCD_EXPORT Application();
 
 	/**
 	 * Access the remote commands.
--- a/lib/irccd/cmd-help.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-help.cpp	Tue May 24 13:00:35 2016 +0200
@@ -43,11 +43,10 @@
 {
 	auto it = irccdctl.commands().find(args.arg(0U));
 
-	if (it == irccdctl.commands().end()) {
+	if (it == irccdctl.commands().end())
 		log::warning() << "there is no command named: " << args.arg(0U) << std::endl;
-	} else {
+	else
 		log::warning() << it->second->usage() << std::flush;
-	}
 
 	return nullptr;
 }
--- a/lib/irccd/cmd-help.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-help.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,19 +36,19 @@
  */
 class Help : public RemoteCommand {
 public:
-	Help();
+	IRCCD_EXPORT Help();
 
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-plugin-info.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-plugin-info.cpp	Tue May 24 13:00:35 2016 +0200
@@ -71,7 +71,7 @@
 {
 	RemoteCommand::result(irccdctl, result);
 
-	/* Plugin information */
+	// Plugin information.
 	if (result.valueOr("status", false).toBool()) {
 		std::cout << std::boolalpha;
 		std::cout << "Author         : " << result.valueOr("author", "").toString(true) << std::endl;
--- a/lib/irccd/cmd-plugin-info.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-plugin-info.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,32 +39,32 @@
 	/**
 	 * Constructor.
 	 */
-	PluginInfo();
+	IRCCD_EXPORT PluginInfo();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 
 	/**
 	 * \copydoc RemoteCommand::result
 	 */
-	void result(Irccdctl &irccdctl, const json::Value &response) const override;
+	IRCCD_EXPORT void result(Irccdctl &irccdctl, const json::Value &response) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-plugin-list.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-plugin-list.cpp	Tue May 24 13:00:35 2016 +0200
@@ -44,9 +44,8 @@
 	json::Value response = RemoteCommand::exec(irccd, request);
 	json::Value list = json::array({});
 
-	for (const auto &plugin : irccd.pluginService().plugins()) {
+	for (const auto &plugin : irccd.pluginService().plugins())
 		list.append(plugin->name());
-	}
 
 	response.insert("list", std::move(list));
 
@@ -63,9 +62,8 @@
 {
 	RemoteCommand::result(irccdctl, object);
 
-	for (const auto &n : object.valueOr("list", json::Type::Array, json::array({}))) {
+	for (const auto &n : object.valueOr("list", json::Type::Array, json::array({})))
 		std::cout << n.toString() << std::endl;
-	}
 }
 
 } // !command
--- a/lib/irccd/cmd-plugin-list.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-plugin-list.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,22 +39,22 @@
 	/**
 	 * Constructor.
 	 */
-	PluginList();
+	IRCCD_EXPORT PluginList();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 
 	/**
 	 * \copydoc RemoteCommand::result
 	 */
-	void result(Irccdctl &irccdctl, const json::Value &response) const override;
+	IRCCD_EXPORT void result(Irccdctl &irccdctl, const json::Value &response) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-plugin-load.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-plugin-load.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,22 +39,22 @@
 	/**
 	 * Constructor.
 	 */
-	PluginLoad();
+	IRCCD_EXPORT PluginLoad();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-plugin-reload.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-plugin-reload.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,22 +39,22 @@
 	/**
 	 * Constructor.
 	 */
-	PluginReload();
+	IRCCD_EXPORT PluginReload();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-plugin-unload.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-plugin-unload.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,22 +39,22 @@
 	/**
 	 * Constructor.
 	 */
-	PluginUnload();
+	IRCCD_EXPORT PluginUnload();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-cmode.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-cmode.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,22 +39,22 @@
 	/**
 	 * Constructor.
 	 */
-	ServerChannelMode();
+	IRCCD_EXPORT ServerChannelMode();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 	 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-cnotice.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-cnotice.hpp	Tue May 24 13:00:35 2016 +0200
@@ -48,22 +48,22 @@
 	/**
 	 * Constructor.
 	 */
-	ServerChannelNotice();
+	IRCCD_EXPORT ServerChannelNotice();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-connect.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-connect.cpp	Tue May 24 13:00:35 2016 +0200
@@ -38,12 +38,10 @@
 {
 	auto it = object.find("name");
 
-	if (it == object.end()) {
+	if (it == object.end())
 		throw std::invalid_argument("missing 'name' property");
-	}
-	if (!it->isString() || !util::isIdentifierValid(it->toString())) {
+	if (!it->isString() || !util::isIdentifierValid(it->toString()))
 		throw std::invalid_argument("invalid server name");
-	}
 
 	return it->toString();
 }
@@ -52,12 +50,10 @@
 {
 	auto it = object.find("host");
 
-	if (it == object.end()) {
+	if (it == object.end())
 		throw std::invalid_argument("missing 'host' property");
-	}
-	if (!it->isString()) {
+	if (!it->isString())
 		throw std::invalid_argument("invalid host");
-	}
 
 	return it->toString();
 }
@@ -67,11 +63,9 @@
 	auto it = object.find("port");
 	uint16_t port = 6667;
 
-	if (it != object.end()) {
-		if (it->isInt() && it->toInt() >= 0 && it->toInt() <= std::numeric_limits<std::uint16_t>::max()) {
+	if (it != object.end())
+		if (it->isInt() && it->toInt() >= 0 && it->toInt() <= std::numeric_limits<std::uint16_t>::max())
 			port = static_cast<std::uint16_t>(it->toInt());
-		}
-	}
 
 	return port;
 }
@@ -90,9 +84,8 @@
 		throw std::invalid_argument("ssl is disabled");
 #endif
 
-	if (object.valueOr("sslVerify", json::Type::Boolean, false).toBool()) {
+	if (object.valueOr("sslVerify", json::Type::Boolean, false).toBool())
 		info.flags |= ServerInfo::SslVerify;
-	}
 
 	return info;
 }
@@ -155,12 +148,10 @@
 
 json::Value ServerConnect::exec(Irccd &irccd, const json::Value &request) const
 {
-	auto server = std::make_shared<Server>(readInfoName(request), readInfo(request), readIdentity(request),
-					       readSettings(request));
+	auto server = std::make_shared<Server>(readInfoName(request), readInfo(request), readIdentity(request), readSettings(request));
 
-	if (irccd.serverService().has(server->name())) {
+	if (irccd.serverService().has(server->name()))
 		throw std::invalid_argument("server '{}' already exists"_format(server->name()));
-	}
 
 	irccd.serverService().add(std::move(server));
 
--- a/lib/irccd/cmd-server-connect.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-connect.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,27 +39,27 @@
 	/**
 	 * Constructor.
 	 */
-	ServerConnect();
+	IRCCD_EXPORT ServerConnect();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::options
 	 */
-	std::vector<Option> options() const override;
+	IRCCD_EXPORT std::vector<Option> options() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-disconnect.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-disconnect.cpp	Tue May 24 13:00:35 2016 +0200
@@ -44,11 +44,10 @@
 {
 	auto it = request.find("server");
 
-	if (it == request.end()) {
+	if (it == request.end())
 		irccd.serverService().clear();
-	} else {
+	else
 		irccd.serverService().remove(it->toString());
-	}
 
 	return RemoteCommand::exec(irccd, request);
 }
--- a/lib/irccd/cmd-server-disconnect.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-disconnect.hpp	Tue May 24 13:00:35 2016 +0200
@@ -39,24 +39,24 @@
 	/**
 	 * Constructor.
 	 */
-	ServerDisconnect();
+	IRCCD_EXPORT ServerDisconnect();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * Get list of arguments required.
 	 *
 	 * \return the arguments required
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-info.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-info.cpp	Tue May 24 13:00:35 2016 +0200
@@ -64,23 +64,19 @@
 	response.insert("username", server->identity().username);
 	response.insert("realname", server->identity().realname);
 
-	/* Optional stuff */
-	if (server->info().flags & irccd::ServerInfo::Ipv6) {
+	// Optional stuff.
+	if (server->info().flags & irccd::ServerInfo::Ipv6)
 		response.insert("ipv6", true);
-	}
-	if (server->info().flags & irccd::ServerInfo::Ssl) {
+	if (server->info().flags & irccd::ServerInfo::Ssl)
 		response.insert("ssl", true);
-	}
-	if (server->info().flags & irccd::ServerInfo::SslVerify) {
+	if (server->info().flags & irccd::ServerInfo::SslVerify)
 		response.insert("sslVerify", true);
-	}
 
 	/* Channel list */
 	auto channels = json::array({});
 
-	for (const auto &c : server->settings().channels) {
+	for (const auto &c : server->settings().channels)
 		channels.append(c.name);
-	}
 
 	response.insert("channels", std::move(channels));
 
@@ -91,7 +87,7 @@
 {
 	RemoteCommand::result(irccdctl, response);
 
-	/* Server information */
+	// Server information.
 	std::cout << std::boolalpha;
 	std::cout << "Name           : " << response.valueOr("name", "").toString(true) << std::endl;
 	std::cout << "Host           : " << response.valueOr("host", "").toString(true) << std::endl;
@@ -100,16 +96,15 @@
 	std::cout << "SSL            : " << response.valueOr("ssl", "").toString(true) << std::endl;
 	std::cout << "SSL verified   : " << response.valueOr("sslVerify", "").toString(true) << std::endl;
 
-	/* Channels */
+	// Channels.
 	std::cout << "Channels       : ";
 
-	for (const json::Value &v : response.valueOr("channels", json::Type::Array, json::array({}))) {
+	for (const json::Value &v : response.valueOr("channels", json::Type::Array, json::array({})))
 		std::cout << v.toString() << " ";
-	}
 
 	std::cout << std::endl;
 
-	/* Identity */
+	// Identity.
 	std::cout << "Nickname       : " << response.valueOr("nickname", "").toString(true) << std::endl;
 	std::cout << "User name      : " << response.valueOr("username", "").toString(true) << std::endl;
 	std::cout << "Real name      : " << response.valueOr("realname", "").toString(true) << std::endl;
--- a/lib/irccd/cmd-server-info.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-info.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,32 +36,32 @@
  */
 class ServerInfo : public RemoteCommand {
 public:
-	ServerInfo();
+	IRCCD_EXPORT ServerInfo();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 
 	/**
 	 * \copydoc RemoteCommand::result
 	 */
-	void result(Irccdctl &irccdctl, const json::Value &response) const override;
+	IRCCD_EXPORT void result(Irccdctl &irccdctl, const json::Value &response) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-invite.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-invite.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,29 +36,27 @@
  */
 class ServerInvite : public RemoteCommand {
 public:
-	ServerInvite();
+	IRCCD_EXPORT ServerInvite();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
-
-	
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-join.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-join.cpp	Tue May 24 13:00:35 2016 +0200
@@ -51,9 +51,8 @@
 		{ "channel", args.args()[1] }
 	});
 
-	if (args.length() == 3) {
+	if (args.length() == 3)
 		req.insert("password", args.args()[2]);
-	}
 
 	return req;
 }
--- a/lib/irccd/cmd-server-join.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-join.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerJoin : public RemoteCommand {
 public:
-	ServerJoin();
+	IRCCD_EXPORT ServerJoin();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-kick.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-kick.cpp	Tue May 24 13:00:35 2016 +0200
@@ -53,9 +53,8 @@
 		{ "channel", args.arg(2) }
 	});
 
-	if (args.length() == 4) {
+	if (args.length() == 4)
 		req.insert("reason", args.arg(3));
-	}
 
 	return req;
 }
--- a/lib/irccd/cmd-server-kick.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-kick.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerKick : public RemoteCommand {
 public:
-	ServerKick();
+	IRCCD_EXPORT ServerKick();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-list.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-list.cpp	Tue May 24 13:00:35 2016 +0200
@@ -42,9 +42,8 @@
 	auto json = json::object({});
 	auto list = json::array({});
 
-	for (const auto &server : irccd.serverService().servers()) {
+	for (const auto &server : irccd.serverService().servers())
 		list.append(server->name());
-	}
 
 	json.insert("list", std::move(list));
 
@@ -53,9 +52,8 @@
 
 void ServerList::result(Irccdctl &, const json::Value &response) const
 {
-	for (const auto &n : response.valueOr("list", json::Type::Array, json::array({}))) {
+	for (const auto &n : response.valueOr("list", json::Type::Array, json::array({})))
 		std::cout << n.toString() << std::endl;
-	}
 }
 
 } // !command
--- a/lib/irccd/cmd-server-list.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-list.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,16 +36,16 @@
  */
 class ServerList : public RemoteCommand {
 public:
-	ServerList();
+	IRCCD_EXPORT ServerList();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 
-	void result(Irccdctl &irccdctl, const json::Value &response) const override;
+	IRCCD_EXPORT void result(Irccdctl &irccdctl, const json::Value &response) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-me.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-me.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerMe : public RemoteCommand {
 public:
-	ServerMe();
+	IRCCD_EXPORT ServerMe();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-message.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-message.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerMessage : public RemoteCommand {
 public:
-	ServerMessage();
+	IRCCD_EXPORT ServerMessage();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-mode.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-mode.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerMode : public RemoteCommand {
 public:
-	ServerMode();
+	IRCCD_EXPORT ServerMode();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-nick.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-nick.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerNick : public RemoteCommand {
 public:
-	ServerNick();
+	IRCCD_EXPORT ServerNick();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-notice.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-notice.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerNotice : public RemoteCommand {
 public:
-	ServerNotice();
+	IRCCD_EXPORT ServerNotice();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-part.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-part.cpp	Tue May 24 13:00:35 2016 +0200
@@ -51,9 +51,8 @@
 		{ "channel", args.arg(1) }
 	});
 
-	if (args.length() == 3) {
+	if (args.length() == 3)
 		req.insert("reason", args.arg(2));
-	}
 
 	return req;
 }
--- a/lib/irccd/cmd-server-part.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-part.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerPart : public RemoteCommand {
 public:
-	ServerPart();
+	IRCCD_EXPORT ServerPart();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-reconnect.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-reconnect.cpp	Tue May 24 13:00:35 2016 +0200
@@ -49,13 +49,11 @@
 {
 	auto server = request.find("server");
 
-	if (server != request.end() && server->isString()) {
+	if (server != request.end() && server->isString())
 		irccd.serverService().require(server->toString())->reconnect();
-	} else {
-		for (auto &server : irccd.serverService().servers()) {
+	else
+		for (auto &server : irccd.serverService().servers())
 			server->reconnect();
-		}
-	}
 
 	return nullptr;
 }
--- a/lib/irccd/cmd-server-reconnect.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-reconnect.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerReconnect : public RemoteCommand {
 public:
-	ServerReconnect();
+	IRCCD_EXPORT ServerReconnect();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-server-topic.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-server-topic.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,27 +36,27 @@
  */
 class ServerTopic : public RemoteCommand {
 public:
-	ServerTopic();
+	IRCCD_EXPORT ServerTopic();
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::args
 	 */
-	std::vector<Arg> args() const override;
+	IRCCD_EXPORT std::vector<Arg> args() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const override;
 
 	/**
 	 * \copydoc RemoteCommand::exec
 	 */
-	json::Value exec(Irccd &irccd, const json::Value &request) const override;
+	IRCCD_EXPORT json::Value exec(Irccd &irccd, const json::Value &request) const override;
 };
 
 } // !command
--- a/lib/irccd/cmd-watch.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-watch.cpp	Tue May 24 13:00:35 2016 +0200
@@ -212,23 +212,21 @@
 {
 	std::string format = request.optionOr("format", "native");
 
-	if (format != "native" && format != "json") {
+	if (format != "native" && format != "json")
 		throw std::invalid_argument("invalid format given: " + format);
-	}
 
 	while (ctl.connection().isConnected()) {
 		try {
 			auto object = ctl.connection().next(-1);
 			auto it = events.find(object.valueOr("event", "").toString());
 
-			/* Silently ignore to avoid breaking user output */
-			if (it == events.end()) {
+			// Silently ignore to avoid breaking user output.
+			if (it == events.end())
 				continue;
-			}
 
-			if (format == "json") {
+			if (format == "json")
 				std::cout << object.toJson() << std::endl;
-			} else {
+			else {
 				it->second(object);
 				std::cout << std::endl;
 			}
--- a/lib/irccd/cmd-watch.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/cmd-watch.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,19 +36,19 @@
  */
 class Watch : public RemoteCommand {
 public:
-	Watch();
+	IRCCD_EXPORT Watch();
 
-	std::vector<Option> options() const override;
+	IRCCD_EXPORT std::vector<Option> options() const override;
 
 	/**
 	 * \copydoc RemoteCommand::help
 	 */
-	std::string help() const override;
+	IRCCD_EXPORT std::string help() const override;
 
 	/**
 	 * \copydoc RemoteCommand::request
 	 */
-	json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &request) const override;
+	IRCCD_EXPORT json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &request) const override;
 };
 
 } // !command
--- a/lib/irccd/command.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/command.cpp	Tue May 24 13:00:35 2016 +0200
@@ -34,37 +34,35 @@
 
 	oss << "usage: " << sys::programName() << " " << m_name;
 
-	/* Options summary */
-	if (options().size() > 0) {
+	// Options summary.
+	if (options().size() > 0)
 		oss << " [options...]";
-	}
 
-	/* Arguments summary */
+	// Arguments summary.
 	if (args().size() > 0) {
 		oss << " ";
 
-		for (const auto &arg : args()) {
+		for (const auto &arg : args())
 			oss << (arg.required() ? "" : "[") << arg.name() << (arg.required() ? "" : "]") << " ";
-		}
 	}
 
-	/* Description */
+	// Description.
 	oss << "\n\n" << help() << "\n\n";
 
-	/* Options */
+	// Options.
 	if (options().size() > 0) {
 		oss << "Options:\n";
 
 		for (const auto &opt : options()) {
 			std::ostringstream optoss;
 
-			/* Construct the line for the option in a single string to pad it correctly */
+			// Construct the line for the option in a single string to pad it correctly.
 			optoss << "  ";
 			optoss << (!opt.simpleKey().empty() ? ("-"s + opt.simpleKey() + " ") : "   ");
 			optoss << (!opt.longKey().empty() ? ("--"s + opt.longKey() + " "s) : "");
 			optoss << opt.arg();
 
-			/* Add it padded with spaces */
+			// Add it padded with spaces.
 			oss << std::left << std::setw(28) << optoss.str();
 			oss << opt.description() << "\n";
 		}
@@ -101,9 +99,8 @@
 {
 	auto it = response.find("error");
 
-	if (it != response.end() && it->isString()) {
+	if (it != response.end() && it->isString())
 		log::warning() << "irccdctl: " << it->toString() << std::endl;
-	}
 }
 
 } // !irccd
--- a/lib/irccd/command.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/command.hpp	Tue May 24 13:00:35 2016 +0200
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include "json.hpp"
+#include "sysconfig.hpp"
 
 namespace irccd {
 
@@ -267,7 +268,7 @@
 	 *
 	 * \return the usage
 	 */
-	std::string usage() const;
+	IRCCD_EXPORT std::string usage() const;
 
 	/**
 	 * Return the help message.
@@ -301,14 +302,14 @@
 	 *
 	 * \return the minimum
 	 */
-	unsigned min() const noexcept;
+	IRCCD_EXPORT unsigned min() const noexcept;
 
 	/**
 	 * Get the maximum number of arguments required.
 	 *
 	 * \return the maximum
 	 */
-	unsigned max() const noexcept;
+	IRCCD_EXPORT unsigned max() const noexcept;
 
 	/**
 	 * Prepare a JSON request to the daemon.
@@ -322,7 +323,7 @@
 	 * \return the JSON object to send to the daemon
 	 * \post the returned JSON value must be an object
 	 */
-	virtual json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const;
+	IRCCD_EXPORT virtual json::Value request(Irccdctl &irccdctl, const RemoteCommandRequest &args) const;
 
 	/**
 	 * Execute the command in the daemon.
@@ -338,7 +339,7 @@
 	 * \param request the JSON request
 	 * \return the response
 	 */
-	virtual json::Value exec(Irccd &irccd, const json::Value &request) const;
+	IRCCD_EXPORT virtual json::Value exec(Irccd &irccd, const json::Value &request) const;
 
 	/**
 	 * What to do when receiving the response from irccd.
@@ -348,7 +349,7 @@
 	 * \param irccdctl the irccdctl instan e
 	 * \param response the JSON response
 	 */
-	virtual void result(Irccdctl &irccdctl, const json::Value &response) const;
+	IRCCD_EXPORT virtual void result(Irccdctl &irccdctl, const json::Value &response) const;
 };
 
 /**
--- a/lib/irccd/config.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/config.cpp	Tue May 24 13:00:35 2016 +0200
@@ -43,9 +43,8 @@
 private:
 	std::string convert(const std::string &tmpl, std::string input) const
 	{
-		if (tmpl.empty()) {
+		if (tmpl.empty())
 			return input;
-		}
 
 		util::Substitution params;
 
@@ -80,15 +79,13 @@
 {
 	auto its = doc.find(section);
 
-	if (its == doc.end()) {
+	if (its == doc.end())
 		return "";
-	}
 
 	auto ito = its->find(key);
 
-	if (ito == its->end()) {
+	if (ito == its->end())
 		return "";
-	}
 
 	return ito->value();
 }
@@ -106,18 +103,14 @@
 	// Optional stuff.
 	ini::Section::const_iterator it;
 
-	if ((it = sc.find("username")) != sc.end()) {
+	if ((it = sc.find("username")) != sc.end())
 		identity.username = it->value();
-	}
-	if ((it = sc.find("realname")) != sc.end()) {
+	if ((it = sc.find("realname")) != sc.end())
 		identity.realname = it->value();
-	}
-	if ((it = sc.find("nickname")) != sc.end()) {
+	if ((it = sc.find("nickname")) != sc.end())
 		identity.nickname = it->value();
-	}
-	if ((it = sc.find("ctcp-version")) != sc.end()) {
+	if ((it = sc.find("ctcp-version")) != sc.end())
 		identity.ctcpversion = it->value();
-	}
 
 	return identity;
 }
@@ -126,9 +119,8 @@
 {
 	PluginConfig config;
 
-	for (const auto &option : sc) {
+	for (const auto &option : sc)
 		config.emplace(option.key(), option.value());
-	}
 
 	return config;
 }
@@ -148,12 +140,10 @@
 
 	ini::Section::const_iterator it;
 
-	if ((it = sc.find("path-logs")) != sc.end()) {
+	if ((it = sc.find("path-logs")) != sc.end())
 		normal = it->value();
-	}
-	if ((it = sc.find("path-errors")) != sc.end()) {
+	if ((it = sc.find("path-errors")) != sc.end())
 		errors = it->value();
-	}
 
 	return std::make_unique<log::File>(std::move(normal), std::move(errors));
 }
@@ -174,12 +164,11 @@
 	std::shared_ptr<TransportServer> transport;
 	ini::Section::const_iterator it;
 
-	// Port
+	// Port.
 	int port;
 
-	if ((it = sc.find("port")) == sc.cend()) {
+	if ((it = sc.find("port")) == sc.cend())
 		throw std::invalid_argument("transport: missing 'port' parameter");
-	}
 
 	try {
 		port = util::toNumber<std::uint16_t>(it->value());
@@ -187,12 +176,11 @@
 		throw std::invalid_argument("transport: invalid port number: {}"_format(it->value()));
 	}
 
-	// Address
+	// Address.
 	std::string address = "*";
 
-	if ((it = sc.find("address")) != sc.end()) {
+	if ((it = sc.find("address")) != sc.end())
 		address = it->value();
-	}
 
 	// Domain
 	bool ipv6 = true;
@@ -203,22 +191,19 @@
 		ipv4 = false;
 
 		for (const auto &v : *it) {
-			if (v == "ipv4") {
+			if (v == "ipv4")
 				ipv4 = true;
-			}
-			if (v == "ipv6") {
+			if (v == "ipv6")
 				ipv6 = true;
-			}
 		}
 	}
 
-	if (ipv6) {
+	if (ipv6)
 		transport = std::make_shared<TransportServerIp>(AF_INET6, move(address), port, !ipv4);
-	} else if (ipv4) {
+	else if (ipv4)
 		transport = std::make_shared<TransportServerIp>(AF_INET, move(address), port);
-	} else {
+	else
 		throw std::invalid_argument("transport: domain must at least have ipv4 or ipv6");
-	}
 
 	return transport;
 }
@@ -230,9 +215,8 @@
 #if !defined(IRCCD_SYSTEM_WINDOWS)
 	ini::Section::const_iterator it = sc.find("path");
 
-	if (it == sc.end()) {
+	if (it == sc.end())
 		throw std::invalid_argument("transport: missing 'path' parameter");
-	}
 
 	return std::make_shared<TransportServerUnix>(it->value());
 #else
@@ -249,17 +233,15 @@
 	std::shared_ptr<TransportServer> transport;
 	ini::Section::const_iterator it = sc.find("type");
 
-	if (it == sc.end()) {
+	if (it == sc.end())
 		throw std::invalid_argument("transport: missing 'type' parameter");
-	}
 
-	if (it->value() == "ip") {
+	if (it->value() == "ip")
 		transport = loadTransportIp(sc);
-	} else if (it->value() == "unix") {
+	else if (it->value() == "unix")
 		transport = loadTransportUnix(sc);
-	} else {
+	else
 		throw std::invalid_argument("transport: invalid type given: {}"_format(it->value()));
-	}
 
 	return transport;
 }
@@ -268,7 +250,7 @@
 {
 	assert(sc.key() == "rule");
 
-	// Simple converter from std::vector to std::unordered_set
+	// Simple converter from std::vector to std::unordered_set.
 	auto toSet = [] (const std::vector<std::string> &v) -> std::unordered_set<std::string> {
 		return std::unordered_set<std::string>(v.begin(), v.end());
 	};
@@ -276,37 +258,30 @@
 	RuleSet servers, channels, origins, plugins, events;
 	RuleAction action = RuleAction::Accept;
 
-	// Get the sets
+	// Get the sets.
 	ini::Section::const_iterator it;
 
-	if ((it = sc.find("servers")) != sc.end()) {
+	if ((it = sc.find("servers")) != sc.end())
 		servers = toSet(*it);
-	}
-	if ((it = sc.find("channels")) != sc.end()) {
+	if ((it = sc.find("channels")) != sc.end())
 		channels = toSet(*it);
-	}
-	if ((it = sc.find("origins")) != sc.end()) {
+	if ((it = sc.find("origins")) != sc.end())
 		origins = toSet(*it);
-	}
-	if ((it = sc.find("plugins")) != sc.end()) {
+	if ((it = sc.find("plugins")) != sc.end())
 		plugins = toSet(*it);
-	}
-	if ((it = sc.find("channels")) != sc.end()) {
+	if ((it = sc.find("channels")) != sc.end())
 		channels = toSet(*it);
-	}
 
-	// Get the action
-	if ((it = sc.find("action")) == sc.end()) {
+	// Get the action.
+	if ((it = sc.find("action")) == sc.end())
 		throw std::invalid_argument("rule: missing 'action'' parameter");
-	}
 
-	if (it->value() == "drop") {
+	if (it->value() == "drop")
 		action = RuleAction::Drop;
-	} else if (it->value() == "accept") {
+	else if (it->value() == "accept")
 		action = RuleAction::Accept;
-	} else {
+	else
 		throw std::invalid_argument("rule: invalid action given: {}"_format(it->value()));
-	}
 
 	return Rule(std::move(servers),
 		    std::move(channels),
@@ -325,28 +300,25 @@
 	ServerIdentity identity;
 	ServerSettings settings;
 
-	// Name
+	// Name.
 	ini::Section::const_iterator it;
 
-	if ((it = sc.find("name")) == sc.end()) {
+	if ((it = sc.find("name")) == sc.end())
 		throw std::invalid_argument("server: missing 'name' parameter");
-	} else if (!util::isIdentifierValid(it->value())) {
+	else if (!util::isIdentifierValid(it->value()))
 		throw std::invalid_argument("server: invalid identifier: {}"_format(it->value()));
-	}
 
 	name = it->value();
 
 	// Host
-	if ((it = sc.find("host")) == sc.end()) {
+	if ((it = sc.find("host")) == sc.end())
 		throw std::invalid_argument("server {}: missing host"_format(name));
-	}
 
 	info.host = it->value();
 
 	// Optional identity
-	if ((it = sc.find("identity")) != sc.end()) {
+	if ((it = sc.find("identity")) != sc.end())
 		identity = config.findIdentity(it->value());
-	}
 
 	// Optional port
 	if ((it = sc.find("port")) != sc.end()) {
@@ -358,32 +330,26 @@
 	}
 
 	// Optional password
-	if ((it = sc.find("password")) != sc.end()) {
+	if ((it = sc.find("password")) != sc.end())
 		info.password = it->value();
-	}
 
 	// Optional flags
-	if ((it = sc.find("ipv6")) != sc.end() && util::isBoolean(it->value())) {
+	if ((it = sc.find("ipv6")) != sc.end() && util::isBoolean(it->value()))
 		info.flags |= ServerInfo::Ipv6;
-	}
 	if ((it = sc.find("ssl")) != sc.end()) {
-		if (util::isBoolean(it->value())) {
+		if (util::isBoolean(it->value()))
 			info.flags |= ServerInfo::Ssl;
-		}
 	}
 	if ((it = sc.find("ssl-verify")) != sc.end()) {
-		if (util::isBoolean(it->value())) {
+		if (util::isBoolean(it->value()))
 			info.flags |= ServerInfo::SslVerify;
-		}
 	}
 
 	// Options
-	if ((it = sc.find("auto-rejoin")) != sc.end() && util::isBoolean(it->value())) {
+	if ((it = sc.find("auto-rejoin")) != sc.end() && util::isBoolean(it->value()))
 		settings.flags |= ServerSettings::AutoRejoin;
-	}
-	if ((it = sc.find("join-invite")) != sc.end() && util::isBoolean(it->value())) {
+	if ((it = sc.find("join-invite")) != sc.end() && util::isBoolean(it->value()))
 		settings.flags |= ServerSettings::JoinInvite;
-	}
 
 	// Channels
 	if ((it = sc.find("channels")) != sc.end()) {
@@ -393,28 +359,23 @@
 			if (auto pos = s.find(":") != std::string::npos) {
 				channel.name = s.substr(0, pos);
 				channel.password = s.substr(pos + 1);
-			} else {
+			} else
 				channel.name = s;
-			}
 
 			settings.channels.push_back(std::move(channel));
 		}
 	}
-	if ((it = sc.find("command-char")) != sc.end()) {
+	if ((it = sc.find("command-char")) != sc.end())
 		settings.command = it->value();
-	}
 
 	// Reconnect and ping timeout
 	try {
-		if ((it = sc.find("reconnect-tries")) != sc.end()) {
+		if ((it = sc.find("reconnect-tries")) != sc.end())
 			settings.reconnectTries = util::toNumber<std::int8_t>(it->value());
-		}
-		if ((it = sc.find("reconnect-timeout")) != sc.end()) {
+		if ((it = sc.find("reconnect-timeout")) != sc.end())
 			settings.reconnectDelay = util::toNumber<std::uint16_t>(it->value());
-		}
-		if ((it = sc.find("ping-timeout")) != sc.end()) {
+		if ((it = sc.find("ping-timeout")) != sc.end())
 			settings.pingTimeout = util::toNumber<std::uint16_t>(it->value());
-		}
 	} catch (const std::exception &) {
 		log::warning("server {}: invalid number for {}: {}"_format(name, it->key(), it->value()));
 	}
@@ -429,9 +390,8 @@
 	for (const auto &path : path::list(path::PathConfig)) {
 		std::string fullpath = path + "irccd.conf";
 
-		if (!fs::isReadable(fullpath)) {
+		if (!fs::isReadable(fullpath))
 			continue;
-		}
 
 		try {
 			return Config(fullpath);
@@ -448,9 +408,8 @@
 	assert(util::isIdentifierValid(name));
 
 	for (const auto &section : m_document) {
-		if (section.key() != "identity") {
+		if (section.key() != "identity")
 			continue;
-		}
 
 		auto it = section.find("name");
 
@@ -462,9 +421,8 @@
 			log::warning("identity: invalid identifier: {}"_format(it->value()));
 			continue;
 		}
-		if (it->value() != name) {
+		if (it->value() != name)
 			continue;
-		}
 
 		return loadIdentity(section);
 	}
@@ -479,9 +437,8 @@
 	std::string fullname = std::string("plugin.") + name;
 
 	for (const auto &section : m_document) {
-		if (section.key() != fullname) {
+		if (section.key() != fullname)
 			continue;
-		}
 
 		return loadPluginConfig(section);
 	}
@@ -535,27 +492,24 @@
 {
 	ini::Document::const_iterator sc = m_document.find("logs");
 
-	if (sc == m_document.end()) {
+	if (sc == m_document.end())
 		return;
-	}
 
 	ini::Section::const_iterator it;
 
 	if ((it = sc->find("type")) != sc->end()) {
 		std::unique_ptr<log::Interface> iface;
 
-		/* Console is the default, no test case */
-		if (it->value() == "file") {
+		// Console is the default, no test case.
+		if (it->value() == "file")
 			iface = loadLogFile(*sc);
-		} else if (it->value() == "syslog") {
+		else if (it->value() == "syslog")
 			iface = loadLogSyslog();
-		} else {
+		else
 			throw std::runtime_error("logs: unknown log type: {}"_format(it->value()));
-		}
 
-		if (iface) {
+		if (iface)
 			log::setInterface(std::move(iface));
-		}
 	}
 }
 
@@ -563,22 +517,18 @@
 {
 	ini::Document::const_iterator sc = m_document.find("format");
 
-	if (sc == m_document.end()) {
+	if (sc == m_document.end())
 		return;
-	}
 
 	std::unique_ptr<IrccdLogFilter> filter = std::make_unique<IrccdLogFilter>();
 	ini::Section::const_iterator it;
 
-	if ((it = sc->find("debug")) != sc->cend()) {
+	if ((it = sc->find("debug")) != sc->cend())
 		filter->m_debug = it->value();
-	}
-	if ((it = sc->find("info")) != sc->cend()) {
+	if ((it = sc->find("info")) != sc->cend())
 		filter->m_info = it->value();
-	}
-	if ((it = sc->find("warning")) != sc->cend()) {
+	if ((it = sc->find("warning")) != sc->cend())
 		filter->m_warning = it->value();
-	}
 
 	log::setFilter(std::move(filter));
 }
@@ -587,11 +537,9 @@
 {
 	std::vector<std::shared_ptr<TransportServer>> transports;
 
-	for (const auto &section : m_document) {
-		if (section.key() == "transport") {
+	for (const auto &section : m_document)
+		if (section.key() == "transport")
 			transports.push_back(loadTransport(section));
-		}
-	}
 
 	return transports;
 }
@@ -600,11 +548,9 @@
 {
 	std::vector<Rule> rules;
 
-	for (const auto &section : m_document) {
-		if (section.key() == "rule") {
+	for (const auto &section : m_document)
+		if (section.key() == "rule")
 			rules.push_back(loadRule(section));
-		}
-	}
 
 	return rules;
 }
@@ -614,9 +560,8 @@
 	std::vector<std::shared_ptr<Server>> servers;
 
 	for (const auto &section : m_document) {
-		if (section.key() != "server") {
+		if (section.key() != "server")
 			continue;
-		}
 
 		try {
 			servers.push_back(loadServer(section, *this));
@@ -634,9 +579,8 @@
 
 	if (it != m_document.end()) {
 		for (const auto &option : *it) {
-			if (!util::isIdentifierValid(option.key())) {
+			if (!util::isIdentifierValid(option.key()))
 				continue;
-			}
 
 			irccd.pluginService().configure(option.key(), findPluginConfig(option.key()));
 			irccd.pluginService().setFormats(option.key(), findPluginFormats(option.key()));
--- a/lib/irccd/config.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/config.hpp	Tue May 24 13:00:35 2016 +0200
@@ -30,6 +30,7 @@
 
 #include "ini.hpp"
 #include "plugin.hpp"
+#include "sysconfig.hpp"
 
 namespace irccd {
 
@@ -55,7 +56,7 @@
 	 * \return the config
 	 * \throw std::exception on errors or if no config could be found
 	 */
-	static Config find();
+	IRCCD_EXPORT static Config find();
 
 	/**
 	 * Load the configuration from the specified path.
@@ -84,7 +85,7 @@
 	 * \pre util::isValidIdentifier(name)
 	 * \return default identity if cannot be found
 	 */
-	ServerIdentity findIdentity(const std::string &name) const;
+	IRCCD_EXPORT ServerIdentity findIdentity(const std::string &name) const;
 
 	/**
 	 * Find a plugin configuration if defined in the configuration file.
@@ -92,7 +93,7 @@
 	 * \pre util::isValidIdentifier(name)
 	 * \return the configuration or empty if not found
 	 */
-	PluginConfig findPluginConfig(const std::string &name) const;
+	IRCCD_EXPORT PluginConfig findPluginConfig(const std::string &name) const;
 
 	/**
 	 * Find plugin formats if defined.
@@ -100,73 +101,73 @@
 	 * \pre util::isValidIdentifier(name)
 	 * \return the formats or empty one if not found
 	 */
-	PluginFormats findPluginFormats(const std::string &name) const;
+	IRCCD_EXPORT PluginFormats findPluginFormats(const std::string &name) const;
 
 	/**
 	 * Get the path to the pidfile.
 	 *
 	 * \return the path or empty if not defined
 	 */
-	std::string pidfile() const;
+	IRCCD_EXPORT std::string pidfile() const;
 
 	/**
 	 * Get the uid.
 	 *
 	 * \return the uid or empty one if no one is set
 	 */
-	std::string uid() const;
+	IRCCD_EXPORT std::string uid() const;
 
 	/**
 	 * Get the gid.
 	 *
 	 * \return the gid or empty one if no one is set
 	 */
-	std::string gid() const;
+	IRCCD_EXPORT std::string gid() const;
 
 	/**
 	 * Check if verbosity is enabled.
 	 *
 	 * \return true if verbosity was requested
 	 */
-	bool isVerbose() const noexcept;
+	IRCCD_EXPORT bool isVerbose() const noexcept;
 
 	/**
 	 * Check if foreground is specified (= no daemonize).
 	 *
 	 * \return true if foreground was requested
 	 */
-	bool isForeground() const noexcept;
+	IRCCD_EXPORT bool isForeground() const noexcept;
 
 	/**
 	 * Load logging interface.
 	 */
-	void loadLogs() const;
+	IRCCD_EXPORT void loadLogs() const;
 
 	/**
 	 * Load formats for logging.
 	 */
-	void loadFormats() const;
+	IRCCD_EXPORT void loadFormats() const;
 
 	/**
 	 * Load transports.
 	 *
 	 * \return the set of transports
 	 */
-	std::vector<std::shared_ptr<TransportServer>> loadTransports() const;
+	IRCCD_EXPORT std::vector<std::shared_ptr<TransportServer>> loadTransports() const;
 
 	/**
 	 * Load rules.
 	 *
 	 * \return the rules
 	 */
-	std::vector<Rule> loadRules() const;
+	IRCCD_EXPORT std::vector<Rule> loadRules() const;
 
 	/**
 	 * Get the list of servers defined.
 	 *
 	 * \return the list of servers
 	 */
-	std::vector<std::shared_ptr<Server>> loadServers() const;
+	IRCCD_EXPORT std::vector<std::shared_ptr<Server>> loadServers() const;
 
 	/**
 	 * Get the list of defined plugins.
@@ -174,7 +175,7 @@
 	 * \param irccd the irccd instance
 	 * \return the list of plugins
 	 */
-	void loadPlugins(Irccd &irccd) const;
+	IRCCD_EXPORT void loadPlugins(Irccd &irccd) const;
 };
 
 } // !irccd
--- a/lib/irccd/connection.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/connection.cpp	Tue May 24 13:00:35 2016 +0200
@@ -28,9 +28,8 @@
 	while (isConnected()) {
 		json::Value object = next(clamp(timeout));
 
-		if (object.isObject() && object["response"].toString() == name) {
+		if (object.isObject() && object["response"].toString() == name)
 			return object;
-		}
 	}
 
 	throw std::runtime_error("connection lost");
@@ -41,9 +40,8 @@
 	auto object = next(name, timeout);
 	auto value = object.at("status").toString();
 
-	if (!value.empty() && value != "ok") {
+	if (!value.empty() && value != "ok")
 		throw std::runtime_error(object.at("error").toString());
-	}
 }
 
 } // !irccd
--- a/lib/irccd/connection.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/connection.hpp	Tue May 24 13:00:35 2016 +0200
@@ -30,6 +30,7 @@
 #include "elapsed-timer.hpp"
 #include "json.hpp"
 #include "sockets.hpp"
+#include "sysconfig.hpp"
 #include "system.hpp"
 #include "util.hpp"
 
@@ -73,7 +74,7 @@
 	 * \return the object
 	 * \throw net::Error on errors or on timeout
 	 */
-	json::Value next(const std::string &name, int timeout = 30000);
+	IRCCD_EXPORT json::Value next(const std::string &name, int timeout = 30000);
 
 	/**
 	 * Just wait if the operation succeeded.
@@ -81,7 +82,7 @@
 	 * \param name the response name
 	 * \param timeout the timeout
 	 */
-	void verify(const std::string &name, int timeout = 30000);
+	IRCCD_EXPORT void verify(const std::string &name, int timeout = 30000);
 
 	/**
 	 * Check if the socket is still connected.
@@ -131,7 +132,7 @@
 	net::Listener<> m_listener;
 	Address m_address;
 
-	/* Input buffer */
+	// Input buffer.
 	std::string m_input;
 
 public:
@@ -140,7 +141,7 @@
 	 *
 	 * \param address the address
 	 */
-	ConnectionBase(Address address)
+	inline ConnectionBase(Address address)
 		: m_address(std::move(address))
 	{
 		m_socket.set(net::option::SockBlockMode{false});
@@ -150,7 +151,7 @@
 	/**
 	 * \copydoc Connection::isConnected
 	 */
-	bool isConnected() const noexcept override
+	inline bool isConnected() const noexcept override
 	{
 		return m_socket.state() == net::State::Connected;
 	}
@@ -189,7 +190,7 @@
 {
 	assert(!msg.empty());
 
-	/* Add termination */
+	// Add termination.
 	msg += "\r\n\r\n";
 
 	m_listener.remove(m_socket.handle());
@@ -197,48 +198,45 @@
 	m_timer.reset();
 
 	while (!msg.empty()) {
-		/* Do not wait the time that is already passed */
+		// Do not wait the time that is already passed.
 		m_listener.wait(clamp(timeout));
 
-		/* Try to send at most as possible */
+		// Try to send at most as possible.
 		msg.erase(0, m_socket.send(msg));
 	}
 
-	/* Timeout? */
-	if (!msg.empty()) {
+	// Timeout?
+	if (!msg.empty())
 		throw std::runtime_error("operation timed out while sending to irccd");
-	}
 }
 
 template <typename Address>
 json::Value ConnectionBase<Address>::next(int timeout)
 {
-	/* Maybe there is already something */
+	// Maybe there is already something.
 	std::string buffer = util::nextNetwork(m_input);
 
 	m_listener.remove(m_socket.handle());
 	m_listener.set(m_socket.handle(), net::Condition::Readable);
 	m_timer.reset();
 
-	/* Read if there is nothing */
+	// Read if there is nothing.
 	while (buffer.empty() && isConnected()) {
-		/* Wait and read */
+		// Wait and read.
 		m_listener.wait(clamp(timeout));
 		m_input += m_socket.recv(512);
 
-		/* Finally try */
+		// Finally try.
 		buffer = util::nextNetwork(m_input);
 	}
 
-	if (!isConnected()) {
+	if (!isConnected())
 		throw std::runtime_error("connection lost");
-	}
 
 	json::Value value(json::Buffer{buffer});
 
-	if (!value.isObject()) {
+	if (!value.isObject())
 		throw std::invalid_argument("invalid message received");
-	}
 
 	return value;
 }
--- a/lib/irccd/elapsed-timer.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/elapsed-timer.hpp	Tue May 24 13:00:35 2016 +0200
@@ -26,6 +26,8 @@
 
 #include <chrono>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 /**
@@ -50,7 +52,7 @@
 	/**
 	 * Construct the elapsed timer, start counting.
 	 */
-	ElapsedTimer() noexcept;
+	IRCCD_EXPORT ElapsedTimer() noexcept;
 
 	/**
 	 * Virtual destructor defaulted.
@@ -60,24 +62,24 @@
 	/**
 	 * Put the timer on pause, the already elapsed time is stored.
 	 */
-	void pause() noexcept;
+	IRCCD_EXPORT void pause() noexcept;
 
 	/**
 	 * Restart the timer, does not reset it.
 	 */
-	void restart() noexcept;
+	IRCCD_EXPORT void restart() noexcept;
 
 	/**
 	 * Reset the timer to 0.
 	 */
-	void reset() noexcept;
+	IRCCD_EXPORT void reset() noexcept;
 
 	/**
 	 * Get the number of elapsed milliseconds.
 	 *
 	 * \return the milliseconds
 	 */
-	unsigned elapsed() noexcept;
+	IRCCD_EXPORT unsigned elapsed() noexcept;
 };
 
 } // !irccd
--- a/lib/irccd/fs.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/fs.cpp	Tue May 24 13:00:35 2016 +0200
@@ -69,9 +69,8 @@
 {
 	auto fp = std::fopen(path.c_str(), mode.c_str());
 
-	if (fp == nullptr) {
+	if (fp == nullptr)
 		return false;
-	}
 
 	std::fclose(fp);
 
@@ -112,17 +111,17 @@
 	if (input.empty())
 		return input;
 
-	/* First, remove any duplicates */
+	// First, remove any duplicates.
 	input.erase(std::unique(input.begin(), input.end(), [&] (char c1, char c2) {
 		return c1 == c2 && (c1 == '/' || c1 == '\\');
 	}), input.end());
 
-	/* Add a trailing / or \\ */
+	// Add a trailing / or \\.
 	char c = input[input.length() - 1];
 	if (c != '/' && c != '\\')
 		input += separator();
 
-	/* Now converts all / to \\ for Windows and the opposite for Unix */
+	// Now converts all / to \\ for Windows and the opposite for Unix.
 #if defined(_WIN32)
 	std::replace(input.begin(), input.end(), '/', '\\');
 #else
--- a/lib/irccd/fs.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/fs.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,6 +36,8 @@
 #include <string>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 namespace fs {
@@ -70,24 +72,6 @@
 };
 
 /**
- * Check if two entries are identical.
- *
- * \param e1 the first entry
- * \param e2 the second entry
- * \return true if they are identical
- */
-bool operator==(const Entry &e1, const Entry &e2) noexcept;
-
-/**
- * Check if two entries are different.
- *
- * \param e1 the first entry
- * \param e2 the second entry
- * \return true if they are different
- */
-bool operator!=(const Entry &e1, const Entry &e2) noexcept;
-
-/**
  * Get the separator for that system.
  *
  * \return \ on Windows and / otherwise
@@ -107,7 +91,7 @@
  * \param path the path
  * \return the updated path
  */
-std::string clean(std::string path);
+IRCCD_EXPORT std::string clean(std::string path);
 
 /**
  * Get the base name from a path.
@@ -117,7 +101,7 @@
  * \param path the path
  * \return the base name
  */
-std::string baseName(std::string path);
+IRCCD_EXPORT std::string baseName(std::string path);
 
 /**
  * Get the parent directory from a path.
@@ -127,7 +111,7 @@
  * \param path the path
  * \return the parent directory
  */
-std::string dirName(std::string path);
+IRCCD_EXPORT std::string dirName(std::string path);
 
 /**
  * Get stat information.
@@ -136,7 +120,7 @@
  * \return the stat information
  * \throw std::runtime_error on failure
  */
-struct stat stat(const std::string &path);
+IRCCD_EXPORT struct stat stat(const std::string &path);
 
 /**
  * Check if a file exists.
@@ -146,7 +130,7 @@
  * \param path the path to check
  * \return true if the path exists
  */
-bool exists(const std::string &path) noexcept;
+IRCCD_EXPORT bool exists(const std::string &path) noexcept;
 
 /**
  * Check if the path is absolute.
@@ -154,7 +138,7 @@
  * \param path the path
  * \return true if the path is absolute
  */
-bool isAbsolute(const std::string &path) noexcept;
+IRCCD_EXPORT bool isAbsolute(const std::string &path) noexcept;
 
 /**
  * Check if the path is relative.
@@ -162,7 +146,7 @@
  * \param path the path
  * \return true if the path is absolute
  */
-bool isRelative(const std::string &path) noexcept;
+IRCCD_EXPORT bool isRelative(const std::string &path) noexcept;
 
 /**
  * Check if the file is readable.
@@ -170,7 +154,7 @@
  * \param path the path
  * \return true if has read access
  */
-bool isReadable(const std::string &path) noexcept;
+IRCCD_EXPORT bool isReadable(const std::string &path) noexcept;
 
 /**
  * Check if the file is writable.
@@ -178,7 +162,7 @@
  * \param path the path
  * \return true if has write access
  */
-bool isWritable(const std::string &path) noexcept;
+IRCCD_EXPORT bool isWritable(const std::string &path) noexcept;
 
 /**
  * Check if the file is a regular file.
@@ -186,7 +170,7 @@
  * \param path the path
  * \return true if it is a file and false if not or not readable
  */
-bool isFile(const std::string &path) noexcept;
+IRCCD_EXPORT bool isFile(const std::string &path) noexcept;
 
 /**
  * Check if the file is a directory.
@@ -194,7 +178,7 @@
  * \param path the path
  * \return true if it is a directory and false if not or not readable
  */
-bool isDirectory(const std::string &path) noexcept;
+IRCCD_EXPORT bool isDirectory(const std::string &path) noexcept;
 
 /**
  * Check if the file is a symbolic link.
@@ -202,7 +186,7 @@
  * \param path the path
  * \return true if it is a symbolic link and false if not or not readable
  */
-bool isSymlink(const std::string &path) noexcept;
+IRCCD_EXPORT bool isSymlink(const std::string &path) noexcept;
 
 /**
  * Read a directory and return a list of entries (not recursive).
@@ -212,7 +196,7 @@
  * \return the list of entries
  * \throw std::runtime_error on failure
  */
-std::vector<Entry> readdir(const std::string &path, int flags = 0);
+IRCCD_EXPORT std::vector<Entry> readdir(const std::string &path, int flags = 0);
 
 /**
  * Create a directory recursively.
@@ -222,7 +206,7 @@
  * \throw std::runtime_error on failure
  * \post all intermediate directories are created
  */
-void mkdir(const std::string &path, int mode = 0700);
+IRCCD_EXPORT void mkdir(const std::string &path, int mode = 0700);
 
 /**
  * Remove a directory recursively.
@@ -231,7 +215,7 @@
  *
  * \param path the path
  */
-void rmdir(const std::string &path) noexcept;
+IRCCD_EXPORT void rmdir(const std::string &path) noexcept;
 
 /**
  * Search an item recursively.
@@ -311,7 +295,7 @@
  * \return the current working directory
  * \throw std::runtime_error on failure
  */
-std::string cwd();
+IRCCD_EXPORT std::string cwd();
 
 } // !fs
 
--- a/lib/irccd/ini.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/ini.hpp	Tue May 24 13:00:35 2016 +0200
@@ -110,6 +110,8 @@
 #include <string>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 /**
@@ -559,7 +561,7 @@
  * \return the list of tokens
  * \throws Error on errors
  */
-Tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end);
+IRCCD_EXPORT Tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end);
 
 /**
  * Overloaded function for stream.
@@ -568,7 +570,7 @@
  * \return the list of tokens
  * \throws Error on errors
  */
-Tokens analyse(std::istream &stream);
+IRCCD_EXPORT Tokens analyse(std::istream &stream);
 
 /**
  * Parse the produced tokens.
@@ -578,7 +580,7 @@
  * \return the document
  * \throw Error on errors
  */
-Document parse(const Tokens &tokens, const std::string &path = ".");
+IRCCD_EXPORT Document parse(const Tokens &tokens, const std::string &path = ".");
 
 /**
  * Parse a file.
@@ -587,7 +589,7 @@
  * \return the document
  * \throw Error on errors
  */
-Document readFile(const std::string &filename);
+IRCCD_EXPORT Document readFile(const std::string &filename);
 
 /**
  * Parse a string.
@@ -598,14 +600,14 @@
  * \return the document
  * \throw Error on errors
  */
-Document readString(const std::string &buffer);
+IRCCD_EXPORT Document readString(const std::string &buffer);
 
 /**
  * Show all tokens and their description.
  *
  * \param tokens the tokens
  */
-void dump(const Tokens &tokens);
+IRCCD_EXPORT void dump(const Tokens &tokens);
 
 } // !ini
 
--- a/lib/irccd/irccd.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/irccd.cpp	Tue May 24 13:00:35 2016 +0200
@@ -70,9 +70,8 @@
 	FD_ZERO(&setinput);
 	FD_ZERO(&setoutput);
 
-	for (const auto &service : m_services) {
+	for (const auto &service : m_services)
 		service->prepare(setinput, setoutput, max);
-	}
 
 	// Do the selection.
 	struct timeval tv;
@@ -83,9 +82,8 @@
 	int error = select(max + 1, &setinput, &setoutput, nullptr, &tv);
 
 	// Skip anyway if requested to stop
-	if (!m_running) {
+	if (!m_running)
 		return;
-	}
 
 	// Skip on error.
 	if (error < 0 && errno != EINTR) {
@@ -94,9 +92,8 @@
 	}
 
 	// Process after selection.
-	for (const auto &service : m_services) {
+	for (const auto &service : m_services)
 		service->sync(setinput, setoutput);
-	}
 }
 
 void Irccd::dispatch()
@@ -114,13 +111,11 @@
 		m_events.clear();
 	}
 
-	if (copy.size() > 0) {
+	if (copy.size() > 0)
 		log::debug() << "irccd: dispatching " << copy.size() << " event" << (copy.size() > 1 ? "s" : "") << endl;
-	}
 
-	for (auto &ev : copy) {
+	for (auto &ev : copy)
 		ev(*this);
-	}
 }
 
 void Irccd::stop()
--- a/lib/irccd/irccd.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/irccd.hpp	Tue May 24 13:00:35 2016 +0200
@@ -32,6 +32,7 @@
 #include <vector>
 
 #include "application.hpp"
+#include "sysconfig.hpp"
 
 namespace irccd {
 
@@ -75,7 +76,7 @@
 	/**
 	 * Prepare standard services.
 	 */
-	Irccd();
+	IRCCD_EXPORT Irccd();
 
 	/**
 	 * Add a generic service.
@@ -144,27 +145,27 @@
 	 * \param ev the event
 	 * \note Thread-safe
 	 */
-	void post(std::function<void (Irccd &)> ev) noexcept;
+	IRCCD_EXPORT void post(std::function<void (Irccd &)> ev) noexcept;
 
 	/**
 	 * Loop forever by calling poll() and dispatch() indefinitely.
 	 */
-	void run();
+	IRCCD_EXPORT void run();
 
 	/**
 	 * Poll the next events without blocking (250 ms max).
 	 */
-	void poll();
+	IRCCD_EXPORT void poll();
 
 	/**
 	 * Dispatch the pending events, usually after calling poll().
 	 */
-	void dispatch();
+	IRCCD_EXPORT void dispatch();
 
 	/**
 	 * Request to stop, usually from a signal.
 	 */
-	void stop();
+	IRCCD_EXPORT void stop();
 };
 
 } // !irccd
--- a/lib/irccd/irccdctl.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/irccdctl.cpp	Tue May 24 13:00:35 2016 +0200
@@ -124,21 +124,19 @@
 {
 	ini::Section::const_iterator it;
 
-	/* host */
+	// Host.
 	std::string host;
 
-	if ((it = sc.find("host")) == sc.end()) {
+	if ((it = sc.find("host")) == sc.end())
 		throw std::invalid_argument("missing host parameter");
-	}
 
 	host = it->value();
 
-	/* port */
+	// Port.
 	int port;
 
-	if ((it = sc.find("port")) == sc.end()) {
+	if ((it = sc.find("port")) == sc.end())
 		throw std::invalid_argument("missing port parameter");
-	}
 
 	try {
 		port = std::stoi(it->value());
@@ -146,17 +144,16 @@
 		throw std::invalid_argument("invalid port number: " + it->value());
 	}
 
-	/* domain */
+	// Domain.
 	Ip::Type domain{Ip::v4};
 
 	if ((it = sc.find("domain")) != sc.end()) {
-		if (it->value() == "ipv6") {
+		if (it->value() == "ipv6")
 			domain = Ip::v6;
-		} else if (it->value() == "ipv4") {
+		else if (it->value() == "ipv4")
 			domain = Ip::v4;
-		} else {
+		else
 			throw std::invalid_argument("invalid domain: " + it->value());
-		}
 	}
 
 	m_connection = std::make_unique<ConnectionBase<Ip>>(Ip{host, port, domain});
@@ -183,66 +180,59 @@
 {
 	auto it = sc.find("type");
 
-	if (it == sc.end()) {
+	if (it == sc.end())
 		throw std::invalid_argument("missing type parameter");
-	}
 
-	if (it->value() == "ip") {
+	if (it->value() == "ip")
 		readConnectIp(sc);
-	} else if (it->value() == "unix") {
+	else if (it->value() == "unix")
 		readConnectUnix(sc);
-	} else {
+	else
 		throw std::invalid_argument("invalid type given: " + it->value());
-	}
 }
 
 void Irccdctl::readGeneral(const ini::Section &sc)
 {
 	auto verbose = sc.find("verbose");
 
-	if (verbose != sc.end()) {
+	if (verbose != sc.end())
 		log::setVerbose(util::isBoolean(verbose->value()));
-	}
 }
 
 void Irccdctl::readAliases(const ini::Section &sc)
 {
 	for (const ini::Option &option : sc) {
-		/* This is the alias name */
+		// This is the alias name.
 		Alias alias(option.key());
 
-		if (m_commands.count(option.key()) > 0) {
+		if (m_commands.count(option.key()) > 0)
 			throw std::invalid_argument("there is already a command named " + option.key());
-		}
 
-		/* Iterate over the list of commands to execute for this alias */
+		// Iterate over the list of commands to execute for this alias.
 		for (const std::string &repl : option) {
-			/* This is the alias split string */
+			// This is the alias split string.
 			std::vector<std::string> list = util::split(repl, " \t");
 
-			if (list.size() < 1) {
+			if (list.size() < 1)
 				throw std::invalid_argument("alias require at least one argument");
-			}
 
-			/* First argument is the command/alias to execute */
+			// First argument is the command/alias to execute.
 			std::string command = list[0];
 
-			/* This is the alias arguments */
+			// This is the alias arguments.
 			std::vector<AliasArg> args;
 
-			for (auto it = list.begin() + 1; it != list.end(); ++it) {
+			for (auto it = list.begin() + 1; it != list.end(); ++it)
 				args.push_back(std::move(*it));
-			}
 
 			alias.push_back({std::move(command), std::move(args)});
 		}
 
-		/* Show for debugging purpose */
+		// Show for debugging purpose.
 		log::debug("alias {}:"_format(option.key()));
 
-		for (const auto &cmd : alias) {
+		for (const auto &cmd : alias)
 			log::debug("  {} {}"_format(cmd.command(), util::join(cmd.args().begin(), cmd.args().end(), ' ')));
-		}
 
 		m_aliases.emplace(option.key(), std::move(alias));
 	}
@@ -253,20 +243,17 @@
 	ini::Document doc = ini::readFile(path);
 	ini::Document::const_iterator it = doc.find("connect");
 
-	/* Do not try to read [connect] if specified at command line */
-	if (it != doc.end() && options.count("-t") == 0 && options.count("--type") == 0) {
+	// Do not try to read [connect] if specified at command line.
+	if (it != doc.end() && options.count("-t") == 0 && options.count("--type") == 0)
 		readConnect(*it);
-	}
-	if ((it = doc.find("general")) != doc.end()) {
+	if ((it = doc.find("general")) != doc.end())
 		readGeneral(*it);
-	}
-	if ((it = doc.find("alias")) != doc.end()) {
+	if ((it = doc.find("alias")) != doc.end())
 		readAliases(*it);
-	}
 }
 
 /*
- * Initialize a connection from the command line
+ * Initialize a connection from the command line.
  * ------------------------------------------------------------------
  */
 
@@ -274,21 +261,19 @@
 {
 	parser::Result::const_iterator it;
 
-	/* host (-h or --host) */
+	// Host (-h or --host).
 	std::string host;
 
-	if ((it = options.find("-h")) == options.end() && (it = options.find("--host")) == options.end()) {
+	if ((it = options.find("-h")) == options.end() && (it = options.find("--host")) == options.end())
 		throw std::invalid_argument("missing host argument (-h or --host)");
-	}
 
 	host = it->second;
 
-	/* port (-p or --port) */
+	// Port (-p or --port).
 	int port;
 
-	if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end()) {
+	if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end())
 		throw std::invalid_argument("missing port argument (-p or --port)");
-	}
 
 	try {
 		port = std::stoi(it->second);
@@ -304,9 +289,8 @@
 #if !defined(IRCCD_SYSTEM_WINDOWS)
 	parser::Result::const_iterator it;
 
-	if ((it = options.find("-P")) == options.end() && (it = options.find("--path")) == options.end()) {
+	if ((it = options.find("-P")) == options.end() && (it = options.find("--path")) == options.end())
 		throw std::invalid_argument("missing path parameter (-P or --path)");
-	}
 
 	m_connection = std::make_unique<ConnectionBase<Local>>(Local{it->second, false});
 #else
@@ -322,22 +306,19 @@
 
 	auto it = options.find("-t");
 
-	if (it == options.end()) {
+	if (it == options.end())
 		it = options.find("--type");
-	}
-	if (it->second == "ip" || it->second == "ipv6") {
+	if (it->second == "ip" || it->second == "ipv6")
 		return parseConnectIp(options, it->second == "ipv6");
-	}
-	if (it->second == "unix") {
+	if (it->second == "unix")
 		return parseConnectUnix(options);
-	}
 
 	throw std::invalid_argument("invalid type given: " + it->second);
 }
 
 parser::Result Irccdctl::parse(int &argc, char **&argv) const
 {
-	/* 1. Parse command line options */
+	// 1. Parse command line options.
 	parser::Options def{
 		{ "-c",		true	},
 		{ "--config",	true	},
@@ -364,9 +345,8 @@
 			// NOTREACHED
 		}
 
-		if (result.count("-v") != 0 || result.count("--verbose") != 0) {
+		if (result.count("-v") != 0 || result.count("--verbose") != 0)
 			log::setVerbose(true);
-		}
 	} catch (const std::exception &ex) {
 		log::warning("{}: {}"_format(sys::programName(), ex.what()));
 		usage();
@@ -377,20 +357,18 @@
 
 void Irccdctl::exec(const RemoteCommand &cmd, std::vector<std::string> args)
 {
-	/* 1. Build options from command line arguments. */
+	// 1. Build options from command line arguments.
 	parser::Options def;
 
 	for (const auto &opt : cmd.options()) {
-		/* parser::read needs '-' and '--' so add them */
-		if (!opt.simpleKey().empty()) {
+		// parser::read needs '-' and '--' so add them.
+		if (!opt.simpleKey().empty())
 			def.emplace("-"s + opt.simpleKey(), !opt.arg().empty());
-		}
-		if (!opt.longKey().empty()) {
+		if (!opt.longKey().empty())
 			def.emplace("--"s + opt.longKey(), !opt.arg().empty());
-		}
 	}
 
-	/* 2. Parse them, remove them from args (in parser::read) and build the map with id. */
+	// 2. Parse them, remove them from args (in parser::read) and build the map with id.
 	RemoteCommandRequest::Options requestOptions;
 
 	for (const auto &pair : parser::read(args, def)) {
@@ -402,27 +380,24 @@
 		requestOptions.emplace(it->id(), pair.second);
 	}
 
-	/* 3. Check number of arguments. */
-	if (args.size() < cmd.min()) {
+	// 3. Check number of arguments.
+	if (args.size() < cmd.min())
 		throw std::runtime_error("too few arguments");
-	}
-	if (args.size() > cmd.max()) {
+	if (args.size() > cmd.max())
 		throw std::runtime_error("too many arguments");
-	}
 
-	/* 4. Construct the request, if the returned value is not an object, do not send anything (e.g. help). */
+	// 4. Construct the request, if the returned value is not an object, do not send anything (e.g. help).
 	json::Value request = cmd.request(*this, RemoteCommandRequest(std::move(requestOptions), std::move(args)));
 
-	if (!request.isObject()) {
+	if (!request.isObject())
 		return;
-	}
 
 	request.insert("command", cmd.name());
 
-	/* 5. Send the command */
+	// 5. Send the command.
 	m_connection->send(request.toJson(0), 30000);
 
-	/* 6. Parse the result */
+	// 6. Parse the result.
 	cmd.result(*this, m_connection->next(cmd.name(), 30000));
 }
 
@@ -433,34 +408,31 @@
 		std::vector<std::string> cmdArgs;
 		std::vector<std::string>::size_type toremove = 0;
 
-		/* 1. Append command name before */
+		// 1. Append command name before.
 		cmdArgs.push_back(cmd.command());
 
 		for (const AliasArg &arg : cmd.args()) {
 			if (arg.isPlaceholder()) {
-				if (args.size() < arg.index() + 1) {
+				if (args.size() < arg.index() + 1)
 					throw std::invalid_argument("missing argument for placeholder %" + std::to_string(arg.index()));
-				}
 
 				cmdArgs.push_back(args[arg.index()]);
 
-				if (arg.index() + 1 > toremove) {
+				if (arg.index() + 1 > toremove)
 					toremove = arg.index() + 1;
-				}
-			} else {
+			} else
 				cmdArgs.push_back(arg.value());
-			}
 		}
 
 		assert(toremove <= args.size());
 
-		/* 2. Remove the arguments that been placed in placeholders */
+		// 2. Remove the arguments that been placed in placeholders.
 		args.erase(args.begin(), args.begin() + toremove);
 
-		/* 3. Now append the rest of arguments */
+		// 3. Now append the rest of arguments.
 		std::copy(args.begin(), args.end(), std::back_inserter(cmdArgs));
 
-		/* 4. Finally try to execute */
+		// 4. Finally try to execute.
 		exec(cmdArgs);
 	}
 }
@@ -472,19 +444,18 @@
 	auto name = args[0];
 	auto alias = m_aliases.find(name);
 
-	/* Remove name */
+	// Remove name.
 	args.erase(args.begin());
 
-	if (alias != m_aliases.end()) {
+	if (alias != m_aliases.end())
 		exec(alias->second, args);
-	} else {
+	else {
 		auto cmd = m_commands.find(name);
 
-		if (cmd != m_commands.end()) {
+		if (cmd != m_commands.end())
 			exec(*cmd->second, args);
-		} else {
+		else
 			throw std::invalid_argument("no alias or command named " + name);
-		}
 	}
 }
 
@@ -492,17 +463,16 @@
 {
 	log::info("{}: connecting to irccd..."_format(sys::programName()));
 
-	/* Try to connect */
+	// Try to connect.
 	m_connection->connect(30000);
 
-	/* Get irccd information */
+	// Get irccd information.
 	json::Value object = m_connection->next(30000);
 
-	if (!object.contains("program") || object.at("program").toString() != "irccd") {
+	if (!object.contains("program") || object.at("program").toString() != "irccd")
 		throw std::runtime_error("not an irccd server");
-	}
 
-	/* Get values */
+	// Get values.
 	m_major = object.at("major").toInt();
 	m_minor = object.at("minor").toInt();
 	m_patch = object.at("patch").toInt();
@@ -516,7 +486,7 @@
 
 void Irccdctl::run(int argc, char **argv)
 {
-	/* 1. Read command line arguments */
+	// 1. Read command line arguments.
 	parser::Result result = parse(argc, argv);
 
 	/*
@@ -529,21 +499,19 @@
 	 * 3. From the configuration file searched through directories
 	 */
 	try {
-		if (result.count("-t") > 0 || result.count("--type") > 0) {
+		if (result.count("-t") > 0 || result.count("--type") > 0)
 			parseConnect(result);
-		}
 
 		auto it = result.find("-c");
 
-		if (it != result.end() || (it = result.find("--config")) != result.end()) {
+		if (it != result.end() || (it = result.find("--config")) != result.end())
 			read(it->second, result);
-		} else {
+		else {
 			for (const std::string &dir : path::list(path::PathConfig)) {
 				std::string path = dir + "irccdctl.conf";
 
-				if (fs::exists(path)) {
+				if (fs::exists(path))
 					read(path, result);
-				}
 			}
 		}
 	} catch (const std::exception &ex) {
@@ -556,7 +524,7 @@
 		// NOTREACHED
 	}
 
-	/* help does not require connection */
+	// Help does not require connection.
 	if (std::strcmp(argv[0], "help") != 0) {
 		if (!m_connection) {
 			log::warning("{}: no connection specified"_format(sys::programName()));
@@ -566,12 +534,11 @@
 		connect();
 	}
 
-	/* Build a vector of arguments */
+	// Build a vector of arguments.
 	std::vector<std::string> args;
 
-	for (int i = 0; i < argc; ++i) {
+	for (int i = 0; i < argc; ++i)
 		args.push_back(argv[i]);
-	}
 
 	exec(args);
 }
--- a/lib/irccd/irccdctl.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/irccdctl.hpp	Tue May 24 13:00:35 2016 +0200
@@ -49,12 +49,12 @@
  */
 class Irccdctl : public Application {
 private:
-	/* Irccd's information */
+	// Irccd's information.
 	unsigned short m_major{0};
 	unsigned short m_minor{0};
 	unsigned short m_patch{0};
 
-	/* Irccd's compilation option */
+	// Irccd's compilation option.
 	bool m_javascript{true};
 	bool m_ssl{true};
 
@@ -84,7 +84,7 @@
 	 * \param cmd the command
 	 * \param args the arguments
 	 */
-	void exec(const RemoteCommand &cmd, std::vector<std::string> args);
+	IRCCD_EXPORT void exec(const RemoteCommand &cmd, std::vector<std::string> args);
 
 	/**
 	 * Execute the given alias.
@@ -92,14 +92,14 @@
 	 * \param alias the alias
 	 * \param args the arguments
 	 */
-	void exec(const Alias &alias, std::vector<std::string> args);
+	IRCCD_EXPORT void exec(const Alias &alias, std::vector<std::string> args);
 
 	/**
 	 * Resolve the command line arguments.
 	 *
 	 * \param args the main arguments
 	 */
-	void exec(std::vector<std::string> args);
+	IRCCD_EXPORT void exec(std::vector<std::string> args);
 
 	/**
 	 * Get the connection.
@@ -117,7 +117,7 @@
 	 * \param argc the number of arguments
 	 * \param argv the arguments
 	 */
-	void run(int argc, char **argv);
+	IRCCD_EXPORT void run(int argc, char **argv);
 };
 
 } // !irccd
--- a/lib/irccd/json.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/json.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,6 +36,8 @@
 #include <utility>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 /**
@@ -372,7 +374,7 @@
 	 *
 	 * \param type the type
 	 */
-	Value(Type type);
+	IRCCD_EXPORT Value(Type type);
 
 	/**
 	 * Construct a null value.
@@ -469,7 +471,7 @@
 	 * \param buffer the text
 	 * \throw Error on errors
 	 */
-	Value(const Buffer &buffer);
+	IRCCD_EXPORT Value(const Buffer &buffer);
 
 	/**
 	 * Construct a value from a file.
@@ -477,7 +479,7 @@
 	 * \param file the file
 	 * \throw Error on errors
 	 */
-	Value(const File &file);
+	IRCCD_EXPORT Value(const File &file);
 
 	/**
 	 * Move constructor.
@@ -527,7 +529,7 @@
 	/**
 	 * Destructor.
 	 */
-	~Value();
+	IRCCD_EXPORT ~Value();
 
 	/**
 	 * Get an iterator to the beginning.
@@ -622,21 +624,21 @@
 	 *
 	 * \return the value or false if not a boolean
 	 */
-	bool toBool() const noexcept;
+	IRCCD_EXPORT bool toBool() const noexcept;
 
 	/**
 	 * Get the value as integer.
 	 *
 	 * \return the value or 0 if not a integer
 	 */
-	int toInt() const noexcept;
+	IRCCD_EXPORT int toInt() const noexcept;
 
 	/**
 	 * Get the value as real.
 	 *
 	 * \return the value or 0 if not a real
 	 */
-	double toReal() const noexcept;
+	IRCCD_EXPORT double toReal() const noexcept;
 
 	/**
 	 * Get the value as string.
@@ -1152,7 +1154,7 @@
  * \param input the input
  * \return the escaped string
  */
-std::string escape(const std::string &input);
+IRCCD_EXPORT std::string escape(const std::string &input);
 
 /**
  * Convenient function for creating array from initializer list.
--- a/lib/irccd/logger.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/logger.hpp	Tue May 24 13:00:35 2016 +0200
@@ -24,12 +24,12 @@
  * \brief Logging facilities.
  */
 
-#include "sysconfig.hpp"
-
 #include <memory>
 #include <sstream>
 #include <utility>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 namespace log {
@@ -161,17 +161,17 @@
 	/**
 	 * \copydoc Interface::debug
 	 */
-	void debug(const std::string &line) override;
+	IRCCD_EXPORT void debug(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::info
 	 */
-	void info(const std::string &line) override;
+	IRCCD_EXPORT void info(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::warning
 	 */
-	void warning(const std::string &line) override;
+	IRCCD_EXPORT void warning(const std::string &line) override;
 };
 
 /*
@@ -194,22 +194,22 @@
 	 * \param normal the path to the normal logs
 	 * \param errors the path to the errors logs
 	 */
-	File(std::string normal, std::string errors);
+	IRCCD_EXPORT File(std::string normal, std::string errors);
 
 	/**
 	 * \copydoc Interface::debug
 	 */
-	void debug(const std::string &line) override;
+	IRCCD_EXPORT void debug(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::info
 	 */
-	void info(const std::string &line) override;
+	IRCCD_EXPORT void info(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::warning
 	 */
-	void warning(const std::string &line) override;
+	IRCCD_EXPORT void warning(const std::string &line) override;
 };
 
 /*
@@ -227,17 +227,17 @@
 	/**
 	 * \copydoc Interface::debug
 	 */
-	void debug(const std::string &line) override;
+	IRCCD_EXPORT void debug(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::info
 	 */
-	void info(const std::string &line) override;
+	IRCCD_EXPORT void info(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::warning
 	 */
-	void warning(const std::string &line) override;
+	IRCCD_EXPORT void warning(const std::string &line) override;
 };
 
 /*
@@ -255,27 +255,27 @@
 	/**
 	 * Open the syslog.
 	 */
-	Syslog();
+	IRCCD_EXPORT Syslog();
 
 	/**
 	 * Close the syslog.
 	 */
-	~Syslog();
+	IRCCD_EXPORT ~Syslog();
 
 	/**
 	 * \copydoc Interface::debug
 	 */
-	void debug(const std::string &line) override;
+	IRCCD_EXPORT void debug(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::info
 	 */
-	void info(const std::string &line) override;
+	IRCCD_EXPORT void info(const std::string &line) override;
 
 	/**
 	 * \copydoc Interface::warning
 	 */
-	void warning(const std::string &line) override;
+	IRCCD_EXPORT void warning(const std::string &line) override;
 };
 
 #endif // !HAVE_SYSLOG
@@ -291,7 +291,7 @@
  * \pre iface must not be null
  * \param iface the new interface
  */
-void setInterface(std::unique_ptr<Interface> iface) noexcept;
+IRCCD_EXPORT void setInterface(std::unique_ptr<Interface> iface) noexcept;
 
 /**
  * Set an optional filter.
@@ -299,7 +299,7 @@
  * \pre filter must not be null
  * \param filter the filter
  */
-void setFilter(std::unique_ptr<Filter> filter) noexcept;
+IRCCD_EXPORT void setFilter(std::unique_ptr<Filter> filter) noexcept;
 
 /**
  * Get the stream for informational messages.
@@ -310,7 +310,7 @@
  * \return the stream
  * \note Has no effect if verbose is set to false.
  */
-std::ostream &info(const std::string &message = "");
+IRCCD_EXPORT std::ostream &info(const std::string &message = "");
 
 /**
  * Get the stream for warnings.
@@ -320,7 +320,7 @@
  * \param message the optional message to write
  * \return the stream
  */
-std::ostream &warning(const std::string &message = "");
+IRCCD_EXPORT std::ostream &warning(const std::string &message = "");
 
 /**
  * Get the stream for debug messages.
@@ -331,21 +331,21 @@
  * \return the stream
  * \note Has no effect if compiled in release mode.
  */
-std::ostream &debug(const std::string &message = "");
+IRCCD_EXPORT std::ostream &debug(const std::string &message = "");
 
 /**
  * Tells if verbose is enabled.
  *
  * \return true if enabled
  */
-bool isVerbose() noexcept;
+IRCCD_EXPORT bool isVerbose() noexcept;
 
 /**
  * Set the verbosity mode.
  *
  * \param mode the new mode
  */
-void setVerbose(bool mode) noexcept;
+IRCCD_EXPORT void setVerbose(bool mode) noexcept;
 
 } // !log
 
--- a/lib/irccd/mod-directory.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-directory.cpp	Tue May 24 13:00:35 2016 +0200
@@ -41,15 +41,13 @@
 	duk::push(ctx, duk::This{});
 	duk::getProperty<void>(ctx, -1, "path");
 
-	if (duk::type(ctx, -1) != DUK_TYPE_STRING) {
+	if (duk::type(ctx, -1) != DUK_TYPE_STRING)
 		duk::raise(ctx, duk::TypeError("invalid this binding"));
-	}
 
 	std::string ret = duk::get<std::string>(ctx, -1);
 
-	if (ret.empty()) {
+	if (ret.empty())
 		duk::raise(ctx, duk::TypeError("invalid directory with empty path"));
-	}
 
 	duk::pop(ctx, 2);
 
@@ -76,23 +74,20 @@
 	auto entries = fs::readdir(base);
 
 	for (const auto &entry : entries) {
-		if (entry.type != fs::Entry::Dir && pred(entry.name)) {
+		if (entry.type != fs::Entry::Dir && pred(entry.name))
 			return base + entry.name;
-		}
 	}
 
-	if (!recursive) {
+	if (!recursive)
 		return "";
-	}
 
 	for (const auto &entry : entries) {
 		if (entry.type == fs::Entry::Dir) {
 			std::string next = base + entry.name + fs::separator();
 			std::string path = findPath(next, true, pred);
 
-			if (!path.empty()) {
+			if (!path.empty())
 				return path;
-			}
 		}
 	}
 
@@ -137,26 +132,24 @@
 	try {
 		std::string path;
 
-		if (duk::is<std::string>(ctx, patternIndex)) {
+		if (duk::is<std::string>(ctx, patternIndex))
 			path = findName(base, duk::get<std::string>(ctx, patternIndex), recursive);
-		} else {
-			/* Check if it's a valid RegExp object */
+		else {
+			// Check if it's a valid RegExp object.
 			duk::getGlobal<void>(ctx, "RegExp");
 
 			bool isRegex = duk::instanceof(ctx, patternIndex, -1);
 
 			duk::pop(ctx);
 
-			if (isRegex) {
+			if (isRegex)
 				path = findRegex(base, duk::getProperty<std::string>(ctx, patternIndex, "source"), recursive);
-			} else {
+			else
 				duk::raise(ctx, duk::TypeError("pattern must be a string or a regex expression"));
-			}
 		}
 
-		if (path.empty()) {
+		if (path.empty())
 			return 0;
-		}
 
 		duk::push(ctx, path);
 	} catch (const std::exception &ex) {
@@ -174,9 +167,8 @@
  */
 duk::Ret remove(duk::ContextPtr ctx, const std::string &path, bool recursive)
 {
-	if (!fs::isDirectory(path)) {
+	if (!fs::isDirectory(path))
 		duk::raise(ctx, SystemError(EINVAL, "not a directory"));
-	}
 
 	if (!recursive) {
 #if defined(_WIN32)
@@ -184,9 +176,8 @@
 #else
 		::remove(path.c_str());
 #endif
-	} else {
+	} else
 		fs::rmdir(path.c_str());
-	}
 
 	return 0;
 }
@@ -251,17 +242,15 @@
  */
 duk::Ret constructor(duk::ContextPtr ctx)
 {
-	if (!duk_is_constructor_call(ctx)) {
+	if (!duk_is_constructor_call(ctx))
 		return 0;
-	}
 
 	try {
 		std::string path = duk::require<std::string>(ctx, 0);
 		std::int8_t flags = duk::optional<int>(ctx, 1, 0);
 
-		if (!fs::isDirectory(path)) {
+		if (!fs::isDirectory(path))
 			duk::raise(ctx, SystemError(EINVAL, "not a directory"));
-		}
 
 		std::vector<fs::Entry> list = fs::readdir(path, flags);
 
--- a/lib/irccd/mod-directory.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-directory.hpp	Tue May 24 13:00:35 2016 +0200
@@ -38,12 +38,12 @@
 	/**
 	 * Irccd.Directory.
 	 */
-	DirectoryModule() noexcept;
+	IRCCD_EXPORT DirectoryModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	virtual void load(Irccd &irccd, JsPlugin &plugin);
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-elapsed-timer.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-elapsed-timer.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,12 +36,12 @@
 	/**
 	 * Irccd.ElapsedTimer.
 	 */
-	ElapsedTimerModule() noexcept;
+	IRCCD_EXPORT ElapsedTimerModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	virtual void load(Irccd &irccd, JsPlugin &plugin);
+	IRCCD_EXPORT virtual void load(Irccd &irccd, JsPlugin &plugin);
 };
 
 } // !irccd
--- a/lib/irccd/mod-file.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-file.cpp	Tue May 24 13:00:35 2016 +0200
@@ -107,9 +107,8 @@
 /* Remove trailing \r for CRLF line style */
 inline std::string clearCr(std::string input)
 {
-	if (input.length() > 0 && input.back() == '\r') {
+	if (input.length() > 0 && input.back() == '\r')
 		input.pop_back();
-	}
 
 	return input;
 }
@@ -195,15 +194,13 @@
 		}
 	}
 
-	/* Maybe an error in the stream */
-	if (std::ferror(fp)) {
+	// Maybe an error in the stream.
+	if (std::ferror(fp))
 		duk::raise(ctx, SystemError());
-	}
 
-	/* Missing '\n' in end of file */
-	if (!buffer.empty()) {
+	// Missing '\n' in end of file.
+	if (!buffer.empty())
 		duk::putProperty(ctx, -1, i++, clearCr(buffer));
-	}
 
 	return 1;
 }
@@ -226,9 +223,8 @@
 	auto amount = duk::optional<int>(ctx, 0, -1);
 	auto file = duk::self<duk::Pointer<File>>(ctx);
 
-	if (amount == 0 || file->handle() == nullptr) {
+	if (amount == 0 || file->handle() == nullptr)
 		return 0;
-	}
 
 	try {
 		std::string data;
@@ -241,9 +237,8 @@
 			while (!std::feof(file->handle())) {
 				nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), file->handle());
 
-				if (std::ferror(file->handle())) {
+				if (std::ferror(file->handle()))
 					duk::raise(ctx, SystemError());
-				}
 
 				std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data));
 				total += nread;
@@ -252,9 +247,8 @@
 			data.resize((std::size_t)amount);
 			total = std::fread(&data[0], sizeof (data[0]), (std::size_t)amount, file->handle());
 
-			if (std::ferror(file->handle())) {
+			if (std::ferror(file->handle()))
 				duk::raise(ctx, SystemError());
-			}
 
 			data.resize(total);
 		}
@@ -283,17 +277,12 @@
 	std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle();
 	std::string result;
 
-	if (fp == nullptr || std::feof(fp)) {
+	if (fp == nullptr || std::feof(fp))
 		return 0;
-	}
-
-	for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) {
+	for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; )
 		result += (char)ch;
-	}
-
-	if (std::ferror(fp)) {
+	if (std::ferror(fp))
 		duk::raise(ctx, SystemError());
-	}
 
 	duk::push(ctx, clearCr(result));
 
@@ -311,9 +300,8 @@
  */
 duk::Ret methodRemove(duk::ContextPtr ctx)
 {
-	if (::remove(duk::self<duk::Pointer<File>>(ctx)->path().c_str()) < 0) {
+	if (::remove(duk::self<duk::Pointer<File>>(ctx)->path().c_str()) < 0)
 		duk::raise(ctx, SystemError());
-	}
 
 	return 0;
 }
@@ -336,9 +324,8 @@
 	auto amount = duk::require<int>(ctx, 1);
 	auto fp = duk::self<duk::Pointer<File>>(ctx)->handle();
 
-	if (fp != nullptr && std::fseek(fp, amount, type) != 0) {
+	if (fp != nullptr && std::fseek(fp, amount, type) != 0)
 		duk::raise(ctx, SystemError());
-	}
 
 	return 0;
 }
@@ -361,11 +348,10 @@
 	struct stat st;
 	auto file = duk::self<duk::Pointer<File>>(ctx);
 
-	if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0) {
+	if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0)
 		duk::raise(ctx, SystemError());
-	} else {
+	else
 		duk::push(ctx, st);
-	}
 
 	return 1;
 }
@@ -388,15 +374,13 @@
 	auto fp = duk::self<duk::Pointer<File>>(ctx)->handle();
 	long pos;
 
-	if (fp == nullptr) {
+	if (fp == nullptr)
 		return 0;
-	}
 
-	if ((pos = std::ftell(fp)) == -1L) {
+	if ((pos = std::ftell(fp)) == -1L)
 		duk::raise(ctx, SystemError());
-	} else {
+	else
 		duk::push(ctx, (int)pos);
-	}
 
 	return 1;
 }
@@ -419,15 +403,13 @@
 	std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle();
 	std::string data = duk::require<std::string>(ctx, 0);
 
-	if (fp == nullptr) {
+	if (fp == nullptr)
 		return 0;
-	}
 
 	std::size_t nwritten = std::fwrite(data.c_str(), 1, data.length(), fp);
 
-	if (std::ferror(fp)) {
+	if (std::ferror(fp))
 		duk::raise(ctx, SystemError());
-	}
 
 	duk::push(ctx, (int)nwritten);
 
@@ -469,9 +451,8 @@
  */
 duk::Ret constructor(duk::ContextPtr ctx)
 {
-	if (!duk_is_constructor_call(ctx)) {
+	if (!duk_is_constructor_call(ctx))
 		return 0;
-	}
 
 	std::string path = duk::require<std::string>(ctx, 0);
 	std::string mode = duk::require<std::string>(ctx, 1);
@@ -554,9 +535,8 @@
  */
 duk::Ret functionRemove(duk::ContextPtr ctx)
 {
-	if (::remove(duk::require<std::string>(ctx, 0).c_str()) < 0) {
+	if (::remove(duk::require<std::string>(ctx, 0).c_str()) < 0)
 		duk::raise(ctx, SystemError());
-	}
 
 	return 0;
 }
@@ -580,9 +560,8 @@
 {
 	struct stat st;
 
-	if (::stat(duk::require<std::string>(ctx, 0).c_str(), &st) < 0) {
+	if (::stat(duk::require<std::string>(ctx, 0).c_str(), &st) < 0)
 		duk::raise(ctx, SystemError());
-	}
 
 	duk::push(ctx, st);
 
--- a/lib/irccd/mod-file.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-file.hpp	Tue May 24 13:00:35 2016 +0200
@@ -189,12 +189,12 @@
 	/**
 	 * Irccd.File.
 	 */
-	FileModule() noexcept;
+	IRCCD_EXPORT FileModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-irccd.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-irccd.hpp	Tue May 24 13:00:35 2016 +0200
@@ -47,7 +47,7 @@
 	/**
 	 * Create a system error from the current errno value.
 	 */
-	SystemError();
+	IRCCD_EXPORT SystemError();
 
 	/**
 	 * Create a system error with the given errno and message.
@@ -55,14 +55,14 @@
 	 * \param e the errno number
 	 * \param message the message
 	 */
-	SystemError(int e, std::string message);
+	IRCCD_EXPORT SystemError(int e, std::string message);
 
 	/**
 	 * Raise the SystemError.
 	 *
 	 * \param ctx the context
 	 */
-	void raise(duk::ContextPtr ctx) const;
+	IRCCD_EXPORT void raise(duk::ContextPtr ctx) const;
 };
 
 /**
@@ -73,12 +73,12 @@
 	/**
 	 * Irccd.
 	 */
-	IrccdModule() noexcept;
+	IRCCD_EXPORT IrccdModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-logger.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-logger.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,12 +36,12 @@
 	/**
 	 * Irccd.Logger.
 	 */
-	LoggerModule() noexcept;
+	IRCCD_EXPORT LoggerModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-plugin.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-plugin.cpp	Tue May 24 13:00:35 2016 +0200
@@ -71,15 +71,13 @@
 {
 	Plugin *plugin = nullptr;
 
-	if (duk::top(ctx) >= 1) {
+	if (duk::top(ctx) >= 1)
 		plugin = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().get(duk::require<std::string>(ctx, 0)).get();
-	} else {
+	else
 		plugin = duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin");
-	}
 
-	if (!plugin) {
+	if (!plugin)
 		return 0;
-	}
 
 	duk::push(ctx, duk::Object{});
 	duk::putProperty(ctx, -1, "name", plugin->name());
@@ -105,9 +103,8 @@
 	duk::push(ctx, duk::Array{});
 
 	int i = 0;
-	for (const auto &plugin : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().plugins()) {
+	for (const auto &plugin : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().plugins())
 		duk::putProperty(ctx, -1, i++, plugin->name());
-	}
 
 	return 1;
 }
--- a/lib/irccd/mod-plugin.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-plugin.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,12 +36,12 @@
 	/**
 	 * Irccd.Plugin.
 	 */
-	PluginModule() noexcept;
+	IRCCD_EXPORT PluginModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	virtual void load(Irccd &irccd, JsPlugin &plugin);
+	IRCCD_EXPORT virtual void load(Irccd &irccd, JsPlugin &plugin);
 };
 
 } // !irccd
--- a/lib/irccd/mod-server.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-server.cpp	Tue May 24 13:00:35 2016 +0200
@@ -91,13 +91,12 @@
 	duk::putProperty(ctx, -1, "nickname", server->identity().nickname);
 	duk::putProperty(ctx, -1, "username", server->identity().username);
 
-	/* Channels */
+	// Channels.
 	duk::push(ctx, duk::Array{});
 
 	int i = 0;
-	for (const auto &channel : server->settings().channels) {
+	for (const auto &channel : server->settings().channels)
 		duk::putProperty(ctx, -1, i++, channel.name);
-	}
 
 	duk::putProperty(ctx, -2, "channels");
 
@@ -390,19 +389,16 @@
 	identity.ctcpversion = duk::optionalProperty<std::string>(ctx, 0, "version", identity.ctcpversion);
 
 	// Settings part.
-	for (const auto &chan: duk::getProperty<std::vector<std::string>>(ctx, 0, "channels")) {
+	for (const auto &chan: duk::getProperty<std::vector<std::string>>(ctx, 0, "channels"))
 		settings.channels.push_back(Server::splitChannel(chan));
-	}
 
 	settings.reconnectTries = duk::optionalProperty<int>(ctx, 0, "recoTries", (int)settings.reconnectTries);
 	settings.reconnectDelay = duk::optionalProperty<int>(ctx, 0, "recoTimeout", (int)settings.reconnectDelay);
 
-	if (duk::optionalProperty<bool>(ctx, 0, "joinInvite", false)) {
+	if (duk::optionalProperty<bool>(ctx, 0, "joinInvite", false))
 		settings.flags |= ServerSettings::JoinInvite;
-	}
-	if (duk::optionalProperty<bool>(ctx, 0, "autoRejoin", false)) {
+	if (duk::optionalProperty<bool>(ctx, 0, "autoRejoin", false))
 		settings.flags |= ServerSettings::AutoRejoin;
-	}
 
 	try {
 		duk::construct(ctx, duk::Shared<Server>{std::make_shared<Server>(std::move(name), std::move(info),
@@ -427,9 +423,8 @@
 {
 	auto server = duk::get<duk::Shared<Server>>(ctx, 0);
 
-	if (server) {
+	if (server)
 		duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().add(server);
-	}
 
 	return 0;
 }
@@ -472,9 +467,8 @@
 {
 	duk::push(ctx, duk::Object{});
 
-	for (const auto &server : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().servers()) {
+	for (const auto &server : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().servers())
 		duk::putProperty(ctx, -1, server->name(), duk::Shared<Server>{server});
-	}
 
 	return 1;
 }
--- a/lib/irccd/mod-server.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-server.hpp	Tue May 24 13:00:35 2016 +0200
@@ -83,12 +83,12 @@
 	/**
 	 * Irccd.Server.
 	 */
-	ServerModule() noexcept;
+	IRCCD_EXPORT ServerModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-system.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-system.cpp	Tue May 24 13:00:35 2016 +0200
@@ -122,9 +122,8 @@
 {
 	auto fp = ::popen(duk::require<const char *>(ctx, 0), duk::require<const char *>(ctx, 1));
 
-	if (fp == nullptr) {
+	if (fp == nullptr)
 		duk::raise(ctx, SystemError{});
-	}
 
 	duk::push(ctx, duk::Pointer<File>{new File(fp, [] (std::FILE *fp) { ::pclose(fp); })});
 
--- a/lib/irccd/mod-system.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-system.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,12 +36,12 @@
 	/**
 	 * Irccd.System.
 	 */
-	SystemModule() noexcept;
+	IRCCD_EXPORT SystemModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-timer.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-timer.cpp	Tue May 24 13:00:35 2016 +0200
@@ -54,9 +54,8 @@
 {
 	auto plugin = ptr.lock();
 
-	if (!plugin) {
+	if (!plugin)
 		return;
-	}
 
 	auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(plugin->context(), "\xff""\xff""irccd");
 
@@ -68,14 +67,12 @@
 		duk::remove(plugin->context(), -2);
 
 		if (duk::is<duk::Function>(plugin->context(), -1)) {
-			if (duk::pcall(plugin->context()) != 0) {
+			if (duk::pcall(plugin->context()) != 0)
 				log::warning("plugin {}: {}"_format(plugin->name(), duk::error(plugin->context(), -1).stack));
-			}
 
 			duk::pop(plugin->context());
-		} else {
+		} else
 			duk::pop(plugin->context());
-		}
 	});
 }
 
@@ -89,9 +86,8 @@
 {
 	auto timer = duk::self<duk::Shared<Timer>>(ctx);
 
-	if (!timer->isRunning()) {
+	if (!timer->isRunning())
 		timer->start();
-	}
 
 	return 0;
 }
@@ -106,9 +102,8 @@
 {
 	auto timer = duk::self<duk::Shared<Timer>>(ctx);
 
-	if (timer->isRunning()) {
+	if (timer->isRunning())
 		timer->stop();
-	}
 
 	return 0;
 }
@@ -135,15 +130,12 @@
 	auto type = duk::require<int>(ctx, 0);
 	auto delay = duk::require<int>(ctx, 1);
 
-	if (type < static_cast<int>(TimerType::Single) || type > static_cast<int>(TimerType::Repeat)) {
+	if (type < static_cast<int>(TimerType::Single) || type > static_cast<int>(TimerType::Repeat))
 		duk::raise(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type");
-	}
-	if (delay < 0) {
+	if (delay < 0)
 		duk::raise(ctx, DUK_ERR_TYPE_ERROR, "negative delay given");
-	}
-	if (!duk::is<duk::Function>(ctx, 2)) {
+	if (!duk::is<duk::Function>(ctx, 2))
 		duk::raise(ctx, DUK_ERR_TYPE_ERROR, "missing callback function");
-	}
 
 	// Construct the timer in 'this'.
 	auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay);
--- a/lib/irccd/mod-timer.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-timer.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,12 +36,12 @@
 	/**
 	 * Irccd.Timer.
 	 */
-	TimerModule() noexcept;
+	IRCCD_EXPORT TimerModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-unicode.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-unicode.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,12 +36,12 @@
 	/**
 	 * Irccd.Unicode.
 	 */
-	UnicodeModule() noexcept;
+	IRCCD_EXPORT UnicodeModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/mod-util.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/mod-util.hpp	Tue May 24 13:00:35 2016 +0200
@@ -36,12 +36,12 @@
 	/**
 	 * Irccd.Util.
 	 */
-	UtilModule() noexcept;
+	IRCCD_EXPORT UtilModule() noexcept;
 
 	/**
 	 * \copydoc Module::load
 	 */
-	void load(Irccd &irccd, JsPlugin &plugin) override;
+	IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override;
 };
 
 } // !irccd
--- a/lib/irccd/module.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/module.hpp	Tue May 24 13:00:35 2016 +0200
@@ -1,95 +1,96 @@
-/*
- * module.hpp -- JavaScript API module
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_MODULE_HPP
-#define IRCCD_MODULE_HPP
-
-/**
- * \file module.hpp
- * \brief JavaScript API module.
- */
-
-#include <cassert>
-
-#include "util.hpp"
-
-namespace irccd {
-
-class Irccd;
-class JsPlugin;
-
-/**
- * \brief JavaScript API module.
- */
-class Module {
-private:
-	std::string m_name;
-
-public:
-	/**
-	 * Default constructor.
-	 *
-	 * \pre !name.empty()
-	 */
-	inline Module(std::string name) noexcept
-		: m_name(std::move(name))
-	{
-		assert(!m_name.empty());
-	}
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~Module() = default;
-
-	/**
-	 * Get the module name.
-	 *
-	 * \return the name
-	 */
-	inline const std::string &name() const noexcept
-	{
-		return m_name;
-	}
-
-	/**
-	 * Load the module into the JavaScript plugin.
-	 *
-	 * \param irccd the irccd instance
-	 * \param plugin the plugin
-	 */
-	virtual void load(Irccd &irccd, JsPlugin &plugin)
-	{
-		util::unused(irccd, plugin);
-	}
-
-	/**
-	 * Unload the module from the JavaScript plugin.
-	 *
-	 * \param irccd the irccd instance
-	 * \param plugin the plugin
-	 */
-	virtual void unload(Irccd &irccd, JsPlugin &plugin)
-	{
-		util::unused(irccd, plugin);
-	}
-};
-
-} // !irccd
-
-#endif // !IRCCD_MODULE_HPP
+/*
+ * module.hpp -- JavaScript API module
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_MODULE_HPP
+#define IRCCD_MODULE_HPP
+
+/**
+ * \file module.hpp
+ * \brief JavaScript API module.
+ */
+
+#include <cassert>
+
+#include "sysconfig.hpp"
+#include "util.hpp"
+
+namespace irccd {
+
+class Irccd;
+class JsPlugin;
+
+/**
+ * \brief JavaScript API module.
+ */
+class Module {
+private:
+	std::string m_name;
+
+public:
+	/**
+	 * Default constructor.
+	 *
+	 * \pre !name.empty()
+	 */
+	inline Module(std::string name) noexcept
+		: m_name(std::move(name))
+	{
+		assert(!m_name.empty());
+	}
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~Module() = default;
+
+	/**
+	 * Get the module name.
+	 *
+	 * \return the name
+	 */
+	inline const std::string &name() const noexcept
+	{
+		return m_name;
+	}
+
+	/**
+	 * Load the module into the JavaScript plugin.
+	 *
+	 * \param irccd the irccd instance
+	 * \param plugin the plugin
+	 */
+	virtual void load(Irccd &irccd, JsPlugin &plugin)
+	{
+		util::unused(irccd, plugin);
+	}
+
+	/**
+	 * Unload the module from the JavaScript plugin.
+	 *
+	 * \param irccd the irccd instance
+	 * \param plugin the plugin
+	 */
+	virtual void unload(Irccd &irccd, JsPlugin &plugin)
+	{
+		util::unused(irccd, plugin);
+	}
+};
+
+} // !irccd
+
+#endif // !IRCCD_MODULE_HPP
--- a/lib/irccd/options.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/options.hpp	Tue May 24 13:00:35 2016 +0200
@@ -30,6 +30,8 @@
 #include <utility>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 /**
@@ -128,7 +130,7 @@
  * \throw MissingValue
  * \throw InvalidOption
  */
-Result read(std::vector<std::string> &args, const Options &definition);
+IRCCD_EXPORT Result read(std::vector<std::string> &args, const Options &definition);
 
 /**
  * Overloaded function for usage with main() arguments.
@@ -141,7 +143,7 @@
  * \throw MissingValue
  * \throw InvalidOption
  */
-Result read(int &argc, char **&argv, const Options &definition);
+IRCCD_EXPORT Result read(int &argc, char **&argv, const Options &definition);
 
 } // !parser
 
--- a/lib/irccd/path.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/path.hpp	Tue May 24 13:00:35 2016 +0200
@@ -27,6 +27,8 @@
 #include <string>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 namespace path {
@@ -64,7 +66,7 @@
  *
  * \param argv0 the path to the executable (argv[0])
  */
-void setApplicationPath(const std::string &argv0);
+IRCCD_EXPORT void setApplicationPath(const std::string &argv0);
 
 /**
  * Clean a path by removing any extra / or \ and add a trailing one.
@@ -72,7 +74,7 @@
  * \param path the path
  * \return the updated path
  */
-std::string clean(std::string path);
+IRCCD_EXPORT std::string clean(std::string path);
 
 /**
  * Generic function for path retrievement.
@@ -84,7 +86,7 @@
  * \param owner system or user wide
  * \return the path
  */
-std::string get(Path path, Owner owner);
+IRCCD_EXPORT std::string get(Path path, Owner owner);
 
 /**
  * Generic function for multiple paths.
@@ -95,7 +97,7 @@
  * \param path the type of path
  * \return the list of preferred directories in order
  */
-std::vector<std::string> list(Path path);
+IRCCD_EXPORT std::vector<std::string> list(Path path);
 
 } // !path
 
--- a/lib/irccd/plugin-dynlib.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/plugin-dynlib.hpp	Tue May 24 13:00:35 2016 +0200
@@ -93,120 +93,120 @@
 	/**
 	 * \copydoc Plugin::onCommand
 	 */
-	void onCommand(Irccd &irccd,
-		       const std::shared_ptr<Server> &server,
-		       const std::string &origin,
-		       const std::string &channel,
-		       const std::string &message) override;
+	IRCCD_EXPORT void onCommand(Irccd &irccd,
+				    const std::shared_ptr<Server> &server,
+				    const std::string &origin,
+				    const std::string &channel,
+				    const std::string &message) override;
 
 	/**
 	 * \copydoc Plugin::onConnect
 	 */
-	void onConnect(Irccd &irccd, const std::shared_ptr<Server> &server) override;
+	IRCCD_EXPORT void onConnect(Irccd &irccd, const std::shared_ptr<Server> &server) override;
 
 	/**
 	 * \copydoc Plugin::onChannelMode
 	 */
-	void onChannelMode(Irccd &irccd,
-			   const std::shared_ptr<Server> &server,
-			   const std::string &origin,
-			   const std::string &channel,
-			   const std::string &mode,
-			   const std::string &arg) override;
+	IRCCD_EXPORT void onChannelMode(Irccd &irccd,
+					const std::shared_ptr<Server> &server,
+					const std::string &origin,
+					const std::string &channel,
+					const std::string &mode,
+					const std::string &arg) override;
 
 	/**
 	 * \copydoc Plugin::onChannelNotice
 	 */
-	void onChannelNotice(Irccd &irccd,
-			     const std::shared_ptr<Server> &server,
-			     const std::string &origin,
-			     const std::string &channel,
-			     const std::string &notice) override;
+	IRCCD_EXPORT void onChannelNotice(Irccd &irccd,
+					  const std::shared_ptr<Server> &server,
+					  const std::string &origin,
+					  const std::string &channel,
+					  const std::string &notice) override;
 
 	/**
 	 * \copydoc Plugin::onInvite
 	 */
-	void onInvite(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
+	IRCCD_EXPORT void onInvite(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
 
 	/**
 	 * \copydoc Plugin::onJoin
 	 */
-	void onJoin(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
+	IRCCD_EXPORT void onJoin(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
 
 	/**
 	 * \copydoc Plugin::onKick
 	 */
-	void onKick(Irccd &irccd,
-		    const std::shared_ptr<Server> &server,
-		    const std::string &origin,
-		    const std::string &channel,
-		    const std::string &target,
-		    const std::string &reason) override;
+	IRCCD_EXPORT void onKick(Irccd &irccd,
+				 const std::shared_ptr<Server> &server,
+				 const std::string &origin,
+				 const std::string &channel,
+				 const std::string &target,
+				 const std::string &reason) override;
 
 	/**
 	 * \copydoc Plugin::onLoad
 	 */
-	void onLoad(Irccd &irccd) override;
+	IRCCD_EXPORT void onLoad(Irccd &irccd) override;
 
 	/**
 	 * \copydoc Plugin::onMessage
 	 */
-	void onMessage(Irccd &irccd,
-		       const std::shared_ptr<Server> &server,
-		       const std::string &origin,
-		       const std::string &channel,
-		       const std::string &message) override;
+	IRCCD_EXPORT void onMessage(Irccd &irccd,
+				    const std::shared_ptr<Server> &server,
+				    const std::string &origin,
+				    const std::string &channel,
+				    const std::string &message) override;
 
 	/**
 	 * \copydoc Plugin::onMe
 	 */
-	void onMe(Irccd &irccd,
-		  const std::shared_ptr<Server> &server,
-		  const std::string &origin,
-		  const std::string &channel,
-		  const std::string &message) override;
+	IRCCD_EXPORT void onMe(Irccd &irccd,
+			       const std::shared_ptr<Server> &server,
+			       const std::string &origin,
+			       const std::string &channel,
+			       const std::string &message) override;
 
 	/**
 	 * \copydoc Plugin::onMode
 	 */
-	void onMode(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &mode) override;
+	IRCCD_EXPORT void onMode(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &mode) override;
 
 	/**
 	 * \copydoc Plugin::onNames
 	 */
-	void onNames(Irccd &irccd,
-		     const std::shared_ptr<Server> &server,
-		     const std::string &channel,
-		     const std::vector<std::string> &list) override;
+	IRCCD_EXPORT void onNames(Irccd &irccd,
+				  const std::shared_ptr<Server> &server,
+				  const std::string &channel,
+				  const std::vector<std::string> &list) override;
 
 	/**
 	 * \copydoc Plugin::onNick
 	 */
-	void onNick(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &nick) override;
+	IRCCD_EXPORT void onNick(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &nick) override;
 
 	/**
 	 * \copydoc Plugin::onNotice
 	 */
-	void onNotice(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &notice) override;
+	IRCCD_EXPORT void onNotice(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &notice) override;
 
 	/**
 	 * \copydoc Plugin::onPart
 	 */
-	void onPart(Irccd &irccd,
-		    const std::shared_ptr<Server> &server,
-		    const std::string &origin,
-		    const std::string &channel,
-		    const std::string &reason) override;
+	IRCCD_EXPORT void onPart(Irccd &irccd,
+				 const std::shared_ptr<Server> &server,
+				 const std::string &origin,
+				 const std::string &channel,
+				 const std::string &reason) override;
 
 	/**
 	 * \copydoc Plugin::onQuery
 	 */
-	void onQuery(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &message) override;
+	IRCCD_EXPORT void onQuery(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &message) override;
 
 	/**
 	 * \copydoc Plugin::onQueryCommand
 	 */
-	void onQueryCommand(Irccd &irccd,
+	IRCCD_EXPORT void onQueryCommand(Irccd &irccd,
 			    const std::shared_ptr<Server> &server,
 			    const std::string &origin,
 			    const std::string &message) override;
@@ -214,26 +214,26 @@
 	/**
 	 * \copydoc Plugin::onReload
 	 */
-	void onReload(Irccd &irccd) override;
+	IRCCD_EXPORT void onReload(Irccd &irccd) override;
 
 	/**
 	 * \copydoc Plugin::onTopic
 	 */
-	void onTopic(Irccd &irccd,
-		     const std::shared_ptr<Server> &server,
-		     const std::string &origin,
-		     const std::string &channel,
-		     const std::string &topic) override;
+	IRCCD_EXPORT void onTopic(Irccd &irccd,
+				  const std::shared_ptr<Server> &server,
+				  const std::string &origin,
+				  const std::string &channel,
+				  const std::string &topic) override;
 
 	/**
 	 * \copydoc Plugin::onUnload
 	 */
-	void onUnload(Irccd &irccd) override;
+	IRCCD_EXPORT void onUnload(Irccd &irccd) override;
 
 	/**
 	 * \copydoc Plugin::onWhois
 	 */
-	void onWhois(Irccd &irccd, const std::shared_ptr<Server> &server, const ServerWhois &info) override;
+	IRCCD_EXPORT void onWhois(Irccd &irccd, const std::shared_ptr<Server> &server, const ServerWhois &info) override;
 };
 
 } // !irccd
--- a/lib/irccd/plugin-js.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/plugin-js.hpp	Tue May 24 13:00:35 2016 +0200
@@ -60,7 +60,7 @@
 	 * \param path the path to the plugin
 	 * \param config the configuration
 	 */
-	JsPlugin(std::string name, std::string path, const PluginConfig &config = PluginConfig());
+	IRCCD_EXPORT JsPlugin(std::string name, std::string path, const PluginConfig &config = PluginConfig());
 
 	/**
 	 * Access the Duktape context.
@@ -73,149 +73,149 @@
 	}
 
 	/**
-	 * \copydoc Plugin::onCommand
-	 */
-	void onCommand(Irccd &irccd,
-		       const std::shared_ptr<Server> &server,
-		       const std::string &origin,
-		       const std::string &channel,
-		       const std::string &message) override;
+	* \copydoc Plugin::onCommand
+	*/
+	IRCCD_EXPORT void onCommand(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &message) override;
 
 	/**
-	 * \copydoc Plugin::onConnect
-	 */
-	void onConnect(Irccd &irccd, const std::shared_ptr<Server> &server) override;
+	* \copydoc Plugin::onConnect
+	*/
+	IRCCD_EXPORT void onConnect(Irccd &irccd, const std::shared_ptr<Server> &server) override;
 
 	/**
-	 * \copydoc Plugin::onChannelMode
-	 */
-	void onChannelMode(Irccd &irccd,
-			   const std::shared_ptr<Server> &server,
-			   const std::string &origin,
-			   const std::string &channel,
-			   const std::string &mode,
-			   const std::string &arg) override;
+	* \copydoc Plugin::onChannelMode
+	*/
+	IRCCD_EXPORT void onChannelMode(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &mode,
+		const std::string &arg) override;
 
 	/**
-	 * \copydoc Plugin::onChannelNotice
-	 */
-	void onChannelNotice(Irccd &irccd,
-			     const std::shared_ptr<Server> &server,
-			     const std::string &origin,
-			     const std::string &channel,
-			     const std::string &notice) override;
+	* \copydoc Plugin::onChannelNotice
+	*/
+	IRCCD_EXPORT void onChannelNotice(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &notice) override;
 
 	/**
-	 * \copydoc Plugin::onInvite
-	 */
-	void onInvite(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
+	* \copydoc Plugin::onInvite
+	*/
+	IRCCD_EXPORT void onInvite(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
 
 	/**
-	 * \copydoc Plugin::onJoin
-	 */
-	void onJoin(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
+	* \copydoc Plugin::onJoin
+	*/
+	IRCCD_EXPORT void onJoin(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &channel) override;
 
 	/**
-	 * \copydoc Plugin::onKick
-	 */
-	void onKick(Irccd &irccd,
-		    const std::shared_ptr<Server> &server,
-		    const std::string &origin,
-		    const std::string &channel,
-		    const std::string &target,
-		    const std::string &reason) override;
+	* \copydoc Plugin::onKick
+	*/
+	IRCCD_EXPORT void onKick(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &target,
+		const std::string &reason) override;
 
 	/**
-	 * \copydoc Plugin::onLoad
-	 */
-	void onLoad(Irccd &irccd) override;
+	* \copydoc Plugin::onLoad
+	*/
+	IRCCD_EXPORT void onLoad(Irccd &irccd) override;
 
 	/**
-	 * \copydoc Plugin::onMessage
-	 */
-	void onMessage(Irccd &irccd,
-		       const std::shared_ptr<Server> &server,
-		       const std::string &origin,
-		       const std::string &channel,
-		       const std::string &message) override;
+	* \copydoc Plugin::onMessage
+	*/
+	IRCCD_EXPORT void onMessage(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &message) override;
 
 	/**
-	 * \copydoc Plugin::onMe
-	 */
-	void onMe(Irccd &irccd,
-		  const std::shared_ptr<Server> &server,
-		  const std::string &origin,
-		  const std::string &channel,
-		  const std::string &message) override;
+	* \copydoc Plugin::onMe
+	*/
+	IRCCD_EXPORT void onMe(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &message) override;
 
 	/**
-	 * \copydoc Plugin::onMode
-	 */
-	void onMode(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &mode) override;
+	* \copydoc Plugin::onMode
+	*/
+	IRCCD_EXPORT void onMode(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &mode) override;
 
 	/**
-	 * \copydoc Plugin::onNames
-	 */
-	void onNames(Irccd &irccd,
-		     const std::shared_ptr<Server> &server,
-		     const std::string &channel,
-		     const std::vector<std::string> &list) override;
+	* \copydoc Plugin::onNames
+	*/
+	IRCCD_EXPORT void onNames(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &channel,
+		const std::vector<std::string> &list) override;
 
 	/**
-	 * \copydoc Plugin::onNick
-	 */
-	void onNick(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &nick) override;
+	* \copydoc Plugin::onNick
+	*/
+	IRCCD_EXPORT void onNick(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &nick) override;
 
 	/**
-	 * \copydoc Plugin::onNotice
-	 */
-	void onNotice(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &notice) override;
+	* \copydoc Plugin::onNotice
+	*/
+	IRCCD_EXPORT void onNotice(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &notice) override;
 
 	/**
-	 * \copydoc Plugin::onPart
-	 */
-	void onPart(Irccd &irccd,
-		    const std::shared_ptr<Server> &server,
-		    const std::string &origin,
-		    const std::string &channel,
-		    const std::string &reason) override;
+	* \copydoc Plugin::onPart
+	*/
+	IRCCD_EXPORT void onPart(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &reason) override;
 
 	/**
-	 * \copydoc Plugin::onQuery
-	 */
-	void onQuery(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &message) override;
+	* \copydoc Plugin::onQuery
+	*/
+	IRCCD_EXPORT void onQuery(Irccd &irccd, const std::shared_ptr<Server> &server, const std::string &origin, const std::string &message) override;
 
 	/**
-	 * \copydoc Plugin::onQueryCommand
-	 */
-	void onQueryCommand(Irccd &irccd,
-			    const std::shared_ptr<Server> &server,
-			    const std::string &origin,
-			    const std::string &message) override;
+	* \copydoc Plugin::onQueryCommand
+	*/
+	IRCCD_EXPORT void onQueryCommand(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &message) override;
 
 	/**
-	 * \copydoc Plugin::onReload
-	 */
-	void onReload(Irccd &irccd) override;
+	* \copydoc Plugin::onReload
+	*/
+	IRCCD_EXPORT void onReload(Irccd &irccd) override;
 
 	/**
-	 * \copydoc Plugin::onTopic
-	 */
-	void onTopic(Irccd &irccd,
-		     const std::shared_ptr<Server> &server,
-		     const std::string &origin,
-		     const std::string &channel,
-		     const std::string &topic) override;
+	* \copydoc Plugin::onTopic
+	*/
+	IRCCD_EXPORT void onTopic(Irccd &irccd,
+		const std::shared_ptr<Server> &server,
+		const std::string &origin,
+		const std::string &channel,
+		const std::string &topic) override;
 
 	/**
-	 * \copydoc Plugin::onUnload
-	 */
-	void onUnload(Irccd &irccd) override;
+	* \copydoc Plugin::onUnload
+	*/
+	IRCCD_EXPORT void onUnload(Irccd &irccd) override;
 
 	/**
-	 * \copydoc Plugin::onWhois
-	 */
-	void onWhois(Irccd &irccd, const std::shared_ptr<Server> &server, const ServerWhois &info) override;
+	* \copydoc Plugin::onWhois
+	*/
+	IRCCD_EXPORT void onWhois(Irccd &irccd, const std::shared_ptr<Server> &server, const ServerWhois &info) override;
 };
 
 } // !irccd
--- a/lib/irccd/plugin.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/plugin.hpp	Tue May 24 13:00:35 2016 +0200
@@ -29,6 +29,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "sysconfig.hpp"
 #include "util.hpp"
 
 namespace irccd {
--- a/lib/irccd/rule.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/rule.hpp	Tue May 24 13:00:35 2016 +0200
@@ -30,6 +30,8 @@
 #include <utility>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 /**
@@ -77,12 +79,12 @@
 	 * \param action the rule action
 	 * \throw std::invalid_argument if events are invalid
 	 */
-	Rule(RuleSet servers = RuleSet{},
-	     RuleSet channels = RuleSet{},
-	     RuleSet nicknames = RuleSet{},
-	     RuleSet plugins = RuleSet{},
-	     RuleSet events = RuleSet{},
-	     RuleAction action = RuleAction::Accept);
+	IRCCD_EXPORT Rule(RuleSet servers = RuleSet{},
+			  RuleSet channels = RuleSet{},
+			  RuleSet nicknames = RuleSet{},
+			  RuleSet plugins = RuleSet{},
+			  RuleSet events = RuleSet{},
+			  RuleAction action = RuleAction::Accept);
 
 	/**
 	 * Check if that rule apply for the given criterias.
@@ -94,53 +96,53 @@
 	 * \param event the event
 	 * \return true if match
 	 */
-	bool match(const std::string &server,
-		   const std::string &channel,
-		   const std::string &nick,
-		   const std::string &plugin,
-		   const std::string &event) const noexcept;
+	IRCCD_EXPORT bool match(const std::string &server,
+				const std::string &channel,
+				const std::string &nick,
+				const std::string &plugin,
+				const std::string &event) const noexcept;
 
 	/**
 	 * Get the action.
 	 *
 	 * \return the action
 	 */
-	RuleAction action() const noexcept;
+	IRCCD_EXPORT RuleAction action() const noexcept;
 
 	/**
 	 * Get the servers.
 	 *
 	 * \return the servers
 	 */
-	const RuleSet &servers() const noexcept;
+	IRCCD_EXPORT const RuleSet &servers() const noexcept;
 
 	/**
 	 * Get the channels.
 	 *
 	 * \return the channels
 	 */
-	const RuleSet &channels() const noexcept;
+	IRCCD_EXPORT const RuleSet &channels() const noexcept;
 
 	/**
 	 * Get the origins.
 	 *
 	 * \return the origins
 	 */
-	const RuleSet &origins() const noexcept;
+	IRCCD_EXPORT const RuleSet &origins() const noexcept;
 
 	/**
 	 * Get the plugins.
 	 *
 	 * \return the plugins
 	 */
-	const RuleSet &plugins() const noexcept;
+	IRCCD_EXPORT const RuleSet &plugins() const noexcept;
 
 	/**
 	 * Get the events.
 	 *
 	 * \return the events
 	 */
-	const RuleSet &events() const noexcept;
+	IRCCD_EXPORT const RuleSet &events() const noexcept;
 };
 
 } // !irccd
--- a/lib/irccd/server-event.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/server-event.cpp	Tue May 24 13:00:35 2016 +0200
@@ -28,41 +28,39 @@
 ServerEvent::ServerEvent(std::string server,
 			 std::string origin,
 			 std::string target,
-			 std::function<std::string (Plugin &)> plugin_function_name,
-			 std::function<void (Plugin &)> plugin_exec)
+			 std::function<std::string (Plugin &)> functionName,
+			 std::function<void (Plugin &)> exec)
 	: m_server(std::move(server))
 	, m_origin(std::move(origin))
 	, m_target(std::move(target))
-	, m_plugin_function_name(std::move(plugin_function_name))
-	, m_plugin_exec(std::move(plugin_exec))
+	, m_functionName(std::move(functionName))
+	, m_exec(std::move(exec))
 {
 }
 
 void ServerEvent::operator()(Irccd &irccd) const
 {
 	for (auto &plugin : irccd.pluginService().plugins()) {
-		auto eventname = m_plugin_function_name(*plugin);
+		auto eventname = m_functionName(*plugin);
 		auto allowed = irccd.ruleService().solve(m_server, m_target, m_origin, plugin->name(), eventname);
 
 		if (!allowed) {
 			log::debug() << "rule: event skipped on match" << std::endl;
 			continue;
-		} else {
+		} else
 			log::debug() << "rule: event allowed" << std::endl;
-		}
 
-		// TODO: server-event must not know which type of plugin
+		// TODO: server-event must not know which type of plugin.
+		// TODO: get generic error.
 		try {
-			m_plugin_exec(*plugin);
+			m_exec(*plugin);
 		} catch (const duk::ErrorInfo &info) {
 			log::warning() << "plugin " << plugin->name() << ": error: " << info.what() << std::endl;
 
-			if (!info.fileName.empty()) {
+			if (!info.fileName.empty())
 				log::warning() << "    " << info.fileName << ":" << info.lineNumber << std::endl;
-			}
-			if (!info.stack.empty()) {
+			if (!info.stack.empty())
 				log::warning() << "    " << info.stack << std::endl;
-			}
 		}
 	}
 }
--- a/lib/irccd/server-event.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/server-event.hpp	Tue May 24 13:00:35 2016 +0200
@@ -47,8 +47,8 @@
 	std::string m_server;
 	std::string m_origin;
 	std::string m_target;
-	std::function<std::string (Plugin &)> m_plugin_function_name;
-	std::function<void (Plugin &)> m_plugin_exec;
+	std::function<std::string (Plugin &)> m_functionName;
+	std::function<void (Plugin &)> m_exec;
 
 public:
 	/**
@@ -57,21 +57,21 @@
 	 * \param server the server name
 	 * \param origin the origin
 	 * \param target the target (channel or nickname)
-	 * \param plugin_function_name the JavaScript function to call (only for rules)
-	 * \param plugin_exec the plugin executor
+	 * \param functionName the function to call (only for rules)
+	 * \param exec the plugin executor
 	 */
-	ServerEvent(std::string server,
-		    std::string origin,
-		    std::string target,
-		    std::function<std::string (Plugin &)> plugin_function_name,
-		    std::function<void (Plugin &)> plugin_exec);
+	IRCCD_EXPORT ServerEvent(std::string server,
+				 std::string origin,
+				 std::string target,
+				 std::function<std::string (Plugin &)> functionName,
+				 std::function<void (Plugin &)> exec);
 
 	/**
 	 * Execute the event.
 	 *
 	 * \param irccd the irccd instance
 	 */
-	void operator()(Irccd &irccd) const;
+	IRCCD_EXPORT void operator()(Irccd &irccd) const;
 };
 
 } // !irccd
--- a/lib/irccd/server-state-connected.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/server-state-connected.hpp	Tue May 24 13:00:35 2016 +0200
@@ -38,12 +38,12 @@
 	/**
 	 * \copydoc ServerState::prepare
 	 */
-	void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
+	IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
 
 	/**
 	 * \copydoc ServerState::ident
 	 */
-	std::string ident() const override;
+	IRCCD_EXPORT std::string ident() const override;
 };
 
 } // !state
--- a/lib/irccd/server-state-connecting.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/server-state-connecting.hpp	Tue May 24 13:00:35 2016 +0200
@@ -43,12 +43,12 @@
 	/**
 	 * \copydoc ServerState::prepare
 	 */
-	void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
+	IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
 
 	/**
 	 * \copydoc ServerState::ident
 	 */
-	std::string ident() const override;
+	IRCCD_EXPORT std::string ident() const override;
 };
 
 } // !state
--- a/lib/irccd/server-state-disconnected.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/server-state-disconnected.hpp	Tue May 24 13:00:35 2016 +0200
@@ -42,12 +42,12 @@
 	/**
 	 * \copydoc ServerState::prepare
 	 */
-	void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
+	IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
 
 	/**
 	 * \copydoc ServerState::ident
 	 */
-	std::string ident() const override;
+	IRCCD_EXPORT std::string ident() const override;
 };
 
 } // !state
--- a/lib/irccd/server.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/server.hpp	Tue May 24 13:00:35 2016 +0200
@@ -38,6 +38,7 @@
 #include "elapsed-timer.hpp"
 #include "server-state.hpp"
 #include "signals.hpp"
+#include "sysconfig.hpp"
 
 namespace irccd {
 
@@ -416,7 +417,7 @@
 	 * \param value the value
 	 * \return a channel
 	 */
-	static ServerChannel splitChannel(const std::string &value);
+	IRCCD_EXPORT static ServerChannel splitChannel(const std::string &value);
 
 	/**
 	 * Construct a server.
@@ -426,12 +427,12 @@
 	 * \param identity the identity
 	 * \param settings the settings
 	 */
-	Server(std::string name, ServerInfo info, ServerIdentity identity = {}, ServerSettings settings = {});
+	IRCCD_EXPORT Server(std::string name, ServerInfo info, ServerIdentity identity = {}, ServerSettings settings = {});
 
 	/**
 	 * Destructor. Close the connection if needed.
 	 */
-	virtual ~Server();
+	IRCCD_EXPORT virtual ~Server();
 
 	/**
 	 * Get the server identifier.
@@ -518,17 +519,17 @@
 	/**
 	 * Switch to next state if it has.
 	 */
-	void update() noexcept;
+	IRCCD_EXPORT void update() noexcept;
 
 	/**
 	 * Force disconnection.
 	 */
-	void disconnect() noexcept;
+	IRCCD_EXPORT void disconnect() noexcept;
 
 	/**
 	 * Asks for a reconnection.
 	 */
-	void reconnect() noexcept;
+	IRCCD_EXPORT void reconnect() noexcept;
 
 	/**
 	 * Prepare the IRC session.
@@ -547,7 +548,7 @@
 	 * \param setoutput
 	 * \throw any exception that have been throw from user functions
 	 */
-	void sync(fd_set &setinput, fd_set &setoutput);
+	IRCCD_EXPORT void sync(fd_set &setinput, fd_set &setoutput);
 
 	/**
 	 * Determine if the nickname is the bot itself.
@@ -555,7 +556,7 @@
 	 * \param nick the nickname to check
 	 * \return true if it is the bot
 	 */
-	bool isSelf(const std::string &nick) const noexcept;
+	IRCCD_EXPORT bool isSelf(const std::string &nick) const noexcept;
 
 	/**
 	 * Change the channel mode.
@@ -563,7 +564,7 @@
 	 * \param channel the channel
 	 * \param mode the new mode
 	 */
-	virtual void cmode(std::string channel, std::string mode);
+	IRCCD_EXPORT virtual void cmode(std::string channel, std::string mode);
 
 	/**
 	 * Send a channel notice.
@@ -571,7 +572,7 @@
 	 * \param channel the channel
 	 * \param message message notice
 	 */
-	virtual void cnotice(std::string channel, std::string message);
+	IRCCD_EXPORT virtual void cnotice(std::string channel, std::string message);
 
 	/**
 	 * Invite a user to a channel.
@@ -579,7 +580,7 @@
 	 * \param target the target nickname
 	 * \param channel the channel
 	 */
-	virtual void invite(std::string target, std::string channel);
+	IRCCD_EXPORT virtual void invite(std::string target, std::string channel);
 
 	/**
 	 * Join a channel, the password is optional and can be kept empty.
@@ -587,7 +588,7 @@
 	 * \param channel the channel to join
 	 * \param password the optional password
 	 */
-	virtual void join(std::string channel, std::string password = "");
+	IRCCD_EXPORT virtual void join(std::string channel, std::string password = "");
 
 	/**
 	 * Kick someone from the channel. Please be sure to have the rights
@@ -597,7 +598,7 @@
 	 * \param channel from which channel
 	 * \param reason the optional reason
 	 */
-	virtual void kick(std::string target, std::string channel, std::string reason = "");
+	IRCCD_EXPORT virtual void kick(std::string target, std::string channel, std::string reason = "");
 
 	/**
 	 * Send a CTCP Action as known as /me. The target may be either a
@@ -606,7 +607,7 @@
 	 * \param target the nickname or the channel
 	 * \param message the message
 	 */
-	virtual void me(std::string target, std::string message);
+	IRCCD_EXPORT virtual void me(std::string target, std::string message);
 
 	/**
 	 * Send a message to the specified target or channel.
@@ -614,28 +615,28 @@
 	 * \param target the target
 	 * \param message the message
 	 */
-	virtual void message(std::string target, std::string message);
+	IRCCD_EXPORT virtual void message(std::string target, std::string message);
 
 	/**
 	 * Change your user mode.
 	 *
 	 * \param mode the mode
 	 */
-	virtual void mode(std::string mode);
+	IRCCD_EXPORT virtual void mode(std::string mode);
 
 	/**
 	 * Request the list of names.
 	 *
 	 * \param channel the channel
 	 */
-	virtual void names(std::string channel);
+	IRCCD_EXPORT virtual void names(std::string channel);
 
 	/**
 	 * Change your nickname.
 	 *
 	 * \param newnick the new nickname to use
 	 */
-	virtual void nick(std::string newnick);
+	IRCCD_EXPORT virtual void nick(std::string newnick);
 
 	/**
 	 * Send a private notice.
@@ -643,7 +644,7 @@
 	 * \param target the target
 	 * \param message the notice message
 	 */
-	virtual void notice(std::string target, std::string message);
+	IRCCD_EXPORT virtual void notice(std::string target, std::string message);
 
 	/**
 	 * Part from a channel.
@@ -653,7 +654,7 @@
 	 * \param channel the channel to leave
 	 * \param reason the optional reason
 	 */
-	virtual void part(std::string channel, std::string reason = "");
+	IRCCD_EXPORT virtual void part(std::string channel, std::string reason = "");
 
 	/**
 	 * Send a raw message to the IRC server. You don't need to add
@@ -662,7 +663,7 @@
 	 * \warning Use this function with care
 	 * \param raw the raw message (without `\r\n\r\n`)
 	 */
-	virtual void send(std::string raw);
+	IRCCD_EXPORT virtual void send(std::string raw);
 
 	/**
 	 * Change the channel topic.
@@ -670,14 +671,14 @@
 	 * \param channel the channel
 	 * \param topic the desired topic
 	 */
-	virtual void topic(std::string channel, std::string topic);
+	IRCCD_EXPORT virtual void topic(std::string channel, std::string topic);
 
 	/**
 	 * Request for whois information.
 	 *
 	 * \param target the target nickname
 	 */
-	virtual void whois(std::string target);
+	IRCCD_EXPORT virtual void whois(std::string target);
 };
 
 } // !irccd
--- a/lib/irccd/service-interrupt.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-interrupt.cpp	Tue May 24 13:00:35 2016 +0200
@@ -38,9 +38,8 @@
 {
 	FD_SET(m_in.handle(), &in);
 
-	if (m_in.handle() > max) {
+	if (m_in.handle() > max)
 		max = m_in.handle();
-	}
 }
 
 void InterruptService::sync(fd_set &in, fd_set &)
--- a/lib/irccd/service-interrupt.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-interrupt.hpp	Tue May 24 13:00:35 2016 +0200
@@ -42,22 +42,22 @@
 	 *
 	 * \throw std::runtime_error on errors
 	 */
-	InterruptService();
+	IRCCD_EXPORT InterruptService();
 
 	/**
 	 * \copydoc Service::prepare
 	 */
-	void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
+	IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
 
 	/**
 	 * \copydoc Service::sync
 	 */
-	void sync(fd_set &in, fd_set &out) override;
+	IRCCD_EXPORT void sync(fd_set &in, fd_set &out) override;
 
 	/**
 	 * Request interruption.
 	 */
-	void interrupt() noexcept;
+	IRCCD_EXPORT void interrupt() noexcept;
 };
 
 } // !irccd
--- a/lib/irccd/service-module.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-module.hpp	Tue May 24 13:00:35 2016 +0200
@@ -42,7 +42,7 @@
 	/**
 	 * Construct the service and predefined irccd API.
 	 */
-	ModuleService();
+	IRCCD_EXPORT ModuleService();
 
 	/**
 	 * Get all modules.
@@ -59,7 +59,7 @@
 	 *
 	 * \param name the name
 	 */
-	bool contains(const std::string &name) const;
+	IRCCD_EXPORT bool contains(const std::string &name) const;
 
 	/**
 	 * Add a JavaScript API module.
@@ -68,7 +68,7 @@
 	 * \pre !contains(module)
 	 * \param module the module
 	 */
-	void add(std::shared_ptr<Module> module);
+	IRCCD_EXPORT void add(std::shared_ptr<Module> module);
 };
 
 } // !irccd
--- a/lib/irccd/service-plugin.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-plugin.hpp	Tue May 24 13:00:35 2016 +0200
@@ -1,166 +1,166 @@
-/*
- * service-plugin.hpp -- manage plugins
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_PLUGIN_HPP
-#define IRCCD_SERVICE_PLUGIN_HPP
-
-/**
- * \file service-plugin.hpp
- * \brief Manage plugins.
- */
-
-#include <unordered_map>
-#include <memory>
-#include <vector>
-
-#include "plugin-js.hpp"
-
-namespace irccd {
-
-class Irccd;
-
-/**
- * \brief Manage plugins.
- */
-class PluginService {
-private:
-	Irccd &m_irccd;
-	std::vector<std::shared_ptr<Plugin>> m_plugins;
-	std::unordered_map<std::string, PluginConfig> m_config;
-	std::unordered_map<std::string, PluginFormats> m_formats;
-
-public:
-	/**
-	 * Create the plugin service.
-	 *
-	 * \param irccd the irccd instance
-	 */
-	PluginService(Irccd &irccd) noexcept;
-
-	/**
-	 * Destroy plugins.
-	 */
-	~PluginService();
-
-	/**
-	 * Get the list of plugins.
-	 *
-	 * \return the list of plugins
-	 */
-	inline const std::vector<std::shared_ptr<Plugin>> &plugins() const noexcept
-	{
-		return m_plugins;
-	}
-
-	/**
-	 * Check if a plugin is loaded.
-	 *
-	 * \param name the plugin id
-	 * \return true if has plugin
-	 */
-	bool has(const std::string &name) const noexcept;
-
-	/**
-	 * Get a loaded plugin or null if not found.
-	 *
-	 * \param name the plugin id
-	 * \return the plugin or empty one if not found
-	 */
-	std::shared_ptr<Plugin> get(const std::string &name) const noexcept;
-
-	/**
-	 * Find a loaded plugin.
-	 *
-	 * \param name the plugin id
-	 * \return the plugin
-	 * \throws std::out_of_range if not found
-	 */
-	std::shared_ptr<Plugin> require(const std::string &name) const;
-
-	/**
-	 * Add the specified plugin to the registry.
-	 *
-	 * \pre plugin != nullptr
-	 * \param plugin the plugin
-	 * \note the plugin is only added to the list, no action is performed on it
-	 */
-	void add(std::shared_ptr<Plugin> plugin);
-
-	/**
-	 * Configure a plugin.
-	 *
-	 * If the plugin is already loaded, its configuration is updated.
-	 *
-	 * \param name the plugin name
-	 * \param config the new configuration
-	 */
-	void configure(const std::string &name, PluginConfig config);
-
-	/**
-	 * Get a configuration for a plugin.
-	 *
-	 * \param name the plugin name
-	 * \return the configuration or default one if not found
-	 */
-	PluginConfig config(const std::string &name) const;
-
-	/**
-	 * Add formatting for a plugin.
-	 *
-	 * \param name the plugin name
-	 * \param formats the formats
-	 */
-	void setFormats(const std::string &name, PluginFormats formats);
-
-	/**
-	 * Get formats for a plugin.
-	 *
-	 * \param name the plugin name
-	 * \return the formats
-	 */
-	PluginFormats formats(const std::string &name) const;
-
-	/**
-	 * Convenient wrapper that loads a plugin, call onLoad and add it to the registry.
-	 *
-	 * Any errors are printed using logger.
-	 *
-	 * \param name the name
-	 * \param path the optional path (searched if empty)
-	 */
-	void load(std::string name, std::string path = "");
-
-	/**
-	 * Unload a plugin and remove it.
-	 *
-	 * \param name the plugin id
-	 */
-	void unload(const std::string &name);
-
-	/**
-	 * Reload a plugin by calling onReload.
-	 *
-	 * \param name the plugin name
-	 * \throw std::exception on failures
-	 */
-	void reload(const std::string &name);
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_PLUGIN_HPP
+/*
+ * service-plugin.hpp -- manage plugins
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_SERVICE_PLUGIN_HPP
+#define IRCCD_SERVICE_PLUGIN_HPP
+
+/**
+ * \file service-plugin.hpp
+ * \brief Manage plugins.
+ */
+
+#include <unordered_map>
+#include <memory>
+#include <vector>
+
+#include "plugin-js.hpp"
+
+namespace irccd {
+
+class Irccd;
+
+/**
+ * \brief Manage plugins.
+ */
+class PluginService {
+private:
+	Irccd &m_irccd;
+	std::vector<std::shared_ptr<Plugin>> m_plugins;
+	std::unordered_map<std::string, PluginConfig> m_config;
+	std::unordered_map<std::string, PluginFormats> m_formats;
+
+public:
+	/**
+	 * Create the plugin service.
+	 *
+	 * \param irccd the irccd instance
+	 */
+	IRCCD_EXPORT PluginService(Irccd &irccd) noexcept;
+
+	/**
+	 * Destroy plugins.
+	 */
+	IRCCD_EXPORT ~PluginService();
+
+	/**
+	 * Get the list of plugins.
+	 *
+	 * \return the list of plugins
+	 */
+	inline const std::vector<std::shared_ptr<Plugin>> &plugins() const noexcept
+	{
+		return m_plugins;
+	}
+
+	/**
+	 * Check if a plugin is loaded.
+	 *
+	 * \param name the plugin id
+	 * \return true if has plugin
+	 */
+	IRCCD_EXPORT bool has(const std::string &name) const noexcept;
+
+	/**
+	 * Get a loaded plugin or null if not found.
+	 *
+	 * \param name the plugin id
+	 * \return the plugin or empty one if not found
+	 */
+	IRCCD_EXPORT std::shared_ptr<Plugin> get(const std::string &name) const noexcept;
+
+	/**
+	 * Find a loaded plugin.
+	 *
+	 * \param name the plugin id
+	 * \return the plugin
+	 * \throws std::out_of_range if not found
+	 */
+	IRCCD_EXPORT std::shared_ptr<Plugin> require(const std::string &name) const;
+
+	/**
+	 * Add the specified plugin to the registry.
+	 *
+	 * \pre plugin != nullptr
+	 * \param plugin the plugin
+	 * \note the plugin is only added to the list, no action is performed on it
+	 */
+	IRCCD_EXPORT void add(std::shared_ptr<Plugin> plugin);
+
+	/**
+	 * Configure a plugin.
+	 *
+	 * If the plugin is already loaded, its configuration is updated.
+	 *
+	 * \param name the plugin name
+	 * \param config the new configuration
+	 */
+	IRCCD_EXPORT void configure(const std::string &name, PluginConfig config);
+
+	/**
+	 * Get a configuration for a plugin.
+	 *
+	 * \param name the plugin name
+	 * \return the configuration or default one if not found
+	 */
+	IRCCD_EXPORT PluginConfig config(const std::string &name) const;
+
+	/**
+	 * Add formatting for a plugin.
+	 *
+	 * \param name the plugin name
+	 * \param formats the formats
+	 */
+	IRCCD_EXPORT void setFormats(const std::string &name, PluginFormats formats);
+
+	/**
+	 * Get formats for a plugin.
+	 *
+	 * \param name the plugin name
+	 * \return the formats
+	 */
+	IRCCD_EXPORT PluginFormats formats(const std::string &name) const;
+
+	/**
+	 * Convenient wrapper that loads a plugin, call onLoad and add it to the registry.
+	 *
+	 * Any errors are printed using logger.
+	 *
+	 * \param name the name
+	 * \param path the optional path (searched if empty)
+	 */
+	IRCCD_EXPORT void load(std::string name, std::string path = "");
+
+	/**
+	 * Unload a plugin and remove it.
+	 *
+	 * \param name the plugin id
+	 */
+	IRCCD_EXPORT void unload(const std::string &name);
+
+	/**
+	 * Reload a plugin by calling onReload.
+	 *
+	 * \param name the plugin name
+	 * \throw std::exception on failures
+	 */
+	IRCCD_EXPORT void reload(const std::string &name);
+};
+
+} // !irccd
+
+#endif // !IRCCD_SERVICE_PLUGIN_HPP
--- a/lib/irccd/service-rule.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-rule.cpp	Tue May 24 13:00:35 2016 +0200
@@ -1,79 +1,78 @@
-/*
- * service-rule.cpp -- store and solve rules
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <cassert>
-
-#include <format.h>
-
-#include "logger.hpp"
-#include "service-rule.hpp"
-#include "util.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-void RuleService::add(Rule rule)
-{
-	m_rules.push_back(std::move(rule));
-}
-
-void RuleService::insert(Rule rule, unsigned position)
-{
-	assert(position <= m_rules.size());
-
-	m_rules.insert(m_rules.begin() + position, std::move(rule));
-}
-
-void RuleService::remove(unsigned position)
-{
-	assert(position < m_rules.size());
-
-	m_rules.erase(m_rules.begin() + position);
-}
-
-bool RuleService::solve(const std::string &server,
-			const std::string &channel,
-			const std::string &origin,
-			const std::string &plugin,
-			const std::string &event) noexcept
-{
-	bool result = true;
-
-	log::debug("rule: solving for server={}, channel={}, origin={}, plugin={}, event={}"_format(server, channel,
-		   origin, plugin, event));
-
-	int i = 0;
-	for (const Rule &rule : m_rules) {
-		log::debug() << "  candidate " << i++ << ":\n"
-			     << "    servers: " << util::join(rule.servers().begin(), rule.servers().end()) << "\n"
-			     << "    channels: " << util::join(rule.channels().begin(), rule.channels().end()) << "\n"
-			     << "    origins: " << util::join(rule.origins().begin(), rule.origins().end()) << "\n"
-			     << "    plugins: " << util::join(rule.plugins().begin(), rule.plugins().end()) << "\n"
-			     << "    events: " << util::join(rule.events().begin(), rule.events().end()) << "\n"
-			     << "    action: " << ((rule.action() == RuleAction::Accept) ? "accept" : "drop") << std::endl;
-
-		if (rule.match(server, channel, origin, plugin, event)) {
-			result = rule.action() == RuleAction::Accept;
-		}
-	}
-
-	return result;
-}
-
-} // !irccd
+/*
+ * service-rule.cpp -- store and solve rules
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cassert>
+
+#include <format.h>
+
+#include "logger.hpp"
+#include "service-rule.hpp"
+#include "util.hpp"
+
+using namespace fmt::literals;
+
+namespace irccd {
+
+void RuleService::add(Rule rule)
+{
+	m_rules.push_back(std::move(rule));
+}
+
+void RuleService::insert(Rule rule, unsigned position)
+{
+	assert(position <= m_rules.size());
+
+	m_rules.insert(m_rules.begin() + position, std::move(rule));
+}
+
+void RuleService::remove(unsigned position)
+{
+	assert(position < m_rules.size());
+
+	m_rules.erase(m_rules.begin() + position);
+}
+
+bool RuleService::solve(const std::string &server,
+			const std::string &channel,
+			const std::string &origin,
+			const std::string &plugin,
+			const std::string &event) noexcept
+{
+	bool result = true;
+
+	log::debug("rule: solving for server={}, channel={}, origin={}, plugin={}, event={}"_format(server, channel,
+		   origin, plugin, event));
+
+	int i = 0;
+	for (const Rule &rule : m_rules) {
+		log::debug() << "  candidate " << i++ << ":\n"
+			     << "    servers: " << util::join(rule.servers().begin(), rule.servers().end()) << "\n"
+			     << "    channels: " << util::join(rule.channels().begin(), rule.channels().end()) << "\n"
+			     << "    origins: " << util::join(rule.origins().begin(), rule.origins().end()) << "\n"
+			     << "    plugins: " << util::join(rule.plugins().begin(), rule.plugins().end()) << "\n"
+			     << "    events: " << util::join(rule.events().begin(), rule.events().end()) << "\n"
+			     << "    action: " << ((rule.action() == RuleAction::Accept) ? "accept" : "drop") << std::endl;
+
+		if (rule.match(server, channel, origin, plugin, event))
+			result = rule.action() == RuleAction::Accept;
+	}
+
+	return result;
+}
+
+} // !irccd
--- a/lib/irccd/service-rule.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-rule.hpp	Tue May 24 13:00:35 2016 +0200
@@ -1,103 +1,103 @@
-/*
- * service-rule.hpp -- store and solve rules
- *
- * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_SERVICE_RULE_HPP
-#define IRCCD_SERVICE_RULE_HPP
-
-/**
- * \file service-rule.hpp
- * \brief Store and solve rules.
- */
-
-#include <vector>
-
-#include "rule.hpp"
-
-namespace irccd {
-
-/**
- * \brief Store and solve rules.
- */
-class RuleService {
-private:
-	std::vector<Rule> m_rules;
-
-public:
-	/**
-	 * Get the list of rules.
-	 *
-	 * \return the list of rules
-	 */
-	inline const std::vector<Rule> &rules() const noexcept
-	{
-		return m_rules;
-	}
-
-	/**
-	 * Get the number of rules.
-	 *
-	 * \return the number of rules
-	 */
-	inline unsigned length() const noexcept
-	{
-		return m_rules.size();
-	}
-
-	/**
-	 * Append a rule.
-	 *
-	 * \param rule the rule to append
-	 */
-	void add(Rule rule);
-
-	/**
-	 * Insert a new rule at the specified position.
-	 *
-	 * \param rule the rule
-	 * \param position the position
-	 */
-	void insert(Rule rule, unsigned position);
-
-	/**
-	 * Remove a new rule from the specified position.
-	 *
-	 * \pre position must be valid
-	 * \param position the position
-	 */
-	void remove(unsigned position);
-
-	/**
-	 * Resolve the action to execute with the specified list of rules.
-	 *
-	 * \param server the server name
-	 * \param channel the channel name
-	 * \param origin the origin
-	 * \param plugin the plugin name
-	 * \param event the event name (e.g onKick)
-	 * \return true if the plugin must be called
-	 */
-	bool solve(const std::string &server,
-		   const std::string &channel,
-		   const std::string &origin,
-		   const std::string &plugin,
-		   const std::string &event) noexcept;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVICE_RULE_HPP
+/*
+ * service-rule.hpp -- store and solve rules
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_SERVICE_RULE_HPP
+#define IRCCD_SERVICE_RULE_HPP
+
+/**
+ * \file service-rule.hpp
+ * \brief Store and solve rules.
+ */
+
+#include <vector>
+
+#include "rule.hpp"
+
+namespace irccd {
+
+/**
+ * \brief Store and solve rules.
+ */
+class RuleService {
+private:
+	std::vector<Rule> m_rules;
+
+public:
+	/**
+	 * Get the list of rules.
+	 *
+	 * \return the list of rules
+	 */
+	inline const std::vector<Rule> &rules() const noexcept
+	{
+		return m_rules;
+	}
+
+	/**
+	 * Get the number of rules.
+	 *
+	 * \return the number of rules
+	 */
+	inline unsigned length() const noexcept
+	{
+		return m_rules.size();
+	}
+
+	/**
+	 * Append a rule.
+	 *
+	 * \param rule the rule to append
+	 */
+	IRCCD_EXPORT void add(Rule rule);
+
+	/**
+	 * Insert a new rule at the specified position.
+	 *
+	 * \param rule the rule
+	 * \param position the position
+	 */
+	IRCCD_EXPORT void insert(Rule rule, unsigned position);
+
+	/**
+	 * Remove a new rule from the specified position.
+	 *
+	 * \pre position must be valid
+	 * \param position the position
+	 */
+	IRCCD_EXPORT void remove(unsigned position);
+
+	/**
+	 * Resolve the action to execute with the specified list of rules.
+	 *
+	 * \param server the server name
+	 * \param channel the channel name
+	 * \param origin the origin
+	 * \param plugin the plugin name
+	 * \param event the event name (e.g onKick)
+	 * \return true if the plugin must be called
+	 */
+	IRCCD_EXPORT bool solve(const std::string &server,
+				const std::string &channel,
+				const std::string &origin,
+				const std::string &plugin,
+				const std::string &event) noexcept;
+};
+
+} // !irccd
+
+#endif // !IRCCD_SERVICE_RULE_HPP
--- a/lib/irccd/service-server.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-server.cpp	Tue May 24 13:00:35 2016 +0200
@@ -38,9 +38,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onChannelMode:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -74,9 +73,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onChannelNotice:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -107,9 +105,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onConnect" << std::endl;
 
@@ -134,9 +131,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onInvite:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -166,9 +162,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onJoin:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -197,9 +192,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onKick:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -232,9 +226,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onMessage:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -270,9 +263,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onMe:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -303,9 +295,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onMode\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -334,9 +325,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onNames:\n";
 	log::debug() << "  channel: " << channel << "\n";
@@ -367,9 +357,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onNick:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -398,9 +387,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onNotice:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -429,9 +417,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onPart:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -462,9 +449,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onQuery:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -498,9 +484,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onTopic:\n";
 	log::debug() << "  origin: " << origin << "\n";
@@ -531,9 +516,8 @@
 {
 	std::shared_ptr<Server> server = ptr.lock();
 
-	if (!server) {
+	if (!server)
 		return;
-	}
 
 	log::debug() << "server " << server->name() << ": event onWhois\n";
 	log::debug() << "  nickname: " << whois.nick << "\n";
@@ -577,9 +561,8 @@
 
 void ServerService::sync(fd_set &in, fd_set &out)
 {
-	for (auto &server : m_servers) {
+	for (auto &server : m_servers)
 		server->sync(in, out);
-	}
 }
 
 bool ServerService::has(const std::string &name) const noexcept
@@ -633,9 +616,8 @@
 		return server->name() == name;
 	});
 
-	if (it == m_servers.end()) {
+	if (it == m_servers.end())
 		return nullptr;
-	}
 
 	return *it;
 }
@@ -644,9 +626,8 @@
 {
 	auto server = get(name);
 
-	if (!server) {
+	if (!server)
 		throw std::invalid_argument("server {} not found"_format(name));
-	}
 
 	return server;
 }
@@ -665,9 +646,8 @@
 
 void ServerService::clear() noexcept
 {
-	for (auto &server : m_servers) {
+	for (auto &server : m_servers)
 		server->disconnect();
-	}
 
 	m_servers.clear();
 }
--- a/lib/irccd/service-server.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-server.hpp	Tue May 24 13:00:35 2016 +0200
@@ -65,17 +65,17 @@
 	/**
 	 * Create the server service.
 	 */
-	ServerService(Irccd &instance);
+	IRCCD_EXPORT ServerService(Irccd &instance);
 
 	/**
 	 * \copydoc Service::prepare
 	 */
-	void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
+	IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
 
 	/**
 	 * \copydoc Service::sync
 	 */
-	void sync(fd_set &in, fd_set &out) override;
+	IRCCD_EXPORT void sync(fd_set &in, fd_set &out) override;
 
 	/**
 	 * Get the list of servers
@@ -93,7 +93,7 @@
 	 * \param name the name
 	 * \return true if exists
 	 */
-	bool has(const std::string &name) const noexcept;
+	IRCCD_EXPORT bool has(const std::string &name) const noexcept;
 
 	/**
 	 * Add a new server to the application.
@@ -101,7 +101,7 @@
 	 * \pre hasServer must return false
 	 * \param sv the server
 	 */
-	void add(std::shared_ptr<Server> sv);
+	IRCCD_EXPORT void add(std::shared_ptr<Server> sv);
 
 	/**
 	 * Get a server or empty one if not found
@@ -109,7 +109,7 @@
 	 * \param name the server name
 	 * \return the server or empty one if not found
 	 */
-	std::shared_ptr<Server> get(const std::string &name) const noexcept;
+	IRCCD_EXPORT std::shared_ptr<Server> get(const std::string &name) const noexcept;
 
 	/**
 	 * Find a server by name.
@@ -118,8 +118,7 @@
 	 * \return the server
 	 * \throw std::out_of_range if the server does not exist
 	 */
-	std::shared_ptr<Server> require(const std::string &name) const;
-
+	IRCCD_EXPORT std::shared_ptr<Server> require(const std::string &name) const;
 
 	/**
 	 * Remove a server from the irccd instance.
@@ -128,14 +127,14 @@
 	 *
 	 * \param name the server name
 	 */
-	void remove(const std::string &name);
+	IRCCD_EXPORT void remove(const std::string &name);
 
 	/**
 	 * Remove all servers.
 	 *
 	 * All servers will be disconnected.
 	 */
-	void clear() noexcept;
+	IRCCD_EXPORT void clear() noexcept;
 };
 
 } // !irccd
--- a/lib/irccd/service-transport.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-transport.cpp	Tue May 24 13:00:35 2016 +0200
@@ -32,9 +32,8 @@
 		// 0. Be sure the object still exists.
 		auto tc = ptr.lock();
 
-		if (!tc) {
+		if (!tc)
 			return;
-		}
 
 		// 1. Check if the Json object is valid.
 		auto name = object.find("command");
@@ -85,9 +84,8 @@
 
 		auto tc = ptr.lock();
 
-		if (tc) {
+		if (tc)
 			m_clients.erase(std::find(m_clients.begin(), m_clients.end(), tc));
-		}
 	});
 }
 
@@ -102,21 +100,18 @@
 	for (const auto &transport : m_servers) {
 		FD_SET(transport->handle(), &in);
 
-		if (transport->handle() > max) {
+		if (transport->handle() > max)
 			max = transport->handle();
-		}
 	}
 
 	// Transport clients.
 	for (const auto &client : m_clients) {
 		FD_SET(client->handle(), &in);
 
-		if (client->hasOutput()) {
+		if (client->hasOutput())
 			FD_SET(client->handle(), &out);
-		}
-		if (client->handle() > max) {
+		if (client->handle() > max)
 			max = client->handle();
-		}
 	}
 }
 
@@ -126,9 +121,8 @@
 
 	// Transport servers.
 	for (const auto &transport : m_servers) {
-		if (!FD_ISSET(transport->handle(), &in)) {
+		if (!FD_ISSET(transport->handle(), &in))
 			continue;
-		}
 
 		log::debug("transport: new client connected");
 
@@ -174,9 +168,8 @@
 void TransportService::broadcast(std::string data)
 {
 	// Asynchronous send.
-	for (const auto &client : m_clients) {
+	for (const auto &client : m_clients)
 		client->send(data);
-	}
 }
 
 } // !irccd
--- a/lib/irccd/service-transport.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service-transport.hpp	Tue May 24 13:00:35 2016 +0200
@@ -56,31 +56,31 @@
 	 *
 	 * \param irccd the irccd instance
 	 */
-	TransportService(Irccd &irccd) noexcept;
+	IRCCD_EXPORT TransportService(Irccd &irccd) noexcept;
 
 	/**
 	 * \copydoc Service::prepare
 	 */
-	void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
+	IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
 
 	/**
 	 * \copydoc Service::sync
 	 */
-	void sync(fd_set &in, fd_set &out) override;
+	IRCCD_EXPORT void sync(fd_set &in, fd_set &out) override;
 
 	/**
 	 * Add a transport server.
 	 *
 	 * \param ts the transport server
 	 */
-	void add(std::shared_ptr<TransportServer> ts);
+	IRCCD_EXPORT void add(std::shared_ptr<TransportServer> ts);
 
 	/**
 	 * Send data to all clients.
 	 *
 	 * \param data the data
 	 */
-	void broadcast(std::string data);
+	IRCCD_EXPORT void broadcast(std::string data);
 
 };
 
--- a/lib/irccd/service.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/service.hpp	Tue May 24 13:00:35 2016 +0200
@@ -25,6 +25,8 @@
  */
 
 #include "sockets.hpp"
+#include "sysconfig.hpp"
+#include "util.hpp"
 
 namespace irccd {
 
@@ -60,9 +62,7 @@
 	 */
 	virtual void prepare(fd_set &in, fd_set &out, net::Handle &max)
 	{
-		(void)in;
-		(void)out;
-		(void)max;
+		util::unused(in, out, max);
 	}
 
 	/**
@@ -73,8 +73,7 @@
 	 */
 	virtual void sync(fd_set &in, fd_set &out)
 	{
-		(void)in;
-		(void)out;
+		util::unused(in, out);
 	}
 };
 
--- a/lib/irccd/sockets.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/sockets.hpp	Tue May 24 13:00:35 2016 +0200
@@ -63,6 +63,8 @@
  * | Mac OS X      | kqueue(2)               | Kqueue       |
  */
 
+#include "sysconfig.hpp"
+
 #if defined(_WIN32)
 #  if _WIN32_WINNT >= 0x0600 && !defined(SOCKET_HAVE_POLL)
 #    define SOCKET_HAVE_POLL
@@ -295,12 +297,12 @@
  * Initialize the socket library. Except if you defined SOCKET_NO_AUTO_INIT, you don't need to call this
  * function manually.
  */
-void init() noexcept;
+IRCCD_EXPORT void init() noexcept;
 
 /**
  * Close the socket library.
  */
-void finish() noexcept;
+IRCCD_EXPORT void finish() noexcept;
 
 #if !defined(SOCKET_NO_SSL)
 
@@ -313,12 +315,12 @@
  * Initialize the OpenSSL library. Except if you defined SOCKET_NO_AUTO_SSL_INIT, you don't need to call this function
  * manually.
  */
-void init() noexcept;
+IRCCD_EXPORT void init() noexcept;
 
 /**
  * Close the OpenSSL library.
  */
-void finish() noexcept;
+IRCCD_EXPORT void finish() noexcept;
 
 } // !ssl
 
@@ -330,7 +332,7 @@
  *
  * \return a string message
  */
-std::string error();
+IRCCD_EXPORT std::string error();
 
 /**
  * Get the last system error.
@@ -338,7 +340,7 @@
  * \param errn the error number (errno or WSAGetLastError)
  * \return the error
  */
-std::string error(int errn);
+IRCCD_EXPORT std::string error(int errn);
 
 /* }}} */
 
@@ -379,7 +381,7 @@
 	 * \param code which kind of error
 	 * \param function the function name
 	 */
-	Error(Code code, std::string function);
+	IRCCD_EXPORT Error(Code code, std::string function);
 
 	/**
 	 * Constructor that use the system error set by the user.
@@ -388,7 +390,7 @@
 	 * \param function the function name
 	 * \param error the error
 	 */
-	Error(Code code, std::string function, int error);
+	IRCCD_EXPORT Error(Code code, std::string function, int error);
 
 	/**
 	 * Constructor that set the error specified by the user.
@@ -397,7 +399,7 @@
 	 * \param function the function name
 	 * \param error the error
 	 */
-	Error(Code code, std::string function, std::string error);
+	IRCCD_EXPORT Error(Code code, std::string function, std::string error);
 
 	/**
 	 * Get which function has triggered the error.
@@ -2826,7 +2828,7 @@
 	/**
 	 * Return the sockets
 	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &table, int ms);
+	IRCCD_EXPORT std::vector<ListenerStatus> wait(const ListenerTable &table, int ms);
 
 	/**
 	 * Backend identifier
@@ -2857,17 +2859,17 @@
 	/**
 	 * Set the handle.
 	 */
-	void set(const ListenerTable &, Handle, Condition, bool);
+	IRCCD_EXPORT void set(const ListenerTable &, Handle, Condition, bool);
 
 	/**
 	 * Unset the handle.
 	 */
-	void unset(const ListenerTable &, Handle, Condition, bool);
+	IRCCD_EXPORT void unset(const ListenerTable &, Handle, Condition, bool);
 
 	/**
 	 * Wait for events.
 	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &, int ms);
+	IRCCD_EXPORT std::vector<ListenerStatus> wait(const ListenerTable &, int ms);
 
 	/**
 	 * Backend identifier
@@ -2904,27 +2906,27 @@
 	/**
 	 * Construct the epoll instance.
 	 */
-	Epoll();
+	IRCCD_EXPORT Epoll();
 
 	/**
 	 * Close the epoll instance.
 	 */
-	~Epoll();
+	IRCCD_EXPORT ~Epoll();
 
 	/**
 	 * Set the handle.
 	 */
-	void set(const ListenerTable &, Handle, Condition, bool);
+	IRCCD_EXPORT void set(const ListenerTable &, Handle, Condition, bool);
 
 	/**
 	 * Unset the handle.
 	 */
-	void unset(const ListenerTable &, Handle, Condition, bool);
+	IRCCD_EXPORT void unset(const ListenerTable &, Handle, Condition, bool);
 
 	/**
 	 * Wait for events.
 	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &, int);
+	IRCCD_EXPORT std::vector<ListenerStatus> wait(const ListenerTable &, int);
 
 	/**
 	 * Backend identifier
@@ -2962,27 +2964,27 @@
 	/**
 	 * Construct the kqueue instance.
 	 */
-	Kqueue();
+	IRCCD_EXPORT Kqueue();
 
 	/**
 	 * Destroy the kqueue instance.
 	 */
-	~Kqueue();
+	IRCCD_EXPORT ~Kqueue();
 
 	/**
 	 * Set the handle.
 	 */
-	void set(const ListenerTable &, Handle, Condition, bool);
+	IRCCD_EXPORT void set(const ListenerTable &, Handle, Condition, bool);
 
 	/**
 	 * Unset the handle.
 	 */
-	void unset(const ListenerTable &, Handle, Condition, bool);
+	IRCCD_EXPORT void unset(const ListenerTable &, Handle, Condition, bool);
 
 	/**
 	 * Wait for events.
 	 */
-	std::vector<ListenerStatus> wait(const ListenerTable &, int);
+	IRCCD_EXPORT std::vector<ListenerStatus> wait(const ListenerTable &, int);
 
 	/**
 	 * Backend identifier
@@ -3303,789 +3305,6 @@
 	}
 };
 
-/* }}} */
-
-/*
- * Callback
- * ------------------------------------------------------------------
- *
- * Function owner with tests.
- */
-
-/* {{{ Callback */
-
-/**
- * \class Callback
- * \brief Convenient signal owner that checks if the target is valid.
- *
- * This class also catch all errors thrown from signals to avoid interfering with our process.
- */
-template <typename... Args>
-class Callback : public std::function<void (Args...)> {
-public:
-	/**
-	 * Inherited constructors.
-	 */
-	using std::function<void (Args...)>::function;
-
-	/**
-	 * Execute the callback only if a target is set.
-	 */
-	void operator()(Args... args) const
-	{
-		if (*this) {
-			try {
-				std::function<void (Args...)>::operator()(args...);
-			} catch (...) {
-			}
-		}
-	}
-};
-
-/* }}} */
-
-/*
- * StreamConnection
- * ------------------------------------------------------------------
- *
- * Client connected on the server side.
- */
-
-/* {{{ StreamConnection */
-
-/**
- * \class StreamConnection
- * \brief Connected client on the server side.
- *
- * This object is created from StreamServer when a new client is connected, it is the higher
- * level object of sockets and completely asynchronous.
- */
-template <typename Address, typename Protocol>
-class StreamConnection {
-public:
-	/**
-	 * Called when the output has changed.
-	 */
-	using WriteHandler = Callback<>;
-
-private:
-	/* Signals */
-	WriteHandler m_onWrite;
-
-	/* Sockets and output buffer */
-	Socket<Address, Protocol> m_socket;
-	std::string m_output;
-
-public:
-	/**
-	 * Create the connection.
-	 *
-	 * \param s the socket
-	 */
-	StreamConnection(Socket<Address, Protocol> s)
-		: m_socket{std::move(s)}
-	{
-		m_socket.set(net::option::SockBlockMode{false});
-	}
-
-	/**
-	 * Access the underlying socket.
-	 *
-	 * \return the socket
-	 * \warning use with care
-	 */
-	inline Socket<Address, Protocol> &socket() noexcept
-	{
-		return m_socket;
-	}
-
-	/**
-	 * Access the current output.
-	 *
-	 * \return the output
-	 */
-	inline const std::string &output() const noexcept
-	{
-		return m_output;
-	}
-
-	/**
-	 * Overloaded function
-	 *
-	 * \return the output
-	 * \warning use with care, avoid modifying the output if you don't know what you're doing
-	 */
-	inline std::string &output() noexcept
-	{
-		return m_output;
-	}
-
-	/**
-	 * Post some data to be sent asynchronously.
-	 *
-	 * \param str the data to append
-	 */
-	inline void send(std::string str)
-	{
-		m_output += str;
-		m_onWrite();
-	}
-
-	/**
-	 * Kill the client.
-	 */
-	inline void close()
-	{
-		m_socket.close();
-	}
-
-	/**
-	 * Set the write handler, the signal is emitted when the output has changed so that the StreamServer owner
-	 * knows that there are some data to send.
-	 *
-	 * \param handler the handler
-	 * \warning you usually never need to set this yourself
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-};
-
-/* }}} */
-
-/*
- * StreamServer
- * ------------------------------------------------------------------
- *
- * Convenient stream oriented server.
- */
-
-/* {{{ StreamServer */
-
-/**
- * \class StreamServer
- * \brief Convenient stream server for TCP and TLS.
- *
- * This class does all the things for you as accepting new clients, listening for it and sending data. It works
- * asynchronously without blocking to let you control your process workflow.
- *
- * This class is not thread safe and you must not call any of the functions from different threads.
- */
-template <typename Address, typename Protocol>
-class StreamServer {
-public:
-	/**
-	 * Handler when a new client is connected.
-	 */
-	using ConnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
-
-	/**
-	 * Handler when a client is disconnected.
-	 */
-	using DisconnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>;
-
-	/**
-	 * Handler when data has been received from a client.
-	 */
-	using ReadHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
-
-	/**
-	 * Handler when data has been correctly sent to a client.
-	 */
-	using WriteHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>;
-
-	/**
-	 * Handler when an error occured.
-	 */
-	using ErrorHandler = Callback<const Error &>;
-
-	/**
-	 * Handler when there was a timeout.
-	 */
-	using TimeoutHandler = Callback<>;
-
-private:
-	using ClientMap = std::map<Handle, std::shared_ptr<StreamConnection<Address, Protocol>>>;
-
-	/* Signals */
-	ConnectionHandler m_onConnection;
-	DisconnectionHandler m_onDisconnection;
-	ReadHandler m_onRead;
-	WriteHandler m_onWrite;
-	ErrorHandler m_onError;
-	TimeoutHandler m_onTimeout;
-
-	/* Sockets */
-	Socket<Address, Protocol> m_master;
-	Listener<> m_listener;
-	ClientMap m_clients;
-
-	/*
-	 * Update flags depending on the required condition.
-	 */
-	void updateFlags(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		assert(client->socket().action() != Action::None);
-
-		m_listener.remove(client->socket().handle());
-		m_listener.set(client->socket().handle(), client->socket().condition());
-	}
-
-	/*
-	 * Continue accept process.
-	 */
-	template <typename AcceptCall>
-	void processAccept(std::shared_ptr<StreamConnection<Address, Protocol>> &client, const AcceptCall &acceptFunc)
-	{
-		try {
-			/* Do the accept */
-			acceptFunc();
-
-			/* 1. First remove completely the client */
-			m_listener.remove(client->socket().handle());
-
-			/* 2. If accept is not finished, wait for the appropriate condition */
-			if (client->socket().state() == State::Accepted) {
-				/* 3. Client is accepted, notify the user */
-				m_listener.set(client->socket().handle(), Condition::Readable);
-				m_onConnection(client);
-			} else {
-				/* Operation still in progress */
-				updateFlags(client);
-			}
-		} catch (const Error &error) {
-			m_clients.erase(client->socket().handle());
-			m_listener.remove(client->socket().handle());
-			m_onError(error);
-		}
-	}
-
-	/*
-	 * Process initial accept of master socket, this is the initial accepting process. Except on errors, the
-	 * socket is stored but the user will be notified only once the socket is completely accepted.
-	 */
-	void processInitialAccept()
-	{
-		// TODO: store address too.
-		std::shared_ptr<StreamConnection<Address, Protocol>> client = std::make_shared<StreamConnection<Address, Protocol>>(m_master.accept(nullptr));
-		std::weak_ptr<StreamConnection<Address, Protocol>> ptr{client};
-
-		/* 1. Register output changed to update listener */
-		client->setWriteHandler([this, ptr] () {
-			auto client = ptr.lock();
-
-			/* Do not update the listener immediately if an action is pending */
-			if (client && client->socket().action() == Action::None && !client->output().empty()) {
-				m_listener.set(client->socket().handle(), Condition::Writable);
-			}
-		});
-
-		/* 2. Add the client */
-		m_clients.insert(std::make_pair(client->socket().handle(), client));
-
-		/*
-		 * 2. Do an initial check to set the listener flags, at this moment the socket may or not be
-		 *    completely accepted.
-		 */
-		processAccept(client, [&] () {});
-	}
-
-	/*
-	 * Read or complete the read operation.
-	 */
-	void processRead(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		/*
-		 * Read because there is something to read or because the pending operation is
-		 * read and must complete.
-		 */
-		auto buffer = client->socket().recv(512);
-
-		/*
-		 * Now the receive operation may be completed, in that case, two possibilities:
-		 *
-		 * 1. The action is set to None (completed)
-		 * 2. The action is still not complete, update the flags
-		 */
-		if (client->socket().action() == Action::None) {
-			/* Empty mean normal disconnection */
-			if (buffer.empty()) {
-				m_listener.remove(client->socket().handle());
-				m_clients.erase(client->socket().handle());
-				m_onDisconnection(client);
-			} else {
-				/*
-				 * At this step, it is possible that we were completing a receive operation, in this
-				 * case the write flag may be removed, add it if required.
-				 */
-				if (!client->output().empty()) {
-					m_listener.set(client->socket().handle(), Condition::Writable);
-				}
-
-				m_onRead(client, buffer);
-			}
-		} else {
-			/* Operation in progress */
-			updateFlags(client);
-		}
-	}
-
-	/*
-	 * Flush the output buffer.
-	 */
-	void processWrite(std::shared_ptr<StreamConnection<Address, Protocol>> &client)
-	{
-		auto &output = client->output();
-		auto nsent = client->socket().send(output);
-
-		if (client->socket().action() == Action::None) {
-			/* 1. Create a copy of content that has been sent */
-			auto sent = output.substr(0, nsent);
-
-			/* 2. Erase the content sent */
-			output.erase(0, nsent);
-
-			/* 3. Update listener */
-			if (output.empty()) {
-				m_listener.unset(client->socket().handle(), Condition::Writable);
-			}
-
-			/* 4. Notify user */
-			m_onWrite(client, sent);
-		} else {
-			updateFlags(client);
-		}
-	}
-
-	void processSync(std::shared_ptr<StreamConnection<Address, Protocol>> &client, Condition flags)
-	{
-		try {
-			auto action = client->socket().action();
-
-			if (action == Action::Receive ||
-			    (action == Action::None && (flags & Condition::Readable) == Condition::Readable)) {
-				processRead(client);
-			} else if ((flags & Condition::Writable) == Condition::Writable) {
-				processWrite(client);
-			}
-		} catch (const Error &error) {
-			m_onDisconnection(client);
-			m_listener.remove(client->socket().handle());
-			m_clients.erase(client->socket().handle());
-		}
-	}
-
-public:
-	/**
-	 * Create a stream server with the specified address to bind.
-	 *
-	 * \param protocol the protocol (Tcp or Tls)
-	 * \param address the address to bind
-	 * \param max the max number to listen
-	 * \throw Error on errors
-	 */
-	StreamServer(Protocol protocol, const Address &address, int max = 128)
-		: m_master{std::move(protocol), address}
-	{
-		// TODO: m_onError
-		m_master.set(SOL_SOCKET, SO_REUSEADDR, 1);
-		m_master.bind(address);
-		m_master.listen(max);
-		m_listener.set(m_master.handle(), Condition::Readable);
-	}
-
-	/**
-	 * Set the connection handler, called when a new client is connected.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setConnectionHandler(ConnectionHandler handler)
-	{
-		m_onConnection = std::move(handler);
-	}
-
-	/**
-	 * Set the disconnection handler, called when a client died.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setDisconnectionHandler(DisconnectionHandler handler)
-	{
-		m_onDisconnection = std::move(handler);
-	}
-
-	/**
-	 * Set the receive handler, called when a client has sent something.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setReadHandler(ReadHandler handler)
-	{
-		m_onRead = std::move(handler);
-	}
-
-	/**
-	 * Set the writing handler, called when some data has been sent to a client.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-
-	/**
-	 * Set the error handler, called when unrecoverable error has occured.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setErrorHandler(ErrorHandler handler)
-	{
-		m_onError = std::move(handler);
-	}
-
-	/**
-	 * Set the timeout handler, called when the selection has timeout.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setTimeoutHandler(TimeoutHandler handler)
-	{
-		m_onTimeout = std::move(handler);
-	}
-
-	/**
-	 * Poll for the next event.
-	 *
-	 * \param timeout the timeout (-1 for indefinitely)
-	 * \throw Error on errors
-	 */
-	void poll(int timeout = -1)
-	{
-		try {
-			auto st = m_listener.wait(timeout);
-
-			if (st.socket == m_master.handle()) {
-				/* New client */
-				processInitialAccept();
-			} else {
-				/* Recv / Send / Accept on a client */
-				auto client = m_clients[st.socket];
-
-				if (client->socket().state() == State::Accepted) {
-					processSync(client, st.flags);
-				} else {
-					processAccept(client, [&] () { client->socket().accept(); });
-				}
-			}
-		} catch (const Error &error) {
-			if (error.code() == Error::Timeout) {
-				m_onTimeout();
-			} else {
-				m_onError(error);
-			}
-		}
-	}
-};
-
-/* }}} */
-
-/*
- * StreamClient
- * ------------------------------------------------------------------
- */
-
-/* {{{ StreamClient */
-
-/**
- * \class StreamClient
- * \brief Client side connection to a server.
- *
- * This class is not thread safe and you must not call any of the functions from different threads.
- */
-template <typename Address, typename Protocol>
-class StreamClient {
-public:
-	/**
-	 * Handler when connection is complete.
-	 */
-	using ConnectionHandler = Callback<>;
-
-	/**
-	 * Handler when data has been received.
-	 */
-	using ReadHandler = Callback<const std::string &>;
-
-	/**
-	 * Handler when data has been sent correctly.
-	 */
-	using WriteHandler = Callback<const std::string &>;
-
-	/**
-	 * Handler when disconnected.
-	 */
-	using DisconnectionHandler = Callback<>;
-
-	/**
-	 * Handler on unrecoverable error.
-	 */
-	using ErrorHandler = Callback<const Error &>;
-
-	/**
-	 * Handler when timeout occured.
-	 */
-	using TimeoutHandler = Callback<>;
-
-private:
-	/* Signals */
-	ConnectionHandler m_onConnection;
-	ReadHandler m_onRead;
-	WriteHandler m_onWrite;
-	DisconnectionHandler m_onDisconnection;
-	ErrorHandler m_onError;
-	TimeoutHandler m_onTimeout;
-
-	/* Socket */
-	Socket<Address, Protocol> m_socket;
-	Listener<> m_listener;
-
-	/* Output buffer */
-	std::string m_output;
-
-	/*
-	 * Update the flags after an uncompleted operation. This function must only be called when the operation
-	 * has not complete (e.g. connect, recv, send).
-	 */
-	void updateFlags()
-	{
-		assert(m_socket.action() != Action::None);
-
-		m_listener.remove(m_socket.handle());
-		m_listener.set(m_socket.handle(), m_socket.condition());
-	}
-
-	/*
-	 * This is the generic connect helper, it will be used to both initiate the connection or to continue the
-	 * connection process if needed.
-	 *
-	 * Thus the template parameter is the appropriate function to call either, m_socket.connect(address) or
-	 * m_socket.connect().
-	 *
-	 * See poll() and connect() to understand.
-	 */
-	template <typename ConnectCall>
-	void processConnect(const ConnectCall &connectFunc)
-	{
-		/* Call m_socket.connect() or m_socket.connect(address) */
-		connectFunc();
-
-		/* Remove entirely */
-		m_listener.remove(m_socket.handle());
-
-		if (m_socket.state() == State::Connected) {
-			m_onConnection();
-			m_listener.set(m_socket.handle(), Condition::Readable);
-		} else {
-			/* Connection still in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Receive or complete the receive command, if the command is not complete, the listener is updated
-	 * accordingly.
-	 */
-	void processRead()
-	{
-		auto received = m_socket.recv(512);
-
-		if (m_socket.action() == Action::None) {
-			/* 0 means disconnection */
-			if (received.empty()) {
-				m_onDisconnection();
-			} else {
-				/*
-				 * At this step, it is possible that we were completing a receive operation, in this
-				 * case the write flag may be removed, add it if required.
-				 */
-				if (m_output.empty()) {
-					m_listener.unset(m_socket.handle(), Condition::Writable);
-				}
-
-				m_onRead(received);
-			}
-		} else {
-			/* Receive operation in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Send or complete the send command, if the command is not complete, the listener is updated
-	 * accordingly.
-	 */
-	void processWrite()
-	{
-		auto nsent = m_socket.send(m_output);
-
-		if (m_socket.action() == Action::None) {
-			/* 1. Make a copy of what has been sent */
-			auto sent = m_output.substr(0, nsent);
-
-			/* 2. Erase sent content */
-			m_output.erase(0, nsent);
-
-			/* 3. Update flags if needed */
-			if (m_output.empty()) {
-				m_listener.unset(m_socket.handle(), Condition::Writable);
-			}
-
-			/* 4. Notify user */
-			m_onWrite(sent);
-		} else {
-			/* Send operation in progress */
-			updateFlags();
-		}
-	}
-
-	/*
-	 * Receive or send.
-	 */
-	void processSync(Condition condition)
-	{
-		if ((m_socket.action() == Action::Receive) ||
-		    (m_socket.action() == Action::None && (condition & Condition::Readable) == Condition::Readable)) {
-			processRead();
-		} else {
-			processWrite();
-		}
-	}
-
-public:
-	/**
-	 * Create a client. The client is automatically marked as non-blocking.
-	 *
-	 * \param protocol the protocol (Tcp or Tls)
-	 * \param address the optional address
-	 * \throw net::Error on failures
-	 */
-	StreamClient(Protocol protocol = {}, const Address &address = {})
-		: m_socket{std::move(protocol), address}
-	{
-		m_socket.set(net::option::SockBlockMode{false});
-		m_listener.set(m_socket.handle(), Condition::Readable);
-	}
-
-	/**
-	 * Set the connection handler, called when the connection succeed.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setConnectionHandler(ConnectionHandler handler)
-	{
-		m_onConnection = std::move(handler);
-	}
-
-	/**
-	 * Set the disconnection handler, called when the server closed the connection.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setDisconnectionHandler(DisconnectionHandler handler)
-	{
-		m_onDisconnection = std::move(handler);
-	}
-
-	/**
-	 * Set the read handler, called when we received something.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setReadHandler(ReadHandler handler)
-	{
-		m_onRead = std::move(handler);
-	}
-
-	/**
-	 * Set the write handler, called when we successfully sent data.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setWriteHandler(WriteHandler handler)
-	{
-		m_onWrite = std::move(handler);
-	}
-
-	/**
-	 * Set the error handler, called when unexpected error occurs.
-	 *
-	 * \param handler the handler
-	 */
-	inline void setErrorHandler(ErrorHandler handler)
-	{
-		m_onError = std::move(handler);
-	}
-
-	/**
-	 * Connect to a server, this function may connect immediately or not in any case the connection handler
-	 * will be called when the connection completed.
-	 *
-	 * \param address the address to connect to
-	 */
-	void connect(const Address &address) noexcept
-	{
-		assert(m_socket.state() == State::Open);
-
-		processConnect([&] () { m_socket.connect(address); });
-	}
-
-	/**
-	 * Asynchronously send data to the server.
-	 *
-	 * \param str the data to append
-	 */
-	void send(std::string str)
-	{
-		m_output += str;
-
-		/* Don't update the listener if there is a pending operation */
-		if (m_socket.state() == State::Connected && m_socket.action() == Action::None && !m_output.empty()) {
-			m_listener.set(m_socket.handle(), Condition::Writable);
-		}
-	}
-
-	/**
-	 * Wait for the next event.
-	 *
-	 * \param timeout the time to wait in milliseconds
-	 * \throw Error on errors
-	 */
-	void poll(int timeout = -1) noexcept
-	{
-		try {
-			auto st = m_listener.wait(timeout);
-
-			if (m_socket.state() != State::Connected) {
-				/* Continue the connection */
-				processConnect([&] () { m_socket.connect(); });
-			} else {
-				/* Read / Write */
-				processSync(st.flags);
-			}
-		} catch (const Error &error) {
-			if (error.code() == Error::Timeout) {
-				m_onTimeout();
-			} else {
-				m_listener.remove(m_socket.handle());
-				m_onError(error);
-			}
-		}
-	}
-};
-
-/* }}} */
-
 } // !net
 
 } // !irccd
--- a/lib/irccd/system.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/system.cpp	Tue May 24 13:00:35 2016 +0200
@@ -52,14 +52,14 @@
 
 #endif
 
-/* For sys::setGid */
+// For sys::setGid.
 #if defined(HAVE_SETGID)
 #  include <sys/types.h>
 #  include <unistd.h>
 #  include <grp.h>
 #endif
 
-/* For sys::setUid */
+// For sys::setUid.
 #if defined(HAVE_SETGID)
 #  include <sys/types.h>
 #  include <unistd.h>
@@ -100,9 +100,9 @@
 {
 	IntType id;
 
-	if (util::isNumber(value)) {
+	if (util::isNumber(value))
 		id = std::stoi(value);
-	} else {
+	else {
 		auto info = lookup(value.c_str());
 
 		if (info == nullptr) {
@@ -110,16 +110,14 @@
 			return;
 		} else {
 			id = getter(info);
-
 			log::debug() << "irccd: " << typeName << " " << value << " resolved to: " << id << std::endl;
 		}
 	}
 
-	if (setter(id) < 0) {
+	if (setter(id) < 0)
 		log::warning() << "irccd: could not set " << typeName << ": " << std::strerror(errno) << std::endl;
-	} else {
+	else
 		log::info() << "irccd: setting " << typeName << " to " << value << std::endl;
-	}
 }
 
 /*
@@ -175,9 +173,8 @@
 #else
 	struct utsname uts;
 
-	if (uname(&uts) < 0) {
+	if (uname(&uts) < 0)
 		throw std::runtime_error(std::strerror(errno));
-	}
 
 	return std::string(uts.release);
 #endif
@@ -190,9 +187,8 @@
 #elif defined(IRCCD_SYSTEM_LINUX)
 	struct sysinfo info;
 
-	if (sysinfo(&info) < 0) {
+	if (sysinfo(&info) < 0)
 		throw std::runtime_error(std::strerror(errno));
-	}
 
 	return info.uptime;
 #elif defined(IRCCD_SYSTEM_MAC)
@@ -200,20 +196,18 @@
 	size_t length = sizeof (boottime);
 	int mib[2] = { CTL_KERN, KERN_BOOTTIME };
 
-	if (sysctl(mib, 2, &boottime, &length, nullptr, 0) < 0) {
+	if (sysctl(mib, 2, &boottime, &length, nullptr, 0) < 0)
 		throw std::runtime_error(std::strerror(errno));
-	}
 
 	time_t bsec = boottime.tv_sec, csec = time(nullptr);
 
 	return difftime(csec, bsec);
 #else
-	/* BSD */
+	// BSD.
 	struct timespec ts;
 
-	if (clock_gettime(CLOCK_UPTIME, &ts) < 0) {
+	if (clock_gettime(CLOCK_UPTIME, &ts) < 0)
 		throw std::runtime_error(std::strerror(errno));
-	}
 
 	return ts.tv_sec;
 #endif
@@ -241,9 +235,8 @@
 #if defined(IRCCD_SYSTEM_WINDOWS)
 	char path[MAX_PATH];
 
-	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK) {
+	if (SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path) != S_OK)
 		return "";
-	}
 
 	return std::string(path);
 #else
@@ -255,9 +248,8 @@
 {
 	auto value = std::getenv(var.c_str());
 
-	if (value == nullptr) {
+	if (value == nullptr)
 		return "";
-	}
 
 	return value;
 }
--- a/lib/irccd/system.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/system.hpp	Tue May 24 13:00:35 2016 +0200
@@ -38,35 +38,35 @@
  *
  * \param name the program name
  */
-void setProgramName(std::string name) noexcept;
+IRCCD_EXPORT void setProgramName(std::string name) noexcept;
 
 /**
  * Get the program name.
  *
  * \return the program name
  */
-const std::string &programName() noexcept;
+IRCCD_EXPORT const std::string &programName() noexcept;
 
 /**
  * Get the system name.
  *
  * \return the name
  */
-std::string name();
+IRCCD_EXPORT std::string name();
 
 /**
  * Get the system version.
  *
  * \return the version
  */
-std::string version();
+IRCCD_EXPORT std::string version();
 
 /**
  * Get the number of seconds elapsed since the boottime.
  *
  * \return the number of seconds
  */
-uint64_t uptime();
+IRCCD_EXPORT uint64_t uptime();
 
 /**
  * Get the milliseconds elapsed since the application
@@ -74,21 +74,21 @@
  *
  * \return the milliseconds
  */
-uint64_t ticks();
+IRCCD_EXPORT uint64_t ticks();
 
 /**
  * Get an environment variable.
  *
  * \return the value or empty string
  */
-std::string env(const std::string &var);
+IRCCD_EXPORT std::string env(const std::string &var);
 
 /**
  * Get home directory usually /home/foo
  *
  * \return the home directory
  */
-std::string home();
+IRCCD_EXPORT std::string home();
 
 #if defined(HAVE_SETUID)
 
@@ -97,7 +97,7 @@
  *
  * \param value the value
  */
-void setUid(const std::string &value);
+IRCCD_EXPORT void setUid(const std::string &value);
 
 #endif
 
@@ -108,7 +108,7 @@
  *
  * \param value the value
  */
-void setGid(const std::string &value);
+IRCCD_EXPORT void setGid(const std::string &value);
 
 #endif
 
--- a/lib/irccd/timer.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/timer.hpp	Tue May 24 13:00:35 2016 +0200
@@ -31,6 +31,7 @@
 #include <thread>
 
 #include "signals.hpp"
+#include "sysconfig.hpp"
 
 namespace irccd {
 
@@ -102,14 +103,14 @@
 	 * \param delay the delay in milliseconds
 	 * \post isRunning() returns false
 	 */
-	Timer(TimerType type, unsigned delay) noexcept;
+	IRCCD_EXPORT Timer(TimerType type, unsigned delay) noexcept;
 
 	/**
 	 * Destructor, closes the thread.
 	 *
 	 * \pre stop() must have been called.
 	 */
-	virtual ~Timer();
+	IRCCD_EXPORT virtual ~Timer();
 
 	/**
 	 * Start the thread.
@@ -119,14 +120,14 @@
 	 * \pre onEnd() must have been called
 	 * \note Thread-safe
 	 */
-	void start();
+	IRCCD_EXPORT void start();
 
 	/**
 	 * Stop the timer, may be used by the user to stop it.
 	 *
 	 * \note Thread-safe
 	 */
-	void stop();
+	IRCCD_EXPORT void stop();
 
 	/**
 	 * Get the type of timer.
--- a/lib/irccd/transport-client.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/transport-client.cpp	Tue May 24 13:00:35 2016 +0200
@@ -26,9 +26,8 @@
 {
 	json::Value document(json::Buffer{message});
 
-	if (!document.isObject()) {
+	if (!document.isObject())
 		throw std::invalid_argument("the message is not a valid JSON object");
-	}
 
 	onCommand(document);
 }
--- a/lib/irccd/transport-client.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/transport-client.hpp	Tue May 24 13:00:35 2016 +0200
@@ -32,6 +32,7 @@
 #include "server.hpp"
 #include "signals.hpp"
 #include "sockets.hpp"
+#include "sysconfig.hpp"
 
 namespace irccd {
 
@@ -89,7 +90,7 @@
 	 * \param setinput the input fd_set
 	 * \param setoutput the output fd_set
 	 */
-	void sync(fd_set &setinput, fd_set &setoutput);
+	IRCCD_EXPORT void sync(fd_set &setinput, fd_set &setoutput);
 
 	/**
 	 * Send some data, it will be pushed to the outgoing buffer.
@@ -99,7 +100,7 @@
 	 *
 	 * \param message the message
 	 */
-	void send(std::string message);
+	IRCCD_EXPORT void send(std::string message);
 
 	/**
 	 * Tell if the client has data pending for output.
@@ -157,9 +158,8 @@
 	try {
 		auto message = m_socket.recv(512);
 
-		if (message.empty()) {
+		if (message.empty())
 			onDie();
-		}
 
 		m_input += message;
 	} catch (const std::exception &) {
--- a/lib/irccd/transport-server.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/transport-server.cpp	Tue May 24 13:00:35 2016 +0200
@@ -39,10 +39,9 @@
 {
 	m_socket.set(net::option::SockReuseAddress{true});
 
-	/* Disable or enable IPv4 when using IPv6 */
-	if (domain == AF_INET6) {
+	// Disable or enable IPv4 when using IPv6.
+	if (domain == AF_INET6)
 		m_socket.set(net::option::Ipv6Only{ipv6only});
-	}
 
 	m_socket.bind(net::address::Ip{address, port, static_cast<net::address::Ip::Type>(domain)});
 	m_socket.listen();
--- a/lib/irccd/transport-server.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/transport-server.hpp	Tue May 24 13:00:35 2016 +0200
@@ -100,17 +100,17 @@
 	 * \param ipv6only set to true to disable IPv4
 	 * \throw net::Error on failures
 	 */
-	TransportServerIp(int domain, const std::string &address, int port, bool ipv6only = true);
+	IRCCD_EXPORT TransportServerIp(int domain, const std::string &address, int port, bool ipv6only = true);
 
 	/**
 	 * \copydoc TransportServer::socket
 	 */
-	net::Handle handle() noexcept override;
+	IRCCD_EXPORT net::Handle handle() noexcept override;
 
 	/**
 	 * \copydoc TransportServer::accept
 	 */
-	std::shared_ptr<TransportClient> accept() override;
+	IRCCD_EXPORT std::shared_ptr<TransportClient> accept() override;
 };
 
 #if !defined(IRCCD_SYSTEM_WINDOWS)
@@ -130,22 +130,22 @@
 	 *
 	 * \param path the path
 	 */
-	TransportServerUnix(std::string path);
+	IRCCD_EXPORT TransportServerUnix(std::string path);
 
 	/**
 	 * Destroy the transport and remove the file.
 	 */
-	~TransportServerUnix();
+	IRCCD_EXPORT ~TransportServerUnix();
 
 	/**
 	 * \copydoc TransportServer::socket
 	 */
-	net::Handle handle() noexcept override;
+	IRCCD_EXPORT net::Handle handle() noexcept override;
 
 	/**
 	 * \copydoc TransportServer::accept
 	 */
-	std::shared_ptr<TransportClient> accept() override;
+	IRCCD_EXPORT std::shared_ptr<TransportClient> accept() override;
 };
 
 #endif // !_WIN32
--- a/lib/irccd/unicode.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/unicode.hpp	Tue May 24 13:00:35 2016 +0200
@@ -27,12 +27,14 @@
 #include <stdexcept>
 #include <string>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 namespace unicode {
 
-void encode(char32_t point, char res[5]) noexcept;
-void decode(char32_t &c, const char *res) noexcept;
+IRCCD_EXPORT void encode(char32_t point, char res[5]) noexcept;
+IRCCD_EXPORT void decode(char32_t &c, const char *res) noexcept;
 
 /**
  * Get the number of bytes for the first multi byte character from a
@@ -44,7 +46,7 @@
  * \param c the first multi byte character
  * \return the number of bytes [1-4]
  */
-int nbytesUtf8(char c) noexcept;
+IRCCD_EXPORT int nbytesUtf8(char c) noexcept;
 
 /**
  * Get the number of bytes for the unicode point.
@@ -52,7 +54,7 @@
  * \param point the unicode point
  * \return the number of bytes [1-4] or -1 on invalid
  */
-int nbytesPoint(char32_t point) noexcept;
+IRCCD_EXPORT int nbytesPoint(char32_t point) noexcept;
 
 /**
  * Get real number of character in a string.
@@ -61,7 +63,7 @@
  * \return the length
  * \throw std::invalid_argument on invalid sequence
  */
-int length(const std::string &str);
+IRCCD_EXPORT int length(const std::string &str);
 
 /**
  * Iterate over all real characters in the UTF-8 string.
@@ -97,7 +99,7 @@
  * \return the UTF-8 string
  * \throw std::invalid_argument on invalid sequence
  */
-std::string toUtf8(const std::u32string &array);
+IRCCD_EXPORT std::string toUtf8(const std::u32string &array);
 
 /**
  * Convert a UTF-8 string to UTF-32 string.
@@ -106,7 +108,7 @@
  * \return the UTF-32 string
  * \throw std::invalid_argument on invalid sequence
  */
-std::u32string toUtf32(const std::string &str);
+IRCCD_EXPORT std::u32string toUtf32(const std::string &str);
 
 /**
  * Check if the unicode character is space.
@@ -114,7 +116,7 @@
  * \param c the character
  * \return true if space
  */
-bool isspace(char32_t c) noexcept;
+IRCCD_EXPORT bool isspace(char32_t c) noexcept;
 
 /**
  * Check if the unicode character is digit.
@@ -122,7 +124,7 @@
  * \param c the character
  * \return true if digit
  */
-bool isdigit(char32_t c) noexcept;
+IRCCD_EXPORT bool isdigit(char32_t c) noexcept;
 
 /**
  * Check if the unicode character is alpha category.
@@ -130,7 +132,7 @@
  * \param c the character
  * \return true if alpha
  */
-bool isalpha(char32_t c) noexcept;
+IRCCD_EXPORT bool isalpha(char32_t c) noexcept;
 
 /**
  * Check if the unicode character is upper case.
@@ -138,7 +140,7 @@
  * \param c the character
  * \return true if upper case
  */
-bool isupper(char32_t c) noexcept;
+IRCCD_EXPORT bool isupper(char32_t c) noexcept;
 
 /**
  * Check if the unicode character is lower case.
@@ -146,7 +148,7 @@
  * \param c the character
  * \return true if lower case
  */
-bool islower(char32_t c) noexcept;
+IRCCD_EXPORT bool islower(char32_t c) noexcept;
 
 /**
  * Check if the unicode character is title case.
@@ -154,7 +156,7 @@
  * \param c the character
  * \return true if title case
  */
-bool istitle(char32_t c) noexcept;
+IRCCD_EXPORT bool istitle(char32_t c) noexcept;
 
 /**
  * Convert to upper case.
@@ -162,7 +164,7 @@
  * \param c the character
  * \return the upper case character
  */
-char32_t toupper(char32_t c) noexcept;
+IRCCD_EXPORT char32_t toupper(char32_t c) noexcept;
 
 /**
  * Convert to lower case.
@@ -170,7 +172,7 @@
  * \param c the character
  * \return the lower case character
  */
-char32_t tolower(char32_t c) noexcept;
+IRCCD_EXPORT char32_t tolower(char32_t c) noexcept;
 
 /**
  * Convert to title case.
@@ -178,7 +180,7 @@
  * \param c the character
  * \return the title case character
  */
-char32_t totitle(char32_t c) noexcept;
+IRCCD_EXPORT char32_t totitle(char32_t c) noexcept;
 
 /**
  * Convert the UTF-32 string to upper case.
--- a/lib/irccd/util.cpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/util.cpp	Tue May 24 13:00:35 2016 +0200
@@ -95,9 +95,8 @@
 {
 	auto value = params.keywords.find(content);
 
-	if (value != params.keywords.end()) {
+	if (value != params.keywords.end())
 		return value->second;
-	}
 
 	return "";
 }
@@ -106,9 +105,8 @@
 {
 	auto value = std::getenv(content.c_str());
 
-	if (value != nullptr) {
+	if (value != nullptr)
 		return value;
-	}
 
 	return "";
 }
@@ -118,12 +116,11 @@
 	std::stringstream oss;
 	std::vector<std::string> list = split(content, ",");
 
-	/* @{} means reset */
-	if (list.empty()) {
+	// @{} means reset.
+	if (list.empty())
 		return std::string(1, attributesTable.at("reset"));
-	}
 
-	/* Remove useless spaces */
+	// Remove useless spaces.
 	std::transform(list.begin(), list.end(), list.begin(), strip);
 
 	/*
@@ -133,27 +130,24 @@
 	 */
 	auto foreground = list[0];
 	if (!foreground.empty() || list.size() >= 2) {
-		/* Color sequence */
+		// Color sequence.
 		oss << '\x03';
 
-		/* Foreground */
+		// Foreground.
 		auto it = colorTable.find(foreground);
-		if (it != colorTable.end()) {
+		if (it != colorTable.end())
 			oss << it->second;
-		}
 
-		/* Background */
-		if (list.size() >= 2 && (it = colorTable.find(list[1])) != colorTable.end()) {
+		// Background.
+		if (list.size() >= 2 && (it = colorTable.find(list[1])) != colorTable.end())
 			oss << "," << it->second;
-		}
 
-		/* Attributes */
+		// Attributes.
 		for (std::size_t i = 2; i < list.size(); ++i) {
 			auto attribute = attributesTable.find(list[i]);
 
-			if (attribute != attributesTable.end()) {
+			if (attribute != attributesTable.end())
 				oss << attribute->second;
-			}
 		}
 	}
 
@@ -166,38 +160,32 @@
 
 	std::string content, value;
 
-	if (it == end) {
+	if (it == end)
 		return "";
-	}
 
-	while (it != end && *it != '}') {
+	while (it != end && *it != '}')
 		content += *it++;
-	}
 
-	if (it == end || *it != '}') {
+	if (it == end || *it != '}')
 		throw std::invalid_argument("unclosed "s + token + " construct"s);
-	}
 
 	it++;
 
-	/* Create default original value if flag is disabled */
+	// Create default original value if flag is disabled.
 	value = std::string(1, token) + "{"s + content + "}"s;
 
 	switch (token) {
 	case '#':
-		if (params.flags & Substitution::Keywords) {
+		if (params.flags & Substitution::Keywords)
 			value = substituteKeywords(content, params);
-		}
 		break;
 	case '$':
-		if (params.flags & Substitution::Env) {
+		if (params.flags & Substitution::Env)
 			value = substituteEnv(content);
-		}
 		break;
 	case '@':
-		if (params.flags & Substitution::IrcAttrs) {
+		if (params.flags & Substitution::IrcAttrs)
 			substituteAttributes(content);
-		}
 		break;
 	default:
 		break;
@@ -214,9 +202,8 @@
 	 * Change the date format before anything else to avoid interpolation with keywords and
 	 * user input.
 	 */
-	if (params.flags & Substitution::Date) {
+	if (params.flags & Substitution::Date)
 		text = substituteDate(text, params);
-	}
 
 	std::ostringstream oss;
 
@@ -260,11 +247,10 @@
 		 * "##hello" -> "##hello"
 		 * "##{hello}" -> "#{hello}"
 		 */
-		if (++it == end) {
+		if (++it == end)
 			oss << token << token;
-		} else if (*it == '{') {
+		else if (*it == '{')
 			oss << token;
-		}
 	}
 
 	return oss.str();
@@ -287,9 +273,8 @@
 	int count = 1;
 	bool finished = false;
 
-	if (list.empty()) {
+	if (list.empty())
 		return result;
-	}
 
 	do {
 		std::string val;
@@ -297,7 +282,7 @@
 		current = next + 1;
 		next = list.find_first_of(delimiters, current);
 
-		// split max, get until the end
+		// split max, get until the end.
 		if (max >= 0 && count++ >= max) {
 			val = list.substr(current, std::string::npos);
 			finished = true;
@@ -328,22 +313,20 @@
 		 * is a space, we check until we find a space, if not
 		 * typing "!foo123123" will trigger foo plugin.
 		 */
-		if (pos == std::string::npos) {
+		if (pos == std::string::npos)
 			iscommand = result == fullcommand;
-		} else {
+		else
 			iscommand = result.length() >= fullcommand.length() && result.compare(0, pos, fullcommand) == 0;
-		}
 
 		if (iscommand) {
 			/*
 			 * If no space is found we just set the message to "" otherwise
 			 * the plugin name will be passed through onCommand
 			 */
-			if (pos == std::string::npos) {
+			if (pos == std::string::npos)
 				result = "";
-			} else {
+			else
 				result = message.substr(pos + 1);
-			}
 		}
 	}
 
@@ -357,9 +340,8 @@
 
 bool isInt(const std::string &str, int base) noexcept
 {
-	if (str.empty()) {
+	if (str.empty())
 		return false;
-	}
 	
 	char *ptr;
 	
@@ -370,9 +352,8 @@
 
 bool isReal(const std::string &str) noexcept
 {
-	if (str.empty()) {
+	if (str.empty())
 		return false;
-	}
 	
 	char *ptr;
 	
--- a/lib/irccd/util.hpp	Mon May 23 14:05:41 2016 +0200
+++ b/lib/irccd/util.hpp	Tue May 24 13:00:35 2016 +0200
@@ -35,6 +35,8 @@
 #include <unordered_map>
 #include <vector>
 
+#include "sysconfig.hpp"
+
 namespace irccd {
 
 namespace util {
@@ -132,7 +134,7 @@
  *   - <strong>\@{default,yellow}</strong>: will write default color text on yellow background,
  *   - <strong>\@{white,black,bold,underline}</strong>: will write white text on black in both bold and underline.
  */
-std::string format(std::string text, const Substitution &params = {});
+IRCCD_EXPORT std::string format(std::string text, const Substitution &params = {});
 
 /**
  * Remove leading and trailing spaces.
@@ -140,7 +142,7 @@
  * \param str the string
  * \return the removed white spaces
  */
-std::string strip(std::string str);
+IRCCD_EXPORT std::string strip(std::string str);
 
 /**
  * Split a string by delimiters.
@@ -150,7 +152,7 @@
  * \param max max number of split
  * \return a list of string splitted
  */
-std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max = -1);
+IRCCD_EXPORT std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max = -1);
 
 /**
  * Join values by a separator and return a string.
@@ -167,9 +169,8 @@
 	if (first != last) {
 		oss << *first;
 
-		while (++first != last) {
+		while (++first != last)
 			oss << delim << *first;
-		}
 	}
 
 	return oss.str();
@@ -196,7 +197,7 @@
  * \param plugin the plugin name
  * \return the pair
  */
-MessagePair parseMessage(std::string message, const std::string &commandChar, const std::string &plugin);
+IRCCD_EXPORT MessagePair parseMessage(std::string message, const std::string &commandChar, const std::string &plugin);
 
 /**
  * Server and identities must have strict names. This function can
@@ -217,7 +218,7 @@
  * \return true if is boolean
  * \note this function is case-insensitive
  */
-bool isBoolean(std::string value) noexcept;
+IRCCD_EXPORT bool isBoolean(std::string value) noexcept;
 
 /**
  * Check if the string is an integer.
@@ -226,7 +227,7 @@
  * \param base the optional base
  * \return true if integer
  */
-bool isInt(const std::string &value, int base = 10) noexcept;
+IRCCD_EXPORT bool isInt(const std::string &value, int base = 10) noexcept;
 
 /**
  * Check if the string is real.
@@ -234,7 +235,7 @@
  * \param value the value
  * \return true if real
  */
-bool isReal(const std::string &value) noexcept;
+IRCCD_EXPORT bool isReal(const std::string &value) noexcept;
 
 /**
  * Check if the string is a number.
@@ -271,15 +272,13 @@
 
 	std::conditional_t<std::is_unsigned<T>::value, unsigned long long, long long> value;
 
-	if (std::is_unsigned<T>::value) {
+	if (std::is_unsigned<T>::value)
 		value = std::stoull(number);
-	} else {
+	else
 		value = std::stoll(number);
-	}
 
-	if (value < min || value > max) {
+	if (value < min || value > max)
 		throw std::out_of_range("out of range");
-	}
 
 	return static_cast<T>(value);
 }
@@ -290,7 +289,7 @@
  * \param input the buffer, will be updated
  * \return the message or empty string if there is nothing
  */
-std::string nextNetwork(std::string &input);
+IRCCD_EXPORT std::string nextNetwork(std::string &input);
 
 /**
  * Use arguments to avoid compiler warnings about unused parameters.