1251881Speter/* error.c: common exception handling for Subversion 2251881Speter * 3251881Speter * ==================================================================== 4251881Speter * Licensed to the Apache Software Foundation (ASF) under one 5251881Speter * or more contributor license agreements. See the NOTICE file 6251881Speter * distributed with this work for additional information 7251881Speter * regarding copyright ownership. The ASF licenses this file 8251881Speter * to you under the Apache License, Version 2.0 (the 9251881Speter * "License"); you may not use this file except in compliance 10251881Speter * with the License. You may obtain a copy of the License at 11251881Speter * 12251881Speter * http://www.apache.org/licenses/LICENSE-2.0 13251881Speter * 14251881Speter * Unless required by applicable law or agreed to in writing, 15251881Speter * software distributed under the License is distributed on an 16251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17251881Speter * KIND, either express or implied. See the License for the 18251881Speter * specific language governing permissions and limitations 19251881Speter * under the License. 20251881Speter * ==================================================================== 21251881Speter */ 22251881Speter 23251881Speter 24251881Speter 25251881Speter#include <stdarg.h> 26251881Speter 27251881Speter#include <apr_general.h> 28251881Speter#include <apr_pools.h> 29251881Speter#include <apr_strings.h> 30251881Speter 31251881Speter#include <zlib.h> 32251881Speter 33251881Speter#ifndef SVN_ERR__TRACING 34251881Speter#define SVN_ERR__TRACING 35251881Speter#endif 36251881Speter#include "svn_cmdline.h" 37251881Speter#include "svn_error.h" 38251881Speter#include "svn_pools.h" 39251881Speter#include "svn_utf.h" 40251881Speter 41251881Speter#ifdef SVN_DEBUG 42251881Speter/* XXX FIXME: These should be protected by a thread mutex. 43251881Speter svn_error__locate and make_error_internal should cooperate 44251881Speter in locking and unlocking it. */ 45251881Speter 46251881Speter/* XXX TODO: Define mutex here #if APR_HAS_THREADS */ 47251881Speterstatic const char * volatile error_file = NULL; 48251881Speterstatic long volatile error_line = -1; 49251881Speter 50251881Speter/* file_line for the non-debug case. */ 51251881Speterstatic const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>"; 52251881Speter#endif /* SVN_DEBUG */ 53251881Speter 54251881Speter#include "svn_private_config.h" 55251881Speter#include "private/svn_error_private.h" 56251881Speter 57251881Speter 58251881Speter/* 59251881Speter * Undefine the helpers for creating errors. 60251881Speter * 61251881Speter * *NOTE*: Any use of these functions in any other function may need 62251881Speter * to call svn_error__locate() because the macro that would otherwise 63251881Speter * do this is being undefined and the filename and line number will 64251881Speter * not be properly set in the static error_file and error_line 65251881Speter * variables. 66251881Speter */ 67251881Speter#undef svn_error_create 68251881Speter#undef svn_error_createf 69251881Speter#undef svn_error_quick_wrap 70299742Sdim#undef svn_error_quick_wrapf 71251881Speter#undef svn_error_wrap_apr 72251881Speter 73251881Speter/* Note: Although this is a "__" function, it was historically in the 74251881Speter * public ABI, so we can never change it or remove its signature, even 75251881Speter * though it is now only used in SVN_DEBUG mode. */ 76251881Spetervoid 77251881Spetersvn_error__locate(const char *file, long line) 78251881Speter{ 79251881Speter#if defined(SVN_DEBUG) 80251881Speter /* XXX TODO: Lock mutex here */ 81251881Speter error_file = file; 82251881Speter error_line = line; 83251881Speter#endif 84251881Speter} 85251881Speter 86251881Speter 87251881Speter/* Cleanup function for errors. svn_error_clear () removes this so 88251881Speter errors that are properly handled *don't* hit this code. */ 89251881Speterstatic apr_status_t err_abort(void *data) 90251881Speter{ 91251881Speter svn_error_t *err = data; /* For easy viewing in a debugger */ 92299742Sdim SVN_UNUSED(err); 93251881Speter 94251881Speter if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK")) 95251881Speter abort(); 96251881Speter return APR_SUCCESS; 97251881Speter} 98251881Speter 99251881Speter 100251881Speterstatic svn_error_t * 101251881Spetermake_error_internal(apr_status_t apr_err, 102251881Speter svn_error_t *child) 103251881Speter{ 104251881Speter apr_pool_t *pool; 105251881Speter svn_error_t *new_error; 106251881Speter 107251881Speter /* Reuse the child's pool, or create our own. */ 108251881Speter if (child) 109251881Speter pool = child->pool; 110251881Speter else 111251881Speter { 112299742Sdim pool = svn_pool_create(NULL); 113299742Sdim if (!pool) 114251881Speter abort(); 115251881Speter } 116251881Speter 117251881Speter /* Create the new error structure */ 118251881Speter new_error = apr_pcalloc(pool, sizeof(*new_error)); 119251881Speter 120251881Speter /* Fill 'er up. */ 121251881Speter new_error->apr_err = apr_err; 122251881Speter new_error->child = child; 123251881Speter new_error->pool = pool; 124251881Speter#if defined(SVN_DEBUG) 125251881Speter new_error->file = error_file; 126251881Speter new_error->line = error_line; 127251881Speter /* XXX TODO: Unlock mutex here */ 128251881Speter 129251881Speter if (! child) 130251881Speter apr_pool_cleanup_register(pool, new_error, 131251881Speter err_abort, 132251881Speter apr_pool_cleanup_null); 133251881Speter#endif 134251881Speter 135251881Speter return new_error; 136251881Speter} 137251881Speter 138251881Speter 139251881Speter 140251881Speter/*** Creating and destroying errors. ***/ 141251881Speter 142251881Spetersvn_error_t * 143251881Spetersvn_error_create(apr_status_t apr_err, 144251881Speter svn_error_t *child, 145251881Speter const char *message) 146251881Speter{ 147251881Speter svn_error_t *err; 148251881Speter 149251881Speter err = make_error_internal(apr_err, child); 150251881Speter 151251881Speter if (message) 152251881Speter err->message = apr_pstrdup(err->pool, message); 153251881Speter 154251881Speter return err; 155251881Speter} 156251881Speter 157251881Speter 158251881Spetersvn_error_t * 159251881Spetersvn_error_createf(apr_status_t apr_err, 160251881Speter svn_error_t *child, 161251881Speter const char *fmt, 162251881Speter ...) 163251881Speter{ 164251881Speter svn_error_t *err; 165251881Speter va_list ap; 166251881Speter 167251881Speter err = make_error_internal(apr_err, child); 168251881Speter 169251881Speter va_start(ap, fmt); 170251881Speter err->message = apr_pvsprintf(err->pool, fmt, ap); 171251881Speter va_end(ap); 172251881Speter 173251881Speter return err; 174251881Speter} 175251881Speter 176251881Speter 177251881Spetersvn_error_t * 178251881Spetersvn_error_wrap_apr(apr_status_t status, 179251881Speter const char *fmt, 180251881Speter ...) 181251881Speter{ 182251881Speter svn_error_t *err, *utf8_err; 183251881Speter va_list ap; 184251881Speter char errbuf[255]; 185251881Speter const char *msg_apr, *msg; 186251881Speter 187251881Speter err = make_error_internal(status, NULL); 188251881Speter 189251881Speter if (fmt) 190251881Speter { 191251881Speter /* Grab the APR error message. */ 192251881Speter apr_strerror(status, errbuf, sizeof(errbuf)); 193251881Speter utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool); 194251881Speter if (utf8_err) 195251881Speter msg_apr = NULL; 196251881Speter svn_error_clear(utf8_err); 197251881Speter 198251881Speter /* Append it to the formatted message. */ 199251881Speter va_start(ap, fmt); 200251881Speter msg = apr_pvsprintf(err->pool, fmt, ap); 201251881Speter va_end(ap); 202251881Speter if (msg_apr) 203251881Speter { 204299742Sdim err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, 205299742Sdim SVN_VA_NULL); 206251881Speter } 207251881Speter else 208251881Speter { 209251881Speter err->message = msg; 210251881Speter } 211251881Speter } 212251881Speter 213251881Speter return err; 214251881Speter} 215251881Speter 216251881Speter 217251881Spetersvn_error_t * 218251881Spetersvn_error_quick_wrap(svn_error_t *child, const char *new_msg) 219251881Speter{ 220251881Speter if (child == SVN_NO_ERROR) 221251881Speter return SVN_NO_ERROR; 222251881Speter 223251881Speter return svn_error_create(child->apr_err, 224251881Speter child, 225251881Speter new_msg); 226251881Speter} 227251881Speter 228299742Sdimsvn_error_t * 229299742Sdimsvn_error_quick_wrapf(svn_error_t *child, 230299742Sdim const char *fmt, 231299742Sdim ...) 232299742Sdim{ 233299742Sdim svn_error_t *err; 234299742Sdim va_list ap; 235299742Sdim 236299742Sdim if (child == SVN_NO_ERROR) 237299742Sdim return SVN_NO_ERROR; 238299742Sdim 239299742Sdim err = make_error_internal(child->apr_err, child); 240299742Sdim 241299742Sdim va_start(ap, fmt); 242299742Sdim err->message = apr_pvsprintf(err->pool, fmt, ap); 243299742Sdim va_end(ap); 244299742Sdim 245299742Sdim return err; 246299742Sdim} 247299742Sdim 248251881Speter/* Messages in tracing errors all point to this static string. */ 249251881Speterstatic const char error_tracing_link[] = "traced call"; 250251881Speter 251251881Spetersvn_error_t * 252251881Spetersvn_error__trace(const char *file, long line, svn_error_t *err) 253251881Speter{ 254251881Speter#ifndef SVN_DEBUG 255251881Speter 256251881Speter /* We shouldn't even be here, but whatever. Just return the error as-is. */ 257251881Speter return err; 258251881Speter 259251881Speter#else 260251881Speter 261251881Speter /* Only do the work when an error occurs. */ 262251881Speter if (err) 263251881Speter { 264251881Speter svn_error_t *trace; 265251881Speter svn_error__locate(file, line); 266251881Speter trace = make_error_internal(err->apr_err, err); 267251881Speter trace->message = error_tracing_link; 268251881Speter return trace; 269251881Speter } 270251881Speter return SVN_NO_ERROR; 271251881Speter 272251881Speter#endif 273251881Speter} 274251881Speter 275251881Speter 276251881Spetersvn_error_t * 277251881Spetersvn_error_compose_create(svn_error_t *err1, 278251881Speter svn_error_t *err2) 279251881Speter{ 280251881Speter if (err1 && err2) 281251881Speter { 282251881Speter svn_error_compose(err1, 283299742Sdim svn_error_create(SVN_ERR_COMPOSED_ERROR, err2, NULL)); 284251881Speter return err1; 285251881Speter } 286251881Speter return err1 ? err1 : err2; 287251881Speter} 288251881Speter 289251881Speter 290251881Spetervoid 291251881Spetersvn_error_compose(svn_error_t *chain, svn_error_t *new_err) 292251881Speter{ 293251881Speter apr_pool_t *pool = chain->pool; 294251881Speter apr_pool_t *oldpool = new_err->pool; 295251881Speter 296251881Speter while (chain->child) 297251881Speter chain = chain->child; 298251881Speter 299251881Speter#if defined(SVN_DEBUG) 300251881Speter /* Kill existing handler since the end of the chain is going to change */ 301251881Speter apr_pool_cleanup_kill(pool, chain, err_abort); 302251881Speter#endif 303251881Speter 304251881Speter /* Copy the new error chain into the old chain's pool. */ 305251881Speter while (new_err) 306251881Speter { 307251881Speter chain->child = apr_palloc(pool, sizeof(*chain->child)); 308251881Speter chain = chain->child; 309251881Speter *chain = *new_err; 310251881Speter if (chain->message) 311251881Speter chain->message = apr_pstrdup(pool, new_err->message); 312289166Speter if (chain->file) 313289166Speter chain->file = apr_pstrdup(pool, new_err->file); 314251881Speter chain->pool = pool; 315251881Speter#if defined(SVN_DEBUG) 316251881Speter if (! new_err->child) 317251881Speter apr_pool_cleanup_kill(oldpool, new_err, err_abort); 318251881Speter#endif 319251881Speter new_err = new_err->child; 320251881Speter } 321251881Speter 322251881Speter#if defined(SVN_DEBUG) 323251881Speter apr_pool_cleanup_register(pool, chain, 324251881Speter err_abort, 325251881Speter apr_pool_cleanup_null); 326251881Speter#endif 327251881Speter 328251881Speter /* Destroy the new error chain. */ 329251881Speter svn_pool_destroy(oldpool); 330251881Speter} 331251881Speter 332251881Spetersvn_error_t * 333251881Spetersvn_error_root_cause(svn_error_t *err) 334251881Speter{ 335251881Speter while (err) 336251881Speter { 337299742Sdim /* I don't think we can change the behavior here, but the additional 338299742Sdim error chain doesn't define the root cause. Perhaps we should rev 339299742Sdim this function. */ 340299742Sdim if (err->child /*&& err->child->apr_err != SVN_ERR_COMPOSED_ERROR*/) 341251881Speter err = err->child; 342251881Speter else 343251881Speter break; 344251881Speter } 345251881Speter 346251881Speter return err; 347251881Speter} 348251881Speter 349251881Spetersvn_error_t * 350251881Spetersvn_error_find_cause(svn_error_t *err, apr_status_t apr_err) 351251881Speter{ 352251881Speter svn_error_t *child; 353251881Speter 354251881Speter for (child = err; child; child = child->child) 355251881Speter if (child->apr_err == apr_err) 356251881Speter return child; 357251881Speter 358251881Speter return SVN_NO_ERROR; 359251881Speter} 360251881Speter 361251881Spetersvn_error_t * 362299742Sdimsvn_error_dup(const svn_error_t *err) 363251881Speter{ 364251881Speter apr_pool_t *pool; 365251881Speter svn_error_t *new_err = NULL, *tmp_err = NULL; 366251881Speter 367299742Sdim if (!err) 368299742Sdim return SVN_NO_ERROR; 369299742Sdim 370299742Sdim pool = svn_pool_create(NULL); 371299742Sdim if (!pool) 372251881Speter abort(); 373251881Speter 374251881Speter for (; err; err = err->child) 375251881Speter { 376251881Speter if (! new_err) 377251881Speter { 378251881Speter new_err = apr_palloc(pool, sizeof(*new_err)); 379251881Speter tmp_err = new_err; 380251881Speter } 381251881Speter else 382251881Speter { 383251881Speter tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child)); 384251881Speter tmp_err = tmp_err->child; 385251881Speter } 386251881Speter *tmp_err = *err; 387251881Speter tmp_err->pool = pool; 388251881Speter if (tmp_err->message) 389251881Speter tmp_err->message = apr_pstrdup(pool, tmp_err->message); 390289166Speter if (tmp_err->file) 391289166Speter tmp_err->file = apr_pstrdup(pool, tmp_err->file); 392251881Speter } 393251881Speter 394251881Speter#if defined(SVN_DEBUG) 395251881Speter apr_pool_cleanup_register(pool, tmp_err, 396251881Speter err_abort, 397251881Speter apr_pool_cleanup_null); 398251881Speter#endif 399251881Speter 400251881Speter return new_err; 401251881Speter} 402251881Speter 403251881Spetervoid 404251881Spetersvn_error_clear(svn_error_t *err) 405251881Speter{ 406251881Speter if (err) 407251881Speter { 408251881Speter#if defined(SVN_DEBUG) 409251881Speter while (err->child) 410251881Speter err = err->child; 411251881Speter apr_pool_cleanup_kill(err->pool, err, err_abort); 412251881Speter#endif 413251881Speter svn_pool_destroy(err->pool); 414251881Speter } 415251881Speter} 416251881Speter 417251881Spetersvn_boolean_t 418299742Sdimsvn_error__is_tracing_link(const svn_error_t *err) 419251881Speter{ 420251881Speter#ifdef SVN_ERR__TRACING 421251881Speter /* ### A strcmp()? Really? I think it's the best we can do unless 422251881Speter ### we add a boolean field to svn_error_t that's set only for 423251881Speter ### these "placeholder error chain" items. Not such a bad idea, 424251881Speter ### really... */ 425251881Speter return (err && err->message && !strcmp(err->message, error_tracing_link)); 426251881Speter#else 427251881Speter return FALSE; 428251881Speter#endif 429251881Speter} 430251881Speter 431251881Spetersvn_error_t * 432251881Spetersvn_error_purge_tracing(svn_error_t *err) 433251881Speter{ 434251881Speter#ifdef SVN_ERR__TRACING 435251881Speter svn_error_t *new_err = NULL, *new_err_leaf = NULL; 436251881Speter 437251881Speter if (! err) 438251881Speter return SVN_NO_ERROR; 439251881Speter 440251881Speter do 441251881Speter { 442251881Speter svn_error_t *tmp_err; 443251881Speter 444251881Speter /* Skip over any trace-only links. */ 445251881Speter while (err && svn_error__is_tracing_link(err)) 446251881Speter err = err->child; 447251881Speter 448251881Speter /* The link must be a real link in the error chain, otherwise an 449251881Speter error chain with trace only links would map into SVN_NO_ERROR. */ 450251881Speter if (! err) 451251881Speter return svn_error_create( 452251881Speter SVN_ERR_ASSERTION_ONLY_TRACING_LINKS, 453299742Sdim svn_error__malfunction(TRUE, __FILE__, __LINE__, 454299742Sdim NULL /* ### say something? */), 455251881Speter NULL); 456251881Speter 457251881Speter /* Copy the current error except for its child error pointer 458251881Speter into the new error. Share any message and source filename 459251881Speter strings from the error. */ 460251881Speter tmp_err = apr_palloc(err->pool, sizeof(*tmp_err)); 461251881Speter *tmp_err = *err; 462251881Speter tmp_err->child = NULL; 463251881Speter 464251881Speter /* Add a new link to the new chain (creating the chain if necessary). */ 465251881Speter if (! new_err) 466251881Speter { 467251881Speter new_err = tmp_err; 468251881Speter new_err_leaf = tmp_err; 469251881Speter } 470251881Speter else 471251881Speter { 472251881Speter new_err_leaf->child = tmp_err; 473251881Speter new_err_leaf = tmp_err; 474251881Speter } 475251881Speter 476251881Speter /* Advance to the next link in the original chain. */ 477251881Speter err = err->child; 478251881Speter } while (err); 479251881Speter 480251881Speter return new_err; 481251881Speter#else /* SVN_ERR__TRACING */ 482251881Speter return err; 483251881Speter#endif /* SVN_ERR__TRACING */ 484251881Speter} 485251881Speter 486251881Speter/* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly 487251881Speter ### coupled to the current sole caller.*/ 488251881Speterstatic void 489251881Speterprint_error(svn_error_t *err, FILE *stream, const char *prefix) 490251881Speter{ 491251881Speter char errbuf[256]; 492251881Speter const char *err_string; 493251881Speter svn_error_t *temp_err = NULL; /* ensure initialized even if 494251881Speter err->file == NULL */ 495251881Speter /* Pretty-print the error */ 496251881Speter /* Note: we can also log errors here someday. */ 497251881Speter 498251881Speter#ifdef SVN_DEBUG 499251881Speter /* Note: err->file is _not_ in UTF-8, because it's expanded from 500251881Speter the __FILE__ preprocessor macro. */ 501251881Speter const char *file_utf8; 502251881Speter 503251881Speter if (err->file 504251881Speter && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file, 505251881Speter err->pool))) 506251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 507251881Speter "%s:%ld", err->file, err->line)); 508251881Speter else 509251881Speter { 510251881Speter svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED, 511251881Speter stream, err->pool)); 512251881Speter svn_error_clear(temp_err); 513251881Speter } 514251881Speter 515251881Speter { 516251881Speter const char *symbolic_name; 517251881Speter if (svn_error__is_tracing_link(err)) 518251881Speter /* Skip it; the error code will be printed by the real link. */ 519251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n")); 520251881Speter else if ((symbolic_name = svn_error_symbolic_name(err->apr_err))) 521251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 522251881Speter ": (apr_err=%s)\n", symbolic_name)); 523251881Speter else 524251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 525251881Speter ": (apr_err=%d)\n", err->apr_err)); 526251881Speter } 527251881Speter#endif /* SVN_DEBUG */ 528251881Speter 529251881Speter /* "traced call" */ 530251881Speter if (svn_error__is_tracing_link(err)) 531251881Speter { 532251881Speter /* Skip it. We already printed the file-line coordinates. */ 533251881Speter } 534251881Speter /* Only print the same APR error string once. */ 535251881Speter else if (err->message) 536251881Speter { 537251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 538251881Speter "%sE%06d: %s\n", 539251881Speter prefix, err->apr_err, err->message)); 540251881Speter } 541251881Speter else 542251881Speter { 543251881Speter /* Is this a Subversion-specific error code? */ 544251881Speter if ((err->apr_err > APR_OS_START_USEERR) 545251881Speter && (err->apr_err <= APR_OS_START_CANONERR)) 546251881Speter err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf)); 547251881Speter /* Otherwise, this must be an APR error code. */ 548251881Speter else if ((temp_err = svn_utf_cstring_to_utf8 549251881Speter (&err_string, apr_strerror(err->apr_err, errbuf, 550251881Speter sizeof(errbuf)), err->pool))) 551251881Speter { 552251881Speter svn_error_clear(temp_err); 553251881Speter err_string = _("Can't recode error string from APR"); 554251881Speter } 555251881Speter 556251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 557251881Speter "%sE%06d: %s\n", 558251881Speter prefix, err->apr_err, err_string)); 559251881Speter } 560251881Speter} 561251881Speter 562251881Spetervoid 563251881Spetersvn_handle_error2(svn_error_t *err, 564251881Speter FILE *stream, 565251881Speter svn_boolean_t fatal, 566251881Speter const char *prefix) 567251881Speter{ 568251881Speter /* In a long error chain, there may be multiple errors with the same 569251881Speter error code and no custom message. We only want to print the 570251881Speter default message for that code once; printing it multiple times 571251881Speter would add no useful information. The 'empties' array below 572251881Speter remembers the codes of empty errors already seen in the chain. 573251881Speter 574251881Speter We could allocate it in err->pool, but there's no telling how 575251881Speter long err will live or how many times it will get handled. So we 576251881Speter use a subpool. */ 577251881Speter apr_pool_t *subpool; 578251881Speter apr_array_header_t *empties; 579251881Speter svn_error_t *tmp_err; 580251881Speter 581299742Sdim subpool = svn_pool_create(err->pool); 582251881Speter empties = apr_array_make(subpool, 0, sizeof(apr_status_t)); 583251881Speter 584251881Speter tmp_err = err; 585251881Speter while (tmp_err) 586251881Speter { 587251881Speter svn_boolean_t printed_already = FALSE; 588251881Speter 589251881Speter if (! tmp_err->message) 590251881Speter { 591251881Speter int i; 592251881Speter 593251881Speter for (i = 0; i < empties->nelts; i++) 594251881Speter { 595251881Speter if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) ) 596251881Speter { 597251881Speter printed_already = TRUE; 598251881Speter break; 599251881Speter } 600251881Speter } 601251881Speter } 602251881Speter 603251881Speter if (! printed_already) 604251881Speter { 605251881Speter print_error(tmp_err, stream, prefix); 606251881Speter if (! tmp_err->message) 607251881Speter { 608251881Speter APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err; 609251881Speter } 610251881Speter } 611251881Speter 612251881Speter tmp_err = tmp_err->child; 613251881Speter } 614251881Speter 615251881Speter svn_pool_destroy(subpool); 616251881Speter 617251881Speter fflush(stream); 618251881Speter if (fatal) 619251881Speter { 620251881Speter /* Avoid abort()s in maintainer mode. */ 621251881Speter svn_error_clear(err); 622251881Speter 623251881Speter /* We exit(1) here instead of abort()ing so that atexit handlers 624251881Speter get called. */ 625251881Speter exit(EXIT_FAILURE); 626251881Speter } 627251881Speter} 628251881Speter 629251881Spetervoid 630299742Sdimsvn_handle_warning2(FILE *stream, const svn_error_t *err, const char *prefix) 631251881Speter{ 632251881Speter char buf[256]; 633299742Sdim#ifdef SVN_DEBUG 634299742Sdim const char *symbolic_name = svn_error_symbolic_name(err->apr_err); 635299742Sdim#endif 636251881Speter 637299742Sdim#ifdef SVN_DEBUG 638299742Sdim if (symbolic_name) 639299742Sdim svn_error_clear( 640299742Sdim svn_cmdline_fprintf(stream, err->pool, "%swarning: apr_err=%s\n", 641299742Sdim prefix, symbolic_name)); 642299742Sdim#endif 643299742Sdim 644251881Speter svn_error_clear(svn_cmdline_fprintf 645251881Speter (stream, err->pool, 646251881Speter _("%swarning: W%06d: %s\n"), 647251881Speter prefix, err->apr_err, 648251881Speter svn_err_best_message(err, buf, sizeof(buf)))); 649251881Speter fflush(stream); 650251881Speter} 651251881Speter 652251881Speterconst char * 653299742Sdimsvn_err_best_message(const svn_error_t *err, char *buf, apr_size_t bufsize) 654251881Speter{ 655251881Speter /* Skip over any trace records. */ 656251881Speter while (svn_error__is_tracing_link(err)) 657251881Speter err = err->child; 658251881Speter if (err->message) 659251881Speter return err->message; 660251881Speter else 661251881Speter return svn_strerror(err->apr_err, buf, bufsize); 662251881Speter} 663251881Speter 664251881Speter 665251881Speter/* svn_strerror() and helpers */ 666251881Speter 667251881Speter/* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */ 668251881Spetertypedef struct err_defn { 669251881Speter svn_errno_t errcode; /* 160004 */ 670251881Speter const char *errname; /* SVN_ERR_FS_CORRUPT */ 671251881Speter const char *errdesc; /* default message */ 672251881Speter} err_defn; 673251881Speter 674251881Speter/* To understand what is going on here, read svn_error_codes.h. */ 675251881Speter#define SVN_ERROR_BUILD_ARRAY 676251881Speter#include "svn_error_codes.h" 677251881Speter 678251881Speterchar * 679251881Spetersvn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize) 680251881Speter{ 681251881Speter const err_defn *defn; 682251881Speter 683251881Speter for (defn = error_table; defn->errdesc != NULL; ++defn) 684251881Speter if (defn->errcode == (svn_errno_t)statcode) 685251881Speter { 686251881Speter apr_cpystrn(buf, _(defn->errdesc), bufsize); 687251881Speter return buf; 688251881Speter } 689251881Speter 690251881Speter return apr_strerror(statcode, buf, bufsize); 691251881Speter} 692251881Speter 693299742Sdim#ifdef SVN_DEBUG 694299742Sdim/* Defines svn__errno and svn__apr_errno */ 695299742Sdim#include "errorcode.inc" 696299742Sdim#endif 697299742Sdim 698251881Speterconst char * 699251881Spetersvn_error_symbolic_name(apr_status_t statcode) 700251881Speter{ 701251881Speter const err_defn *defn; 702299742Sdim#ifdef SVN_DEBUG 703299742Sdim int i; 704299742Sdim#endif /* SVN_DEBUG */ 705251881Speter 706251881Speter for (defn = error_table; defn->errdesc != NULL; ++defn) 707251881Speter if (defn->errcode == (svn_errno_t)statcode) 708251881Speter return defn->errname; 709251881Speter 710251881Speter /* "No error" is not in error_table. */ 711299742Sdim if (statcode == APR_SUCCESS) 712251881Speter return "SVN_NO_ERROR"; 713251881Speter 714299742Sdim#ifdef SVN_DEBUG 715299742Sdim /* Try errno.h symbols. */ 716299742Sdim /* Linear search through a sorted array */ 717299742Sdim for (i = 0; i < sizeof(svn__errno) / sizeof(svn__errno[0]); i++) 718299742Sdim if (svn__errno[i].errcode == (int)statcode) 719299742Sdim return svn__errno[i].errname; 720299742Sdim 721299742Sdim /* Try APR errors. */ 722299742Sdim /* Linear search through a sorted array */ 723299742Sdim for (i = 0; i < sizeof(svn__apr_errno) / sizeof(svn__apr_errno[0]); i++) 724299742Sdim if (svn__apr_errno[i].errcode == (int)statcode) 725299742Sdim return svn__apr_errno[i].errname; 726299742Sdim#endif /* SVN_DEBUG */ 727299742Sdim 728299742Sdim /* ### TODO: do we need APR_* error macros? What about APR_TO_OS_ERROR()? */ 729299742Sdim 730251881Speter return NULL; 731251881Speter} 732251881Speter 733251881Speter 734251881Speter 735251881Speter/* Malfunctions. */ 736251881Speter 737251881Spetersvn_error_t * 738251881Spetersvn_error_raise_on_malfunction(svn_boolean_t can_return, 739251881Speter const char *file, int line, 740251881Speter const char *expr) 741251881Speter{ 742251881Speter if (!can_return) 743251881Speter abort(); /* Nothing else we can do as a library */ 744251881Speter 745251881Speter /* The filename and line number of the error source needs to be set 746251881Speter here because svn_error_createf() is not the macro defined in 747251881Speter svn_error.h but the real function. */ 748251881Speter svn_error__locate(file, line); 749251881Speter 750251881Speter if (expr) 751251881Speter return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, 752251881Speter _("In file '%s' line %d: assertion failed (%s)"), 753251881Speter file, line, expr); 754251881Speter else 755251881Speter return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, 756251881Speter _("In file '%s' line %d: internal malfunction"), 757251881Speter file, line); 758251881Speter} 759251881Speter 760251881Spetersvn_error_t * 761251881Spetersvn_error_abort_on_malfunction(svn_boolean_t can_return, 762251881Speter const char *file, int line, 763251881Speter const char *expr) 764251881Speter{ 765251881Speter svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr); 766251881Speter 767251881Speter svn_handle_error2(err, stderr, FALSE, "svn: "); 768251881Speter abort(); 769251881Speter return err; /* Not reached. */ 770251881Speter} 771251881Speter 772251881Speter/* The current handler for reporting malfunctions, and its default setting. */ 773251881Speterstatic svn_error_malfunction_handler_t malfunction_handler 774251881Speter = svn_error_abort_on_malfunction; 775251881Speter 776251881Spetersvn_error_malfunction_handler_t 777251881Spetersvn_error_set_malfunction_handler(svn_error_malfunction_handler_t func) 778251881Speter{ 779251881Speter svn_error_malfunction_handler_t old_malfunction_handler 780251881Speter = malfunction_handler; 781251881Speter 782251881Speter malfunction_handler = func; 783251881Speter return old_malfunction_handler; 784251881Speter} 785251881Speter 786299742Sdimsvn_error_malfunction_handler_t 787299742Sdimsvn_error_get_malfunction_handler(void) 788299742Sdim{ 789299742Sdim return malfunction_handler; 790299742Sdim} 791299742Sdim 792251881Speter/* Note: Although this is a "__" function, it is in the public ABI, so 793251881Speter * we can never remove it or change its signature. */ 794251881Spetersvn_error_t * 795251881Spetersvn_error__malfunction(svn_boolean_t can_return, 796251881Speter const char *file, int line, 797251881Speter const char *expr) 798251881Speter{ 799251881Speter return malfunction_handler(can_return, file, line, expr); 800251881Speter} 801251881Speter 802251881Speter 803251881Speter/* Misc. */ 804251881Speter 805251881Spetersvn_error_t * 806251881Spetersvn_error__wrap_zlib(int zerr, const char *function, const char *message) 807251881Speter{ 808251881Speter apr_status_t status; 809251881Speter const char *zmsg; 810251881Speter 811251881Speter if (zerr == Z_OK) 812251881Speter return SVN_NO_ERROR; 813251881Speter 814251881Speter switch (zerr) 815251881Speter { 816251881Speter case Z_STREAM_ERROR: 817251881Speter status = SVN_ERR_STREAM_MALFORMED_DATA; 818251881Speter zmsg = _("stream error"); 819251881Speter break; 820251881Speter 821251881Speter case Z_MEM_ERROR: 822251881Speter status = APR_ENOMEM; 823251881Speter zmsg = _("out of memory"); 824251881Speter break; 825251881Speter 826251881Speter case Z_BUF_ERROR: 827251881Speter status = APR_ENOMEM; 828251881Speter zmsg = _("buffer error"); 829251881Speter break; 830251881Speter 831251881Speter case Z_VERSION_ERROR: 832251881Speter status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; 833251881Speter zmsg = _("version error"); 834251881Speter break; 835251881Speter 836251881Speter case Z_DATA_ERROR: 837251881Speter status = SVN_ERR_STREAM_MALFORMED_DATA; 838251881Speter zmsg = _("corrupt data"); 839251881Speter break; 840251881Speter 841251881Speter default: 842251881Speter status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; 843251881Speter zmsg = _("unknown error"); 844251881Speter break; 845251881Speter } 846251881Speter 847251881Speter if (message != NULL) 848251881Speter return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function, 849251881Speter zmsg, message); 850251881Speter else 851251881Speter return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg); 852251881Speter} 853