mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
232 lines
6.9 KiB
C++
232 lines
6.9 KiB
C++
//===----------------------------------------------------------------------===////
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===////
|
|
|
|
#ifndef FILESYSTEM_ERROR_H
|
|
#define FILESYSTEM_ERROR_H
|
|
|
|
#include <__assert>
|
|
#include <__config>
|
|
#include <cerrno>
|
|
#include <cstdarg>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <system_error>
|
|
#include <utility> // __libcpp_unreachable
|
|
|
|
#include "format_string.h"
|
|
|
|
#if defined(_LIBCPP_WIN32API)
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# define NOMINMAX
|
|
# include <windows.h> // ERROR_* macros
|
|
#endif
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
|
|
|
|
namespace detail {
|
|
|
|
#if defined(_LIBCPP_WIN32API)
|
|
|
|
inline errc __win_err_to_errc(int err) {
|
|
constexpr struct {
|
|
DWORD win;
|
|
errc errc;
|
|
} win_error_mapping[] = {
|
|
{ERROR_ACCESS_DENIED, errc::permission_denied},
|
|
{ERROR_ALREADY_EXISTS, errc::file_exists},
|
|
{ERROR_BAD_NETPATH, errc::no_such_file_or_directory},
|
|
{ERROR_BAD_PATHNAME, errc::no_such_file_or_directory},
|
|
{ERROR_BAD_UNIT, errc::no_such_device},
|
|
{ERROR_BROKEN_PIPE, errc::broken_pipe},
|
|
{ERROR_BUFFER_OVERFLOW, errc::filename_too_long},
|
|
{ERROR_BUSY, errc::device_or_resource_busy},
|
|
{ERROR_BUSY_DRIVE, errc::device_or_resource_busy},
|
|
{ERROR_CANNOT_MAKE, errc::permission_denied},
|
|
{ERROR_CANTOPEN, errc::io_error},
|
|
{ERROR_CANTREAD, errc::io_error},
|
|
{ERROR_CANTWRITE, errc::io_error},
|
|
{ERROR_CURRENT_DIRECTORY, errc::permission_denied},
|
|
{ERROR_DEV_NOT_EXIST, errc::no_such_device},
|
|
{ERROR_DEVICE_IN_USE, errc::device_or_resource_busy},
|
|
{ERROR_DIR_NOT_EMPTY, errc::directory_not_empty},
|
|
{ERROR_DIRECTORY, errc::invalid_argument},
|
|
{ERROR_DISK_FULL, errc::no_space_on_device},
|
|
{ERROR_FILE_EXISTS, errc::file_exists},
|
|
{ERROR_FILE_NOT_FOUND, errc::no_such_file_or_directory},
|
|
{ERROR_HANDLE_DISK_FULL, errc::no_space_on_device},
|
|
{ERROR_INVALID_ACCESS, errc::permission_denied},
|
|
{ERROR_INVALID_DRIVE, errc::no_such_device},
|
|
{ERROR_INVALID_FUNCTION, errc::function_not_supported},
|
|
{ERROR_INVALID_HANDLE, errc::invalid_argument},
|
|
{ERROR_INVALID_NAME, errc::no_such_file_or_directory},
|
|
{ERROR_INVALID_PARAMETER, errc::invalid_argument},
|
|
{ERROR_LOCK_VIOLATION, errc::no_lock_available},
|
|
{ERROR_LOCKED, errc::no_lock_available},
|
|
{ERROR_NEGATIVE_SEEK, errc::invalid_argument},
|
|
{ERROR_NOACCESS, errc::permission_denied},
|
|
{ERROR_NOT_ENOUGH_MEMORY, errc::not_enough_memory},
|
|
{ERROR_NOT_READY, errc::resource_unavailable_try_again},
|
|
{ERROR_NOT_SAME_DEVICE, errc::cross_device_link},
|
|
{ERROR_NOT_SUPPORTED, errc::not_supported},
|
|
{ERROR_OPEN_FAILED, errc::io_error},
|
|
{ERROR_OPEN_FILES, errc::device_or_resource_busy},
|
|
{ERROR_OPERATION_ABORTED, errc::operation_canceled},
|
|
{ERROR_OUTOFMEMORY, errc::not_enough_memory},
|
|
{ERROR_PATH_NOT_FOUND, errc::no_such_file_or_directory},
|
|
{ERROR_READ_FAULT, errc::io_error},
|
|
{ERROR_REPARSE_TAG_INVALID, errc::invalid_argument},
|
|
{ERROR_RETRY, errc::resource_unavailable_try_again},
|
|
{ERROR_SEEK, errc::io_error},
|
|
{ERROR_SHARING_VIOLATION, errc::permission_denied},
|
|
{ERROR_TOO_MANY_OPEN_FILES, errc::too_many_files_open},
|
|
{ERROR_WRITE_FAULT, errc::io_error},
|
|
{ERROR_WRITE_PROTECT, errc::permission_denied},
|
|
};
|
|
|
|
for (const auto& pair : win_error_mapping)
|
|
if (pair.win == static_cast<DWORD>(err))
|
|
return pair.errc;
|
|
return errc::invalid_argument;
|
|
}
|
|
|
|
#endif // _LIBCPP_WIN32API
|
|
|
|
inline error_code capture_errno() {
|
|
_LIBCPP_ASSERT_INTERNAL(errno != 0, "Expected errno to be non-zero");
|
|
return error_code(__errc_to_err((errc)errno), generic_category());
|
|
}
|
|
|
|
#if defined(_LIBCPP_WIN32API)
|
|
inline error_code make_windows_error(int err) { return make_error_code(__win_err_to_errc(err)); }
|
|
#endif
|
|
|
|
template <class T>
|
|
T error_value();
|
|
template <>
|
|
inline constexpr void error_value<void>() {}
|
|
template <>
|
|
inline bool error_value<bool>() {
|
|
return false;
|
|
}
|
|
#if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
|
|
template <>
|
|
inline size_t error_value<size_t>() {
|
|
return size_t(-1);
|
|
}
|
|
#endif
|
|
template <>
|
|
inline uintmax_t error_value<uintmax_t>() {
|
|
return uintmax_t(-1);
|
|
}
|
|
template <>
|
|
inline constexpr file_time_type error_value<file_time_type>() {
|
|
return file_time_type::min();
|
|
}
|
|
template <>
|
|
inline path error_value<path>() {
|
|
return {};
|
|
}
|
|
|
|
template <class T>
|
|
struct ErrorHandler {
|
|
const char* func_name_;
|
|
error_code* ec_ = nullptr;
|
|
const path* p1_ = nullptr;
|
|
const path* p2_ = nullptr;
|
|
|
|
ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr, const path* p2 = nullptr)
|
|
: func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
|
|
if (ec_)
|
|
ec_->clear();
|
|
}
|
|
|
|
T report(const error_code& ec) const {
|
|
if (ec_) {
|
|
*ec_ = ec;
|
|
return error_value<T>();
|
|
}
|
|
string what = string("in ") + func_name_;
|
|
switch (bool(p1_) + bool(p2_)) {
|
|
case 0:
|
|
__throw_filesystem_error(what, ec);
|
|
case 1:
|
|
__throw_filesystem_error(what, *p1_, ec);
|
|
case 2:
|
|
__throw_filesystem_error(what, *p1_, *p2_, ec);
|
|
}
|
|
__libcpp_unreachable();
|
|
}
|
|
|
|
_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 0)
|
|
void report_impl(const error_code& ec, const char* msg, va_list ap) const {
|
|
if (ec_) {
|
|
*ec_ = ec;
|
|
return;
|
|
}
|
|
string what = string("in ") + func_name_ + ": " + detail::vformat_string(msg, ap);
|
|
switch (bool(p1_) + bool(p2_)) {
|
|
case 0:
|
|
__throw_filesystem_error(what, ec);
|
|
case 1:
|
|
__throw_filesystem_error(what, *p1_, ec);
|
|
case 2:
|
|
__throw_filesystem_error(what, *p1_, *p2_, ec);
|
|
}
|
|
__libcpp_unreachable();
|
|
}
|
|
|
|
_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
|
|
T report(const error_code& ec, const char* msg, ...) const {
|
|
va_list ap;
|
|
va_start(ap, msg);
|
|
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
|
try {
|
|
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
|
report_impl(ec, msg, ap);
|
|
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
|
} catch (...) {
|
|
va_end(ap);
|
|
throw;
|
|
}
|
|
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
|
va_end(ap);
|
|
return error_value<T>();
|
|
}
|
|
|
|
T report(errc const& err) const { return report(make_error_code(err)); }
|
|
|
|
_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
|
|
T report(errc const& err, const char* msg, ...) const {
|
|
va_list ap;
|
|
va_start(ap, msg);
|
|
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
|
try {
|
|
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
|
report_impl(make_error_code(err), msg, ap);
|
|
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
|
|
} catch (...) {
|
|
va_end(ap);
|
|
throw;
|
|
}
|
|
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
|
|
va_end(ap);
|
|
return error_value<T>();
|
|
}
|
|
|
|
private:
|
|
ErrorHandler(ErrorHandler const&) = delete;
|
|
ErrorHandler& operator=(ErrorHandler const&) = delete;
|
|
};
|
|
|
|
} // end namespace detail
|
|
|
|
_LIBCPP_END_NAMESPACE_FILESYSTEM
|
|
|
|
#endif // FILESYSTEM_ERROR_H
|