Mercurial > irccd
changeset 118:2a63c8ec45cd
Irccd: fix some errors in util::format, #408
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 29 Apr 2016 14:19:30 +0200 |
parents | 9b9b09543d2a |
children | b39573fc066e |
files | lib/irccd/util.cpp tests/util/main.cpp |
diffstat | 2 files changed, 64 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/irccd/util.cpp Fri Apr 29 10:08:17 2016 +0200 +++ b/lib/irccd/util.cpp Fri Apr 29 14:19:30 2016 +0200 @@ -65,6 +65,11 @@ { "reverse", '\x16' } }; +inline bool isReserved(char token) noexcept +{ + return token == '#' || token == '@' || token == '$'; +} + std::string substituteDate(const std::string &text, const Substitution ¶ms) { std::ostringstream oss; @@ -195,48 +200,58 @@ std::string format(std::string text, const Substitution ¶ms) { - std::ostringstream oss; - /* * Change the date format before anything else to avoid interpolation with keywords and * user input. */ text = substituteDate(text, params); - std::string::const_iterator it = text.begin(); - std::string::const_iterator end = text.end(); + std::ostringstream oss; - while (it != end) { + for (auto it = text.cbegin(), end = text.cend(); it != end; ) { auto token = *it; - if (token == '#' || token == '@' || token == '$') { - ++ it; + /* Is the current character a reserved token or not? */ + if (!isReserved(token)) { + oss << *it++; + continue; + } - if (it == end) { - oss << token; - continue; - } + /* The token was at the end, just write it and return now. */ + if (++it == end) { + oss << token; + continue; + } + + /* The token is declaring a template variable, substitute it. */ + if (*it == '{') { + oss << substitute(++it, end, token, params); + continue; + } - if (*it == '{') { - /* Do we have a variable? */ - oss << substitute(++it, end, token, params); - } else if (*it == token) { - /* Need one for sure */ - oss << token; + /* + * If the next token is different from the previous one, just let the next iteration parse the string because + * we can have the following constructs. + * + * "@#{var}" -> "@value" + */ + if (*it != token) { + oss << token; + continue; + } - /* Do we have a double token followed by a { for escaping? */ - if (++it == end) { - continue; - } - - if (*it != '{') { - oss << token; - } - } else { - oss << *it++; - } - } else { - oss << *it++; + /* + * Write the token only if it's not a variable because at this step we may have the following + * constructs. + * + * "##" -> "##" + * "##hello" -> "##hello" + * "##{hello}" -> "#{hello}" + */ + if (++it == end) { + oss << token << token; + } else if (*it == '{') { + oss << token; } }
--- a/tests/util/main.cpp Fri Apr 29 10:08:17 2016 +0200 +++ b/tests/util/main.cpp Fri Apr 29 14:19:30 2016 +0200 @@ -37,6 +37,25 @@ ASSERT_EQ(expected, result); } +TEST(Format, escape) +{ + util::Substitution params; + + params.keywords.emplace("target", "hello"); + + ASSERT_EQ("$@#", util::format("$@#")); + ASSERT_EQ(" $ @ # ", util::format(" $ @ # ")); + ASSERT_EQ("#", util::format("#")); + ASSERT_EQ(" # ", util::format(" # ")); + ASSERT_EQ("#@", util::format("#@")); + ASSERT_EQ("##", util::format("##")); + ASSERT_EQ("#!", util::format("#!")); + ASSERT_EQ("#{target}", util::format("##{target}")); + ASSERT_EQ("@hello", util::format("@#{target}", params)); + ASSERT_EQ("hello#", util::format("#{target}#", params)); + ASSERT_ANY_THROW(util::format("#{failure")); +} + TEST(Format, keywordSimple) { util::Substitution params;