2012-08-26 17:26:17 +02:00
|
|
|
/*
|
|
|
|
|
2016-01-18 00:57:46 +01:00
|
|
|
Copyright (c) 2012-2016, Arvid Norberg
|
2012-08-26 17:26:17 +02:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "libtorrent/config.hpp"
|
|
|
|
#include "libtorrent/string_util.hpp"
|
2012-08-27 07:39:34 +02:00
|
|
|
#include "libtorrent/random.hpp"
|
2015-04-21 02:23:00 +02:00
|
|
|
#include "libtorrent/error_code.hpp"
|
|
|
|
#include "libtorrent/parse_url.hpp"
|
2016-02-08 08:01:25 +01:00
|
|
|
#include "libtorrent/address.hpp"
|
2016-08-26 18:36:09 +02:00
|
|
|
#include "libtorrent/assert.hpp"
|
2015-04-21 02:23:00 +02:00
|
|
|
|
2015-03-15 05:25:54 +01:00
|
|
|
#include <cstdlib> // for malloc
|
2017-09-17 11:52:04 +02:00
|
|
|
#include <cstring> // for strlen
|
2017-08-22 22:20:18 +02:00
|
|
|
#include <algorithm> // for search
|
2012-08-26 17:26:17 +02:00
|
|
|
|
2017-04-12 19:00:57 +02:00
|
|
|
namespace libtorrent {
|
2012-08-26 17:26:17 +02:00
|
|
|
|
2015-03-15 00:10:20 +01:00
|
|
|
// lexical_cast's result depends on the locale. We need
|
|
|
|
// a well defined result
|
2016-06-18 20:01:38 +02:00
|
|
|
std::array<char, 4 + std::numeric_limits<std::int64_t>::digits10>
|
2017-01-17 03:51:49 +01:00
|
|
|
to_string(std::int64_t const n)
|
2015-03-15 00:10:20 +01:00
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
std::array<char, 4 + std::numeric_limits<std::int64_t>::digits10> ret;
|
2015-03-15 00:10:20 +01:00
|
|
|
char *p = &ret.back();
|
|
|
|
*p = '\0';
|
2017-09-17 11:52:04 +02:00
|
|
|
// we want "un" to be the absolute value
|
|
|
|
// since the absolute of INT64_MIN cannot be represented by a signed
|
|
|
|
// int64, we calculate the abs in unsigned space
|
|
|
|
std::uint64_t un = n < 0
|
|
|
|
? std::numeric_limits<std::uint64_t>::max() - std::uint64_t(n) + 1
|
|
|
|
: std::uint64_t(n);
|
2015-03-15 00:10:20 +01:00
|
|
|
do {
|
|
|
|
*--p = '0' + un % 10;
|
|
|
|
un /= 10;
|
|
|
|
} while (un);
|
|
|
|
if (n < 0) *--p = '-';
|
2017-01-17 03:51:49 +01:00
|
|
|
std::memmove(ret.data(), p, std::size_t(&ret.back() - p + 1));
|
2015-03-15 00:10:20 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-26 17:26:17 +02:00
|
|
|
bool is_alpha(char c)
|
|
|
|
{
|
|
|
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_print(char c)
|
|
|
|
{
|
|
|
|
return c >= 32 && c < 127;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_space(char c)
|
|
|
|
{
|
2017-07-12 18:19:46 +02:00
|
|
|
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
2012-08-26 17:26:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
char to_lower(char c)
|
|
|
|
{
|
|
|
|
return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c;
|
|
|
|
}
|
|
|
|
|
|
|
|
int split_string(char const** tags, int buf_size, char* in)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
char* i = in;
|
|
|
|
for (;*i; ++i)
|
|
|
|
{
|
|
|
|
if (!is_print(*i) || is_space(*i))
|
|
|
|
{
|
|
|
|
*i = 0;
|
|
|
|
if (ret == buf_size) return ret;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (i == in || i[-1] == 0)
|
|
|
|
{
|
|
|
|
tags[ret++] = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool string_begins_no_case(char const* s1, char const* s2)
|
|
|
|
{
|
2016-08-26 18:36:09 +02:00
|
|
|
TORRENT_ASSERT(s1 != nullptr);
|
|
|
|
TORRENT_ASSERT(s2 != nullptr);
|
|
|
|
|
2012-08-26 17:26:17 +02:00
|
|
|
while (*s1 != 0)
|
|
|
|
{
|
|
|
|
if (to_lower(*s1) != to_lower(*s2)) return false;
|
|
|
|
++s1;
|
|
|
|
++s2;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-19 13:34:55 +01:00
|
|
|
bool string_equal_no_case(string_view s1, string_view s2)
|
2012-08-26 17:26:17 +02:00
|
|
|
{
|
2017-02-19 13:34:55 +01:00
|
|
|
if (s1.size() != s2.size()) return false;
|
|
|
|
return std::equal(s1.begin(), s1.end(), s2.begin()
|
|
|
|
, [] (char const c1, char const c2)
|
|
|
|
{ return to_lower(c1) == to_lower(c2); });
|
2012-08-26 17:26:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// generate a url-safe random string
|
|
|
|
void url_random(char* begin, char* end)
|
|
|
|
{
|
|
|
|
// http-accepted characters:
|
|
|
|
// excluding ', since some buggy trackers don't support that
|
|
|
|
static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz-_.!~*()";
|
|
|
|
|
|
|
|
// the random number
|
|
|
|
while (begin != end)
|
2016-08-15 22:17:13 +02:00
|
|
|
*begin++ = printable[random(sizeof(printable) - 2)];
|
2012-08-26 17:26:17 +02:00
|
|
|
}
|
|
|
|
|
2016-08-26 18:36:09 +02:00
|
|
|
bool string_ends_with(string_view s1, string_view s2)
|
|
|
|
{
|
|
|
|
return s1.size() >= s2.size() && std::equal(s2.rbegin(), s2.rend(), s1.rbegin());
|
|
|
|
}
|
|
|
|
|
2017-08-22 22:20:18 +02:00
|
|
|
int search(span<char const> src, span<char const> target)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(!src.empty());
|
|
|
|
TORRENT_ASSERT(!target.empty());
|
|
|
|
TORRENT_ASSERT(target.size() >= src.size());
|
|
|
|
TORRENT_ASSERT(target.size() < std::size_t(std::numeric_limits<int>::max()));
|
|
|
|
|
|
|
|
auto it = std::search(target.begin(), target.end(), src.begin(), src.end());
|
|
|
|
|
|
|
|
// no complete sync
|
|
|
|
if (it == target.end()) return -1;
|
|
|
|
return static_cast<int>(it - target.begin());
|
|
|
|
}
|
|
|
|
|
2012-08-26 17:26:17 +02:00
|
|
|
char* allocate_string_copy(char const* str)
|
|
|
|
{
|
2016-07-09 22:26:26 +02:00
|
|
|
if (str == nullptr) return nullptr;
|
2017-01-17 03:51:49 +01:00
|
|
|
std::size_t const len = std::strlen(str);
|
2017-09-17 12:50:09 +02:00
|
|
|
char* tmp = new char[len + 1];
|
|
|
|
std::copy(str, str + len, tmp);
|
2016-12-09 23:17:27 +01:00
|
|
|
tmp[len] = '\0';
|
2012-08-26 17:26:17 +02:00
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
2016-02-08 08:01:25 +01:00
|
|
|
std::string print_listen_interfaces(std::vector<listen_interface_t> const& in)
|
|
|
|
{
|
|
|
|
std::string ret;
|
2017-03-12 16:34:42 +01:00
|
|
|
for (auto const& i : in)
|
2016-02-08 08:01:25 +01:00
|
|
|
{
|
2017-06-01 10:17:01 +02:00
|
|
|
if (!ret.empty()) ret += ',';
|
2016-02-08 08:01:25 +01:00
|
|
|
|
2016-02-09 00:11:00 +01:00
|
|
|
#if TORRENT_USE_IPV6
|
2016-02-08 08:01:25 +01:00
|
|
|
error_code ec;
|
2017-03-12 16:34:42 +01:00
|
|
|
address_v6::from_string(i.device, ec);
|
2016-02-08 08:01:25 +01:00
|
|
|
if (!ec)
|
|
|
|
{
|
|
|
|
// IPv6 addresses must be wrapped in square brackets
|
2017-06-01 10:17:01 +02:00
|
|
|
ret += '[';
|
2017-03-12 16:34:42 +01:00
|
|
|
ret += i.device;
|
2017-06-01 10:17:01 +02:00
|
|
|
ret += ']';
|
2016-02-08 08:01:25 +01:00
|
|
|
}
|
|
|
|
else
|
2016-02-09 00:11:00 +01:00
|
|
|
#endif
|
2016-02-08 08:01:25 +01:00
|
|
|
{
|
2017-03-12 16:34:42 +01:00
|
|
|
ret += i.device;
|
2016-02-08 08:01:25 +01:00
|
|
|
}
|
2017-06-01 10:17:01 +02:00
|
|
|
ret += ':';
|
2017-03-12 16:34:42 +01:00
|
|
|
ret += to_string(i.port).data();
|
2017-06-01 10:17:01 +02:00
|
|
|
if (i.ssl) ret += 's';
|
2016-02-08 08:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-05-26 19:34:13 +02:00
|
|
|
// this parses the string that's used as the listen_interfaces setting.
|
2014-07-06 21:18:00 +02:00
|
|
|
// it is a comma-separated list of IP or device names with ports. For
|
|
|
|
// example: "eth0:6881,eth1:6881" or "127.0.0.1:6881"
|
2016-09-20 17:24:24 +02:00
|
|
|
std::vector<listen_interface_t> parse_listen_interfaces(std::string const& in)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-09-20 17:24:24 +02:00
|
|
|
std::vector<listen_interface_t> out;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
std::string::size_type start = 0;
|
|
|
|
|
|
|
|
while (start < in.size())
|
|
|
|
{
|
|
|
|
// skip leading spaces
|
2016-06-12 08:00:10 +02:00
|
|
|
while (start < in.size() && is_space(in[start]))
|
2014-07-06 21:18:00 +02:00
|
|
|
++start;
|
|
|
|
|
2016-09-20 17:24:24 +02:00
|
|
|
if (start == in.size()) return out;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-12 08:00:10 +02:00
|
|
|
listen_interface_t iface;
|
|
|
|
iface.ssl = false;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-12 08:00:10 +02:00
|
|
|
#if !TORRENT_USE_IPV6
|
|
|
|
bool ipv6 = false;
|
|
|
|
#endif
|
|
|
|
if (in[start] == '[')
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-06-12 08:00:10 +02:00
|
|
|
#if !TORRENT_USE_IPV6
|
|
|
|
ipv6 = true;
|
|
|
|
#endif
|
|
|
|
++start;
|
|
|
|
// IPv6 address
|
|
|
|
while (start < in.size() && in[start] != ']')
|
|
|
|
iface.device += in[start++];
|
2016-02-08 08:01:25 +01:00
|
|
|
|
2016-06-12 08:00:10 +02:00
|
|
|
// skip to the colon
|
|
|
|
while (start < in.size() && in[start] != ':')
|
|
|
|
++start;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// consume device name
|
|
|
|
while (start < in.size() && !is_space(in[start]) && in[start] != ':')
|
|
|
|
iface.device += in[start++];
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-12 08:00:10 +02:00
|
|
|
// skip spaces
|
|
|
|
while (start < in.size() && is_space(in[start]))
|
|
|
|
++start;
|
|
|
|
|
2016-09-20 17:24:24 +02:00
|
|
|
if (start == in.size() || in[start] != ':') return out;
|
2016-06-12 08:00:10 +02:00
|
|
|
++start; // skip colon
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-12 08:00:10 +02:00
|
|
|
// skip spaces
|
|
|
|
while (start < in.size() && is_space(in[start]))
|
|
|
|
++start;
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-06-12 08:00:10 +02:00
|
|
|
// consume port
|
|
|
|
std::string port;
|
|
|
|
while (start < in.size() && is_digit(in[start]) && in[start] != ',')
|
|
|
|
port += in[start++];
|
|
|
|
|
2016-09-05 21:17:12 +02:00
|
|
|
if (port.empty() || port.size() > 5)
|
|
|
|
{
|
|
|
|
iface.port = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
iface.port = std::atoi(port.c_str());
|
2016-09-20 17:11:24 +02:00
|
|
|
if (iface.port < 0 || iface.port > 65535) iface.port = -1;
|
2016-09-05 21:17:12 +02:00
|
|
|
}
|
2016-06-12 08:00:10 +02:00
|
|
|
|
|
|
|
// skip spaces
|
|
|
|
while (start < in.size() && is_space(in[start]))
|
|
|
|
++start;
|
|
|
|
|
|
|
|
// consume potential SSL 's'
|
|
|
|
if (start < in.size() && in[start] == 's')
|
|
|
|
{
|
|
|
|
iface.ssl = true;
|
|
|
|
++start;
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip until end or comma
|
|
|
|
while (start < in.size() && in[start] != ',')
|
|
|
|
++start;
|
|
|
|
|
|
|
|
if (iface.port >= 0
|
|
|
|
#if !TORRENT_USE_IPV6
|
|
|
|
&& ipv6 == false
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
2016-02-08 08:01:25 +01:00
|
|
|
out.push_back(iface);
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2016-06-12 08:00:10 +02:00
|
|
|
// skip the comma
|
|
|
|
if (start < in.size() && in[start] == ',')
|
|
|
|
++start;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
2016-09-20 17:24:24 +02:00
|
|
|
|
|
|
|
return out;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2016-08-21 18:15:19 +02:00
|
|
|
// this parses the string that's used as the listen_interfaces setting.
|
2014-07-06 21:18:00 +02:00
|
|
|
// it is a comma-separated list of IP or device names with ports. For
|
|
|
|
// example: "eth0:6881,eth1:6881" or "127.0.0.1:6881"
|
|
|
|
void parse_comma_separated_string_port(std::string const& in
|
2016-09-02 22:42:55 +02:00
|
|
|
, std::vector<std::pair<std::string, int>>& out)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
out.clear();
|
|
|
|
|
|
|
|
std::string::size_type start = 0;
|
|
|
|
std::string::size_type end = 0;
|
|
|
|
|
|
|
|
while (start < in.size())
|
|
|
|
{
|
|
|
|
// skip leading spaces
|
|
|
|
while (start < in.size()
|
|
|
|
&& is_space(in[start]))
|
|
|
|
++start;
|
|
|
|
|
|
|
|
end = in.find_first_of(',', start);
|
|
|
|
if (end == std::string::npos) end = in.size();
|
|
|
|
|
|
|
|
std::string::size_type colon = in.find_last_of(':', end);
|
|
|
|
|
|
|
|
if (colon != std::string::npos && colon > start)
|
|
|
|
{
|
|
|
|
int port = atoi(in.substr(colon + 1, end - colon - 1).c_str());
|
|
|
|
|
|
|
|
// skip trailing spaces
|
|
|
|
std::string::size_type soft_end = colon;
|
|
|
|
while (soft_end > start
|
|
|
|
&& is_space(in[soft_end-1]))
|
|
|
|
--soft_end;
|
|
|
|
|
|
|
|
// in case this is an IPv6 address, strip off the square brackets
|
|
|
|
// to make it more easily parseable into an ip::address
|
|
|
|
if (in[start] == '[') ++start;
|
|
|
|
if (soft_end > start && in[soft_end-1] == ']') --soft_end;
|
|
|
|
|
|
|
|
out.push_back(std::make_pair(in.substr(start, soft_end - start), port));
|
|
|
|
}
|
|
|
|
|
|
|
|
start = end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void parse_comma_separated_string(std::string const& in, std::vector<std::string>& out)
|
|
|
|
{
|
|
|
|
out.clear();
|
|
|
|
|
|
|
|
std::string::size_type start = 0;
|
|
|
|
std::string::size_type end = 0;
|
|
|
|
|
|
|
|
while (start < in.size())
|
|
|
|
{
|
|
|
|
// skip leading spaces
|
|
|
|
while (start < in.size()
|
|
|
|
&& is_space(in[start]))
|
|
|
|
++start;
|
|
|
|
|
|
|
|
end = in.find_first_of(',', start);
|
|
|
|
if (end == std::string::npos) end = in.size();
|
|
|
|
|
|
|
|
// skip trailing spaces
|
|
|
|
std::string::size_type soft_end = end;
|
|
|
|
while (soft_end > start
|
|
|
|
&& is_space(in[soft_end-1]))
|
|
|
|
--soft_end;
|
|
|
|
|
|
|
|
out.push_back(in.substr(start, soft_end - start));
|
|
|
|
start = end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-02 00:17:21 +02:00
|
|
|
std::pair<string_view, string_view> split_string(string_view last, char const sep)
|
2014-05-05 00:40:30 +02:00
|
|
|
{
|
2017-09-02 00:17:21 +02:00
|
|
|
if (last.empty()) return {{}, {}};
|
|
|
|
|
|
|
|
std::size_t pos = 0;
|
|
|
|
if (last[0] == '"' && sep != '"')
|
2014-05-05 00:40:30 +02:00
|
|
|
{
|
2017-09-02 00:17:21 +02:00
|
|
|
for (auto const c : last.substr(1))
|
|
|
|
{
|
|
|
|
++pos;
|
|
|
|
if (c == '"') break;
|
|
|
|
}
|
2014-05-05 00:40:30 +02:00
|
|
|
}
|
2017-09-02 00:17:21 +02:00
|
|
|
std::size_t found_sep = 0;
|
|
|
|
for (char const c : last.substr(pos))
|
2014-05-05 00:40:30 +02:00
|
|
|
{
|
2017-09-02 00:17:21 +02:00
|
|
|
if (c == sep)
|
|
|
|
{
|
|
|
|
found_sep = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++pos;
|
2014-05-05 00:40:30 +02:00
|
|
|
}
|
2017-09-02 00:17:21 +02:00
|
|
|
return {last.substr(0, pos), last.substr(pos + found_sep)};
|
2014-05-05 00:40:30 +02:00
|
|
|
}
|
|
|
|
|
2015-04-21 02:23:00 +02:00
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
|
|
|
|
bool is_i2p_url(std::string const& url)
|
|
|
|
{
|
2016-06-20 17:32:06 +02:00
|
|
|
using std::ignore;
|
2015-04-21 02:23:00 +02:00
|
|
|
std::string hostname;
|
|
|
|
error_code ec;
|
2016-06-20 17:32:06 +02:00
|
|
|
std::tie(ignore, ignore, hostname, ignore, ignore)
|
2015-04-21 02:23:00 +02:00
|
|
|
= parse_url_components(url, ec);
|
2016-08-26 18:36:09 +02:00
|
|
|
return string_ends_with(hostname, ".i2p");
|
2015-04-21 02:23:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-08-15 11:59:13 +02:00
|
|
|
std::size_t string_hash_no_case::operator()(std::string const& s) const
|
|
|
|
{
|
2017-08-16 22:46:47 +02:00
|
|
|
std::size_t ret = 5381;
|
2017-08-15 11:59:13 +02:00
|
|
|
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
|
2017-08-16 22:46:47 +02:00
|
|
|
ret = (ret * 33) ^ static_cast<std::size_t>(to_lower(*i));
|
2017-08-15 11:59:13 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool string_eq_no_case::operator()(std::string const& lhs, std::string const& rhs) const
|
|
|
|
{
|
|
|
|
if (lhs.size() != rhs.size()) return false;
|
|
|
|
|
|
|
|
std::string::const_iterator s1 = lhs.begin();
|
|
|
|
std::string::const_iterator s2 = rhs.begin();
|
|
|
|
|
|
|
|
while (s1 != lhs.end() && s2 != rhs.end())
|
|
|
|
{
|
|
|
|
if (to_lower(*s1) != to_lower(*s2)) return false;
|
|
|
|
++s1;
|
|
|
|
++s2;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2012-08-26 17:26:17 +02:00
|
|
|
}
|