1 /* error.c: common exception handling for Subversion
3 * ====================================================================
4 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
23 #include <apr_general.h>
24 #include <apr_pools.h>
25 #include <apr_strings.h>
27 #include "svn_cmdline.h"
28 #include "svn_error.h"
29 #include "svn_pools.h"
32 /* file_line for the non-debug case. */
33 static const char SVN_FILE_LINE_UNDEFINED
[] = "svn:<undefined>";
34 #endif /* SVN_DEBUG */
36 #include "svn_private_config.h"
39 /*** Helpers for creating errors ***/
40 #undef svn_error_create
41 #undef svn_error_createf
42 #undef svn_error_quick_wrap
43 #undef svn_error_wrap_apr
46 /* XXX FIXME: These should be protected by a thread mutex.
47 svn_error__locate and make_error_internal should cooperate
48 in locking and unlocking it. */
50 /* XXX TODO: Define mutex here #if APR_HAS_THREADS */
51 static const char *error_file
= NULL
;
52 static long error_line
= -1;
55 svn_error__locate(const char *file
, long line
)
57 /* XXX TODO: Lock mutex here */
63 /* Cleanup function for errors. svn_error_clear () removes this so
64 errors that are properly handled *don't* hit this code. */
65 #if defined(SVN_DEBUG)
66 static apr_status_t
err_abort(void *data
)
68 svn_error_t
*err
= data
; /* For easy viewing in a debugger */
70 err
= err
; /* Fake a use for the variable */
76 make_error_internal(apr_status_t apr_err
,
80 svn_error_t
*new_error
;
82 /* Reuse the child's pool, or create our own. */
87 if (apr_pool_create(&pool
, NULL
))
91 /* Create the new error structure */
92 new_error
= apr_pcalloc(pool
, sizeof(*new_error
));
95 new_error
->apr_err
= apr_err
;
96 new_error
->child
= child
;
97 new_error
->pool
= pool
;
98 new_error
->file
= error_file
;
99 new_error
->line
= error_line
;
100 /* XXX TODO: Unlock mutex here */
102 #if defined(SVN_DEBUG)
104 apr_pool_cleanup_register(pool
, new_error
,
106 apr_pool_cleanup_null
);
114 /*** Creating and destroying errors. ***/
117 svn_error_create(apr_status_t apr_err
,
123 err
= make_error_internal(apr_err
, child
);
126 err
->message
= apr_pstrdup(err
->pool
, message
);
133 svn_error_createf(apr_status_t apr_err
,
141 err
= make_error_internal(apr_err
, child
);
144 err
->message
= apr_pvsprintf(err
->pool
, fmt
, ap
);
152 svn_error_wrap_apr(apr_status_t status
,
156 svn_error_t
*err
, *utf8_err
;
159 const char *msg_apr
, *msg
;
161 err
= make_error_internal(status
, NULL
);
165 /* Grab the APR error message. */
166 apr_strerror(status
, errbuf
, sizeof(errbuf
));
167 utf8_err
= svn_utf_cstring_to_utf8(&msg_apr
, errbuf
, err
->pool
);
170 svn_error_clear(utf8_err
);
172 /* Append it to the formatted message. */
174 msg
= apr_pvsprintf(err
->pool
, fmt
, ap
);
176 err
->message
= apr_psprintf(err
->pool
, "%s%s%s", msg
,
177 (msg_apr
) ? ": " : "",
178 (msg_apr
) ? msg_apr
: "");
186 svn_error_quick_wrap(svn_error_t
*child
, const char *new_msg
)
188 return svn_error_create(child
->apr_err
,
195 svn_error_compose(svn_error_t
*chain
, svn_error_t
*new_err
)
197 apr_pool_t
*pool
= chain
->pool
;
198 apr_pool_t
*oldpool
= new_err
->pool
;
201 chain
= chain
->child
;
203 #if defined(SVN_DEBUG)
204 /* Kill existing handler since the end of the chain is going to change */
205 apr_pool_cleanup_kill(pool
, chain
, err_abort
);
208 /* Copy the new error chain into the old chain's pool. */
211 chain
->child
= apr_palloc(pool
, sizeof(*chain
->child
));
212 chain
= chain
->child
;
215 chain
->message
= apr_pstrdup(pool
, new_err
->message
);
217 #if defined(SVN_DEBUG)
218 if (! new_err
->child
)
219 apr_pool_cleanup_kill(oldpool
, new_err
, err_abort
);
221 new_err
= new_err
->child
;
224 #if defined(SVN_DEBUG)
225 apr_pool_cleanup_register(pool
, chain
,
227 apr_pool_cleanup_null
);
230 /* Destroy the new error chain. */
231 svn_pool_destroy(oldpool
);
235 svn_error_root_cause(svn_error_t
*err
)
249 svn_error_dup(svn_error_t
*err
)
252 svn_error_t
*new_err
= NULL
, *tmp_err
= NULL
;
254 if (apr_pool_create(&pool
, NULL
))
257 for (; err
; err
= err
->child
)
261 new_err
= apr_palloc(pool
, sizeof(*new_err
));
266 tmp_err
->child
= apr_palloc(pool
, sizeof(*tmp_err
->child
));
267 tmp_err
= tmp_err
->child
;
270 tmp_err
->pool
= pool
;
271 if (tmp_err
->message
)
272 tmp_err
->message
= apr_pstrdup(pool
, tmp_err
->message
);
275 #if defined(SVN_DEBUG)
276 apr_pool_cleanup_register(pool
, tmp_err
,
278 apr_pool_cleanup_null
);
285 svn_error_clear(svn_error_t
*err
)
289 #if defined(SVN_DEBUG)
292 apr_pool_cleanup_kill(err
->pool
, err
, err_abort
);
294 svn_pool_destroy(err
->pool
);
299 print_error(svn_error_t
*err
, FILE *stream
, const char *prefix
)
302 const char *err_string
;
303 svn_error_t
*temp_err
= NULL
; /* ensure initialized even if
305 /* Pretty-print the error */
306 /* Note: we can also log errors here someday. */
309 /* Note: err->file is _not_ in UTF-8, because it's expanded from
310 the __FILE__ preprocessor macro. */
311 const char *file_utf8
;
314 && !(temp_err
= svn_utf_cstring_to_utf8(&file_utf8
, err
->file
,
316 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
317 "%s:%ld", err
->file
, err
->line
));
320 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED
,
322 svn_error_clear(temp_err
);
325 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
326 ": (apr_err=%d)\n", err
->apr_err
));
327 #endif /* SVN_DEBUG */
329 /* Only print the same APR error string once. */
332 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
, "%s%s\n",
333 prefix
, err
->message
));
337 /* Is this a Subversion-specific error code? */
338 if ((err
->apr_err
> APR_OS_START_USEERR
)
339 && (err
->apr_err
<= APR_OS_START_CANONERR
))
340 err_string
= svn_strerror(err
->apr_err
, errbuf
, sizeof(errbuf
));
341 /* Otherwise, this must be an APR error code. */
342 else if ((temp_err
= svn_utf_cstring_to_utf8
343 (&err_string
, apr_strerror(err
->apr_err
, errbuf
,
344 sizeof(errbuf
)), err
->pool
)))
346 svn_error_clear(temp_err
);
347 err_string
= _("Can't recode error string from APR");
350 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
351 "%s%s\n", prefix
, err_string
));
356 svn_handle_error(svn_error_t
*err
, FILE *stream
, svn_boolean_t fatal
)
358 svn_handle_error2(err
, stream
, fatal
, "svn: ");
362 svn_handle_error2(svn_error_t
*err
,
367 /* In a long error chain, there may be multiple errors with the same
368 error code and no custom message. We only want to print the
369 default message for that code once; printing it multiple times
370 would add no useful information. The 'empties' array below
371 remembers the codes of empty errors already seen in the chain.
373 We could allocate it in err->pool, but there's no telling how
374 long err will live or how many times it will get handled. So we
377 apr_array_header_t
*empties
;
379 /* ### The rest of this file carefully avoids using svn_pool_*(),
380 preferring apr_pool_*() instead. I can't remember why -- it may
381 be an artifact of r3719, or it may be for some deeper reason --
382 but I'm playing it safe and using apr_pool_*() here too. */
383 apr_pool_create(&subpool
, err
->pool
);
384 empties
= apr_array_make(subpool
, 0, sizeof(apr_status_t
));
389 svn_boolean_t printed_already
= FALSE
;
393 for (i
= 0; i
< empties
->nelts
; i
++)
395 if (err
->apr_err
== APR_ARRAY_IDX(empties
, i
, apr_status_t
) )
397 printed_already
= TRUE
;
403 if (! printed_already
)
405 print_error(err
, stream
, prefix
);
408 APR_ARRAY_PUSH(empties
, apr_status_t
) = err
->apr_err
;
415 svn_pool_destroy(subpool
);
419 /* XXX Shouldn't we exit(1) here instead, so that atexit handlers
426 svn_handle_warning(FILE *stream
, svn_error_t
*err
)
428 svn_handle_warning2(stream
, err
, "svn: ");
432 svn_handle_warning2(FILE *stream
, svn_error_t
*err
, const char *prefix
)
436 svn_error_clear(svn_cmdline_fprintf
438 _("%swarning: %s\n"),
439 prefix
, svn_err_best_message(err
, buf
, sizeof(buf
))));
444 svn_err_best_message(svn_error_t
*err
, char *buf
, apr_size_t bufsize
)
449 return svn_strerror(err
->apr_err
, buf
, bufsize
);
453 /* svn_strerror() and helpers */
460 /* To understand what is going on here, read svn_error_codes.h. */
461 #define SVN_ERROR_BUILD_ARRAY
462 #include "svn_error_codes.h"
465 svn_strerror(apr_status_t statcode
, char *buf
, apr_size_t bufsize
)
467 const err_defn
*defn
;
469 for (defn
= error_table
; defn
->errdesc
!= NULL
; ++defn
)
470 if (defn
->errcode
== (svn_errno_t
)statcode
)
472 apr_cpystrn(buf
, _(defn
->errdesc
), bufsize
);
476 return apr_strerror(statcode
, buf
, bufsize
);