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 */
47 static const char * volatile error_file = NULL;
48 static long volatile error_line = -1;
49
50 /* file_line for the non-debug case. */
51 static 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. */
76 void
svn_error__locate(const char * file,long line)77 svn_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. */
err_abort(void * data)89 static 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
100 static svn_error_t *
make_error_internal(apr_status_t apr_err,svn_error_t * child)101 make_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
142 svn_error_t *
svn_error_create(apr_status_t apr_err,svn_error_t * child,const char * message)143 svn_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
158 svn_error_t *
svn_error_createf(apr_status_t apr_err,svn_error_t * child,const char * fmt,...)159 svn_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
177 svn_error_t *
svn_error_wrap_apr(apr_status_t status,const char * fmt,...)178 svn_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
217 svn_error_t *
svn_error_quick_wrap(svn_error_t * child,const char * new_msg)218 svn_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
228 svn_error_t *
svn_error_quick_wrapf(svn_error_t * child,const char * fmt,...)229 svn_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. */
249 static const char error_tracing_link[] = "traced call";
250
251 svn_error_t *
svn_error__trace(const char * file,long line,svn_error_t * err)252 svn_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
276 svn_error_t *
svn_error_compose_create(svn_error_t * err1,svn_error_t * err2)277 svn_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
290 void
svn_error_compose(svn_error_t * chain,svn_error_t * new_err)291 svn_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
332 svn_error_t *
svn_error_root_cause(svn_error_t * err)333 svn_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
349 svn_error_t *
svn_error_find_cause(svn_error_t * err,apr_status_t apr_err)350 svn_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
361 svn_error_t *
svn_error_dup(const svn_error_t * err)362 svn_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
403 void
svn_error_clear(svn_error_t * err)404 svn_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
417 svn_boolean_t
svn_error__is_tracing_link(const svn_error_t * err)418 svn_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
431 svn_error_t *
svn_error_purge_tracing(svn_error_t * err)432 svn_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.*/
488 static void
print_error(svn_error_t * err,FILE * stream,const char * prefix)489 print_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
562 void
svn_handle_error2(svn_error_t * err,FILE * stream,svn_boolean_t fatal,const char * prefix)563 svn_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
629 void
svn_handle_warning2(FILE * stream,const svn_error_t * err,const char * prefix)630 svn_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
652 const char *
svn_err_best_message(const svn_error_t * err,char * buf,apr_size_t bufsize)653 svn_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 */
668 typedef 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
678 char *
svn_strerror(apr_status_t statcode,char * buf,apr_size_t bufsize)679 svn_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
698 const char *
svn_error_symbolic_name(apr_status_t statcode)699 svn_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
737 svn_error_t *
svn_error_raise_on_malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)738 svn_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
760 svn_error_t *
svn_error_abort_on_malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)761 svn_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. */
773 static svn_error_malfunction_handler_t malfunction_handler
774 = svn_error_abort_on_malfunction;
775
776 svn_error_malfunction_handler_t
svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)777 svn_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
786 svn_error_malfunction_handler_t
svn_error_get_malfunction_handler(void)787 svn_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. */
794 svn_error_t *
svn_error__malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)795 svn_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
805 svn_error_t *
svn_error__wrap_zlib(int zerr,const char * function,const char * message)806 svn_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