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 70251881Speter#undef svn_error_wrap_apr 71251881Speter 72251881Speter/* Note: Although this is a "__" function, it was historically in the 73251881Speter * public ABI, so we can never change it or remove its signature, even 74251881Speter * though it is now only used in SVN_DEBUG mode. */ 75251881Spetervoid 76251881Spetersvn_error__locate(const char *file, long line) 77251881Speter{ 78251881Speter#if defined(SVN_DEBUG) 79251881Speter /* XXX TODO: Lock mutex here */ 80251881Speter error_file = file; 81251881Speter error_line = line; 82251881Speter#endif 83251881Speter} 84251881Speter 85251881Speter 86251881Speter/* Cleanup function for errors. svn_error_clear () removes this so 87251881Speter errors that are properly handled *don't* hit this code. */ 88251881Speter#if defined(SVN_DEBUG) 89251881Speterstatic apr_status_t err_abort(void *data) 90251881Speter{ 91251881Speter svn_error_t *err = data; /* For easy viewing in a debugger */ 92251881Speter err = err; /* Fake a use for the variable to avoid compiler warnings */ 93251881Speter 94251881Speter if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK")) 95251881Speter abort(); 96251881Speter return APR_SUCCESS; 97251881Speter} 98251881Speter#endif 99251881Speter 100251881Speter 101251881Speterstatic svn_error_t * 102251881Spetermake_error_internal(apr_status_t apr_err, 103251881Speter svn_error_t *child) 104251881Speter{ 105251881Speter apr_pool_t *pool; 106251881Speter svn_error_t *new_error; 107251881Speter 108251881Speter /* Reuse the child's pool, or create our own. */ 109251881Speter if (child) 110251881Speter pool = child->pool; 111251881Speter else 112251881Speter { 113251881Speter if (apr_pool_create(&pool, NULL)) 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 { 204251881Speter err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL); 205251881Speter } 206251881Speter else 207251881Speter { 208251881Speter err->message = msg; 209251881Speter } 210251881Speter } 211251881Speter 212251881Speter return err; 213251881Speter} 214251881Speter 215251881Speter 216251881Spetersvn_error_t * 217251881Spetersvn_error_quick_wrap(svn_error_t *child, const char *new_msg) 218251881Speter{ 219251881Speter if (child == SVN_NO_ERROR) 220251881Speter return SVN_NO_ERROR; 221251881Speter 222251881Speter return svn_error_create(child->apr_err, 223251881Speter child, 224251881Speter new_msg); 225251881Speter} 226251881Speter 227251881Speter/* Messages in tracing errors all point to this static string. */ 228251881Speterstatic const char error_tracing_link[] = "traced call"; 229251881Speter 230251881Spetersvn_error_t * 231251881Spetersvn_error__trace(const char *file, long line, svn_error_t *err) 232251881Speter{ 233251881Speter#ifndef SVN_DEBUG 234251881Speter 235251881Speter /* We shouldn't even be here, but whatever. Just return the error as-is. */ 236251881Speter return err; 237251881Speter 238251881Speter#else 239251881Speter 240251881Speter /* Only do the work when an error occurs. */ 241251881Speter if (err) 242251881Speter { 243251881Speter svn_error_t *trace; 244251881Speter svn_error__locate(file, line); 245251881Speter trace = make_error_internal(err->apr_err, err); 246251881Speter trace->message = error_tracing_link; 247251881Speter return trace; 248251881Speter } 249251881Speter return SVN_NO_ERROR; 250251881Speter 251251881Speter#endif 252251881Speter} 253251881Speter 254251881Speter 255251881Spetersvn_error_t * 256251881Spetersvn_error_compose_create(svn_error_t *err1, 257251881Speter svn_error_t *err2) 258251881Speter{ 259251881Speter if (err1 && err2) 260251881Speter { 261251881Speter svn_error_compose(err1, 262251881Speter svn_error_quick_wrap(err2, 263251881Speter _("Additional errors:"))); 264251881Speter return err1; 265251881Speter } 266251881Speter return err1 ? err1 : err2; 267251881Speter} 268251881Speter 269251881Speter 270251881Spetervoid 271251881Spetersvn_error_compose(svn_error_t *chain, svn_error_t *new_err) 272251881Speter{ 273251881Speter apr_pool_t *pool = chain->pool; 274251881Speter apr_pool_t *oldpool = new_err->pool; 275251881Speter 276251881Speter while (chain->child) 277251881Speter chain = chain->child; 278251881Speter 279251881Speter#if defined(SVN_DEBUG) 280251881Speter /* Kill existing handler since the end of the chain is going to change */ 281251881Speter apr_pool_cleanup_kill(pool, chain, err_abort); 282251881Speter#endif 283251881Speter 284251881Speter /* Copy the new error chain into the old chain's pool. */ 285251881Speter while (new_err) 286251881Speter { 287251881Speter chain->child = apr_palloc(pool, sizeof(*chain->child)); 288251881Speter chain = chain->child; 289251881Speter *chain = *new_err; 290251881Speter if (chain->message) 291251881Speter chain->message = apr_pstrdup(pool, new_err->message); 292251881Speter chain->pool = pool; 293251881Speter#if defined(SVN_DEBUG) 294251881Speter if (! new_err->child) 295251881Speter apr_pool_cleanup_kill(oldpool, new_err, err_abort); 296251881Speter#endif 297251881Speter new_err = new_err->child; 298251881Speter } 299251881Speter 300251881Speter#if defined(SVN_DEBUG) 301251881Speter apr_pool_cleanup_register(pool, chain, 302251881Speter err_abort, 303251881Speter apr_pool_cleanup_null); 304251881Speter#endif 305251881Speter 306251881Speter /* Destroy the new error chain. */ 307251881Speter svn_pool_destroy(oldpool); 308251881Speter} 309251881Speter 310251881Spetersvn_error_t * 311251881Spetersvn_error_root_cause(svn_error_t *err) 312251881Speter{ 313251881Speter while (err) 314251881Speter { 315251881Speter if (err->child) 316251881Speter err = err->child; 317251881Speter else 318251881Speter break; 319251881Speter } 320251881Speter 321251881Speter return err; 322251881Speter} 323251881Speter 324251881Spetersvn_error_t * 325251881Spetersvn_error_find_cause(svn_error_t *err, apr_status_t apr_err) 326251881Speter{ 327251881Speter svn_error_t *child; 328251881Speter 329251881Speter for (child = err; child; child = child->child) 330251881Speter if (child->apr_err == apr_err) 331251881Speter return child; 332251881Speter 333251881Speter return SVN_NO_ERROR; 334251881Speter} 335251881Speter 336251881Spetersvn_error_t * 337251881Spetersvn_error_dup(svn_error_t *err) 338251881Speter{ 339251881Speter apr_pool_t *pool; 340251881Speter svn_error_t *new_err = NULL, *tmp_err = NULL; 341251881Speter 342251881Speter if (apr_pool_create(&pool, NULL)) 343251881Speter abort(); 344251881Speter 345251881Speter for (; err; err = err->child) 346251881Speter { 347251881Speter if (! new_err) 348251881Speter { 349251881Speter new_err = apr_palloc(pool, sizeof(*new_err)); 350251881Speter tmp_err = new_err; 351251881Speter } 352251881Speter else 353251881Speter { 354251881Speter tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child)); 355251881Speter tmp_err = tmp_err->child; 356251881Speter } 357251881Speter *tmp_err = *err; 358251881Speter tmp_err->pool = pool; 359251881Speter if (tmp_err->message) 360251881Speter tmp_err->message = apr_pstrdup(pool, tmp_err->message); 361251881Speter } 362251881Speter 363251881Speter#if defined(SVN_DEBUG) 364251881Speter apr_pool_cleanup_register(pool, tmp_err, 365251881Speter err_abort, 366251881Speter apr_pool_cleanup_null); 367251881Speter#endif 368251881Speter 369251881Speter return new_err; 370251881Speter} 371251881Speter 372251881Spetervoid 373251881Spetersvn_error_clear(svn_error_t *err) 374251881Speter{ 375251881Speter if (err) 376251881Speter { 377251881Speter#if defined(SVN_DEBUG) 378251881Speter while (err->child) 379251881Speter err = err->child; 380251881Speter apr_pool_cleanup_kill(err->pool, err, err_abort); 381251881Speter#endif 382251881Speter svn_pool_destroy(err->pool); 383251881Speter } 384251881Speter} 385251881Speter 386251881Spetersvn_boolean_t 387251881Spetersvn_error__is_tracing_link(svn_error_t *err) 388251881Speter{ 389251881Speter#ifdef SVN_ERR__TRACING 390251881Speter /* ### A strcmp()? Really? I think it's the best we can do unless 391251881Speter ### we add a boolean field to svn_error_t that's set only for 392251881Speter ### these "placeholder error chain" items. Not such a bad idea, 393251881Speter ### really... */ 394251881Speter return (err && err->message && !strcmp(err->message, error_tracing_link)); 395251881Speter#else 396251881Speter return FALSE; 397251881Speter#endif 398251881Speter} 399251881Speter 400251881Spetersvn_error_t * 401251881Spetersvn_error_purge_tracing(svn_error_t *err) 402251881Speter{ 403251881Speter#ifdef SVN_ERR__TRACING 404251881Speter svn_error_t *new_err = NULL, *new_err_leaf = NULL; 405251881Speter 406251881Speter if (! err) 407251881Speter return SVN_NO_ERROR; 408251881Speter 409251881Speter do 410251881Speter { 411251881Speter svn_error_t *tmp_err; 412251881Speter 413251881Speter /* Skip over any trace-only links. */ 414251881Speter while (err && svn_error__is_tracing_link(err)) 415251881Speter err = err->child; 416251881Speter 417251881Speter /* The link must be a real link in the error chain, otherwise an 418251881Speter error chain with trace only links would map into SVN_NO_ERROR. */ 419251881Speter if (! err) 420251881Speter return svn_error_create( 421251881Speter SVN_ERR_ASSERTION_ONLY_TRACING_LINKS, 422251881Speter svn_error_compose_create( 423251881Speter svn_error__malfunction(TRUE, __FILE__, __LINE__, 424251881Speter NULL /* ### say something? */), 425251881Speter err), 426251881Speter NULL); 427251881Speter 428251881Speter /* Copy the current error except for its child error pointer 429251881Speter into the new error. Share any message and source filename 430251881Speter strings from the error. */ 431251881Speter tmp_err = apr_palloc(err->pool, sizeof(*tmp_err)); 432251881Speter *tmp_err = *err; 433251881Speter tmp_err->child = NULL; 434251881Speter 435251881Speter /* Add a new link to the new chain (creating the chain if necessary). */ 436251881Speter if (! new_err) 437251881Speter { 438251881Speter new_err = tmp_err; 439251881Speter new_err_leaf = tmp_err; 440251881Speter } 441251881Speter else 442251881Speter { 443251881Speter new_err_leaf->child = tmp_err; 444251881Speter new_err_leaf = tmp_err; 445251881Speter } 446251881Speter 447251881Speter /* Advance to the next link in the original chain. */ 448251881Speter err = err->child; 449251881Speter } while (err); 450251881Speter 451251881Speter return new_err; 452251881Speter#else /* SVN_ERR__TRACING */ 453251881Speter return err; 454251881Speter#endif /* SVN_ERR__TRACING */ 455251881Speter} 456251881Speter 457251881Speter/* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly 458251881Speter ### coupled to the current sole caller.*/ 459251881Speterstatic void 460251881Speterprint_error(svn_error_t *err, FILE *stream, const char *prefix) 461251881Speter{ 462251881Speter char errbuf[256]; 463251881Speter const char *err_string; 464251881Speter svn_error_t *temp_err = NULL; /* ensure initialized even if 465251881Speter err->file == NULL */ 466251881Speter /* Pretty-print the error */ 467251881Speter /* Note: we can also log errors here someday. */ 468251881Speter 469251881Speter#ifdef SVN_DEBUG 470251881Speter /* Note: err->file is _not_ in UTF-8, because it's expanded from 471251881Speter the __FILE__ preprocessor macro. */ 472251881Speter const char *file_utf8; 473251881Speter 474251881Speter if (err->file 475251881Speter && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file, 476251881Speter err->pool))) 477251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 478251881Speter "%s:%ld", err->file, err->line)); 479251881Speter else 480251881Speter { 481251881Speter svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED, 482251881Speter stream, err->pool)); 483251881Speter svn_error_clear(temp_err); 484251881Speter } 485251881Speter 486251881Speter { 487251881Speter const char *symbolic_name; 488251881Speter if (svn_error__is_tracing_link(err)) 489251881Speter /* Skip it; the error code will be printed by the real link. */ 490251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n")); 491251881Speter else if ((symbolic_name = svn_error_symbolic_name(err->apr_err))) 492251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 493251881Speter ": (apr_err=%s)\n", symbolic_name)); 494251881Speter else 495251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 496251881Speter ": (apr_err=%d)\n", err->apr_err)); 497251881Speter } 498251881Speter#endif /* SVN_DEBUG */ 499251881Speter 500251881Speter /* "traced call" */ 501251881Speter if (svn_error__is_tracing_link(err)) 502251881Speter { 503251881Speter /* Skip it. We already printed the file-line coordinates. */ 504251881Speter } 505251881Speter /* Only print the same APR error string once. */ 506251881Speter else if (err->message) 507251881Speter { 508251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 509251881Speter "%sE%06d: %s\n", 510251881Speter prefix, err->apr_err, err->message)); 511251881Speter } 512251881Speter else 513251881Speter { 514251881Speter /* Is this a Subversion-specific error code? */ 515251881Speter if ((err->apr_err > APR_OS_START_USEERR) 516251881Speter && (err->apr_err <= APR_OS_START_CANONERR)) 517251881Speter err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf)); 518251881Speter /* Otherwise, this must be an APR error code. */ 519251881Speter else if ((temp_err = svn_utf_cstring_to_utf8 520251881Speter (&err_string, apr_strerror(err->apr_err, errbuf, 521251881Speter sizeof(errbuf)), err->pool))) 522251881Speter { 523251881Speter svn_error_clear(temp_err); 524251881Speter err_string = _("Can't recode error string from APR"); 525251881Speter } 526251881Speter 527251881Speter svn_error_clear(svn_cmdline_fprintf(stream, err->pool, 528251881Speter "%sE%06d: %s\n", 529251881Speter prefix, err->apr_err, err_string)); 530251881Speter } 531251881Speter} 532251881Speter 533251881Spetervoid 534251881Spetersvn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal) 535251881Speter{ 536251881Speter svn_handle_error2(err, stream, fatal, "svn: "); 537251881Speter} 538251881Speter 539251881Spetervoid 540251881Spetersvn_handle_error2(svn_error_t *err, 541251881Speter FILE *stream, 542251881Speter svn_boolean_t fatal, 543251881Speter const char *prefix) 544251881Speter{ 545251881Speter /* In a long error chain, there may be multiple errors with the same 546251881Speter error code and no custom message. We only want to print the 547251881Speter default message for that code once; printing it multiple times 548251881Speter would add no useful information. The 'empties' array below 549251881Speter remembers the codes of empty errors already seen in the chain. 550251881Speter 551251881Speter We could allocate it in err->pool, but there's no telling how 552251881Speter long err will live or how many times it will get handled. So we 553251881Speter use a subpool. */ 554251881Speter apr_pool_t *subpool; 555251881Speter apr_array_header_t *empties; 556251881Speter svn_error_t *tmp_err; 557251881Speter 558251881Speter /* ### The rest of this file carefully avoids using svn_pool_*(), 559251881Speter preferring apr_pool_*() instead. I can't remember why -- it may 560251881Speter be an artifact of r843793, or it may be for some deeper reason -- 561251881Speter but I'm playing it safe and using apr_pool_*() here too. */ 562251881Speter apr_pool_create(&subpool, err->pool); 563251881Speter empties = apr_array_make(subpool, 0, sizeof(apr_status_t)); 564251881Speter 565251881Speter tmp_err = err; 566251881Speter while (tmp_err) 567251881Speter { 568251881Speter svn_boolean_t printed_already = FALSE; 569251881Speter 570251881Speter if (! tmp_err->message) 571251881Speter { 572251881Speter int i; 573251881Speter 574251881Speter for (i = 0; i < empties->nelts; i++) 575251881Speter { 576251881Speter if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) ) 577251881Speter { 578251881Speter printed_already = TRUE; 579251881Speter break; 580251881Speter } 581251881Speter } 582251881Speter } 583251881Speter 584251881Speter if (! printed_already) 585251881Speter { 586251881Speter print_error(tmp_err, stream, prefix); 587251881Speter if (! tmp_err->message) 588251881Speter { 589251881Speter APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err; 590251881Speter } 591251881Speter } 592251881Speter 593251881Speter tmp_err = tmp_err->child; 594251881Speter } 595251881Speter 596251881Speter svn_pool_destroy(subpool); 597251881Speter 598251881Speter fflush(stream); 599251881Speter if (fatal) 600251881Speter { 601251881Speter /* Avoid abort()s in maintainer mode. */ 602251881Speter svn_error_clear(err); 603251881Speter 604251881Speter /* We exit(1) here instead of abort()ing so that atexit handlers 605251881Speter get called. */ 606251881Speter exit(EXIT_FAILURE); 607251881Speter } 608251881Speter} 609251881Speter 610251881Speter 611251881Spetervoid 612251881Spetersvn_handle_warning(FILE *stream, svn_error_t *err) 613251881Speter{ 614251881Speter svn_handle_warning2(stream, err, "svn: "); 615251881Speter} 616251881Speter 617251881Spetervoid 618251881Spetersvn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix) 619251881Speter{ 620251881Speter char buf[256]; 621251881Speter 622251881Speter svn_error_clear(svn_cmdline_fprintf 623251881Speter (stream, err->pool, 624251881Speter _("%swarning: W%06d: %s\n"), 625251881Speter prefix, err->apr_err, 626251881Speter svn_err_best_message(err, buf, sizeof(buf)))); 627251881Speter fflush(stream); 628251881Speter} 629251881Speter 630251881Speterconst char * 631251881Spetersvn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize) 632251881Speter{ 633251881Speter /* Skip over any trace records. */ 634251881Speter while (svn_error__is_tracing_link(err)) 635251881Speter err = err->child; 636251881Speter if (err->message) 637251881Speter return err->message; 638251881Speter else 639251881Speter return svn_strerror(err->apr_err, buf, bufsize); 640251881Speter} 641251881Speter 642251881Speter 643251881Speter/* svn_strerror() and helpers */ 644251881Speter 645251881Speter/* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */ 646251881Spetertypedef struct err_defn { 647251881Speter svn_errno_t errcode; /* 160004 */ 648251881Speter const char *errname; /* SVN_ERR_FS_CORRUPT */ 649251881Speter const char *errdesc; /* default message */ 650251881Speter} err_defn; 651251881Speter 652251881Speter/* To understand what is going on here, read svn_error_codes.h. */ 653251881Speter#define SVN_ERROR_BUILD_ARRAY 654251881Speter#include "svn_error_codes.h" 655251881Speter 656251881Speterchar * 657251881Spetersvn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize) 658251881Speter{ 659251881Speter const err_defn *defn; 660251881Speter 661251881Speter for (defn = error_table; defn->errdesc != NULL; ++defn) 662251881Speter if (defn->errcode == (svn_errno_t)statcode) 663251881Speter { 664251881Speter apr_cpystrn(buf, _(defn->errdesc), bufsize); 665251881Speter return buf; 666251881Speter } 667251881Speter 668251881Speter return apr_strerror(statcode, buf, bufsize); 669251881Speter} 670251881Speter 671251881Speterconst char * 672251881Spetersvn_error_symbolic_name(apr_status_t statcode) 673251881Speter{ 674251881Speter const err_defn *defn; 675251881Speter 676251881Speter for (defn = error_table; defn->errdesc != NULL; ++defn) 677251881Speter if (defn->errcode == (svn_errno_t)statcode) 678251881Speter return defn->errname; 679251881Speter 680251881Speter /* "No error" is not in error_table. */ 681251881Speter if (statcode == SVN_NO_ERROR) 682251881Speter return "SVN_NO_ERROR"; 683251881Speter 684251881Speter return NULL; 685251881Speter} 686251881Speter 687251881Speter 688251881Speter 689251881Speter/* Malfunctions. */ 690251881Speter 691251881Spetersvn_error_t * 692251881Spetersvn_error_raise_on_malfunction(svn_boolean_t can_return, 693251881Speter const char *file, int line, 694251881Speter const char *expr) 695251881Speter{ 696251881Speter if (!can_return) 697251881Speter abort(); /* Nothing else we can do as a library */ 698251881Speter 699251881Speter /* The filename and line number of the error source needs to be set 700251881Speter here because svn_error_createf() is not the macro defined in 701251881Speter svn_error.h but the real function. */ 702251881Speter svn_error__locate(file, line); 703251881Speter 704251881Speter if (expr) 705251881Speter return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, 706251881Speter _("In file '%s' line %d: assertion failed (%s)"), 707251881Speter file, line, expr); 708251881Speter else 709251881Speter return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, 710251881Speter _("In file '%s' line %d: internal malfunction"), 711251881Speter file, line); 712251881Speter} 713251881Speter 714251881Spetersvn_error_t * 715251881Spetersvn_error_abort_on_malfunction(svn_boolean_t can_return, 716251881Speter const char *file, int line, 717251881Speter const char *expr) 718251881Speter{ 719251881Speter svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr); 720251881Speter 721251881Speter svn_handle_error2(err, stderr, FALSE, "svn: "); 722251881Speter abort(); 723251881Speter return err; /* Not reached. */ 724251881Speter} 725251881Speter 726251881Speter/* The current handler for reporting malfunctions, and its default setting. */ 727251881Speterstatic svn_error_malfunction_handler_t malfunction_handler 728251881Speter = svn_error_abort_on_malfunction; 729251881Speter 730251881Spetersvn_error_malfunction_handler_t 731251881Spetersvn_error_set_malfunction_handler(svn_error_malfunction_handler_t func) 732251881Speter{ 733251881Speter svn_error_malfunction_handler_t old_malfunction_handler 734251881Speter = malfunction_handler; 735251881Speter 736251881Speter malfunction_handler = func; 737251881Speter return old_malfunction_handler; 738251881Speter} 739251881Speter 740251881Speter/* Note: Although this is a "__" function, it is in the public ABI, so 741251881Speter * we can never remove it or change its signature. */ 742251881Spetersvn_error_t * 743251881Spetersvn_error__malfunction(svn_boolean_t can_return, 744251881Speter const char *file, int line, 745251881Speter const char *expr) 746251881Speter{ 747251881Speter return malfunction_handler(can_return, file, line, expr); 748251881Speter} 749251881Speter 750251881Speter 751251881Speter/* Misc. */ 752251881Speter 753251881Spetersvn_error_t * 754251881Spetersvn_error__wrap_zlib(int zerr, const char *function, const char *message) 755251881Speter{ 756251881Speter apr_status_t status; 757251881Speter const char *zmsg; 758251881Speter 759251881Speter if (zerr == Z_OK) 760251881Speter return SVN_NO_ERROR; 761251881Speter 762251881Speter switch (zerr) 763251881Speter { 764251881Speter case Z_STREAM_ERROR: 765251881Speter status = SVN_ERR_STREAM_MALFORMED_DATA; 766251881Speter zmsg = _("stream error"); 767251881Speter break; 768251881Speter 769251881Speter case Z_MEM_ERROR: 770251881Speter status = APR_ENOMEM; 771251881Speter zmsg = _("out of memory"); 772251881Speter break; 773251881Speter 774251881Speter case Z_BUF_ERROR: 775251881Speter status = APR_ENOMEM; 776251881Speter zmsg = _("buffer error"); 777251881Speter break; 778251881Speter 779251881Speter case Z_VERSION_ERROR: 780251881Speter status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; 781251881Speter zmsg = _("version error"); 782251881Speter break; 783251881Speter 784251881Speter case Z_DATA_ERROR: 785251881Speter status = SVN_ERR_STREAM_MALFORMED_DATA; 786251881Speter zmsg = _("corrupt data"); 787251881Speter break; 788251881Speter 789251881Speter default: 790251881Speter status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; 791251881Speter zmsg = _("unknown error"); 792251881Speter break; 793251881Speter } 794251881Speter 795251881Speter if (message != NULL) 796251881Speter return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function, 797251881Speter zmsg, message); 798251881Speter else 799251881Speter return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg); 800251881Speter} 801