From b08af9801b47edbb4684bac691cb1e8d5eb9e3b8 Mon Sep 17 00:00:00 2001 From: Jackmcbarn Date: Sun, 3 Apr 2011 11:27:33 -0400 Subject: [PATCH] Rewrite sepstream logic, add an option to suppress empty items, and add test cases --- include/hashcomp.h | 23 +++++---- src/command_parse.cpp | 4 +- src/hashcomp.cpp | 77 ++++++++++++++++++----------- src/modules/account.h | 10 ++-- src/modules/m_callerid.cpp | 5 -- src/modules/m_cloaking_12.cpp | 2 +- src/modules/m_flatfile_account.cpp | 4 +- src/modules/m_flatfile_channels.cpp | 4 +- src/modules/m_helpop.cpp | 2 +- src/testsuite.cpp | 33 ++++++++----- 10 files changed, 97 insertions(+), 67 deletions(-) diff --git a/include/hashcomp.h b/include/hashcomp.h index 8de799786..8626deb53 100644 --- a/include/hashcomp.h +++ b/include/hashcomp.h @@ -314,20 +314,23 @@ namespace irc private: /** Original string. */ - std::string tokens; - /** Last position of a seperator token + const std::string tokens; + /** Whether to suppress empty items */ - std::string::iterator last_starting_position; + const bool suppress_empty; /** Current string position */ - std::string::iterator n; + std::string::const_iterator n; /** Seperator value */ - char sep; + const char sep; + /** Whether the end has been reached + */ + bool endreached; public: /** Create a sepstream and fill it with the provided data */ - sepstream(const std::string &source, char seperator); + sepstream(const std::string &source, char seperator, bool suppress_empty_items = true); /** Destructor */ @@ -342,12 +345,12 @@ namespace irc /** Fetch the entire remaining stream, without tokenizing * @return The remaining part of the stream */ - virtual const std::string GetRemaining(); + virtual std::string GetRemaining() const; /** Returns true if the end of the stream has been reached * @return True if the end of the stream has been reached, otherwise false */ - virtual bool StreamEnd(); + virtual bool StreamEnd() const; }; /** A derived form of sepstream, which seperates on commas @@ -357,7 +360,7 @@ namespace irc public: /** Initialize with comma seperator */ - commasepstream(const std::string &source) : sepstream(source, ',') + commasepstream(const std::string &source, bool suppress_empty_items = true) : sepstream(source, ',', suppress_empty_items) { } }; @@ -369,7 +372,7 @@ namespace irc public: /** Initialize with space seperator */ - spacesepstream(const std::string &source) : sepstream(source, ' ') + spacesepstream(const std::string &source, bool suppress_empty_items = true) : sepstream(source, ' ', suppress_empty_items) { } }; diff --git a/src/command_parse.cpp b/src/command_parse.cpp index 92682fed5..5f4890cbe 100644 --- a/src/command_parse.cpp +++ b/src/command_parse.cpp @@ -67,8 +67,8 @@ int CommandParser::LoopCall(User* user, Command* CommandObj, const std::vector= 0 ? parameters[extra] : ""); + irc::commasepstream items1(parameters[splithere], false); + irc::commasepstream items2(extra >= 0 ? parameters[extra] : "", false); std::string extrastuff; std::string item; unsigned int max = 0; diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp index 90d601d43..4287c63cb 100644 --- a/src/hashcomp.cpp +++ b/src/hashcomp.cpp @@ -305,47 +305,70 @@ bool irc::tokenstream::GetToken(long &token) return returnval; } -irc::sepstream::sepstream(const std::string &source, char seperator) : tokens(source), sep(seperator) +irc::sepstream::sepstream(const std::string &source, char seperator, bool suppress_empty_items) : tokens(source), suppress_empty(suppress_empty_items), sep(seperator), endreached(false) { - last_starting_position = tokens.begin(); n = tokens.begin(); + if(suppress_empty) + { + while(n != tokens.end() && *n == sep) + ++n; + if(n == tokens.end()) + endreached = true; + } } bool irc::sepstream::GetToken(std::string &token) { - std::string::iterator lsp = last_starting_position; - - while (n != tokens.end()) + if(endreached) { - if ((*n == sep) || (n+1 == tokens.end())) - { - last_starting_position = n+1; - token = std::string(lsp, n+1 == tokens.end() ? n+1 : n++); - - while ((token.length()) && (token.find_last_of(sep) == token.length() - 1)) - token.erase(token.end() - 1); - - if (token.empty()) - n++; - - return n == tokens.end() ? false : true; - } - - n++; + token = ""; + return false; } - token = ""; - return false; + std::string::const_iterator lsp = n; + + for(;; ++n) + { + if(n == tokens.end()) + { + endreached = true; + token = std::string(lsp, n); + if(suppress_empty) + { + std::string::size_type i = token.find_first_of(sep); + if(i != std::string::npos) + token.erase(i); + return !token.empty(); + } + else + return true; + } + else if(*n == sep) + { + if(suppress_empty && (n+1 == tokens.end() || *(n+1) == sep)) + continue; + token = std::string(lsp, n++); + if(suppress_empty) + { + std::string::size_type i = token.find_first_of(sep); + if(i != std::string::npos) + token.erase(i); + return !token.empty(); + } + else + return true; + } + } } -const std::string irc::sepstream::GetRemaining() +std::string irc::sepstream::GetRemaining() const { return std::string(n, tokens.end()); } -bool irc::sepstream::StreamEnd() +bool irc::sepstream::StreamEnd() const { - return ((n + 1) == tokens.end()); + return endreached; } irc::sepstream::~sepstream() @@ -468,9 +491,7 @@ long irc::portparser::GetToken() } std::string x; - sep->GetToken(x); - - if (x.empty()) + if (!sep->GetToken(x)) return 0; while (Overlaps(atoi(x.c_str()))) diff --git a/src/modules/account.h b/src/modules/account.h index 637f90717..efa62cb50 100644 --- a/src/modules/account.h +++ b/src/modules/account.h @@ -394,15 +394,17 @@ class TSStringVectorExtItem : public TSGenericExtItem > protected: virtual std::string value_serialize(SerializeFormat format, const std::vector* value) const { - std::ostringstream retval; + std::ostringstream str; for(std::vector::const_iterator i = value->begin(); i != value->end(); ++i) - retval << *i << delimeter; - return retval.str(); + str << *i << delimeter; + std::string retval = str.str(); + retval.erase(retval.length() - 1); + return retval; } virtual std::vector* value_unserialize(SerializeFormat format, const std::string& value) { - irc::sepstream sep(value, delimeter); + irc::sepstream sep(value, delimeter, false); std::string token; std::vector* retval = new std::vector; while(sep.GetToken(token)) diff --git a/src/modules/m_callerid.cpp b/src/modules/m_callerid.cpp index ff22f7203..72890829f 100644 --- a/src/modules/m_callerid.cpp +++ b/src/modules/m_callerid.cpp @@ -43,11 +43,6 @@ class callerid_data } while (s.GetToken(tok)) { - if (tok.empty()) - { - continue; - } - User *u = ServerInstance->FindNick(tok); if (!u) { diff --git a/src/modules/m_cloaking_12.cpp b/src/modules/m_cloaking_12.cpp index 96d51c6bd..59118d0a0 100644 --- a/src/modules/m_cloaking_12.cpp +++ b/src/modules/m_cloaking_12.cpp @@ -203,7 +203,7 @@ class ModuleCloaking : public Module std::string CompatCloak4(const char* ip) { - irc::sepstream seps(ip, '.'); + irc::sepstream seps(ip, '.', false); std::string octet[4]; int i[4]; diff --git a/src/modules/m_flatfile_account.cpp b/src/modules/m_flatfile_account.cpp index db89fd43b..ec5094ec2 100644 --- a/src/modules/m_flatfile_account.cpp +++ b/src/modules/m_flatfile_account.cpp @@ -67,7 +67,7 @@ class DatabaseReader std::string hash, password; std::map extensions; std::string token; - irc::spacesepstream sep(str); + irc::spacesepstream sep(str, false); /* get first one */ /* malformed if it is not acctinfo */ if (!sep.GetToken (token) || token != "acctinfo") @@ -130,7 +130,7 @@ class DatabaseReader if (c2 == '\r') continue; str.push_back (c2); } - irc::spacesepstream sep2(str); + irc::spacesepstream sep2(str, false); /* get the token */ if (str == "") { diff --git a/src/modules/m_flatfile_channels.cpp b/src/modules/m_flatfile_channels.cpp index 17d14f7d0..297ea1419 100644 --- a/src/modules/m_flatfile_channels.cpp +++ b/src/modules/m_flatfile_channels.cpp @@ -72,7 +72,7 @@ class DatabaseReader /* ready to parse the line */ if (str == "") return 0; std::string token; - irc::spacesepstream sep(str); + irc::spacesepstream sep(str, false); /* get first one */ /* malformed if it is not chaninfo */ if (!sep.GetToken (token) || token != "chaninfo") @@ -109,7 +109,7 @@ class DatabaseReader if (c2 == '\r') continue; str.push_back (c2); } - irc::spacesepstream sep2(str); + irc::spacesepstream sep2(str, false); /* get the token */ if (str == "") { diff --git a/src/modules/m_helpop.cpp b/src/modules/m_helpop.cpp index c145433bc..34cd8964d 100644 --- a/src/modules/m_helpop.cpp +++ b/src/modules/m_helpop.cpp @@ -87,7 +87,7 @@ class CommandHelpop : public Command } std::string value = iter->second; - irc::sepstream stream(value, '\n'); + irc::sepstream stream(value, '\n', false); std::string token = "*"; while (stream.GetToken(token)) diff --git a/src/testsuite.cpp b/src/testsuite.cpp index 3178a094e..3b008f130 100644 --- a/src/testsuite.cpp +++ b/src/testsuite.cpp @@ -138,7 +138,7 @@ static bool DoStreamTest(const char* expected[], T& stream) #define STREAMTEST(a,b,...) \ do { \ - a stream(b); \ + a stream b; \ static const char* expected[] = __VA_ARGS__; \ failed = !DoStreamTest(expected, stream) || failed; \ std::cout << std::endl; \ @@ -149,8 +149,12 @@ static bool DoCommaSepStreamTests() std::cout << "Comma sepstream tests" << std::endl << std::endl; bool failed = false; - STREAMTEST(irc::commasepstream, "this,is,a,comma,stream", { "this", "is", "a", "comma", "stream", NULL }); - STREAMTEST(irc::commasepstream, "with,lots,,of,,,commas", { "with", "lots", "", "of", "", "", "commas", NULL }); + STREAMTEST(irc::commasepstream, ("this,is,a,comma,stream", false), { "this", "is", "a", "comma", "stream", NULL }); + STREAMTEST(irc::commasepstream, ("with,lots,,of,,,commas", false), { "with", "lots", "", "of", "", "", "commas", NULL }); + STREAMTEST(irc::commasepstream, (",comma,at,the,beginning", false), { "", "comma", "at", "the", "beginning", NULL }); + STREAMTEST(irc::commasepstream, ("commas,at,the,end,,", false), { "commas", "at", "the", "end", "", "", NULL }); + STREAMTEST(irc::commasepstream, (",", false), { "", "", NULL }); + STREAMTEST(irc::commasepstream, ("", false), { "", NULL }); std::cout << "Result of comma sepstream tests:"; COUTFAILED(); @@ -162,7 +166,12 @@ static bool DoSpaceSepStreamTests() std::cout << "Space sepstream tests" << std::endl << std::endl; bool failed = false; - STREAMTEST(irc::spacesepstream, "this is a space stream", { "this", "is", "a", "space", "stream", NULL }); + STREAMTEST(irc::spacesepstream, ("this is a space stream", true), { "this", "is", "a", "space", "stream", NULL }); + STREAMTEST(irc::spacesepstream, ("with lots of spaces", true), { "with", "lots", "of", "spaces", NULL }); + STREAMTEST(irc::spacesepstream, (" space at the beginning", true), { "space", "at", "the", "beginning", NULL }); + STREAMTEST(irc::spacesepstream, ("spaces at the end ", true), { "spaces", "at", "the", "end", NULL }); + STREAMTEST(irc::spacesepstream, (" ", true), { NULL }); + STREAMTEST(irc::spacesepstream, ("", true), { NULL }); std::cout << "Result of space sepstream tests:"; COUTFAILED(); @@ -174,14 +183,14 @@ static bool DoTokenStreamTests() std::cout << "Token stream tests" << std::endl << std::endl; bool failed = false; - STREAMTEST(irc::tokenstream, "just some words and spaces", { "just", "some", "words", "and", "spaces", NULL }); - STREAMTEST(irc::tokenstream, ":not actually all one token", { ":not", "actually", "all", "one", "token", NULL }); - STREAMTEST(irc::tokenstream, "several small tokens :and one large one", { "several", "small", "tokens", "and one large one", NULL }); - STREAMTEST(irc::tokenstream, "with 3 tokens ", { "with", "3", "tokens", NULL }); - STREAMTEST(irc::tokenstream, "with a blank token at the end :", { "with", "a", "blank", "token", "at", "the", "end", "", NULL }); - STREAMTEST(irc::tokenstream, "with a space at the end : ", { "with", "a", "space", "at", "the", "end", " ", NULL }); - STREAMTEST(irc::tokenstream, "a :large token ending in a colon:", { "a", "large token ending in a colon:", NULL }); - STREAMTEST(irc::tokenstream, "several tokens with the last ending in a colon:", { "several", "tokens", "with", "the", "last", "ending", "in", "a", "colon:", NULL }); + STREAMTEST(irc::tokenstream, ("just some words and spaces"), { "just", "some", "words", "and", "spaces", NULL }); + STREAMTEST(irc::tokenstream, (":not actually all one token"), { ":not", "actually", "all", "one", "token", NULL }); + STREAMTEST(irc::tokenstream, ("several small tokens :and one large one"), { "several", "small", "tokens", "and one large one", NULL }); + STREAMTEST(irc::tokenstream, ("with 3 tokens "), { "with", "3", "tokens", NULL }); + STREAMTEST(irc::tokenstream, ("with a blank token at the end :"), { "with", "a", "blank", "token", "at", "the", "end", "", NULL }); + STREAMTEST(irc::tokenstream, ("with a space at the end : "), { "with", "a", "space", "at", "the", "end", " ", NULL }); + STREAMTEST(irc::tokenstream, ("a :large token ending in a colon:"), { "a", "large token ending in a colon:", NULL }); + STREAMTEST(irc::tokenstream, ("several tokens with the last ending in a colon:"), { "several", "tokens", "with", "the", "last", "ending", "in", "a", "colon:", NULL }); std::cout << "Result of token stream tests:"; COUTFAILED();