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