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