error.c revision 362181
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 56static apr_threadkey_t *error_file_key = NULL; 57static apr_threadkey_t *error_line_key = NULL; 58 59/* No-op destructor for apr_threadkey_private_create(). */ 60static 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. */ 66static const char * 67locate_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. */ 96static const char * volatile error_file = NULL; 97static long volatile error_line = -1; 98 99/* file_line for the non-debug case. */ 100static 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. */ 122void 123svn_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 150static 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 162static svn_error_t * 163make_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 225svn_error_t * 226svn_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 241svn_error_t * 242svn_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 260svn_error_t * 261svn_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 300svn_error_t * 301svn_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 311svn_error_t * 312svn_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. */ 332static const char error_tracing_link[] = "traced call"; 333 334svn_error_t * 335svn_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 359svn_error_t * 360svn_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 373void 374svn_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 415svn_error_t * 416svn_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 432svn_error_t * 433svn_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 444svn_error_t * 445svn_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 486void 487svn_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 500svn_boolean_t 501svn_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 514svn_error_t * 515svn_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.*/ 571static void 572print_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 645void 646svn_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 712void 713svn_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 735const char * 736svn_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 */ 751typedef 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 761char * 762svn_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 781const char * 782svn_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 820svn_error_t * 821svn_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 843svn_error_t * 844svn_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. */ 856static svn_error_malfunction_handler_t malfunction_handler 857 = svn_error_abort_on_malfunction; 858 859svn_error_malfunction_handler_t 860svn_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 869svn_error_malfunction_handler_t 870svn_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. */ 877svn_error_t * 878svn_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 888svn_error_t * 889svn_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