1 /* error.c: common exception handling for Subversion
\r
3 * ====================================================================
\r
4 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
\r
6 * This software is licensed as described in the file COPYING, which
\r
7 * you should have received as part of this distribution. The terms
\r
8 * are also available at http://subversion.tigris.org/license-1.html.
\r
9 * If newer versions of this license are posted there, you may use a
\r
10 * newer version instead, at your option.
\r
12 * This software consists of voluntary contributions made by many
\r
13 * individuals. For exact contribution history, see the revision
\r
14 * history and logs, available at http://subversion.tigris.org/.
\r
15 * ====================================================================
\r
22 #include <apr_general.h>
\r
23 #include <apr_pools.h>
\r
24 #include <apr_strings.h>
\r
26 //#include "svn_cmdline.h"
\r
27 #include "svn_error.h"
\r
28 #include "svn_pools.h"
\r
29 #include "svn_utf.h"
\r
32 /* file_line for the non-debug case. */
\r
33 static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
\r
34 #endif /* SVN_DEBUG */
\r
36 //#include "svn_private_config.h"
\r
39 /*** Helpers for creating errors ***/
\r
40 #undef svn_error_create
\r
41 #undef svn_error_createf
\r
42 #undef svn_error_quick_wrap
\r
43 #undef svn_error_wrap_apr
\r
46 /* XXX FIXME: These should be protected by a thread mutex.
\r
47 svn_error__locate and make_error_internal should cooperate
\r
48 in locking and unlocking it. */
\r
50 /* XXX TODO: Define mutex here #if APR_HAS_THREADS */
\r
51 static const char *error_file = NULL;
\r
52 static long error_line = -1;
\r
55 svn_error__locate(const char *file, long line)
\r
57 /* XXX TODO: Lock mutex here */
\r
63 /* Cleanup function for errors. svn_error_clear () removes this so
\r
64 errors that are properly handled *don't* hit this code. */
\r
65 #if defined(SVN_DEBUG)
\r
66 static apr_status_t err_abort(void *data)
\r
68 svn_error_t *err = data; /* For easy viewing in a debugger */
\r
69 err = err; /* Fake a use for the variable to avoid compiler warnings */
\r
75 static svn_error_t *
\r
76 make_error_internal(apr_status_t apr_err,
\r
80 svn_error_t *new_error;
\r
82 /* Reuse the child's pool, or create our own. */
\r
87 if (apr_pool_create(&pool, NULL))
\r
91 /* Create the new error structure */
\r
92 new_error = apr_pcalloc(pool, sizeof(*new_error));
\r
95 new_error->apr_err = apr_err;
\r
96 new_error->child = child;
\r
97 new_error->pool = pool;
\r
98 new_error->file = error_file;
\r
99 new_error->line = error_line;
\r
100 /* XXX TODO: Unlock mutex here */
\r
102 #if defined(SVN_DEBUG)
\r
104 apr_pool_cleanup_register(pool, new_error,
\r
106 apr_pool_cleanup_null);
\r
114 /*** Creating and destroying errors. ***/
\r
117 svn_error_create(apr_status_t apr_err,
\r
118 svn_error_t *child,
\r
119 const char *message)
\r
123 err = make_error_internal(apr_err, child);
\r
126 err->message = apr_pstrdup(err->pool, message);
\r
133 svn_error_createf(apr_status_t apr_err,
\r
134 svn_error_t *child,
\r
141 err = make_error_internal(apr_err, child);
\r
144 err->message = apr_pvsprintf(err->pool, fmt, ap);
\r
152 svn_error_wrap_apr(apr_status_t status,
\r
156 svn_error_t *err, *utf8_err;
\r
159 const char *msg_apr, *msg;
\r
161 err = make_error_internal(status, NULL);
\r
165 /* Grab the APR error message. */
\r
166 apr_strerror(status, errbuf, sizeof(errbuf));
\r
167 utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
\r
170 svn_error_clear(utf8_err);
\r
172 /* Append it to the formatted message. */
\r
174 msg = apr_pvsprintf(err->pool, fmt, ap);
\r
176 err->message = apr_psprintf(err->pool, "%s%s%s", msg,
\r
177 (msg_apr) ? ": " : "",
\r
178 (msg_apr) ? msg_apr : "");
\r
186 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
\r
188 return svn_error_create(child->apr_err,
\r
195 svn_error_compose_create(svn_error_t *err1,
\r
200 svn_error_compose(err1, err2);
\r
203 return err1 ? err1 : err2;
\r
208 svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
\r
210 apr_pool_t *pool = chain->pool;
\r
211 apr_pool_t *oldpool = new_err->pool;
\r
213 while (chain->child)
\r
214 chain = chain->child;
\r
216 #if defined(SVN_DEBUG)
\r
217 /* Kill existing handler since the end of the chain is going to change */
\r
218 apr_pool_cleanup_kill(pool, chain, err_abort);
\r
221 /* Copy the new error chain into the old chain's pool. */
\r
224 chain->child = apr_palloc(pool, sizeof(*chain->child));
\r
225 chain = chain->child;
\r
227 if (chain->message)
\r
228 chain->message = apr_pstrdup(pool, new_err->message);
\r
229 chain->pool = pool;
\r
230 #if defined(SVN_DEBUG)
\r
231 if (! new_err->child)
\r
232 apr_pool_cleanup_kill(oldpool, new_err, err_abort);
\r
234 new_err = new_err->child;
\r
237 #if defined(SVN_DEBUG)
\r
238 apr_pool_cleanup_register(pool, chain,
\r
240 apr_pool_cleanup_null);
\r
243 /* Destroy the new error chain. */
\r
244 svn_pool_destroy(oldpool);
\r
248 svn_error_root_cause(svn_error_t *err)
\r
262 svn_error_dup(svn_error_t *err)
\r
265 svn_error_t *new_err = NULL, *tmp_err = NULL;
\r
267 if (apr_pool_create(&pool, NULL))
\r
270 for (; err; err = err->child)
\r
274 new_err = apr_palloc(pool, sizeof(*new_err));
\r
279 tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
\r
280 tmp_err = tmp_err->child;
\r
283 tmp_err->pool = pool;
\r
284 if (tmp_err->message)
\r
285 tmp_err->message = apr_pstrdup(pool, tmp_err->message);
\r
288 #if defined(SVN_DEBUG)
\r
289 apr_pool_cleanup_register(pool, tmp_err,
\r
291 apr_pool_cleanup_null);
\r
298 svn_error_clear(svn_error_t *err)
\r
302 #if defined(SVN_DEBUG)
\r
305 apr_pool_cleanup_kill(err->pool, err, err_abort);
\r
307 svn_pool_destroy(err->pool);
\r
312 print_error(svn_error_t *err, FILE *stream, const char *prefix)
\r
315 const char *err_string;
\r
316 svn_error_t *temp_err = NULL; /* ensure initialized even if
\r
317 err->file == NULL */
\r
318 /* Pretty-print the error */
\r
319 /* Note: we can also log errors here someday. */
\r
322 /* Note: err->file is _not_ in UTF-8, because it's expanded from
\r
323 the __FILE__ preprocessor macro. */
\r
324 const char *file_utf8;
\r
327 && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
\r
329 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
\r
330 "%s:%ld", err->file, err->line));
\r
333 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
\r
334 stream, err->pool));
\r
335 svn_error_clear(temp_err);
\r
338 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
\r
339 ": (apr_err=%d)\n", err->apr_err));
\r
340 #endif /* SVN_DEBUG */
\r
342 /* Only print the same APR error string once. */
\r
345 svn_error_clear(svn_cmdline_fprintf(stream, err->pool, "%s%s\n",
\r
346 prefix, err->message));
\r
350 /* Is this a Subversion-specific error code? */
\r
351 if ((err->apr_err > APR_OS_START_USEERR)
\r
352 && (err->apr_err <= APR_OS_START_CANONERR))
\r
353 err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
\r
354 /* Otherwise, this must be an APR error code. */
\r
355 else if ((temp_err = svn_utf_cstring_to_utf8
\r
356 (&err_string, apr_strerror(err->apr_err, errbuf,
\r
357 sizeof(errbuf)), err->pool)))
\r
359 svn_error_clear(temp_err);
\r
360 err_string = _("Can't recode error string from APR");
\r
363 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
\r
364 "%s%s\n", prefix, err_string));
\r
369 svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
\r
371 svn_handle_error2(err, stream, fatal, "svn: ");
\r
375 svn_handle_error2(svn_error_t *err,
\r
377 svn_boolean_t fatal,
\r
378 const char *prefix)
\r
380 /* In a long error chain, there may be multiple errors with the same
\r
381 error code and no custom message. We only want to print the
\r
382 default message for that code once; printing it multiple times
\r
383 would add no useful information. The 'empties' array below
\r
384 remembers the codes of empty errors already seen in the chain.
\r
386 We could allocate it in err->pool, but there's no telling how
\r
387 long err will live or how many times it will get handled. So we
\r
389 apr_pool_t *subpool;
\r
390 apr_array_header_t *empties;
\r
391 svn_error_t *tmp_err;
\r
393 /* ### The rest of this file carefully avoids using svn_pool_*(),
\r
394 preferring apr_pool_*() instead. I can't remember why -- it may
\r
395 be an artifact of r3719, or it may be for some deeper reason --
\r
396 but I'm playing it safe and using apr_pool_*() here too. */
\r
397 apr_pool_create(&subpool, err->pool);
\r
398 empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
\r
404 svn_boolean_t printed_already = FALSE;
\r
406 if (! tmp_err->message)
\r
408 for (i = 0; i < empties->nelts; i++)
\r
410 if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
\r
412 printed_already = TRUE;
\r
418 if (! printed_already)
\r
420 print_error(tmp_err, stream, prefix);
\r
421 if (! tmp_err->message)
\r
423 APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
\r
427 tmp_err = tmp_err->child;
\r
430 svn_pool_destroy(subpool);
\r
435 /* Avoid abort()s in maintainer mode. */
\r
436 svn_error_clear(err);
\r
438 /* We exit(1) here instead of abort()ing so that atexit handlers
\r
440 exit(EXIT_FAILURE);
\r
446 svn_handle_warning(FILE *stream, svn_error_t *err)
\r
448 svn_handle_warning2(stream, err, "svn: ");
\r
452 svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
\r
456 svn_error_clear(svn_cmdline_fprintf
\r
457 (stream, err->pool,
\r
458 _("%swarning: %s\n"),
\r
459 prefix, svn_err_best_message(err, buf, sizeof(buf))));
\r
464 svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
\r
467 return err->message;
\r
469 return svn_strerror(err->apr_err, buf, bufsize);
\r
473 /* svn_strerror() and helpers */
\r
476 svn_errno_t errcode;
\r
477 const char *errdesc;
\r
480 /* To understand what is going on here, read svn_error_codes.h. */
\r
481 #define SVN_ERROR_BUILD_ARRAY
\r
482 #include "svn_error_codes.h"
\r
485 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
\r
487 const err_defn *defn;
\r
489 for (defn = error_table; defn->errdesc != NULL; ++defn)
\r
490 if (defn->errcode == (svn_errno_t)statcode)
\r
492 apr_cpystrn(buf, _(defn->errdesc), bufsize);
\r
496 return apr_strerror(statcode, buf, bufsize);
\r
500 svn_error_raise_on_malfunction(svn_boolean_t can_return,
\r
501 const char *file, int line,
\r
505 abort(); /* Nothing else we can do as a library */
\r
508 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
\r
509 _("In file '%s' line %d: assertion failed (%s)"),
\r
512 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
\r
513 _("In file '%s' line %d: internal malfunction"),
\r
518 svn_error_abort_on_malfunction(svn_boolean_t can_return,
\r
519 const char *file, int line,
\r
522 svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
\r
524 svn_handle_error2(err, stderr, FALSE, "svn: ");
\r
526 return err; /* Not reached. */
\r
529 /* The current handler for reporting malfunctions, and its default setting. */
\r
530 static svn_error_malfunction_handler_t malfunction_handler
\r
531 = svn_error_abort_on_malfunction;
\r
533 svn_error_malfunction_handler_t
\r
534 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
\r
536 svn_error_malfunction_handler_t old_malfunction_handler
\r
537 = malfunction_handler;
\r
539 malfunction_handler = func;
\r
540 return old_malfunction_handler;
\r
544 svn_error__malfunction(svn_boolean_t can_return,
\r
545 const char *file, int line,
\r
548 return malfunction_handler(can_return, file, line, expr);
\r