diff options
Diffstat (limited to 'llvm/lib/Support')
-rw-r--r-- | llvm/lib/Support/Unix/Path.inc | 64 | ||||
-rw-r--r-- | llvm/lib/Support/Windows/Path.inc | 113 |
2 files changed, 161 insertions, 16 deletions
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index 43549c0f564..cdc692b8f1f 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -48,6 +48,8 @@ # endif #endif +#include <pwd.h> + #ifdef __APPLE__ #include <mach-o/dyld.h> #include <sys/attr.h> @@ -478,6 +480,45 @@ std::error_code equivalent(const Twine &A, const Twine &B, bool &result) { return std::error_code(); } +static void expandTildeExpr(SmallVectorImpl<char> &Path) { + StringRef PathStr(Path.begin(), Path.size()); + if (PathStr.empty() || !PathStr.startswith("~")) + return; + + PathStr = PathStr.drop_front(); + StringRef Expr = PathStr.take_until(path::is_separator); + StringRef Remainder = PathStr.substr(Expr.size() + 1); + SmallString<128> Storage; + if (Expr.empty()) { + // This is just ~/..., resolve it to the current user's home dir. + if (!path::home_directory(Storage)) { + // For some reason we couldn't get the home directory. Just exit. + return; + } + + // Overwrite the first character and insert the rest. + Path[0] = Storage[0]; + Path.insert(Path.begin() + 1, Storage.begin() + 1, Storage.end()); + return; + } + + // This is a string of the form ~username/, look up this user's entry in the + // password database. + struct passwd *Entry = nullptr; + std::string User = Expr.str(); + Entry = ::getpwnam(User.c_str()); + + if (!Entry) { + // Unable to look up the entry, just return back the original path. + return; + } + + Storage = Remainder; + Path.clear(); + Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir)); + llvm::sys::path::append(Path, Storage); +} + static std::error_code fillStatus(int StatRet, const struct stat &Status, file_status &Result) { if (StatRet != 0) { @@ -839,6 +880,28 @@ std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { return std::error_code(); } +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); + } + + int fd; + std::error_code EC = openFileForRead(path, fd, &dest); + + if (EC) + return EC; + ::close(fd); + return std::error_code(); +} + } // end namespace fs namespace path { @@ -849,7 +912,6 @@ bool home_directory(SmallVectorImpl<char> &result) { result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); return true; } - return false; } 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 { |