linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #05294
[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2851: XML parser: allow mixed content. HTML-RTF conv: allow nested tags, improve some
------------------------------------------------------------
revno: 2851
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Sat 2012-02-04 16:43:31 +0100
message:
XML parser: allow mixed content. HTML-RTF conv: allow nested tags, improve some
modified:
dcpp/ChatMessage.cpp
dcpp/DirectoryListing.cpp
dcpp/FavoriteManager.cpp
dcpp/HashManager.cpp
dcpp/QueueManager.cpp
dcpp/ShareManager.cpp
dcpp/SimpleXML.h
dcpp/SimpleXMLReader.cpp
dcpp/SimpleXMLReader.h
test/testxml.cpp
win32/HtmlToRtf.cpp
--
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-02-02 23:16:07 +0000
+++ dcpp/ChatMessage.cpp 2012-02-04 15:43:31 +0000
@@ -88,29 +88,31 @@
return stream.str();
};
+ htmlMessage += "<span id=\"message\">";
+
if(BOOLSETTING(TIME_STAMPS)) {
- tmp = "[" + Util::getShortTimeString(timestamp) + "] ";
- message += tmp;
- htmlMessage += addSpan("timestamp", tmp, Util::emptyString);
+ tmp = "[" + Util::getShortTimeString(timestamp) + "]";
+ message += tmp + " ";
+ htmlMessage += addSpan("timestamp", tmp, Util::emptyString) + " ";
}
if(messageTimestamp) {
- tmp = "["; tmp += str(F_("Sent %1%") % Util::getShortTimeString(messageTimestamp)); tmp += "] ";
- message += tmp;
- htmlMessage += addSpan("messageTimestamp", tmp, Util::emptyString);
+ tmp = "["; tmp += str(F_("Sent %1%") % Util::getShortTimeString(messageTimestamp)); tmp += "]";
+ message += tmp + " ";
+ htmlMessage += addSpan("messageTimestamp", tmp, Util::emptyString) + " ";
}
tmp = from->getIdentity().getNick();
// let's *not* obey the spec here and add a space after the star. :P
- tmp = thirdPerson ? "* " + tmp + " " : "<" + tmp + "> ";
- message += tmp;
+ tmp = thirdPerson ? "* " + tmp + " " : "<" + tmp + ">";
+ message += tmp + " ";
auto style = from->getIdentity().getStyle();
string styleAttr;
if(!style.font.empty()) { styleAttr += "font: " + cssFont(style.font) + ";"; }
if(style.textColor != -1) { styleAttr += "color: #" + cssColor(style.textColor) + ";"; }
if(style.bgColor != -1) { styleAttr += "background-color: #" + cssColor(style.bgColor) + ";"; }
- htmlMessage += addSpan("nick", tmp, styleAttr);
+ htmlMessage += addSpan("nick", tmp, styleAttr) + " ";
// Check all '<' and '[' after newlines as they're probably pastes...
tmp = text;
@@ -126,7 +128,7 @@
}
message += tmp;
- htmlMessage += addSpan("message", tmp, Util::emptyString);
+ htmlMessage += addSpan("text", tmp, Util::emptyString) + "</span>";
/// @todo send this to plugins
}
=== modified file 'dcpp/DirectoryListing.cpp'
--- dcpp/DirectoryListing.cpp 2012-01-13 20:55:20 +0000
+++ dcpp/DirectoryListing.cpp 2012-02-04 15:43:31 +0000
@@ -119,7 +119,7 @@
virtual ~ListLoader() { }
void startTag(const string& name, StringPairList& attribs, bool simple);
- void endTag(const string& name, const string& data);
+ void endTag(const string& name);
const string& getBase() const { return base; }
@@ -219,7 +219,7 @@
if(simple) {
// To handle <Directory Name="..." />
- endTag(name, Util::emptyString);
+ endTag(name);
}
}
} else if(name == sFileListing) {
@@ -251,12 +251,12 @@
if(simple) {
// To handle <Directory Name="..." />
- endTag(name, Util::emptyString);
+ endTag(name);
}
}
}
-void ListLoader::endTag(const string& name, const string&) {
+void ListLoader::endTag(const string& name) {
if(inListing) {
if(name == sDirectory) {
cur = cur->getParent();
=== modified file 'dcpp/FavoriteManager.cpp'
--- dcpp/FavoriteManager.cpp 2012-01-23 20:18:58 +0000
+++ dcpp/FavoriteManager.cpp 2012-02-04 15:43:31 +0000
@@ -278,8 +278,7 @@
class XmlListLoader : public SimpleXMLReader::CallBack {
public:
XmlListLoader(HubEntryList& lst) : publicHubs(lst) { }
- virtual ~XmlListLoader() { }
- virtual void startTag(const string& name, StringPairList& attribs, bool) {
+ void startTag(const string& name, StringPairList& attribs, bool) {
if(name == "Hub") {
const string& name = getAttrib(attribs, "Name", 0);
const string& server = getAttrib(attribs, "Address", 1);
@@ -297,9 +296,6 @@
publicHubs.push_back(HubEntry(name, server, description, users, country, shared, minShare, minSlots, maxHubs, maxUsers, reliability, rating));
}
}
- virtual void endTag(const string&, const string&) {
-
- }
private:
HubEntryList& publicHubs;
};
=== modified file 'dcpp/HashManager.cpp'
--- dcpp/HashManager.cpp 2012-01-23 20:18:58 +0000
+++ dcpp/HashManager.cpp 2012-02-04 15:43:31 +0000
@@ -343,8 +343,8 @@
HashLoader(HashManager::HashStore& s) :
store(s), size(0), timeStamp(0), version(HASH_FILE_VERSION), inTrees(false), inFiles(false), inHashStore(false) {
}
- virtual void startTag(const string& name, StringPairList& attribs, bool simple);
- virtual void endTag(const string& name, const string& data);
+ void startTag(const string& name, StringPairList& attribs, bool simple);
+ void endTag(const string& name);
private:
HashManager::HashStore& store;
@@ -423,7 +423,7 @@
}
}
-void HashLoader::endTag(const string& name, const string&) {
+void HashLoader::endTag(const string& name) {
if (name == sFile) {
file.clear();
}
=== modified file 'dcpp/QueueManager.cpp'
--- dcpp/QueueManager.cpp 2012-02-01 20:48:11 +0000
+++ dcpp/QueueManager.cpp 2012-02-04 15:43:31 +0000
@@ -1376,9 +1376,8 @@
class QueueLoader : public SimpleXMLReader::CallBack {
public:
QueueLoader() : cur(NULL), inDownloads(false) { }
- virtual ~QueueLoader() { }
- virtual void startTag(const string& name, StringPairList& attribs, bool simple);
- virtual void endTag(const string& name, const string& data);
+ void startTag(const string& name, StringPairList& attribs, bool simple);
+ void endTag(const string& name);
private:
string target;
@@ -1498,7 +1497,7 @@
}
}
-void QueueLoader::endTag(const string& name, const string&) {
+void QueueLoader::endTag(const string& name) {
if(inDownloads) {
if(name == sDownload) {
cur = NULL;
=== modified file 'dcpp/ShareManager.cpp'
--- dcpp/ShareManager.cpp 2012-01-23 20:18:58 +0000
+++ dcpp/ShareManager.cpp 2012-02-04 15:43:31 +0000
@@ -355,7 +355,7 @@
struct ShareLoader : public SimpleXMLReader::CallBack {
ShareLoader(ShareManager::DirList& aDirs) : dirs(aDirs), cur(0), depth(0) { }
- virtual void startTag(const string& name, StringPairList& attribs, bool simple) {
+ void startTag(const string& name, StringPairList& attribs, bool simple) {
if(name == SDIRECTORY) {
const string& name = getAttrib(attribs, SNAME, 0);
if(!name.empty()) {
@@ -390,7 +390,7 @@
cur->files.insert(ShareManager::Directory::File(fname, Util::toInt64(size), cur, TTHValue(root)));
}
}
- virtual void endTag(const string& name, const string&) {
+ void endTag(const string& name) {
if(name == SDIRECTORY) {
depth--;
if(cur) {
=== modified file 'dcpp/SimpleXML.h'
--- dcpp/SimpleXML.h 2012-01-13 20:55:20 +0000
+++ dcpp/SimpleXML.h 2012-02-04 15:43:31 +0000
@@ -203,14 +203,15 @@
class TagReader : public SimpleXMLReader::CallBack {
public:
TagReader(Tag* root) : cur(root) { }
- virtual bool getData(string&) { return false; }
- virtual void startTag(const string& name, StringPairList& attribs, bool simple) {
+ void startTag(const string& name, StringPairList& attribs, bool simple) {
cur->children.push_back(new Tag(name, attribs, cur));
if(!simple)
cur = cur->children.back();
}
- virtual void endTag(const string&, const string& d) {
- cur->data = d;
+ void data(const string& data) {
+ cur->data += data;
+ }
+ void endTag(const string&) {
if(cur->parent == NULL)
throw SimpleXMLException("Invalid end tag");
cur = cur->parent;
=== modified file 'dcpp/SimpleXMLReader.cpp'
--- dcpp/SimpleXMLReader.cpp 2012-02-01 16:26:37 +0000
+++ dcpp/SimpleXMLReader.cpp 2012-02-04 15:43:31 +0000
@@ -421,7 +421,7 @@
d.append(1, '\'');
advancePos(6);
return true;
-
+
// Ignore � decimal and � hex values to avoid error, they wouldn't be parsed anyway
} else if(charAt(1) == '#' && isdigit(charAt(2)) && charAt(3) == ';') {
advancePos(4);
@@ -465,12 +465,6 @@
}
int c = charAt(0);
- if(c == '<') {
- if(!value.empty()) {
- error("Mixed content not supported");
- }
- return false;
- }
if(c == '&') {
return entref(value);
@@ -508,11 +502,7 @@
}
if(charAt(0) == '>') {
- if(!encoding.empty() && encoding != Text::utf8) {
- value = Text::toUtf8(encoding);
- }
- cb->endTag(elements.back(), value);
- value.clear();
+ cb->endTag(elements.back());
elements.pop_back();
state = STATE_CONTENT;
@@ -718,8 +708,11 @@
return true;
}
- if(state == STATE_CONTENT && state != oldState) {
- // might contain whitespace from previous unfruitful contents (that turned out to be elements / comments)
+ if(oldState == STATE_CONTENT && state != oldState && !value.empty()) {
+ if(!encoding.empty() && encoding != Text::utf8) {
+ value = Text::toUtf8(value, encoding);
+ }
+ cb->data(value);
value.clear();
}
=== modified file 'dcpp/SimpleXMLReader.h'
--- dcpp/SimpleXMLReader.h 2012-02-01 16:26:37 +0000
+++ dcpp/SimpleXMLReader.h 2012-02-04 15:43:31 +0000
@@ -29,8 +29,24 @@
public:
struct CallBack : private boost::noncopyable {
virtual ~CallBack() { }
- virtual void startTag(const std::string& name, StringPairList& attribs, bool simple) = 0;
- virtual void endTag(const std::string& name, const std::string& data) = 0;
+
+ /** A new XML tag has been encountered.
+ @param name Name of the tag.
+ @param attribs List of attribute name / contents pairs representing attributes of the tag.
+ Use the getAttrib function to retrieve one particular attribute.
+ @param simple Whether this tag is void of any data (<example/>). */
+ virtual void startTag(const std::string& name, StringPairList& attribs, bool simple) { }
+
+ /** Contents of an XML tag have been read.
+ @param data Contents of the tag.
+ @note This may be called several times per tag with partial contents in mixed content
+ situations, such as: <outer>Data1<inner>Data2</inner>Data3</outer> (data will be called
+ once for "Data1", once for "Data2", once for "Data3"). */
+ virtual void data(const std::string& data) { }
+
+ /** Contents of an XML tag have been read.
+ @param name Name of the tag. */
+ virtual void endTag(const std::string& name) { }
protected:
static const std::string& getAttrib(StringPairList& attribs, const std::string& name, size_t hint);
=== modified file 'test/testxml.cpp'
--- test/testxml.cpp 2012-02-01 16:26:37 +0000
+++ test/testxml.cpp 2012-02-04 15:43:31 +0000
@@ -15,7 +15,7 @@
class Collector : public SimpleXMLReader::CallBack {
public:
- virtual void startTag(const std::string& name, dcpp::StringPairList& attribs, bool simple) {
+ void startTag(const std::string& name, dcpp::StringPairList& attribs, bool simple) {
if(simple) {
simpleTags[name]++;
} else {
@@ -28,9 +28,12 @@
}
}
- virtual void endTag(const std::string& name, const std::string& data) {
+ void data(const std::string& data) {
+ dataValues[data]++;
+ }
+
+ void endTag(const std::string& name) {
endTags[name]++;
- dataValues[data]++;
}
Counter simpleTags;
@@ -46,7 +49,7 @@
Collector collector;
SimpleXMLReader reader(&collector);
- const char xml[] = "<?xml version='1.0' encoding='utf-8' ?><complex a='1'> <simple b=\"2\"/><complex2> data </complex2></complex>";
+ const char xml[] = "<?xml version='1.0' encoding='utf-8' ?><complex a='1'> data <simple b=\"2\"/><complex2> data </complex2></complex>";
for(size_t i = 0, iend = sizeof(xml); i < iend; ++i) {
reader.parse(xml + i, 1);
}
@@ -60,7 +63,7 @@
ASSERT_EQ(collector.attribValues["2"], 1);
ASSERT_EQ(collector.startTags["complex2"], 1);
ASSERT_EQ(collector.endTags["complex2"], 1);
- ASSERT_EQ(collector.dataValues[" data "], 1);
+ ASSERT_EQ(collector.dataValues[" data "], 2);
}
TEST(testxml, test_entref)
=== modified file 'win32/HtmlToRtf.cpp'
--- win32/HtmlToRtf.cpp 2012-02-02 23:16:07 +0000
+++ win32/HtmlToRtf.cpp 2012-02-04 15:43:31 +0000
@@ -22,6 +22,7 @@
#include "HtmlToRtf.h"
#include <dcpp/debug.h>
+#include <dcpp/Flags.h>
#include <dcpp/SimpleXML.h>
#include <dcpp/StringTokenizer.h>
#include <dcpp/Text.h>
@@ -32,31 +33,36 @@
struct Parser : SimpleXMLReader::CallBack {
Parser(dwt::RichTextBox* box);
void startTag(const string& name, StringPairList& attribs, bool simple);
- void endTag(const string& name, const string& data);
+ void data(const string& data);
+ void endTag(const string& name);
tstring finalize();
private:
- int addFont(string&& font);
+ struct Context : Flags {
+ enum { Bold = 1 << 0, Italic = 1 << 1, Underlined = 1 << 2 };
+ size_t font; // index in the "fonts" table
+ int fontSize;
+ size_t textColor; // index in the "colors" table
+ size_t bgColor; // index in the "colors" table
+ Context(dwt::RichTextBox* box, Parser& parser);
+ };
+
+ void write(Context& context);
+
+ size_t addFont(string&& font);
static int rtfFontSize(float px);
- int addColor(COLORREF color);
+ size_t addColor(COLORREF color);
void parseFont(const string& s);
- void parseColor(int& contextColor, const string& s);
+ void parseColor(size_t& contextColor, const string& s);
+ void parseDecoration(const string& s);
tstring ret;
StringList fonts;
StringList colors;
- struct {
- int font;
- int fontSize;
- int textColor;
- int bgColor;
- void clear() { font = fontSize = textColor = bgColor = -1; }
- } context, defaultContext;
-
- string header;
+ std::deque<Context> contexts;
};
tstring HtmlToRtf::convert(const string& html, dwt::RichTextBox* box) {
@@ -67,51 +73,31 @@
}
Parser::Parser(dwt::RichTextBox* box) {
- auto lf = box->getFont()->getLogFont();
- defaultContext.font = addFont("{\\f0\\fnil\\fcharset" + Util::toString(lf.lfCharSet) + " " + Text::fromT(lf.lfFaceName) + ";}");
- defaultContext.fontSize = rtfFontSize(abs(lf.lfHeight));
-
- defaultContext.textColor = addColor(box->getTextColor());
- defaultContext.bgColor = addColor(box->getBgColor());
-
- context.clear();
-}
-
-int Parser::addFont(string&& font) {
- auto ret = fonts.size();
- fonts.push_back(std::forward<string>(font));
- return ret;
-}
-
-int Parser::rtfFontSize(float px) {
- return px * 72.0 / 96.0 // px -> font points
- * dwt::util::dpiFactor() // respect DPI settings
- * 2.0; // RTF font sizes are expressed in half-points
-}
-
-int Parser::addColor(COLORREF color) {
- auto ret = colors.size();
- colors.push_back("\\red" + Util::toString(GetRValue(color)) +
- "\\green" + Util::toString(GetGValue(color)) +
- "\\blue" + Util::toString(GetBValue(color)) + ";");
- return ret;
-}
-
-static const string styleAttr = "style";
-static const string fontStyle = "font";
-static const string textColorStyle = "color";
-static const string bgColorStyle = "background-color";
-static string tmp;
+ // create a default context with the Rich Edit control's current formatting.
+ contexts.emplace_back(box, *this);
+ write(contexts.back());
+}
void Parser::startTag(const string& name, StringPairList& attribs, bool simple) {
- if(attribs.empty()) {
+ if(simple) {
return;
}
- const auto& style = getAttrib(attribs, styleAttr, 0);
-
- enum { Declaration, Font, TextColor, BgColor } state = Declaration;
-
+ contexts.emplace_back(contexts.back());
+
+ if(name == "b") {
+ contexts.back().setFlag(Context::Bold);
+ } else if(name == "i") {
+ contexts.back().setFlag(Context::Italic);
+ } else if(name == "u") {
+ contexts.back().setFlag(Context::Underlined);
+ }
+
+ const auto& style = getAttrib(attribs, "style", 0);
+
+ enum { Declaration, Font, Decoration, TextColor, BgColor, Unknown } state = Declaration;
+
+ string tmp;
size_t i = 0, j;
while((j = style.find_first_of(":;", i)) != string::npos) {
tmp = style.substr(i, j - i);
@@ -120,9 +106,11 @@
switch(state) {
case Declaration:
{
- if(tmp == fontStyle) { state = Font; }
- else if(tmp == textColorStyle) { state = TextColor; }
- else if(tmp == bgColorStyle) { state = BgColor; }
+ if(tmp == "font") { state = Font; }
+ else if(tmp == "color") { state = TextColor; }
+ else if(tmp == "text-decoration") { state = Decoration; }
+ else if(tmp == "background-color") { state = BgColor; }
+ else { state = Unknown; }
break;
}
@@ -133,35 +121,45 @@
break;
}
+ case Decoration:
+ {
+ parseDecoration(tmp);
+ state = Declaration;
+ break;
+ }
+
case TextColor:
{
- parseColor(context.textColor, tmp);
+ parseColor(contexts.back().textColor, tmp);
state = Declaration;
break;
}
case BgColor:
{
- parseColor(context.bgColor, tmp);
+ parseColor(contexts.back().bgColor, tmp);
+ state = Declaration;
+ break;
+ }
+
+ case Unknown:
+ {
state = Declaration;
break;
}
}
}
-}
-
-void Parser::endTag(const string& name, const string& data) {
- if(context.font == -1) { context.font = defaultContext.font; }
- if(context.fontSize == -1) { context.fontSize = defaultContext.fontSize; }
- if(context.textColor == -1) { context.textColor = defaultContext.textColor; }
- if(context.bgColor == -1) { context.bgColor = defaultContext.bgColor; }
-
- ret += Text::toT("{\\f" + Util::toString(context.font) + "\\fs" + Util::toString(context.fontSize) +
- "\\cf" + Util::toString(context.textColor) + "\\highlight" + Util::toString(context.bgColor) +
- header + " ") + dwt::RichTextBox::rtfEscape(Text::toT(Text::toDOS(data))) + _T("}");
-
- context.clear();
- header.clear();
+
+ write(contexts.back());
+}
+
+void Parser::data(const string& data) {
+ ret += dwt::RichTextBox::rtfEscape(Text::toT(Text::toDOS(data)));
+}
+
+void Parser::endTag(const string& name) {
+ ret += _T("}");
+ contexts.pop_back();
}
tstring Parser::finalize() {
@@ -169,6 +167,48 @@
"}{\\colortbl" + Util::toString(Util::emptyString, colors) + "}") + ret + _T("}");
}
+Parser::Context::Context(dwt::RichTextBox* box, Parser& parser) {
+ // create a default context with the Rich Edit control's current formatting.
+ auto lf = box->getFont()->getLogFont();
+ font = parser.addFont("\\fnil\\fcharset" + Util::toString(lf.lfCharSet) + " " + Text::fromT(lf.lfFaceName));
+ fontSize = rtfFontSize(abs(lf.lfHeight));
+ if(lf.lfWeight >= FW_BOLD) { setFlag(Bold); }
+ if(lf.lfItalic) { setFlag(Italic); }
+
+ textColor = parser.addColor(box->getTextColor());
+ bgColor = parser.addColor(box->getBgColor());
+}
+
+void Parser::write(Context& context) {
+ string header;
+ if(context.isSet(Context::Bold)) { header += "\\b"; }
+ if(context.isSet(Context::Italic)) { header += "\\i"; }
+ if(context.isSet(Context::Underlined)) { header += "\\ul"; }
+ ret += Text::toT("{\\f" + Util::toString(context.font) + "\\fs" + Util::toString(context.fontSize) +
+ "\\cf" + Util::toString(context.textColor) + "\\highlight" + Util::toString(context.bgColor) +
+ header + " ");
+}
+
+size_t Parser::addFont(string&& font) {
+ auto ret = fonts.size();
+ fonts.push_back("{\\f" + Util::toString(ret) + std::forward<string>(font) + ";}");
+ return ret;
+}
+
+int Parser::rtfFontSize(float px) {
+ return px * 72.0 / 96.0 // px -> font points
+ * dwt::util::dpiFactor() // respect DPI settings
+ * 2.0; // RTF font sizes are expressed in half-points
+}
+
+size_t Parser::addColor(COLORREF color) {
+ auto ret = colors.size();
+ colors.push_back("\\red" + Util::toString(GetRValue(color)) +
+ "\\green" + Util::toString(GetGValue(color)) +
+ "\\blue" + Util::toString(GetBValue(color)) + ";");
+ return ret;
+}
+
void Parser::parseFont(const string& s) {
// this contains multiple params separated by spaces.
StringTokenizer<string> tok(s, ' ');
@@ -195,32 +235,45 @@
family.erase(std::remove(family.begin(), family.end(), '\''), family.end());
if(family.empty())
return;
- context.font = addFont("{\\f" + Util::toString(context.font) + "\\fnil " + std::move(family) + ";}");
+ contexts.back().font = addFont("\\fnil " + std::move(family));
// parse the second to last param (font size).
/// @todo handle more than px sizes
auto& size = *(l.end() - 2);
if(size.size() > 2 && *(size.end() - 2) == 'p' && *(size.end() - 1) == 'x') { // 16px
- context.fontSize = rtfFontSize(Util::toFloat(size.substr(0, size.size() - 2)));
+ contexts.back().fontSize = rtfFontSize(Util::toFloat(size.substr(0, size.size() - 2)));
}
// parse the optional third to last param (font weight).
- if(params >= 3 && Util::toInt(*(l.end() - 3)) >= FW_BOLD) {
- header += "\\b";
+ if(params > 2 && Util::toInt(*(l.end() - 3)) >= FW_BOLD) {
+ contexts.back().setFlag(Context::Bold);
}
// parse the optional first param (font style).
- if(params >= 4 && l[0] == "italic") {
- header += "\\i";
- }
-}
-
-void Parser::parseColor(int& contextColor, const string& s) {
+ if(params > 2 && l[0] == "italic") {
+ contexts.back().setFlag(Context::Italic);
+ }
+}
+
+void Parser::parseDecoration(const string& s) {
+ if(s.find("underline")) {
+ contexts.back().setFlag(Context::Underlined);
+ }
+}
+
+void Parser::parseColor(size_t& contextColor, const string& s) {
auto sharp = s.find('#');
if(sharp != string::npos && s.size() > sharp + 6) {
try {
+#if defined(__MINGW32__) && defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)
+ /// @todo use stol on MinGW when it's available
+ unsigned int color = 0;
+ auto colStr = s.substr(sharp + 1, 6);
+ sscanf(colStr.c_str(), "%X", &color);
+#else
size_t pos = 0;
auto color = std::stol(s.substr(sharp + 1, 6), &pos, 16);
+#endif
contextColor = addColor(RGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
} catch(const std::exception& e) { dcdebug("color parsing exception: %s with str: %s\n", e.what(), s.c_str()); }
}