1 /* error.c: common exception handling for Subversion
2 *
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 * ====================================================================
21 */
22
23
24
25 #include <stdarg.h>
26
27 #include <apr_general.h>
28 #include <apr_pools.h>
29 #include <apr_strings.h>
30
31 #if defined(SVN_DEBUG) && APR_HAS_THREADS
32 #include <apr_thread_proc.h>
33 #endif
34
35 #include <zlib.h>
36
37 #ifndef SVN_ERR__TRACING
38 #define SVN_ERR__TRACING
39 #endif
40 #include "svn_cmdline.h"
41 #include "svn_error.h"
42 #include "svn_pools.h"
43 #include "svn_utf.h"
44
45 #include "private/svn_error_private.h"
46 #include "svn_private_config.h"
47
48 #if defined(SVN_DEBUG) && APR_HAS_THREADS
49 #include "private/svn_atomic.h"
50 #include "pools.h"
51 #endif
52
53
54 #ifdef SVN_DEBUG
55 # if APR_HAS_THREADS
56 static apr_threadkey_t *error_file_key = NULL;
57 static apr_threadkey_t *error_line_key = NULL;
58
59 /* No-op destructor for apr_threadkey_private_create(). */
null_threadkey_dtor(void * stuff)60 static void null_threadkey_dtor(void *stuff) {}
61
62 /* Implements svn_atomic__str_init_func_t.
63 Callback used by svn_error__locate to initialize the thread-local
64 error location storage. This function will never return an
65 error string. */
66 static const char *
locate_init_once(void * ignored_baton)67 locate_init_once(void *ignored_baton)
68 {
69 /* Strictly speaking, this is a memory leak, since we're creating an
70 unmanaged, top-level pool and never destroying it. We do this
71 because this pool controls the lifetime of the thread-local
72 storage for error locations, and that storage must always be
73 available. */
74 apr_pool_t *threadkey_pool = svn_pool__create_unmanaged(TRUE);
75 apr_status_t status;
76
77 status = apr_threadkey_private_create(&error_file_key,
78 null_threadkey_dtor,
79 threadkey_pool);
80 if (status == APR_SUCCESS)
81 status = apr_threadkey_private_create(&error_line_key,
82 null_threadkey_dtor,
83 threadkey_pool);
84
85 /* If anything went wrong with the creation of the thread-local
86 storage, we'll revert to the old, thread-agnostic behaviour */
87 if (status != APR_SUCCESS)
88 error_file_key = error_line_key = NULL;
89
90 return NULL;
91 }
92 # endif /* APR_HAS_THREADS */
93
94 /* These location variables will be used in no-threads mode or if
95 thread-local storage is not available. */
96 static const char * volatile error_file = NULL;
97 static long volatile error_line = -1;
98
99 /* file_line for the non-debug case. */
100 static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
101 #endif /* SVN_DEBUG */
102
103
104 /*
105 * Undefine the helpers for creating errors.
106 *
107 * *NOTE*: Any use of these functions in any other function may need
108 * to call svn_error__locate() because the macro that would otherwise
109 * do this is being undefined and the filename and line number will
110 * not be properly set in the static error_file and error_line
111 * variables.
112 */
113 #undef svn_error_create
114 #undef svn_error_createf
115 #undef svn_error_quick_wrap
116 #undef svn_error_quick_wrapf
117 #undef svn_error_wrap_apr
118
119 /* Note: Although this is a "__" function, it was historically in the
120 * public ABI, so we can never change it or remove its signature, even
121 * though it is now only used in SVN_DEBUG mode. */
122 void
svn_error__locate(const char * file,long line)123 svn_error__locate(const char *file, long line)
124 {
125 #ifdef SVN_DEBUG
126 # if APR_HAS_THREADS
127 static volatile svn_atomic_t init_status = 0;
128 svn_atomic__init_once_no_error(&init_status, locate_init_once, NULL);
129
130 if (error_file_key && error_line_key)
131 {
132 apr_status_t status;
133 status = apr_threadkey_private_set((char*)file, error_file_key);
134 if (status == APR_SUCCESS)
135 status = apr_threadkey_private_set((void*)line, error_line_key);
136 if (status == APR_SUCCESS)
137 return;
138 }
139 # endif /* APR_HAS_THREADS */
140
141 error_file = file;
142 error_line = line;
143 #endif /* SVN_DEBUG */
144 }
145
146
147 /* Cleanup function for errors. svn_error_clear () removes this so
148 errors that are properly handled *don't* hit this code. */
149 #ifdef SVN_DEBUG
err_abort(void * data)150 static apr_status_t err_abort(void *data)
151 {
152 svn_error_t *err = data; /* For easy viewing in a debugger */
153 SVN_UNUSED(err);
154
155 if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
156 abort();
157 return APR_SUCCESS;
158 }
159 #endif
160
161
162 static svn_error_t *
make_error_internal(apr_status_t apr_err,svn_error_t * child)163 make_error_internal(apr_status_t apr_err,
164 svn_error_t *child)
165 {
166 apr_pool_t *pool;
167 svn_error_t *new_error;
168 #ifdef SVN_DEBUG
169 apr_status_t status = APR_ENOTIMPL;
170 #endif
171
172 /* Reuse the child's pool, or create our own. */
173 if (child)
174 pool = child->pool;
175 else
176 {
177 pool = svn_pool_create(NULL);
178 if (!pool)
179 abort();
180 }
181
182 /* Create the new error structure */
183 new_error = apr_pcalloc(pool, sizeof(*new_error));
184
185 /* Fill 'er up. */
186 new_error->apr_err = apr_err;
187 new_error->child = child;
188 new_error->pool = pool;
189
190 #ifdef SVN_DEBUG
191 #if APR_HAS_THREADS
192 if (error_file_key && error_line_key)
193 {
194 void *item;
195 status = apr_threadkey_private_get(&item, error_file_key);
196 if (status == APR_SUCCESS)
197 {
198 new_error->file = item;
199 status = apr_threadkey_private_get(&item, error_line_key);
200 if (status == APR_SUCCESS)
201 new_error->line = (long)item;
202 }
203 }
204 # endif /* APR_HAS_THREADS */
205
206 if (status != APR_SUCCESS)
207 {
208 new_error->file = error_file;
209 new_error->line = error_line;
210 }
211
212 if (! child)
213 apr_pool_cleanup_register(pool, new_error,
214 err_abort,
215 apr_pool_cleanup_null);
216 #endif /* SVN_DEBUG */
217
218 return new_error;
219 }
220
221
222
223 /*** Creating and destroying errors. ***/
224
225 svn_error_t *
svn_error_create(apr_status_t apr_err,svn_error_t * child,const char * message)226 svn_error_create(apr_status_t apr_err,
227 svn_error_t *child,
228 const char *message)
229 {
230 svn_error_t *err;
231
232 err = make_error_internal(apr_err, child);
233
234 if (message)
235 err->message = apr_pstrdup(err->pool, message);
236
237 return err;
238 }
239
240
241 svn_error_t *
svn_error_createf(apr_status_t apr_err,svn_error_t * child,const char * fmt,...)242 svn_error_createf(apr_status_t apr_err,
243 svn_error_t *child,
244 const char *fmt,
245 ...)
246 {
247 svn_error_t *err;
248 va_list ap;
249
250 err = make_error_internal(apr_err, child);
251
252 va_start(ap, fmt);
253 err->message = apr_pvsprintf(err->pool, fmt, ap);
254 va_end(ap);
255
256 return err;
257 }
258
259
260 svn_error_t *
svn_error_wrap_apr(apr_status_t status,const char * fmt,...)261 svn_error_wrap_apr(apr_status_t status,
262 const char *fmt,
263 ...)
264 {
265 svn_error_t *err, *utf8_err;
266 va_list ap;
267 char errbuf[255];
268 const char *msg_apr, *msg;
269
270 err = make_error_internal(status, NULL);
271
272 if (fmt)
273 {
274 /* Grab the APR error message. */
275 apr_strerror(status, errbuf, sizeof(errbuf));
276 utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
277 if (utf8_err)
278 msg_apr = NULL;
279 svn_error_clear(utf8_err);
280
281 /* Append it to the formatted message. */
282 va_start(ap, fmt);
283 msg = apr_pvsprintf(err->pool, fmt, ap);
284 va_end(ap);
285 if (msg_apr)
286 {
287 err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr,
288 SVN_VA_NULL);
289 }
290 else
291 {
292 err->message = msg;
293 }
294 }
295
296 return err;
297 }
298
299
300 svn_error_t *
svn_error_quick_wrap(svn_error_t * child,const char * new_msg)301 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
302 {
303 if (child == SVN_NO_ERROR)
304 return SVN_NO_ERROR;
305
306 return svn_error_create(child->apr_err,
307 child,
308 new_msg);
309 }
310
311 svn_error_t *
svn_error_quick_wrapf(svn_error_t * child,const char * fmt,...)312 svn_error_quick_wrapf(svn_error_t *child,
313 const char *fmt,
314 ...)
315 {
316 svn_error_t *err;
317 va_list ap;
318
319 if (child == SVN_NO_ERROR)
320 return SVN_NO_ERROR;
321
322 err = make_error_internal(child->apr_err, child);
323
324 va_start(ap, fmt);
325 err->message = apr_pvsprintf(err->pool, fmt, ap);
326 va_end(ap);
327
328 return err;
329 }
330
331 /* Messages in tracing errors all point to this static string. */
332 static const char error_tracing_link[] = "traced call";
333
334 svn_error_t *
svn_error__trace(const char * file,long line,svn_error_t * err)335 svn_error__trace(const char *file, long line, svn_error_t *err)
336 {
337 #ifndef SVN_DEBUG
338
339 /* We shouldn't even be here, but whatever. Just return the error as-is. */
340 return err;
341
342 #else
343
344 /* Only do the work when an error occurs. */
345 if (err)
346 {
347 svn_error_t *trace;
348 svn_error__locate(file, line);
349 trace = make_error_internal(err->apr_err, err);
350 trace->message = error_tracing_link;
351 return trace;
352 }
353 return SVN_NO_ERROR;
354
355 #endif
356 }
357
358
359 svn_error_t *
svn_error_compose_create(svn_error_t * err1,svn_error_t * err2)360 svn_error_compose_create(svn_error_t *err1,
361 svn_error_t *err2)
362 {
363 if (err1 && err2)
364 {
365 svn_error_compose(err1,
366 svn_error_create(SVN_ERR_COMPOSED_ERROR, err2, NULL));
367 return err1;
368 }
369 return err1 ? err1 : err2;
370 }
371
372
373 void
svn_error_compose(svn_error_t * chain,svn_error_t * new_err)374 svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
375 {
376 apr_pool_t *pool = chain->pool;
377 apr_pool_t *oldpool = new_err->pool;
378
379 while (chain->child)
380 chain = chain->child;
381
382 #if defined(SVN_DEBUG)
383 /* Kill existing handler since the end of the chain is going to change */
384 apr_pool_cleanup_kill(pool, chain, err_abort);
385 #endif
386
387 /* Copy the new error chain into the old chain's pool. */
388 while (new_err)
389 {
390 chain->child = apr_palloc(pool, sizeof(*chain->child));
391 chain = chain->child;
392 *chain = *new_err;
393 if (chain->message)
394 chain->message = apr_pstrdup(pool, new_err->message);
395 if (chain->file)
396 chain->file = apr_pstrdup(pool, new_err->file);
397 chain->pool = pool;
398 #if defined(SVN_DEBUG)
399 if (! new_err->child)
400 apr_pool_cleanup_kill(oldpool, new_err, err_abort);
401 #endif
402 new_err = new_err->child;
403 }
404
405 #if defined(SVN_DEBUG)
406 apr_pool_cleanup_register(pool, chain,
407 err_abort,
408 apr_pool_cleanup_null);
409 #endif
410
411 /* Destroy the new error chain. */
412 svn_pool_destroy(oldpool);
413 }
414
415 svn_error_t *
svn_error_root_cause(svn_error_t * err)416 svn_error_root_cause(svn_error_t *err)
417 {
418 while (err)
419 {
420 /* I don't think we can change the behavior here, but the additional
421 error chain doesn't define the root cause. Perhaps we should rev
422 this function. */
423 if (err->child /*&& err->child->apr_err != SVN_ERR_COMPOSED_ERROR*/)
424 err = err->child;
425 else
426 break;
427 }
428
429 return err;
430 }
431
432 svn_error_t *
svn_error_find_cause(svn_error_t * err,apr_status_t apr_err)433 svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
434 {
435 svn_error_t *child;
436
437 for (child = err; child; child = child->child)
438 if (child->apr_err == apr_err)
439 return child;
440
441 return SVN_NO_ERROR;
442 }
443
444 svn_error_t *
svn_error_dup(const svn_error_t * err)445 svn_error_dup(const svn_error_t *err)
446 {
447 apr_pool_t *pool;
448 svn_error_t *new_err = NULL, *tmp_err = NULL;
449
450 if (!err)
451 return SVN_NO_ERROR;
452
453 pool = svn_pool_create(NULL);
454 if (!pool)
455 abort();
456
457 for (; err; err = err->child)
458 {
459 if (! new_err)
460 {
461 new_err = apr_palloc(pool, sizeof(*new_err));
462 tmp_err = new_err;
463 }
464 else
465 {
466 tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
467 tmp_err = tmp_err->child;
468 }
469 *tmp_err = *err;
470 tmp_err->pool = pool;
471 if (tmp_err->message)
472 tmp_err->message = apr_pstrdup(pool, tmp_err->message);
473 if (tmp_err->file)
474 tmp_err->file = apr_pstrdup(pool, tmp_err->file);
475 }
476
477 #if defined(SVN_DEBUG)
478 apr_pool_cleanup_register(pool, tmp_err,
479 err_abort,
480 apr_pool_cleanup_null);
481 #endif
482
483 return new_err;
484 }
485
486 void
svn_error_clear(svn_error_t * err)487 svn_error_clear(svn_error_t *err)
488 {
489 if (err)
490 {
491 #if defined(SVN_DEBUG)
492 while (err->child)
493 err = err->child;
494 apr_pool_cleanup_kill(err->pool, err, err_abort);
495 #endif
496 svn_pool_destroy(err->pool);
497 }
498 }
499
500 svn_boolean_t
svn_error__is_tracing_link(const svn_error_t * err)501 svn_error__is_tracing_link(const svn_error_t *err)
502 {
503 #ifdef SVN_ERR__TRACING
504 /* ### A strcmp()? Really? I think it's the best we can do unless
505 ### we add a boolean field to svn_error_t that's set only for
506 ### these "placeholder error chain" items. Not such a bad idea,
507 ### really... */
508 return (err && err->message && !strcmp(err->message, error_tracing_link));
509 #else
510 return FALSE;
511 #endif
512 }
513
514 svn_error_t *
svn_error_purge_tracing(svn_error_t * err)515 svn_error_purge_tracing(svn_error_t *err)
516 {
517 #ifdef SVN_ERR__TRACING
518 svn_error_t *new_err = NULL, *new_err_leaf = NULL;
519
520 if (! err)
521 return SVN_NO_ERROR;
522
523 do
524 {
525 svn_error_t *tmp_err;
526
527 /* Skip over any trace-only links. */
528 while (err && svn_error__is_tracing_link(err))
529 err = err->child;
530
531 /* The link must be a real link in the error chain, otherwise an
532 error chain with trace only links would map into SVN_NO_ERROR. */
533 if (! err)
534 return svn_error_create(
535 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
536 svn_error__malfunction(TRUE, __FILE__, __LINE__,
537 NULL /* ### say something? */),
538 NULL);
539
540 /* Copy the current error except for its child error pointer
541 into the new error. Share any message and source filename
542 strings from the error. */
543 tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
544 *tmp_err = *err;
545 tmp_err->child = NULL;
546
547 /* Add a new link to the new chain (creating the chain if necessary). */
548 if (! new_err)
549 {
550 new_err = tmp_err;
551 new_err_leaf = tmp_err;
552 }
553 else
554 {
555 new_err_leaf->child = tmp_err;
556 new_err_leaf = tmp_err;
557 }
558
559 /* Advance to the next link in the original chain. */
560 err = err->child;
561 } while (err);
562
563 return new_err;
564 #else /* SVN_ERR__TRACING */
565 return err;
566 #endif /* SVN_ERR__TRACING */
567 }
568
569 /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
570 ### coupled to the current sole caller.*/
571 static void
print_error(svn_error_t * err,FILE * stream,const char * prefix)572 print_error(svn_error_t *err, FILE *stream, const char *prefix)
573 {
574 char errbuf[256];
575 const char *err_string;
576 svn_error_t *temp_err = NULL; /* ensure initialized even if
577 err->file == NULL */
578 /* Pretty-print the error */
579 /* Note: we can also log errors here someday. */
580
581 #ifdef SVN_DEBUG
582 /* Note: err->file is _not_ in UTF-8, because it's expanded from
583 the __FILE__ preprocessor macro. */
584 const char *file_utf8;
585
586 if (err->file
587 && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
588 err->pool)))
589 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
590 "%s:%ld", err->file, err->line));
591 else
592 {
593 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
594 stream, err->pool));
595 svn_error_clear(temp_err);
596 }
597
598 {
599 const char *symbolic_name;
600 if (svn_error__is_tracing_link(err))
601 /* Skip it; the error code will be printed by the real link. */
602 svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
603 else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
604 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
605 ": (apr_err=%s)\n", symbolic_name));
606 else
607 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
608 ": (apr_err=%d)\n", err->apr_err));
609 }
610 #endif /* SVN_DEBUG */
611
612 /* "traced call" */
613 if (svn_error__is_tracing_link(err))
614 {
615 /* Skip it. We already printed the file-line coordinates. */
616 }
617 /* Only print the same APR error string once. */
618 else if (err->message)
619 {
620 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
621 "%sE%06d: %s\n",
622 prefix, err->apr_err, err->message));
623 }
624 else
625 {
626 /* Is this a Subversion-specific error code? */
627 if ((err->apr_err > APR_OS_START_USEERR)
628 && (err->apr_err <= APR_OS_START_CANONERR))
629 err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
630 /* Otherwise, this must be an APR error code. */
631 else if ((temp_err = svn_utf_cstring_to_utf8
632 (&err_string, apr_strerror(err->apr_err, errbuf,
633 sizeof(errbuf)), err->pool)))
634 {
635 svn_error_clear(temp_err);
636 err_string = _("Can't recode error string from APR");
637 }
638
639 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
640 "%sE%06d: %s\n",
641 prefix, err->apr_err, err_string));
642 }
643 }
644
645 void
svn_handle_error2(svn_error_t * err,FILE * stream,svn_boolean_t fatal,const char * prefix)646 svn_handle_error2(svn_error_t *err,
647 FILE *stream,
648 svn_boolean_t fatal,
649 const char *prefix)
650 {
651 /* In a long error chain, there may be multiple errors with the same
652 error code and no custom message. We only want to print the
653 default message for that code once; printing it multiple times
654 would add no useful information. The 'empties' array below
655 remembers the codes of empty errors already seen in the chain.
656
657 We could allocate it in err->pool, but there's no telling how
658 long err will live or how many times it will get handled. So we
659 use a subpool. */
660 apr_pool_t *subpool;
661 apr_array_header_t *empties;
662 svn_error_t *tmp_err;
663
664 subpool = svn_pool_create(err->pool);
665 empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
666
667 tmp_err = err;
668 while (tmp_err)
669 {
670 svn_boolean_t printed_already = FALSE;
671
672 if (! tmp_err->message)
673 {
674 int i;
675
676 for (i = 0; i < empties->nelts; i++)
677 {
678 if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
679 {
680 printed_already = TRUE;
681 break;
682 }
683 }
684 }
685
686 if (! printed_already)
687 {
688 print_error(tmp_err, stream, prefix);
689 if (! tmp_err->message)
690 {
691 APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
692 }
693 }
694
695 tmp_err = tmp_err->child;
696 }
697
698 svn_pool_destroy(subpool);
699
700 fflush(stream);
701 if (fatal)
702 {
703 /* Avoid abort()s in maintainer mode. */
704 svn_error_clear(err);
705
706 /* We exit(1) here instead of abort()ing so that atexit handlers
707 get called. */
708 exit(EXIT_FAILURE);
709 }
710 }
711
712 void
svn_handle_warning2(FILE * stream,const svn_error_t * err,const char * prefix)713 svn_handle_warning2(FILE *stream, const svn_error_t *err, const char *prefix)
714 {
715 char buf[256];
716 #ifdef SVN_DEBUG
717 const char *symbolic_name = svn_error_symbolic_name(err->apr_err);
718 #endif
719
720 #ifdef SVN_DEBUG
721 if (symbolic_name)
722 svn_error_clear(
723 svn_cmdline_fprintf(stream, err->pool, "%swarning: apr_err=%s\n",
724 prefix, symbolic_name));
725 #endif
726
727 svn_error_clear(svn_cmdline_fprintf
728 (stream, err->pool,
729 _("%swarning: W%06d: %s\n"),
730 prefix, err->apr_err,
731 svn_err_best_message(err, buf, sizeof(buf))));
732 fflush(stream);
733 }
734
735 const char *
svn_err_best_message(const svn_error_t * err,char * buf,apr_size_t bufsize)736 svn_err_best_message(const svn_error_t *err, char *buf, apr_size_t bufsize)
737 {
738 /* Skip over any trace records. */
739 while (svn_error__is_tracing_link(err))
740 err = err->child;
741 if (err->message)
742 return err->message;
743 else
744 return svn_strerror(err->apr_err, buf, bufsize);
745 }
746
747
748 /* svn_strerror() and helpers */
749
750 /* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
751 typedef struct err_defn {
752 svn_errno_t errcode; /* 160004 */
753 const char *errname; /* SVN_ERR_FS_CORRUPT */
754 const char *errdesc; /* default message */
755 } err_defn;
756
757 /* To understand what is going on here, read svn_error_codes.h. */
758 #define SVN_ERROR_BUILD_ARRAY
759 #include "svn_error_codes.h"
760
761 char *
svn_strerror(apr_status_t statcode,char * buf,apr_size_t bufsize)762 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
763 {
764 const err_defn *defn;
765
766 for (defn = error_table; defn->errdesc != NULL; ++defn)
767 if (defn->errcode == (svn_errno_t)statcode)
768 {
769 apr_cpystrn(buf, _(defn->errdesc), bufsize);
770 return buf;
771 }
772
773 return apr_strerror(statcode, buf, bufsize);
774 }
775
776 #ifdef SVN_DEBUG
777 /* Defines svn__errno and svn__apr_errno */
778 #include "errorcode.inc"
779 #endif
780
781 const char *
svn_error_symbolic_name(apr_status_t statcode)782 svn_error_symbolic_name(apr_status_t statcode)
783 {
784 const err_defn *defn;
785 #ifdef SVN_DEBUG
786 int i;
787 #endif /* SVN_DEBUG */
788
789 for (defn = error_table; defn->errdesc != NULL; ++defn)
790 if (defn->errcode == (svn_errno_t)statcode)
791 return defn->errname;
792
793 /* "No error" is not in error_table. */
794 if (statcode == APR_SUCCESS)
795 return "SVN_NO_ERROR";
796
797 #ifdef SVN_DEBUG
798 /* Try errno.h symbols. */
799 /* Linear search through a sorted array */
800 for (i = 0; i < sizeof(svn__errno) / sizeof(svn__errno[0]); i++)
801 if (svn__errno[i].errcode == (int)statcode)
802 return svn__errno[i].errname;
803
804 /* Try APR errors. */
805 /* Linear search through a sorted array */
806 for (i = 0; i < sizeof(svn__apr_errno) / sizeof(svn__apr_errno[0]); i++)
807 if (svn__apr_errno[i].errcode == (int)statcode)
808 return svn__apr_errno[i].errname;
809 #endif /* SVN_DEBUG */
810
811 /* ### TODO: do we need APR_* error macros? What about APR_TO_OS_ERROR()? */
812
813 return NULL;
814 }
815
816
817
818 /* Malfunctions. */
819
820 svn_error_t *
svn_error_raise_on_malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)821 svn_error_raise_on_malfunction(svn_boolean_t can_return,
822 const char *file, int line,
823 const char *expr)
824 {
825 if (!can_return)
826 abort(); /* Nothing else we can do as a library */
827
828 /* The filename and line number of the error source needs to be set
829 here because svn_error_createf() is not the macro defined in
830 svn_error.h but the real function. */
831 svn_error__locate(file, line);
832
833 if (expr)
834 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
835 _("In file '%s' line %d: assertion failed (%s)"),
836 file, line, expr);
837 else
838 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
839 _("In file '%s' line %d: internal malfunction"),
840 file, line);
841 }
842
843 svn_error_t *
svn_error_abort_on_malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)844 svn_error_abort_on_malfunction(svn_boolean_t can_return,
845 const char *file, int line,
846 const char *expr)
847 {
848 svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
849
850 svn_handle_error2(err, stderr, FALSE, "svn: ");
851 abort();
852 return err; /* Not reached. */
853 }
854
855 /* The current handler for reporting malfunctions, and its default setting. */
856 static svn_error_malfunction_handler_t malfunction_handler
857 = svn_error_abort_on_malfunction;
858
859 svn_error_malfunction_handler_t
svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)860 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
861 {
862 svn_error_malfunction_handler_t old_malfunction_handler
863 = malfunction_handler;
864
865 malfunction_handler = func;
866 return old_malfunction_handler;
867 }
868
869 svn_error_malfunction_handler_t
svn_error_get_malfunction_handler(void)870 svn_error_get_malfunction_handler(void)
871 {
872 return malfunction_handler;
873 }
874
875 /* Note: Although this is a "__" function, it is in the public ABI, so
876 * we can never remove it or change its signature. */
877 svn_error_t *
svn_error__malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)878 svn_error__malfunction(svn_boolean_t can_return,
879 const char *file, int line,
880 const char *expr)
881 {
882 return malfunction_handler(can_return, file, line, expr);
883 }
884
885
886 /* Misc. */
887
888 svn_error_t *
svn_error__wrap_zlib(int zerr,const char * function,const char * message)889 svn_error__wrap_zlib(int zerr, const char *function, const char *message)
890 {
891 apr_status_t status;
892 const char *zmsg;
893
894 if (zerr == Z_OK)
895 return SVN_NO_ERROR;
896
897 switch (zerr)
898 {
899 case Z_STREAM_ERROR:
900 status = SVN_ERR_STREAM_MALFORMED_DATA;
901 zmsg = _("stream error");
902 break;
903
904 case Z_MEM_ERROR:
905 status = APR_ENOMEM;
906 zmsg = _("out of memory");
907 break;
908
909 case Z_BUF_ERROR:
910 status = APR_ENOMEM;
911 zmsg = _("buffer error");
912 break;
913
914 case Z_VERSION_ERROR:
915 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
916 zmsg = _("version error");
917 break;
918
919 case Z_DATA_ERROR:
920 status = SVN_ERR_STREAM_MALFORMED_DATA;
921 zmsg = _("corrupt data");
922 break;
923
924 default:
925 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
926 zmsg = _("unknown error");
927 break;
928 }
929
930 if (message != NULL)
931 return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
932 zmsg, message);
933 else
934 return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);
935 }
936