diff options
author | Zachary Turner <zturner@google.com> | 2017-03-10 17:39:21 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2017-03-10 17:39:21 +0000 |
commit | e48ace6a65514a4f71669c50fd5d4ead67acdc7f (patch) | |
tree | f16ef818e20451ed8031e0fe16248b273043dfd8 /llvm/lib/Support/Windows/Path.inc | |
parent | 62e0759d56b34c0b044a180e1bc89c419531cf57 (diff) | |
download | bcm5719-llvm-e48ace6a65514a4f71669c50fd5d4ead67acdc7f.tar.gz bcm5719-llvm-e48ace6a65514a4f71669c50fd5d4ead67acdc7f.zip |
Add llvm::sys::fs::real_path.
LLVM already has real_path like functionality, but it is
cumbersome to use and involves clean up after (e.g. you have
to call openFileForRead, then close the resulting FD).
Furthermore, on Windows it doesn't work for directories since
opening a directory and opening a file require slightly
different flags.
So I add a simple function `real_path` which works for all
paths on all platforms and has a simple to use interface.
In doing so, I add the ability to opt in to resolving tilde
expressions (e.g. ~/foo), which are normally handled by
the shell.
Differential Revision: https://reviews.llvm.org/D30668
llvm-svn: 297483
Diffstat (limited to 'llvm/lib/Support/Windows/Path.inc')
-rw-r--r-- | llvm/lib/Support/Windows/Path.inc | 113 |
1 files changed, 98 insertions, 15 deletions
diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index dc6922291a4..c7582894fa9 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -780,6 +780,52 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { return std::error_code(); } +static std::error_code realPathFromHandle(HANDLE H, + SmallVectorImpl<char> &RealPath) { + RealPath.clear(); + llvm::SmallVector<wchar_t, MAX_PATH> Buffer; + DWORD CountChars = ::GetFinalPathNameByHandleW( + H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); + if (CountChars > Buffer.capacity()) { + // The buffer wasn't big enough, try again. In this case the return value + // *does* indicate the size of the null terminator. + Buffer.reserve(CountChars); + CountChars = ::GetFinalPathNameByHandleW( + H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); + } + if (CountChars == 0) + return mapWindowsError(GetLastError()); + + const wchar_t *Data = Buffer.data(); + if (CountChars >= 4) { + if (0 == ::memcmp(Data, L"\\\\?\\", 8)) { + CountChars -= 4; + Data += 4; + } + } + + // Convert the result from UTF-16 to UTF-8. + return UTF16ToUTF8(Data, CountChars, RealPath); +} + +static std::error_code directoryRealPath(const Twine &Name, + SmallVectorImpl<char> &RealPath) { + SmallVector<wchar_t, 128> PathUTF16; + + if (std::error_code EC = widenPath(Name, PathUTF16)) + return EC; + + HANDLE H = + ::CreateFileW(PathUTF16.begin(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (H == INVALID_HANDLE_VALUE) + return mapWindowsError(GetLastError()); + std::error_code EC = realPathFromHandle(H, RealPath); + ::CloseHandle(H); + return EC; +} + std::error_code openFileForRead(const Twine &Name, int &ResultFD, SmallVectorImpl<char> *RealPath) { SmallVector<wchar_t, 128> PathUTF16; @@ -811,23 +857,12 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, } // Fetch the real name of the file, if the user asked - if (RealPath) { - RealPath->clear(); - wchar_t RealPathUTF16[MAX_PATH]; - DWORD CountChars = - ::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH, - FILE_NAME_NORMALIZED); - if (CountChars > 0 && CountChars < MAX_PATH) { - // Convert the result from UTF-16 to UTF-8. - SmallString<MAX_PATH> RealPathUTF8; - if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8)) - RealPath->append(RealPathUTF8.data(), - RealPathUTF8.data() + strlen(RealPathUTF8.data())); - } - } + std::error_code EC; + if (RealPath) + EC = realPathFromHandle(H, *RealPath); ResultFD = FD; - return std::error_code(); + return EC; } std::error_code openFileForWrite(const Twine &Name, int &ResultFD, @@ -949,6 +984,54 @@ std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { return std::error_code(); } +static void expandTildeExpr(SmallVectorImpl<char> &Path) { + // Path does not begin with a tilde expression. + if (Path.empty() || Path[0] != '~') + return; + + StringRef PathStr(Path.begin(), Path.size()); + PathStr = PathStr.drop_front(); + StringRef Expr = PathStr.take_until(path::is_separator); + + if (!Expr.empty()) { + // This is probably a ~username/ expression. Don't support this on Windows. + return; + } + + SmallString<128> HomeDir; + if (!path::home_directory(HomeDir)) { + // For some reason we couldn't get the home directory. Just exit. + return; + } + + // Overwrite the first character and insert the rest. + Path[0] = HomeDir[0]; + Path.insert(Path.begin() + 1, HomeDir.begin() + 1, HomeDir.end()); +} + +std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, + bool expand_tilde) { + dest.clear(); + if (path.isTriviallyEmpty()) + return std::error_code(); + + if (expand_tilde) { + SmallString<128> Storage; + path.toVector(Storage); + expandTildeExpr(Storage); + return real_path(Storage, dest, false); + } + + if (is_directory(path)) + return directoryRealPath(path, dest); + + int fd; + if (std::error_code EC = llvm::sys::fs::openFileForRead(path, fd, &dest)) + return EC; + ::close(fd); + return std::error_code(); +} + } // end namespace fs namespace path { |