return strpbrk (path, "<>:\"|?*") == NULL;
}
+static wchar_t *
+path_to_native_path_ex (const char * path,
+ int extra_chars_after,
+ int * real_result_size)
+{
+ /* 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 */
+
+ const wchar_t local_prefix[] = { '\\', '\\', '?', '\\' };
+ const wchar_t unc_prefix[] = { '\\', '\\', '?', '\\', 'U', 'N', 'C', '\\' };
+
+ const bool is_relative = tr_sys_path_is_relative (path);
+ const bool is_unc = is_unc_path (path);
+
+ /* `-2` for UNC since we overwrite existing prefix slashes */
+ const int extra_chars_before = is_relative ? 0 : (is_unc ? ARRAYSIZE (unc_prefix) - 2
+ : ARRAYSIZE (local_prefix));
+
+ /* TODO (?): assert (!is_relative); */
+
+ wchar_t * const wide_path = tr_win32_utf8_to_native_ex (path, -1, extra_chars_before,
+ extra_chars_after, real_result_size);
+ if (wide_path == NULL)
+ return NULL;
+
+ /* 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, unc_prefix, sizeof (unc_prefix));
+ else
+ /* Local path: "C:" -> "\\?\C:" */
+ memcpy (wide_path, local_prefix, sizeof (local_prefix));
+ }
+
+ /* Automatic '/' to '\' conversion is disabled for "\\?\"-prefixed paths */
+ wchar_t * p = wide_path + extra_chars_before;
+ while ((p = wcschr (p, L'/')) != NULL)
+ *p++ = L'\\';
+
+ if (real_result_size != NULL)
+ *real_result_size += extra_chars_before;
+
+ return wide_path;
+}
+
+static wchar_t *
+path_to_native_path (const char * path)
+{
+ return path_to_native_path_ex (path, 0, NULL);
+}
+
static tr_sys_file_t
open_file (const char * path,
DWORD access,
assert (path != NULL);
- wide_path = tr_win32_utf8_to_native (path, -1);
+ wide_path = path_to_native_path (path);
if (wide_path != NULL)
ret = CreateFileW (wide_path, access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
(void) permissions;
- wide_path = tr_win32_utf8_to_native (path, -1);
+ wide_path = path_to_native_path (path);
if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
{
- /* For some reason SHCreateDirectoryEx has issues with forward slashes */
- wchar_t * p = wide_path;
- while ((p = wcschr (p, L'/')) != NULL)
- *p++ = L'\\';
-
error_code = SHCreateDirectoryExW (NULL, wide_path, NULL);
ret = error_code == ERROR_SUCCESS;
}
assert (path != NULL);
- wide_path = tr_win32_utf8_to_native (path, -1);
+ wide_path = path_to_native_path (path);
if (wide_path != NULL)
{
assert (path != NULL);
assert (info != NULL);
- wide_path = tr_win32_utf8_to_native (path, -1);
+ wide_path = path_to_native_path (path);
if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0)
{
assert (path1 != NULL);
assert (path2 != NULL);
- wide_path1 = tr_win32_utf8_to_native (path1, -1);
+ wide_path1 = path_to_native_path (path1);
if (wide_path1 == NULL)
goto fail;
- wide_path2 = tr_win32_utf8_to_native (path2, -1);
+ wide_path2 = path_to_native_path (path2);
if (wide_path2 == NULL)
goto fail;
assert (path != NULL);
- wide_path = tr_win32_utf8_to_native (path, -1);
+ wide_path = path_to_native_path (path);
if (wide_path == NULL)
goto fail;
assert (src_path != NULL);
assert (dst_path != NULL);
- wide_src_path = tr_win32_utf8_to_native (src_path, -1);
- wide_dst_path = tr_win32_utf8_to_native (dst_path, -1);
+ wide_src_path = path_to_native_path (src_path);
+ wide_dst_path = path_to_native_path (dst_path);
if (wide_src_path != NULL && wide_dst_path != NULL)
{
assert (path != NULL);
- wide_path = tr_win32_utf8_to_native (path, -1);
+ wide_path = path_to_native_path (path);
if (wide_path != NULL)
{
tr_error ** error)
{
tr_sys_dir_t ret;
+ int pattern_size;
#ifndef __clang__
/* Clang gives "static_assert expression is not an integral constant expression" error */
assert (path != NULL);
ret = tr_new (struct tr_sys_dir_win32, 1);
- ret->pattern = tr_win32_utf8_to_native_ex (path, -1, 2);
+ ret->pattern = path_to_native_path_ex (path, 2, &pattern_size);
if (ret->pattern != NULL)
{
- const size_t pattern_size = wcslen (ret->pattern);
ret->pattern[pattern_size + 0] = L'\\';
ret->pattern[pattern_size + 1] = L'*';
- ret->pattern[pattern_size + 2] = L'\0';
ret->find_handle = INVALID_HANDLE_VALUE;
ret->utf8_name = NULL;
tr_win32_utf8_to_native (const char * text,
int text_size)
{
- return tr_win32_utf8_to_native_ex (text, text_size, 0);
+ return tr_win32_utf8_to_native_ex (text, text_size, 0, 0, NULL);
}
wchar_t *
tr_win32_utf8_to_native_ex (const char * text,
int text_size,
- int extra_chars)
+ int extra_chars_before,
+ int extra_chars_after,
+ int * real_result_size)
{
wchar_t * ret = NULL;
int size;
+ if (text_size == -1)
+ text_size = strlen (text);
+
size = MultiByteToWideChar (CP_UTF8, 0, text, text_size, NULL, 0);
if (size == 0)
goto fail;
- ret = tr_new (wchar_t, size + extra_chars + 1);
- size = MultiByteToWideChar (CP_UTF8, 0, text, text_size, ret, size);
+ 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] = L'\0';
+ ret[size + extra_chars_before + extra_chars_after] = L'\0';
+
+ if (real_result_size != NULL)
+ *real_result_size = size;
return ret;