refactor: tr_strv*() util functions (#2123)

* feat: add tr_strv utils
This commit is contained in:
Charles Kerr
2021-11-09 18:13:47 -06:00
committed by GitHub
parent d8b57fe4dc
commit bdf1bb6d17
18 changed files with 314 additions and 291 deletions
+3 -3
View File
@@ -251,12 +251,12 @@ static void on_announce_done(
if (tr_variantDictFindStrView(&benc, TR_KEY_failure_reason, &sv))
{
response->errmsg = tr_strvdup(sv);
response->errmsg = tr_strvDup(sv);
}
if (tr_variantDictFindStrView(&benc, TR_KEY_warning_message, &sv))
{
response->warning = tr_strvdup(sv);
response->warning = tr_strvDup(sv);
}
if (tr_variantDictFindInt(&benc, TR_KEY_interval, &i))
@@ -271,7 +271,7 @@ static void on_announce_done(
if (tr_variantDictFindStrView(&benc, TR_KEY_tracker_id, &sv))
{
response->tracker_id_str = tr_strvdup(sv);
response->tracker_id_str = tr_strvDup(sv);
}
if (tr_variantDictFindInt(&benc, TR_KEY_complete, &i))
+1 -1
View File
@@ -253,7 +253,7 @@ static void trackerConstruct(tr_announcer* announcer, tr_tracker* tracker, tr_tr
{
memset(tracker, 0, sizeof(tr_tracker));
tracker->key = tr_announcerGetKey(inf->announce);
tracker->announce_url = tr_quark_new(tr_strvstrip(inf->announce));
tracker->announce_url = tr_quark_new(tr_strvStrip(inf->announce));
tracker->scrape_info = inf->scrape == nullptr ? nullptr : tr_announcerGetScrapeInfo(announcer, tr_quark_new(inf->scrape));
tracker->id = inf->id;
tracker->seederCount = -1;
+50
View File
@@ -275,3 +275,53 @@ void* tr_base64_decode_str(char const* input, size_t* output_length)
{
return tr_base64_decode(input, input == nullptr ? 0 : strlen(input), output_length);
}
/***
****
***/
static void tr_binary_to_hex(void const* vinput, void* voutput, size_t byte_length)
{
static char const hex[] = "0123456789abcdef";
auto const* input = static_cast<uint8_t const*>(vinput);
auto* output = static_cast<char*>(voutput);
/* go from back to front to allow for in-place conversion */
input += byte_length;
output += byte_length * 2;
*output = '\0';
while (byte_length-- > 0)
{
unsigned int const val = *(--input);
*(--output) = hex[val & 0xf];
*(--output) = hex[val >> 4];
}
}
void tr_sha1_to_hex(void* hex, void const* sha1)
{
tr_binary_to_hex(sha1, hex, SHA_DIGEST_LENGTH);
}
static void tr_hex_to_binary(void const* vinput, void* voutput, size_t byte_length)
{
static char const hex[] = "0123456789abcdef";
auto const* input = static_cast<uint8_t const*>(vinput);
auto* output = static_cast<uint8_t*>(voutput);
for (size_t i = 0; i < byte_length; ++i)
{
int const hi = strchr(hex, tolower(*input++)) - hex;
int const lo = strchr(hex, tolower(*input++)) - hex;
*output++ = (uint8_t)((hi << 4) | lo);
}
}
void tr_hex_to_sha1(void* sha1, void const* hex)
{
tr_hex_to_binary(hex, sha1, SHA_DIGEST_LENGTH);
}
+2 -9
View File
@@ -15,7 +15,6 @@
#include "transmission.h" /* SHA_DIGEST_LENGTH */
#include "tr-macros.h"
#include "utils.h" // tr_binary_to_hex(), tr_hex_to_binary()
/**
*** @addtogroup utils Utilities
@@ -178,18 +177,12 @@ void* tr_base64_decode_str(char const* input, size_t* output_length) TR_GNUC_MAL
/**
* @brief Wrapper around tr_binary_to_hex() for SHA_DIGEST_LENGTH.
*/
static inline void tr_sha1_to_hex(void* hex, void const* sha1)
{
tr_binary_to_hex(sha1, hex, SHA_DIGEST_LENGTH);
}
void tr_sha1_to_hex(void* hex, void const* sha1);
/**
* @brief Wrapper around tr_hex_to_binary() for SHA_DIGEST_LENGTH.
*/
static inline void tr_hex_to_sha1(void* sha1, void const* hex)
{
tr_hex_to_binary(hex, sha1, SHA_DIGEST_LENGTH);
}
void tr_hex_to_sha1(void* sha1, void const* hex);
/** @} */
+3 -3
View File
@@ -126,16 +126,16 @@ tr_magnet_info* tr_magnetParse(std::string_view magnet_link)
{
if (key == "dn"sv)
{
display_name = tr_strvdup(tr_urlPercentDecode(value));
display_name = tr_strvDup(tr_urlPercentDecode(value));
}
else if (key == "tr"sv || key.find("tr.") == 0)
{
// "tr." explanation @ https://trac.transmissionbt.com/ticket/3341
tr.push_back(tr_strvdup(tr_urlPercentDecode(value)));
tr.push_back(tr_strvDup(tr_urlPercentDecode(value)));
}
else if (key == "ws"sv)
{
ws.push_back(tr_strvdup(tr_urlPercentDecode(value)));
ws.push_back(tr_strvDup(tr_urlPercentDecode(value)));
}
else if (key == "xt"sv)
{
+6 -12
View File
@@ -368,21 +368,15 @@ static void getFileInfo(
/* build the path list */
tr_variantInitList(uninitialized_path, 0);
if (strlen(file->filename) > offset)
auto filename = std::string_view{ file->filename };
if (std::size(filename) > offset)
{
char* filename = tr_strdup(file->filename + offset);
char* walk = filename;
char const* token = nullptr;
while ((token = tr_strsep(&walk, TR_PATH_DELIMITER_STR)) != nullptr)
filename.remove_prefix(offset);
auto token = std::string_view{};
while (tr_strvSep(&filename, &token, TR_PATH_DELIMITER))
{
if (!tr_str_is_empty(token))
{
tr_variantListAddStr(uninitialized_path, token);
}
tr_variantListAddStr(uninitialized_path, token);
}
tr_free(filename);
}
}
+10 -10
View File
@@ -127,7 +127,7 @@ bool tr_metainfoAppendSanitizedPathComponent(std::string& out, std::string_view
auto constexpr ensure_legal_char = [](auto ch)
{
auto constexpr Banned = std::string_view{ "<>:\"/\\|?*" };
auto const banned = Banned.find(ch) != Banned.npos || (unsigned char)ch < 0x20;
auto const banned = tr_strvContains(Banned, ch) || (unsigned char)ch < 0x20;
return banned ? '_' : ch;
};
auto const old_out_len = std::size(out);
@@ -311,7 +311,7 @@ static char* tr_convertAnnounceToScrape(std::string_view url)
// some torrents with UDP announce URLs don't have /announce
else if (url.find("udp:"sv) == 0)
{
scrape = tr_strvdup(url);
scrape = tr_strvDup(url);
}
return scrape;
@@ -348,13 +348,13 @@ static char const* getannounce(tr_info* inf, tr_variant* meta)
{
if (tr_variantGetStrView(tr_variantListChild(tier, j), &url))
{
url = tr_strvstrip(url);
url = tr_strvStrip(url);
if (tr_urlIsValidTracker(url))
{
tr_tracker_info* t = trackers + trackerCount;
t->tier = validTiers;
t->announce = tr_strvdup(url);
t->announce = tr_strvDup(url);
t->scrape = tr_convertAnnounceToScrape(url);
t->id = trackerCount;
@@ -381,13 +381,13 @@ static char const* getannounce(tr_info* inf, tr_variant* meta)
/* Regular announce value */
if (trackerCount == 0 && tr_variantDictFindStrView(meta, TR_KEY_announce, &url))
{
url = tr_strvstrip(url);
url = tr_strvStrip(url);
if (tr_urlIsValidTracker(url))
{
trackers = tr_new0(tr_tracker_info, 1);
trackers[trackerCount].tier = 0;
trackers[trackerCount].announce = tr_strvdup(url);
trackers[trackerCount].announce = tr_strvDup(url);
trackers[trackerCount].scrape = tr_convertAnnounceToScrape(url);
trackers[trackerCount].id = 0;
trackerCount++;
@@ -413,7 +413,7 @@ static char const* getannounce(tr_info* inf, tr_variant* meta)
*/
static char* fix_webseed_url(tr_info const* inf, std::string_view url)
{
url = tr_strvstrip(url);
url = tr_strvStrip(url);
if (!tr_urlIsValid(url))
{
@@ -425,7 +425,7 @@ static char* fix_webseed_url(tr_info const* inf, std::string_view url)
return tr_strdup_printf("%" TR_PRIsv "/", TR_PRIsv_ARG(url));
}
return tr_strvdup(url);
return tr_strvDup(url);
}
static void geturllist(tr_info* inf, tr_variant* meta)
@@ -514,8 +514,8 @@ static char const* tr_metainfoParseImpl(
{
tr_free(inf->name);
tr_free(inf->originalName);
inf->name = tr_strvdup(sv);
inf->originalName = tr_strvdup(sv);
inf->name = tr_strvDup(sv);
inf->originalName = tr_strvDup(sv);
}
if (inf->name == nullptr)
+1
View File
@@ -23,6 +23,7 @@
#include "crypto.h"
#include "net.h" /* tr_address */
#include "peer-socket.h"
#include "utils.h" // tr_time()
class tr_peerIo;
struct Bandwidth;
+4 -4
View File
@@ -396,7 +396,7 @@ static uint64_t loadName(tr_variant* dict, tr_torrent* tor)
if (name != tr_torrentName(tor))
{
tr_free(tor->info.name);
tor->info.name = tr_strvdup(name);
tor->info.name = tr_strvDup(name);
}
return TR_FR_NAME;
@@ -445,7 +445,7 @@ static uint64_t loadFilenames(tr_variant* dict, tr_torrent* tor)
if (tr_variantGetStrView(tr_variantListChild(list, i), &sv) && !std::empty(sv))
{
tr_free(files[i].name);
files[i].name = tr_strvdup(sv);
files[i].name = tr_strvDup(sv);
files[i].is_renamed = true;
}
}
@@ -792,7 +792,7 @@ static uint64_t loadFromFile(tr_torrent* tor, uint64_t fieldsToLoad, bool* didRe
{
bool const is_current_dir = tor->currentDir == tor->downloadDir;
tr_free(tor->downloadDir);
tor->downloadDir = tr_strvdup(sv);
tor->downloadDir = tr_strvDup(sv);
if (is_current_dir)
{
@@ -807,7 +807,7 @@ static uint64_t loadFromFile(tr_torrent* tor, uint64_t fieldsToLoad, bool* didRe
{
bool const is_current_dir = tor->currentDir == tor->incompleteDir;
tr_free(tor->incompleteDir);
tor->incompleteDir = tr_strvdup(sv);
tor->incompleteDir = tr_strvDup(sv);
if (is_current_dir)
{
+1 -1
View File
@@ -1149,7 +1149,7 @@ tr_rpc_server* tr_rpcInit(tr_session* session, tr_variant* settings)
}
else
{
s->url = tr_strvdup(url);
s->url = tr_strvDup(url);
}
key = TR_KEY_rpc_whitelist_enabled;
+5 -5
View File
@@ -962,13 +962,13 @@ static char const* setLabels(tr_torrent* tor, tr_variant* list)
continue;
}
label = tr_strvstrip(label);
label = tr_strvStrip(label);
if (std::empty(label))
{
return "labels cannot be empty";
}
if (label.find(',') != label.npos)
if (tr_strvContains(label, ','))
{
return "labels cannot contain comma (,) character";
}
@@ -1115,7 +1115,7 @@ static char const* addTrackerUrls(tr_torrent* tor, tr_variant* urls)
if (tr_variantGetStrView(val, &announce) && tr_urlIsValidTracker(announce) && !hasAnnounceUrl(trackers, n, announce))
{
trackers[n].tier = ++tier; /* add a new tier */
trackers[n].announce = tr_strvdup(announce);
trackers[n].announce = tr_strvDup(announce);
++n;
changed = true;
}
@@ -1158,7 +1158,7 @@ static char const* replaceTrackers(tr_torrent* tor, tr_variant* urls)
pos >= 0)
{
tr_free(trackers[pos].announce);
trackers[pos].announce = tr_strvdup(newval);
trackers[pos].announce = tr_strvDup(newval);
changed = true;
}
}
@@ -1820,7 +1820,7 @@ static char const* torrentAdd(tr_session* session, tr_variant* args_in, tr_varia
}
else
{
char* fname = tr_strstrip(tr_strdup(filename));
char* fname = tr_strdup(filename);
if (fname == nullptr)
{
+1 -1
View File
@@ -2936,7 +2936,7 @@ static void deleteLocalData(tr_torrent* tor, tr_fileFunc func)
/* go from the bottom up */
for (auto const& file : files)
{
char* walk = tr_strvdup(file);
char* walk = tr_strvDup(file);
while (tr_sys_path_exists(walk, nullptr) && !tr_sys_path_is_same(tmpdir, walk, nullptr))
{
+39 -138
View File
@@ -415,7 +415,7 @@ char* evbuffer_free_to_str(struct evbuffer* buf, size_t* result_len)
return ret;
}
char* tr_strvdup(std::string_view in)
char* tr_strvDup(std::string_view in)
{
auto const n = std::size(in);
auto* const ret = tr_new(char, n + 1);
@@ -427,7 +427,7 @@ char* tr_strvdup(std::string_view in)
char* tr_strndup(void const* vin, size_t len)
{
auto const* const in = static_cast<char const*>(vin);
return in == nullptr ? nullptr : tr_strvdup({ in, len == TR_BAD_SIZE ? strlen(in) : len });
return in == nullptr ? nullptr : tr_strvDup({ in, len == TR_BAD_SIZE ? strlen(in) : len });
}
char* tr_strdup(void const* in)
@@ -546,45 +546,7 @@ int tr_strcmp0(char const* str1, char const* str2)
*****
****/
/* https://bugs.launchpad.net/percona-patches/+bug/526863/+attachment/1160199/+files/solaris_10_fix.patch */
char* tr_strsep(char** str, char const* delims)
{
#ifdef HAVE_STRSEP
return strsep(str, delims);
#else
char* token;
if (*str == nullptr) /* no more tokens */
{
return nullptr;
}
token = *str;
while (**str != '\0')
{
if (strchr(delims, **str) != nullptr)
{
**str = '\0';
(*str)++;
return token;
}
(*str)++;
}
/* there is not another token */
*str = nullptr;
return token;
#endif
}
std::string_view tr_strvstrip(std::string_view str)
std::string_view tr_strvStrip(std::string_view str)
{
auto constexpr test = [](auto ch)
{
@@ -600,32 +562,6 @@ std::string_view tr_strvstrip(std::string_view str)
return str;
}
char* tr_strstrip(char* str)
{
if (str != nullptr)
{
size_t len = strlen(str);
while (len != 0 && isspace(str[len - 1]))
{
--len;
}
size_t pos = 0;
while (pos < len && isspace(str[pos]))
{
++pos;
}
len -= pos;
memmove(str, str + pos, len);
str[len] = '\0';
}
return str;
}
bool tr_str_has_suffix(char const* str, char const* suffix)
{
if (str == nullptr)
@@ -763,42 +699,6 @@ double tr_getRatio(uint64_t numerator, uint64_t denominator)
return TR_RATIO_NA;
}
void tr_binary_to_hex(void const* vinput, void* voutput, size_t byte_length)
{
static char const hex[] = "0123456789abcdef";
auto const* input = static_cast<uint8_t const*>(vinput);
auto* output = static_cast<char*>(voutput);
/* go from back to front to allow for in-place conversion */
input += byte_length;
output += byte_length * 2;
*output = '\0';
while (byte_length-- > 0)
{
unsigned int const val = *(--input);
*(--output) = hex[val & 0xf];
*(--output) = hex[val >> 4];
}
}
void tr_hex_to_binary(void const* vinput, void* voutput, size_t byte_length)
{
static char const hex[] = "0123456789abcdef";
auto const* input = static_cast<uint8_t const*>(vinput);
auto* output = static_cast<uint8_t*>(voutput);
for (size_t i = 0; i < byte_length; ++i)
{
int const hi = strchr(hex, tolower(*input++)) - hex;
int const lo = strchr(hex, tolower(*input++)) - hex;
*output++ = (uint8_t)((hi << 4) | lo);
}
}
/***
****
***/
@@ -838,16 +738,19 @@ static char* strip_non_utf8(char const* in, size_t inlen)
static char* to_utf8(char const* in, size_t inlen)
{
char* ret = nullptr;
#ifdef HAVE_ICONV
char const* encodings[] = { "CURRENT", "ISO-8859-15" };
size_t const buflen = inlen * 4 + 10;
char* out = tr_new(char, buflen);
for (size_t i = 0; ret == nullptr && i < TR_N_ELEMENTS(encodings); ++i)
auto constexpr Encodings = std::array<char const*, 2>{ "CURRENT", "ISO-8859-15" };
for (auto const* test_encoding : Encodings)
{
iconv_t cd = iconv_open("UTF-8", test_encoding);
if (cd == (iconv_t)-1) // NOLINT(performance-no-int-to-ptr)
{
continue;
}
#ifdef ICONV_SECOND_ARGUMENT_IS_CONST
auto const* inbuf = in;
#else
@@ -856,18 +759,13 @@ static char* to_utf8(char const* in, size_t inlen)
char* outbuf = out;
size_t inbytesleft = inlen;
size_t outbytesleft = buflen;
char const* test_encoding = encodings[i];
iconv_t cd = iconv_open("UTF-8", test_encoding);
if (cd != (iconv_t)-1) // NOLINT(performance-no-int-to-ptr)
auto const rv = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
iconv_close(cd);
if (rv != size_t(-1))
{
if (iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft) != (size_t)-1)
{
ret = tr_strndup(out, buflen - outbytesleft);
}
iconv_close(cd);
char* const ret = tr_strndup(out, buflen - outbytesleft);
tr_free(out);
return ret;
}
}
@@ -875,12 +773,7 @@ static char* to_utf8(char const* in, size_t inlen)
#endif
if (ret == nullptr)
{
ret = strip_non_utf8(in, inlen);
}
return ret;
return strip_non_utf8(in, inlen);
}
char* tr_utf8clean(std::string_view str)
@@ -891,6 +784,24 @@ char* tr_utf8clean(std::string_view str)
return ret;
}
static bool tr_strvUtf8Validate(std::string_view sv)
{
return tr_utf8_validate(std::data(sv), std::size(sv), nullptr);
}
std::string tr_strvUtf8Clean(std::string_view sv)
{
if (tr_strvUtf8Validate(sv))
{
return std::string{ sv };
}
auto* const tmp = to_utf8(std::data(sv), std::size(sv));
auto ret = std::string{ tmp ? tmp : "" };
tr_free(tmp);
return ret;
}
#ifdef _WIN32
char* tr_win32_native_to_utf8(wchar_t const* text, int text_size)
@@ -1161,24 +1072,14 @@ static bool parseNumberSection(std::string_view str, number_range& range)
std::vector<int> tr_parseNumberRange(std::string_view str)
{
auto values = std::set<int>{};
for (;;)
auto token = std::string_view{};
auto range = number_range{};
while (tr_strvSep(&str, &token, ',') && parseNumberSection(token, range))
{
auto const delim = str.find(',');
auto range = number_range{};
if (!parseNumberSection(str.substr(0, delim), range))
{
break;
}
for (auto i = range.low; i <= range.high; ++i)
{
values.insert(i);
}
if (delim == std::string_view::npos)
{
break;
}
str.remove_prefix(delim + 1);
}
return { std::begin(values), std::end(values) };
+38 -13
View File
@@ -208,8 +208,6 @@ void* tr_memdup(void const* src, size_t byteCount);
*/
char* tr_strndup(void const* in, size_t len) TR_GNUC_MALLOC;
char* tr_strvdup(std::string_view) TR_GNUC_MALLOC;
/**
* @brief make a newly-allocated copy of a string
* @param in is a void* so that callers can pass in both signed & unsigned without a cast
@@ -246,12 +244,6 @@ int tr_snprintf(void* buf, size_t buflen, char const* fmt, ...) TR_GNUC_PRINTF(3
@param errnum the error number to describe */
char const* tr_strerror(int errnum);
/** @brief strips leading and trailing whitspace from a string
@return the stripped string */
char* tr_strstrip(char* str);
std::string_view tr_strvstrip(std::string_view str);
/** @brief Returns true if the string ends with the specified case-insensitive suffix */
bool tr_str_has_suffix(char const* str, char const* suffix);
@@ -261,16 +253,49 @@ char const* tr_memmem(char const* haystack, size_t haystack_len, char const* nee
/** @brief Portability wrapper for strcasestr() that uses the system implementation if available */
char const* tr_strcasestr(char const* haystack, char const* needle);
/** @brief Portability wrapper for strsep() that uses the system implementation if available */
char* tr_strsep(char** str, char const* delim);
/***
**** std::string_view utils
***/
template<typename T>
constexpr bool tr_strvContains(std::string_view sv, T key) // c++23
{
return sv.find(key) != sv.npos;
}
constexpr bool tr_strvStartsWith(std::string_view sv, std::string_view key) // c++20
{
return std::size(key) <= std::size(sv) && sv.substr(0, std::size(key)) == key;
}
constexpr bool tr_strvEndsWith(std::string_view sv, std::string_view key) // c++20
{
return std::size(key) <= std::size(sv) && sv.substr(std::size(sv) - std::size(key)) == key;
}
constexpr std::string_view tr_strvSep(std::string_view* sv, char delim)
{
auto pos = sv->find(delim);
auto const ret = sv->substr(0, pos);
sv->remove_prefix(pos != sv->npos ? pos + 1 : std::size(*sv));
return ret;
}
constexpr bool tr_strvSep(std::string_view* sv, std::string_view* token, char delim)
{
return !std::empty((*token = tr_strvSep(sv, delim)));
}
std::string_view tr_strvStrip(std::string_view sv);
char* tr_strvDup(std::string_view) TR_GNUC_MALLOC;
std::string tr_strvUtf8Clean(std::string_view sv);
/***
****
***/
void tr_binary_to_hex(void const* input, void* output, size_t byte_length) TR_GNUC_NONNULL(1, 2);
void tr_hex_to_binary(void const* input, void* output, size_t byte_length) TR_GNUC_NONNULL(1, 2);
/** @brief return TR_RATIO_NA, TR_RATIO_INF, or a number in [0..1]
@return TR_RATIO_NA, TR_RATIO_INF, or a number in [0..1] */
double tr_getRatio(uint64_t numerator, uint64_t denominator);
+16 -40
View File
@@ -175,7 +175,7 @@ void tr_http_escape(struct evbuffer* out, std::string_view str, bool escape_rese
for (auto& ch : str)
{
if ((UnescapedChars.find(ch) != std::string_view::npos) || (ReservedChars.find(ch) && !escape_reserved))
if (tr_strvContains(UnescapedChars, ch) || (tr_strvContains(ReservedChars, ch) && !escape_reserved))
{
evbuffer_add_printf(out, "%c", ch);
}
@@ -276,7 +276,7 @@ bool urlCharsAreValid(std::string_view url)
};
return !std::empty(url) &&
std::all_of(std::begin(url), std::end(url), [&ValidChars](auto ch) { return ValidChars.find(ch) != ValidChars.npos; });
std::all_of(std::begin(url), std::end(url), [&ValidChars](auto ch) { return tr_strvContains(ValidChars, ch); });
}
bool tr_isValidTrackerScheme(std::string_view scheme)
@@ -289,7 +289,7 @@ bool tr_isValidTrackerScheme(std::string_view scheme)
std::optional<tr_url_parsed_t> tr_urlParse(std::string_view url)
{
url = tr_strvstrip(url);
url = tr_strvStrip(url);
if (!urlCharsAreValid(url))
{
@@ -300,46 +300,33 @@ std::optional<tr_url_parsed_t> tr_urlParse(std::string_view url)
parsed.full = url;
// scheme
auto key = ":"sv;
auto pos = url.find(key);
if (pos == std::string_view::npos || pos == 0)
parsed.scheme = tr_strvSep(&url, ':');
if (std::empty(parsed.scheme))
{
return {};
}
parsed.scheme = url.substr(0, pos);
url.remove_prefix(pos + std::size(key));
// authority
// The authority component is preceded by a double slash ("//") and is
// terminated by the next slash ("/"), question mark ("?"), or number
// sign ("#") character, or by the end of the URI.
key = "//"sv;
pos = url.find(key);
if (pos == 0)
auto key = "//"sv;
if (tr_strvStartsWith(url, key))
{
url.remove_prefix(pos + std::size(key));
pos = url.find_first_of("/?#");
url.remove_prefix(std::size(key));
auto pos = url.find_first_of("/?#");
parsed.authority = url.substr(0, pos);
url = pos == url.npos ? ""sv : url.substr(pos);
// host
key = ":"sv;
pos = parsed.authority.find(key);
parsed.host = pos == std::string_view::npos ? parsed.authority : parsed.authority.substr(0, pos);
if (std::empty(parsed.host))
{
return {};
}
// port
parsed.portstr = pos == std::string_view::npos ? getPortForScheme(parsed.scheme) :
parsed.authority.substr(pos + std::size(key));
auto remain = parsed.authority;
parsed.host = tr_strvSep(&remain, ':');
parsed.portstr = !std::empty(remain) ? remain : getPortForScheme(parsed.scheme);
parsed.port = parsePort(parsed.portstr);
}
// The path is terminated by the first question mark ("?") or
// number sign ("#") character, or by the end of the URI.
pos = url.find_first_of("?#");
auto pos = url.find_first_of("?#");
parsed.path = url.substr(0, pos);
url = pos == url.npos ? ""sv : url.substr(pos);
@@ -381,20 +368,9 @@ bool tr_urlIsValid(std::string_view url)
tr_url_query_view::iterator& tr_url_query_view::iterator::operator++()
{
// find the next key/value delimiter
auto pos = remain.find('&');
auto const pair = remain.substr(0, pos);
remain = pos == remain.npos ? ""sv : remain.substr(pos + 1);
if (std::empty(pair))
{
keyval.first = keyval.second = remain = ""sv;
return *this;
}
// split it into key and value
pos = pair.find('=');
keyval.first = pair.substr(0, pos);
keyval.second = pos == pair.npos ? ""sv : pair.substr(pos + 1);
auto pair = tr_strvSep(&remain, '&');
keyval.first = tr_strvSep(&pair, '=');
keyval.second = pair;
return *this;
}
+3 -1
View File
@@ -9,9 +9,11 @@
#include <QDir>
#include <QFile>
#include <libtransmission/crypto-utils.h> // tr_base64_encode()
#include <libtransmission/transmission.h>
#include <libtransmission/crypto-utils.h> // tr_base64_encode()
#include <libtransmission/utils.h>
#include "AddData.h"
#include "Utils.h"
+126 -37
View File
@@ -49,34 +49,94 @@ TEST_F(UtilsTest, trStripPositionalArgs)
EXPECT_STREQ(expected, out);
}
TEST_F(UtilsTest, trStrstrip)
TEST_F(UtilsTest, trStrvContains)
{
auto* in = tr_strdup(" test ");
auto* out = tr_strstrip(in);
EXPECT_EQ(in, out);
EXPECT_STREQ("test", out);
tr_free(in);
EXPECT_FALSE(tr_strvContains("a test is this"sv, "TEST"sv));
EXPECT_FALSE(tr_strvContains("test"sv, "testt"sv));
EXPECT_FALSE(tr_strvContains("test"sv, "this is a test"sv));
EXPECT_TRUE(tr_strvContains(" test "sv, "tes"sv));
EXPECT_TRUE(tr_strvContains(" test"sv, "test"sv));
EXPECT_TRUE(tr_strvContains("a test is this"sv, "test"sv));
EXPECT_TRUE(tr_strvContains("test "sv, "test"sv));
EXPECT_TRUE(tr_strvContains("test"sv, ""sv));
EXPECT_TRUE(tr_strvContains("test"sv, "t"sv));
EXPECT_TRUE(tr_strvContains("test"sv, "te"sv));
EXPECT_TRUE(tr_strvContains("test"sv, "test"sv));
EXPECT_TRUE(tr_strvContains("this is a test"sv, "test"sv));
EXPECT_TRUE(tr_strvContains(""sv, ""sv));
}
in = tr_strdup(" test test ");
out = tr_strstrip(in);
EXPECT_EQ(in, out);
EXPECT_STREQ("test test", out);
tr_free(in);
TEST_F(UtilsTest, trStrvStartsWith)
{
EXPECT_FALSE(tr_strvStartsWith(""sv, "this is a string"sv));
EXPECT_FALSE(tr_strvStartsWith("this is a strin"sv, "this is a string"sv));
EXPECT_FALSE(tr_strvStartsWith("this is a strin"sv, "this is a string"sv));
EXPECT_FALSE(tr_strvStartsWith("this is a string"sv, " his is a string"sv));
EXPECT_FALSE(tr_strvStartsWith("this is a string"sv, "his is a string"sv));
EXPECT_FALSE(tr_strvStartsWith("this is a string"sv, "string"sv));
EXPECT_TRUE(tr_strvStartsWith(""sv, ""sv));
EXPECT_TRUE(tr_strvStartsWith("this is a string"sv, ""sv));
EXPECT_TRUE(tr_strvStartsWith("this is a string"sv, "this "sv));
EXPECT_TRUE(tr_strvStartsWith("this is a string"sv, "this is"sv));
EXPECT_TRUE(tr_strvStartsWith("this is a string"sv, "this"sv));
}
/* strstrip */
in = tr_strdup("test");
out = tr_strstrip(in);
EXPECT_EQ(in, out);
EXPECT_STREQ("test", out);
tr_free(in);
TEST_F(UtilsTest, trStrvEndsWith)
{
EXPECT_FALSE(tr_strvEndsWith(""sv, "string"sv));
EXPECT_FALSE(tr_strvEndsWith("this is a string"sv, "alphabet"sv));
EXPECT_FALSE(tr_strvEndsWith("this is a string"sv, "strin"sv));
EXPECT_FALSE(tr_strvEndsWith("this is a string"sv, "this is"sv));
EXPECT_FALSE(tr_strvEndsWith("this is a string"sv, "this"sv));
EXPECT_FALSE(tr_strvEndsWith("tring"sv, "string"sv));
EXPECT_TRUE(tr_strvEndsWith(""sv, ""sv));
EXPECT_TRUE(tr_strvEndsWith("this is a string"sv, " string"sv));
EXPECT_TRUE(tr_strvEndsWith("this is a string"sv, ""sv));
EXPECT_TRUE(tr_strvEndsWith("this is a string"sv, "a string"sv));
EXPECT_TRUE(tr_strvEndsWith("this is a string"sv, "g"sv));
EXPECT_TRUE(tr_strvEndsWith("this is a string"sv, "string"sv));
}
EXPECT_EQ(""sv, tr_strvstrip(" "sv));
EXPECT_EQ("test test"sv, tr_strvstrip(" test test "sv));
EXPECT_EQ("test"sv, tr_strvstrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvstrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvstrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvstrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvstrip("test"sv));
TEST_F(UtilsTest, trStrvSep)
{
auto constexpr Delim = ',';
auto sv = "token1,token2,token3"sv;
EXPECT_EQ("token1"sv, tr_strvSep(&sv, Delim));
EXPECT_EQ("token2"sv, tr_strvSep(&sv, Delim));
EXPECT_EQ("token3"sv, tr_strvSep(&sv, Delim));
EXPECT_EQ(""sv, tr_strvSep(&sv, Delim));
sv = " token1,token2"sv;
EXPECT_EQ(" token1"sv, tr_strvSep(&sv, Delim));
EXPECT_EQ("token2"sv, tr_strvSep(&sv, Delim));
sv = "token1;token2"sv;
EXPECT_EQ("token1;token2"sv, tr_strvSep(&sv, Delim));
EXPECT_EQ(""sv, tr_strvSep(&sv, Delim));
sv = ""sv;
EXPECT_EQ(""sv, tr_strvSep(&sv, Delim));
}
TEST_F(UtilsTest, trStrvStrip)
{
EXPECT_EQ(""sv, tr_strvStrip(" "sv));
EXPECT_EQ("test test"sv, tr_strvStrip(" test test "sv));
EXPECT_EQ("test"sv, tr_strvStrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvStrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvStrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvStrip(" test "sv));
EXPECT_EQ("test"sv, tr_strvStrip("test"sv));
}
TEST_F(UtilsTest, trStrvDup)
{
auto constexpr Key = "this is a test"sv;
char* str = tr_strvDup(Key);
EXPECT_NE(nullptr, str);
EXPECT_EQ(Key, str);
tr_free(str);
}
TEST_F(UtilsTest, trBuildpath)
@@ -124,7 +184,48 @@ TEST_F(UtilsTest, trUtf8clean)
EXPECT_TRUE(tr_utf8_validate(out.c_str(), out.size(), nullptr));
}
TEST_F(UtilsTest, numbers)
TEST_F(UtilsTest, trStrvUtf8Clean)
{
auto in = "hello world"sv;
auto out = tr_strvUtf8Clean(in);
EXPECT_EQ(in, out);
in = "hello world"sv;
out = tr_strvUtf8Clean(in.substr(0, 5));
EXPECT_EQ("hello"sv, out);
// this version is not utf-8 (but cp866)
in = "\x92\xE0\xE3\xA4\xAD\xAE \xA1\xEB\xE2\xEC \x81\xAE\xA3\xAE\xAC"sv;
out = tr_strvUtf8Clean(in);
EXPECT_TRUE(std::size(out) == 17 || std::size(out) == 33);
EXPECT_TRUE(tr_utf8_validate(out.c_str(), out.size(), nullptr));
// same string, but utf-8 clean
in = "Трудно быть Богом"sv;
out = tr_strvUtf8Clean(in);
EXPECT_NE(nullptr, out.data());
EXPECT_TRUE(tr_utf8_validate(out.c_str(), out.size(), nullptr));
EXPECT_EQ(in, out);
// https://trac.transmissionbt.com/ticket/6064
// TODO: It seems like that bug was not fixed so much as we just let
// strlen() solve the problem for us; however, it's probably better to
// wait until https://github.com/transmission/transmission/issues/612
// is resolved before revisiting this.
in = "\xF4\x00\x81\x82"sv;
out = tr_strvUtf8Clean(in);
EXPECT_NE(nullptr, out.data());
EXPECT_TRUE(out.size() == 1 || out.size() == 2);
EXPECT_TRUE(tr_utf8_validate(out.c_str(), out.size(), nullptr));
in = "\xF4\x33\x81\x82"sv;
out = tr_strvUtf8Clean(in);
EXPECT_NE(nullptr, out.data());
EXPECT_TRUE(out.size() == 4 || out.size() == 7);
EXPECT_TRUE(tr_utf8_validate(out.c_str(), out.size(), nullptr));
}
TEST_F(UtilsTest, trParseNumberRange)
{
auto const tostring = [](std::vector<int> const& v)
{
@@ -190,18 +291,6 @@ TEST_F(UtilsTest, trMemmem)
EXPECT_EQ(nullptr, tr_memmem(needle.data(), needle.size(), haystack.data(), haystack.size()));
}
TEST_F(UtilsTest, trBinaryHex)
{
auto const hex_in = std::string{ "fb5ef5507427b17e04b69cef31fa3379b456735a" };
auto binary = std::array<uint8_t, SHA_DIGEST_LENGTH>{};
tr_hex_to_binary(hex_in.data(), binary.data(), hex_in.size() / 2);
auto hex_out = std::array<uint8_t, SHA_DIGEST_LENGTH * 2 + 1>{};
tr_binary_to_hex(binary.data(), hex_out.data(), 20);
EXPECT_EQ(hex_in, reinterpret_cast<char const*>(hex_out.data()));
}
TEST_F(UtilsTest, array)
{
auto array = std::array<size_t, 10>{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+5 -13
View File
@@ -674,7 +674,7 @@ static void addDays(tr_variant* args, tr_quark const key, char const* arg)
}
}
static void addLabels(tr_variant* args, char const* arg)
static void addLabels(tr_variant* args, std::string_view comma_delimited_labels)
{
tr_variant* labels;
if (!tr_variantDictFindList(args, TR_KEY_labels, &labels))
@@ -682,19 +682,11 @@ static void addLabels(tr_variant* args, char const* arg)
labels = tr_variantDictAddList(args, TR_KEY_labels, 10);
}
char* argcpy = tr_strdup(arg);
char* const tmp = argcpy; /* save copied string start pointer to free later */
char* token;
while ((token = tr_strsep(&argcpy, ",")) != nullptr)
auto label = std::string_view{};
while (tr_strvSep(&comma_delimited_labels, &label, ','))
{
tr_strstrip(token);
if (!tr_str_is_empty(token))
{
tr_variantListAddStr(labels, token);
}
tr_variantListAddStr(labels, label);
}
tr_free(tmp);
}
static void addFiles(tr_variant* args, tr_quark const key, char const* arg)
@@ -2759,7 +2751,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
switch (c)
{
case 'L':
addLabels(args, optarg);
addLabels(args, optarg ? optarg : "");
break;
case 712: