In the command-line client, forbid
[svn.git] / subversion / libsvn_subr / error.c
blobb92af7620d730b19c27ad4d782f1ada7b945e740
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 * ====================================================================
20 #include <stdarg.h>
21 #include <assert.h>
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"
31 #ifdef SVN_DEBUG
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;
54 void
55 svn_error__locate(const char *file, long line)
57 /* XXX TODO: Lock mutex here */
58 error_file = file;
59 error_line = line;
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 */
69 abort();
70 err = err; /* Fake a use for the variable */
72 #endif
75 static svn_error_t *
76 make_error_internal(apr_status_t apr_err,
77 svn_error_t *child)
79 apr_pool_t *pool;
80 svn_error_t *new_error;
82 /* Reuse the child's pool, or create our own. */
83 if (child)
84 pool = child->pool;
85 else
87 if (apr_pool_create(&pool, NULL))
88 abort();
91 /* Create the new error structure */
92 new_error = apr_pcalloc(pool, sizeof(*new_error));
94 /* Fill 'er up. */
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)
103 if (! child)
104 apr_pool_cleanup_register(pool, new_error,
105 err_abort,
106 apr_pool_cleanup_null);
107 #endif
109 return new_error;
114 /*** Creating and destroying errors. ***/
116 svn_error_t *
117 svn_error_create(apr_status_t apr_err,
118 svn_error_t *child,
119 const char *message)
121 svn_error_t *err;
123 err = make_error_internal(apr_err, child);
125 if (message)
126 err->message = apr_pstrdup(err->pool, message);
128 return err;
132 svn_error_t *
133 svn_error_createf(apr_status_t apr_err,
134 svn_error_t *child,
135 const char *fmt,
136 ...)
138 svn_error_t *err;
139 va_list ap;
141 err = make_error_internal(apr_err, child);
143 va_start(ap, fmt);
144 err->message = apr_pvsprintf(err->pool, fmt, ap);
145 va_end(ap);
147 return err;
151 svn_error_t *
152 svn_error_wrap_apr(apr_status_t status,
153 const char *fmt,
154 ...)
156 svn_error_t *err, *utf8_err;
157 va_list ap;
158 char errbuf[255];
159 const char *msg_apr, *msg;
161 err = make_error_internal(status, NULL);
163 if (fmt)
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);
168 if (utf8_err)
169 msg_apr = NULL;
170 svn_error_clear(utf8_err);
172 /* Append it to the formatted message. */
173 va_start(ap, fmt);
174 msg = apr_pvsprintf(err->pool, fmt, ap);
175 va_end(ap);
176 err->message = apr_psprintf(err->pool, "%s%s%s", msg,
177 (msg_apr) ? ": " : "",
178 (msg_apr) ? msg_apr : "");
181 return err;
185 svn_error_t *
186 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
188 return svn_error_create(child->apr_err,
189 child,
190 new_msg);
194 void
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;
200 while (chain->child)
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);
206 #endif
208 /* Copy the new error chain into the old chain's pool. */
209 while (new_err)
211 chain->child = apr_palloc(pool, sizeof(*chain->child));
212 chain = chain->child;
213 *chain = *new_err;
214 if (chain->message)
215 chain->message = apr_pstrdup(pool, new_err->message);
216 chain->pool = pool;
217 #if defined(SVN_DEBUG)
218 if (! new_err->child)
219 apr_pool_cleanup_kill(oldpool, new_err, err_abort);
220 #endif
221 new_err = new_err->child;
224 #if defined(SVN_DEBUG)
225 apr_pool_cleanup_register(pool, chain,
226 err_abort,
227 apr_pool_cleanup_null);
228 #endif
230 /* Destroy the new error chain. */
231 svn_pool_destroy(oldpool);
234 svn_error_t *
235 svn_error_root_cause(svn_error_t *err)
237 while (err)
239 if (err->child)
240 err = err->child;
241 else
242 break;
245 return err;
248 svn_error_t *
249 svn_error_dup(svn_error_t *err)
251 apr_pool_t *pool;
252 svn_error_t *new_err = NULL, *tmp_err = NULL;
254 if (apr_pool_create(&pool, NULL))
255 abort();
257 for (; err; err = err->child)
259 if (! new_err)
261 new_err = apr_palloc(pool, sizeof(*new_err));
262 tmp_err = new_err;
264 else
266 tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
267 tmp_err = tmp_err->child;
269 *tmp_err = *err;
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,
277 err_abort,
278 apr_pool_cleanup_null);
279 #endif
281 return new_err;
284 void
285 svn_error_clear(svn_error_t *err)
287 if (err)
289 #if defined(SVN_DEBUG)
290 while (err->child)
291 err = err->child;
292 apr_pool_cleanup_kill(err->pool, err, err_abort);
293 #endif
294 svn_pool_destroy(err->pool);
298 static void
299 print_error(svn_error_t *err, FILE *stream, const char *prefix)
301 char errbuf[256];
302 const char *err_string;
303 svn_error_t *temp_err = NULL; /* ensure initialized even if
304 err->file == NULL */
305 /* Pretty-print the error */
306 /* Note: we can also log errors here someday. */
308 #ifdef SVN_DEBUG
309 /* Note: err->file is _not_ in UTF-8, because it's expanded from
310 the __FILE__ preprocessor macro. */
311 const char *file_utf8;
313 if (err->file
314 && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
315 err->pool)))
316 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
317 "%s:%ld", err->file, err->line));
318 else
320 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
321 stream, err->pool));
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. */
330 if (err->message)
332 svn_error_clear(svn_cmdline_fprintf(stream, err->pool, "%s%s\n",
333 prefix, err->message));
335 else
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));
355 void
356 svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
358 svn_handle_error2(err, stream, fatal, "svn: ");
361 void
362 svn_handle_error2(svn_error_t *err,
363 FILE *stream,
364 svn_boolean_t fatal,
365 const char *prefix)
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
375 use a subpool. */
376 apr_pool_t *subpool;
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));
386 while (err)
388 int i;
389 svn_boolean_t printed_already = FALSE;
391 if (! err->message)
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;
398 break;
403 if (! printed_already)
405 print_error(err, stream, prefix);
406 if (! err->message)
408 APR_ARRAY_PUSH(empties, apr_status_t) = err->apr_err;
412 err = err->child;
415 svn_pool_destroy(subpool);
417 fflush(stream);
418 if (fatal)
419 /* XXX Shouldn't we exit(1) here instead, so that atexit handlers
420 get called? --xbc */
421 abort();
425 void
426 svn_handle_warning(FILE *stream, svn_error_t *err)
428 svn_handle_warning2(stream, err, "svn: ");
431 void
432 svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
434 char buf[256];
436 svn_error_clear(svn_cmdline_fprintf
437 (stream, err->pool,
438 _("%swarning: %s\n"),
439 prefix, svn_err_best_message(err, buf, sizeof(buf))));
440 fflush(stream);
443 const char *
444 svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
446 if (err->message)
447 return err->message;
448 else
449 return svn_strerror(err->apr_err, buf, bufsize);
453 /* svn_strerror() and helpers */
455 typedef struct {
456 svn_errno_t errcode;
457 const char *errdesc;
458 } err_defn;
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"
464 char *
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);
473 return buf;
476 return apr_strerror(statcode, buf, bufsize);