* subversion/libsvn_subr/validate.c
[svn.git] / subversion / libsvn_subr / stream.c
blob532c41ef86ca1934a7fc7db9f331d7a270b7e8cd
1 /*
2 * stream.c: svn_stream operations
4 * ====================================================================
5 * Copyright (c) 2000-2004, 2006 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 #include "svn_private_config.h"
21 #include <assert.h>
22 #include <stdio.h>
24 #include <apr.h>
25 #include <apr_pools.h>
26 #include <apr_strings.h>
27 #include <apr_file_io.h>
28 #include <apr_errno.h>
29 #include <apr_md5.h>
31 #include <zlib.h>
33 #include "svn_pools.h"
34 #include "svn_io.h"
35 #include "svn_error.h"
36 #include "svn_string.h"
37 #include "svn_utf.h"
40 struct svn_stream_t {
41 void *baton;
42 svn_read_fn_t read_fn;
43 svn_write_fn_t write_fn;
44 svn_close_fn_t close_fn;
49 /*** Generic streams. ***/
51 svn_stream_t *
52 svn_stream_create(void *baton, apr_pool_t *pool)
54 svn_stream_t *stream;
56 stream = apr_palloc(pool, sizeof(*stream));
57 stream->baton = baton;
58 stream->read_fn = NULL;
59 stream->write_fn = NULL;
60 stream->close_fn = NULL;
61 return stream;
65 void
66 svn_stream_set_baton(svn_stream_t *stream, void *baton)
68 stream->baton = baton;
72 void
73 svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn)
75 stream->read_fn = read_fn;
79 void
80 svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
82 stream->write_fn = write_fn;
86 void
87 svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
89 stream->close_fn = close_fn;
93 svn_error_t *
94 svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len)
96 assert(stream->read_fn != NULL);
97 return stream->read_fn(stream->baton, buffer, len);
101 svn_error_t *
102 svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)
104 assert(stream->write_fn != NULL);
105 return stream->write_fn(stream->baton, data, len);
109 svn_error_t *
110 svn_stream_close(svn_stream_t *stream)
112 if (stream->close_fn == NULL)
113 return SVN_NO_ERROR;
114 return stream->close_fn(stream->baton);
118 svn_error_t *
119 svn_stream_printf(svn_stream_t *stream,
120 apr_pool_t *pool,
121 const char *fmt,
122 ...)
124 const char *message;
125 va_list ap;
126 apr_size_t len;
128 va_start(ap, fmt);
129 message = apr_pvsprintf(pool, fmt, ap);
130 va_end(ap);
132 len = strlen(message);
133 return svn_stream_write(stream, message, &len);
137 svn_error_t *
138 svn_stream_printf_from_utf8(svn_stream_t *stream,
139 const char *encoding,
140 apr_pool_t *pool,
141 const char *fmt,
142 ...)
144 const char *message, *translated;
145 va_list ap;
146 apr_size_t len;
148 va_start(ap, fmt);
149 message = apr_pvsprintf(pool, fmt, ap);
150 va_end(ap);
152 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,
153 pool));
155 len = strlen(translated);
157 return svn_stream_write(stream, translated, &len);
161 svn_error_t *
162 svn_stream_readline(svn_stream_t *stream,
163 svn_stringbuf_t **stringbuf,
164 const char *eol,
165 svn_boolean_t *eof,
166 apr_pool_t *pool)
168 apr_size_t numbytes;
169 const char *match;
170 char c;
171 /* Since we're reading one character at a time, let's at least
172 optimize for the 90% case. 90% of the time, we can avoid the
173 stringbuf ever having to realloc() itself if we start it out at
174 80 chars. */
175 svn_stringbuf_t *str = svn_stringbuf_create_ensure(80, pool);
177 match = eol;
178 while (*match)
180 numbytes = 1;
181 SVN_ERR(svn_stream_read(stream, &c, &numbytes));
182 if (numbytes != 1)
184 /* a 'short' read means the stream has run out. */
185 *eof = TRUE;
186 *stringbuf = str;
187 return SVN_NO_ERROR;
190 if (c == *match)
191 match++;
192 else
193 match = eol;
195 svn_stringbuf_appendbytes(str, &c, 1);
198 *eof = FALSE;
199 svn_stringbuf_chop(str, match - eol);
200 *stringbuf = str;
201 return SVN_NO_ERROR;
205 svn_error_t *svn_stream_copy2(svn_stream_t *from, svn_stream_t *to,
206 svn_cancel_func_t cancel_func,
207 void *cancel_baton,
208 apr_pool_t *pool)
210 char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
211 apr_size_t len;
213 /* Read and write chunks until we get a short read, indicating the
214 end of the stream. (We can't get a short write without an
215 associated error.) */
216 while (1)
218 len = SVN__STREAM_CHUNK_SIZE;
220 if (cancel_func)
221 SVN_ERR(cancel_func(cancel_baton));
223 SVN_ERR(svn_stream_read(from, buf, &len));
224 if (len > 0)
225 SVN_ERR(svn_stream_write(to, buf, &len));
226 if (len != SVN__STREAM_CHUNK_SIZE)
227 break;
229 return SVN_NO_ERROR;
232 svn_error_t *svn_stream_copy(svn_stream_t *from, svn_stream_t *to,
233 apr_pool_t *pool)
235 return svn_stream_copy2(from, to, NULL, NULL, pool);
238 svn_error_t *
239 svn_stream_contents_same(svn_boolean_t *same,
240 svn_stream_t *stream1,
241 svn_stream_t *stream2,
242 apr_pool_t *pool)
244 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
245 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
246 apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;
247 apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;
249 *same = TRUE; /* assume TRUE, until disproved below */
250 while (bytes_read1 == SVN__STREAM_CHUNK_SIZE
251 && bytes_read2 == SVN__STREAM_CHUNK_SIZE)
253 SVN_ERR(svn_stream_read(stream1, buf1, &bytes_read1));
254 SVN_ERR(svn_stream_read(stream2, buf2, &bytes_read2));
256 if ((bytes_read1 != bytes_read2)
257 || (memcmp(buf1, buf2, bytes_read1)))
259 *same = FALSE;
260 break;
264 return SVN_NO_ERROR;
269 /*** Generic readable empty stream ***/
271 static svn_error_t *
272 read_handler_empty(void *baton, char *buffer, apr_size_t *len)
274 *len = 0;
275 return SVN_NO_ERROR;
279 static svn_error_t *
280 write_handler_empty(void *baton, const char *data, apr_size_t *len)
282 return SVN_NO_ERROR;
286 svn_stream_t *
287 svn_stream_empty(apr_pool_t *pool)
289 svn_stream_t *stream;
291 stream = svn_stream_create(NULL, pool);
292 svn_stream_set_read(stream, read_handler_empty);
293 svn_stream_set_write(stream, write_handler_empty);
294 return stream;
300 /*** Ownership detaching stream ***/
302 static svn_error_t *
303 read_handler_disown(void *baton, char *buffer, apr_size_t *len)
305 return svn_stream_read((svn_stream_t *)baton, buffer, len);
308 static svn_error_t *
309 write_handler_disown(void *baton, const char *buffer, apr_size_t *len)
311 return svn_stream_write((svn_stream_t *)baton, buffer, len);
315 svn_stream_t *
316 svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
318 svn_stream_t *s = svn_stream_create(stream, pool);
320 svn_stream_set_read(s, read_handler_disown);
321 svn_stream_set_write(s, write_handler_disown);
323 return s;
328 /*** Generic stream for APR files ***/
329 struct baton_apr {
330 apr_file_t *file;
331 apr_pool_t *pool;
335 static svn_error_t *
336 read_handler_apr(void *baton, char *buffer, apr_size_t *len)
338 struct baton_apr *btn = baton;
339 svn_error_t *err;
341 err = svn_io_file_read_full(btn->file, buffer, *len, len, btn->pool);
342 if (err && APR_STATUS_IS_EOF(err->apr_err))
344 svn_error_clear(err);
345 err = SVN_NO_ERROR;
348 return err;
352 static svn_error_t *
353 write_handler_apr(void *baton, const char *data, apr_size_t *len)
355 struct baton_apr *btn = baton;
357 return svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
360 static svn_error_t *
361 close_handler_apr(void *baton)
363 struct baton_apr *btn = baton;
365 return svn_io_file_close(btn->file, btn->pool);
369 svn_stream_t *
370 svn_stream_from_aprfile2(apr_file_t *file,
371 svn_boolean_t disown,
372 apr_pool_t *pool)
374 struct baton_apr *baton;
375 svn_stream_t *stream;
377 if (file == NULL)
378 return svn_stream_empty(pool);
380 baton = apr_palloc(pool, sizeof(*baton));
381 baton->file = file;
382 baton->pool = pool;
383 stream = svn_stream_create(baton, pool);
384 svn_stream_set_read(stream, read_handler_apr);
385 svn_stream_set_write(stream, write_handler_apr);
387 if (! disown)
388 svn_stream_set_close(stream, close_handler_apr);
390 return stream;
393 svn_stream_t *
394 svn_stream_from_aprfile(apr_file_t *file, apr_pool_t *pool)
396 return svn_stream_from_aprfile2(file, TRUE, pool);
401 /* Compressed stream support */
403 #define ZBUFFER_SIZE 4096 /* The size of the buffer the
404 compressed stream uses to read from
405 the substream. Basically an
406 arbitrary value, picked to be about
407 page-sized. */
409 struct zbaton {
410 z_stream *in; /* compressed stream for reading */
411 z_stream *out; /* compressed stream for writing */
412 svn_read_fn_t read; /* substream's read function */
413 svn_write_fn_t write; /* substream's write function */
414 svn_close_fn_t close; /* substream's close function */
415 void *read_buffer; /* buffer used for reading from
416 substream */
417 int read_flush; /* what flush mode to use while
418 reading */
419 apr_pool_t *pool; /* The pool this baton is allocated
420 on */
421 void *subbaton; /* The substream's baton */
424 /* zlib alloc function. opaque is the pool we need. */
425 static voidpf
426 zalloc(voidpf opaque, uInt items, uInt size)
428 apr_pool_t *pool = opaque;
430 return apr_palloc(pool, items * size);
433 /* zlib free function */
434 static void
435 zfree(voidpf opaque, voidpf address)
437 /* Empty, since we allocate on the pool */
440 /* Converts a zlib error to an svn_error_t. zerr is the error code,
441 function is the function name, and stream is the z_stream we are
442 using. */
443 static svn_error_t *
444 zerr_to_svn_error(int zerr, const char *function, z_stream *stream)
446 apr_status_t status;
447 const char *message;
449 if (zerr == Z_OK)
450 return SVN_NO_ERROR;
452 switch (zerr)
454 case Z_STREAM_ERROR:
455 status = SVN_ERR_STREAM_MALFORMED_DATA;
456 message = "stream error";
457 break;
459 case Z_MEM_ERROR:
460 status = APR_ENOMEM;
461 message = "out of memory";
462 break;
464 case Z_BUF_ERROR:
465 status = APR_ENOMEM;
466 message = "buffer error";
467 break;
469 case Z_VERSION_ERROR:
470 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
471 message = "version error";
472 break;
474 case Z_DATA_ERROR:
475 status = SVN_ERR_STREAM_MALFORMED_DATA;
476 message = "corrupted data";
477 break;
479 default:
480 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
481 message = "error";
482 break;
485 if (stream->msg != NULL)
486 return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
487 message, stream->msg);
488 else
489 return svn_error_createf(status, NULL, "zlib (%s): %s", function,
490 message);
493 /* Helper function to figure out the sync mode */
494 static svn_error_t *
495 read_helper_gz(svn_read_fn_t read_fn,
496 void *baton,
497 char *buffer,
498 uInt *len, int *zflush)
500 uInt orig_len = *len;
502 /* There's no reason this value should grow bigger than the range of
503 uInt, but Subversion's API requires apr_size_t. */
504 apr_size_t apr_len = (apr_size_t) *len;
506 SVN_ERR((*read_fn)(baton, buffer, &apr_len));
508 /* Type cast back to uInt type that zlib uses. On LP64 platforms
509 apr_size_t will be bigger than uInt. */
510 *len = (uInt) apr_len;
512 /* I wanted to use Z_FINISH here, but we need to know our buffer is
513 big enough */
514 *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;
516 return SVN_NO_ERROR;
519 /* Handle reading from a compressed stream */
520 static svn_error_t *
521 read_handler_gz(void *baton, char *buffer, apr_size_t *len)
523 struct zbaton *btn = baton;
524 int zerr;
526 if (btn->in == NULL)
528 btn->in = apr_palloc(btn->pool, sizeof(z_stream));
529 btn->in->zalloc = zalloc;
530 btn->in->zfree = zfree;
531 btn->in->opaque = btn->pool;
532 btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);
533 btn->in->next_in = btn->read_buffer;
534 btn->in->avail_in = ZBUFFER_SIZE;
536 SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
537 &btn->in->avail_in, &btn->read_flush));
539 zerr = inflateInit(btn->in);
540 SVN_ERR(zerr_to_svn_error(zerr, "inflateInit", btn->in));
543 btn->in->next_out = (Bytef *) buffer;
544 btn->in->avail_out = *len;
546 while (btn->in->avail_out > 0)
548 if (btn->in->avail_in <= 0)
550 btn->in->avail_in = ZBUFFER_SIZE;
551 btn->in->next_in = btn->read_buffer;
552 SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
553 &btn->in->avail_in, &btn->read_flush));
556 zerr = inflate(btn->in, btn->read_flush);
557 if (zerr == Z_STREAM_END)
558 break;
559 else if (zerr != Z_OK)
560 return zerr_to_svn_error(zerr, "inflate", btn->in);
563 *len -= btn->in->avail_out;
564 return SVN_NO_ERROR;
567 /* Compress data and write it to the substream */
568 static svn_error_t *
569 write_handler_gz(void *baton, const char *buffer, apr_size_t *len)
571 struct zbaton *btn = baton;
572 apr_pool_t *subpool;
573 void *write_buf;
574 apr_size_t buf_size, write_len;
575 int zerr;
577 if (btn->out == NULL)
579 btn->out = apr_palloc(btn->pool, sizeof(z_stream));
580 btn->out->zalloc = zalloc;
581 btn->out->zfree = zfree;
582 btn->out->opaque = btn->pool;
584 zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);
585 SVN_ERR(zerr_to_svn_error(zerr, "deflateInit", btn->out));
588 /* The largest buffer we should need is 0.1% larger than the
589 compressed data, + 12 bytes. This info comes from zlib.h. */
590 buf_size = *len + (*len / 1000) + 13;
591 subpool = svn_pool_create(btn->pool);
592 write_buf = apr_palloc(subpool, buf_size);
594 btn->out->next_in = (Bytef *) buffer; /* Casting away const! */
595 btn->out->avail_in = *len;
597 while (btn->out->avail_in > 0)
599 btn->out->next_out = write_buf;
600 btn->out->avail_out = buf_size;
602 zerr = deflate(btn->out, Z_NO_FLUSH);
603 SVN_ERR(zerr_to_svn_error(zerr, "deflate", btn->out));
604 write_len = buf_size - btn->out->avail_out;
605 if (write_len > 0)
606 SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len));
609 svn_pool_destroy(subpool);
611 return SVN_NO_ERROR;
614 /* Handle flushing and closing the stream */
615 static svn_error_t *
616 close_handler_gz(void *baton)
618 struct zbaton *btn = baton;
619 int zerr;
621 if (btn->in != NULL)
623 zerr = inflateEnd(btn->in);
624 SVN_ERR(zerr_to_svn_error(zerr, "inflateEnd", btn->in));
627 if (btn->out != NULL)
629 void *buf;
630 apr_size_t write_len;
632 buf = apr_palloc(btn->pool, ZBUFFER_SIZE);
634 while (TRUE)
636 btn->out->next_out = buf;
637 btn->out->avail_out = ZBUFFER_SIZE;
639 zerr = deflate(btn->out, Z_FINISH);
640 if (zerr != Z_STREAM_END && zerr != Z_OK)
641 return zerr_to_svn_error(zerr, "deflate", btn->out);
642 write_len = ZBUFFER_SIZE - btn->out->avail_out;
643 if (write_len > 0)
644 SVN_ERR(btn->write(btn->subbaton, buf, &write_len));
645 if (zerr == Z_STREAM_END)
646 break;
649 zerr = deflateEnd(btn->out);
650 SVN_ERR(zerr_to_svn_error(zerr, "deflateEnd", btn->out));
653 if (btn->close != NULL)
654 return btn->close(btn->subbaton);
655 else
656 return SVN_NO_ERROR;
660 svn_stream_t *
661 svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)
663 struct svn_stream_t *zstream;
664 struct zbaton *baton;
666 assert(stream != NULL);
668 baton = apr_palloc(pool, sizeof(*baton));
669 baton->in = baton->out = NULL;
670 baton->read = stream->read_fn;
671 baton->write = stream->write_fn;
672 baton->close = stream->close_fn;
673 baton->subbaton = stream->baton;
674 baton->pool = pool;
675 baton->read_buffer = NULL;
676 baton->read_flush = Z_SYNC_FLUSH;
678 zstream = svn_stream_create(baton, pool);
679 svn_stream_set_read(zstream, read_handler_gz);
680 svn_stream_set_write(zstream, write_handler_gz);
681 svn_stream_set_close(zstream, close_handler_gz);
683 return zstream;
687 /* MD5 checked stream support */
689 struct md5_stream_baton
691 apr_md5_ctx_t read_ctx, write_ctx;
692 const unsigned char **read_digest;
693 const unsigned char **write_digest;
694 unsigned char read_digest_buf[APR_MD5_DIGESTSIZE];
695 unsigned char write_digest_buf[APR_MD5_DIGESTSIZE];
696 svn_stream_t *proxy;
698 /* True if more data should be read when closing the stream. */
699 svn_boolean_t read_more;
701 /* Pool to allocate read buffer from. */
702 apr_pool_t *pool;
705 static svn_error_t *
706 read_handler_md5(void *baton, char *buffer, apr_size_t *len)
708 struct md5_stream_baton *btn = baton;
709 apr_size_t saved_len = *len;
711 SVN_ERR(svn_stream_read(btn->proxy, buffer, len));
713 if (btn->read_digest)
715 apr_status_t apr_err = apr_md5_update(&btn->read_ctx, buffer, *len);
717 if (apr_err)
718 return svn_error_create(apr_err, NULL, NULL);
721 if (saved_len != *len)
722 btn->read_more = FALSE;
724 return SVN_NO_ERROR;
728 static svn_error_t *
729 write_handler_md5(void *baton, const char *buffer, apr_size_t *len)
731 struct md5_stream_baton *btn = baton;
733 if (btn->write_digest && *len > 0)
735 apr_status_t apr_err = apr_md5_update(&btn->write_ctx, buffer, *len);
737 if (apr_err)
738 return svn_error_create(apr_err, NULL, NULL);
741 return svn_stream_write(btn->proxy, buffer, len);
745 static svn_error_t *
746 close_handler_md5(void *baton)
748 struct md5_stream_baton *btn = baton;
750 /* If we're supposed to drain the stream, do so before finalizing the
751 checksum. */
752 if (btn->read_more)
754 char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);
755 apr_size_t len = SVN__STREAM_CHUNK_SIZE;
759 SVN_ERR(read_handler_md5(baton, buf, &len));
761 while (btn->read_more);
764 if (btn->read_digest)
766 apr_status_t apr_err
767 = apr_md5_final(btn->read_digest_buf, &btn->read_ctx);
769 if (apr_err)
770 return svn_error_create(apr_err, NULL, NULL);
772 *btn->read_digest = btn->read_digest_buf;
775 if (btn->write_digest)
777 apr_status_t apr_err
778 = apr_md5_final(btn->write_digest_buf, &btn->write_ctx);
780 if (apr_err)
781 return svn_error_create(apr_err, NULL, NULL);
783 *btn->write_digest = btn->write_digest_buf;
786 return svn_stream_close(btn->proxy);
790 svn_stream_t *
791 svn_stream_checksummed(svn_stream_t *stream,
792 const unsigned char **read_digest,
793 const unsigned char **write_digest,
794 svn_boolean_t read_all,
795 apr_pool_t *pool)
797 svn_stream_t *s;
798 struct md5_stream_baton *baton;
800 if (! read_digest && ! write_digest)
801 return stream;
803 baton = apr_palloc(pool, sizeof(*baton));
804 apr_md5_init(&baton->read_ctx);
805 apr_md5_init(&baton->write_ctx);
806 baton->read_digest = read_digest;
807 baton->write_digest = write_digest;
808 baton->proxy = stream;
809 baton->read_more = read_all;
810 baton->pool = pool;
812 s = svn_stream_create(baton, pool);
813 svn_stream_set_read(s, read_handler_md5);
814 svn_stream_set_write(s, write_handler_md5);
815 svn_stream_set_close(s, close_handler_md5);
816 return s;
822 /* Miscellaneous stream functions. */
823 struct string_stream_baton
825 svn_stringbuf_t *str;
826 apr_size_t amt_read;
829 static svn_error_t *
830 read_handler_string(void *baton, char *buffer, apr_size_t *len)
832 struct string_stream_baton *btn = baton;
833 apr_size_t left_to_read = btn->str->len - btn->amt_read;
835 *len = (*len > left_to_read) ? left_to_read : *len;
836 memcpy(buffer, btn->str->data + btn->amt_read, *len);
837 btn->amt_read += *len;
838 return SVN_NO_ERROR;
841 static svn_error_t *
842 write_handler_string(void *baton, const char *data, apr_size_t *len)
844 struct string_stream_baton *btn = baton;
846 svn_stringbuf_appendbytes(btn->str, data, *len);
847 return SVN_NO_ERROR;
850 svn_stream_t *
851 svn_stream_from_stringbuf(svn_stringbuf_t *str,
852 apr_pool_t *pool)
854 svn_stream_t *stream;
855 struct string_stream_baton *baton;
857 if (! str)
858 return svn_stream_empty(pool);
860 baton = apr_palloc(pool, sizeof(*baton));
861 baton->str = str;
862 baton->amt_read = 0;
863 stream = svn_stream_create(baton, pool);
864 svn_stream_set_read(stream, read_handler_string);
865 svn_stream_set_write(stream, write_handler_string);
866 return stream;
870 svn_error_t *
871 svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
873 apr_file_t *stdout_file;
874 apr_status_t apr_err;
876 apr_err = apr_file_open_stdout(&stdout_file, pool);
877 if (apr_err)
878 return svn_error_wrap_apr(apr_err, "Can't open stdout");
880 *out = svn_stream_from_aprfile(stdout_file, pool);
882 return SVN_NO_ERROR;