Skip to content

Commit

Permalink
support long paths on Windows, add tests for long paths and unicode p…
Browse files Browse the repository at this point in the history
…aths
  • Loading branch information
matyalatte committed Jun 14, 2024
1 parent fece2df commit f9c4372
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 32 deletions.
63 changes: 42 additions & 21 deletions src/windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
#include "env_utils_windows.h"
#include "env_utils_priv.h"

#ifndef MAX_PATH
#warning("Warning: Failed to get MAX_PATH. The compiler uses 260 for it.")
#define MAX_PATH 260
#endif
#ifndef UNLEN
#warning("Warning: Failed to get UNLEN. The compiler uses 256 for it.")
#define UNLEN 256
Expand Down Expand Up @@ -110,12 +106,30 @@ char *envuUTF16toUTF8(const wchar_t* wstr) {
}

char *envuGetExecutablePath() {
wchar_t filename[MAX_PATH + 1];
filename[MAX_PATH] = 0;
int ret = GetModuleFileNameW(NULL, filename, MAX_PATH);
if (ret == 0)
wchar_t *wpath = NULL;
int max_size = 256;
int size;

// Note: The max length of file paths is not MAX_PATH now.
while (max_size <= 32768) {
wpath = envuAllocWstr(max_size);
size = GetModuleFileNameW(NULL, wpath, max_size);
if (size < max_size)
break;

// the buffer size was not enough.
envuFree(wpath);
max_size *= 2;
}

if (size == 0) {
envuFree(wpath);
return NULL;
return envuUTF16toUTF8(filename);
}

char *path = envuUTF16toUTF8(wpath);
envuFree(wpath);
return path;
}

static inline DWORD getFileAttributes(const char *path) {
Expand Down Expand Up @@ -150,18 +164,23 @@ char *envuGetFullPath(const char *path) {
return envuGetCwd();

wchar_t *wpath = envuUTF8toUTF16(path);
wchar_t fullpath[MAX_PATH + 1];
fullpath[MAX_PATH] = 0;
int size = GetFullPathNameW(wpath, MAX_PATH, fullpath, NULL);
if (size < 2) // failed to get full path.

wchar_t *wfullpath = _wfullpath(NULL, wpath, 0);
envuFree(wpath);

size_t size = wcslen(wfullpath);
if (size < 2) {
envuFree(wfullpath);
return NULL;
}

// remove the last slash
if (fullpath[size - 1] == L'\\' && fullpath[size - 2] != L':')
fullpath[size - 1] = L'\0';
if (wfullpath[size - 1] == L'\\' && wfullpath[size - 2] != L':')
wfullpath[size - 1] = L'\0';

envuFree(wpath);
return envuUTF16toUTF8(fullpath);
char *fullpath = envuUTF16toUTF8(wfullpath);
envuFree(wfullpath);
return fullpath;
}

static int isAbsPath(const char *path) {
Expand Down Expand Up @@ -228,10 +247,12 @@ char *envuGetDirectory(const char *path) {
}

char *envuGetCwd() {
wchar_t cwd[MAX_PATH + 1];
cwd[MAX_PATH] = 0;
wchar_t *ret = _wgetcwd(cwd, MAX_PATH);
return envuUTF16toUTF8(ret);
wchar_t *cwd = _wgetcwd(NULL, 0);
if (cwd == NULL)
return NULL;
char *ret = envuUTF16toUTF8(cwd);
free(cwd);
return ret;
}

int envuSetCwd(const char *path) {
Expand Down
14 changes: 13 additions & 1 deletion tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ else
r = run_command('grep', 'PRETTY_NAME', '/etc/os-release', check: true)
true_os_product_name = r.stdout().strip().split('=')[1].strip('"')
elif envu_OS == 'haiku'
true_os_product_name = 'failed'
r = run_command('version', '/boot/system/lib/libbe.so', check: true)
ver = r.stdout().strip()
true_os_product_name = true_os + ' ' + ver
elif envu_OS == 'sunos'
# get os name from /etc/release
r = run_command('head', '-1', '/etc/release', check: true)
Expand Down Expand Up @@ -85,8 +87,18 @@ if envu_OS != 'windows'
subdir('test_cli')
endif

test_cpp_args = []
if envu_OS == 'windows'
if envu_compiler == 'msvc'
test_cpp_args += ['/source-charset:utf-8']
elif envu_compiler == 'gcc'
test_cpp_args += ['-finput-charset=UTF-8']
endif
endif

test_exe = executable('env_utils_test',
'main.cpp',
cpp_args: test_cpp_args,
dependencies : [env_utils_dep, gtest_dep, gmock_dep],
install : false)

Expand Down
41 changes: 33 additions & 8 deletions tests/path_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,39 @@ TEST(PathTest, envuGetFullPathAbsolute) {
}
}

TEST(PathTest, envuGetFullPathLongPath) {
std::vector<std::pair<const char*, const char*>> cases = {
};
for (auto c : cases) {
char* fullpath = envuGetFullPath(c.first);
EXPECT_STREQ(c.second, fullpath) << " c.first: " << c.first << std::endl;
envuFree(fullpath);
}
#define TEST_DIRS "/testdir/testdir/testdir/testdir/testdir/testdir/testdir/testdir"
#define TEST_DIRS_WIN "\\testdir\\testdir\\testdir\\testdir\\testdir\\testdir\\testdir\\testdir"

TEST(PathTest, envuGetFullPathLong) {
const char* longpath =
TEST_DIRS TEST_DIRS TEST_DIRS TEST_DIRS
TEST_DIRS TEST_DIRS TEST_DIRS TEST_DIRS "/../testdir/";
const char* expected =
#ifdef _WIN32
WIN_DRIVE ":"
TEST_DIRS_WIN TEST_DIRS_WIN TEST_DIRS_WIN TEST_DIRS_WIN
TEST_DIRS_WIN TEST_DIRS_WIN TEST_DIRS_WIN TEST_DIRS_WIN;
#else
TEST_DIRS TEST_DIRS TEST_DIRS TEST_DIRS
TEST_DIRS TEST_DIRS TEST_DIRS TEST_DIRS;
#endif
char* fullpath = envuGetFullPath(longpath);
EXPECT_STREQ(expected, fullpath);
envuFree(fullpath);
}

TEST(PathTest, envuGetFullPathUnicode) {
// "/フォルダ/폴더/文件/."
const char* unipath = u8"/\u30d5\u30a9\u30eb\u30c0/\ud3f4\ub354/\u6587\u4ef6/.";
const char* expected =
#ifdef _WIN32
WIN_DRIVE u8":\\\u30d5\u30a9\u30eb\u30c0\\\ud3f4\ub354\\\u6587\u4ef6";
#else
u8"/\u30d5\u30a9\u30eb\u30c0/\ud3f4\ub354/\u6587\u4ef6";
#endif
char* fullpath = envuGetFullPath(unipath);
EXPECT_STREQ(expected, fullpath);
envuFree(fullpath);
}

TEST(PathTest, envuGetFullPathRelative) {
Expand Down
2 changes: 0 additions & 2 deletions tests/util_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,11 @@ TEST(UtilTest, envuGetOSVersion) {
envuFree(os_ver);
}

#ifndef __HAIKU__
TEST(UtilTest, envuGetOSProductName) {
char* os_prod_name = envuGetOSProductName();
ASSERT_STREQ(TRUE_OS_PRODUCT_NAME, os_prod_name);
envuFree(os_prod_name);
}
#endif

// TODO: test with long paths
// TODO: test with unicode strings
Expand Down

0 comments on commit f9c4372

Please sign in to comment.