diff options
Diffstat (limited to 'subversion/bindings/cxxhl/src/exception.cpp')
-rw-r--r-- | subversion/bindings/cxxhl/src/exception.cpp | 339 |
1 files changed, 174 insertions, 165 deletions
diff --git a/subversion/bindings/cxxhl/src/exception.cpp b/subversion/bindings/cxxhl/src/exception.cpp index 6c55792..0f0ee3f 100644 --- a/subversion/bindings/cxxhl/src/exception.cpp +++ b/subversion/bindings/cxxhl/src/exception.cpp @@ -28,6 +28,8 @@ #include <sstream> #include "svncxxhl/exception.hpp" +#include "private.hpp" +#include "aprwrap.hpp" #include "svn_error.h" #include "svn_utf.h" @@ -37,103 +39,85 @@ #undef TRUE #undef FALSE +namespace apache { namespace subversion { namespace cxxhl { namespace detail { -class error_description +class ErrorDescription { public: - static error_description* create(const char* message, - const char *loc_file, long loc_line, - bool trace_link) + typedef compat::shared_ptr<ErrorDescription> shared_ptr; + + static shared_ptr create(const char* message, int error_code, + const char *loc_file, long loc_line, + bool trace_link) { - bool empty_message = (message == NULL); + const bool empty_message = (message == NULL); const std::size_t length = (empty_message ? 0 : std::strlen(message)); - void *memblock = ::operator new(length + sizeof(error_description)); + void* memblock = ::operator new(length + sizeof(ErrorDescription)); - error_description* description = new(memblock) error_description( - loc_file, loc_line, trace_link, empty_message); + ErrorDescription* description = new(memblock) ErrorDescription( + error_code, loc_file, loc_line, trace_link, empty_message); if (length) std::memcpy(description->m_message, message, length); description->m_message[length] = 0; - return description; + return shared_ptr(description); } - static error_description* create(const char* message) + static shared_ptr create(const char* message, int error_code) { - return create(message, NULL, 0, false); + return create(message, error_code, NULL, 0, false); } - error_description* reference() throw() - { - if (this) - svn_atomic_inc(&m_refcount); - return this; - } - - error_description* dereference() throw() - { - if (this && 0 == svn_atomic_dec(&m_refcount)) - { - this->~error_description(); - ::operator delete(this, std::nothrow); - return NULL; - } - return this; - } + ~ErrorDescription() throw() {} const char* what() const throw() { return (m_empty ? NULL : m_message); } + int code() const throw() { return m_errno; } const char* file() const throw() { return m_loc_file; } long line() const throw() { return m_loc_line; } bool trace() const throw() { return m_trace; } + shared_ptr& nested() throw() { return m_nested; } + const shared_ptr& nested() const throw() { return m_nested; } private: - error_description(const char *loc_file, long loc_line, - bool trace_link, bool empty_message) throw() + ErrorDescription(int error_code, + const char *loc_file, long loc_line, + bool trace_link, bool empty_message) throw() : m_loc_file(loc_file), m_loc_line(loc_line), m_trace(trace_link), m_empty(empty_message), - m_refcount(0) + m_errno(error_code) {} - ~error_description() throw() {} - const char* m_loc_file; long m_loc_line; bool m_trace; bool m_empty; - volatile svn_atomic_t m_refcount; - char m_message[1]; + shared_ptr m_nested; + + int m_errno; ///< The (SVN or APR) error code. + char m_message[1]; ///< The error message }; } // namespace detail +// +// Class InternalError +// -namespace version_1_9_dev { - -error::error(const char* description, int error_code) - : m_errno(error_code), - m_description(detail::error_description::create(description)->reference()) -{} - -error::error(const char* description, int error_code, - error::shared_ptr nested_error) - : m_errno(error_code), - m_nested(nested_error), - m_description(detail::error_description::create(description)->reference()) +InternalError::InternalError(const char* description) + : m_description(detail::ErrorDescription::create(description, 0)) {} -error::error(const error& that) throw() - : m_errno(that.m_errno), - m_nested(that.m_nested), - m_description(that.m_description->reference()) +InternalError::InternalError(const InternalError& that) throw() + : m_description(that.m_description) {} -error& error::operator=(const error& that) throw() +InternalError& InternalError::operator=(const InternalError& that) throw() { if (this == &that) return *this; @@ -141,89 +125,85 @@ error& error::operator=(const error& that) throw() // This in-place destroy+copy implementation of the assignment // operator is safe because both the destructor and the copy // constructor do not throw exceptions. - this->~error(); - return *new(this) error(that); + this->~InternalError(); + return *new(this) InternalError(that); } -error::~error() throw() -{ - m_description->dereference(); -} +InternalError::~InternalError() throw() {} -const char* error::what() const throw() +const char* InternalError::what() const throw() { return m_description->what(); } -error::error(int error_code, detail::error_description* description) throw() - : m_errno(error_code), - m_description(description) +InternalError::InternalError(description_ptr description) throw() + : m_description(description) {} -void error::throw_svn_error(svn_error_t* err) +// +// Class Error +// + +Error::Error(const Error& that) throw() + : InternalError(that.m_description) +{} + +Error& Error::operator=(const Error& that) throw() { - const bool throw_cancelled = (err->apr_err == SVN_ERR_CANCELLED); - detail::error_description* description = NULL; - try - { - // Be very careful when creating the error descriptions, so that - // the exception unwinder can free them if an allocation fails. - // The private constructor does not increment the refcount - // precisely for this reason. + if (this == &that) + return *this; - shared_ptr nested; - shared_ptr* current = &nested; + // This in-place destroy+copy implementation of the assignment + // operator is safe because both the destructor and the copy + // constructor do not throw exceptions. + this->~Error(); + return *new(this) Error(that); +} - for (svn_error_t* next = err->child; next; next = next->child) - { - description = detail::error_description::create( - next->message, next->file, next->line, - svn_error__is_tracing_link(next)); - description->reference(); - current->reset(new error(next->apr_err, description)); - description = NULL; - current = &(*current)->m_nested; - } +Error::~Error() throw() {} - const int apr_err = err->apr_err; - description = detail::error_description::create( - err->message, err->file, err->line, - svn_error__is_tracing_link(err)); - description->reference(); - svn_error_clear(err); - if (throw_cancelled) - { - cancelled converted = cancelled(apr_err, description); - description = NULL; - converted.m_nested = nested; - throw converted; - } - else - { - error converted = error(apr_err, description); - description = NULL; - converted.m_nested = nested; - throw converted; - } - } - catch (...) +int Error::code() const throw() +{ + return m_description->code(); +} + +namespace { +const char* get_generic_message(apr_status_t error_code, + const APR::Pool& scratch_pool) +{ + char errorbuf[512]; + + // Is this a Subversion-specific error code? + if (error_code > APR_OS_START_USEERR && error_code <= APR_OS_START_CANONERR) + return svn_strerror(error_code, errorbuf, sizeof(errorbuf)); + // Otherwise, this must be an APR error code. + else { - description->dereference(); - throw; + const char* generic; + svn_error_t* err = svn_utf_cstring_to_utf8( + &generic, + apr_strerror(error_code, errorbuf, sizeof(errorbuf)), + scratch_pool.get()); + if (!err) + return generic; + + // Use fuzzy transliteration instead. + svn_error_clear(err); + return svn_utf_cstring_from_utf8_fuzzy(errorbuf, scratch_pool.get()); } } - -namespace { -void handle_one_error(error::message_list& ml, bool show_traces, - int error_code, detail::error_description* descr, - apr_pool_t* pool) +void handle_one_error(Error::MessageList& ml, bool show_traces, + const detail::ErrorDescription* descr, + const APR::Pool& pool) { + const int error_code = descr->code(); + if (show_traces && descr->file()) { const char* file_utf8 = NULL; svn_error_t* err = - svn_utf_cstring_to_utf8(&file_utf8, descr->file(), pool); + svn_utf_cstring_to_utf8(&file_utf8, descr->file(), pool.get()); if (err) { svn_error_clear(err); @@ -234,8 +214,18 @@ void handle_one_error(error::message_list& ml, bool show_traces, buffer << file_utf8 << ':' << descr->line(); else buffer << "svn:<undefined>"; - buffer << ": (apr_err=" << error_code << ')'; - ml.push_back(error::message(0, buffer.str())); + if (descr->trace()) + buffer << ','; + else + { +#ifdef SVN_DEBUG + if (const char* symbolic_name = svn_error_symbolic_name(error_code)) + buffer << ": (apr_err=" << symbolic_name << ')'; + else +#endif + buffer << ": (apr_err=" << error_code << ')'; + } + ml.push_back(Error::Message(error_code, buffer.str(), true)); } if (descr->trace()) @@ -243,43 +233,24 @@ void handle_one_error(error::message_list& ml, bool show_traces, const char *description = descr->what(); if (!description) - { - char errorbuf[512]; - - // Is this a Subversion-specific error code? - if (error_code > APR_OS_START_USEERR - && error_code <= APR_OS_START_CANONERR) - description = svn_strerror(error_code, errorbuf, sizeof(errorbuf)); - // Otherwise, this must be an APR error code. - else - { - svn_error_t* err = svn_utf_cstring_to_utf8( - &description, - apr_strerror(error_code, errorbuf, sizeof(errorbuf)), - pool); - if (err) - { - svn_error_clear(err); - description = _("Can't recode error string from APR"); - } - } - } - ml.push_back(error::message(error_code, std::string(description))); + description = get_generic_message(error_code, pool); + ml.push_back(Error::Message(error_code, std::string(description), false)); } } // anonymous namespace -error::message_list error::compile_messages(bool show_traces) const +Error::MessageList Error::compile_messages(bool show_traces) const { // Determine the maximum size of the returned list - message_list::size_type max_length = 0; - for (const error* err = this; err; err = err->m_nested.get()) + MessageList::size_type max_length = 0; + for (const detail::ErrorDescription* description = m_description.get(); + description; description = description->nested().get()) { - if (show_traces && m_description->file()) + if (show_traces && description->file()) ++max_length; // We will display an error location - if (!m_description->trace()) + if (!description->trace()) ++max_length; // Traces do not emit a message line } - message_list ml; + MessageList ml; ml.reserve(max_length); // This vector holds a list of all error codes that we've printed @@ -287,36 +258,74 @@ error::message_list error::compile_messages(bool show_traces) const std::vector<int> empties; empties.reserve(max_length); - apr_pool_t* pool = NULL; - apr_pool_create(&pool, NULL); - try + APR::IterationPool iterbase; + for (const detail::ErrorDescription* description = m_description.get(); + description; description = description->nested().get()) { - for (const error* err = this; err; err = err->m_nested.get()) + APR::Pool::Iteration iterpool(iterbase); + + if (!description->what()) { - if (!err->m_description->what()) - { - // Non-specific messages are printed only once. - std::vector<int>::iterator it = std::find( - empties.begin(), empties.end(), err->m_errno); - if (it != empties.end()) - continue; - empties.push_back(err->m_errno); - } - handle_one_error(ml, show_traces, - err->m_errno, err->m_description, - pool); + // Non-specific messages are printed only once. + std::vector<int>::iterator it = std::find( + empties.begin(), empties.end(), description->code()); + if (it != empties.end()) + continue; + empties.push_back(description->code()); } + handle_one_error(ml, show_traces, description, iterpool.pool()); } - catch (...) + return ml; +} + +const char* Error::Message::generic_message() const +{ + APR::Pool pool; + return get_generic_message(m_errno, pool); +} + +namespace detail { +void checked_call(svn_error_t* err) +{ + if (!err) + return; + + struct ErrorBuilder : public Error + { + explicit ErrorBuilder (ErrorDescription::shared_ptr description) + : Error(description) + {} + }; + + struct CancelledBuilder : public Cancelled + { + explicit CancelledBuilder (ErrorDescription::shared_ptr description) + : Cancelled(description) + {} + }; + + ErrorDescription::shared_ptr description; + ErrorDescription::shared_ptr* current = &description; + + bool cancelled = false; + for (svn_error_t* next = err; next; next = next->child) { - apr_pool_destroy(pool); - throw; + *current = ErrorDescription::create(next->message, next->apr_err, + next->file, next->line, + svn_error__is_tracing_link(next)); + current = &(*current)->nested(); + if (next->apr_err == SVN_ERR_CANCELLED) + cancelled = true; } + svn_error_clear(err); - apr_pool_destroy(pool); - return ml; + if (cancelled) + throw CancelledBuilder(description); + else + throw ErrorBuilder(description); } +} // namespace detail -} // namespace version_1_9_dev } // namespace cxxhl } // namespace subversion +} // namespace apache |