xref: /freebsd-11-stable/contrib/subversion/subversion/libsvn_subr/error.c (revision 3c9339f7792540596bf97077a8f403e944af7f39)
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
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(). */
null_threadkey_dtor(void * stuff)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 *
locate_init_once(void * ignored_baton)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 
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
svn_error__locate(const char * file,long line)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 #ifdef SVN_DEBUG
err_abort(void * data)150 static 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 
162 static svn_error_t *
make_error_internal(apr_status_t apr_err,svn_error_t * child)163 make_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 
225 svn_error_t *
svn_error_create(apr_status_t apr_err,svn_error_t * child,const char * message)226 svn_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 
241 svn_error_t *
svn_error_createf(apr_status_t apr_err,svn_error_t * child,const char * fmt,...)242 svn_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 
260 svn_error_t *
svn_error_wrap_apr(apr_status_t status,const char * fmt,...)261 svn_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 
300 svn_error_t *
svn_error_quick_wrap(svn_error_t * child,const char * new_msg)301 svn_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 
311 svn_error_t *
svn_error_quick_wrapf(svn_error_t * child,const char * fmt,...)312 svn_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. */
332 static const char error_tracing_link[] = "traced call";
333 
334 svn_error_t *
svn_error__trace(const char * file,long line,svn_error_t * err)335 svn_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 
359 svn_error_t *
svn_error_compose_create(svn_error_t * err1,svn_error_t * err2)360 svn_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 
373 void
svn_error_compose(svn_error_t * chain,svn_error_t * new_err)374 svn_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 
415 svn_error_t *
svn_error_root_cause(svn_error_t * err)416 svn_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 
432 svn_error_t *
svn_error_find_cause(svn_error_t * err,apr_status_t apr_err)433 svn_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 
444 svn_error_t *
svn_error_dup(const svn_error_t * err)445 svn_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 
486 void
svn_error_clear(svn_error_t * err)487 svn_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 
500 svn_boolean_t
svn_error__is_tracing_link(const svn_error_t * err)501 svn_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 
514 svn_error_t *
svn_error_purge_tracing(svn_error_t * err)515 svn_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.*/
571 static void
print_error(svn_error_t * err,FILE * stream,const char * prefix)572 print_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 
645 void
svn_handle_error2(svn_error_t * err,FILE * stream,svn_boolean_t fatal,const char * prefix)646 svn_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 
712 void
svn_handle_warning2(FILE * stream,const svn_error_t * err,const char * prefix)713 svn_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 
735 const char *
svn_err_best_message(const svn_error_t * err,char * buf,apr_size_t bufsize)736 svn_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 */
751 typedef 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 
761 char *
svn_strerror(apr_status_t statcode,char * buf,apr_size_t bufsize)762 svn_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 
781 const char *
svn_error_symbolic_name(apr_status_t statcode)782 svn_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 
820 svn_error_t *
svn_error_raise_on_malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)821 svn_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 
843 svn_error_t *
svn_error_abort_on_malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)844 svn_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. */
856 static svn_error_malfunction_handler_t malfunction_handler
857   = svn_error_abort_on_malfunction;
858 
859 svn_error_malfunction_handler_t
svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)860 svn_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 
869 svn_error_malfunction_handler_t
svn_error_get_malfunction_handler(void)870 svn_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. */
877 svn_error_t *
svn_error__malfunction(svn_boolean_t can_return,const char * file,int line,const char * expr)878 svn_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 
888 svn_error_t *
svn_error__wrap_zlib(int zerr,const char * function,const char * message)889 svn_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