diff options
Diffstat (limited to 'subversion/bindings/cxx/src/exception.cpp')
-rw-r--r-- | subversion/bindings/cxx/src/exception.cpp | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/subversion/bindings/cxx/src/exception.cpp b/subversion/bindings/cxx/src/exception.cpp new file mode 100644 index 0000000..90ff198 --- /dev/null +++ b/subversion/bindings/cxx/src/exception.cpp @@ -0,0 +1,251 @@ +/** + * @copyright + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * @endcopyright + */ + +#include <cstddef> +#include <sstream> +#include <unordered_set> + +#include "svnxx/exception.hpp" +#include "private.hpp" +#include "aprwrap.hpp" + +#include "svn_error.h" +#include "svn_utf.h" +#include "private/svn_error_private.h" + +namespace apache { +namespace subversion { +namespace svnxx { + +// +// checked_call +// + +namespace impl { + +void checked_call(svn_error_t* const err) +{ + using error_ptr = detail::error_ptr; + + if (!err) + return; + + struct error_builder final : public error + { + explicit error_builder(error_ptr ptr) + : error(ptr) + {} + }; + + struct cancelled_builder final : public cancelled + { + explicit cancelled_builder(error_ptr ptr) + : cancelled(ptr) + {} + }; + + static const auto error_deleter = + [](svn_error_t* err_) noexcept + { + svn_error_clear(err_); + }; + + for (auto next = err; next; next = next->child) + { + if (next->apr_err == SVN_ERR_CANCELLED + || next->apr_err == SVN_ERR_ITER_BREAK) + throw cancelled_builder(error_ptr(err, error_deleter)); + } + throw error_builder(error_ptr(err, error_deleter)); +} + +} // namespace impl + +// +// Class error +// + +namespace { +inline const char* best_message(const detail::error_ptr& err) +{ + if (!err) + return ""; + + const apr_size_t bufsize = 512; + char* buf = static_cast<char*>(apr_palloc(err->pool, bufsize)); + return svn_err_best_message(err.get(), buf, bufsize); +} +} // anonymous namspace + +error::error(detail::error_ptr err) + : detail::error_ptr(err), + m_message(best_message(err)) +{} + +const char* error::what() const noexcept +{ + return m_message; +} + +int error::code() const noexcept +{ + const auto err = detail::error_ptr::get(); + if (!err) + return 0; + + return static_cast<int>(err->apr_err); +} + +const char* error::name() const noexcept +{ + const auto err = detail::error_ptr::get(); + return svn_error_symbolic_name(!err ? 0 : err->apr_err); +} + +namespace { +const char* get_generic_message(apr_status_t error_code, + apr::pool& result_pool) +{ + const std::size_t errorbuf_size = 512; + const auto errorbuf = result_pool.alloc<char>(errorbuf_size); + + // Wondering about what's in UTF-8? Yes, do keep on wondering ... + return svn_strerror(error_code, errorbuf, errorbuf_size); +} + +// +// Class error::message +// + +void handle_one_error(std::vector<error::message>& messages, + bool show_traces, + const svn_error_t* err, + apr::pool& scratch_pool) +{ + struct message_builder final : public error::message + { + message_builder(apr_status_t errval, const char* errname, + const std::string& message_, bool trace) + : error::message(static_cast<int>(errval), errname, message_, trace) + {} + }; + + const char* const symbolic_name = svn_error_symbolic_name(err->apr_err); + const bool tracing_link = svn_error__is_tracing_link(err); + + if (show_traces && err->file) + { + const char* file_utf8 = nullptr; + svn_error_t* inner_err = + svn_utf_cstring_to_utf8(&file_utf8, err->file, scratch_pool.get()); + if (inner_err) + { + svn_error_clear(inner_err); + file_utf8 = nullptr; + } + + std::ostringstream buffer; + if (file_utf8) + buffer << file_utf8 << ':' << err->line; + else + buffer << "svn:<undefined>"; + + if (tracing_link) + buffer << ','; + else + { + if (symbolic_name) + buffer << ": (apr_err=" << symbolic_name << ')'; + else + buffer << ": (apr_err=" << err->apr_err << ')'; + } + messages.emplace_back(message_builder(err->apr_err, symbolic_name, + buffer.str(), true)); + } + + if (tracing_link) + return; + + const char* description = err->message; + if (!description) + description = get_generic_message(err->apr_err, scratch_pool); + messages.emplace_back(message_builder(err->apr_err, symbolic_name, + description, false)); +} +} // anonymous namespace + +std::vector<error::message> error::compile_messages(bool show_traces) const +{ + // Determine the maximum size of the returned list + std::vector<message>::size_type max_length = 0; + for (svn_error_t* err = detail::error_ptr::get(); err; err = err->child) + { + if (show_traces && err->file) + ++max_length; // We will display an error location + if (!svn_error__is_tracing_link(err)) + ++max_length; // Traces do not emit a message line + } + + std::vector<message> messages; + messages.reserve(max_length); + + // This the set of error codes that we've printed the generic + // description for. See svn_handle_error2 for details. + std::unordered_set<apr_status_t> empties; + empties.reserve(max_length); + + apr::pool iterbase; + for (svn_error_t* err = detail::error_ptr::get(); err; err = err->child) + { + apr::pool::iteration iterpool(iterbase); + + if (!err->message) + { + // Non-specific messages are printed only once. + if (empties.count(err->apr_err)) + continue; + empties.emplace(err->apr_err); + } + handle_one_error(messages, show_traces, err, iterpool.get_pool()); + } + return messages; +} + +std::string error::message::generic_text() const +{ + apr::pool scratch_pool; + return get_generic_message(m_errno, scratch_pool); +} + +// +// Class stop_iteration +// + +const char* stop_iteration::what() const noexcept +{ + return "svn::stop_iteration"; +} + +} // namespace svnxx +} // namespace subversion +} // namespace apache |