]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/error.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_subr / error.c
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 \f
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 \f
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
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. */
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 *
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 \f
140 /*** Creating and destroying errors. ***/
141
142 svn_error_t *
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 *
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 *
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 *
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 *
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 *
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 *
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
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 *
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 *
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 *
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
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
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 *
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
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
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
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 *
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 \f
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 *
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 *
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 \f
735 /* Malfunctions. */
736
737 svn_error_t *
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 *
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
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
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 *
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 \f
803 /* Misc. */
804
805 svn_error_t *
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 }