Skip to content

Commit 83d705a

Browse files
committed
[libcxx] Implement the canonical function for windows
Differential Revision: https://reviews.llvm.org/D91170
1 parent 0c71c91 commit 83d705a

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

libcxx/src/filesystem/operations.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -636,20 +636,20 @@ path __canonical(path const& orig_p, error_code* ec) {
636636
ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
637637

638638
path p = __do_absolute(orig_p, &cwd, ec);
639-
#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112
640-
std::unique_ptr<char, decltype(&::free)>
641-
hold(::realpath(p.c_str(), nullptr), &::free);
639+
#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
640+
std::unique_ptr<path::value_type, decltype(&::free)>
641+
hold(detail::realpath(p.c_str(), nullptr), &::free);
642642
if (hold.get() == nullptr)
643643
return err.report(capture_errno());
644644
return {hold.get()};
645645
#else
646646
#if defined(__MVS__) && !defined(PATH_MAX)
647-
char buff[ _XOPEN_PATH_MAX + 1 ];
647+
path::value_type buff[ _XOPEN_PATH_MAX + 1 ];
648648
#else
649-
char buff[PATH_MAX + 1];
649+
path::value_type buff[PATH_MAX + 1];
650650
#endif
651-
char* ret;
652-
if ((ret = ::realpath(p.c_str(), buff)) == nullptr)
651+
path::value_type* ret;
652+
if ((ret = detail::realpath(p.c_str(), buff)) == nullptr)
653653
return err.report(capture_errno());
654654
return {ret};
655655
#endif

libcxx/src/filesystem/posix_compat.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,43 @@ int statvfs(const wchar_t *p, StatVFS *buf) {
312312
}
313313

314314
wchar_t *getcwd(wchar_t *buff, size_t size) { return _wgetcwd(buff, size); }
315+
316+
wchar_t *realpath(const wchar_t *path, wchar_t *resolved_name) {
317+
// Only expected to be used with us allocating the buffer.
318+
_LIBCPP_ASSERT(resolved_name == nullptr,
319+
"Windows realpath() assumes a null resolved_name");
320+
321+
WinHandle h(path, FILE_READ_ATTRIBUTES, 0);
322+
if (!h) {
323+
set_errno();
324+
return nullptr;
325+
}
326+
size_t buff_size = MAX_PATH + 10;
327+
std::unique_ptr<wchar_t, decltype(&::free)> buff(
328+
static_cast<wchar_t *>(malloc(buff_size * sizeof(wchar_t))), &::free);
329+
DWORD retval = GetFinalPathNameByHandleW(
330+
h, buff.get(), buff_size, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
331+
if (retval > buff_size) {
332+
buff_size = retval;
333+
buff.reset(static_cast<wchar_t *>(malloc(buff_size * sizeof(wchar_t))));
334+
retval = GetFinalPathNameByHandleW(h, buff.get(), buff_size,
335+
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
336+
}
337+
if (!retval) {
338+
set_errno();
339+
return nullptr;
340+
}
341+
wchar_t *ptr = buff.get();
342+
if (!wcsncmp(ptr, L"\\\\?\\", 4)) {
343+
if (ptr[5] == ':') { // \\?\X: -> X:
344+
memmove(&ptr[0], &ptr[4], (wcslen(&ptr[4]) + 1) * sizeof(wchar_t));
345+
} else if (!wcsncmp(&ptr[4], L"UNC\\", 4)) { // \\?\UNC\server -> \\server
346+
wcscpy(&ptr[0], L"\\\\");
347+
memmove(&ptr[2], &ptr[8], (wcslen(&ptr[8]) + 1) * sizeof(wchar_t));
348+
}
349+
}
350+
return buff.release();
351+
}
315352
#else
316353
int symlink_file(const char *oldname, const char *newname) {
317354
return ::symlink(oldname, newname);
@@ -328,6 +365,7 @@ using ::link;
328365
using ::lstat;
329366
using ::mkdir;
330367
using ::open;
368+
using ::realpath;
331369
using ::remove;
332370
using ::rename;
333371
using ::stat;

0 commit comments

Comments
 (0)