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