X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=blobdiff_plain;f=lib%2FSupport%2FWindows%2FPath.inc;h=b09c198969938376bde89a2011adce86e9d325bd;hp=2b75e737bac2751d73a3ad92568fc1d998962cce;hb=9be4884731b20e540b13b47737f0b4acb7c66e86;hpb=b4d07b770c41b285a613a5b4953fe815044fad19 diff --git a/lib/Support/Windows/Path.inc b/lib/Support/Windows/Path.inc index 2b75e737bac..b09c1989699 100644 --- a/lib/Support/Windows/Path.inc +++ b/lib/Support/Windows/Path.inc @@ -1,4 +1,4 @@ -//===- llvm/Support/Win32/Path.cpp - Win32 Path Implementation ---*- C++ -*-===// +//===- llvm/Support/Windows/Path.inc - Windows Path Impl --------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,819 +7,945 @@ // //===----------------------------------------------------------------------===// // -// This file provides the Win32 specific implementation of the Path class. +// This file implements the Windows specific implementation of the Path API. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// -//=== WARNING: Implementation here must contain only generic Win32 code that -//=== is guaranteed to work on *all* Win32 variants. +//=== WARNING: Implementation here must contain only generic Windows code that +//=== is guaranteed to work on *all* Windows variants. //===----------------------------------------------------------------------===// -#include "Windows.h" -#include -#include +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/WindowsError.h" +#include +#include +#include +#include -// We need to undo a macro defined in Windows.h, otherwise we won't compile: -#undef CopyFile -#undef GetCurrentDirectory +// These two headers must be included last, and make sure shlobj is required +// after Windows.h to make sure it picks up our definition of _WIN32_WINNT +#include "WindowsSupport.h" +#include -// Windows happily accepts either forward or backward slashes, though any path -// returned by a Win32 API will have backward slashes. As LLVM code basically -// assumes forward slashes are used, backward slashs are converted where they -// can be introduced into a path. -// -// Another invariant is that a path ends with a slash if and only if the path -// is a root directory. Any other use of a trailing slash is stripped. Unlike -// in Unix, Windows has a rather complicated notion of a root path and this -// invariant helps simply the code. +#undef max -static void FlipBackSlashes(std::string& s) { - for (size_t i = 0; i < s.size(); i++) - if (s[i] == '\\') - s[i] = '/'; -} +// MinGW doesn't define this. +#ifndef _ERRNO_T_DEFINED +#define _ERRNO_T_DEFINED +typedef int errno_t; +#endif -namespace llvm { -namespace sys { +#ifdef _MSC_VER +# pragma comment(lib, "advapi32.lib") // This provides CryptAcquireContextW. +#endif -const char PathSeparator = ';'; +using namespace llvm; -StringRef Path::GetEXESuffix() { - return "exe"; -} +using llvm::sys::windows::UTF8ToUTF16; +using llvm::sys::windows::UTF16ToUTF8; -Path::Path(llvm::StringRef p) - : path(p) { - FlipBackSlashes(path); +static std::error_code windows_error(DWORD E) { + return mapWindowsError(E); } -Path::Path(const char *StrStart, unsigned StrLen) - : path(StrStart, StrLen) { - FlipBackSlashes(path); +static bool is_separator(const wchar_t value) { + switch (value) { + case L'\\': + case L'/': + return true; + default: + return false; + } } -Path& -Path::operator=(StringRef that) { - path.assign(that.data(), that.size()); - FlipBackSlashes(path); - return *this; -} +namespace llvm { +namespace sys { +namespace fs { -bool -Path::isValid() const { - if (path.empty()) - return false; +std::string getMainExecutable(const char *argv0, void *MainExecAddr) { + SmallVector PathName; + DWORD Size = ::GetModuleFileNameW(NULL, PathName.data(), PathName.capacity()); - size_t len = path.size(); - // If there is a null character, it and all its successors are ignored. - size_t pos = path.find_first_of('\0'); - if (pos != std::string::npos) - len = pos; - - // If there is a colon, it must be the second character, preceded by a letter - // and followed by something. - pos = path.rfind(':',len); - size_t rootslash = 0; - if (pos != std::string::npos) { - if (pos != 1 || !isalpha(static_cast(path[0])) || len < 3) - return false; - rootslash = 2; - } + // A zero return value indicates a failure other than insufficient space. + if (Size == 0) + return ""; - // Look for a UNC path, and if found adjust our notion of the root slash. - if (len > 3 && path[0] == '/' && path[1] == '/') { - rootslash = path.find('/', 2); - if (rootslash == std::string::npos) - rootslash = 0; - } + // Insufficient space is determined by a return value equal to the size of + // the buffer passed in. + if (Size == PathName.capacity()) + return ""; - // Check for illegal characters. - if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012" - "\013\014\015\016\017\020\021\022\023\024\025\026" - "\027\030\031\032\033\034\035\036\037") - != std::string::npos) - return false; + // On success, GetModuleFileNameW returns the number of characters written to + // the buffer not including the NULL terminator. + PathName.set_size(Size); - // Remove trailing slash, unless it's a root slash. - if (len > rootslash+1 && path[len-1] == '/') - path.erase(--len); + // Convert the result from UTF-16 to UTF-8. + SmallVector PathNameUTF8; + if (UTF16ToUTF8(PathName.data(), PathName.size(), PathNameUTF8)) + return ""; - // Check each component for legality. - for (pos = 0; pos < len; ++pos) { - // A component may not end in a space. - if (path[pos] == ' ') { - if (pos+1 == len || path[pos+1] == '/' || path[pos+1] == '\0') - return false; - } + return std::string(PathNameUTF8.data()); +} - // A component may not end in a period. - if (path[pos] == '.') { - if (pos+1 == len || path[pos+1] == '/') { - // Unless it is the pseudo-directory "."... - if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':') - return true; - // or "..". - if (pos > 0 && path[pos-1] == '.') { - if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':') - return true; - } - return false; - } - } - } +UniqueID file_status::getUniqueID() const { + // The file is uniquely identified by the volume serial number along + // with the 64-bit file identifier. + uint64_t FileID = (static_cast(FileIndexHigh) << 32ULL) | + static_cast(FileIndexLow); - return true; + return UniqueID(VolumeSerialNumber, FileID); } -void Path::makeAbsolute() { - TCHAR FullPath[MAX_PATH + 1] = {0}; - LPTSTR FilePart = NULL; +TimeValue file_status::getLastModificationTime() const { + ULARGE_INTEGER UI; + UI.LowPart = LastWriteTimeLow; + UI.HighPart = LastWriteTimeHigh; - DWORD RetLength = ::GetFullPathNameA(path.c_str(), - sizeof(FullPath)/sizeof(FullPath[0]), - FullPath, &FilePart); - - if (0 == RetLength) { - // FIXME: Report the error GetLastError() - assert(0 && "Unable to make absolute path!"); - } else if (RetLength > MAX_PATH) { - // FIXME: Report too small buffer (needed RetLength bytes). - assert(0 && "Unable to make absolute path!"); - } else { - path = FullPath; - } + TimeValue Ret; + Ret.fromWin32Time(UI.QuadPart); + return Ret; } -bool -Path::isAbsolute(const char *NameStart, unsigned NameLen) { - assert(NameStart); - // FIXME: This does not handle correctly an absolute path starting from - // a drive letter or in UNC format. - switch (NameLen) { - case 0: - return false; - case 1: - case 2: - return NameStart[0] == '/'; - default: - return - (NameStart[0] == '/' || (NameStart[1] == ':' && NameStart[2] == '/')) || - (NameStart[0] == '\\' || (NameStart[1] == ':' && NameStart[2] == '\\')); - } -} +std::error_code current_path(SmallVectorImpl &result) { + SmallVector cur_path; + DWORD len = MAX_PATH; -bool -Path::isAbsolute() const { - // FIXME: This does not handle correctly an absolute path starting from - // a drive letter or in UNC format. - switch (path.length()) { - case 0: - return false; - case 1: - case 2: - return path[0] == '/'; - default: - return path[0] == '/' || (path[1] == ':' && path[2] == '/'); - } + do { + cur_path.reserve(len); + len = ::GetCurrentDirectoryW(cur_path.capacity(), cur_path.data()); + + // A zero return value indicates a failure other than insufficient space. + if (len == 0) + return windows_error(::GetLastError()); + + // If there's insufficient space, the len returned is larger than the len + // given. + } while (len > cur_path.capacity()); + + // On success, GetCurrentDirectoryW returns the number of characters not + // including the null-terminator. + cur_path.set_size(len); + return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result); } -static Path *TempDirectory; +std::error_code create_directory(const Twine &path, bool IgnoreExisting) { + SmallString<128> path_storage; + SmallVector path_utf16; -Path -Path::GetTemporaryDirectory(std::string* ErrMsg) { - if (TempDirectory) { -#if defined(_MSC_VER) - // Visual Studio gets confused and emits a diagnostic about calling exists, - // even though this is the implementation for PathV1. Temporarily - // disable the deprecated warning message - #pragma warning(push) - #pragma warning(disable:4996) -#endif - assert(TempDirectory->exists() && "Who has removed TempDirectory?"); -#if defined(_MSC_VER) - #pragma warning(pop) -#endif - return *TempDirectory; - } + if (std::error_code ec = + UTF8ToUTF16(path.toStringRef(path_storage), path_utf16)) + return ec; - char pathname[MAX_PATH]; - if (!GetTempPath(MAX_PATH, pathname)) { - if (ErrMsg) - *ErrMsg = "Can't determine temporary directory"; - return Path(); + if (!::CreateDirectoryW(path_utf16.begin(), NULL)) { + DWORD LastError = ::GetLastError(); + if (LastError != ERROR_ALREADY_EXISTS || !IgnoreExisting) + return windows_error(LastError); } - Path result; - result.set(pathname); + return std::error_code(); +} - // Append a subdirectory based on our process id so multiple LLVMs don't - // step on each other's toes. -#ifdef __MINGW32__ - // Mingw's Win32 header files are broken. - sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId())); -#else - sprintf(pathname, "LLVM_%u", GetCurrentProcessId()); -#endif - result.appendComponent(pathname); +// We can't use symbolic links for windows. +std::error_code create_link(const Twine &to, const Twine &from) { + // Get arguments. + SmallString<128> from_storage; + SmallString<128> to_storage; + StringRef f = from.toStringRef(from_storage); + StringRef t = to.toStringRef(to_storage); - // If there's a directory left over from a previous LLVM execution that - // happened to have the same process id, get rid of it. - result.eraseFromDisk(true); + // Convert to utf-16. + SmallVector wide_from; + SmallVector wide_to; + if (std::error_code ec = UTF8ToUTF16(f, wide_from)) + return ec; + if (std::error_code ec = UTF8ToUTF16(t, wide_to)) + return ec; - // And finally (re-)create the empty directory. - result.createDirectoryOnDisk(false); - TempDirectory = new Path(result); - return *TempDirectory; -} + if (!::CreateHardLinkW(wide_from.begin(), wide_to.begin(), NULL)) + return windows_error(::GetLastError()); -Path -Path::GetCurrentDirectory() { - char pathname[MAX_PATH]; - ::GetCurrentDirectoryA(MAX_PATH,pathname); - return Path(pathname); + return std::error_code(); } -/// GetMainExecutable - Return the path to the main executable, given the -/// value of argv[0] from program startup. -Path Path::GetMainExecutable(const char *argv0, void *MainAddr) { - char pathname[MAX_PATH]; - DWORD ret = ::GetModuleFileNameA(NULL, pathname, MAX_PATH); - return ret != MAX_PATH ? Path(pathname) : Path(); -} +std::error_code remove(const Twine &path, bool IgnoreNonExisting) { + SmallString<128> path_storage; + SmallVector path_utf16; + file_status ST; + if (std::error_code EC = status(path, ST)) { + if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting) + return EC; + return std::error_code(); + } -// FIXME: the above set of functions don't map to Windows very well. + if (std::error_code ec = + UTF8ToUTF16(path.toStringRef(path_storage), path_utf16)) + return ec; + if (ST.type() == file_type::directory_file) { + if (!::RemoveDirectoryW(c_str(path_utf16))) { + std::error_code EC = windows_error(::GetLastError()); + if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting) + return EC; + } + return std::error_code(); + } + if (!::DeleteFileW(c_str(path_utf16))) { + std::error_code EC = windows_error(::GetLastError()); + if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting) + return EC; + } + return std::error_code(); +} + +std::error_code rename(const Twine &from, const Twine &to) { + // Get arguments. + SmallString<128> from_storage; + SmallString<128> to_storage; + StringRef f = from.toStringRef(from_storage); + StringRef t = to.toStringRef(to_storage); + + // Convert to utf-16. + SmallVector wide_from; + SmallVector wide_to; + if (std::error_code ec = UTF8ToUTF16(f, wide_from)) + return ec; + if (std::error_code ec = UTF8ToUTF16(t, wide_to)) + return ec; + + std::error_code ec = std::error_code(); + for (int i = 0; i < 2000; i++) { + if (::MoveFileExW(wide_from.begin(), wide_to.begin(), + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) + return std::error_code(); + DWORD LastError = ::GetLastError(); + if (LastError != ERROR_ACCESS_DENIED) + break; + // Retry MoveFile() at ACCESS_DENIED. + // System scanners (eg. indexer) might open the source file when + // It is written and closed. + ::Sleep(1); + } -StringRef Path::getDirname() const { - return getDirnameCharSep(path, "/"); + return ec; } -StringRef -Path::getSuffix() const { - // Find the last slash - size_t slash = path.rfind('/'); - if (slash == std::string::npos) - slash = 0; - else - slash++; +std::error_code resize_file(const Twine &path, uint64_t size) { + SmallString<128> path_storage; + SmallVector path_utf16; - size_t dot = path.rfind('.'); - if (dot == std::string::npos || dot < slash) - return StringRef(""); - else - return StringRef(path).substr(dot + 1); -} + if (std::error_code ec = + UTF8ToUTF16(path.toStringRef(path_storage), path_utf16)) + return ec; -bool -Path::exists() const { - DWORD attr = GetFileAttributes(path.c_str()); - return attr != INVALID_FILE_ATTRIBUTES; + int fd = ::_wopen(path_utf16.begin(), O_BINARY | _O_RDWR, S_IWRITE); + if (fd == -1) + return std::error_code(errno, std::generic_category()); +#ifdef HAVE__CHSIZE_S + errno_t error = ::_chsize_s(fd, size); +#else + errno_t error = ::_chsize(fd, size); +#endif + ::close(fd); + return std::error_code(error, std::generic_category()); } -bool -Path::isDirectory() const { - DWORD attr = GetFileAttributes(path.c_str()); - return (attr != INVALID_FILE_ATTRIBUTES) && - (attr & FILE_ATTRIBUTE_DIRECTORY); -} +std::error_code exists(const Twine &path, bool &result) { + SmallString<128> path_storage; + SmallVector path_utf16; -bool -Path::isSymLink() const { - DWORD attributes = GetFileAttributes(path.c_str()); + if (std::error_code ec = + UTF8ToUTF16(path.toStringRef(path_storage), path_utf16)) + return ec; - if (attributes == INVALID_FILE_ATTRIBUTES) - // There's no sane way to report this :(. - assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES"); + DWORD attributes = ::GetFileAttributesW(path_utf16.begin()); - // This isn't exactly what defines a NTFS symlink, but it is only true for - // paths that act like a symlink. - return attributes & FILE_ATTRIBUTE_REPARSE_POINT; + if (attributes == INVALID_FILE_ATTRIBUTES) { + // See if the file didn't actually exist. + DWORD LastError = ::GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND && + LastError != ERROR_PATH_NOT_FOUND) + return windows_error(LastError); + result = false; + } else + result = true; + return std::error_code(); } -bool -Path::canRead() const { +bool can_write(const Twine &Path) { // FIXME: take security attributes into account. - DWORD attr = GetFileAttributes(path.c_str()); - return attr != INVALID_FILE_ATTRIBUTES; -} + SmallString<128> PathStorage; + SmallVector PathUtf16; -bool -Path::canWrite() const { - // FIXME: take security attributes into account. - DWORD attr = GetFileAttributes(path.c_str()); - return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY); -} + if (UTF8ToUTF16(Path.toStringRef(PathStorage), PathUtf16)) + return false; -bool -Path::canExecute() const { - // FIXME: take security attributes into account. - DWORD attr = GetFileAttributes(path.c_str()); - return attr != INVALID_FILE_ATTRIBUTES; + DWORD Attr = ::GetFileAttributesW(PathUtf16.begin()); + return (Attr != INVALID_FILE_ATTRIBUTES) && !(Attr & FILE_ATTRIBUTE_READONLY); } -bool -Path::isRegularFile() const { - bool res; - if (fs::is_regular_file(path, res)) +bool can_execute(const Twine &Path) { + SmallString<128> PathStorage; + SmallVector PathUtf16; + + if (UTF8ToUTF16(Path.toStringRef(PathStorage), PathUtf16)) return false; - return res; -} -const FileStatus * -PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const { - if (!fsIsValid || update) { - WIN32_FILE_ATTRIBUTE_DATA fi; - if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { - MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) + - ": Can't get status: "); - return 0; - } + DWORD Attr = ::GetFileAttributesW(PathUtf16.begin()); + return Attr != INVALID_FILE_ATTRIBUTES; +} + +bool equivalent(file_status A, file_status B) { + assert(status_known(A) && status_known(B)); + return A.FileIndexHigh == B.FileIndexHigh && + A.FileIndexLow == B.FileIndexLow && + A.FileSizeHigh == B.FileSizeHigh && + A.FileSizeLow == B.FileSizeLow && + A.LastWriteTimeHigh == B.LastWriteTimeHigh && + A.LastWriteTimeLow == B.LastWriteTimeLow && + A.VolumeSerialNumber == B.VolumeSerialNumber; +} + +std::error_code equivalent(const Twine &A, const Twine &B, bool &result) { + file_status fsA, fsB; + if (std::error_code ec = status(A, fsA)) + return ec; + if (std::error_code ec = status(B, fsB)) + return ec; + result = equivalent(fsA, fsB); + return std::error_code(); +} + +static bool isReservedName(StringRef path) { + // This list of reserved names comes from MSDN, at: + // http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx + static const char *sReservedNames[] = { "nul", "con", "prn", "aux", + "com1", "com2", "com3", "com4", "com5", "com6", + "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", + "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" }; + + // First, check to see if this is a device namespace, which always + // starts with \\.\, since device namespaces are not legal file paths. + if (path.startswith("\\\\.\\")) + return true; - status.fileSize = fi.nFileSizeHigh; - status.fileSize <<= sizeof(fi.nFileSizeHigh)*8; - status.fileSize += fi.nFileSizeLow; + // Then compare against the list of ancient reserved names + for (size_t i = 0; i < array_lengthof(sReservedNames); ++i) { + if (path.equals_lower(sReservedNames[i])) + return true; + } - status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777; - status.user = 9999; // Not applicable to Windows, so... - status.group = 9999; // Not applicable to Windows, so... + // The path isn't what we consider reserved. + return false; +} - // FIXME: this is only unique if the file is accessed by the same file path. - // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode - // numbers, but the concept doesn't exist in Windows. - status.uniqueID = 0; - for (unsigned i = 0; i < path.length(); ++i) - status.uniqueID += path[i]; +static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { + if (FileHandle == INVALID_HANDLE_VALUE) + goto handle_status_error; - ULARGE_INTEGER ui; - ui.LowPart = fi.ftLastWriteTime.dwLowDateTime; - ui.HighPart = fi.ftLastWriteTime.dwHighDateTime; - status.modTime.fromWin32Time(ui.QuadPart); + switch (::GetFileType(FileHandle)) { + default: + llvm_unreachable("Don't know anything about this file type"); + case FILE_TYPE_UNKNOWN: { + DWORD Err = ::GetLastError(); + if (Err != NO_ERROR) + return windows_error(Err); + Result = file_status(file_type::type_unknown); + return std::error_code(); + } + case FILE_TYPE_DISK: + break; + case FILE_TYPE_CHAR: + Result = file_status(file_type::character_file); + return std::error_code(); + case FILE_TYPE_PIPE: + Result = file_status(file_type::fifo_file); + return std::error_code(); + } - status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - fsIsValid = true; + BY_HANDLE_FILE_INFORMATION Info; + if (!::GetFileInformationByHandle(FileHandle, &Info)) + goto handle_status_error; + + { + file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ? file_type::directory_file + : file_type::regular_file; + Result = + file_status(Type, Info.ftLastWriteTime.dwHighDateTime, + Info.ftLastWriteTime.dwLowDateTime, + Info.dwVolumeSerialNumber, Info.nFileSizeHigh, + Info.nFileSizeLow, Info.nFileIndexHigh, Info.nFileIndexLow); + return std::error_code(); } - return &status; -} -bool Path::makeReadableOnDisk(std::string* ErrMsg) { - // All files are readable on Windows (ignoring security attributes). - return false; +handle_status_error: + DWORD LastError = ::GetLastError(); + if (LastError == ERROR_FILE_NOT_FOUND || + LastError == ERROR_PATH_NOT_FOUND) + Result = file_status(file_type::file_not_found); + else if (LastError == ERROR_SHARING_VIOLATION) + Result = file_status(file_type::type_unknown); + else + Result = file_status(file_type::status_error); + return windows_error(LastError); } -bool Path::makeWriteableOnDisk(std::string* ErrMsg) { - DWORD attr = GetFileAttributes(path.c_str()); +std::error_code status(const Twine &path, file_status &result) { + SmallString<128> path_storage; + SmallVector path_utf16; - // If it doesn't exist, we're done. - if (attr == INVALID_FILE_ATTRIBUTES) - return false; + StringRef path8 = path.toStringRef(path_storage); + if (isReservedName(path8)) { + result = file_status(file_type::character_file); + return std::error_code(); + } - if (attr & FILE_ATTRIBUTE_READONLY) { - if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) { - MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: "); - return true; - } + if (std::error_code ec = UTF8ToUTF16(path8, path_utf16)) + return ec; + + DWORD attr = ::GetFileAttributesW(path_utf16.begin()); + if (attr == INVALID_FILE_ATTRIBUTES) + return getStatus(INVALID_HANDLE_VALUE, result); + + // Handle reparse points. + if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { + ScopedFileHandle h( + ::CreateFileW(path_utf16.begin(), + 0, // Attributes only. + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + 0)); + if (!h) + return getStatus(INVALID_HANDLE_VALUE, result); } - return false; -} -bool Path::makeExecutableOnDisk(std::string* ErrMsg) { - // All files are executable on Windows (ignoring security attributes). - return false; -} + ScopedFileHandle h( + ::CreateFileW(path_utf16.begin(), 0, // Attributes only. + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + if (!h) + return getStatus(INVALID_HANDLE_VALUE, result); + + return getStatus(h, result); +} + +std::error_code status(int FD, file_status &Result) { + HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD)); + return getStatus(FileHandle, Result); +} + +std::error_code setLastModificationAndAccessTime(int FD, TimeValue Time) { + ULARGE_INTEGER UI; + UI.QuadPart = Time.toWin32Time(); + FILETIME FT; + FT.dwLowDateTime = UI.LowPart; + FT.dwHighDateTime = UI.HighPart; + HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD)); + if (!SetFileTime(FileHandle, NULL, &FT, &FT)) + return windows_error(::GetLastError()); + return std::error_code(); +} + +std::error_code mapped_file_region::init(int FD, bool CloseFD, uint64_t Offset) { + FileDescriptor = FD; + // Make sure that the requested size fits within SIZE_T. + if (Size > std::numeric_limits::max()) { + if (FileDescriptor) { + if (CloseFD) + _close(FileDescriptor); + } else + ::CloseHandle(FileHandle); + return make_error_code(errc::invalid_argument); + } -bool -Path::getDirectoryContents(std::set& result, std::string* ErrMsg) const { - WIN32_FILE_ATTRIBUTE_DATA fi; - if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) { - MakeErrMsg(ErrMsg, path + ": can't get status of file"); - return true; + DWORD flprotect; + switch (Mode) { + case readonly: flprotect = PAGE_READONLY; break; + case readwrite: flprotect = PAGE_READWRITE; break; + case priv: flprotect = PAGE_WRITECOPY; break; } - if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - if (ErrMsg) - *ErrMsg = path + ": not a directory"; - return true; + FileMappingHandle = + ::CreateFileMappingW(FileHandle, 0, flprotect, + (Offset + Size) >> 32, + (Offset + Size) & 0xffffffff, + 0); + if (FileMappingHandle == NULL) { + std::error_code ec = windows_error(GetLastError()); + if (FileDescriptor) { + if (CloseFD) + _close(FileDescriptor); + } else + ::CloseHandle(FileHandle); + return ec; } - result.clear(); - WIN32_FIND_DATA fd; - std::string searchpath = path; - if (path.size() == 0 || searchpath[path.size()-1] == '/') - searchpath += "*"; - else - searchpath += "/*"; + DWORD dwDesiredAccess; + switch (Mode) { + case readonly: dwDesiredAccess = FILE_MAP_READ; break; + case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break; + case priv: dwDesiredAccess = FILE_MAP_COPY; break; + } + Mapping = ::MapViewOfFile(FileMappingHandle, + dwDesiredAccess, + Offset >> 32, + Offset & 0xffffffff, + Size); + if (Mapping == NULL) { + std::error_code ec = windows_error(GetLastError()); + ::CloseHandle(FileMappingHandle); + if (FileDescriptor) { + if (CloseFD) + _close(FileDescriptor); + } else + ::CloseHandle(FileHandle); + return ec; + } - HANDLE h = FindFirstFile(searchpath.c_str(), &fd); - if (h == INVALID_HANDLE_VALUE) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) - return true; // not really an error, now is it? - MakeErrMsg(ErrMsg, path + ": Can't read directory: "); - return true; + if (Size == 0) { + MEMORY_BASIC_INFORMATION mbi; + SIZE_T Result = VirtualQuery(Mapping, &mbi, sizeof(mbi)); + if (Result == 0) { + std::error_code ec = windows_error(GetLastError()); + ::UnmapViewOfFile(Mapping); + ::CloseHandle(FileMappingHandle); + if (FileDescriptor) { + if (CloseFD) + _close(FileDescriptor); + } else + ::CloseHandle(FileHandle); + return ec; + } + Size = mbi.RegionSize; } - do { - if (fd.cFileName[0] == '.') - continue; - Path aPath(path); - aPath.appendComponent(&fd.cFileName[0]); - result.insert(aPath); - } while (FindNextFile(h, &fd)); - - DWORD err = GetLastError(); - FindClose(h); - if (err != ERROR_NO_MORE_FILES) { - SetLastError(err); - MakeErrMsg(ErrMsg, path + ": Can't read directory: "); - return true; + // Close all the handles except for the view. It will keep the other handles + // alive. + ::CloseHandle(FileMappingHandle); + if (FileDescriptor) { + if (CloseFD) + _close(FileDescriptor); // Also closes FileHandle. + } else + ::CloseHandle(FileHandle); + return std::error_code(); +} + +mapped_file_region::mapped_file_region(const Twine &path, + mapmode mode, + uint64_t length, + uint64_t offset, + std::error_code &ec) + : Mode(mode) + , Size(length) + , Mapping() + , FileDescriptor() + , FileHandle(INVALID_HANDLE_VALUE) + , FileMappingHandle() { + SmallString<128> path_storage; + SmallVector path_utf16; + + // Convert path to UTF-16. + if ((ec = UTF8ToUTF16(path.toStringRef(path_storage), path_utf16))) + return; + + // Get file handle for creating a file mapping. + FileHandle = ::CreateFileW(c_str(path_utf16), + Mode == readonly ? GENERIC_READ + : GENERIC_READ | GENERIC_WRITE, + Mode == readonly ? FILE_SHARE_READ + : 0, + 0, + Mode == readonly ? OPEN_EXISTING + : OPEN_ALWAYS, + Mode == readonly ? FILE_ATTRIBUTE_READONLY + : FILE_ATTRIBUTE_NORMAL, + 0); + if (FileHandle == INVALID_HANDLE_VALUE) { + ec = windows_error(::GetLastError()); + return; } - return false; -} -bool -Path::set(StringRef a_path) { - if (a_path.empty()) - return false; - std::string save(path); - path = a_path; - FlipBackSlashes(path); - if (!isValid()) { - path = save; - return false; + FileDescriptor = 0; + ec = init(FileDescriptor, true, offset); + if (ec) { + Mapping = FileMappingHandle = 0; + FileHandle = INVALID_HANDLE_VALUE; + FileDescriptor = 0; } - return true; } -bool -Path::appendComponent(StringRef name) { - if (name.empty()) - return false; - std::string save(path); - if (!path.empty()) { - size_t last = path.size() - 1; - if (path[last] != '/') - path += '/'; +mapped_file_region::mapped_file_region(int fd, + bool closefd, + mapmode mode, + uint64_t length, + uint64_t offset, + std::error_code &ec) + : Mode(mode) + , Size(length) + , Mapping() + , FileDescriptor(fd) + , FileHandle(INVALID_HANDLE_VALUE) + , FileMappingHandle() { + FileHandle = reinterpret_cast(_get_osfhandle(fd)); + if (FileHandle == INVALID_HANDLE_VALUE) { + if (closefd) + _close(FileDescriptor); + FileDescriptor = 0; + ec = make_error_code(errc::bad_file_descriptor); + return; } - path += name; - if (!isValid()) { - path = save; - return false; + + ec = init(FileDescriptor, closefd, offset); + if (ec) { + Mapping = FileMappingHandle = 0; + FileHandle = INVALID_HANDLE_VALUE; + FileDescriptor = 0; } - return true; } -bool -Path::eraseComponent() { - size_t slashpos = path.rfind('/',path.size()); - if (slashpos == path.size() - 1 || slashpos == std::string::npos) - return false; - std::string save(path); - path.erase(slashpos); - if (!isValid()) { - path = save; - return false; - } - return true; +mapped_file_region::~mapped_file_region() { + if (Mapping) + ::UnmapViewOfFile(Mapping); } -bool -Path::eraseSuffix() { - size_t dotpos = path.rfind('.',path.size()); - size_t slashpos = path.rfind('/',path.size()); - if (dotpos != std::string::npos) { - if (slashpos == std::string::npos || dotpos > slashpos+1) { - std::string save(path); - path.erase(dotpos, path.size()-dotpos); - if (!isValid()) { - path = save; - return false; - } - return true; - } - } - return false; +mapped_file_region::mapped_file_region(mapped_file_region &&other) + : Mode(other.Mode) + , Size(other.Size) + , Mapping(other.Mapping) + , FileDescriptor(other.FileDescriptor) + , FileHandle(other.FileHandle) + , FileMappingHandle(other.FileMappingHandle) { + other.Mapping = other.FileMappingHandle = 0; + other.FileHandle = INVALID_HANDLE_VALUE; + other.FileDescriptor = 0; } -inline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) { - if (ErrMsg) - *ErrMsg = std::string(pathname) + ": " + std::string(msg); - return true; +mapped_file_region::mapmode mapped_file_region::flags() const { + assert(Mapping && "Mapping failed but used anyway!"); + return Mode; } -bool -Path::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) { - // Get a writeable copy of the path name - size_t len = path.length(); - char *pathname = reinterpret_cast(_alloca(len+2)); - path.copy(pathname, len); - pathname[len] = 0; +uint64_t mapped_file_region::size() const { + assert(Mapping && "Mapping failed but used anyway!"); + return Size; +} - // Make sure it ends with a slash. - if (len == 0 || pathname[len - 1] != '/') { - pathname[len] = '/'; - pathname[++len] = 0; - } +char *mapped_file_region::data() const { + assert(Mode != readonly && "Cannot get non-const data for readonly mapping!"); + assert(Mapping && "Mapping failed but used anyway!"); + return reinterpret_cast(Mapping); +} - // Determine starting point for initial / search. - char *next = pathname; - if (pathname[0] == '/' && pathname[1] == '/') { - // Skip host name. - next = strchr(pathname+2, '/'); - if (next == NULL) - return PathMsg(ErrMsg, pathname, "badly formed remote directory"); +const char *mapped_file_region::const_data() const { + assert(Mapping && "Mapping failed but used anyway!"); + return reinterpret_cast(Mapping); +} - // Skip share name. - next = strchr(next+1, '/'); - if (next == NULL) - return PathMsg(ErrMsg, pathname,"badly formed remote directory"); +int mapped_file_region::alignment() { + SYSTEM_INFO SysInfo; + ::GetSystemInfo(&SysInfo); + return SysInfo.dwAllocationGranularity; +} + +std::error_code detail::directory_iterator_construct(detail::DirIterState &it, + StringRef path){ + SmallVector path_utf16; - next++; - if (*next == 0) - return PathMsg(ErrMsg, pathname, "badly formed remote directory"); + if (std::error_code ec = UTF8ToUTF16(path, path_utf16)) + return ec; + // Convert path to the format that Windows is happy with. + if (path_utf16.size() > 0 && + !is_separator(path_utf16[path.size() - 1]) && + path_utf16[path.size() - 1] != L':') { + path_utf16.push_back(L'\\'); + path_utf16.push_back(L'*'); } else { - if (pathname[1] == ':') - next += 2; // skip drive letter - if (*next == '/') - next++; // skip root directory + path_utf16.push_back(L'*'); } - // If we're supposed to create intermediate directories - if (create_parents) { - // Loop through the directory components until we're done - while (*next) { - next = strchr(next, '/'); - *next = 0; - if (!CreateDirectory(pathname, NULL) && - GetLastError() != ERROR_ALREADY_EXISTS) - return MakeErrMsg(ErrMsg, - std::string(pathname) + ": Can't create directory: "); - *next++ = '/'; - } - } else { - // Drop trailing slash. - pathname[len-1] = 0; - if (!CreateDirectory(pathname, NULL) && - GetLastError() != ERROR_ALREADY_EXISTS) { - return MakeErrMsg(ErrMsg, std::string(pathname) + - ": Can't create directory: "); - } + // Get the first directory entry. + WIN32_FIND_DATAW FirstFind; + ScopedFindHandle FindHandle(::FindFirstFileW(c_str(path_utf16), &FirstFind)); + if (!FindHandle) + return windows_error(::GetLastError()); + + size_t FilenameLen = ::wcslen(FirstFind.cFileName); + while ((FilenameLen == 1 && FirstFind.cFileName[0] == L'.') || + (FilenameLen == 2 && FirstFind.cFileName[0] == L'.' && + FirstFind.cFileName[1] == L'.')) + if (!::FindNextFileW(FindHandle, &FirstFind)) { + DWORD LastError = ::GetLastError(); + // Check for end. + if (LastError == ERROR_NO_MORE_FILES) + return detail::directory_iterator_destruct(it); + return windows_error(LastError); + } else + FilenameLen = ::wcslen(FirstFind.cFileName); + + // Construct the current directory entry. + SmallString<128> directory_entry_name_utf8; + if (std::error_code ec = + UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName), + directory_entry_name_utf8)) + return ec; + + it.IterationHandle = intptr_t(FindHandle.take()); + SmallString<128> directory_entry_path(path); + path::append(directory_entry_path, directory_entry_name_utf8.str()); + it.CurrentEntry = directory_entry(directory_entry_path.str()); + + return std::error_code(); +} + +std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) { + if (it.IterationHandle != 0) + // Closes the handle if it's valid. + ScopedFindHandle close(HANDLE(it.IterationHandle)); + it.IterationHandle = 0; + it.CurrentEntry = directory_entry(); + return std::error_code(); +} + +std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { + WIN32_FIND_DATAW FindData; + if (!::FindNextFileW(HANDLE(it.IterationHandle), &FindData)) { + DWORD LastError = ::GetLastError(); + // Check for end. + if (LastError == ERROR_NO_MORE_FILES) + return detail::directory_iterator_destruct(it); + return windows_error(LastError); + } + + size_t FilenameLen = ::wcslen(FindData.cFileName); + if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') || + (FilenameLen == 2 && FindData.cFileName[0] == L'.' && + FindData.cFileName[1] == L'.')) + return directory_iterator_increment(it); + + SmallString<128> directory_entry_path_utf8; + if (std::error_code ec = + UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName), + directory_entry_path_utf8)) + return ec; + + it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8)); + return std::error_code(); +} + +std::error_code openFileForRead(const Twine &Name, int &ResultFD) { + SmallString<128> PathStorage; + SmallVector PathUTF16; + + if (std::error_code EC = + UTF8ToUTF16(Name.toStringRef(PathStorage), PathUTF16)) + return EC; + + HANDLE H = ::CreateFileW(PathUTF16.begin(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (H == INVALID_HANDLE_VALUE) { + DWORD LastError = ::GetLastError(); + std::error_code EC = windows_error(LastError); + // Provide a better error message when trying to open directories. + // This only runs if we failed to open the file, so there is probably + // no performances issues. + if (LastError != ERROR_ACCESS_DENIED) + return EC; + if (is_directory(Name)) + return make_error_code(errc::is_a_directory); + return EC; } - return false; -} -bool -Path::createFileOnDisk(std::string* ErrMsg) { - // Create the file - HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, - FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) - return MakeErrMsg(ErrMsg, path + ": Can't create file: "); + int FD = ::_open_osfhandle(intptr_t(H), 0); + if (FD == -1) { + ::CloseHandle(H); + return windows_error(ERROR_INVALID_HANDLE); + } - CloseHandle(h); - return false; + ResultFD = FD; + return std::error_code(); } -bool -Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const { - WIN32_FILE_ATTRIBUTE_DATA fi; - if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) - return true; +std::error_code openFileForWrite(const Twine &Name, int &ResultFD, + sys::fs::OpenFlags Flags, unsigned Mode) { + // Verify that we don't have both "append" and "excl". + assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) && + "Cannot specify both 'excl' and 'append' file creation flags!"); - if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - // If it doesn't exist, we're done. - bool Exists; - if (fs::exists(path, Exists) || !Exists) - return false; + SmallString<128> PathStorage; + SmallVector PathUTF16; - char *pathname = reinterpret_cast(_alloca(path.length()+3)); - int lastchar = path.length() - 1 ; - path.copy(pathname, lastchar+1); - - // Make path end with '/*'. - if (pathname[lastchar] != '/') - pathname[++lastchar] = '/'; - pathname[lastchar+1] = '*'; - pathname[lastchar+2] = 0; - - if (remove_contents) { - WIN32_FIND_DATA fd; - HANDLE h = FindFirstFile(pathname, &fd); - - // It's a bad idea to alter the contents of a directory while enumerating - // its contents. So build a list of its contents first, then destroy them. - - if (h != INVALID_HANDLE_VALUE) { - std::vector list; - - do { - if (strcmp(fd.cFileName, ".") == 0) - continue; - if (strcmp(fd.cFileName, "..") == 0) - continue; - - Path aPath(path); - aPath.appendComponent(&fd.cFileName[0]); - list.push_back(aPath); - } while (FindNextFile(h, &fd)); - - DWORD err = GetLastError(); - FindClose(h); - if (err != ERROR_NO_MORE_FILES) { - SetLastError(err); - return MakeErrMsg(ErrStr, path + ": Can't read directory: "); - } - - for (std::vector::iterator I = list.begin(); I != list.end(); - ++I) { - Path &aPath = *I; - aPath.eraseFromDisk(true); - } - } else { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - return MakeErrMsg(ErrStr, path + ": Can't read directory: "); - } - } + if (std::error_code EC = + UTF8ToUTF16(Name.toStringRef(PathStorage), PathUTF16)) + return EC; - pathname[lastchar] = 0; - if (!RemoveDirectory(pathname)) - return MakeErrMsg(ErrStr, - std::string(pathname) + ": Can't destroy directory: "); - return false; - } else { - // Read-only files cannot be deleted on Windows. Must remove the read-only - // attribute first. - if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { - if (!SetFileAttributes(path.c_str(), - fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) - return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); - } + DWORD CreationDisposition; + if (Flags & F_Excl) + CreationDisposition = CREATE_NEW; + else if (Flags & F_Append) + CreationDisposition = OPEN_ALWAYS; + else + CreationDisposition = CREATE_ALWAYS; + + DWORD Access = GENERIC_WRITE; + if (Flags & F_RW) + Access |= GENERIC_READ; + + HANDLE H = ::CreateFileW(PathUTF16.begin(), Access, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + + if (H == INVALID_HANDLE_VALUE) { + DWORD LastError = ::GetLastError(); + std::error_code EC = windows_error(LastError); + // Provide a better error message when trying to open directories. + // This only runs if we failed to open the file, so there is probably + // no performances issues. + if (LastError != ERROR_ACCESS_DENIED) + return EC; + if (is_directory(Name)) + return make_error_code(errc::is_a_directory); + return EC; + } - if (!DeleteFile(path.c_str())) - return MakeErrMsg(ErrStr, path + ": Can't destroy file: "); - return false; + int OpenFlags = 0; + if (Flags & F_Append) + OpenFlags |= _O_APPEND; + + if (Flags & F_Text) + OpenFlags |= _O_TEXT; + + int FD = ::_open_osfhandle(intptr_t(H), OpenFlags); + if (FD == -1) { + ::CloseHandle(H); + return windows_error(ERROR_INVALID_HANDLE); } + + ResultFD = FD; + return std::error_code(); } +} // end namespace fs + +namespace path { -bool Path::getMagicNumber(std::string& Magic, unsigned len) const { - assert(len < 1024 && "Request for magic string too long"); - char* buf = reinterpret_cast(alloca(len)); +bool home_directory(SmallVectorImpl &result) { + wchar_t Path[MAX_PATH]; + if (::SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, + /*SHGFP_TYPE_CURRENT*/0, Path) != S_OK) + return false; - HANDLE h = CreateFile(path.c_str(), - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if (h == INVALID_HANDLE_VALUE) + if (UTF16ToUTF8(Path, ::wcslen(Path), result)) return false; - DWORD nRead = 0; - BOOL ret = ReadFile(h, buf, len, &nRead, NULL); - CloseHandle(h); + return true; +} - if (!ret || nRead != len) +static bool getTempDirEnvVar(const char *Var, SmallVectorImpl &Res) { + SmallVector NameUTF16; + if (windows::UTF8ToUTF16(Var, NameUTF16)) return false; - Magic = std::string(buf, len); + SmallVector Buf; + size_t Size = 1024; + do { + Buf.reserve(Size); + Size = + GetEnvironmentVariableW(NameUTF16.data(), Buf.data(), Buf.capacity()); + if (Size == 0) + return false; + + // Try again with larger buffer. + } while (Size > Buf.capacity()); + Buf.set_size(Size); + + if (windows::UTF16ToUTF8(Buf.data(), Size, Res)) + return false; return true; } -bool -Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) { - if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING)) - return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path - + "': "); +static bool getTempDirEnvVar(SmallVectorImpl &Res) { + const char *EnvironmentVariables[] = {"TMP", "TEMP", "USERPROFILE"}; + for (const char *Env : EnvironmentVariables) { + if (getTempDirEnvVar(Env, Res)) + return true; + } return false; } -bool -Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const { - // FIXME: should work on directories also. - if (!si.isFile) { - return true; - } +void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { + (void)ErasedOnReboot; + Result.clear(); - HANDLE h = CreateFile(path.c_str(), - FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if (h == INVALID_HANDLE_VALUE) - return true; + // Check whether the temporary directory is specified by an environment + // variable. + if (getTempDirEnvVar(Result)) + return; - BY_HANDLE_FILE_INFORMATION bhfi; - if (!GetFileInformationByHandle(h, &bhfi)) { - DWORD err = GetLastError(); - CloseHandle(h); - SetLastError(err); - return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: "); - } + // Fall back to a system default. + const char *DefaultResult = "C:\\TEMP"; + Result.append(DefaultResult, DefaultResult + strlen(DefaultResult)); +} +} // end namespace path - ULARGE_INTEGER ui; - ui.QuadPart = si.modTime.toWin32Time(); - FILETIME ft; - ft.dwLowDateTime = ui.LowPart; - ft.dwHighDateTime = ui.HighPart; - BOOL ret = SetFileTime(h, NULL, &ft, &ft); - DWORD err = GetLastError(); - CloseHandle(h); - if (!ret) { - SetLastError(err); - return MakeErrMsg(ErrMsg, path + ": SetFileTime: "); - } +namespace windows { +std::error_code UTF8ToUTF16(llvm::StringRef utf8, + llvm::SmallVectorImpl &utf16) { + if (!utf8.empty()) { + int len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(), + utf8.size(), utf16.begin(), 0); - // Best we can do with Unix permission bits is to interpret the owner - // writable bit. - if (si.mode & 0200) { - if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { - if (!SetFileAttributes(path.c_str(), - bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) - return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); - } - } else { - if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { - if (!SetFileAttributes(path.c_str(), - bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY)) - return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: "); - } - } + if (len == 0) + return windows_error(::GetLastError()); - return false; -} + utf16.reserve(len + 1); + utf16.set_size(len); -bool -CopyFile(const sys::Path &Dest, const sys::Path &Src, std::string* ErrMsg) { - // Can't use CopyFile macro defined in Windows.h because it would mess up the - // above line. We use the expansion it would have in a non-UNICODE build. - if (!::CopyFileA(Src.c_str(), Dest.c_str(), false)) - return MakeErrMsg(ErrMsg, "Can't copy '" + Src.str() + - "' to '" + Dest.str() + "': "); - return false; -} + len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(), + utf8.size(), utf16.begin(), utf16.size()); -bool -Path::makeUnique(bool reuse_current, std::string* ErrMsg) { - bool Exists; - if (reuse_current && (fs::exists(path, Exists) || !Exists)) - return false; // File doesn't exist already, just use it! - - // Reserve space for -XXXXXX at the end. - char *FNBuffer = (char*) alloca(path.size()+8); - unsigned offset = path.size(); - path.copy(FNBuffer, offset); - - // Find a numeric suffix that isn't used by an existing file. Assume there - // won't be more than 1 million files with the same prefix. Probably a safe - // bet. - static int FCounter = -1; - if (FCounter < 0) { - // Give arbitrary initial seed. - // FIXME: We should use sys::fs::unique_file() in future. - LARGE_INTEGER cnt64; - DWORD x = GetCurrentProcessId(); - x = (x << 16) | (x >> 16); - if (QueryPerformanceCounter(&cnt64)) // RDTSC - x ^= cnt64.HighPart ^ cnt64.LowPart; - FCounter = x % 1000000; + if (len == 0) + return windows_error(::GetLastError()); } - do { - sprintf(FNBuffer+offset, "-%06u", FCounter); - if (++FCounter > 999999) - FCounter = 0; - path = FNBuffer; - } while (!fs::exists(path, Exists) && Exists); - return false; + + // Make utf16 null terminated. + utf16.push_back(0); + utf16.pop_back(); + + return std::error_code(); } -bool -Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) { - // Make this into a unique file name - makeUnique(reuse_current, ErrMsg); +std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, + llvm::SmallVectorImpl &utf8) { + if (utf16_len) { + // Get length. + int len = ::WideCharToMultiByte(CP_UTF8, 0, utf16, utf16_len, utf8.begin(), + 0, NULL, NULL); - // Now go and create it - HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, - FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) - return MakeErrMsg(ErrMsg, path + ": can't create file"); + if (len == 0) + return windows_error(::GetLastError()); - CloseHandle(h); - return false; -} + utf8.reserve(len); + utf8.set_size(len); -/// MapInFilePages - Not yet implemented on win32. -const char *Path::MapInFilePages(int FD, size_t FileSize, off_t Offset) { - return 0; -} + // Now do the actual conversion. + len = ::WideCharToMultiByte(CP_UTF8, 0, utf16, utf16_len, utf8.data(), + utf8.size(), NULL, NULL); -/// MapInFilePages - Not yet implemented on win32. -void Path::UnMapFilePages(const char *Base, size_t FileSize) { - assert(0 && "NOT IMPLEMENTED"); -} + if (len == 0) + return windows_error(::GetLastError()); + } + // Make utf8 null terminated. + utf8.push_back(0); + utf8.pop_back(); + + return std::error_code(); } -} +} // end namespace windows +} // end namespace sys +} // end namespace llvm