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
70299742Sdim#undef svn_error_quick_wrapf
71251881Speter#undef svn_error_wrap_apr
72251881Speter
73251881Speter/* Note: Although this is a "__" function, it was historically in the
74251881Speter * public ABI, so we can never change it or remove its signature, even
75251881Speter * though it is now only used in SVN_DEBUG mode. */
76251881Spetervoid
77251881Spetersvn_error__locate(const char *file, long line)
78251881Speter{
79251881Speter#if defined(SVN_DEBUG)
80251881Speter  /* XXX TODO: Lock mutex here */
81251881Speter  error_file = file;
82251881Speter  error_line = line;
83251881Speter#endif
84251881Speter}
85251881Speter
86251881Speter
87251881Speter/* Cleanup function for errors.  svn_error_clear () removes this so
88251881Speter   errors that are properly handled *don't* hit this code. */
89251881Speterstatic apr_status_t err_abort(void *data)
90251881Speter{
91251881Speter  svn_error_t *err = data;  /* For easy viewing in a debugger */
92299742Sdim  SVN_UNUSED(err);
93251881Speter
94251881Speter  if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
95251881Speter    abort();
96251881Speter  return APR_SUCCESS;
97251881Speter}
98251881Speter
99251881Speter
100251881Speterstatic svn_error_t *
101251881Spetermake_error_internal(apr_status_t apr_err,
102251881Speter                    svn_error_t *child)
103251881Speter{
104251881Speter  apr_pool_t *pool;
105251881Speter  svn_error_t *new_error;
106251881Speter
107251881Speter  /* Reuse the child's pool, or create our own. */
108251881Speter  if (child)
109251881Speter    pool = child->pool;
110251881Speter  else
111251881Speter    {
112299742Sdim      pool = svn_pool_create(NULL);
113299742Sdim      if (!pool)
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        {
204299742Sdim          err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr,
205299742Sdim                                     SVN_VA_NULL);
206251881Speter        }
207251881Speter      else
208251881Speter        {
209251881Speter          err->message = msg;
210251881Speter        }
211251881Speter    }
212251881Speter
213251881Speter  return err;
214251881Speter}
215251881Speter
216251881Speter
217251881Spetersvn_error_t *
218251881Spetersvn_error_quick_wrap(svn_error_t *child, const char *new_msg)
219251881Speter{
220251881Speter  if (child == SVN_NO_ERROR)
221251881Speter    return SVN_NO_ERROR;
222251881Speter
223251881Speter  return svn_error_create(child->apr_err,
224251881Speter                          child,
225251881Speter                          new_msg);
226251881Speter}
227251881Speter
228299742Sdimsvn_error_t *
229299742Sdimsvn_error_quick_wrapf(svn_error_t *child,
230299742Sdim                      const char *fmt,
231299742Sdim                      ...)
232299742Sdim{
233299742Sdim  svn_error_t *err;
234299742Sdim  va_list ap;
235299742Sdim
236299742Sdim  if (child == SVN_NO_ERROR)
237299742Sdim    return SVN_NO_ERROR;
238299742Sdim
239299742Sdim  err = make_error_internal(child->apr_err, child);
240299742Sdim
241299742Sdim  va_start(ap, fmt);
242299742Sdim  err->message = apr_pvsprintf(err->pool, fmt, ap);
243299742Sdim  va_end(ap);
244299742Sdim
245299742Sdim  return err;
246299742Sdim}
247299742Sdim
248251881Speter/* Messages in tracing errors all point to this static string. */
249251881Speterstatic const char error_tracing_link[] = "traced call";
250251881Speter
251251881Spetersvn_error_t *
252251881Spetersvn_error__trace(const char *file, long line, svn_error_t *err)
253251881Speter{
254251881Speter#ifndef SVN_DEBUG
255251881Speter
256251881Speter  /* We shouldn't even be here, but whatever. Just return the error as-is.  */
257251881Speter  return err;
258251881Speter
259251881Speter#else
260251881Speter
261251881Speter  /* Only do the work when an error occurs.  */
262251881Speter  if (err)
263251881Speter    {
264251881Speter      svn_error_t *trace;
265251881Speter      svn_error__locate(file, line);
266251881Speter      trace = make_error_internal(err->apr_err, err);
267251881Speter      trace->message = error_tracing_link;
268251881Speter      return trace;
269251881Speter    }
270251881Speter  return SVN_NO_ERROR;
271251881Speter
272251881Speter#endif
273251881Speter}
274251881Speter
275251881Speter
276251881Spetersvn_error_t *
277251881Spetersvn_error_compose_create(svn_error_t *err1,
278251881Speter                         svn_error_t *err2)
279251881Speter{
280251881Speter  if (err1 && err2)
281251881Speter    {
282251881Speter      svn_error_compose(err1,
283299742Sdim                        svn_error_create(SVN_ERR_COMPOSED_ERROR, err2, NULL));
284251881Speter      return err1;
285251881Speter    }
286251881Speter  return err1 ? err1 : err2;
287251881Speter}
288251881Speter
289251881Speter
290251881Spetervoid
291251881Spetersvn_error_compose(svn_error_t *chain, svn_error_t *new_err)
292251881Speter{
293251881Speter  apr_pool_t *pool = chain->pool;
294251881Speter  apr_pool_t *oldpool = new_err->pool;
295251881Speter
296251881Speter  while (chain->child)
297251881Speter    chain = chain->child;
298251881Speter
299251881Speter#if defined(SVN_DEBUG)
300251881Speter  /* Kill existing handler since the end of the chain is going to change */
301251881Speter  apr_pool_cleanup_kill(pool, chain, err_abort);
302251881Speter#endif
303251881Speter
304251881Speter  /* Copy the new error chain into the old chain's pool. */
305251881Speter  while (new_err)
306251881Speter    {
307251881Speter      chain->child = apr_palloc(pool, sizeof(*chain->child));
308251881Speter      chain = chain->child;
309251881Speter      *chain = *new_err;
310251881Speter      if (chain->message)
311251881Speter        chain->message = apr_pstrdup(pool, new_err->message);
312289166Speter      if (chain->file)
313289166Speter        chain->file = apr_pstrdup(pool, new_err->file);
314251881Speter      chain->pool = pool;
315251881Speter#if defined(SVN_DEBUG)
316251881Speter      if (! new_err->child)
317251881Speter        apr_pool_cleanup_kill(oldpool, new_err, err_abort);
318251881Speter#endif
319251881Speter      new_err = new_err->child;
320251881Speter    }
321251881Speter
322251881Speter#if defined(SVN_DEBUG)
323251881Speter  apr_pool_cleanup_register(pool, chain,
324251881Speter                            err_abort,
325251881Speter                            apr_pool_cleanup_null);
326251881Speter#endif
327251881Speter
328251881Speter  /* Destroy the new error chain. */
329251881Speter  svn_pool_destroy(oldpool);
330251881Speter}
331251881Speter
332251881Spetersvn_error_t *
333251881Spetersvn_error_root_cause(svn_error_t *err)
334251881Speter{
335251881Speter  while (err)
336251881Speter    {
337299742Sdim      /* I don't think we can change the behavior here, but the additional
338299742Sdim         error chain doesn't define the root cause. Perhaps we should rev
339299742Sdim         this function. */
340299742Sdim      if (err->child /*&& err->child->apr_err != SVN_ERR_COMPOSED_ERROR*/)
341251881Speter        err = err->child;
342251881Speter      else
343251881Speter        break;
344251881Speter    }
345251881Speter
346251881Speter  return err;
347251881Speter}
348251881Speter
349251881Spetersvn_error_t *
350251881Spetersvn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
351251881Speter{
352251881Speter  svn_error_t *child;
353251881Speter
354251881Speter  for (child = err; child; child = child->child)
355251881Speter    if (child->apr_err == apr_err)
356251881Speter      return child;
357251881Speter
358251881Speter  return SVN_NO_ERROR;
359251881Speter}
360251881Speter
361251881Spetersvn_error_t *
362299742Sdimsvn_error_dup(const svn_error_t *err)
363251881Speter{
364251881Speter  apr_pool_t *pool;
365251881Speter  svn_error_t *new_err = NULL, *tmp_err = NULL;
366251881Speter
367299742Sdim  if (!err)
368299742Sdim    return SVN_NO_ERROR;
369299742Sdim
370299742Sdim  pool = svn_pool_create(NULL);
371299742Sdim  if (!pool)
372251881Speter    abort();
373251881Speter
374251881Speter  for (; err; err = err->child)
375251881Speter    {
376251881Speter      if (! new_err)
377251881Speter        {
378251881Speter          new_err = apr_palloc(pool, sizeof(*new_err));
379251881Speter          tmp_err = new_err;
380251881Speter        }
381251881Speter      else
382251881Speter        {
383251881Speter          tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
384251881Speter          tmp_err = tmp_err->child;
385251881Speter        }
386251881Speter      *tmp_err = *err;
387251881Speter      tmp_err->pool = pool;
388251881Speter      if (tmp_err->message)
389251881Speter        tmp_err->message = apr_pstrdup(pool, tmp_err->message);
390289166Speter      if (tmp_err->file)
391289166Speter        tmp_err->file = apr_pstrdup(pool, tmp_err->file);
392251881Speter    }
393251881Speter
394251881Speter#if defined(SVN_DEBUG)
395251881Speter  apr_pool_cleanup_register(pool, tmp_err,
396251881Speter                            err_abort,
397251881Speter                            apr_pool_cleanup_null);
398251881Speter#endif
399251881Speter
400251881Speter  return new_err;
401251881Speter}
402251881Speter
403251881Spetervoid
404251881Spetersvn_error_clear(svn_error_t *err)
405251881Speter{
406251881Speter  if (err)
407251881Speter    {
408251881Speter#if defined(SVN_DEBUG)
409251881Speter      while (err->child)
410251881Speter        err = err->child;
411251881Speter      apr_pool_cleanup_kill(err->pool, err, err_abort);
412251881Speter#endif
413251881Speter      svn_pool_destroy(err->pool);
414251881Speter    }
415251881Speter}
416251881Speter
417251881Spetersvn_boolean_t
418299742Sdimsvn_error__is_tracing_link(const svn_error_t *err)
419251881Speter{
420251881Speter#ifdef SVN_ERR__TRACING
421251881Speter  /* ### A strcmp()?  Really?  I think it's the best we can do unless
422251881Speter     ### we add a boolean field to svn_error_t that's set only for
423251881Speter     ### these "placeholder error chain" items.  Not such a bad idea,
424251881Speter     ### really...  */
425251881Speter  return (err && err->message && !strcmp(err->message, error_tracing_link));
426251881Speter#else
427251881Speter  return FALSE;
428251881Speter#endif
429251881Speter}
430251881Speter
431251881Spetersvn_error_t *
432251881Spetersvn_error_purge_tracing(svn_error_t *err)
433251881Speter{
434251881Speter#ifdef SVN_ERR__TRACING
435251881Speter  svn_error_t *new_err = NULL, *new_err_leaf = NULL;
436251881Speter
437251881Speter  if (! err)
438251881Speter    return SVN_NO_ERROR;
439251881Speter
440251881Speter  do
441251881Speter    {
442251881Speter      svn_error_t *tmp_err;
443251881Speter
444251881Speter      /* Skip over any trace-only links. */
445251881Speter      while (err && svn_error__is_tracing_link(err))
446251881Speter        err = err->child;
447251881Speter
448251881Speter      /* The link must be a real link in the error chain, otherwise an
449251881Speter         error chain with trace only links would map into SVN_NO_ERROR. */
450251881Speter      if (! err)
451251881Speter        return svn_error_create(
452251881Speter                 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
453299742Sdim                 svn_error__malfunction(TRUE, __FILE__, __LINE__,
454299742Sdim                                        NULL /* ### say something? */),
455251881Speter                 NULL);
456251881Speter
457251881Speter      /* Copy the current error except for its child error pointer
458251881Speter         into the new error.  Share any message and source filename
459251881Speter         strings from the error. */
460251881Speter      tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
461251881Speter      *tmp_err = *err;
462251881Speter      tmp_err->child = NULL;
463251881Speter
464251881Speter      /* Add a new link to the new chain (creating the chain if necessary). */
465251881Speter      if (! new_err)
466251881Speter        {
467251881Speter          new_err = tmp_err;
468251881Speter          new_err_leaf = tmp_err;
469251881Speter        }
470251881Speter      else
471251881Speter        {
472251881Speter          new_err_leaf->child = tmp_err;
473251881Speter          new_err_leaf = tmp_err;
474251881Speter        }
475251881Speter
476251881Speter      /* Advance to the next link in the original chain. */
477251881Speter      err = err->child;
478251881Speter    } while (err);
479251881Speter
480251881Speter  return new_err;
481251881Speter#else  /* SVN_ERR__TRACING */
482251881Speter  return err;
483251881Speter#endif /* SVN_ERR__TRACING */
484251881Speter}
485251881Speter
486251881Speter/* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
487251881Speter   ### coupled to the current sole caller.*/
488251881Speterstatic void
489251881Speterprint_error(svn_error_t *err, FILE *stream, const char *prefix)
490251881Speter{
491251881Speter  char errbuf[256];
492251881Speter  const char *err_string;
493251881Speter  svn_error_t *temp_err = NULL;  /* ensure initialized even if
494251881Speter                                    err->file == NULL */
495251881Speter  /* Pretty-print the error */
496251881Speter  /* Note: we can also log errors here someday. */
497251881Speter
498251881Speter#ifdef SVN_DEBUG
499251881Speter  /* Note: err->file is _not_ in UTF-8, because it's expanded from
500251881Speter           the __FILE__ preprocessor macro. */
501251881Speter  const char *file_utf8;
502251881Speter
503251881Speter  if (err->file
504251881Speter      && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
505251881Speter                                              err->pool)))
506251881Speter    svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
507251881Speter                                        "%s:%ld", err->file, err->line));
508251881Speter  else
509251881Speter    {
510251881Speter      svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
511251881Speter                                        stream, err->pool));
512251881Speter      svn_error_clear(temp_err);
513251881Speter    }
514251881Speter
515251881Speter  {
516251881Speter    const char *symbolic_name;
517251881Speter    if (svn_error__is_tracing_link(err))
518251881Speter      /* Skip it; the error code will be printed by the real link. */
519251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
520251881Speter    else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
521251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
522251881Speter                                          ": (apr_err=%s)\n", symbolic_name));
523251881Speter    else
524251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
525251881Speter                                          ": (apr_err=%d)\n", err->apr_err));
526251881Speter  }
527251881Speter#endif /* SVN_DEBUG */
528251881Speter
529251881Speter  /* "traced call" */
530251881Speter  if (svn_error__is_tracing_link(err))
531251881Speter    {
532251881Speter      /* Skip it.  We already printed the file-line coordinates. */
533251881Speter    }
534251881Speter  /* Only print the same APR error string once. */
535251881Speter  else if (err->message)
536251881Speter    {
537251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
538251881Speter                                          "%sE%06d: %s\n",
539251881Speter                                          prefix, err->apr_err, err->message));
540251881Speter    }
541251881Speter  else
542251881Speter    {
543251881Speter      /* Is this a Subversion-specific error code? */
544251881Speter      if ((err->apr_err > APR_OS_START_USEERR)
545251881Speter          && (err->apr_err <= APR_OS_START_CANONERR))
546251881Speter        err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
547251881Speter      /* Otherwise, this must be an APR error code. */
548251881Speter      else if ((temp_err = svn_utf_cstring_to_utf8
549251881Speter                (&err_string, apr_strerror(err->apr_err, errbuf,
550251881Speter                                           sizeof(errbuf)), err->pool)))
551251881Speter        {
552251881Speter          svn_error_clear(temp_err);
553251881Speter          err_string = _("Can't recode error string from APR");
554251881Speter        }
555251881Speter
556251881Speter      svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
557251881Speter                                          "%sE%06d: %s\n",
558251881Speter                                          prefix, err->apr_err, err_string));
559251881Speter    }
560251881Speter}
561251881Speter
562251881Spetervoid
563251881Spetersvn_handle_error2(svn_error_t *err,
564251881Speter                  FILE *stream,
565251881Speter                  svn_boolean_t fatal,
566251881Speter                  const char *prefix)
567251881Speter{
568251881Speter  /* In a long error chain, there may be multiple errors with the same
569251881Speter     error code and no custom message.  We only want to print the
570251881Speter     default message for that code once; printing it multiple times
571251881Speter     would add no useful information.  The 'empties' array below
572251881Speter     remembers the codes of empty errors already seen in the chain.
573251881Speter
574251881Speter     We could allocate it in err->pool, but there's no telling how
575251881Speter     long err will live or how many times it will get handled.  So we
576251881Speter     use a subpool. */
577251881Speter  apr_pool_t *subpool;
578251881Speter  apr_array_header_t *empties;
579251881Speter  svn_error_t *tmp_err;
580251881Speter
581299742Sdim  subpool = svn_pool_create(err->pool);
582251881Speter  empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
583251881Speter
584251881Speter  tmp_err = err;
585251881Speter  while (tmp_err)
586251881Speter    {
587251881Speter      svn_boolean_t printed_already = FALSE;
588251881Speter
589251881Speter      if (! tmp_err->message)
590251881Speter        {
591251881Speter          int i;
592251881Speter
593251881Speter          for (i = 0; i < empties->nelts; i++)
594251881Speter            {
595251881Speter              if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
596251881Speter                {
597251881Speter                  printed_already = TRUE;
598251881Speter                  break;
599251881Speter                }
600251881Speter            }
601251881Speter        }
602251881Speter
603251881Speter      if (! printed_already)
604251881Speter        {
605251881Speter          print_error(tmp_err, stream, prefix);
606251881Speter          if (! tmp_err->message)
607251881Speter            {
608251881Speter              APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
609251881Speter            }
610251881Speter        }
611251881Speter
612251881Speter      tmp_err = tmp_err->child;
613251881Speter    }
614251881Speter
615251881Speter  svn_pool_destroy(subpool);
616251881Speter
617251881Speter  fflush(stream);
618251881Speter  if (fatal)
619251881Speter    {
620251881Speter      /* Avoid abort()s in maintainer mode. */
621251881Speter      svn_error_clear(err);
622251881Speter
623251881Speter      /* We exit(1) here instead of abort()ing so that atexit handlers
624251881Speter         get called. */
625251881Speter      exit(EXIT_FAILURE);
626251881Speter    }
627251881Speter}
628251881Speter
629251881Spetervoid
630299742Sdimsvn_handle_warning2(FILE *stream, const svn_error_t *err, const char *prefix)
631251881Speter{
632251881Speter  char buf[256];
633299742Sdim#ifdef SVN_DEBUG
634299742Sdim  const char *symbolic_name = svn_error_symbolic_name(err->apr_err);
635299742Sdim#endif
636251881Speter
637299742Sdim#ifdef SVN_DEBUG
638299742Sdim  if (symbolic_name)
639299742Sdim    svn_error_clear(
640299742Sdim      svn_cmdline_fprintf(stream, err->pool, "%swarning: apr_err=%s\n",
641299742Sdim                          prefix, symbolic_name));
642299742Sdim#endif
643299742Sdim
644251881Speter  svn_error_clear(svn_cmdline_fprintf
645251881Speter                  (stream, err->pool,
646251881Speter                   _("%swarning: W%06d: %s\n"),
647251881Speter                   prefix, err->apr_err,
648251881Speter                   svn_err_best_message(err, buf, sizeof(buf))));
649251881Speter  fflush(stream);
650251881Speter}
651251881Speter
652251881Speterconst char *
653299742Sdimsvn_err_best_message(const svn_error_t *err, char *buf, apr_size_t bufsize)
654251881Speter{
655251881Speter  /* Skip over any trace records.  */
656251881Speter  while (svn_error__is_tracing_link(err))
657251881Speter    err = err->child;
658251881Speter  if (err->message)
659251881Speter    return err->message;
660251881Speter  else
661251881Speter    return svn_strerror(err->apr_err, buf, bufsize);
662251881Speter}
663251881Speter
664251881Speter
665251881Speter/* svn_strerror() and helpers */
666251881Speter
667251881Speter/* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
668251881Spetertypedef struct err_defn {
669251881Speter  svn_errno_t errcode; /* 160004 */
670251881Speter  const char *errname; /* SVN_ERR_FS_CORRUPT */
671251881Speter  const char *errdesc; /* default message */
672251881Speter} err_defn;
673251881Speter
674251881Speter/* To understand what is going on here, read svn_error_codes.h. */
675251881Speter#define SVN_ERROR_BUILD_ARRAY
676251881Speter#include "svn_error_codes.h"
677251881Speter
678251881Speterchar *
679251881Spetersvn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
680251881Speter{
681251881Speter  const err_defn *defn;
682251881Speter
683251881Speter  for (defn = error_table; defn->errdesc != NULL; ++defn)
684251881Speter    if (defn->errcode == (svn_errno_t)statcode)
685251881Speter      {
686251881Speter        apr_cpystrn(buf, _(defn->errdesc), bufsize);
687251881Speter        return buf;
688251881Speter      }
689251881Speter
690251881Speter  return apr_strerror(statcode, buf, bufsize);
691251881Speter}
692251881Speter
693299742Sdim#ifdef SVN_DEBUG
694299742Sdim/* Defines svn__errno and svn__apr_errno */
695299742Sdim#include "errorcode.inc"
696299742Sdim#endif
697299742Sdim
698251881Speterconst char *
699251881Spetersvn_error_symbolic_name(apr_status_t statcode)
700251881Speter{
701251881Speter  const err_defn *defn;
702299742Sdim#ifdef SVN_DEBUG
703299742Sdim  int i;
704299742Sdim#endif /* SVN_DEBUG */
705251881Speter
706251881Speter  for (defn = error_table; defn->errdesc != NULL; ++defn)
707251881Speter    if (defn->errcode == (svn_errno_t)statcode)
708251881Speter      return defn->errname;
709251881Speter
710251881Speter  /* "No error" is not in error_table. */
711299742Sdim  if (statcode == APR_SUCCESS)
712251881Speter    return "SVN_NO_ERROR";
713251881Speter
714299742Sdim#ifdef SVN_DEBUG
715299742Sdim  /* Try errno.h symbols. */
716299742Sdim  /* Linear search through a sorted array */
717299742Sdim  for (i = 0; i < sizeof(svn__errno) / sizeof(svn__errno[0]); i++)
718299742Sdim    if (svn__errno[i].errcode == (int)statcode)
719299742Sdim      return svn__errno[i].errname;
720299742Sdim
721299742Sdim  /* Try APR errors. */
722299742Sdim  /* Linear search through a sorted array */
723299742Sdim  for (i = 0; i < sizeof(svn__apr_errno) / sizeof(svn__apr_errno[0]); i++)
724299742Sdim    if (svn__apr_errno[i].errcode == (int)statcode)
725299742Sdim      return svn__apr_errno[i].errname;
726299742Sdim#endif /* SVN_DEBUG */
727299742Sdim
728299742Sdim  /* ### TODO: do we need APR_* error macros?  What about APR_TO_OS_ERROR()? */
729299742Sdim
730251881Speter  return NULL;
731251881Speter}
732251881Speter
733251881Speter
734251881Speter
735251881Speter/* Malfunctions. */
736251881Speter
737251881Spetersvn_error_t *
738251881Spetersvn_error_raise_on_malfunction(svn_boolean_t can_return,
739251881Speter                               const char *file, int line,
740251881Speter                               const char *expr)
741251881Speter{
742251881Speter  if (!can_return)
743251881Speter    abort(); /* Nothing else we can do as a library */
744251881Speter
745251881Speter  /* The filename and line number of the error source needs to be set
746251881Speter     here because svn_error_createf() is not the macro defined in
747251881Speter     svn_error.h but the real function. */
748251881Speter  svn_error__locate(file, line);
749251881Speter
750251881Speter  if (expr)
751251881Speter    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
752251881Speter                             _("In file '%s' line %d: assertion failed (%s)"),
753251881Speter                             file, line, expr);
754251881Speter  else
755251881Speter    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
756251881Speter                             _("In file '%s' line %d: internal malfunction"),
757251881Speter                             file, line);
758251881Speter}
759251881Speter
760251881Spetersvn_error_t *
761251881Spetersvn_error_abort_on_malfunction(svn_boolean_t can_return,
762251881Speter                               const char *file, int line,
763251881Speter                               const char *expr)
764251881Speter{
765251881Speter  svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
766251881Speter
767251881Speter  svn_handle_error2(err, stderr, FALSE, "svn: ");
768251881Speter  abort();
769251881Speter  return err;  /* Not reached. */
770251881Speter}
771251881Speter
772251881Speter/* The current handler for reporting malfunctions, and its default setting. */
773251881Speterstatic svn_error_malfunction_handler_t malfunction_handler
774251881Speter  = svn_error_abort_on_malfunction;
775251881Speter
776251881Spetersvn_error_malfunction_handler_t
777251881Spetersvn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
778251881Speter{
779251881Speter  svn_error_malfunction_handler_t old_malfunction_handler
780251881Speter    = malfunction_handler;
781251881Speter
782251881Speter  malfunction_handler = func;
783251881Speter  return old_malfunction_handler;
784251881Speter}
785251881Speter
786299742Sdimsvn_error_malfunction_handler_t
787299742Sdimsvn_error_get_malfunction_handler(void)
788299742Sdim{
789299742Sdim  return malfunction_handler;
790299742Sdim}
791299742Sdim
792251881Speter/* Note: Although this is a "__" function, it is in the public ABI, so
793251881Speter * we can never remove it or change its signature. */
794251881Spetersvn_error_t *
795251881Spetersvn_error__malfunction(svn_boolean_t can_return,
796251881Speter                       const char *file, int line,
797251881Speter                       const char *expr)
798251881Speter{
799251881Speter  return malfunction_handler(can_return, file, line, expr);
800251881Speter}
801251881Speter
802251881Speter
803251881Speter/* Misc. */
804251881Speter
805251881Spetersvn_error_t *
806251881Spetersvn_error__wrap_zlib(int zerr, const char *function, const char *message)
807251881Speter{
808251881Speter  apr_status_t status;
809251881Speter  const char *zmsg;
810251881Speter
811251881Speter  if (zerr == Z_OK)
812251881Speter    return SVN_NO_ERROR;
813251881Speter
814251881Speter  switch (zerr)
815251881Speter    {
816251881Speter    case Z_STREAM_ERROR:
817251881Speter      status = SVN_ERR_STREAM_MALFORMED_DATA;
818251881Speter      zmsg = _("stream error");
819251881Speter      break;
820251881Speter
821251881Speter    case Z_MEM_ERROR:
822251881Speter      status = APR_ENOMEM;
823251881Speter      zmsg = _("out of memory");
824251881Speter      break;
825251881Speter
826251881Speter    case Z_BUF_ERROR:
827251881Speter      status = APR_ENOMEM;
828251881Speter      zmsg = _("buffer error");
829251881Speter      break;
830251881Speter
831251881Speter    case Z_VERSION_ERROR:
832251881Speter      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
833251881Speter      zmsg = _("version error");
834251881Speter      break;
835251881Speter
836251881Speter    case Z_DATA_ERROR:
837251881Speter      status = SVN_ERR_STREAM_MALFORMED_DATA;
838251881Speter      zmsg = _("corrupt data");
839251881Speter      break;
840251881Speter
841251881Speter    default:
842251881Speter      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
843251881Speter      zmsg = _("unknown error");
844251881Speter      break;
845251881Speter    }
846251881Speter
847251881Speter  if (message != NULL)
848251881Speter    return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
849251881Speter                             zmsg, message);
850251881Speter  else
851251881Speter    return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);
852251881Speter}
853