This is an automated email from the ASF dual-hosted git repository. amc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push: new 3adcde5484 Update to libswoc 1.5.3 (#10094) 3adcde5484 is described below commit 3adcde54840d80bff6d362540cdb263d6bb4669c Author: Alan M. Carroll <a...@apache.org> AuthorDate: Wed Jul 26 10:50:53 2023 -0500 Update to libswoc 1.5.3 (#10094) --- lib/swoc/CMakeLists.txt | 2 +- lib/swoc/Makefile.am | 2 +- lib/swoc/include/swoc/BufferWriter.h | 27 +++-- lib/swoc/include/swoc/MemSpan.h | 184 ++++++++++++++++++++++++++++++++++- lib/swoc/include/swoc/TextView.h | 4 +- lib/swoc/include/swoc/Vectray.h | 26 ++--- lib/swoc/include/swoc/bwf_base.h | 15 ++- lib/swoc/include/swoc/bwf_fwd.h | 2 + lib/swoc/include/swoc/bwf_ip.h | 16 ++- lib/swoc/include/swoc/swoc_version.h | 4 +- lib/swoc/src/Errata.cc | 4 +- lib/swoc/src/bw_format.cc | 8 +- lib/swoc/src/bw_ip_format.cc | 2 +- lib/swoc/src/swoc_file.cc | 4 + 14 files changed, 256 insertions(+), 44 deletions(-) diff --git a/lib/swoc/CMakeLists.txt b/lib/swoc/CMakeLists.txt index 5f0696bafb..33a0ce7f57 100644 --- a/lib/swoc/CMakeLists.txt +++ b/lib/swoc/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.11) project(Lib-SWOC CXX) -set(LIBSWOC_VERSION "1.5.2") +set(LIBSWOC_VERSION "1.5.3") set(CMAKE_CXX_STANDARD 17) cmake_policy(SET CMP0087 NEW) # override "lib64" to be "lib" unless the user explicitly sets it. diff --git a/lib/swoc/Makefile.am b/lib/swoc/Makefile.am index deb1de366e..ad56edd792 100644 --- a/lib/swoc/Makefile.am +++ b/lib/swoc/Makefile.am @@ -22,7 +22,7 @@ library_includedir=$(includedir)/swoc AM_CPPFLAGS += @SWOC_INCLUDES@ -libtsswoc_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -release 1.5.2 +libtsswoc_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -release 1.5.3 libtsswoc_la_SOURCES = \ src/ArenaWriter.cc src/bw_format.cc src/bw_ip_format.cc src/Errata.cc src/MemArena.cc src/RBTree.cc src/swoc_file.cc src/swoc_ip.cc src/TextView.cc src/string_view_util.cc diff --git a/lib/swoc/include/swoc/BufferWriter.h b/lib/swoc/include/swoc/BufferWriter.h index 5ea465f5ea..4de2d97074 100644 --- a/lib/swoc/include/swoc/BufferWriter.h +++ b/lib/swoc/include/swoc/BufferWriter.h @@ -53,17 +53,19 @@ public: */ virtual BufferWriter &write(void const *data, size_t length); - /** Add the contents of @a sv to the buffer, up to the size of the view. - - Data is added only up to the remaining room in the buffer. If the remaining capacity is - exceeded (i.e. data is not written to the output), the instance is put in to an error - state. In either case the value for @c extent is incremented by the size of @a sv. - - @return @c *this - */ - BufferWriter &write(const std::string_view &sv); + /** Write data to the buffer. + * + * @param span Data source. + * @return @a this + * + * Data from @a span is written directly to the buffer, and clipped to the size of the buffer. + * + * @internal The char poointer overloads protect this method in order to protect against including + * the terminal nul in the destination buffer. + */ + BufferWriter & write(MemSpan<void const> span); - /// Address of the first byte in the output buffer. + /// @return Pointer to first byte in buffer. virtual const char *data() const = 0; /// Get the error state. @@ -440,10 +442,15 @@ BufferWriter::write(const void *data, size_t length) { return *this; } +# if 0 inline BufferWriter & BufferWriter::write(const std::string_view &sv) { return this->write(sv.data(), sv.size()); } +# endif + +inline BufferWriter & +BufferWriter::write(MemSpan<void const> span) { return this->write(span.data(), span.size()); } inline char * BufferWriter::aux_data() { diff --git a/lib/swoc/include/swoc/MemSpan.h b/lib/swoc/include/swoc/MemSpan.h index bb097ede59..53d7a3f539 100644 --- a/lib/swoc/include/swoc/MemSpan.h +++ b/lib/swoc/include/swoc/MemSpan.h @@ -138,7 +138,7 @@ public: * * @internal A non-const variant of this is needed because passing by CR means imposing constness * on the container which can then undesirably propagate that to the element type. Best example - - * consstructing from @c std::string. Without this variant it's not possible to construct a @c char + * constructing from @c std::string. Without this variant it's not possible to construct a @c char * span vs. a @c char @c const. */ template < typename C @@ -264,8 +264,11 @@ public: * * @tparam U Type for the created span. * @return A @c MemSpan which contains the same memory as instances of @a U. + * + * if no type is specified, the default is @c void or @c void @c const according to whether + * @a value_type is @c const. */ - template <typename U = void> MemSpan<U> rebind() const; + template <typename U = std::conditional_t<std::is_const_v<T>, void const, void> > MemSpan<U> rebind() const; /// Set the span. /// This is faster but equivalent to constructing a new span with the same @@ -310,6 +313,15 @@ public: */ self_type &remove_prefix(size_t count); + /** Remove and return a prefix. + * + * @param count Number of items in the prefix. + * @return The removed prefix. + * + * @a count items are removed from the beginning of @a this and a view of those elements is returned. + */ + self_type clip_prefix(size_t count); + /** Get the trailing segment of @a count elements. * * @param count Number of elements to retrieve. @@ -333,6 +345,15 @@ public: */ self_type &remove_suffix(size_t count); + /** Remove and return a suffix. + * + * @param count Number of items in the suffix. + * @return The removed suffix. + * + * @a count items are removed from the end of @a this and a view of those elements is returned. + */ + self_type clip_suffix(size_t count); + /** Return a sub span of @a this span. * * @param offset Offset (index) of first element in subspan. @@ -344,6 +365,15 @@ public: */ constexpr self_type subspan(size_t offset, size_t count) const; + /** Limit the size of the span. + * + * @param n Maximum number of elements. + * @return @a this + * + * If the number of elements is greater than @a n, the size is changed to @a n. + */ + constexpr self_type &restrict(size_t n); + /** Construct all elements in the span. * * For each element in the span, construct an instance of the span type using the @a args. If the @@ -390,6 +420,13 @@ public: /// Copy assignment constexpr self_type & operator = (self_type const& that) = default; + /** Construct to cover an array. + * + * @tparam N Number of elements in the array. + * @param a The array. + */ + template <auto N, typename U> constexpr MemSpan(U (&a)[N]); + /// Special constructor for @c void constexpr MemSpan(MemSpan<void> const& that); @@ -550,6 +587,15 @@ public: */ self_type &remove_prefix(size_t n); + /** Remove and return a prefix. + * + * @param n Number of bytes in the prefix. + * @return The removed prefix. + * + * @a n bytes are removed from the beginning of @a this and a view of those elements is returned. + */ + self_type clip_prefix(size_t n); + /** Get the trailing segment of @a n bytes. * * @param n Number of bytes to retrieve. @@ -564,6 +610,15 @@ public: */ self_type &remove_suffix(size_t n); + /** Remove and return a suffix. + * + * @param n Number of items in the suffix. + * @return The removed suffix. + * + * @a n bytes are removed from the end of @a this and a view of those bytes is returned. + */ + self_type clip_suffix(size_t n); + /** Return a sub span of @a this span. * * @param offset Offset (index) of first element. @@ -618,6 +673,7 @@ template <> class MemSpan<void> : public MemSpan<void const> { template <typename U> friend class MemSpan; public: using value_type = void; + using pointer_type = value_type *; /// Default constructor (empty buffer). constexpr MemSpan() = default; @@ -652,6 +708,14 @@ public: */ MemSpan(value_type *begin, value_type *end); + /** Construct to cover an array. + * + * @tparam N Number of elements in the array. + * @tparam U Element type. + * @param a The array. + */ + template <auto N, typename U> constexpr MemSpan(U (&a)[N]); + /** Construct from nullptr. This implicitly makes the length 0. */ @@ -709,6 +773,16 @@ public: */ self_type &remove_prefix(size_t count); + /** Remove and return a prefix. + * + * @param count Number of byte in the prefix. + * @return The removed prefix. + * + * @a n bytes are removed from the beginning of @a this and a view of those bytes is returned. + */ + self_type clip_prefix(size_t n); + + /** Get the trailing segment of @a n bytes. * * @param n Number of bytes to retrieve. @@ -723,6 +797,15 @@ public: */ self_type &remove_suffix(size_t n); + /** Remove and return a suffix. + * + * @param n Number of items in the suffix. + * @return The removed suffix. + * + * @a n bytes are removed from the end of @a this and a view of those bytes is returned. + */ + self_type clip_suffix(size_t n); + /** Return a sub span of @a this span. * * @param offset Offset (index) of first element. @@ -788,6 +871,17 @@ public: /// Clear the span (become an empty span). self_type &clear(); +protected: + /** Construct from @c const @c void span. + * + * @param super Source span. + * + * @note This is protected because it can only be used in situations were @c const correctness + * is not violated by converting a @c const span. The primary use is to enable @c void span + * method implementations to return a @c const span as @c self_type without explicit casting. + * In such cases the original span was not @c const and so there is no violation. + */ + MemSpan(super_type const& super) : super_type(super) {} }; @@ -995,7 +1089,14 @@ template <typename T> constexpr MemSpan<T>::MemSpan(T *ptr, size_t count) : _ptr template <typename T> constexpr MemSpan<T>::MemSpan(T *begin, T *end) : _ptr{begin}, _count{detail::ptr_distance(begin, end)} {} -template <typename T> template <auto N> constexpr MemSpan<T>::MemSpan(T (&a)[N]) : _ptr{a}, _count{N} {} +template <typename T> template <auto N> constexpr MemSpan<T>::MemSpan(T (&a)[N]) : _ptr{a}, _count{N} { + // Magic for string literals to drop the trailing nul terminator, which is almost always what is expected. + if constexpr (N > 0 && std::is_same_v<char const, T>) { + if (a[N-1] == 0) { + _count -= 1; + } + } +} template <typename T> constexpr MemSpan<T>::MemSpan(std::nullptr_t) {} @@ -1163,6 +1264,32 @@ MemSpan<T>::remove_suffix(size_t count) { return *this; } +template <typename T> +auto +MemSpan<T>::clip_prefix(size_t count) -> self_type { + if (count >= _count) { + auto zret = *this; + _count = 0; + return zret; + } + self_type zret { _ptr, count }; + _ptr += count; + _count -= count; + return zret; +} + +template <typename T> +auto +MemSpan<T>::clip_suffix(size_t count) -> self_type { + if (count >= _count) { + auto zret = *this; + _count = 0; + return zret; + } + _count -= count; + return { _ptr + _count , count }; +} + template <typename T> constexpr MemSpan<T> MemSpan<T>::subspan(size_t offset, size_t count) const { @@ -1232,6 +1359,20 @@ inline constexpr MemSpan<void>::MemSpan(value_type *ptr, size_t n) : super_type( inline MemSpan<void const>::MemSpan(value_type *begin, value_type *end) : _ptr{const_cast<void*>(begin)}, _size{detail::ptr_distance(begin, end)} {} inline MemSpan<void >::MemSpan(value_type *begin, value_type *end) : super_type(begin, end) {} +template <auto N, typename U> +constexpr MemSpan<void const>::MemSpan(U (&a)[N]) : _ptr(const_cast<std::remove_const_t<U>*>(a)), _size(N * sizeof(U)) { + // Magic for string literals to drop the trailing nul terminator, which is almost always what is expected. + if constexpr (N > 0 && std::is_same_v<char const, U>) { + if (a[N-1] == 0) { + _size -= 1; + } + } +} +template <auto N, typename U> +constexpr MemSpan<void>::MemSpan(U (&a)[N]) : super_type(a) { + static_assert(!std::is_const_v<U>, "Error: constructing non-constant view with constant data."); +} + template <typename C, typename> constexpr MemSpan<void const>::MemSpan(C const &c) : _ptr(const_cast<std::remove_const_t<std::remove_reference_t<decltype(*(std::declval<C>().data()))>> *>(c.data())) @@ -1426,6 +1567,36 @@ MemSpan<void>::remove_suffix(size_t n) -> self_type & { return *this; } +inline auto +MemSpan<void const>::clip_prefix(size_t n) -> self_type { + if (n >= _size) { + auto zret = *this; + _size = 0; + return zret; + } + self_type zret { _ptr, n }; + _ptr = detail::ptr_add(_ptr, n); + _size -= n; + return zret; +} + +inline auto +MemSpan<void>::clip_prefix(size_t n) -> self_type { return super_type::clip_prefix(n); } + +inline auto +MemSpan<void const>::clip_suffix(size_t n) -> self_type { + if (n >= _size) { + auto zret = *this; + _size = 0; + return zret; + } + _size -= n; + return { detail::ptr_add(_ptr, _size) , n }; +} + +inline auto +MemSpan<void>::clip_suffix(size_t n) -> self_type { return super_type::clip_suffix(n); } + inline constexpr auto MemSpan<void const>::subspan(size_t offset, size_t n) const -> self_type { return offset <= _size ? self_type{detail::ptr_add(this->data(), offset), std::min(n, _size - offset)} : self_type{}; @@ -1436,6 +1607,13 @@ MemSpan<void>::subspan(size_t offset, size_t n) const -> self_type { return offset <= _size ? self_type{detail::ptr_add(this->data(), offset), std::min(n, _size - offset)} : self_type{}; } +template <typename T> +constexpr auto +MemSpan<T>::restrict(size_t n) -> self_type & { + _count = std::min(_count, n); + return *this; +} + template <typename T> auto MemSpan<void const>::align() const -> self_type { return this->align(alignof(T), sizeof(T)); } diff --git a/lib/swoc/include/swoc/TextView.h b/lib/swoc/include/swoc/TextView.h index d65ae0718e..77894e987f 100644 --- a/lib/swoc/include/swoc/TextView.h +++ b/lib/swoc/include/swoc/TextView.h @@ -788,8 +788,8 @@ public: * @param count Number of bytes in the view. * @return The view starting at @a pos for @a count bytes. * - * The returned view is clipped by @a this. @a count is reduced such that it covers only data - * in @a this. + * The returned view is clipped by @a this - that is, it will not extend beyond the original view. + * @a count is reduced such that it covers only data in @a this. * * @note This is provided primarily for co-variance, i.e. the returned view is a @c TextView * instead of a @c std::string_view. diff --git a/lib/swoc/include/swoc/Vectray.h b/lib/swoc/include/swoc/Vectray.h index 75eb394644..d32fc9538c 100644 --- a/lib/swoc/include/swoc/Vectray.h +++ b/lib/swoc/include/swoc/Vectray.h @@ -300,25 +300,25 @@ auto Vectray<T,N,A>::push_back(const T& t) -> self_type& { , [&](DynamicStore& ds) -> void { ds.push_back(t); } - }, _store); + }, _store); return *this; } template<typename T, size_t N, typename A> auto Vectray<T,N,A>::push_back(T && t) -> self_type& { std::visit(swoc::meta::vary{ - [&](FixedStore& fs) -> void { - if (fs._count < N) { - new(reinterpret_cast<T*>(fs._raw.data()) + fs._count++) T(std::move(t)); // A::traits ? - } else { - this->transfer(); - std::get<DYNAMIC>(_store).push_back(t); - } - } - , [&](DynamicStore& ds) -> void { - ds.push_back(std::move(t)); - } - }, _store); + [&](FixedStore& fs) -> void { + if (fs._count < N) { + new(reinterpret_cast<T*>(fs._raw.data()) + fs._count++) T(std::move(t)); // A::traits ? + } else { + this->transfer(); + std::get<DYNAMIC>(_store).push_back(std::move(t)); + } + } + , [&](DynamicStore& ds) -> void { + ds.push_back(std::move(t)); + } + }, _store); return *this; } diff --git a/lib/swoc/include/swoc/bwf_base.h b/lib/swoc/include/swoc/bwf_base.h index 0fa926d908..c36206afae 100644 --- a/lib/swoc/include/swoc/bwf_base.h +++ b/lib/swoc/include/swoc/bwf_base.h @@ -301,9 +301,12 @@ public: */ self_type &assign(std::string_view const &name, Generator const &generator); - bool contains(std::string_view name) { - return _map.end() != _map.find(name); - } + /** Check if a specific name is contained in this mapping. + * + * @param name Name to check. + * @return @c true if present, @c false if not. + */ + bool contains(std::string_view name); protected: /// Copy @a name in to local storage and return a view of it. @@ -558,6 +561,12 @@ template <typename F> NameMap<F>::NameMap(std::initializer_list<std::tuple<std:: } } +template <typename F> +bool +NameMap<F>::contains(std::string_view name) { + return _map.end() != _map.find(name); +} + template <typename F> std::string_view NameMap<F>::localize(std::string_view const &name) { diff --git a/lib/swoc/include/swoc/bwf_fwd.h b/lib/swoc/include/swoc/bwf_fwd.h index c07bcf115a..4cfcf8130d 100644 --- a/lib/swoc/include/swoc/bwf_fwd.h +++ b/lib/swoc/include/swoc/bwf_fwd.h @@ -8,7 +8,9 @@ #pragma once #include <cstdint> +#include <string> #include "swoc/swoc_version.h" +#include "swoc/TextView.h" namespace swoc { inline namespace SWOC_VERSION_NS { class BufferWriter; diff --git a/lib/swoc/include/swoc/bwf_ip.h b/lib/swoc/include/swoc/bwf_ip.h index 6335df9d27..ced32878fd 100644 --- a/lib/swoc/include/swoc/bwf_ip.h +++ b/lib/swoc/include/swoc/bwf_ip.h @@ -17,12 +17,24 @@ namespace swoc { inline namespace SWOC_VERSION_NS { // All of these expect the address to be in network order. -BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, sockaddr const *addr); - BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, in6_addr const &addr); BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, sockaddr const *addr); +/// @{ +/// @internal The various @c sockaddr types are handled almost identically due to how formatting +/// codes are handled so it's ugly to split the code base on family. Instead we depend on +/// @a sa_family being set correctly. + +inline BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, sockaddr_in const *addr) { + return bwformat(w,spec,reinterpret_cast<sockaddr const*>(addr)); +} +inline BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, sockaddr_in6 const *addr) { + return bwformat(w,spec,reinterpret_cast<sockaddr const*>(addr)); +} + +/// @} + // Use class information for ordering. BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, IP4Addr const &addr); diff --git a/lib/swoc/include/swoc/swoc_version.h b/lib/swoc/include/swoc/swoc_version.h index cfb0a439f0..9ebfe193c7 100644 --- a/lib/swoc/include/swoc/swoc_version.h +++ b/lib/swoc/include/swoc/swoc_version.h @@ -23,11 +23,11 @@ #pragma once #if !defined(SWOC_VERSION_NS) -#define SWOC_VERSION_NS _1_5_2 +#define SWOC_VERSION_NS _1_5_3 #endif namespace swoc { inline namespace SWOC_VERSION_NS { static constexpr unsigned MAJOR_VERSION = 1; static constexpr unsigned MINOR_VERSION = 5; -static constexpr unsigned POINT_VERSION = 2; +static constexpr unsigned POINT_VERSION = 3; }} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/src/Errata.cc b/lib/swoc/src/Errata.cc index f42c11ffa0..80e6bea917 100644 --- a/lib/swoc/src/Errata.cc +++ b/lib/swoc/src/Errata.cc @@ -61,12 +61,12 @@ Errata::sink() { Errata & Errata::note(code_type const &code) { - return this->note("{}"sv, code); + return this->note("{}"_sv, code); } Errata & Errata::note(code_type const &code, Severity severity) { - return this->note(severity, "{}"sv, code); + return this->note(severity, "{}"_sv, code); } Errata::Data * diff --git a/lib/swoc/src/bw_format.cc b/lib/swoc/src/bw_format.cc index b4770e6a4a..ea5c2a6d47 100644 --- a/lib/swoc/src/bw_format.cc +++ b/lib/swoc/src/bw_format.cc @@ -671,8 +671,8 @@ bwformat(BufferWriter &w, bwf::Spec const &spec, const void *ptr) { if (ptr == nullptr) { if (spec._type == 's' || spec._type == 'S') { ptr_spec._type = bwf::Spec::DEFAULT_TYPE; - ptr_spec._ext = ""sv; // clear any extension. - return bwformat(w, spec, spec._type == 's' ? "null"sv : "NULL"sv); + ptr_spec._ext = ""_sv; // clear any extension. + return bwformat(w, spec, spec._type == 's' ? "null"_sv : "NULL"_sv); } else if (spec._type == bwf::Spec::DEFAULT_TYPE) { return w; // print nothing if there is no format character override. } @@ -899,7 +899,7 @@ bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Errno const &e) { if (short_p) { w.write(": "); } - w.write(strerror(e._e)); + w.write(TextView(strerror(e._e), TextView::npos)); } if (spec._type != 's' && spec._type != 'S') { w.write(' '); @@ -966,7 +966,7 @@ bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, std::error_code con static const auto SYSTEM_CATEGORY = &std::system_category(); // This provides convenient safe access to the errno short name array. - static const swoc::bwf::Format number_fmt{"[{}]"sv}; // numeric value format. + static const swoc::bwf::Format number_fmt{"[{}]"_sv}; // numeric value format. if (spec.has_numeric_type()) { // if numeric type, print just the numeric part. bwformat(w, spec, ec.value()); diff --git a/lib/swoc/src/bw_ip_format.cc b/lib/swoc/src/bw_ip_format.cc index ca7de50715..9b52261ffe 100644 --- a/lib/swoc/src/bw_ip_format.cc +++ b/lib/swoc/src/bw_ip_format.cc @@ -138,7 +138,7 @@ bwformat(BufferWriter &w, Spec const &spec, sockaddr const *addr) { bwformat(w, spec, reinterpret_cast<sockaddr_in6 const *>(addr)->sin6_addr); break; default: - w.print("*Not IP address [{}]*", addr->sa_family); + w.print("*Invalid IP family [{}]*", addr->sa_family); break; } if (bracket_p) diff --git a/lib/swoc/src/swoc_file.cc b/lib/swoc/src/swoc_file.cc index 44ea3e7426..93086c271e 100644 --- a/lib/swoc/src/swoc_file.cc +++ b/lib/swoc/src/swoc_file.cc @@ -359,6 +359,7 @@ copy(const path &from, const path &to, std::error_code &ec) uintmax_t remove_all(const path &p, std::error_code &ec) { + // coverity TOCTOU - issue is doing stat before doing operation. Stupid complaint, ignore. DIR *dir; struct dirent *entry; std::error_code err; @@ -372,6 +373,7 @@ remove_all(const path &p, std::error_code &ec) ec = std::error_code(errno, std::system_category()); return zret; } else if (S_ISREG(s.st_mode)) { // regular file, try to remove it! + //coverity[toctou : SUPPRESS] if (unlink(p.c_str()) != 0) { ec = std::error_code(errno, std::system_category()); } else { @@ -411,6 +413,7 @@ remove_all(const path &p, std::error_code &ec) } bool remove(path const& p, std::error_code &ec) { + // coverity TOCTOU - issue is doing stat before doing operation. Stupid complaint, ignore. struct ::stat fs; if (p.empty()) { ec = std::error_code(EINVAL, std::system_category()); @@ -422,6 +425,7 @@ bool remove(path const& p, std::error_code &ec) { ec = std::error_code(errno, std::system_category()); } } else if (S_ISDIR(fs.st_mode)) { // not a directory + //coverity[toctou : SUPPRESS] if (rmdir(p.c_str()) != 0) { ec = std::error_code(errno, std::system_category()); }