← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3037: plugins: add a hook to ease chat message tagging; merge 2 formatting hooks

 

------------------------------------------------------------
revno: 3037
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Sat 2012-09-08 15:54:40 +0200
message:
  plugins: add a hook to ease chat message tagging; merge 2 formatting hooks
added:
  dcpp/Tagger.cpp
  dcpp/Tagger.h
modified:
  dcpp/ChatMessage.cpp
  dcpp/PluginApiImpl.cpp
  dcpp/PluginApiImpl.h
  dcpp/PluginDefs.h
  dcpp/PluginEntity.h
  dcpp/PluginManager.cpp
  dcpp/PluginManager.h
  dcpp/stdinc.h


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'dcpp/ChatMessage.cpp'
--- dcpp/ChatMessage.cpp	2012-08-02 17:11:14 +0000
+++ dcpp/ChatMessage.cpp	2012-09-08 13:54:40 +0000
@@ -23,17 +23,14 @@
 #include "format.h"
 #include "Magnet.h"
 #include "OnlineUser.h"
+#include "PluginManager.h"
 #include "SettingsManager.h"
 #include "SimpleXML.h"
+#include "Tagger.h"
 #include "Util.h"
-#include "PluginManager.h"
-
-#include <boost/range/adaptor/reversed.hpp>
 
 namespace dcpp {
 
-using std::list;
-
 ChatMessage::ChatMessage(const string& text, OnlineUser* from,
 	const OnlineUser* to, const OnlineUser* replyTo,
 	bool thirdPerson, time_t messageTimestamp) :
@@ -98,28 +95,16 @@
 
 	message += tmp;
 
-	/* format the message; this will involve adding custom tags. this table holds the tags to be
-	added along with their position. */
+	/* format the message; this will involve adding custom tags. use the Tagger class to that end. */
 
-	struct Tag { size_t pos; string s; bool opening; Tag* otherTag; };
-	list<Tag> tags;
+	Tagger tags;
 
 	/* link formatting - optimize the lookup a bit by using the fact that every link identifier
 	(except www ones) contains a colon. */
 	/// @todo add support for spaces within links enclosed by brackets / quotes (see URI RFC)
 
 	auto addLinkStr = [&xmlTmp, &tags](size_t begin, size_t end, const string& link) {
-		Tag openingTag = { begin, "<a href=\"" + SimpleXML::escape(link, xmlTmp, true) + "\">", true },
-			closingTag = { end, "</a>", false };
-
-		tags.push_back(std::move(openingTag));
-		auto& opening = tags.back();
-
-		tags.push_back(std::move(closingTag));
-		auto& closing = tags.back();
-
-		opening.otherTag = &closing;
-		closing.otherTag = &opening;
+		tags.add(begin, end, "a", "href=\"" + SimpleXML::escape(link, xmlTmp, true) + "\"");
 	};
 
 	auto addLink = [&tmp, &addLinkStr](size_t begin, size_t end) {
@@ -184,50 +169,12 @@
 		i += 5;
 	}
 
-	htmlMessage += "<span id=\"text\">";
-
-	/* write tags and the message data. support entangled tags, such as:
-	<a> <b> </a> </b> -> <a> <b> </b></a><b> </b> */
-
-	tags.sort([](const Tag& a, const Tag& b) { return a.pos < b.pos; });
-
-	size_t pos = 0;
-	vector<Tag*> openTags;
-
-	for(auto& tag: tags) {
-		htmlMessage += SimpleXML::escape(tmp.substr(pos, tag.pos - pos), xmlTmp, false);
-		pos = tag.pos;
-
-		if(tag.opening) {
-			htmlMessage += tag.s;
-			openTags.push_back(&tag);
-
-		} else {
-			if(openTags.back() == tag.otherTag) {
-				// common case: no entangled tag; just write the closing tag.
-				htmlMessage += tag.s;
-				openTags.pop_back();
-
-			} else {
-				// there are entangled tags: write closing & opening tags of currently open tags.
-				for(auto openTag: openTags | boost::adaptors::reversed) { if(openTag->pos >= tag.otherTag->pos) {
-					htmlMessage += openTag->otherTag->s;
-				} }
-				openTags.erase(remove(openTags.begin(), openTags.end(), tag.otherTag), openTags.end());
-				for(auto openTag: openTags) { if(openTag->pos >= tag.otherTag->pos) {
-					htmlMessage += openTag->s;
-				} }
-			}
-		}
-	}
-
-	if(pos != n) {
-		htmlMessage += SimpleXML::escape(tmp.substr(pos), xmlTmp, false);
-	}
-
-	htmlMessage += "</span></span>";
-
-	/// forward to plugins
+	// let plugins play with the tag list
+	PluginManager::getInstance()->onChatTags(tmp, tags, from);
+
+	htmlMessage += "<span id=\"text\">" + tags.merge(tmp, xmlTmp) + "</span></span>";
+
+	// forward to plugins
 	PluginManager::getInstance()->onChatDisplay(htmlMessage, from);
 }
 

=== modified file 'dcpp/PluginApiImpl.cpp'
--- dcpp/PluginApiImpl.cpp	2012-08-02 17:49:34 +0000
+++ dcpp/PluginApiImpl.cpp	2012-09-08 13:54:40 +0000
@@ -36,6 +36,7 @@
 #include "LogManager.h"
 #include "PluginManager.h"
 #include "QueueManager.h"
+#include "Tagger.h"
 #include "UserConnection.h"
 #include "version.h"
 
@@ -66,8 +67,8 @@
 	HOOK_QUEUE_FINISHED,
 
 	HOOK_UI_CREATED,
+	HOOK_UI_CHAT_TAGS,
 	HOOK_UI_CHAT_DISPLAY,
-	HOOK_UI_LOCAL_CHAT_DISPLAY,
 	HOOK_UI_PROCESS_CHAT_CMD		
 };
 
@@ -160,6 +161,12 @@
 	&PluginApiImpl::fromBase32
 };
 
+DCTagger PluginApiImpl::dcTagger = {
+	DCINTF_DCPP_TAGGER_VER,
+
+	&PluginApiImpl::addTag
+};
+
 Socket* PluginApiImpl::udpSocket = nullptr;
 Socket& PluginApiImpl::getUdpSocket() {
 	if(!udpSocket) {
@@ -189,6 +196,7 @@
 	dcCore.register_interface(DCINTF_DCPP_HUBS, &dcHub);
 	dcCore.register_interface(DCINTF_DCPP_QUEUE, &dcQueue);
 	dcCore.register_interface(DCINTF_DCPP_UTILS, &dcUtils);
+	dcCore.register_interface(DCINTF_DCPP_TAGGER, &dcTagger);
 
 	// Create provided hooks (since these outlast any plugin they don't need to be explictly released)
 	for(int i = 0; i < IMPL_HOOKS_COUNT; ++i)
@@ -471,6 +479,11 @@
 	return n;
 }
 
+// Functions for DCTagger
+void PluginApiImpl::addTag(TagDataPtr hTags, size_t start, size_t end, const char* id, const char* attributes) {
+	reinterpret_cast<Tagger*>(hTags->object)->add(start, end, id, attributes);
+}
+
 // Functions for DCQueue
 QueueDataPtr PluginApiImpl::addList(UserDataPtr user, Bool silent) {
 	auto u = ClientManager::getInstance()->findUser(CID(user->cid));
@@ -703,8 +716,3 @@
 }
 
 } // namespace dcpp
-
-/**
- * @file
- * $Id: PluginApiImpl.cpp 1248 2012-01-22 01:49:30Z crise $
- */

=== modified file 'dcpp/PluginApiImpl.h'
--- dcpp/PluginApiImpl.h	2012-08-02 17:49:34 +0000
+++ dcpp/PluginApiImpl.h	2012-09-08 13:54:40 +0000
@@ -90,6 +90,9 @@
 	static size_t DCAPI toBase32(char* dst, const uint8_t* src, size_t n);
 	static size_t DCAPI fromBase32(uint8_t* dst, const char* src, size_t n);
 
+	// Functions for DCTagger
+	static void DCAPI addTag(TagDataPtr hTags, size_t start, size_t end, const char* id, const char* attributes);
+
 	// Functions for DCQueue
 	static QueueDataPtr DCAPI addList(UserDataPtr user, Bool silent);
 	static QueueDataPtr DCAPI addDownload(const char* hash, uint64_t size, const char* target);
@@ -120,6 +123,7 @@
 	static DCHub dcHub;
 	static DCQueue dcQueue;
 	static DCUtils dcUtils;
+	static DCTagger dcTagger;
 
 	static Socket* udpSocket;
 	static Socket& getUdpSocket();
@@ -128,8 +132,3 @@
 } // namepsace dcpp
 
 #endif // !defined(DCPLUSPLUS_DCPP_PLUGIN_API_H)
-
-/**
- * @file
- * $Id: PluginApiImpl.h 1248 2012-01-22 01:49:30Z crise $
- */

=== modified file 'dcpp/PluginDefs.h'
--- dcpp/PluginDefs.h	2012-08-02 17:49:34 +0000
+++ dcpp/PluginDefs.h	2012-09-08 13:54:40 +0000
@@ -24,7 +24,7 @@
 #endif
 
 /* Version of the plugin api (must change if old plugins simply can't be seen as viably working) */
-#define DCAPI_CORE_VER				4
+#define DCAPI_CORE_VER				5
 
 #ifdef _WIN32
 # define DCAPI __stdcall
@@ -62,12 +62,15 @@
 #define DCINTF_DCPP_HUBS			"dcpp.network.DCHub"		/* Hubs */
 #define DCINTF_DCPP_HUBS_VER		1
 
-#define DCINTF_DCPP_QUEUE			"dcpp.queue.DCQueue"		/* Download Queue (TODO: expand) */
+#define DCINTF_DCPP_QUEUE			"dcpp.queue.DCQueue"		/* Download Queue */
 #define DCINTF_DCPP_QUEUE_VER		1
 
 #define DCINTF_DCPP_UTILS			"dcpp.utils.DCUtils"		/* Utility and convenience functions */
 #define DCINTF_DCPP_UTILS_VER		1
 
+#define DCINTF_DCPP_TAGGER			"dcpp.xml.Tagger"			/* Manipulation of an XML tagger */
+#define DCINTF_DCPP_TAGGER_VER		1
+
 /* Hook GUID's for Hooks (events) system */
 #define HOOK_CHAT_IN				"dcpp.chat.onIncomingChat"	/* Incoming chat from hub (obj: HubData) */
 #define HOOK_CHAT_OUT				"dcpp.chat.onOutgoingChat"	/* Outgoing chat (obj: HubData) */
@@ -93,8 +96,8 @@
 #define HOOK_QUEUE_FINISHED			"dcpp.queue.onFinished"		/* Item has just finished downloading (obj: QueueData) */
 
 #define HOOK_UI_CREATED				"dcpp.ui.onCreated"				/* Host application UI has been created (obj: if any, impl. dependant) */
-#define HOOK_UI_CHAT_DISPLAY		"dcpp.ui.onChatDisplay"			/* Chat messages before displayed in chat (obj: UserData; data: StringData) */
-#define HOOK_UI_LOCAL_CHAT_DISPLAY	"dcpp.ui.onLocalChatDisplay"	/* Local chat messages (such as system messages) before they are displayed in chat (obj: NULL; data: StringData) */
+#define HOOK_UI_CHAT_TAGS			"dcpp.ui.onTags"				/* Chat message tags before tag merging (obj: UserData; data: TagData) */
+#define HOOK_UI_CHAT_DISPLAY		"dcpp.ui.onChatDisplay"			/* Chat messages before they are displayed in chat (obj: UserData; data: StringData) */
 #define HOOK_UI_PROCESS_CHAT_CMD	"dcpp.ui.onProcessCmd"			/* Client side commands in chat (obj: HubData/UserData; data: CommandData) */
 
 /* Main hook events (returned by pluginInit) */
@@ -244,6 +247,13 @@
 	Bool isManaged;												/* False if plugin has to call release(...) for this object */
 } QueueData, *QueueDataPtr;
 
+/* Tagging intentions */
+typedef struct tagTagData {
+	const char* text;											/* Plain text string to apply tags on */
+	dcptr_t object;												/* Internal */
+	Bool isManaged;												/* Always True for now */
+} TagData, *TagDataPtr;
+
 /* Plugin meta data */
 typedef struct tagMetaData { 
 	const char* name;											/* Name of the plugin */
@@ -363,7 +373,7 @@
 	void			(DCAPI *release)				(HubDataPtr hCopy);
 } DCHub, *DCHubPtr;
 
-/* Download Queue (TODO: expand) */
+/* Download Queue */
 typedef struct tagDCQueue {
 	/* Queue API version */
 	uint32_t apiVersion;
@@ -394,13 +404,16 @@
 	size_t		(DCAPI *from_base32)				(uint8_t* dst, const char* src, size_t n);
 } DCUtils, *DCUtilsPtr;
 
+/* Manipulation of an XML tagger */
+typedef struct tagDCTagger {
+	/* Tagger API version */
+	uint32_t apiVersion;
+
+	void		(DCAPI *add_tag)					(TagDataPtr hTags, size_t start, size_t end, const char* id, const char* attributes);
+} DCTagger, *DCTaggerPtr;
+
 #ifdef __cplusplus
 }
 #endif
 
 #endif /* !defined(DCPLUSPLUS_DCPP_PLUGIN_DEFS_H) */
-
-/**
- * @file
- * $Id: PluginDefs.h 1248 2012-01-22 01:49:30Z crise $
- */

=== modified file 'dcpp/PluginEntity.h'
--- dcpp/PluginEntity.h	2012-07-02 18:13:18 +0000
+++ dcpp/PluginEntity.h	2012-09-08 13:54:40 +0000
@@ -21,6 +21,7 @@
 
 #include <list>
 
+#include "CriticalSection.h"
 #include "PluginApiImpl.h"
 
 namespace dcpp {
@@ -31,7 +32,14 @@
 class PluginEntity
 {
 public:
-	PluginEntity() { memzero(&pod, sizeof(PluginType)); pod.isManaged = True; }
+	PluginEntity() {
+#ifndef _MSC_VER
+		pod = { };
+#else
+		memset(&pod, 0, sizeof(PluginType));
+#endif
+		pod.isManaged = True;
+	}
 	virtual ~PluginEntity() { psCache.clear(); }
 
 	PluginType* copyPluginObject() {

=== modified file 'dcpp/PluginManager.cpp'
--- dcpp/PluginManager.cpp	2012-08-02 17:49:34 +0000
+++ dcpp/PluginManager.cpp	2012-09-08 13:54:40 +0000
@@ -62,6 +62,19 @@
 	}
 }
 
+PluginManager::PluginManager() : shutdown(false), secNum(Util::rand()) {
+#ifndef _MSC_VER
+	dcCore = { };
+#else
+	memset(&dcCore, 0, sizeof(DCCore));
+#endif
+	SettingsManager::getInstance()->addListener(this);
+}
+
+PluginManager::~PluginManager() {
+	SettingsManager::getInstance()->removeListener(this);
+}
+
 void PluginManager::loadPlugins(function<void (const string&)> f) {
 	PluginApiImpl::initAPI(dcCore);
 
@@ -91,11 +104,9 @@
 	PluginInfo::PLUGIN_INIT pluginInfo = reinterpret_cast<PluginInfo::PLUGIN_INIT>(GET_ADDRESS(hr, "pluginInit"));
 
 	if(pluginInfo != NULL) {
-		MetaData info;
-		memzero(&info, sizeof(MetaData));
-
+		MetaData info = { 0 };
 		DCMAIN dcMain;
-		if((dcMain = pluginInfo(&info)) != NULL) {
+		if((dcMain = pluginInfo(&info))) {
 			if(checkPlugin(info, err)) {
 				if(dcMain((install ? ON_INSTALL : ON_LOAD), &dcCore, NULL) != False) {
 					plugins.emplace_back(new PluginInfo(fileName, hr, info, dcMain));
@@ -203,27 +214,24 @@
 }
 
 // Functions that call the plugin
+bool PluginManager::onChatTags(const string& text, Tagger& tagger, OnlineUser* from) {
+	TagData data = { text.c_str(), reinterpret_cast<dcptr_t>(&tagger), True };
+	return runHook(HOOK_UI_CHAT_TAGS, from, &data);
+}
+
 bool PluginManager::onChatDisplay(string& htmlMessage, OnlineUser* from) {
-	StringData data;
-	memzero(&data, sizeof(StringData));
-
-	data.in = htmlMessage.c_str();
-
-	bool handled = from ? runHook(HOOK_UI_CHAT_DISPLAY, from, &data) : runHook(HOOK_UI_LOCAL_CHAT_DISPLAY, NULL, &data);
-	if(handled && data.out != NULL) {
+	StringData data = { htmlMessage.c_str() };
+	bool handled = runHook(HOOK_UI_CHAT_DISPLAY, from, &data);
+	if(handled && data.out) {
 		htmlMessage = data.out;
 		return true;
 	}
-
 	return false;
 }
 
 bool PluginManager::onChatCommand(Client* client, const string& line) {
-	CommandData data;
-	memzero(&data, sizeof(CommandData));
-
 	string cmd, param;
-	string::size_type si = line.find(' ');
+	auto si = line.find(' ');
 	if(si != string::npos) {
 		param = line.substr(si + 1);
 		cmd = line.substr(1, si - 1);
@@ -231,10 +239,7 @@
 		cmd = line.substr(1);
 	}
 
-	data.command = cmd.c_str();
-	data.params = param.c_str();
-	data.isPrivate = False;
-
+	CommandData data = { cmd.c_str(), param.c_str(), False };
 	return runHook(HOOK_UI_PROCESS_CHAT_CMD, client, &data);
 }
 
@@ -245,11 +250,8 @@
 	OnlineUser* ou = ClientManager::getInstance()->findOnlineUser(user.user->getCID(), user.hint);
 
 	if(ou) {
-		CommandData data;
-		memzero(&data, sizeof(CommandData));
-
 		string cmd, param;
-		string::size_type si = line.find(' ');
+		auto si = line.find(' ');
 		if(si != string::npos) {
 			param = line.substr(si + 1);
 			cmd = line.substr(1, si - 1);
@@ -257,10 +259,7 @@
 			cmd = line.substr(1);
 		}
 
-		data.command = cmd.c_str();
-		data.params = param.c_str();
-		data.isPrivate = True;
-
+		CommandData data = { cmd.c_str(), param.c_str(), True };
 		res = runHook(HOOK_UI_PROCESS_CHAT_CMD, ou, &data);
 	}
 
@@ -513,8 +512,3 @@
 }
 
 } // namespace dcpp
-
-/**
- * @file
- * $Id: PluginManager.cpp 1245 2012-01-21 15:09:54Z crise $
- */

=== modified file 'dcpp/PluginManager.h'
--- dcpp/PluginManager.h	2012-07-08 03:48:50 +0000
+++ dcpp/PluginManager.h	2012-09-08 13:54:40 +0000
@@ -19,24 +19,23 @@
 #ifndef DCPLUSPLUS_DCPP_PLUGIN_MANAGER_H
 #define DCPLUSPLUS_DCPP_PLUGIN_MANAGER_H
 
-#include "typedefs.h"
-
 #include <functional>
 #include <map>
 #include <memory>
 #include <string>
 #include <vector>
 
+#include "typedefs.h"
+
+#include "ClientManagerListener.h"
+#include "PluginApiImpl.h"
+#include "PluginEntity.h"
+#include "QueueManagerListener.h"
+#include "SettingsManager.h"
 #include "Singleton.h"
-#include "SettingsManager.h"
+#include "Tagger.h"
 #include "TimerManager.h"
 
-#include "QueueManagerListener.h"
-#include "ClientManagerListener.h"
-
-#include "PluginEntity.h"
-#include "PluginApiImpl.h"
-
 #ifdef _WIN32
 typedef HMODULE PluginHandle;
 #else
@@ -92,14 +91,8 @@
 	private ClientManagerListener, private QueueManagerListener, private SettingsManagerListener
 {
 public:
-	PluginManager() : shutdown(false), secNum(Util::rand()) {
-		memzero(&dcCore, sizeof(DCCore));
-		SettingsManager::getInstance()->addListener(this);
-	}
-
-	~PluginManager() {
-		SettingsManager::getInstance()->removeListener(this);
-	}
+	PluginManager();
+	~PluginManager();
 
 	void loadPlugins(function<void (const string&)> f);
 	bool loadPlugin(const string& fileName, function<void (const string&)> err, bool install = false);
@@ -118,7 +111,8 @@
 	DCCorePtr getCore() { return &dcCore; }
 
 	// Functions that call the plugin
-	bool onChatDisplay(string& htmlMessage, OnlineUser* from = NULL);
+	bool onChatTags(const string& text, Tagger& tagger, OnlineUser* from = nullptr);
+	bool onChatDisplay(string& htmlMessage, OnlineUser* from = nullptr);
 	bool onChatCommand(Client* client, const string& line);
 	bool onChatCommandPM(const HintedUser& user, const string& line);
 
@@ -132,8 +126,11 @@
 
 	template<class T>
 	bool runHook(const string& guid, PluginEntity<T>* entity, dcptr_t pData = NULL) {
-		Lock l(entity->cs);
-		return runHook(guid, entity->getPluginObject(), pData);
+		if(entity) {
+			Lock l(entity->cs);
+			return runHook(guid, entity->getPluginObject(), pData);
+		}
+		return runHook(guid, nullptr, pData);
 	}
 
 	template<class T>
@@ -199,8 +196,3 @@
 } // namespace dcpp
 
 #endif // !defined(DCPLUSPLUS_DCPP_PLUGIN_MANAGER_H)
-
-/**
- * @file
- * $Id: PluginManager.h 1245 2012-01-21 15:09:54Z crise $
- */

=== added file 'dcpp/Tagger.cpp'
--- dcpp/Tagger.cpp	1970-01-01 00:00:00 +0000
+++ dcpp/Tagger.cpp	2012-09-08 13:54:40 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "stdinc.h"
+#include "Tagger.h"
+
+#include "SimpleXML.h"
+
+#include <vector>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+namespace dcpp {
+
+using std::vector;
+
+void Tagger::add(size_t start, size_t end, string id, string attributes) {
+	Tag openingTag = { start, "<" + id + " " + move(attributes) + ">", true },
+		closingTag = { end, "</" + move(id) + ">", false };
+
+	tags.push_back(std::move(openingTag));
+	auto& opening = tags.back();
+
+	tags.push_back(std::move(closingTag));
+	auto& closing = tags.back();
+
+	opening.otherTag = &closing;
+	closing.otherTag = &opening;
+}
+
+string Tagger::merge(const string& text, string& tmp) {
+	tags.sort([](const Tag& a, const Tag& b) { return a.pos < b.pos; });
+
+	string ret;
+
+	size_t pos = 0;
+	vector<Tag*> openTags;
+
+	for(auto& tag: tags) {
+		ret += SimpleXML::escape(text.substr(pos, tag.pos - pos), tmp, false);
+		pos = tag.pos;
+
+		if(tag.opening) {
+			ret += tag.s;
+			openTags.push_back(&tag);
+
+		} else {
+			if(openTags.back() == tag.otherTag) {
+				// common case: no entangled tag; just write the closing tag.
+				ret += tag.s;
+				openTags.pop_back();
+
+			} else {
+				// there are entangled tags: write closing & opening tags of currently open tags.
+				for(auto openTag: openTags | boost::adaptors::reversed) { if(openTag->pos >= tag.otherTag->pos) {
+					ret += openTag->otherTag->s;
+				} }
+				openTags.erase(remove(openTags.begin(), openTags.end(), tag.otherTag), openTags.end());
+				for(auto openTag: openTags) { if(openTag->pos >= tag.otherTag->pos) {
+					ret += openTag->s;
+				} }
+			}
+		}
+	}
+
+	if(pos != text.size()) {
+		ret += SimpleXML::escape(text.substr(pos), tmp, false);
+	}
+
+	return ret;
+}
+
+} // namespace dcpp

=== added file 'dcpp/Tagger.h'
--- dcpp/Tagger.h	1970-01-01 00:00:00 +0000
+++ dcpp/Tagger.h	2012-09-08 13:54:40 +0000
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DCPLUSPLUS_DCPP_TAGGER_H
+#define DCPLUSPLUS_DCPP_TAGGER_H
+
+#include "forward.h"
+
+#include <list>
+#include <string>
+
+namespace dcpp {
+
+using std::list;
+using std::string;
+
+/** Adds XML tags to a plain text string. The tags are added all at once. The tagger supports
+entangled tags, such as: <a> <b> </a> </b> -> <a> <b> </b></a><b> </b> */
+class Tagger {
+public:
+	void add(size_t start, size_t end, string id, string attributes);
+	string merge(const string& text, string& tmp);
+
+private:
+	struct Tag { size_t pos; string s; bool opening; Tag* otherTag; };
+	list<Tag> tags; // this table holds the tags to be added along with their position.
+};
+
+} // namespace dcpp
+
+#endif

=== modified file 'dcpp/stdinc.h'
--- dcpp/stdinc.h	2012-07-01 18:41:13 +0000
+++ dcpp/stdinc.h	2012-09-08 13:54:40 +0000
@@ -29,10 +29,6 @@
 #define DCAPI_HOST 1
 #endif
 
-#ifndef memzero
-#define memzero(dest, n) memset(dest, 0, n)
-#endif
-
 #ifdef HAS_PCH
 
 #ifdef _WIN32