refactor: remove tr_win32_utf8_to_native_ex() (#3590)

This commit is contained in:
Charles Kerr 2022-08-05 14:16:25 -05:00 committed by GitHub
parent 8b983b3d1c
commit 4bc1589c5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 188 additions and 378 deletions

View File

@ -5,8 +5,8 @@
#include <algorithm>
#include <array>
#include <cstring>
#include <ctype.h> /* isalpha() */
#include <cctype> // for isalpha()
#include <string>
#include <string_view>
#include <shlobj.h> /* SHCreateDirectoryEx() */
@ -145,88 +145,61 @@ static bool is_valid_path(std::string_view path)
return path.find_first_of("<>:\"|?*"sv) == path.npos;
}
static wchar_t* path_to_native_path(char const* path)
namespace
{
if (path == nullptr)
namespace path_to_native_path_helpers
{
auto path_to_fixed_native_path(std::string_view path)
{
auto wide_path = tr_win32_utf8_to_native(path);
// convert '/' to '\'
static auto constexpr Convert = [](wchar_t wch)
{
return nullptr;
}
return wch == L'/' ? L'\\' : wch;
};
std::transform(std::begin(wide_path), std::end(wide_path), std::begin(wide_path), Convert);
/* Extending maximum path length limit up to ~32K. See "Naming Files, Paths, and Namespaces"
(https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx) for more info */
bool const is_relative = tr_sys_path_is_relative(path);
bool const is_unc = is_unc_path(path);
/* `-2` for UNC since we overwrite existing prefix slashes */
int const extra_chars_before = is_relative ?
0 :
(is_unc ? TR_N_ELEMENTS(native_unc_path_prefix) - 2 : TR_N_ELEMENTS(native_local_path_prefix));
/* TODO (?): TR_ASSERT(!is_relative); */
int real_result_size = 0;
wchar_t* const wide_path = tr_win32_utf8_to_native_ex(path, -1, extra_chars_before, 0, &real_result_size);
if (wide_path == nullptr)
// squash multiple consecutive separators into one to avoid ERROR_INVALID_NAME
static auto constexpr Equal = [](wchar_t a, wchar_t b)
{
return nullptr;
}
real_result_size += extra_chars_before;
/* Relative paths cannot be used with "\\?\" prefixes. This also means that relative paths are
limited to ~260 chars... but we should rarely work with relative paths in the first place */
if (!is_relative)
{
if (is_unc)
{
/* UNC path: "\\server\share" -> "\\?\UNC\server\share" */
memcpy(wide_path, native_unc_path_prefix, sizeof(native_unc_path_prefix));
}
else
{
/* Local path: "C:" -> "\\?\C:" */
memcpy(wide_path, native_local_path_prefix, sizeof(native_local_path_prefix));
}
}
/* Automatic '/' to '\' conversion is disabled for "\\?\"-prefixed paths */
wchar_t* p = wide_path + extra_chars_before;
while ((p = wcschr(p, L'/')) != nullptr)
{
*p++ = L'\\';
}
/* Squash multiple consecutive path separators into one to avoid ERROR_INVALID_NAME */
wchar_t* first_conseq_sep = wide_path + extra_chars_before;
while ((first_conseq_sep = std::wcsstr(first_conseq_sep, L"\\\\")) != nullptr)
{
wchar_t const* last_conseq_sep = first_conseq_sep + 1;
while (*(last_conseq_sep + 1) == L'\\')
{
++last_conseq_sep;
}
std::copy_n(last_conseq_sep, real_result_size - (last_conseq_sep - wide_path) + 1, first_conseq_sep);
real_result_size -= last_conseq_sep - first_conseq_sep;
}
return a == b && a == L'\\';
};
auto const tmp = wide_path;
wide_path.clear();
std::unique_copy(std::begin(tmp), std::end(tmp), std::back_inserter(wide_path), Equal);
return wide_path;
}
static std::wstring path_to_native_path_wstr(std::string_view path)
} // namespace path_to_native_path_helpers
} // namespace
/* Extending maximum path length limit up to ~32K. See "Naming Files, Paths, and Namespaces"
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx for more info */
static auto path_to_native_path(std::string_view path)
{
if (auto* const rawptr = path_to_native_path(tr_pathbuf{ path }); rawptr != nullptr)
using namespace path_to_native_path_helpers;
if (is_unc_path(path))
{
auto ret = std::wstring{ rawptr };
tr_free(rawptr);
return ret;
// UNC path: "\\server\share" -> "\\?\UNC\server\share"
path.remove_prefix(2); // remove path's UNC prefix slashes
auto wide_path = path_to_fixed_native_path(path);
wide_path.insert(0, NativeUncPathPrefix);
return wide_path;
}
return {};
if (!tr_sys_path_is_relative(path))
{
// local path: "C:" -> "\\?\C:"
auto wide_path = path_to_fixed_native_path(path);
wide_path.insert(0, NativeLocalPathPrefix);
return wide_path;
}
return path_to_fixed_native_path(path);
}
static std::string native_path_to_path(std::wstring_view wide_path)
@ -257,7 +230,7 @@ static tr_sys_file_t open_file(std::string_view path, DWORD access, DWORD dispos
{
tr_sys_file_t ret = TR_BAD_SYS_FILE;
if (auto const wide_path = path_to_native_path_wstr(path); !std::empty(wide_path))
if (auto const wide_path = path_to_native_path(path); !std::empty(wide_path))
{
ret = CreateFileW(
wide_path.c_str(),
@ -281,7 +254,7 @@ static bool create_dir(std::string_view path, int flags, int /*permissions*/, bo
{
bool ret;
DWORD error_code = ERROR_SUCCESS;
auto const wide_path = path_to_native_path_wstr(path);
auto const wide_path = path_to_native_path(path);
if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
{
@ -374,7 +347,7 @@ bool tr_sys_path_exists(char const* path, tr_error** error)
bool ret = false;
HANDLE handle = INVALID_HANDLE_VALUE;
if (auto const wide_path = path_to_native_path_wstr(path); !std::empty(wide_path))
if (auto const wide_path = path_to_native_path(path); !std::empty(wide_path))
{
DWORD attributes = GetFileAttributesW(wide_path.c_str());
@ -407,7 +380,7 @@ bool tr_sys_path_exists(char const* path, tr_error** error)
std::optional<tr_sys_path_info> tr_sys_path_get_info(std::string_view path, int flags, tr_error** error)
{
if (auto const wide_path = path_to_native_path_wstr(path); std::empty(wide_path))
if (auto const wide_path = path_to_native_path(path); std::empty(wide_path))
{
// do nothing
}
@ -469,8 +442,8 @@ bool tr_sys_path_is_same(char const* path1, char const* path2, tr_error** error)
HANDLE handle2 = INVALID_HANDLE_VALUE;
BY_HANDLE_FILE_INFORMATION fi1, fi2;
auto const wide_path1 = path_to_native_path_wstr(path1);
auto const wide_path2 = path_to_native_path_wstr(path2);
auto const wide_path1 = path_to_native_path(path1);
auto const wide_path2 = path_to_native_path(path2);
if (std::empty(wide_path1) || std::empty(wide_path2))
{
@ -517,7 +490,7 @@ std::string tr_sys_path_resolve(std::string_view path, tr_error** error)
{
auto ret = std::string{};
if (auto const wide_path = path_to_native_path_wstr(path); !std::empty(wide_path))
if (auto const wide_path = path_to_native_path(path); !std::empty(wide_path))
{
if (auto const handle = CreateFileW(
wide_path.c_str(),
@ -726,8 +699,8 @@ bool tr_sys_path_rename(char const* src_path, char const* dst_path, tr_error** e
TR_ASSERT(dst_path != nullptr);
bool ret = false;
auto const wide_src_path = path_to_native_path_wstr(src_path);
auto const wide_dst_path = path_to_native_path_wstr(dst_path);
auto const wide_src_path = path_to_native_path(src_path);
auto const wide_dst_path = path_to_native_path(dst_path);
if (!std::empty(wide_src_path) && !std::empty(wide_dst_path))
{
@ -766,8 +739,8 @@ bool tr_sys_path_copy(char const* src_path, char const* dst_path, tr_error** err
TR_ASSERT(src_path != nullptr);
TR_ASSERT(dst_path != nullptr);
auto const wide_src_path = path_to_native_path_wstr(src_path);
auto const wide_dst_path = path_to_native_path_wstr(dst_path);
auto const wide_src_path = path_to_native_path(src_path);
auto const wide_dst_path = path_to_native_path(dst_path);
if (std::empty(wide_src_path) || std::empty(wide_dst_path))
{
set_system_error(error, ERROR_INVALID_PARAMETER);
@ -791,7 +764,7 @@ bool tr_sys_path_remove(char const* path, tr_error** error)
bool ret = false;
if (auto const wide_path = path_to_native_path_wstr(path); !std::empty(wide_path))
if (auto const wide_path = path_to_native_path(path); !std::empty(wide_path))
{
DWORD const attributes = GetFileAttributesW(wide_path.c_str());
@ -1367,7 +1340,7 @@ tr_sys_dir_t tr_sys_dir_open(char const* path, tr_error** error)
static_assert(TR_BAD_SYS_DIR == nullptr, "values should match");
#endif
auto pattern = path_to_native_path_wstr(path);
auto pattern = path_to_native_path(path);
if (std::empty(pattern))
{
set_system_error(error, GetLastError());

View File

@ -16,10 +16,12 @@
#include <fmt/format.h>
#include "transmission.h"
#include "error.h"
#include "subprocess.h"
#include "tr-assert.h"
#include "tr-macros.h"
#include "tr-strbuf.h"
#include "utils.h"
using namespace std::literals;
@ -50,7 +52,7 @@ static void set_system_error(tr_error** error, int code, std::string_view what)
static bool tr_spawn_async_in_child(
char const* const* cmd,
std::map<std::string_view, std::string_view> const& env,
char const* work_dir,
std::string_view work_dir,
int pipe_fd)
{
auto key_sz = std::string{};
@ -67,7 +69,7 @@ static bool tr_spawn_async_in_child(
}
}
if (work_dir != nullptr && chdir(work_dir) == -1)
if (!std::empty(work_dir) && chdir(tr_pathbuf{ work_dir }) == -1)
{
goto FAIL;
}
@ -120,7 +122,7 @@ static bool tr_spawn_async_in_parent(int pipe_fd, tr_error** error)
bool tr_spawn_async(
char const* const* cmd,
std::map<std::string_view, std::string_view> const& env,
char const* work_dir,
std::string_view work_dir,
tr_error** error)
{
static bool sigchld_handler_set = false;

View File

@ -9,9 +9,11 @@
#include <cstring>
#include <cwchar>
#include <map>
#include <iterator>
#include <string_view>
#include <fmt/format.h>
#include <fmt/xchar.h> // for wchar_t support
#include <windows.h>
@ -23,13 +25,16 @@
using namespace std::literals;
enum tr_app_type
namespace
{
TR_APP_TYPE_EXE,
TR_APP_TYPE_BATCH
enum class tr_app_type
{
EXE,
BATCH
};
static void set_system_error(tr_error** error, DWORD code, std::string_view what)
void set_system_error(tr_error** error, DWORD code, std::string_view what)
{
if (error == nullptr)
{
@ -46,206 +51,97 @@ static void set_system_error(tr_error** error, DWORD code, std::string_view what
}
}
static void append_to_env_block(wchar_t** env_block, size_t* env_block_len, wchar_t const* part, size_t part_len)
// "The sort is case-insensitive, Unicode order, without regard to locale" © MSDN
class WStrICompare
{
*env_block = tr_renew(wchar_t, *env_block, *env_block_len + part_len + 1);
wmemcpy(*env_block + *env_block_len, part, part_len);
*env_block_len += part_len;
}
static bool parse_env_block_part(wchar_t const* part, size_t* full_len, size_t* name_len)
{
TR_ASSERT(part != nullptr);
auto const* const equals_pos = wcschr(part, L'=');
if (equals_pos == nullptr)
public:
[[nodiscard]] auto compare(std::wstring_view a, std::wstring_view b) const noexcept // <=>
{
/* Invalid part */
return false;
}
int diff = wcsnicmp(std::data(a), std::data(b), std::min(std::size(a), std::size(b)));
ptrdiff_t const my_name_len = equals_pos - part;
if (my_name_len > SIZE_MAX)
{
/* Invalid part */
return false;
}
if (full_len != nullptr)
{
/* Includes terminating '\0' */
*full_len = wcslen(part) + 1;
}
if (name_len != nullptr)
{
*name_len = (size_t)my_name_len;
}
return true;
}
static int compare_wide_strings_ci(wchar_t const* lhs, size_t lhs_len, wchar_t const* rhs, size_t rhs_len)
{
int diff = wcsnicmp(lhs, rhs, std::min(lhs_len, rhs_len));
if (diff == 0)
{
diff = lhs_len < rhs_len ? -1 : (lhs_len > rhs_len ? 1 : 0);
}
return diff;
}
static int compare_env_part_names(void const* vlhs, void const* vrhs)
{
int ret = 0;
auto const* const* const lhs = reinterpret_cast<wchar_t const* const*>(vlhs);
auto const* const* const rhs = reinterpret_cast<wchar_t const* const*>(vrhs);
size_t lhs_part_len;
size_t lhs_name_len;
if (parse_env_block_part(*lhs, &lhs_part_len, &lhs_name_len))
{
size_t rhs_part_len;
size_t rhs_name_len;
if (parse_env_block_part(*rhs, &rhs_part_len, &rhs_name_len))
if (diff == 0)
{
ret = compare_wide_strings_ci(*lhs, lhs_name_len, *rhs, rhs_name_len);
diff = std::size(a) < std::size(b) ? -1 : (std::size(a) > std::size(b) ? 1 : 0);
}
return diff;
}
[[nodiscard]] auto operator()(std::wstring_view a, std::wstring_view b) const noexcept // <
{
return compare(a, b) < 0;
}
};
using SortedWideEnv = std::map<std::wstring, std::wstring, WStrICompare>;
/*
* Var1=Value1\0
* Var2=Value2\0
* Var3=Value3\0
* ...
* VarN=ValueN\0\0
*/
auto to_env_string(SortedWideEnv const& wide_env)
{
auto ret = std::vector<wchar_t>{};
for (auto const& [key, val] : wide_env)
{
fmt::format_to(std::back_inserter(ret), FMT_STRING(L"{:s}={:s}"), key, val);
ret.insert(std::end(ret), L'\0');
}
ret.insert(std::end(ret), L'\0');
return ret;
}
static wchar_t** to_wide_env(std::map<std::string_view, std::string_view> const& env)
/*
* Var1=Value1\0
* Var2=Value2\0
* Var3=Value3\0
* ...
* VarN=ValueN\0\0
*/
auto parse_env_string(wchar_t const* env)
{
auto const part_count = std::size(env);
wchar_t** const wide_env = tr_new(wchar_t*, part_count + 1);
auto sorted = SortedWideEnv{};
int i = 0;
for (auto const& [key, val] : env)
for (;;)
{
auto const line = fmt::format(FMT_STRING("{:s}={:s}"), key, val);
wide_env[i++] = tr_win32_utf8_to_native(std::data(line), std::size(line));
}
wide_env[i] = nullptr;
TR_ASSERT(i == part_count);
/* "The sort is case-insensitive, Unicode order, without regard to locale" © MSDN */
qsort(wide_env, part_count, sizeof(wchar_t*), &compare_env_part_names);
return wide_env;
}
static void tr_free_ptrv(void* const* p)
{
if (p == nullptr)
{
return;
}
while (*p != nullptr)
{
tr_free(*p);
++p;
}
}
static bool create_env_block(std::map<std::string_view, std::string_view> const& env, wchar_t** env_block, tr_error** error)
{
wchar_t** wide_env = to_wide_env(env);
if (wide_env == nullptr)
{
*env_block = nullptr;
return true;
}
wchar_t* const old_env_block = GetEnvironmentStringsW();
if (old_env_block == nullptr)
{
set_system_error(error, GetLastError(), "Call to GetEnvironmentStrings()");
return false;
}
*env_block = nullptr;
wchar_t const* old_part = old_env_block;
size_t env_block_len = 0;
for (size_t i = 0; wide_env[i] != nullptr; ++i)
{
wchar_t const* const part = wide_env[i];
size_t part_len;
size_t name_len;
if (!parse_env_block_part(part, &part_len, &name_len))
auto const line = std::wstring_view{ env };
if (std::empty(line))
{
continue;
break;
}
while (*old_part != L'\0')
if (auto const pos = line.find(L'='); pos != std::string_view::npos)
{
size_t old_part_len;
size_t old_name_len;
if (!parse_env_block_part(old_part, &old_part_len, &old_name_len))
{
continue;
}
int const name_diff = compare_wide_strings_ci(old_part, old_name_len, part, name_len);
if (name_diff < 0)
{
append_to_env_block(env_block, &env_block_len, old_part, old_part_len);
}
if (name_diff <= 0)
{
old_part += old_part_len;
}
if (name_diff >= 0)
{
break;
}
sorted.insert_or_assign(std::wstring{ line.substr(0, pos) }, std::wstring{ line.substr(pos + 1) });
}
append_to_env_block(env_block, &env_block_len, part, part_len);
env += std::size(line) + 1 /*'\0'*/;
}
while (*old_part != L'\0')
{
size_t old_part_len;
if (!parse_env_block_part(old_part, &old_part_len, nullptr))
{
continue;
}
append_to_env_block(env_block, &env_block_len, old_part, old_part_len);
old_part += old_part_len;
}
(*env_block)[env_block_len] = '\0';
FreeEnvironmentStringsW(old_env_block);
tr_free_ptrv((void* const*)wide_env);
tr_free(wide_env);
return true;
return sorted;
}
static void append_argument(char** arguments, char const* argument)
auto get_current_env()
{
auto env = SortedWideEnv{};
if (auto* pwch = GetEnvironmentStringsW(); pwch != nullptr)
{
env = parse_env_string(pwch);
FreeEnvironmentStringsW(pwch);
}
return env;
}
void append_argument(char** arguments, char const* argument)
{
size_t arguments_len = *arguments != nullptr ? strlen(*arguments) : 0u;
size_t const argument_len = strlen(argument);
@ -304,7 +200,7 @@ static void append_argument(char** arguments, char const* argument)
*(dst++) = '\0';
}
static bool contains_batch_metachars(char const* text)
bool contains_batch_metachars(char const* text)
{
/* First part - chars explicitly documented by `cmd.exe /?` as "special" */
return strpbrk(
@ -313,26 +209,28 @@ static bool contains_batch_metachars(char const* text)
"%!^\"") != nullptr;
}
static enum tr_app_type get_app_type(char const* app)
auto get_app_type(char const* app)
{
if (tr_str_has_suffix(app, ".cmd") || tr_str_has_suffix(app, ".bat"))
auto const lower = tr_strlower(app);
if (tr_strvEndsWith(lower, ".cmd") || tr_strvEndsWith(lower, ".bat"))
{
return TR_APP_TYPE_BATCH;
return tr_app_type::BATCH;
}
/* TODO: Support other types? */
return TR_APP_TYPE_EXE;
return tr_app_type::EXE;
}
static void append_app_launcher_arguments(enum tr_app_type app_type, char** args)
void append_app_launcher_arguments(tr_app_type app_type, char** args)
{
switch (app_type)
{
case TR_APP_TYPE_EXE:
case tr_app_type::EXE:
break;
case TR_APP_TYPE_BATCH:
case tr_app_type::BATCH:
append_argument(args, "cmd.exe");
append_argument(args, "/d");
append_argument(args, "/e:off");
@ -342,64 +240,65 @@ static void append_app_launcher_arguments(enum tr_app_type app_type, char** args
break;
default:
TR_ASSERT_MSG(false, fmt::format(FMT_STRING("unsupported application type {:d}"), app_type));
TR_ASSERT_MSG(false, "unsupported application type");
break;
}
}
static bool construct_cmd_line(char const* const* cmd, wchar_t** cmd_line)
std::wstring construct_cmd_line(char const* const* cmd)
{
enum tr_app_type const app_type = get_app_type(cmd[0]);
auto const app_type = get_app_type(cmd[0]);
char* args = nullptr;
size_t arg_count = 0;
bool ret = false;
append_app_launcher_arguments(app_type, &args);
for (size_t i = 0; cmd[i] != nullptr; ++i)
{
if (app_type == TR_APP_TYPE_BATCH && i > 0 && contains_batch_metachars(cmd[i]))
if (app_type == tr_app_type::BATCH && i > 0 && contains_batch_metachars(cmd[i]))
{
/* FIXME: My attempts to escape them one or another way didn't lead to anything good so far */
goto cleanup;
tr_free(args);
args = nullptr;
break;
}
append_argument(&args, cmd[i]);
++arg_count;
}
*cmd_line = args != nullptr ? tr_win32_utf8_to_native(args, -1) : nullptr;
if (args != nullptr)
{
auto cmd_line = tr_win32_utf8_to_native(args);
tr_free(args);
return cmd_line;
}
ret = true;
cleanup:
tr_free(args);
return ret;
return {};
}
} // namespace
bool tr_spawn_async(
char const* const* cmd,
std::map<std::string_view, std::string_view> const& env,
char const* work_dir,
std::string_view work_dir,
tr_error** error)
{
wchar_t* env_block = nullptr;
if (!create_env_block(env, &env_block, error))
// full_env = current_env + env;
auto full_env = get_current_env();
for (auto const& [key, val] : env)
{
return false;
full_env.insert_or_assign(tr_win32_utf8_to_native(key), tr_win32_utf8_to_native(val));
}
wchar_t* cmd_line;
if (!construct_cmd_line(cmd, &cmd_line))
auto cmd_line = construct_cmd_line(cmd);
if (std::empty(cmd_line))
{
set_system_error(error, ERROR_INVALID_PARAMETER, "Constructing command line");
return false;
}
wchar_t* current_dir = work_dir != nullptr ? tr_win32_utf8_to_native(work_dir, -1) : nullptr;
auto const current_dir = tr_win32_utf8_to_native(work_dir);
auto si = STARTUPINFOW{};
si.cb = sizeof(si);
@ -410,13 +309,13 @@ bool tr_spawn_async(
bool const ret = CreateProcessW(
nullptr,
cmd_line,
std::data(cmd_line),
nullptr,
nullptr,
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW | CREATE_DEFAULT_ERROR_MODE,
env_block,
current_dir,
std::empty(full_env) ? nullptr : to_env_string(full_env).data(),
std::empty(current_dir) ? nullptr : current_dir.c_str(),
&si,
&pi);
@ -430,9 +329,5 @@ bool tr_spawn_async(
set_system_error(error, GetLastError(), "Call to CreateProcess()");
}
tr_free(current_dir);
tr_free(cmd_line);
tr_free(env_block);
return ret;
}

View File

@ -13,5 +13,5 @@ struct tr_error;
bool tr_spawn_async(
char const* const* cmd,
std::map<std::string_view, std::string_view> const& env,
char const* work_dir,
std::string_view work_dir,
tr_error** error);

View File

@ -521,7 +521,7 @@ std::string tr_win32_native_to_utf8(std::wstring_view in)
return out;
}
char* tr_win32_native_to_utf8(wchar_t const* text, int text_size)
static char* tr_win32_native_to_utf8(wchar_t const* text, int text_size)
{
if (text == nullptr)
{
@ -545,56 +545,6 @@ std::wstring tr_win32_utf8_to_native(std::string_view in)
return out;
}
wchar_t* tr_win32_utf8_to_native(char const* text, int text_size)
{
return tr_win32_utf8_to_native_ex(text, text_size, 0, 0, nullptr);
}
wchar_t* tr_win32_utf8_to_native_ex(
char const* text,
int text_size,
int extra_chars_before,
int extra_chars_after,
int* real_result_size)
{
wchar_t* ret = nullptr;
int size;
if (text_size == -1)
{
text_size = strlen(text);
}
size = MultiByteToWideChar(CP_UTF8, 0, text, text_size, nullptr, 0);
if (size == 0)
{
goto fail;
}
ret = tr_new(wchar_t, size + extra_chars_before + extra_chars_after + 1);
size = MultiByteToWideChar(CP_UTF8, 0, text, text_size, ret + extra_chars_before, size);
if (size == 0)
{
goto fail;
}
ret[size + extra_chars_before + extra_chars_after] = L'\0';
if (real_result_size != nullptr)
{
*real_result_size = size;
}
return ret;
fail:
tr_free(ret);
return nullptr;
}
std::string tr_win32_format_message(uint32_t code)
{
wchar_t* wide_text = nullptr;
@ -630,7 +580,7 @@ std::string tr_win32_format_message(uint32_t code)
return text;
}
void tr_win32_make_args_utf8(int* argc, char*** argv)
static void tr_win32_make_args_utf8(int* argc, char*** argv)
{
int my_argc;
wchar_t** my_wide_argv;

View File

@ -119,21 +119,10 @@ bool tr_utf8_validate(std::string_view sv, char const** good_end);
#ifdef _WIN32
std::string tr_win32_format_message(uint32_t code);
std::string tr_win32_native_to_utf8(std::wstring_view);
std::wstring tr_win32_utf8_to_native(std::string_view);
char* tr_win32_native_to_utf8(wchar_t const* text, int text_size);
wchar_t* tr_win32_utf8_to_native(char const* text, int text_size);
wchar_t* tr_win32_utf8_to_native_ex(
char const* text,
int text_size,
int extra_chars_before,
int extra_chars_after,
int* real_result_size);
std::string tr_win32_format_message(uint32_t code);
void tr_win32_make_args_utf8(int* argc, char*** argv);
int tr_main_win32(int argc, char** argv, int (*real_main)(int, char**));
#define tr_main(...) \

View File

@ -67,7 +67,8 @@ std::optional<int64_t> ParseInt(std::string_view* benc)
}
// leading zeroes are not allowed
if ((walk[0] == '0' && (isdigit(walk[1]) != 0)) || (walk[0] == '-' && walk[1] == '0' && (isdigit(walk[2]) != 0)))
if ((walk[0] == '0' && (isdigit(static_cast<unsigned char>(walk[1])) != 0)) ||
(walk[0] == '-' && walk[1] == '0' && (isdigit(static_cast<unsigned char>(walk[2])) != 0)))
{
return {};
}

View File

@ -87,7 +87,7 @@ TEST_P(SubprocessTest, SpawnAsyncMissingExec)
auto args = std::array<char const*, 2>{ missing_exe_path.data(), nullptr };
tr_error* error = nullptr;
auto const ret = tr_spawn_async(std::data(args), {}, nullptr, &error);
auto const ret = tr_spawn_async(std::data(args), {}, {}, &error);
EXPECT_FALSE(ret);
EXPECT_NE(nullptr, error);
EXPECT_NE(0, error->code);
@ -116,7 +116,7 @@ TEST_P(SubprocessTest, SpawnAsyncArgs)
nullptr };
tr_error* error = nullptr;
bool const ret = tr_spawn_async(std::data(args), {}, nullptr, &error);
bool const ret = tr_spawn_async(std::data(args), {}, {}, &error);
EXPECT_TRUE(ret) << args[0] << ' ' << args[1];
EXPECT_EQ(nullptr, error) << *error;
@ -197,7 +197,7 @@ TEST_P(SubprocessTest, SpawnAsyncEnv)
setenv("ZOO", "tar", 1 /*true*/); // overridden
tr_error* error = nullptr;
bool const ret = tr_spawn_async(std::data(args), env, nullptr, &error);
bool const ret = tr_spawn_async(std::data(args), env, {}, &error);
EXPECT_TRUE(ret);
EXPECT_EQ(nullptr, error) << *error;
@ -245,7 +245,7 @@ TEST_P(SubprocessTest, SpawnAsyncCwdExplicit)
auto const args = std::array<char const*, 4>{ self_path_.c_str(), result_path.data(), arg_dump_cwd_.data(), nullptr };
tr_error* error = nullptr;
bool const ret = tr_spawn_async(std::data(args), {}, test_dir.c_str(), &error);
bool const ret = tr_spawn_async(std::data(args), {}, test_dir, &error);
EXPECT_TRUE(ret);
EXPECT_EQ(nullptr, error) << *error;
@ -273,7 +273,7 @@ TEST_P(SubprocessTest, SpawnAsyncCwdInherit)
auto const args = std::array<char const*, 4>{ self_path_.c_str(), result_path.data(), arg_dump_cwd_.data(), nullptr };
tr_error* error = nullptr;
auto const ret = tr_spawn_async(std::data(args), {}, nullptr, &error);
auto const ret = tr_spawn_async(std::data(args), {}, {}, &error);
EXPECT_TRUE(ret);
EXPECT_EQ(nullptr, error) << *error;