Import filesystem directory iterators from llvm. (#43)

Note: these are similar to the C++17 ones, but don't have quite the same API.
This commit is contained in:
Peter Johnson
2017-11-03 11:40:42 -07:00
committed by GitHub
parent a6c7789b5e
commit 8e797a1a1d
7 changed files with 1947 additions and 298 deletions

View File

@@ -657,6 +657,143 @@ bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot) {
}
} // end namespace path
namespace fs {
std::error_code getUniqueID(const Twine Path, UniqueID &Result) {
file_status Status;
std::error_code EC = status(Path, Status);
if (EC)
return EC;
Result = Status.getUniqueID();
return std::error_code();
}
static std::error_code make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path,
bool use_current_directory) {
StringRef p(path.data(), path.size());
bool rootDirectory = path::has_root_directory(p),
#ifdef _WIN32
rootName = path::has_root_name(p);
#else
rootName = true;
#endif
// Already absolute.
if (rootName && rootDirectory)
return std::error_code();
// All of the following conditions will need the current directory.
SmallString<128> current_dir;
if (use_current_directory)
current_directory.toVector(current_dir);
else if (std::error_code ec = current_path(current_dir))
return ec;
// Relative path. Prepend the current directory.
if (!rootName && !rootDirectory) {
// Append path to the current directory.
path::append(current_dir, p);
// Set path to the result.
path.swap(current_dir);
return std::error_code();
}
if (!rootName && rootDirectory) {
StringRef cdrn = path::root_name(current_dir);
SmallString<128> curDirRootName(cdrn.begin(), cdrn.end());
path::append(curDirRootName, p);
// Set path to the result.
path.swap(curDirRootName);
return std::error_code();
}
if (rootName && !rootDirectory) {
StringRef pRootName = path::root_name(p);
StringRef bRootDirectory = path::root_directory(current_dir);
StringRef bRelativePath = path::relative_path(current_dir);
StringRef pRelativePath = path::relative_path(p);
SmallString<128> res;
path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath);
path.swap(res);
return std::error_code();
}
assert(false && "All rootName and rootDirectory combinations should have "
"occurred above!");
return std::error_code();
}
std::error_code make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path) {
return make_absolute(current_directory, path, true);
}
std::error_code make_absolute(SmallVectorImpl<char> &path) {
return make_absolute(Twine(), path, false);
}
bool exists(file_status status) {
return status_known(status) && status.type() != file_type::file_not_found;
}
bool status_known(file_status s) {
return s.type() != file_type::status_error;
}
bool is_directory(file_status status) {
return status.type() == file_type::directory_file;
}
std::error_code is_directory(const Twine &path, bool &result) {
file_status st;
if (std::error_code ec = status(path, st))
return ec;
result = is_directory(st);
return std::error_code();
}
bool is_regular_file(file_status status) {
return status.type() == file_type::regular_file;
}
std::error_code is_regular_file(const Twine &path, bool &result) {
file_status st;
if (std::error_code ec = status(path, st))
return ec;
result = is_regular_file(st);
return std::error_code();
}
bool is_other(file_status status) {
return exists(status) &&
!is_regular_file(status) &&
!is_directory(status);
}
std::error_code is_other(const Twine &Path, bool &Result) {
file_status FileStatus;
if (std::error_code EC = status(Path, FileStatus))
return EC;
Result = is_other(FileStatus);
return std::error_code();
}
void directory_entry::replace_filename(const Twine &filename, file_status st) {
SmallString<128> path = path::parent_path(Path);
path::append(path, filename);
Path = path.str();
Status = st;
}
std::error_code directory_entry::status(file_status &result) const {
return fs::status(Path, result);
}
} // end namespace fs
} // end namespace sys
} // end namespace llvm

View File

@@ -20,6 +20,10 @@
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)
#include <sys/param.h>
#include <sys/types.h>
#include <unistd.h>
@@ -27,6 +31,180 @@
namespace llvm {
namespace sys {
namespace fs {
UniqueID file_status::getUniqueID() const {
return UniqueID(fs_st_dev, fs_st_ino);
}
std::error_code current_path(SmallVectorImpl<char> &result) {
result.clear();
const char *pwd = ::getenv("PWD");
llvm::sys::fs::file_status PWDStatus, DotStatus;
if (pwd && llvm::sys::path::is_absolute(pwd) &&
!llvm::sys::fs::status(pwd, PWDStatus) &&
!llvm::sys::fs::status(".", DotStatus) &&
PWDStatus.getUniqueID() == DotStatus.getUniqueID()) {
result.append(pwd, pwd + strlen(pwd));
return std::error_code();
}
#ifdef MAXPATHLEN
result.reserve(MAXPATHLEN);
#else
result.reserve(1024);
#endif
while (true) {
if (::getcwd(result.data(), result.capacity()) == nullptr) {
// See if there was a real error.
if (errno != ENOMEM)
return std::error_code(errno, std::generic_category());
// Otherwise there just wasn't enough space.
result.reserve(result.capacity() * 2);
} else
break;
}
result.set_size(strlen(result.data()));
return std::error_code();
}
static int convertAccessMode(AccessMode Mode) {
switch (Mode) {
case AccessMode::Exist:
return F_OK;
case AccessMode::Write:
return W_OK;
case AccessMode::Execute:
return R_OK | X_OK; // scripts also need R_OK.
default:
return F_OK;
}
}
std::error_code access(const Twine &Path, AccessMode Mode) {
SmallString<128> PathStorage;
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
if (::access(P.begin(), convertAccessMode(Mode)) == -1)
return std::error_code(errno, std::generic_category());
if (Mode == AccessMode::Execute) {
// Don't say that directories are executable.
struct stat buf;
if (0 != stat(P.begin(), &buf))
return std::make_error_code(std::errc::permission_denied);
if (!S_ISREG(buf.st_mode))
return std::make_error_code(std::errc::permission_denied);
}
return std::error_code();
}
bool equivalent(file_status A, file_status B) {
assert(status_known(A) && status_known(B));
return A.fs_st_dev == B.fs_st_dev &&
A.fs_st_ino == B.fs_st_ino;
}
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 std::error_code fillStatus(int StatRet, const struct stat &Status,
file_status &Result) {
if (StatRet != 0) {
std::error_code ec(errno, std::generic_category());
if (ec == std::errc::no_such_file_or_directory)
Result = file_status(file_type::file_not_found);
else
Result = file_status(file_type::status_error);
return ec;
}
file_type Type = file_type::type_unknown;
if (S_ISDIR(Status.st_mode))
Type = file_type::directory_file;
else if (S_ISREG(Status.st_mode))
Type = file_type::regular_file;
else if (S_ISBLK(Status.st_mode))
Type = file_type::block_file;
else if (S_ISCHR(Status.st_mode))
Type = file_type::character_file;
else if (S_ISFIFO(Status.st_mode))
Type = file_type::fifo_file;
else if (S_ISSOCK(Status.st_mode))
Type = file_type::socket_file;
perms Perms = static_cast<perms>(Status.st_mode);
Result =
file_status(Type, Perms, Status.st_dev, Status.st_ino, Status.st_atime,
Status.st_mtime, Status.st_uid, Status.st_gid,
Status.st_size);
return std::error_code();
}
std::error_code status(const Twine &Path, file_status &Result) {
SmallString<128> PathStorage;
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
struct stat Status;
int StatRet = ::stat(P.begin(), &Status);
return fillStatus(StatRet, Status, Result);
}
std::error_code status(int FD, file_status &Result) {
struct stat Status;
int StatRet = ::fstat(FD, &Status);
return fillStatus(StatRet, Status, Result);
}
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path){
SmallString<128> path_null(path);
DIR *directory = ::opendir(path_null.c_str());
if (!directory)
return std::error_code(errno, std::generic_category());
it.IterationHandle = reinterpret_cast<intptr_t>(directory);
// Add something for replace_filename to replace.
path::append(path_null, ".");
it.CurrentEntry = directory_entry(path_null.str());
return directory_iterator_increment(it);
}
std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
if (it.IterationHandle)
::closedir(reinterpret_cast<DIR *>(it.IterationHandle));
it.IterationHandle = 0;
it.CurrentEntry = directory_entry();
return std::error_code();
}
std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
errno = 0;
dirent *cur_dir = ::readdir(reinterpret_cast<DIR *>(it.IterationHandle));
if (cur_dir == nullptr && errno != 0) {
return std::error_code(errno, std::generic_category());
} else if (cur_dir != nullptr) {
StringRef name(cur_dir->d_name, NAMLEN(cur_dir));
if ((name.size() == 1 && name[0] == '.') ||
(name.size() == 2 && name[0] == '.' && name[1] == '.'))
return directory_iterator_increment(it);
it.CurrentEntry.replace_filename(name);
} else
return directory_iterator_destruct(it);
return std::error_code();
}
#if !defined(F_GETPATH)
static bool hasProcSelfFD() {
@@ -103,11 +281,7 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
}
} // end namespace fs
} // end namespace sys
} // end namespace llvm
namespace llvm {
namespace sys {
namespace path {
bool home_directory(SmallVectorImpl<char> &result) {

View File

@@ -17,22 +17,565 @@
//===----------------------------------------------------------------------===//
#include "llvm/STLExtras.h"
#include "llvm/WindowsError.h"
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <windows.h>
// 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 <shlobj.h>
#include "llvm/WindowsError.h"
#undef max
#ifdef _MSC_VER
# pragma comment(lib, "shell32.lib")
# pragma comment(lib, "ole32.lib")
#endif
using namespace llvm;
using llvm::sys::windows::UTF8ToUTF16;
using llvm::sys::windows::UTF16ToUTF8;
using llvm::sys::path::widenPath;
static bool is_separator(const wchar_t value) {
switch (value) {
case L'\\':
case L'/':
return true;
default:
return false;
}
}
namespace llvm {
namespace sys {
namespace path {
// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the
// path is longer than CreateDirectory can tolerate, make it absolute and
// prefixed by '\\?\'.
std::error_code widenPath(const Twine &Path8,
SmallVectorImpl<wchar_t> &Path16) {
const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename.
// Several operations would convert Path8 to SmallString; more efficient to
// do it once up front.
SmallString<128> Path8Str;
Path8.toVector(Path8Str);
// If we made this path absolute, how much longer would it get?
size_t CurPathLen;
if (llvm::sys::path::is_absolute(Twine(Path8Str)))
CurPathLen = 0; // No contribution from current_path needed.
else {
CurPathLen = ::GetCurrentDirectoryW(0, NULL);
if (CurPathLen == 0)
return mapWindowsError(::GetLastError());
}
// Would the absolute path be longer than our limit?
if ((Path8Str.size() + CurPathLen) >= MaxDirLen &&
!Path8Str.startswith("\\\\?\\")) {
SmallString<2*MAX_PATH> FullPath("\\\\?\\");
if (CurPathLen) {
SmallString<80> CurPath;
if (std::error_code EC = llvm::sys::fs::current_path(CurPath))
return EC;
FullPath.append(CurPath);
}
// Traverse the requested path, canonicalizing . and .. as we go (because
// the \\?\ prefix is documented to treat them as real components).
// The iterators don't report separators and append() always attaches
// preferred_separator so we don't need to call native() on the result.
for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(Path8Str),
E = llvm::sys::path::end(Path8Str);
I != E; ++I) {
if (I->size() == 1 && *I == ".")
continue;
if (I->size() == 2 && *I == "..")
llvm::sys::path::remove_filename(FullPath);
else
llvm::sys::path::append(FullPath, *I);
}
return UTF8ToUTF16(FullPath, Path16);
}
// Just use the caller's original path.
return UTF8ToUTF16(Path8Str, Path16);
}
} // end namespace path
namespace fs {
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<uint64_t>(FileIndexHigh) << 32ULL) |
static_cast<uint64_t>(FileIndexLow);
return UniqueID(VolumeSerialNumber, FileID);
}
std::error_code current_path(SmallVectorImpl<char> &result) {
SmallVector<wchar_t, MAX_PATH> cur_path;
DWORD len = MAX_PATH;
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 mapWindowsError(::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);
}
std::error_code access(const Twine &Path, AccessMode Mode) {
SmallVector<wchar_t, 128> PathUtf16;
if (std::error_code EC = widenPath(Path, PathUtf16))
return EC;
DWORD Attributes = ::GetFileAttributesW(PathUtf16.begin());
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 mapWindowsError(LastError);
return std::make_error_code(std::errc::no_such_file_or_directory);
}
if (Mode == AccessMode::Write && (Attributes & FILE_ATTRIBUTE_READONLY))
return std::make_error_code(std::errc::permission_denied);
return std::error_code();
}
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.LastAccessedTimeHigh == B.LastAccessedTimeHigh &&
A.LastAccessedTimeLow == B.LastAccessedTimeLow &&
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 *const 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;
// 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;
}
// The path isn't what we consider reserved.
return false;
}
static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
if (FileHandle == INVALID_HANDLE_VALUE)
goto handle_status_error;
switch (::GetFileType(FileHandle)) {
default:
Result = file_status(file_type::type_unknown);
return std::error_code();
case FILE_TYPE_UNKNOWN: {
DWORD Err = ::GetLastError();
if (Err != NO_ERROR)
return mapWindowsError(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();
}
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.ftLastAccessTime.dwHighDateTime,
Info.ftLastAccessTime.dwLowDateTime,
Info.ftLastWriteTime.dwHighDateTime,
Info.ftLastWriteTime.dwLowDateTime,
Info.dwVolumeSerialNumber, Info.nFileSizeHigh,
Info.nFileSizeLow, Info.nFileIndexHigh, Info.nFileIndexLow);
return std::error_code();
}
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 mapWindowsError(LastError);
}
std::error_code status(const Twine &path, file_status &result) {
SmallString<128> path_storage;
SmallVector<wchar_t, 128> path_utf16;
StringRef path8 = path.toStringRef(path_storage);
if (isReservedName(path8)) {
result = file_status(file_type::character_file);
return std::error_code();
}
if (std::error_code ec = widenPath(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);
}
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<HANDLE>(_get_osfhandle(FD));
return getStatus(FileHandle, Result);
}
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path){
SmallVector<wchar_t, 128> path_utf16;
if (std::error_code ec = widenPath(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 {
path_utf16.push_back(L'*');
}
// Get the first directory entry.
WIN32_FIND_DATAW FirstFind;
ScopedFindHandle FindHandle(::FindFirstFileW(c_str(path_utf16), &FirstFind));
if (!FindHandle)
return mapWindowsError(::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 mapWindowsError(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);
it.CurrentEntry = directory_entry(directory_entry_path);
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 mapWindowsError(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,
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_ATTRIBUTE_NORMAL, NULL);
if (H == INVALID_HANDLE_VALUE) {
DWORD LastError = ::GetLastError();
std::error_code EC = mapWindowsError(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 std::make_error_code(std::errc::is_a_directory);
return EC;
}
int FD = ::_open_osfhandle(intptr_t(H), 0);
if (FD == -1) {
::CloseHandle(H);
return mapWindowsError(ERROR_INVALID_HANDLE);
}
// 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()));
}
}
ResultFD = FD;
return std::error_code();
}
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
OpenFlags Flags, unsigned Mode) {
// Verify that we don't have both "append" and "excl".
assert((!(Flags & F_Excl) || !(Flags & F_Append)) &&
"Cannot specify both 'excl' and 'append' file creation flags!");
SmallVector<wchar_t, 128> PathUTF16;
if (std::error_code EC = widenPath(Name, PathUTF16))
return EC;
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 = mapWindowsError(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 std::make_error_code(std::errc::is_a_directory);
return EC;
}
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 mapWindowsError(ERROR_INVALID_HANDLE);
}
ResultFD = FD;
return std::error_code();
}
} // end namespace fs
namespace path {
static bool getKnownFolderPath(KNOWNFOLDERID folderId,
SmallVectorImpl<char> &result) {
wchar_t *path = nullptr;
if (::SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, nullptr, &path) != S_OK)
return false;
bool ok = !UTF16ToUTF8(path, ::wcslen(path), result);
::CoTaskMemFree(path);
return ok;
}
bool getUserCacheDir(SmallVectorImpl<char> &Result) {
return getKnownFolderPath(FOLDERID_LocalAppData, Result);
}
bool home_directory(SmallVectorImpl<char> &result) {
return getKnownFolderPath(FOLDERID_Profile, result);
}
static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) {
SmallVector<wchar_t, 1024> Buf;
size_t Size = 1024;
do {
Buf.reserve(Size);
Size = GetEnvironmentVariableW(Var, Buf.data(), Buf.capacity());
if (Size == 0)
return false;
// Try again with larger buffer.
} while (Size > Buf.capacity());
Buf.set_size(Size);
return !windows::UTF16ToUTF8(Buf.data(), Size, Res);
}
static bool getTempDirEnvVar(SmallVectorImpl<char> &Res) {
const wchar_t *EnvironmentVariables[] = {L"TMP", L"TEMP", L"USERPROFILE"};
for (auto *Env : EnvironmentVariables) {
if (getTempDirEnvVar(Env, Res))
return true;
}
return false;
}
void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
(void)ErasedOnReboot;
Result.clear();
// Check whether the temporary directory is specified by an environment var.
// This matches GetTempPath logic to some degree. GetTempPath is not used
// directly as it cannot handle evn var longer than 130 chars on Windows 7
// (fixed on Windows 8).
if (getTempDirEnvVar(Result)) {
assert(!Result.empty() && "Unexpected empty path");
native(Result); // Some Unix-like shells use Unix path separator in $TMP.
fs::make_absolute(Result); // Make it absolute if not already.
return;
}
// Fall back to a system default.
const char *DefaultResult = "C:\\Temp";
Result.append(DefaultResult, DefaultResult + strlen(DefaultResult));
}
} // end namespace path
namespace windows {
std::error_code UTF8ToUTF16(llvm::StringRef utf8,
llvm::SmallVectorImpl<wchar_t> &utf16) {
@@ -101,270 +644,5 @@ std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
}
} // end namespace windows
using llvm::sys::windows::UTF8ToUTF16;
using llvm::sys::windows::UTF16ToUTF8;
namespace fs {
std::error_code current_path(SmallVectorImpl<char> &result) {
SmallVector<wchar_t, MAX_PATH> cur_path;
DWORD len = MAX_PATH;
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 mapWindowsError(::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);
}
} // end namespace fs
namespace path {
// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the
// path is longer than CreateDirectory can tolerate, make it absolute and
// prefixed by '\\?\'.
std::error_code widenPath(const Twine &Path8,
SmallVectorImpl<wchar_t> &Path16) {
const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename.
// Several operations would convert Path8 to SmallString; more efficient to
// do it once up front.
SmallString<128> Path8Str;
Path8.toVector(Path8Str);
// If we made this path absolute, how much longer would it get?
size_t CurPathLen;
if (llvm::sys::path::is_absolute(Twine(Path8Str)))
CurPathLen = 0; // No contribution from current_path needed.
else {
CurPathLen = ::GetCurrentDirectoryW(0, NULL);
if (CurPathLen == 0)
return mapWindowsError(::GetLastError());
}
// Would the absolute path be longer than our limit?
if ((Path8Str.size() + CurPathLen) >= MaxDirLen &&
!Path8Str.startswith("\\\\?\\")) {
SmallString<2*MAX_PATH> FullPath("\\\\?\\");
if (CurPathLen) {
SmallString<80> CurPath;
if (std::error_code EC = llvm::sys::fs::current_path(CurPath))
return EC;
FullPath.append(CurPath);
}
// Traverse the requested path, canonicalizing . and .. as we go (because
// the \\?\ prefix is documented to treat them as real components).
// The iterators don't report separators and append() always attaches
// preferred_separator so we don't need to call native() on the result.
for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(Path8Str),
E = llvm::sys::path::end(Path8Str);
I != E; ++I) {
if (I->size() == 1 && *I == ".")
continue;
if (I->size() == 2 && *I == "..")
llvm::sys::path::remove_filename(FullPath);
else
llvm::sys::path::append(FullPath, *I);
}
return UTF8ToUTF16(FullPath, Path16);
}
// Just use the caller's original path.
return UTF8ToUTF16(Path8Str, Path16);
}
} // end namespace path
using llvm::sys::path::widenPath;
namespace fs {
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
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_ATTRIBUTE_NORMAL, NULL);
if (H == INVALID_HANDLE_VALUE) {
DWORD LastError = ::GetLastError();
std::error_code EC = mapWindowsError(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;
}
int FD = ::_open_osfhandle(intptr_t(H), 0);
if (FD == -1) {
::CloseHandle(H);
return mapWindowsError(ERROR_INVALID_HANDLE);
}
// 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()));
}
}
ResultFD = FD;
return std::error_code();
}
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
OpenFlags Flags, unsigned Mode) {
// Verify that we don't have both "append" and "excl".
assert((!(Flags & F_Excl) || !(Flags & F_Append)) &&
"Cannot specify both 'excl' and 'append' file creation flags!");
SmallVector<wchar_t, 128> PathUTF16;
if (std::error_code EC = widenPath(Name, PathUTF16))
return EC;
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 = mapWindowsError(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;
}
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 mapWindowsError(ERROR_INVALID_HANDLE);
}
ResultFD = FD;
return std::error_code();
}
} // end namespace fs
namespace path {
static bool getKnownFolderPath(KNOWNFOLDERID folderId,
SmallVectorImpl<char> &result) {
wchar_t *path = nullptr;
if (::SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, nullptr, &path) != S_OK)
return false;
bool ok = !UTF16ToUTF8(path, ::wcslen(path), result);
::CoTaskMemFree(path);
return ok;
}
bool getUserCacheDir(SmallVectorImpl<char> &Result) {
return getKnownFolderPath(FOLDERID_LocalAppData, Result);
}
bool home_directory(SmallVectorImpl<char> &result) {
return getKnownFolderPath(FOLDERID_Profile, result);
}
static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) {
SmallVector<wchar_t, 1024> Buf;
size_t Size = 1024;
do {
Buf.reserve(Size);
Size = GetEnvironmentVariableW(Var, Buf.data(), Buf.capacity());
if (Size == 0)
return false;
// Try again with larger buffer.
} while (Size > Buf.capacity());
Buf.set_size(Size);
return !windows::UTF16ToUTF8(Buf.data(), Size, Res);
}
static bool getTempDirEnvVar(SmallVectorImpl<char> &Res) {
const wchar_t *EnvironmentVariables[] = {L"TMP", L"TEMP", L"USERPROFILE"};
for (auto *Env : EnvironmentVariables) {
if (getTempDirEnvVar(Env, Res))
return true;
}
return false;
}
void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
(void)ErasedOnReboot;
Result.clear();
// Check whether the temporary directory is specified by an environment var.
// This matches GetTempPath logic to some degree. GetTempPath is not used
// directly as it cannot handle evn var longer than 130 chars on Windows 7
// (fixed on Windows 8).
if (getTempDirEnvVar(Result)) {
assert(!Result.empty() && "Unexpected empty path");
native(Result); // Some Unix-like shells use Unix path separator in $TMP.
//fs::make_absolute(Result); // Make it absolute if not already.
return;
}
// Fall back to a system default.
const char *DefaultResult = "C:\\Temp";
Result.append(DefaultResult, DefaultResult + strlen(DefaultResult));
}
} // end namespace path
} // end namespace sys
} // end namespace llvm

View File

@@ -0,0 +1,211 @@
//===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines things specific to Windows implementations. In addition to
// providing some helpers for working with win32 APIs, this header wraps
// <windows.h> with some portability macros. Always include WindowsSupport.h
// instead of including <windows.h> directly.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//=== WARNING: Implementation here must contain only generic Win32 code that
//=== is guaranteed to work on *all* Win32 variants.
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H
#define LLVM_SUPPORT_WINDOWSSUPPORT_H
// mingw-w64 tends to define it as 0x0502 in its headers.
#undef _WIN32_WINNT
#undef _WIN32_IE
// Require at least Windows 7 API.
#define _WIN32_WINNT 0x0601
#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "llvm/SmallVector.h"
#include "llvm/StringExtras.h"
#include "llvm/StringRef.h"
#include "llvm/Twine.h"
#include "llvm/Compiler.h"
#include <system_error>
#include <windows.h>
#include <cassert>
#include <string>
/// Determines if the program is running on Windows 8 or newer. This
/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't
/// yet have VersionHelpers.h, so we have our own helper.
inline bool RunningWindows8OrGreater() {
// Windows 8 is version 6.2, service pack 0.
OSVERSIONINFOEXW osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
osvi.dwMajorVersion = 6;
osvi.dwMinorVersion = 2;
osvi.wServicePackMajor = 0;
DWORDLONG Mask = 0;
Mask = VerSetConditionMask(Mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
Mask = VerSetConditionMask(Mask, VER_MINORVERSION, VER_GREATER_EQUAL);
Mask = VerSetConditionMask(Mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION |
VER_SERVICEPACKMAJOR,
Mask) != FALSE;
}
inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) {
if (!ErrMsg)
return true;
char *buffer = NULL;
DWORD LastError = GetLastError();
DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL, LastError, 0, (LPSTR)&buffer, 1, NULL);
if (R)
*ErrMsg = prefix + ": " + buffer;
else
*ErrMsg = prefix + ": Unknown error";
*ErrMsg += " (0x" + llvm::utohexstr(LastError) + ")";
LocalFree(buffer);
return R != 0;
}
template <typename HandleTraits>
class ScopedHandle {
typedef typename HandleTraits::handle_type handle_type;
handle_type Handle;
ScopedHandle(const ScopedHandle &other); // = delete;
void operator=(const ScopedHandle &other); // = delete;
public:
ScopedHandle()
: Handle(HandleTraits::GetInvalid()) {}
explicit ScopedHandle(handle_type h)
: Handle(h) {}
~ScopedHandle() {
if (HandleTraits::IsValid(Handle))
HandleTraits::Close(Handle);
}
handle_type take() {
handle_type t = Handle;
Handle = HandleTraits::GetInvalid();
return t;
}
ScopedHandle &operator=(handle_type h) {
if (HandleTraits::IsValid(Handle))
HandleTraits::Close(Handle);
Handle = h;
return *this;
}
// True if Handle is valid.
explicit operator bool() const {
return HandleTraits::IsValid(Handle) ? true : false;
}
operator handle_type() const {
return Handle;
}
};
struct CommonHandleTraits {
typedef HANDLE handle_type;
static handle_type GetInvalid() {
return INVALID_HANDLE_VALUE;
}
static void Close(handle_type h) {
::CloseHandle(h);
}
static bool IsValid(handle_type h) {
return h != GetInvalid();
}
};
struct JobHandleTraits : CommonHandleTraits {
static handle_type GetInvalid() {
return NULL;
}
};
struct RegTraits : CommonHandleTraits {
typedef HKEY handle_type;
static handle_type GetInvalid() {
return NULL;
}
static void Close(handle_type h) {
::RegCloseKey(h);
}
static bool IsValid(handle_type h) {
return h != GetInvalid();
}
};
struct FindHandleTraits : CommonHandleTraits {
static void Close(handle_type h) {
::FindClose(h);
}
};
struct FileHandleTraits : CommonHandleTraits {};
typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
typedef ScopedHandle<RegTraits> ScopedRegHandle;
typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
namespace llvm {
template <class T>
class SmallVectorImpl;
template <class T>
typename SmallVectorImpl<T>::const_pointer
c_str(SmallVectorImpl<T> &str) {
str.push_back(0);
str.pop_back();
return str.data();
}
namespace sys {
namespace path {
std::error_code widenPath(const Twine &Path8,
SmallVectorImpl<wchar_t> &Path16);
} // end namespace path
namespace windows {
std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
/// Convert from UTF16 to the current code page used in the system
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
} // end namespace windows
} // end namespace sys
} // end namespace llvm.
#endif

View File

@@ -50,30 +50,7 @@
#endif
#if defined(_WIN32)
#include <windows.h>
/// Determines if the program is running on Windows 8 or newer. This
/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't
/// yet have VersionHelpers.h, so we have our own helper.
static inline bool RunningWindows8OrGreater() {
// Windows 8 is version 6.2, service pack 0.
OSVERSIONINFOEXW osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
osvi.dwMajorVersion = 6;
osvi.dwMinorVersion = 2;
osvi.wServicePackMajor = 0;
DWORDLONG Mask = 0;
Mask = VerSetConditionMask(Mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
Mask = VerSetConditionMask(Mask, VER_MINORVERSION, VER_GREATER_EQUAL);
Mask = VerSetConditionMask(Mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION |
VER_SERVICEPACKMAJOR,
Mask) != FALSE;
}
#include "Windows/WindowsSupport.h"
#endif
using namespace llvm;

View File

@@ -27,19 +27,388 @@
#ifndef LLVM_SUPPORT_FILESYSTEM_H
#define LLVM_SUPPORT_FILESYSTEM_H
#include "llvm/IntrusiveRefCntPtr.h"
#include "llvm/SmallString.h"
#include "llvm/Twine.h"
#include <sys/stat.h>
#include <ctime>
#include <stack>
#include <system_error>
#include <tuple>
#include <vector>
namespace llvm {
template <typename T> class SmallVectorImpl;
namespace sys {
namespace fs {
/// An enumeration for the file system's view of the type.
enum class file_type {
status_error,
file_not_found,
regular_file,
directory_file,
symlink_file,
block_file,
character_file,
fifo_file,
socket_file,
type_unknown
};
enum perms {
no_perms = 0,
owner_read = 0400,
owner_write = 0200,
owner_exe = 0100,
owner_all = owner_read | owner_write | owner_exe,
group_read = 040,
group_write = 020,
group_exe = 010,
group_all = group_read | group_write | group_exe,
others_read = 04,
others_write = 02,
others_exe = 01,
others_all = others_read | others_write | others_exe,
all_read = owner_read | group_read | others_read,
all_write = owner_write | group_write | others_write,
all_exe = owner_exe | group_exe | others_exe,
all_all = owner_all | group_all | others_all,
set_uid_on_exe = 04000,
set_gid_on_exe = 02000,
sticky_bit = 01000,
perms_not_known = 0xFFFF
};
// Helper functions so that you can use & and | to manipulate perms bits:
inline perms operator|(perms l, perms r) {
return static_cast<perms>(static_cast<unsigned short>(l) |
static_cast<unsigned short>(r));
}
inline perms operator&(perms l, perms r) {
return static_cast<perms>(static_cast<unsigned short>(l) &
static_cast<unsigned short>(r));
}
inline perms &operator|=(perms &l, perms r) {
l = l | r;
return l;
}
inline perms &operator&=(perms &l, perms r) {
l = l & r;
return l;
}
inline perms operator~(perms x) {
return static_cast<perms>(
static_cast<unsigned short>(~static_cast<unsigned short>(x)));
}
class UniqueID {
uint64_t Device;
uint64_t File;
public:
UniqueID() = default;
UniqueID(uint64_t Device, uint64_t File) : Device(Device), File(File) {}
bool operator==(const UniqueID &Other) const {
return Device == Other.Device && File == Other.File;
}
bool operator!=(const UniqueID &Other) const { return !(*this == Other); }
bool operator<(const UniqueID &Other) const {
return std::tie(Device, File) < std::tie(Other.Device, Other.File);
}
uint64_t getDevice() const { return Device; }
uint64_t getFile() const { return File; }
};
/// file_status - Represents the result of a call to stat and friends. It has
/// a platform-specific member to store the result.
class file_status
{
#ifdef _WIN32
uint32_t LastAccessedTimeHigh;
uint32_t LastAccessedTimeLow;
uint32_t LastWriteTimeHigh;
uint32_t LastWriteTimeLow;
uint32_t VolumeSerialNumber;
uint32_t FileSizeHigh;
uint32_t FileSizeLow;
uint32_t FileIndexHigh;
uint32_t FileIndexLow;
#else
dev_t fs_st_dev;
ino_t fs_st_ino;
time_t fs_st_atime;
time_t fs_st_mtime;
uid_t fs_st_uid;
gid_t fs_st_gid;
off_t fs_st_size;
#endif
friend bool equivalent(file_status A, file_status B);
file_type Type;
perms Perms;
public:
#ifdef _WIN32
file_status()
: LastAccessedTimeHigh(0), LastAccessedTimeLow(0), LastWriteTimeHigh(0),
LastWriteTimeLow(0), VolumeSerialNumber(0), FileSizeHigh(0),
FileSizeLow(0), FileIndexHigh(0), FileIndexLow(0),
Type(file_type::status_error), Perms(perms_not_known) {}
file_status(file_type Type)
: LastAccessedTimeHigh(0), LastAccessedTimeLow(0), LastWriteTimeHigh(0),
LastWriteTimeLow(0), VolumeSerialNumber(0), FileSizeHigh(0),
FileSizeLow(0), FileIndexHigh(0), FileIndexLow(0), Type(Type),
Perms(perms_not_known) {}
file_status(file_type Type, uint32_t LastAccessTimeHigh,
uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh,
uint32_t LastWriteTimeLow, uint32_t VolumeSerialNumber,
uint32_t FileSizeHigh, uint32_t FileSizeLow,
uint32_t FileIndexHigh, uint32_t FileIndexLow)
: LastAccessedTimeHigh(LastAccessTimeHigh), LastAccessedTimeLow(LastAccessTimeLow),
LastWriteTimeHigh(LastWriteTimeHigh),
LastWriteTimeLow(LastWriteTimeLow),
VolumeSerialNumber(VolumeSerialNumber), FileSizeHigh(FileSizeHigh),
FileSizeLow(FileSizeLow), FileIndexHigh(FileIndexHigh),
FileIndexLow(FileIndexLow), Type(Type), Perms(perms_not_known) {}
#else
file_status()
: fs_st_dev(0), fs_st_ino(0), fs_st_atime(0), fs_st_mtime(0),
fs_st_uid(0), fs_st_gid(0), fs_st_size(0),
Type(file_type::status_error), Perms(perms_not_known) {}
file_status(file_type Type)
: fs_st_dev(0), fs_st_ino(0), fs_st_atime(0), fs_st_mtime(0),
fs_st_uid(0), fs_st_gid(0), fs_st_size(0), Type(Type),
Perms(perms_not_known) {}
file_status(file_type Type, perms Perms, dev_t Dev, ino_t Ino, time_t ATime,
time_t MTime, uid_t UID, gid_t GID, off_t Size)
: fs_st_dev(Dev), fs_st_ino(Ino), fs_st_atime(ATime), fs_st_mtime(MTime),
fs_st_uid(UID), fs_st_gid(GID), fs_st_size(Size), Type(Type),
Perms(Perms) {}
#endif
// getters
file_type type() const { return Type; }
perms permissions() const { return Perms; }
UniqueID getUniqueID() const;
#ifdef _WIN32
uint32_t getUser() const {
return 9999; // Not applicable to Windows, so...
}
uint32_t getGroup() const {
return 9999; // Not applicable to Windows, so...
}
uint64_t getSize() const {
return (uint64_t(FileSizeHigh) << 32) + FileSizeLow;
}
#else
uint32_t getUser() const { return fs_st_uid; }
uint32_t getGroup() const { return fs_st_gid; }
uint64_t getSize() const { return fs_st_size; }
#endif
// setters
void type(file_type v) { Type = v; }
void permissions(perms p) { Perms = p; }
};
/// @}
/// @name Physical Operators
/// @{
/// @brief Make \a path an absolute path.
///
/// Makes \a path absolute using the \a current_directory if it is not already.
/// An empty \a path will result in the \a current_directory.
///
/// /absolute/path => /absolute/path
/// relative/../path => <current-directory>/relative/../path
///
/// @param path A path that is modified to be an absolute path.
/// @returns errc::success if \a path has been made absolute, otherwise a
/// platform-specific error_code.
std::error_code make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path);
/// @brief Make \a path an absolute path.
///
/// Makes \a path absolute using the current directory if it is not already. An
/// empty \a path will result in the current directory.
///
/// /absolute/path => /absolute/path
/// relative/../path => <current-directory>/relative/../path
///
/// @param path A path that is modified to be an absolute path.
/// @returns errc::success if \a path has been made absolute, otherwise a
/// platform-specific error_code.
std::error_code make_absolute(SmallVectorImpl<char> &path);
/// @brief Get the current path.
///
/// @param result Holds the current path on return.
/// @returns errc::success if the current path has been stored in result,
/// otherwise a platform-specific error_code.
std::error_code current_path(SmallVectorImpl<char> &result);
/// @}
/// @name Physical Observers
/// @{
/// @brief Does file exist?
///
/// @param status A file_status previously returned from stat.
/// @returns True if the file represented by status exists, false if it does
/// not.
bool exists(file_status status);
enum class AccessMode { Exist, Write, Execute };
/// @brief Can the file be accessed?
///
/// @param Path Input path.
/// @returns errc::success if the path can be accessed, otherwise a
/// platform-specific error_code.
std::error_code access(const Twine &Path, AccessMode Mode);
/// @brief Does file exist?
///
/// @param Path Input path.
/// @returns True if it exists, false otherwise.
inline bool exists(const Twine &Path) {
return !access(Path, AccessMode::Exist);
}
/// @brief Can we write this file?
///
/// @param Path Input path.
/// @returns True if we can write to it, false otherwise.
inline bool can_write(const Twine &Path) {
return !access(Path, AccessMode::Write);
}
/// @brief Do file_status's represent the same thing?
///
/// @param A Input file_status.
/// @param B Input file_status.
///
/// assert(status_known(A) || status_known(B));
///
/// @returns True if A and B both represent the same file system entity, false
/// otherwise.
bool equivalent(file_status A, file_status B);
/// @brief Do paths represent the same thing?
///
/// assert(status_known(A) || status_known(B));
///
/// @param A Input path A.
/// @param B Input path B.
/// @param result Set to true if stat(A) and stat(B) have the same device and
/// inode (or equivalent).
/// @returns errc::success if result has been successfully set, otherwise a
/// platform-specific error_code.
std::error_code equivalent(const Twine &A, const Twine &B, bool &result);
/// @brief Simpler version of equivalent for clients that don't need to
/// differentiate between an error and false.
inline bool equivalent(const Twine &A, const Twine &B) {
bool result;
return !equivalent(A, B, result) && result;
}
/// @brief Does status represent a directory?
///
/// @param status A file_status previously returned from status.
/// @returns status.type() == file_type::directory_file.
bool is_directory(file_status status);
/// @brief Is path a directory?
///
/// @param path Input path.
/// @param result Set to true if \a path is a directory, false if it is not.
/// Undefined otherwise.
/// @returns errc::success if result has been successfully set, otherwise a
/// platform-specific error_code.
std::error_code is_directory(const Twine &path, bool &result);
/// @brief Simpler version of is_directory for clients that don't need to
/// differentiate between an error and false.
inline bool is_directory(const Twine &Path) {
bool Result;
return !is_directory(Path, Result) && Result;
}
/// @brief Does status represent a regular file?
///
/// @param status A file_status previously returned from status.
/// @returns status_known(status) && status.type() == file_type::regular_file.
bool is_regular_file(file_status status);
/// @brief Is path a regular file?
///
/// @param path Input path.
/// @param result Set to true if \a path is a regular file, false if it is not.
/// Undefined otherwise.
/// @returns errc::success if result has been successfully set, otherwise a
/// platform-specific error_code.
std::error_code is_regular_file(const Twine &path, bool &result);
/// @brief Simpler version of is_regular_file for clients that don't need to
/// differentiate between an error and false.
inline bool is_regular_file(const Twine &Path) {
bool Result;
if (is_regular_file(Path, Result))
return false;
return Result;
}
/// @brief Does this status represent something that exists but is not a
/// directory, regular file, or symlink?
///
/// @param status A file_status previously returned from status.
/// @returns exists(s) && !is_regular_file(s) && !is_directory(s)
bool is_other(file_status status);
/// @brief Is path something that exists but is not a directory,
/// regular file, or symlink?
///
/// @param path Input path.
/// @param result Set to true if \a path exists, but is not a directory, regular
/// file, or a symlink, false if it does not. Undefined otherwise.
/// @returns errc::success if result has been successfully set, otherwise a
/// platform-specific error_code.
std::error_code is_other(const Twine &path, bool &result);
/// @brief Get file status as if by POSIX stat().
///
/// @param path Input path.
/// @param result Set to the file status.
/// @returns errc::success if result has been successfully set, otherwise a
/// platform-specific error_code.
std::error_code status(const Twine &path, file_status &result);
/// @brief A version for when a file descriptor is already available.
std::error_code status(int FD, file_status &Result);
/// @brief Is status available?
///
/// @param s Input file status.
/// @returns True if status() != status_error.
bool status_known(file_status s);
/// @brief Is status available?
///
/// @param path Input path.
/// @param result Set to true if status() != status_error.
/// @returns errc::success if result has been successfully set, otherwise a
/// platform-specific error_code.
std::error_code status_known(const Twine &path, bool &result);
enum OpenFlags : unsigned {
F_None = 0,
@@ -74,6 +443,221 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
SmallVectorImpl<char> *RealPath = nullptr);
std::error_code getUniqueID(const Twine Path, UniqueID &Result);
/// @}
/// @name Iterators
/// @{
/// directory_entry - A single entry in a directory. Caches the status either
/// from the result of the iteration syscall, or the first time status is
/// called.
class directory_entry {
std::string Path;
mutable file_status Status;
public:
explicit directory_entry(const Twine &path, file_status st = file_status())
: Path(path.str())
, Status(st) {}
directory_entry() {}
void assign(const Twine &path, file_status st = file_status()) {
Path = path.str();
Status = st;
}
void replace_filename(const Twine &filename, file_status st = file_status());
const std::string &path() const { return Path; }
std::error_code status(file_status &result) const;
bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; }
bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); }
bool operator< (const directory_entry& rhs) const;
bool operator<=(const directory_entry& rhs) const;
bool operator> (const directory_entry& rhs) const;
bool operator>=(const directory_entry& rhs) const;
};
namespace detail {
struct DirIterState;
std::error_code directory_iterator_construct(DirIterState &, StringRef);
std::error_code directory_iterator_increment(DirIterState &);
std::error_code directory_iterator_destruct(DirIterState &);
/// DirIterState - Keeps state for the directory_iterator. It is reference
/// counted in order to preserve InputIterator semantics on copy.
struct DirIterState : public RefCountedBase<DirIterState> {
DirIterState()
: IterationHandle(0) {}
~DirIterState() {
directory_iterator_destruct(*this);
}
intptr_t IterationHandle;
directory_entry CurrentEntry;
};
} // end namespace detail
/// directory_iterator - Iterates through the entries in path. There is no
/// operator++ because we need an error_code. If it's really needed we can make
/// it call report_fatal_error on error.
class directory_iterator {
IntrusiveRefCntPtr<detail::DirIterState> State;
public:
explicit directory_iterator(const Twine &path, std::error_code &ec) {
State = new detail::DirIterState;
SmallString<128> path_storage;
ec = detail::directory_iterator_construct(*State,
path.toStringRef(path_storage));
}
explicit directory_iterator(const directory_entry &de, std::error_code &ec) {
State = new detail::DirIterState;
ec = detail::directory_iterator_construct(*State, de.path());
}
/// Construct end iterator.
directory_iterator() : State(nullptr) {}
// No operator++ because we need error_code.
directory_iterator &increment(std::error_code &ec) {
ec = directory_iterator_increment(*State);
return *this;
}
const directory_entry &operator*() const { return State->CurrentEntry; }
const directory_entry *operator->() const { return &State->CurrentEntry; }
bool operator==(const directory_iterator &RHS) const {
if (State == RHS.State)
return true;
if (!RHS.State)
return State->CurrentEntry == directory_entry();
if (!State)
return RHS.State->CurrentEntry == directory_entry();
return State->CurrentEntry == RHS.State->CurrentEntry;
}
bool operator!=(const directory_iterator &RHS) const {
return !(*this == RHS);
}
// Other members as required by
// C++ Std, 24.1.1 Input iterators [input.iterators]
};
namespace detail {
/// RecDirIterState - Keeps state for the recursive_directory_iterator. It is
/// reference counted in order to preserve InputIterator semantics on copy.
struct RecDirIterState : public RefCountedBase<RecDirIterState> {
RecDirIterState()
: Level(0)
, HasNoPushRequest(false) {}
std::stack<directory_iterator, std::vector<directory_iterator> > Stack;
uint16_t Level;
bool HasNoPushRequest;
};
} // end namespace detail
/// recursive_directory_iterator - Same as directory_iterator except for it
/// recurses down into child directories.
class recursive_directory_iterator {
IntrusiveRefCntPtr<detail::RecDirIterState> State;
public:
recursive_directory_iterator() {}
explicit recursive_directory_iterator(const Twine &path, std::error_code &ec)
: State(new detail::RecDirIterState) {
State->Stack.push(directory_iterator(path, ec));
if (State->Stack.top() == directory_iterator())
State.reset();
}
// No operator++ because we need error_code.
recursive_directory_iterator &increment(std::error_code &ec) {
const directory_iterator end_itr;
if (State->HasNoPushRequest)
State->HasNoPushRequest = false;
else {
file_status st;
if ((ec = State->Stack.top()->status(st))) return *this;
if (is_directory(st)) {
State->Stack.push(directory_iterator(*State->Stack.top(), ec));
if (ec) return *this;
if (State->Stack.top() != end_itr) {
++State->Level;
return *this;
}
State->Stack.pop();
}
}
while (!State->Stack.empty()
&& State->Stack.top().increment(ec) == end_itr) {
State->Stack.pop();
--State->Level;
}
// Check if we are done. If so, create an end iterator.
if (State->Stack.empty())
State.reset();
return *this;
}
const directory_entry &operator*() const { return *State->Stack.top(); }
const directory_entry *operator->() const { return &*State->Stack.top(); }
// observers
/// Gets the current level. Starting path is at level 0.
int level() const { return State->Level; }
/// Returns true if no_push has been called for this directory_entry.
bool no_push_request() const { return State->HasNoPushRequest; }
// modifiers
/// Goes up one level if Level > 0.
void pop() {
assert(State && "Cannot pop an end iterator!");
assert(State->Level > 0 && "Cannot pop an iterator with level < 1");
const directory_iterator end_itr;
std::error_code ec;
do {
if (ec) {
//report_fatal_error("Error incrementing directory iterator.");
while (!State->Stack.empty()) State->Stack.pop();
break;
}
State->Stack.pop();
--State->Level;
} while (!State->Stack.empty()
&& State->Stack.top().increment(ec) == end_itr);
// Check if we are done. If so, create an end iterator.
if (State->Stack.empty())
State.reset();
}
/// Does not go down into the current directory_entry.
void no_push() { State->HasNoPushRequest = true; }
bool operator==(const recursive_directory_iterator &RHS) const {
return State == RHS.State;
}
bool operator!=(const recursive_directory_iterator &RHS) const {
return !(*this == RHS);
}
// Other members as required by
// C++ Std, 24.1.1 Input iterators [input.iterators]
};
/// @}

View File

@@ -0,0 +1,288 @@
//== llvm/ADT/IntrusiveRefCntPtr.h - Smart Refcounting Pointer ---*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines IntrusiveRefCntPtr, a template class that
// implements a "smart" pointer for objects that maintain their own
// internal reference count, and RefCountedBase/RefCountedBaseVPTR, two
// generic base classes for objects that wish to have their lifetimes
// managed using reference counting.
//
// IntrusiveRefCntPtr is similar to Boost's intrusive_ptr with added
// LLVM-style casting.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_ADT_INTRUSIVEREFCNTPTR_H
#define LLVM_ADT_INTRUSIVEREFCNTPTR_H
#include <atomic>
#include <cassert>
#include <cstddef>
namespace llvm {
template <class T>
class IntrusiveRefCntPtr;
//===----------------------------------------------------------------------===//
/// RefCountedBase - A generic base class for objects that wish to
/// have their lifetimes managed using reference counts. Classes
/// subclass RefCountedBase to obtain such functionality, and are
/// typically handled with IntrusiveRefCntPtr "smart pointers" (see below)
/// which automatically handle the management of reference counts.
/// Objects that subclass RefCountedBase should not be allocated on
/// the stack, as invoking "delete" (which is called when the
/// reference count hits 0) on such objects is an error.
//===----------------------------------------------------------------------===//
template <class Derived>
class RefCountedBase {
mutable unsigned ref_cnt;
public:
RefCountedBase() : ref_cnt(0) {}
RefCountedBase(const RefCountedBase &) : ref_cnt(0) {}
void Retain() const { ++ref_cnt; }
void Release() const {
assert (ref_cnt > 0 && "Reference count is already zero.");
if (--ref_cnt == 0) delete static_cast<const Derived*>(this);
}
};
//===----------------------------------------------------------------------===//
/// RefCountedBaseVPTR - A class that has the same function as
/// RefCountedBase, but with a virtual destructor. Should be used
/// instead of RefCountedBase for classes that already have virtual
/// methods to enforce dynamic allocation via 'new'. Classes that
/// inherit from RefCountedBaseVPTR can't be allocated on stack -
/// attempting to do this will produce a compile error.
//===----------------------------------------------------------------------===//
class RefCountedBaseVPTR {
mutable unsigned ref_cnt;
virtual void anchor();
protected:
RefCountedBaseVPTR() : ref_cnt(0) {}
RefCountedBaseVPTR(const RefCountedBaseVPTR &) : ref_cnt(0) {}
virtual ~RefCountedBaseVPTR() {}
void Retain() const { ++ref_cnt; }
void Release() const {
assert (ref_cnt > 0 && "Reference count is already zero.");
if (--ref_cnt == 0) delete this;
}
template <typename T>
friend struct IntrusiveRefCntPtrInfo;
};
template <typename T> struct IntrusiveRefCntPtrInfo {
static void retain(T *obj) { obj->Retain(); }
static void release(T *obj) { obj->Release(); }
};
/// \brief A thread-safe version of \c llvm::RefCountedBase.
///
/// A generic base class for objects that wish to have their lifetimes managed
/// using reference counts. Classes subclass \c ThreadSafeRefCountedBase to
/// obtain such functionality, and are typically handled with
/// \c IntrusiveRefCntPtr "smart pointers" which automatically handle the
/// management of reference counts.
template <class Derived>
class ThreadSafeRefCountedBase {
mutable std::atomic<int> RefCount;
protected:
ThreadSafeRefCountedBase() : RefCount(0) {}
public:
void Retain() const { ++RefCount; }
void Release() const {
int NewRefCount = --RefCount;
assert(NewRefCount >= 0 && "Reference count was already zero.");
if (NewRefCount == 0)
delete static_cast<const Derived*>(this);
}
};
//===----------------------------------------------------------------------===//
/// IntrusiveRefCntPtr - A template class that implements a "smart pointer"
/// that assumes the wrapped object has a reference count associated
/// with it that can be managed via calls to
/// IntrusivePtrAddRef/IntrusivePtrRelease. The smart pointers
/// manage reference counts via the RAII idiom: upon creation of
/// smart pointer the reference count of the wrapped object is
/// incremented and upon destruction of the smart pointer the
/// reference count is decremented. This class also safely handles
/// wrapping NULL pointers.
///
/// Reference counting is implemented via calls to
/// Obj->Retain()/Obj->Release(). Release() is required to destroy
/// the object when the reference count reaches zero. Inheriting from
/// RefCountedBase/RefCountedBaseVPTR takes care of this
/// automatically.
//===----------------------------------------------------------------------===//
template <typename T>
class IntrusiveRefCntPtr {
T* Obj;
public:
typedef T element_type;
explicit IntrusiveRefCntPtr() : Obj(nullptr) {}
IntrusiveRefCntPtr(T* obj) : Obj(obj) {
retain();
}
IntrusiveRefCntPtr(const IntrusiveRefCntPtr& S) : Obj(S.Obj) {
retain();
}
IntrusiveRefCntPtr(IntrusiveRefCntPtr&& S) : Obj(S.Obj) {
S.Obj = nullptr;
}
template <class X>
IntrusiveRefCntPtr(IntrusiveRefCntPtr<X>&& S) : Obj(S.get()) {
S.Obj = nullptr;
}
template <class X>
IntrusiveRefCntPtr(const IntrusiveRefCntPtr<X>& S)
: Obj(S.get()) {
retain();
}
IntrusiveRefCntPtr& operator=(IntrusiveRefCntPtr S) {
swap(S);
return *this;
}
~IntrusiveRefCntPtr() { release(); }
T& operator*() const { return *Obj; }
T* operator->() const { return Obj; }
T* get() const { return Obj; }
explicit operator bool() const { return Obj; }
void swap(IntrusiveRefCntPtr& other) {
T* tmp = other.Obj;
other.Obj = Obj;
Obj = tmp;
}
void reset() {
release();
Obj = nullptr;
}
void resetWithoutRelease() {
Obj = nullptr;
}
private:
void retain() { if (Obj) IntrusiveRefCntPtrInfo<T>::retain(Obj); }
void release() { if (Obj) IntrusiveRefCntPtrInfo<T>::release(Obj); }
template <typename X>
friend class IntrusiveRefCntPtr;
};
template<class T, class U>
inline bool operator==(const IntrusiveRefCntPtr<T>& A,
const IntrusiveRefCntPtr<U>& B)
{
return A.get() == B.get();
}
template<class T, class U>
inline bool operator!=(const IntrusiveRefCntPtr<T>& A,
const IntrusiveRefCntPtr<U>& B)
{
return A.get() != B.get();
}
template<class T, class U>
inline bool operator==(const IntrusiveRefCntPtr<T>& A,
U* B)
{
return A.get() == B;
}
template<class T, class U>
inline bool operator!=(const IntrusiveRefCntPtr<T>& A,
U* B)
{
return A.get() != B;
}
template<class T, class U>
inline bool operator==(T* A,
const IntrusiveRefCntPtr<U>& B)
{
return A == B.get();
}
template<class T, class U>
inline bool operator!=(T* A,
const IntrusiveRefCntPtr<U>& B)
{
return A != B.get();
}
template <class T>
bool operator==(std::nullptr_t A, const IntrusiveRefCntPtr<T> &B) {
return !B;
}
template <class T>
bool operator==(const IntrusiveRefCntPtr<T> &A, std::nullptr_t B) {
return B == A;
}
template <class T>
bool operator!=(std::nullptr_t A, const IntrusiveRefCntPtr<T> &B) {
return !(A == B);
}
template <class T>
bool operator!=(const IntrusiveRefCntPtr<T> &A, std::nullptr_t B) {
return !(A == B);
}
//===----------------------------------------------------------------------===//
// LLVM-style downcasting support for IntrusiveRefCntPtr objects
//===----------------------------------------------------------------------===//
template <typename From> struct simplify_type;
template<class T> struct simplify_type<IntrusiveRefCntPtr<T> > {
typedef T* SimpleType;
static SimpleType getSimplifiedValue(IntrusiveRefCntPtr<T>& Val) {
return Val.get();
}
};
template<class T> struct simplify_type<const IntrusiveRefCntPtr<T> > {
typedef /*const*/ T* SimpleType;
static SimpleType getSimplifiedValue(const IntrusiveRefCntPtr<T>& Val) {
return Val.get();
}
};
} // end namespace llvm
#endif // LLVM_ADT_INTRUSIVEREFCNTPTR_H