|  | /* error.c:  common exception handling for Subversion | 
|  | * | 
|  | * ==================================================================== | 
|  | *    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. | 
|  | * ==================================================================== | 
|  | */ | 
|  |  | 
|  |  | 
|  |  | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #include <apr_general.h> | 
|  | #include <apr_pools.h> | 
|  | #include <apr_strings.h> | 
|  |  | 
|  | #include <zlib.h> | 
|  |  | 
|  | #ifndef SVN_ERR__TRACING | 
|  | #define SVN_ERR__TRACING | 
|  | #endif | 
|  | #include "svn_cmdline.h" | 
|  | #include "svn_error.h" | 
|  | #include "svn_pools.h" | 
|  | #include "svn_utf.h" | 
|  |  | 
|  | #ifdef SVN_DEBUG | 
|  | /* XXX FIXME: These should be protected by a thread mutex. | 
|  | svn_error__locate and make_error_internal should cooperate | 
|  | in locking and unlocking it. */ | 
|  |  | 
|  | /* XXX TODO: Define mutex here #if APR_HAS_THREADS */ | 
|  | static const char * volatile error_file = NULL; | 
|  | static long volatile error_line = -1; | 
|  |  | 
|  | /* file_line for the non-debug case. */ | 
|  | static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>"; | 
|  | #endif /* SVN_DEBUG */ | 
|  |  | 
|  | #include "svn_private_config.h" | 
|  | #include "private/svn_error_private.h" | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Undefine the helpers for creating errors. | 
|  | * | 
|  | * *NOTE*: Any use of these functions in any other function may need | 
|  | * to call svn_error__locate() because the macro that would otherwise | 
|  | * do this is being undefined and the filename and line number will | 
|  | * not be properly set in the static error_file and error_line | 
|  | * variables. | 
|  | */ | 
|  | #undef svn_error_create | 
|  | #undef svn_error_createf | 
|  | #undef svn_error_quick_wrap | 
|  | #undef svn_error_wrap_apr | 
|  |  | 
|  | /* Note: Although this is a "__" function, it was historically in the | 
|  | * public ABI, so we can never change it or remove its signature, even | 
|  | * though it is now only used in SVN_DEBUG mode. */ | 
|  | void | 
|  | svn_error__locate(const char *file, long line) | 
|  | { | 
|  | #if defined(SVN_DEBUG) | 
|  | /* XXX TODO: Lock mutex here */ | 
|  | error_file = file; | 
|  | error_line = line; | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Cleanup function for errors.  svn_error_clear () removes this so | 
|  | errors that are properly handled *don't* hit this code. */ | 
|  | #if defined(SVN_DEBUG) | 
|  | static apr_status_t err_abort(void *data) | 
|  | { | 
|  | svn_error_t *err = data;  /* For easy viewing in a debugger */ | 
|  | err = err; /* Fake a use for the variable to avoid compiler warnings */ | 
|  |  | 
|  | if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK")) | 
|  | abort(); | 
|  | return APR_SUCCESS; | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | static svn_error_t * | 
|  | make_error_internal(apr_status_t apr_err, | 
|  | svn_error_t *child) | 
|  | { | 
|  | apr_pool_t *pool; | 
|  | svn_error_t *new_error; | 
|  |  | 
|  | /* Reuse the child's pool, or create our own. */ | 
|  | if (child) | 
|  | pool = child->pool; | 
|  | else | 
|  | { | 
|  | if (apr_pool_create(&pool, NULL)) | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | /* Create the new error structure */ | 
|  | new_error = apr_pcalloc(pool, sizeof(*new_error)); | 
|  |  | 
|  | /* Fill 'er up. */ | 
|  | new_error->apr_err = apr_err; | 
|  | new_error->child   = child; | 
|  | new_error->pool    = pool; | 
|  | #if defined(SVN_DEBUG) | 
|  | new_error->file    = error_file; | 
|  | new_error->line    = error_line; | 
|  | /* XXX TODO: Unlock mutex here */ | 
|  |  | 
|  | if (! child) | 
|  | apr_pool_cleanup_register(pool, new_error, | 
|  | err_abort, | 
|  | apr_pool_cleanup_null); | 
|  | #endif | 
|  |  | 
|  | return new_error; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /*** Creating and destroying errors. ***/ | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_create(apr_status_t apr_err, | 
|  | svn_error_t *child, | 
|  | const char *message) | 
|  | { | 
|  | svn_error_t *err; | 
|  |  | 
|  | err = make_error_internal(apr_err, child); | 
|  |  | 
|  | if (message) | 
|  | err->message = apr_pstrdup(err->pool, message); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_createf(apr_status_t apr_err, | 
|  | svn_error_t *child, | 
|  | const char *fmt, | 
|  | ...) | 
|  | { | 
|  | svn_error_t *err; | 
|  | va_list ap; | 
|  |  | 
|  | err = make_error_internal(apr_err, child); | 
|  |  | 
|  | va_start(ap, fmt); | 
|  | err->message = apr_pvsprintf(err->pool, fmt, ap); | 
|  | va_end(ap); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_wrap_apr(apr_status_t status, | 
|  | const char *fmt, | 
|  | ...) | 
|  | { | 
|  | svn_error_t *err, *utf8_err; | 
|  | va_list ap; | 
|  | char errbuf[255]; | 
|  | const char *msg_apr, *msg; | 
|  |  | 
|  | err = make_error_internal(status, NULL); | 
|  |  | 
|  | if (fmt) | 
|  | { | 
|  | /* Grab the APR error message. */ | 
|  | apr_strerror(status, errbuf, sizeof(errbuf)); | 
|  | utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool); | 
|  | if (utf8_err) | 
|  | msg_apr = NULL; | 
|  | svn_error_clear(utf8_err); | 
|  |  | 
|  | /* Append it to the formatted message. */ | 
|  | va_start(ap, fmt); | 
|  | msg = apr_pvsprintf(err->pool, fmt, ap); | 
|  | va_end(ap); | 
|  | if (msg_apr) | 
|  | { | 
|  | err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL); | 
|  | } | 
|  | else | 
|  | { | 
|  | err->message = msg; | 
|  | } | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_quick_wrap(svn_error_t *child, const char *new_msg) | 
|  | { | 
|  | if (child == SVN_NO_ERROR) | 
|  | return SVN_NO_ERROR; | 
|  |  | 
|  | return svn_error_create(child->apr_err, | 
|  | child, | 
|  | new_msg); | 
|  | } | 
|  |  | 
|  | /* Messages in tracing errors all point to this static string. */ | 
|  | static const char error_tracing_link[] = "traced call"; | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error__trace(const char *file, long line, svn_error_t *err) | 
|  | { | 
|  | #ifndef SVN_DEBUG | 
|  |  | 
|  | /* We shouldn't even be here, but whatever. Just return the error as-is.  */ | 
|  | return err; | 
|  |  | 
|  | #else | 
|  |  | 
|  | /* Only do the work when an error occurs.  */ | 
|  | if (err) | 
|  | { | 
|  | svn_error_t *trace; | 
|  | svn_error__locate(file, line); | 
|  | trace = make_error_internal(err->apr_err, err); | 
|  | trace->message = error_tracing_link; | 
|  | return trace; | 
|  | } | 
|  | return SVN_NO_ERROR; | 
|  |  | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_compose_create(svn_error_t *err1, | 
|  | svn_error_t *err2) | 
|  | { | 
|  | if (err1 && err2) | 
|  | { | 
|  | svn_error_compose(err1, | 
|  | svn_error_quick_wrap(err2, | 
|  | _("Additional errors:"))); | 
|  | return err1; | 
|  | } | 
|  | return err1 ? err1 : err2; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | svn_error_compose(svn_error_t *chain, svn_error_t *new_err) | 
|  | { | 
|  | apr_pool_t *pool = chain->pool; | 
|  | apr_pool_t *oldpool = new_err->pool; | 
|  |  | 
|  | while (chain->child) | 
|  | chain = chain->child; | 
|  |  | 
|  | #if defined(SVN_DEBUG) | 
|  | /* Kill existing handler since the end of the chain is going to change */ | 
|  | apr_pool_cleanup_kill(pool, chain, err_abort); | 
|  | #endif | 
|  |  | 
|  | /* Copy the new error chain into the old chain's pool. */ | 
|  | while (new_err) | 
|  | { | 
|  | chain->child = apr_palloc(pool, sizeof(*chain->child)); | 
|  | chain = chain->child; | 
|  | *chain = *new_err; | 
|  | if (chain->message) | 
|  | chain->message = apr_pstrdup(pool, new_err->message); | 
|  | if (chain->file) | 
|  | chain->file = apr_pstrdup(pool, new_err->file); | 
|  | chain->pool = pool; | 
|  | #if defined(SVN_DEBUG) | 
|  | if (! new_err->child) | 
|  | apr_pool_cleanup_kill(oldpool, new_err, err_abort); | 
|  | #endif | 
|  | new_err = new_err->child; | 
|  | } | 
|  |  | 
|  | #if defined(SVN_DEBUG) | 
|  | apr_pool_cleanup_register(pool, chain, | 
|  | err_abort, | 
|  | apr_pool_cleanup_null); | 
|  | #endif | 
|  |  | 
|  | /* Destroy the new error chain. */ | 
|  | svn_pool_destroy(oldpool); | 
|  | } | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_root_cause(svn_error_t *err) | 
|  | { | 
|  | while (err) | 
|  | { | 
|  | if (err->child) | 
|  | err = err->child; | 
|  | else | 
|  | break; | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_find_cause(svn_error_t *err, apr_status_t apr_err) | 
|  | { | 
|  | svn_error_t *child; | 
|  |  | 
|  | for (child = err; child; child = child->child) | 
|  | if (child->apr_err == apr_err) | 
|  | return child; | 
|  |  | 
|  | return SVN_NO_ERROR; | 
|  | } | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_dup(svn_error_t *err) | 
|  | { | 
|  | apr_pool_t *pool; | 
|  | svn_error_t *new_err = NULL, *tmp_err = NULL; | 
|  |  | 
|  | if (apr_pool_create(&pool, NULL)) | 
|  | abort(); | 
|  |  | 
|  | for (; err; err = err->child) | 
|  | { | 
|  | if (! new_err) | 
|  | { | 
|  | new_err = apr_palloc(pool, sizeof(*new_err)); | 
|  | tmp_err = new_err; | 
|  | } | 
|  | else | 
|  | { | 
|  | tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child)); | 
|  | tmp_err = tmp_err->child; | 
|  | } | 
|  | *tmp_err = *err; | 
|  | tmp_err->pool = pool; | 
|  | if (tmp_err->message) | 
|  | tmp_err->message = apr_pstrdup(pool, tmp_err->message); | 
|  | if (tmp_err->file) | 
|  | tmp_err->file = apr_pstrdup(pool, tmp_err->file); | 
|  | } | 
|  |  | 
|  | #if defined(SVN_DEBUG) | 
|  | apr_pool_cleanup_register(pool, tmp_err, | 
|  | err_abort, | 
|  | apr_pool_cleanup_null); | 
|  | #endif | 
|  |  | 
|  | return new_err; | 
|  | } | 
|  |  | 
|  | void | 
|  | svn_error_clear(svn_error_t *err) | 
|  | { | 
|  | if (err) | 
|  | { | 
|  | #if defined(SVN_DEBUG) | 
|  | while (err->child) | 
|  | err = err->child; | 
|  | apr_pool_cleanup_kill(err->pool, err, err_abort); | 
|  | #endif | 
|  | svn_pool_destroy(err->pool); | 
|  | } | 
|  | } | 
|  |  | 
|  | svn_boolean_t | 
|  | svn_error__is_tracing_link(svn_error_t *err) | 
|  | { | 
|  | #ifdef SVN_ERR__TRACING | 
|  | /* ### A strcmp()?  Really?  I think it's the best we can do unless | 
|  | ### we add a boolean field to svn_error_t that's set only for | 
|  | ### these "placeholder error chain" items.  Not such a bad idea, | 
|  | ### really...  */ | 
|  | return (err && err->message && !strcmp(err->message, error_tracing_link)); | 
|  | #else | 
|  | return FALSE; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_purge_tracing(svn_error_t *err) | 
|  | { | 
|  | #ifdef SVN_ERR__TRACING | 
|  | svn_error_t *new_err = NULL, *new_err_leaf = NULL; | 
|  |  | 
|  | if (! err) | 
|  | return SVN_NO_ERROR; | 
|  |  | 
|  | do | 
|  | { | 
|  | svn_error_t *tmp_err; | 
|  |  | 
|  | /* Skip over any trace-only links. */ | 
|  | while (err && svn_error__is_tracing_link(err)) | 
|  | err = err->child; | 
|  |  | 
|  | /* The link must be a real link in the error chain, otherwise an | 
|  | error chain with trace only links would map into SVN_NO_ERROR. */ | 
|  | if (! err) | 
|  | return svn_error_create( | 
|  | SVN_ERR_ASSERTION_ONLY_TRACING_LINKS, | 
|  | svn_error_compose_create( | 
|  | svn_error__malfunction(TRUE, __FILE__, __LINE__, | 
|  | NULL /* ### say something? */), | 
|  | err), | 
|  | NULL); | 
|  |  | 
|  | /* Copy the current error except for its child error pointer | 
|  | into the new error.  Share any message and source filename | 
|  | strings from the error. */ | 
|  | tmp_err = apr_palloc(err->pool, sizeof(*tmp_err)); | 
|  | *tmp_err = *err; | 
|  | tmp_err->child = NULL; | 
|  |  | 
|  | /* Add a new link to the new chain (creating the chain if necessary). */ | 
|  | if (! new_err) | 
|  | { | 
|  | new_err = tmp_err; | 
|  | new_err_leaf = tmp_err; | 
|  | } | 
|  | else | 
|  | { | 
|  | new_err_leaf->child = tmp_err; | 
|  | new_err_leaf = tmp_err; | 
|  | } | 
|  |  | 
|  | /* Advance to the next link in the original chain. */ | 
|  | err = err->child; | 
|  | } while (err); | 
|  |  | 
|  | return new_err; | 
|  | #else  /* SVN_ERR__TRACING */ | 
|  | return err; | 
|  | #endif /* SVN_ERR__TRACING */ | 
|  | } | 
|  |  | 
|  | /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly | 
|  | ### coupled to the current sole caller.*/ | 
|  | static void | 
|  | print_error(svn_error_t *err, FILE *stream, const char *prefix) | 
|  | { | 
|  | char errbuf[256]; | 
|  | const char *err_string; | 
|  | svn_error_t *temp_err = NULL;  /* ensure initialized even if | 
|  | err->file == NULL */ | 
|  | /* Pretty-print the error */ | 
|  | /* Note: we can also log errors here someday. */ | 
|  |  | 
|  | #ifdef SVN_DEBUG | 
|  | /* Note: err->file is _not_ in UTF-8, because it's expanded from | 
|  | the __FILE__ preprocessor macro. */ | 
|  | const char *file_utf8; | 
|  |  | 
|  | if (err->file | 
|  | && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file, | 
|  | err->pool))) | 
|  | svn_error_clear(svn_cmdline_fprintf(stream, err->pool, | 
|  | "%s:%ld", err->file, err->line)); | 
|  | else | 
|  | { | 
|  | svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED, | 
|  | stream, err->pool)); | 
|  | svn_error_clear(temp_err); | 
|  | } | 
|  |  | 
|  | { | 
|  | const char *symbolic_name; | 
|  | if (svn_error__is_tracing_link(err)) | 
|  | /* Skip it; the error code will be printed by the real link. */ | 
|  | svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n")); | 
|  | else if ((symbolic_name = svn_error_symbolic_name(err->apr_err))) | 
|  | svn_error_clear(svn_cmdline_fprintf(stream, err->pool, | 
|  | ": (apr_err=%s)\n", symbolic_name)); | 
|  | else | 
|  | svn_error_clear(svn_cmdline_fprintf(stream, err->pool, | 
|  | ": (apr_err=%d)\n", err->apr_err)); | 
|  | } | 
|  | #endif /* SVN_DEBUG */ | 
|  |  | 
|  | /* "traced call" */ | 
|  | if (svn_error__is_tracing_link(err)) | 
|  | { | 
|  | /* Skip it.  We already printed the file-line coordinates. */ | 
|  | } | 
|  | /* Only print the same APR error string once. */ | 
|  | else if (err->message) | 
|  | { | 
|  | svn_error_clear(svn_cmdline_fprintf(stream, err->pool, | 
|  | "%sE%06d: %s\n", | 
|  | prefix, err->apr_err, err->message)); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Is this a Subversion-specific error code? */ | 
|  | if ((err->apr_err > APR_OS_START_USEERR) | 
|  | && (err->apr_err <= APR_OS_START_CANONERR)) | 
|  | err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf)); | 
|  | /* Otherwise, this must be an APR error code. */ | 
|  | else if ((temp_err = svn_utf_cstring_to_utf8 | 
|  | (&err_string, apr_strerror(err->apr_err, errbuf, | 
|  | sizeof(errbuf)), err->pool))) | 
|  | { | 
|  | svn_error_clear(temp_err); | 
|  | err_string = _("Can't recode error string from APR"); | 
|  | } | 
|  |  | 
|  | svn_error_clear(svn_cmdline_fprintf(stream, err->pool, | 
|  | "%sE%06d: %s\n", | 
|  | prefix, err->apr_err, err_string)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal) | 
|  | { | 
|  | svn_handle_error2(err, stream, fatal, "svn: "); | 
|  | } | 
|  |  | 
|  | void | 
|  | svn_handle_error2(svn_error_t *err, | 
|  | FILE *stream, | 
|  | svn_boolean_t fatal, | 
|  | const char *prefix) | 
|  | { | 
|  | /* In a long error chain, there may be multiple errors with the same | 
|  | error code and no custom message.  We only want to print the | 
|  | default message for that code once; printing it multiple times | 
|  | would add no useful information.  The 'empties' array below | 
|  | remembers the codes of empty errors already seen in the chain. | 
|  |  | 
|  | We could allocate it in err->pool, but there's no telling how | 
|  | long err will live or how many times it will get handled.  So we | 
|  | use a subpool. */ | 
|  | apr_pool_t *subpool; | 
|  | apr_array_header_t *empties; | 
|  | svn_error_t *tmp_err; | 
|  |  | 
|  | /* ### The rest of this file carefully avoids using svn_pool_*(), | 
|  | preferring apr_pool_*() instead.  I can't remember why -- it may | 
|  | be an artifact of r843793, or it may be for some deeper reason -- | 
|  | but I'm playing it safe and using apr_pool_*() here too. */ | 
|  | apr_pool_create(&subpool, err->pool); | 
|  | empties = apr_array_make(subpool, 0, sizeof(apr_status_t)); | 
|  |  | 
|  | tmp_err = err; | 
|  | while (tmp_err) | 
|  | { | 
|  | svn_boolean_t printed_already = FALSE; | 
|  |  | 
|  | if (! tmp_err->message) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < empties->nelts; i++) | 
|  | { | 
|  | if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) ) | 
|  | { | 
|  | printed_already = TRUE; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (! printed_already) | 
|  | { | 
|  | print_error(tmp_err, stream, prefix); | 
|  | if (! tmp_err->message) | 
|  | { | 
|  | APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err; | 
|  | } | 
|  | } | 
|  |  | 
|  | tmp_err = tmp_err->child; | 
|  | } | 
|  |  | 
|  | svn_pool_destroy(subpool); | 
|  |  | 
|  | fflush(stream); | 
|  | if (fatal) | 
|  | { | 
|  | /* Avoid abort()s in maintainer mode. */ | 
|  | svn_error_clear(err); | 
|  |  | 
|  | /* We exit(1) here instead of abort()ing so that atexit handlers | 
|  | get called. */ | 
|  | exit(EXIT_FAILURE); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | svn_handle_warning(FILE *stream, svn_error_t *err) | 
|  | { | 
|  | svn_handle_warning2(stream, err, "svn: "); | 
|  | } | 
|  |  | 
|  | void | 
|  | svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix) | 
|  | { | 
|  | char buf[256]; | 
|  |  | 
|  | svn_error_clear(svn_cmdline_fprintf | 
|  | (stream, err->pool, | 
|  | _("%swarning: W%06d: %s\n"), | 
|  | prefix, err->apr_err, | 
|  | svn_err_best_message(err, buf, sizeof(buf)))); | 
|  | fflush(stream); | 
|  | } | 
|  |  | 
|  | const char * | 
|  | svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize) | 
|  | { | 
|  | /* Skip over any trace records.  */ | 
|  | while (svn_error__is_tracing_link(err)) | 
|  | err = err->child; | 
|  | if (err->message) | 
|  | return err->message; | 
|  | else | 
|  | return svn_strerror(err->apr_err, buf, bufsize); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* svn_strerror() and helpers */ | 
|  |  | 
|  | /* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */ | 
|  | typedef struct err_defn { | 
|  | svn_errno_t errcode; /* 160004 */ | 
|  | const char *errname; /* SVN_ERR_FS_CORRUPT */ | 
|  | const char *errdesc; /* default message */ | 
|  | } err_defn; | 
|  |  | 
|  | /* To understand what is going on here, read svn_error_codes.h. */ | 
|  | #define SVN_ERROR_BUILD_ARRAY | 
|  | #include "svn_error_codes.h" | 
|  |  | 
|  | char * | 
|  | svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize) | 
|  | { | 
|  | const err_defn *defn; | 
|  |  | 
|  | for (defn = error_table; defn->errdesc != NULL; ++defn) | 
|  | if (defn->errcode == (svn_errno_t)statcode) | 
|  | { | 
|  | apr_cpystrn(buf, _(defn->errdesc), bufsize); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | return apr_strerror(statcode, buf, bufsize); | 
|  | } | 
|  |  | 
|  | const char * | 
|  | svn_error_symbolic_name(apr_status_t statcode) | 
|  | { | 
|  | const err_defn *defn; | 
|  |  | 
|  | for (defn = error_table; defn->errdesc != NULL; ++defn) | 
|  | if (defn->errcode == (svn_errno_t)statcode) | 
|  | return defn->errname; | 
|  |  | 
|  | /* "No error" is not in error_table. */ | 
|  | if (statcode == SVN_NO_ERROR) | 
|  | return "SVN_NO_ERROR"; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* Malfunctions. */ | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_raise_on_malfunction(svn_boolean_t can_return, | 
|  | const char *file, int line, | 
|  | const char *expr) | 
|  | { | 
|  | if (!can_return) | 
|  | abort(); /* Nothing else we can do as a library */ | 
|  |  | 
|  | /* The filename and line number of the error source needs to be set | 
|  | here because svn_error_createf() is not the macro defined in | 
|  | svn_error.h but the real function. */ | 
|  | svn_error__locate(file, line); | 
|  |  | 
|  | if (expr) | 
|  | return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, | 
|  | _("In file '%s' line %d: assertion failed (%s)"), | 
|  | file, line, expr); | 
|  | else | 
|  | return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, | 
|  | _("In file '%s' line %d: internal malfunction"), | 
|  | file, line); | 
|  | } | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error_abort_on_malfunction(svn_boolean_t can_return, | 
|  | const char *file, int line, | 
|  | const char *expr) | 
|  | { | 
|  | svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr); | 
|  |  | 
|  | svn_handle_error2(err, stderr, FALSE, "svn: "); | 
|  | abort(); | 
|  | return err;  /* Not reached. */ | 
|  | } | 
|  |  | 
|  | /* The current handler for reporting malfunctions, and its default setting. */ | 
|  | static svn_error_malfunction_handler_t malfunction_handler | 
|  | = svn_error_abort_on_malfunction; | 
|  |  | 
|  | svn_error_malfunction_handler_t | 
|  | svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func) | 
|  | { | 
|  | svn_error_malfunction_handler_t old_malfunction_handler | 
|  | = malfunction_handler; | 
|  |  | 
|  | malfunction_handler = func; | 
|  | return old_malfunction_handler; | 
|  | } | 
|  |  | 
|  | /* Note: Although this is a "__" function, it is in the public ABI, so | 
|  | * we can never remove it or change its signature. */ | 
|  | svn_error_t * | 
|  | svn_error__malfunction(svn_boolean_t can_return, | 
|  | const char *file, int line, | 
|  | const char *expr) | 
|  | { | 
|  | return malfunction_handler(can_return, file, line, expr); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Misc. */ | 
|  |  | 
|  | svn_error_t * | 
|  | svn_error__wrap_zlib(int zerr, const char *function, const char *message) | 
|  | { | 
|  | apr_status_t status; | 
|  | const char *zmsg; | 
|  |  | 
|  | if (zerr == Z_OK) | 
|  | return SVN_NO_ERROR; | 
|  |  | 
|  | switch (zerr) | 
|  | { | 
|  | case Z_STREAM_ERROR: | 
|  | status = SVN_ERR_STREAM_MALFORMED_DATA; | 
|  | zmsg = _("stream error"); | 
|  | break; | 
|  |  | 
|  | case Z_MEM_ERROR: | 
|  | status = APR_ENOMEM; | 
|  | zmsg = _("out of memory"); | 
|  | break; | 
|  |  | 
|  | case Z_BUF_ERROR: | 
|  | status = APR_ENOMEM; | 
|  | zmsg = _("buffer error"); | 
|  | break; | 
|  |  | 
|  | case Z_VERSION_ERROR: | 
|  | status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; | 
|  | zmsg = _("version error"); | 
|  | break; | 
|  |  | 
|  | case Z_DATA_ERROR: | 
|  | status = SVN_ERR_STREAM_MALFORMED_DATA; | 
|  | zmsg = _("corrupt data"); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; | 
|  | zmsg = _("unknown error"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (message != NULL) | 
|  | return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function, | 
|  | zmsg, message); | 
|  | else | 
|  | return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg); | 
|  | } |