1251881Speter/* error.c:  common exception handling for Subversion
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter
24251881Speter
25251881Speter#include <stdarg.h>
26251881Speter
27251881Speter#include <apr_general.h>
28251881Speter#include <apr_pools.h>
29251881Speter#include <apr_strings.h>
30251881Speter
31251881Speter#include <zlib.h>
32251881Speter
33251881Speter#ifndef SVN_ERR__TRACING
34251881Speter#define SVN_ERR__TRACING
35251881Speter#endif
36251881Speter#include "svn_cmdline.h"
37251881Speter#include "svn_error.h"
38251881Speter#include "svn_pools.h"
39251881Speter#include "svn_utf.h"
40251881Speter
41251881Speter#ifdef SVN_DEBUG
42251881Speter/* XXX FIXME: These should be protected by a thread mutex.
43251881Speter   svn_error__locate and make_error_internal should cooperate
44251881Speter   in locking and unlocking it. */
45251881Speter
46251881Speter/* XXX TODO: Define mutex here #if APR_HAS_THREADS */
47251881Speterstatic const char * volatile error_file = NULL;
48251881Speterstatic long volatile error_line = -1;
49251881Speter
50251881Speter/* file_line for the non-debug case. */
51251881Speterstatic const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
52251881Speter#endif /* SVN_DEBUG */
53251881Speter
54251881Speter#include "svn_private_config.h"
55251881Speter#include "private/svn_error_private.h"
56251881Speter
57251881Speter
58251881Speter/*
59251881Speter * Undefine the helpers for creating errors.
60251881Speter *
61251881Speter * *NOTE*: Any use of these functions in any other function may need
62251881Speter * to call svn_error__locate() because the macro that would otherwise
63251881Speter * do this is being undefined and the filename and line number will
64251881Speter * not be properly set in the static error_file and error_line
65251881Speter * variables.
66251881Speter */
67251881Speter#undef svn_error_create
68251881Speter#undef svn_error_createf
69251881Speter#undef svn_error_quick_wrap
70251881Speter#undef svn_error_wrap_apr
71251881Speter
72251881Speter/* Note: Although this is a "__" function, it was historically in the
73251881Speter * public ABI, so we can never change it or remove its signature, even
74251881Speter * though it is now only used in SVN_DEBUG mode. */
75251881Spetervoid
76251881Spetersvn_error__locate(const char *file, long line)
77251881Speter{
78251881Speter#if defined(SVN_DEBUG)
79251881Speter  /* XXX TODO: Lock mutex here */
80251881Speter  error_file = file;
81251881Speter  error_line = line;
82251881Speter#endif
83251881Speter}
84251881Speter
85251881Speter
86251881Speter/* Cleanup function for errors.  svn_error_clear () removes this so
87251881Speter   errors that are properly handled *don't* hit this code. */
88251881Speter#if defined(SVN_DEBUG)
89251881Speterstatic apr_status_t err_abort(void *data)
90251881Speter{
91251881Speter  svn_error_t *err = data;  /* For easy viewing in a debugger */
92251881Speter  err = err; /* Fake a use for the variable to avoid compiler warnings */
93251881Speter
94251881Speter  if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
95251881Speter    abort();
96251881Speter  return APR_SUCCESS;
97251881Speter}
98251881Speter#endif
99251881Speter
100251881Speter
101251881Speterstatic svn_error_t *
102251881Spetermake_error_internal(apr_status_t apr_err,
103251881Speter                    svn_error_t *child)
104251881Speter{
105251881Speter  apr_pool_t *pool;
106251881Speter  svn_error_t *new_error;
107251881Speter
108251881Speter  /* Reuse the child's pool, or create our own. */
109251881Speter  if (child)
110251881Speter    pool = child->pool;
111251881Speter  else
112251881Speter    {
113251881Speter      if (apr_pool_create(&pool, NULL))
114251881Speter        abort();
115251881Speter    }
116251881Speter
117251881Speter  /* Create the new error structure */
118251881Speter  new_error = apr_pcalloc(pool, sizeof(*new_error));
119251881Speter
120251881Speter  /* Fill 'er up. */
121251881Speter  new_error->apr_err = apr_err;
122251881Speter  new_error->child   = child;
123251881Speter  new_error->pool    = pool;
124251881Speter#if defined(SVN_DEBUG)
125251881Speter  new_error->file    = error_file;
126251881Speter  new_error->line    = error_line;
127251881Speter  /* XXX TODO: Unlock mutex here */
128251881Speter
129251881Speter  if (! child)
130251881Speter      apr_pool_cleanup_register(pool, new_error,
131251881Speter                                err_abort,
132251881Speter                                apr_pool_cleanup_null);
133251881Speter#endif
134251881Speter
135251881Speter  return new_error;
136251881Speter}
137251881Speter
138251881Speter
139251881Speter
140251881Speter/*** Creating and destroying errors. ***/
141251881Speter
142251881Spetersvn_error_t *
143251881Spetersvn_error_create(apr_status_t apr_err,
144251881Speter                 svn_error_t *child,
145251881Speter                 const char *message)
146251881Speter{
147251881Speter  svn_error_t *err;
148251881Speter
149251881Speter  err = make_error_internal(apr_err, child);
150251881Speter
151251881Speter  if (message)
152251881Speter    err->message = apr_pstrdup(err->pool, message);
153251881Speter
154251881Speter  return err;
155251881Speter}
156251881Speter
157251881Speter
158251881Spetersvn_error_t *
159251881Spetersvn_error_createf(apr_status_t apr_err,
160251881Speter                  svn_error_t *child,
161251881Speter                  const char *fmt,
162251881Speter                  ...)
163251881Speter{
164251881Speter  svn_error_t *err;
165251881Speter  va_list ap;
166251881Speter
167251881Speter  err = make_error_internal(apr_err, child);
168251881Speter
169251881Speter  va_start(ap, fmt);
170251881Speter  err->message = apr_pvsprintf(err->pool, fmt, ap);
171251881Speter  va_end(ap);
172251881Speter
173251881Speter  return err;
174251881Speter}
175251881Speter
176251881Speter
177251881Spetersvn_error_t *
178251881Spetersvn_error_wrap_apr(apr_status_t status,
179251881Speter                   const char *fmt,
180251881Speter                   ...)
181251881Speter{
182251881Speter  svn_error_t *err, *utf8_err;
183251881Speter  va_list ap;
184251881Speter  char errbuf[255];
185251881Speter  const char *msg_apr, *msg;
186251881Speter
187251881Speter  err = make_error_internal(status, NULL);
188251881Speter
189251881Speter  if (fmt)
190251881Speter    {
191251881Speter      /* Grab the APR error message. */
192251881Speter      apr_strerror(status, errbuf, sizeof(errbuf));
193251881Speter      utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
194251881Speter      if (utf8_err)
195251881Speter        msg_apr = NULL;
196251881Speter      svn_error_clear(utf8_err);
197251881Speter
198251881Speter      /* Append it to the formatted message. */
199251881Speter      va_start(ap, fmt);
200251881Speter      msg = apr_pvsprintf(err->pool, fmt, ap);
201251881Speter      va_end(ap);
202251881Speter      if (msg_apr)
203251881Speter        {
204251881Speter          err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL);
205251881Speter        }
206251881Speter      else
207251881Speter        {
208251881Speter          err->message = msg;
209251881Speter        }
210251881Speter    }
211251881Speter
212251881Speter  return err;
213251881Speter}
214251881Speter
215251881Speter
216251881Spetersvn_error_t *
217251881Spetersvn_error_quick_wrap(svn_error_t *child, const char *new_msg)
218251881Speter{
219251881Speter  if (child == SVN_NO_ERROR)
220251881Speter    return SVN_NO_ERROR;
221251881Speter
222251881Speter  return svn_error_create(child->apr_err,
223251881Speter                          child,
224251881Speter                          new_msg);
225251881Speter}
226251881Speter
227251881Speter/* Messages in tracing errors all point to this static string. */
228251881Speterstatic const char error_tracing_link[] = "traced call";
229251881Speter
230251881Spetersvn_error_t *
231251881Spetersvn_error__trace(const char *file, long line, svn_error_t *err)
232251881Speter{
233251881Speter#ifndef SVN_DEBUG
234251881Speter
235251881Speter  /* We shouldn't even be here, but whatever. Just return the error as-is.  */
236251881Speter  return err;
237251881Speter
238251881Speter#else
239251881Speter
240251881Speter  /* Only do the work when an error occurs.  */
241251881Speter  if (err)
242251881Speter    {
243251881Speter      svn_error_t *trace;
244251881Speter      svn_error__locate(file, line);
245251881Speter      trace = make_error_internal(err->apr_err, err);
246251881Speter      trace->message = error_tracing_link;
247251881Speter      return trace;
248251881Speter    }
249251881Speter  return SVN_NO_ERROR;
250251881Speter
251251881Speter#endif
252251881Speter}
253251881Speter
254251881Speter
255251881Spetersvn_error_t *
256251881Spetersvn_error_compose_create(svn_error_t *err1,
257251881Speter                         svn_error_t *err2)
258251881Speter{
259251881Speter  if (err1 && err2)
260251881Speter    {
261251881Speter      svn_error_compose(err1,
262251881Speter                        svn_error_quick_wrap(err2,
263251881Speter                                             _("Additional errors:")));
264251881Speter      return err1;
265251881Speter    }
266251881Speter  return err1 ? err1 : err2;
267251881Speter}
268251881Speter
269251881Speter
270251881Spetervoid
271251881Spetersvn_error_compose(svn_error_t *chain, svn_error_t *new_err)
272251881Speter{
273251881Speter  apr_pool_t *pool = chain->pool;
274251881Speter  apr_pool_t *oldpool = new_err->pool;
275251881Speter
276251881Speter  while (chain->child)
277251881Speter    chain = chain->child;
278251881Speter
279251881Speter#if defined(SVN_DEBUG)
280251881Speter  /* Kill existing handler since the end of the chain is going to change */
281251881Speter  apr_pool_cleanup_kill(pool, chain, err_abort);
282251881Speter#endif
283251881Speter
284251881Speter  /* Copy the new error chain into the old chain's pool. */
285251881Speter  while (new_err)
286251881Speter    {
287251881Speter      chain->child = apr_palloc(pool, sizeof(*chain->child));
288251881Speter      chain = chain->child;
289251881Speter      *chain = *new_err;
290251881Speter      if (chain->message)
291251881Speter        chain->message = apr_pstrdup(pool, new_err->message);
292251881Speter      chain->pool = pool;
293251881Speter#if defined(SVN_DEBUG)
294251881Speter      if (! new_err->child)
295251881Speter        apr_pool_cleanup_kill(oldpool, new_err, err_abort);
296251881Speter#endif
297251881Speter      new_err = new_err->child;
298251881Speter    }
299251881Speter
300251881Speter#if defined(SVN_DEBUG)
301251881Speter  apr_pool_cleanup_register(pool, chain,
302251881Speter                            err_abort,
303251881Speter                            apr_pool_cleanup_null);
304251881Speter#endif
305251881Speter
306251881Speter  /* Destroy the new error chain. */
307251881Speter  svn_pool_destroy(oldpool);
308251881Speter}
309251881Speter
310251881Spetersvn_error_t *
311251881Spetersvn_error_root_cause(svn_error_t *err)
312251881Speter{
313251881Speter  while (err)
314251881Speter    {
315251881Speter      if (err->child)
316251881Speter        err = err->child;
317251881Speter      else
318251881Speter        break;
319251881Speter    }
320251881Speter
321251881Speter  return err;
322251881Speter}
323251881Speter
324251881Spetersvn_error_t *
325251881Spetersvn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
326251881Speter{
327251881Speter  svn_error_t *child;
328251881Speter
329251881Speter  for (child = err; child; child = child->child)
330251881Speter    if (child->apr_err == apr_err)
331251881Speter      return child;
332251881Speter
333251881Speter  return SVN_NO_ERROR;
334251881Speter}
335251881Speter
336251881Spetersvn_error_t *
337251881Spetersvn_error_dup(svn_error_t *err)
338251881Speter{
339251881Speter  apr_pool_t *pool;
340251881Speter  svn_error_t *new_err = NULL, *tmp_err = NULL;
341251881Speter
342251881Speter  if (apr_pool_create(&pool, NULL))
343251881Speter    abort();
344251881Speter
345251881Speter  for (; err; err = err->child)
346251881Speter    {
347251881Speter      if (! new_err)
348251881Speter        {
349251881Speter          new_err = apr_palloc(pool, sizeof(*new_err));
350251881Speter          tmp_err = new_err;
351251881Speter        }
352251881Speter      else
353251881Speter        {
354251881Speter          tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
355251881Speter          tmp_err = tmp_err->child;
356251881Speter        }
357251881Speter      *tmp_err = *err;
358251881Speter      tmp_err->pool = pool;
359251881Speter      if (tmp_err->message)
360251881Speter        tmp_err->message = apr_pstrdup(pool, tmp_err->message);
361251881Speter    }
362251881Speter
363251881Speter#if defined(SVN_DEBUG)
364251881Speter  apr_pool_cleanup_register(pool, tmp_err,
365251881Speter                            err_abort,
366251881Speter                            apr_pool_cleanup_null);
367251881Speter#endif
368251881Speter
369251881Speter  return new_err;
370251881Speter}
371251881Speter
372251881Spetervoid
373251881Spetersvn_error_clear(svn_error_t *err)
374251881Speter{
375251881Speter  if (err)
376251881Speter    {
377251881Speter#if defined(SVN_DEBUG)
378251881Speter      while (err->child)
379251881Speter        err = err->child;
380251881Speter      apr_pool_cleanup_kill(err->pool, err, err_abort);
381251881Speter#endif
382251881Speter      svn_pool_destroy(err->pool);
383251881Speter    }
384251881Speter}
385251881Speter
386251881Spetersvn_boolean_t
387251881Spetersvn_error__is_tracing_link(svn_error_t *err)
388251881Speter{
389251881Speter#ifdef SVN_ERR__TRACING
390251881Speter  /* ### A strcmp()?  Really?  I think it's the best we can do unless
391251881Speter     ### we add a boolean field to svn_error_t that's set only for
392251881Speter     ### these "placeholder error chain" items.  Not such a bad idea,
393251881Speter     ### really...  */
394251881Speter  return (err && err->message && !strcmp(err->message, error_tracing_link));
395251881Speter#else
396251881Speter  return FALSE;
397251881Speter#endif
398251881Speter}
399251881Speter
400251881Spetersvn_error_t *
401251881Spetersvn_error_purge_tracing(svn_error_t *err)
402251881Speter{
403251881Speter#ifdef SVN_ERR__TRACING
404251881Speter  svn_error_t *new_err = NULL, *new_err_leaf = NULL;
405251881Speter
406251881Speter  if (! err)
407251881Speter    return SVN_NO_ERROR;
408251881Speter
409251881Speter  do
410251881Speter    {
411251881Speter      svn_error_t *tmp_err;
412251881Speter
413251881Speter      /* Skip over any trace-only links. */
414251881Speter      while (err && svn_error__is_tracing_link(err))
415251881Speter        err = err->child;
416251881Speter
417251881Speter      /* The link must be a real link in the error chain, otherwise an
418251881Speter         error chain with trace only links would map into SVN_NO_ERROR. */
419251881Speter      if (! err)
420251881Speter        return svn_error_create(
421251881Speter                 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
422251881Speter                 svn_error_compose_create(
423251881Speter                   svn_error__malfunction(TRUE, __FILE__, __LINE__,
424251881Speter                                          NULL /* ### say something? */),
425251881Speter                   err),
426251881Speter                 NULL);
427251881Speter
428251881Speter      /* Copy the current error except for its child error pointer
429251881Speter         into the new error.  Share any message and source filename
430251881Speter         strings from the error. */
431251881Speter      tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
432251881Speter      *tmp_err = *err;
433251881Speter      tmp_err->child = NULL;
434251881Speter
435251881Speter      /* Add a new link to the new chain (creating the chain if necessary). */
436251881Speter      if (! new_err)
437251881Speter        {
438251881Speter          new_err = tmp_err;
439251881Speter          new_err_leaf = tmp_err;
440251881Speter        }
441251881Speter      else
442251881Speter        {
443251881Speter          new_err_leaf->child = tmp_err;
444251881Speter          new_err_leaf = tmp_err;
445251881Speter        }
446251881Speter
447251881Speter      /* Advance to the next link in the original chain. */
448251881Speter      err = err->child;
449251881Speter    } while (err);
450251881Speter
451251881Speter  return new_err;
452251881Speter#else  /* SVN_ERR__TRACING */
453251881Speter  return err;
454251881Speter#endif /* SVN_ERR__TRACING */
455251881Speter}
456251881Speter
457251881Speter/* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
458251881Speter   ### coupled to the current sole caller.*/
459251881Speterstatic void
460251881Speterprint_error(svn_error_t *err, FILE *stream, const char *prefix)
461251881Speter{
462251881Speter  char errbuf[256];
463251881Speter  const char *err_string;
464251881Speter  svn_error_t *temp_err = NULL;  /* ensure initialized even if
465251881Speter                                    err->file == NULL */
466251881Speter  /* Pretty-print the error */
467251881Speter  /* Note: we can also log errors here someday. */
468251881Speter
469251881Speter#ifdef SVN_DEBUG
470251881Speter  /* Note: err->file is _not_ in UTF-8, because it's expanded from
471251881Speter           the __FILE__ preprocessor macro. */
472251881Speter  const char *file_utf8;
473251881Speter
474251881Speter  if (err->file
475251881Speter      && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
476251881Speter                                              err->pool)))
477251881Speter    svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
478251881Speter                                        "%s:%ld", err->file, err->line));
479251881Speter  else
480251881Speter    {
481251881Speter      svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
482251881Speter                                        stream, err->pool));
483251881Speter      svn_error_clear(temp_err);
484251881Speter    }
485251881Speter
486251881Speter  {
487251881Speter    const char *symbolic_name;
488251881Speter    if (svn_error__is_tracing_link(err))
489251881Speter      /* Skip it; the error code will be printed by the real link. */
490251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
491251881Speter    else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
492251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
493251881Speter                                          ": (apr_err=%s)\n", symbolic_name));
494251881Speter    else
495251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
496251881Speter                                          ": (apr_err=%d)\n", err->apr_err));
497251881Speter  }
498251881Speter#endif /* SVN_DEBUG */
499251881Speter
500251881Speter  /* "traced call" */
501251881Speter  if (svn_error__is_tracing_link(err))
502251881Speter    {
503251881Speter      /* Skip it.  We already printed the file-line coordinates. */
504251881Speter    }
505251881Speter  /* Only print the same APR error string once. */
506251881Speter  else if (err->message)
507251881Speter    {
508251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
509251881Speter                                          "%sE%06d: %s\n",
510251881Speter                                          prefix, err->apr_err, err->message));
511251881Speter    }
512251881Speter  else
513251881Speter    {
514251881Speter      /* Is this a Subversion-specific error code? */
515251881Speter      if ((err->apr_err > APR_OS_START_USEERR)
516251881Speter          && (err->apr_err <= APR_OS_START_CANONERR))
517251881Speter        err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
518251881Speter      /* Otherwise, this must be an APR error code. */
519251881Speter      else if ((temp_err = svn_utf_cstring_to_utf8
520251881Speter                (&err_string, apr_strerror(err->apr_err, errbuf,
521251881Speter                                           sizeof(errbuf)), err->pool)))
522251881Speter        {
523251881Speter          svn_error_clear(temp_err);
524251881Speter          err_string = _("Can't recode error string from APR");
525251881Speter        }
526251881Speter
527251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
528251881Speter                                          "%sE%06d: %s\n",
529251881Speter                                          prefix, err->apr_err, err_string));
530251881Speter    }
531251881Speter}
532251881Speter
533251881Spetervoid
534251881Spetersvn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
535251881Speter{
536251881Speter  svn_handle_error2(err, stream, fatal, "svn: ");
537251881Speter}
538251881Speter
539251881Spetervoid
540251881Spetersvn_handle_error2(svn_error_t *err,
541251881Speter                  FILE *stream,
542251881Speter                  svn_boolean_t fatal,
543251881Speter                  const char *prefix)
544251881Speter{
545251881Speter  /* In a long error chain, there may be multiple errors with the same
546251881Speter     error code and no custom message.  We only want to print the
547251881Speter     default message for that code once; printing it multiple times
548251881Speter     would add no useful information.  The 'empties' array below
549251881Speter     remembers the codes of empty errors already seen in the chain.
550251881Speter
551251881Speter     We could allocate it in err->pool, but there's no telling how
552251881Speter     long err will live or how many times it will get handled.  So we
553251881Speter     use a subpool. */
554251881Speter  apr_pool_t *subpool;
555251881Speter  apr_array_header_t *empties;
556251881Speter  svn_error_t *tmp_err;
557251881Speter
558251881Speter  /* ### The rest of this file carefully avoids using svn_pool_*(),
559251881Speter     preferring apr_pool_*() instead.  I can't remember why -- it may
560251881Speter     be an artifact of r843793, or it may be for some deeper reason --
561251881Speter     but I'm playing it safe and using apr_pool_*() here too. */
562251881Speter  apr_pool_create(&subpool, err->pool);
563251881Speter  empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
564251881Speter
565251881Speter  tmp_err = err;
566251881Speter  while (tmp_err)
567251881Speter    {
568251881Speter      svn_boolean_t printed_already = FALSE;
569251881Speter
570251881Speter      if (! tmp_err->message)
571251881Speter        {
572251881Speter          int i;
573251881Speter
574251881Speter          for (i = 0; i < empties->nelts; i++)
575251881Speter            {
576251881Speter              if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
577251881Speter                {
578251881Speter                  printed_already = TRUE;
579251881Speter                  break;
580251881Speter                }
581251881Speter            }
582251881Speter        }
583251881Speter
584251881Speter      if (! printed_already)
585251881Speter        {
586251881Speter          print_error(tmp_err, stream, prefix);
587251881Speter          if (! tmp_err->message)
588251881Speter            {
589251881Speter              APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
590251881Speter            }
591251881Speter        }
592251881Speter
593251881Speter      tmp_err = tmp_err->child;
594251881Speter    }
595251881Speter
596251881Speter  svn_pool_destroy(subpool);
597251881Speter
598251881Speter  fflush(stream);
599251881Speter  if (fatal)
600251881Speter    {
601251881Speter      /* Avoid abort()s in maintainer mode. */
602251881Speter      svn_error_clear(err);
603251881Speter
604251881Speter      /* We exit(1) here instead of abort()ing so that atexit handlers
605251881Speter         get called. */
606251881Speter      exit(EXIT_FAILURE);
607251881Speter    }
608251881Speter}
609251881Speter
610251881Speter
611251881Spetervoid
612251881Spetersvn_handle_warning(FILE *stream, svn_error_t *err)
613251881Speter{
614251881Speter  svn_handle_warning2(stream, err, "svn: ");
615251881Speter}
616251881Speter
617251881Spetervoid
618251881Spetersvn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
619251881Speter{
620251881Speter  char buf[256];
621251881Speter
622251881Speter  svn_error_clear(svn_cmdline_fprintf
623251881Speter                  (stream, err->pool,
624251881Speter                   _("%swarning: W%06d: %s\n"),
625251881Speter                   prefix, err->apr_err,
626251881Speter                   svn_err_best_message(err, buf, sizeof(buf))));
627251881Speter  fflush(stream);
628251881Speter}
629251881Speter
630251881Speterconst char *
631251881Spetersvn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
632251881Speter{
633251881Speter  /* Skip over any trace records.  */
634251881Speter  while (svn_error__is_tracing_link(err))
635251881Speter    err = err->child;
636251881Speter  if (err->message)
637251881Speter    return err->message;
638251881Speter  else
639251881Speter    return svn_strerror(err->apr_err, buf, bufsize);
640251881Speter}
641251881Speter
642251881Speter
643251881Speter/* svn_strerror() and helpers */
644251881Speter
645251881Speter/* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
646251881Spetertypedef struct err_defn {
647251881Speter  svn_errno_t errcode; /* 160004 */
648251881Speter  const char *errname; /* SVN_ERR_FS_CORRUPT */
649251881Speter  const char *errdesc; /* default message */
650251881Speter} err_defn;
651251881Speter
652251881Speter/* To understand what is going on here, read svn_error_codes.h. */
653251881Speter#define SVN_ERROR_BUILD_ARRAY
654251881Speter#include "svn_error_codes.h"
655251881Speter
656251881Speterchar *
657251881Spetersvn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
658251881Speter{
659251881Speter  const err_defn *defn;
660251881Speter
661251881Speter  for (defn = error_table; defn->errdesc != NULL; ++defn)
662251881Speter    if (defn->errcode == (svn_errno_t)statcode)
663251881Speter      {
664251881Speter        apr_cpystrn(buf, _(defn->errdesc), bufsize);
665251881Speter        return buf;
666251881Speter      }
667251881Speter
668251881Speter  return apr_strerror(statcode, buf, bufsize);
669251881Speter}
670251881Speter
671251881Speterconst char *
672251881Spetersvn_error_symbolic_name(apr_status_t statcode)
673251881Speter{
674251881Speter  const err_defn *defn;
675251881Speter
676251881Speter  for (defn = error_table; defn->errdesc != NULL; ++defn)
677251881Speter    if (defn->errcode == (svn_errno_t)statcode)
678251881Speter      return defn->errname;
679251881Speter
680251881Speter  /* "No error" is not in error_table. */
681251881Speter  if (statcode == SVN_NO_ERROR)
682251881Speter    return "SVN_NO_ERROR";
683251881Speter
684251881Speter  return NULL;
685251881Speter}
686251881Speter
687251881Speter
688251881Speter
689251881Speter/* Malfunctions. */
690251881Speter
691251881Spetersvn_error_t *
692251881Spetersvn_error_raise_on_malfunction(svn_boolean_t can_return,
693251881Speter                               const char *file, int line,
694251881Speter                               const char *expr)
695251881Speter{
696251881Speter  if (!can_return)
697251881Speter    abort(); /* Nothing else we can do as a library */
698251881Speter
699251881Speter  /* The filename and line number of the error source needs to be set
700251881Speter     here because svn_error_createf() is not the macro defined in
701251881Speter     svn_error.h but the real function. */
702251881Speter  svn_error__locate(file, line);
703251881Speter
704251881Speter  if (expr)
705251881Speter    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
706251881Speter                             _("In file '%s' line %d: assertion failed (%s)"),
707251881Speter                             file, line, expr);
708251881Speter  else
709251881Speter    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
710251881Speter                             _("In file '%s' line %d: internal malfunction"),
711251881Speter                             file, line);
712251881Speter}
713251881Speter
714251881Spetersvn_error_t *
715251881Spetersvn_error_abort_on_malfunction(svn_boolean_t can_return,
716251881Speter                               const char *file, int line,
717251881Speter                               const char *expr)
718251881Speter{
719251881Speter  svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
720251881Speter
721251881Speter  svn_handle_error2(err, stderr, FALSE, "svn: ");
722251881Speter  abort();
723251881Speter  return err;  /* Not reached. */
724251881Speter}
725251881Speter
726251881Speter/* The current handler for reporting malfunctions, and its default setting. */
727251881Speterstatic svn_error_malfunction_handler_t malfunction_handler
728251881Speter  = svn_error_abort_on_malfunction;
729251881Speter
730251881Spetersvn_error_malfunction_handler_t
731251881Spetersvn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
732251881Speter{
733251881Speter  svn_error_malfunction_handler_t old_malfunction_handler
734251881Speter    = malfunction_handler;
735251881Speter
736251881Speter  malfunction_handler = func;
737251881Speter  return old_malfunction_handler;
738251881Speter}
739251881Speter
740251881Speter/* Note: Although this is a "__" function, it is in the public ABI, so
741251881Speter * we can never remove it or change its signature. */
742251881Spetersvn_error_t *
743251881Spetersvn_error__malfunction(svn_boolean_t can_return,
744251881Speter                       const char *file, int line,
745251881Speter                       const char *expr)
746251881Speter{
747251881Speter  return malfunction_handler(can_return, file, line, expr);
748251881Speter}
749251881Speter
750251881Speter
751251881Speter/* Misc. */
752251881Speter
753251881Spetersvn_error_t *
754251881Spetersvn_error__wrap_zlib(int zerr, const char *function, const char *message)
755251881Speter{
756251881Speter  apr_status_t status;
757251881Speter  const char *zmsg;
758251881Speter
759251881Speter  if (zerr == Z_OK)
760251881Speter    return SVN_NO_ERROR;
761251881Speter
762251881Speter  switch (zerr)
763251881Speter    {
764251881Speter    case Z_STREAM_ERROR:
765251881Speter      status = SVN_ERR_STREAM_MALFORMED_DATA;
766251881Speter      zmsg = _("stream error");
767251881Speter      break;
768251881Speter
769251881Speter    case Z_MEM_ERROR:
770251881Speter      status = APR_ENOMEM;
771251881Speter      zmsg = _("out of memory");
772251881Speter      break;
773251881Speter
774251881Speter    case Z_BUF_ERROR:
775251881Speter      status = APR_ENOMEM;
776251881Speter      zmsg = _("buffer error");
777251881Speter      break;
778251881Speter
779251881Speter    case Z_VERSION_ERROR:
780251881Speter      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
781251881Speter      zmsg = _("version error");
782251881Speter      break;
783251881Speter
784251881Speter    case Z_DATA_ERROR:
785251881Speter      status = SVN_ERR_STREAM_MALFORMED_DATA;
786251881Speter      zmsg = _("corrupt data");
787251881Speter      break;
788251881Speter
789251881Speter    default:
790251881Speter      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
791251881Speter      zmsg = _("unknown error");
792251881Speter      break;
793251881Speter    }
794251881Speter
795251881Speter  if (message != NULL)
796251881Speter    return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
797251881Speter                             zmsg, message);
798251881Speter  else
799251881Speter    return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);
800251881Speter}
801