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