]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_subr/stream.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_subr / stream.c
1 /*
2  * stream.c:   svn_stream operations
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include <assert.h>
25 #include <stdio.h>
26
27 #include <apr.h>
28 #include <apr_pools.h>
29 #include <apr_strings.h>
30 #include <apr_file_io.h>
31 #include <apr_errno.h>
32 #include <apr_md5.h>
33
34 #include <zlib.h>
35
36 #include "svn_pools.h"
37 #include "svn_io.h"
38 #include "svn_error.h"
39 #include "svn_string.h"
40 #include "svn_utf.h"
41 #include "svn_checksum.h"
42 #include "svn_path.h"
43 #include "svn_private_config.h"
44 #include "private/svn_error_private.h"
45 #include "private/svn_eol_private.h"
46 #include "private/svn_io_private.h"
47 #include "private/svn_subr_private.h"
48
49
50 struct svn_stream_t {
51   void *baton;
52   svn_read_fn_t read_fn;
53   svn_stream_skip_fn_t skip_fn;
54   svn_write_fn_t write_fn;
55   svn_close_fn_t close_fn;
56   svn_stream_mark_fn_t mark_fn;
57   svn_stream_seek_fn_t seek_fn;
58   svn_stream__is_buffered_fn_t is_buffered_fn;
59   apr_file_t *file; /* Maybe NULL */
60 };
61
62 \f
63 /*** Forward declarations. ***/
64
65 static svn_error_t *
66 skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn);
67
68 \f
69 /*** Generic streams. ***/
70
71 svn_stream_t *
72 svn_stream_create(void *baton, apr_pool_t *pool)
73 {
74   svn_stream_t *stream;
75
76   stream = apr_palloc(pool, sizeof(*stream));
77   stream->baton = baton;
78   stream->read_fn = NULL;
79   stream->skip_fn = NULL;
80   stream->write_fn = NULL;
81   stream->close_fn = NULL;
82   stream->mark_fn = NULL;
83   stream->seek_fn = NULL;
84   stream->is_buffered_fn = NULL;
85   stream->file = NULL;
86   return stream;
87 }
88
89
90 void
91 svn_stream_set_baton(svn_stream_t *stream, void *baton)
92 {
93   stream->baton = baton;
94 }
95
96
97 void
98 svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn)
99 {
100   stream->read_fn = read_fn;
101 }
102
103 void
104 svn_stream_set_skip(svn_stream_t *stream, svn_stream_skip_fn_t skip_fn)
105 {
106   stream->skip_fn = skip_fn;
107 }
108
109 void
110 svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
111 {
112   stream->write_fn = write_fn;
113 }
114
115 void
116 svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
117 {
118   stream->close_fn = close_fn;
119 }
120
121 void
122 svn_stream_set_mark(svn_stream_t *stream, svn_stream_mark_fn_t mark_fn)
123 {
124   stream->mark_fn = mark_fn;
125 }
126
127 void
128 svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn)
129 {
130   stream->seek_fn = seek_fn;
131 }
132
133 void
134 svn_stream__set_is_buffered(svn_stream_t *stream,
135                             svn_stream__is_buffered_fn_t is_buffered_fn)
136 {
137   stream->is_buffered_fn = is_buffered_fn;
138 }
139
140 svn_error_t *
141 svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len)
142 {
143   SVN_ERR_ASSERT(stream->read_fn != NULL);
144   return svn_error_trace(stream->read_fn(stream->baton, buffer, len));
145 }
146
147
148 svn_error_t *
149 svn_stream_skip(svn_stream_t *stream, apr_size_t len)
150 {
151   if (stream->skip_fn == NULL)
152     return svn_error_trace(
153             skip_default_handler(stream->baton, len, stream->read_fn));
154
155   return svn_error_trace(stream->skip_fn(stream->baton, len));
156 }
157
158
159 svn_error_t *
160 svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)
161 {
162   SVN_ERR_ASSERT(stream->write_fn != NULL);
163   return svn_error_trace(stream->write_fn(stream->baton, data, len));
164 }
165
166
167 svn_error_t *
168 svn_stream_reset(svn_stream_t *stream)
169 {
170   return svn_error_trace(
171             svn_stream_seek(stream, NULL));
172 }
173
174 svn_boolean_t
175 svn_stream_supports_mark(svn_stream_t *stream)
176 {
177   return stream->mark_fn != NULL;
178 }
179
180 svn_error_t *
181 svn_stream_mark(svn_stream_t *stream, svn_stream_mark_t **mark,
182                 apr_pool_t *pool)
183 {
184   if (stream->mark_fn == NULL)
185     return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
186
187   return svn_error_trace(stream->mark_fn(stream->baton, mark, pool));
188 }
189
190 svn_error_t *
191 svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark)
192 {
193   if (stream->seek_fn == NULL)
194     return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
195
196   return svn_error_trace(stream->seek_fn(stream->baton, mark));
197 }
198
199 svn_boolean_t
200 svn_stream__is_buffered(svn_stream_t *stream)
201 {
202   if (stream->is_buffered_fn == NULL)
203     return FALSE;
204
205   return stream->is_buffered_fn(stream->baton);
206 }
207
208 svn_error_t *
209 svn_stream_close(svn_stream_t *stream)
210 {
211   if (stream->close_fn == NULL)
212     return SVN_NO_ERROR;
213   return svn_error_trace(stream->close_fn(stream->baton));
214 }
215
216 svn_error_t *
217 svn_stream_puts(svn_stream_t *stream,
218                 const char *str)
219 {
220   apr_size_t len;
221   len = strlen(str);
222   return svn_error_trace(svn_stream_write(stream, str, &len));
223 }
224
225 svn_error_t *
226 svn_stream_printf(svn_stream_t *stream,
227                   apr_pool_t *pool,
228                   const char *fmt,
229                   ...)
230 {
231   const char *message;
232   va_list ap;
233
234   va_start(ap, fmt);
235   message = apr_pvsprintf(pool, fmt, ap);
236   va_end(ap);
237
238   return svn_error_trace(svn_stream_puts(stream, message));
239 }
240
241
242 svn_error_t *
243 svn_stream_printf_from_utf8(svn_stream_t *stream,
244                             const char *encoding,
245                             apr_pool_t *pool,
246                             const char *fmt,
247                             ...)
248 {
249   const char *message, *translated;
250   va_list ap;
251
252   va_start(ap, fmt);
253   message = apr_pvsprintf(pool, fmt, ap);
254   va_end(ap);
255
256   SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,
257                                         pool));
258
259   return svn_error_trace(svn_stream_puts(stream, translated));
260 }
261
262 /* Size that 90% of the lines we encounter will be not longer than.
263    used by stream_readline_bytewise() and stream_readline_chunky().
264  */
265 #define LINE_CHUNK_SIZE 80
266
267 /* Guts of svn_stream_readline().
268  * Returns the line read from STREAM in *STRINGBUF, and indicates
269  * end-of-file in *EOF.  If DETECT_EOL is TRUE, the end-of-line indicator
270  * is detected automatically and returned in *EOL.
271  * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line
272  * indicator.  STRINGBUF is allocated in POOL. */
273 static svn_error_t *
274 stream_readline_bytewise(svn_stringbuf_t **stringbuf,
275                          svn_boolean_t *eof,
276                          const char *eol,
277                          svn_stream_t *stream,
278                          apr_pool_t *pool)
279 {
280   svn_stringbuf_t *str;
281   apr_size_t numbytes;
282   const char *match;
283   char c;
284
285   /* Since we're reading one character at a time, let's at least
286      optimize for the 90% case.  90% of the time, we can avoid the
287      stringbuf ever having to realloc() itself if we start it out at
288      80 chars.  */
289   str = svn_stringbuf_create_ensure(LINE_CHUNK_SIZE, pool);
290
291   /* Read into STR up to and including the next EOL sequence. */
292   match = eol;
293   while (*match)
294     {
295       numbytes = 1;
296       SVN_ERR(svn_stream_read(stream, &c, &numbytes));
297       if (numbytes != 1)
298         {
299           /* a 'short' read means the stream has run out. */
300           *eof = TRUE;
301           *stringbuf = str;
302           return SVN_NO_ERROR;
303         }
304
305       if (c == *match)
306         match++;
307       else
308         match = eol;
309
310       svn_stringbuf_appendbyte(str, c);
311     }
312
313   *eof = FALSE;
314   svn_stringbuf_chop(str, match - eol);
315   *stringbuf = str;
316
317   return SVN_NO_ERROR;
318 }
319
320 static svn_error_t *
321 stream_readline_chunky(svn_stringbuf_t **stringbuf,
322                        svn_boolean_t *eof,
323                        const char *eol,
324                        svn_stream_t *stream,
325                        apr_pool_t *pool)
326 {
327   /* Read larger chunks of data at once into this buffer and scan
328    * that for EOL. A good chunk size should be about 80 chars since
329    * most text lines will be shorter. However, don't use a much
330    * larger value because filling the buffer from the stream takes
331    * time as well.
332    */
333   char buffer[LINE_CHUNK_SIZE+1];
334
335   /* variables */
336   svn_stream_mark_t *mark;
337   apr_size_t numbytes;
338   const char *eol_pos;
339   apr_size_t total_parsed = 0;
340
341   /* invariant for this call */
342   const size_t eol_len = strlen(eol);
343
344   /* Remember the line start so this plus the line length will be
345    * the position to move to at the end of this function.
346    */
347   SVN_ERR(svn_stream_mark(stream, &mark, pool));
348
349   /* Read the first chunk. */
350   numbytes = LINE_CHUNK_SIZE;
351   SVN_ERR(svn_stream_read(stream, buffer, &numbytes));
352   buffer[numbytes] = '\0';
353
354   /* Look for the EOL in this first chunk. If we find it, we are done here.
355    */
356   eol_pos = strstr(buffer, eol);
357   if (eol_pos != NULL)
358     {
359       *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool);
360       total_parsed = eol_pos - buffer + eol_len;
361     }
362   else if (numbytes < LINE_CHUNK_SIZE)
363     {
364       /* We hit EOF but not EOL.
365        */
366       *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool);
367       *eof = TRUE;
368       return SVN_NO_ERROR;
369      }
370   else
371     {
372       /* A larger buffer for the string is needed. */
373       svn_stringbuf_t *str;
374       str = svn_stringbuf_create_ensure(2*LINE_CHUNK_SIZE, pool);
375       svn_stringbuf_appendbytes(str, buffer, numbytes);
376       *stringbuf = str;
377
378       /* Loop reading chunks until an EOL was found. If we hit EOF, fall
379        * back to the standard implementation. */
380       do
381       {
382         /* Append the next chunk to the string read so far.
383          */
384         svn_stringbuf_ensure(str, str->len + LINE_CHUNK_SIZE);
385         numbytes = LINE_CHUNK_SIZE;
386         SVN_ERR(svn_stream_read(stream, str->data + str->len, &numbytes));
387         str->len += numbytes;
388         str->data[str->len] = '\0';
389
390         /* Look for the EOL in the new data plus the last part of the
391          * previous chunk because the EOL may span over the boundary
392          * between both chunks.
393          */
394         eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol);
395
396         if ((numbytes < LINE_CHUNK_SIZE) && (eol_pos == NULL))
397         {
398           /* We hit EOF instead of EOL. */
399           *eof = TRUE;
400           return SVN_NO_ERROR;
401         }
402       }
403       while (eol_pos == NULL);
404
405       /* Number of bytes we actually consumed (i.e. line + EOF).
406        * We need to "return" the rest to the stream by moving its
407        * read pointer.
408        */
409       total_parsed = eol_pos - str->data + eol_len;
410
411       /* Terminate the string at the EOL postion and return it. */
412       str->len = eol_pos - str->data;
413       str->data[str->len] = 0;
414     }
415
416   /* Move the stream read pointer to the first position behind the EOL.
417    */
418   SVN_ERR(svn_stream_seek(stream, mark));
419   return svn_error_trace(svn_stream_skip(stream, total_parsed));
420 }
421
422 /* Guts of svn_stream_readline().
423  * Returns the line read from STREAM in *STRINGBUF, and indicates
424  * end-of-file in *EOF.  EOL must point to the desired end-of-line
425  * indicator.  STRINGBUF is allocated in POOL. */
426 static svn_error_t *
427 stream_readline(svn_stringbuf_t **stringbuf,
428                 svn_boolean_t *eof,
429                 const char *eol,
430                 svn_stream_t *stream,
431                 apr_pool_t *pool)
432 {
433   *eof = FALSE;
434
435   /* Often, we operate on APR file or string-based streams and know what
436    * EOL we are looking for. Optimize that common case.
437    */
438   if (svn_stream_supports_mark(stream) &&
439       svn_stream__is_buffered(stream))
440     {
441       /* We can efficiently read chunks speculatively and reposition the
442        * stream pointer to the end of the line once we found that.
443        */
444       SVN_ERR(stream_readline_chunky(stringbuf,
445                                      eof,
446                                      eol,
447                                      stream,
448                                      pool));
449     }
450   else
451     {
452       /* Use the standard byte-byte implementation.
453        */
454       SVN_ERR(stream_readline_bytewise(stringbuf,
455                                        eof,
456                                        eol,
457                                        stream,
458                                        pool));
459     }
460
461   return SVN_NO_ERROR;
462 }
463
464 svn_error_t *
465 svn_stream_readline(svn_stream_t *stream,
466                     svn_stringbuf_t **stringbuf,
467                     const char *eol,
468                     svn_boolean_t *eof,
469                     apr_pool_t *pool)
470 {
471   return svn_error_trace(stream_readline(stringbuf, eof, eol, stream,
472                                          pool));
473 }
474
475 svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
476                               svn_cancel_func_t cancel_func,
477                               void *cancel_baton,
478                               apr_pool_t *scratch_pool)
479 {
480   char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
481   svn_error_t *err;
482   svn_error_t *err2;
483
484   /* Read and write chunks until we get a short read, indicating the
485      end of the stream.  (We can't get a short write without an
486      associated error.) */
487   while (1)
488     {
489       apr_size_t len = SVN__STREAM_CHUNK_SIZE;
490
491       if (cancel_func)
492         {
493           err = cancel_func(cancel_baton);
494           if (err)
495              break;
496         }
497
498       err = svn_stream_read(from, buf, &len);
499       if (err)
500          break;
501
502       if (len > 0)
503         err = svn_stream_write(to, buf, &len);
504
505       if (err || (len != SVN__STREAM_CHUNK_SIZE))
506           break;
507     }
508
509   err2 = svn_error_compose_create(svn_stream_close(from),
510                                   svn_stream_close(to));
511
512   return svn_error_compose_create(err, err2);
513 }
514
515 svn_error_t *
516 svn_stream_contents_same2(svn_boolean_t *same,
517                           svn_stream_t *stream1,
518                           svn_stream_t *stream2,
519                           apr_pool_t *pool)
520 {
521   char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
522   char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
523   apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;
524   apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;
525   svn_error_t *err = NULL;
526
527   *same = TRUE;  /* assume TRUE, until disproved below */
528   while (bytes_read1 == SVN__STREAM_CHUNK_SIZE
529          && bytes_read2 == SVN__STREAM_CHUNK_SIZE)
530     {
531       err = svn_stream_read(stream1, buf1, &bytes_read1);
532       if (err)
533         break;
534       err = svn_stream_read(stream2, buf2, &bytes_read2);
535       if (err)
536         break;
537
538       if ((bytes_read1 != bytes_read2)
539           || (memcmp(buf1, buf2, bytes_read1)))
540         {
541           *same = FALSE;
542           break;
543         }
544     }
545
546   return svn_error_compose_create(err,
547                                   svn_error_compose_create(
548                                     svn_stream_close(stream1),
549                                     svn_stream_close(stream2)));
550 }
551
552 \f
553 /*** Stream implementation utilities ***/
554
555 /* Skip data from any stream by reading and simply discarding it. */
556 static svn_error_t *
557 skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn)
558 {
559   apr_size_t bytes_read = 1;
560   char buffer[4096];
561   apr_size_t to_read = len;
562
563   while ((to_read > 0) && (bytes_read > 0))
564     {
565       bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read;
566       SVN_ERR(read_fn(baton, buffer, &bytes_read));
567       to_read -= bytes_read;
568     }
569
570   return SVN_NO_ERROR;
571 }
572
573
574 \f
575 /*** Generic readable empty stream ***/
576
577 static svn_error_t *
578 read_handler_empty(void *baton, char *buffer, apr_size_t *len)
579 {
580   *len = 0;
581   return SVN_NO_ERROR;
582 }
583
584 static svn_error_t *
585 write_handler_empty(void *baton, const char *data, apr_size_t *len)
586 {
587   return SVN_NO_ERROR;
588 }
589
590 static svn_error_t *
591 mark_handler_empty(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
592 {
593   *mark = NULL; /* Seek to start of stream marker */
594   return SVN_NO_ERROR;
595 }
596
597 static svn_error_t *
598 seek_handler_empty(void *baton, const svn_stream_mark_t *mark)
599 {
600   return SVN_NO_ERROR;
601 }
602
603 static svn_boolean_t
604 is_buffered_handler_empty(void *baton)
605 {
606   return FALSE;
607 }
608
609
610 svn_stream_t *
611 svn_stream_empty(apr_pool_t *pool)
612 {
613   svn_stream_t *stream;
614
615   stream = svn_stream_create(NULL, pool);
616   svn_stream_set_read(stream, read_handler_empty);
617   svn_stream_set_write(stream, write_handler_empty);
618   svn_stream_set_mark(stream, mark_handler_empty);
619   svn_stream_set_seek(stream, seek_handler_empty);
620   svn_stream__set_is_buffered(stream, is_buffered_handler_empty);
621   return stream;
622 }
623
624
625 \f
626 /*** Stream duplication support ***/
627 struct baton_tee {
628   svn_stream_t *out1;
629   svn_stream_t *out2;
630 };
631
632
633 static svn_error_t *
634 write_handler_tee(void *baton, const char *data, apr_size_t *len)
635 {
636   struct baton_tee *bt = baton;
637
638   SVN_ERR(svn_stream_write(bt->out1, data, len));
639   SVN_ERR(svn_stream_write(bt->out2, data, len));
640
641   return SVN_NO_ERROR;
642 }
643
644
645 static svn_error_t *
646 close_handler_tee(void *baton)
647 {
648   struct baton_tee *bt = baton;
649
650   SVN_ERR(svn_stream_close(bt->out1));
651   SVN_ERR(svn_stream_close(bt->out2));
652
653   return SVN_NO_ERROR;
654 }
655
656
657 svn_stream_t *
658 svn_stream_tee(svn_stream_t *out1,
659                svn_stream_t *out2,
660                apr_pool_t *pool)
661 {
662   struct baton_tee *baton;
663   svn_stream_t *stream;
664
665   if (out1 == NULL)
666     return out2;
667
668   if (out2 == NULL)
669     return out1;
670
671   baton = apr_palloc(pool, sizeof(*baton));
672   baton->out1 = out1;
673   baton->out2 = out2;
674   stream = svn_stream_create(baton, pool);
675   svn_stream_set_write(stream, write_handler_tee);
676   svn_stream_set_close(stream, close_handler_tee);
677
678   return stream;
679 }
680
681
682 \f
683 /*** Ownership detaching stream ***/
684
685 static svn_error_t *
686 read_handler_disown(void *baton, char *buffer, apr_size_t *len)
687 {
688   return svn_error_trace(svn_stream_read(baton, buffer, len));
689 }
690
691 static svn_error_t *
692 skip_handler_disown(void *baton, apr_size_t len)
693 {
694   return svn_error_trace(svn_stream_skip(baton, len));
695 }
696
697 static svn_error_t *
698 write_handler_disown(void *baton, const char *buffer, apr_size_t *len)
699 {
700   return svn_error_trace(svn_stream_write(baton, buffer, len));
701 }
702
703 static svn_error_t *
704 mark_handler_disown(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
705 {
706   return svn_error_trace(svn_stream_mark(baton, mark, pool));
707 }
708
709 static svn_error_t *
710 seek_handler_disown(void *baton, const svn_stream_mark_t *mark)
711 {
712   return svn_error_trace(svn_stream_seek(baton, mark));
713 }
714
715 static svn_boolean_t
716 is_buffered_handler_disown(void *baton)
717 {
718   return svn_stream__is_buffered(baton);
719 }
720
721 svn_stream_t *
722 svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
723 {
724   svn_stream_t *s = svn_stream_create(stream, pool);
725
726   svn_stream_set_read(s, read_handler_disown);
727   svn_stream_set_skip(s, skip_handler_disown);
728   svn_stream_set_write(s, write_handler_disown);
729   svn_stream_set_mark(s, mark_handler_disown);
730   svn_stream_set_seek(s, seek_handler_disown);
731   svn_stream__set_is_buffered(s, is_buffered_handler_disown);
732
733   return s;
734 }
735
736
737 \f
738 /*** Generic stream for APR files ***/
739 struct baton_apr {
740   apr_file_t *file;
741   apr_pool_t *pool;
742 };
743
744 /* svn_stream_mark_t for streams backed by APR files. */
745 struct mark_apr {
746   apr_off_t off;
747 };
748
749 static svn_error_t *
750 read_handler_apr(void *baton, char *buffer, apr_size_t *len)
751 {
752   struct baton_apr *btn = baton;
753   svn_error_t *err;
754   svn_boolean_t eof;
755
756   if (*len == 1)
757     {
758       err = svn_io_file_getc(buffer, btn->file, btn->pool);
759       if (err)
760         {
761           *len = 0;
762           if (APR_STATUS_IS_EOF(err->apr_err))
763             {
764               svn_error_clear(err);
765               err = SVN_NO_ERROR;
766             }
767         }
768     }
769   else
770     err = svn_io_file_read_full2(btn->file, buffer, *len, len,
771                                  &eof, btn->pool);
772
773   return svn_error_trace(err);
774 }
775
776 static svn_error_t *
777 skip_handler_apr(void *baton, apr_size_t len)
778 {
779   struct baton_apr *btn = baton;
780   apr_off_t offset = len;
781
782   return svn_error_trace(
783             svn_io_file_seek(btn->file, APR_CUR, &offset, btn->pool));
784 }
785
786 static svn_error_t *
787 write_handler_apr(void *baton, const char *data, apr_size_t *len)
788 {
789   struct baton_apr *btn = baton;
790   svn_error_t *err;
791
792   if (*len == 1)
793     {
794       err = svn_io_file_putc(*data, btn->file, btn->pool);
795       if (err)
796         *len = 0;
797     }
798   else
799     err = svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
800
801   return svn_error_trace(err);
802 }
803
804 static svn_error_t *
805 close_handler_apr(void *baton)
806 {
807   struct baton_apr *btn = baton;
808
809   return svn_error_trace(svn_io_file_close(btn->file, btn->pool));
810 }
811
812 static svn_error_t *
813 mark_handler_apr(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
814 {
815   struct baton_apr *btn = baton;
816   struct mark_apr *mark_apr;
817
818   mark_apr = apr_palloc(pool, sizeof(*mark_apr));
819   mark_apr->off = 0;
820   SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &mark_apr->off, btn->pool));
821   *mark = (svn_stream_mark_t *)mark_apr;
822   return SVN_NO_ERROR;
823 }
824
825 static svn_error_t *
826 seek_handler_apr(void *baton, const svn_stream_mark_t *mark)
827 {
828   struct baton_apr *btn = baton;
829   apr_off_t offset = (mark != NULL) ? ((const struct mark_apr *)mark)->off : 0;
830
831   SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool));
832
833   return SVN_NO_ERROR;
834 }
835
836 static svn_boolean_t
837 is_buffered_handler_apr(void *baton)
838 {
839   struct baton_apr *btn = baton;
840   return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0;
841 }
842
843 svn_error_t *
844 svn_stream_open_readonly(svn_stream_t **stream,
845                          const char *path,
846                          apr_pool_t *result_pool,
847                          apr_pool_t *scratch_pool)
848 {
849   apr_file_t *file;
850
851   SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
852                            APR_OS_DEFAULT, result_pool));
853   *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
854
855   return SVN_NO_ERROR;
856 }
857
858
859 svn_error_t *
860 svn_stream_open_writable(svn_stream_t **stream,
861                          const char *path,
862                          apr_pool_t *result_pool,
863                          apr_pool_t *scratch_pool)
864 {
865   apr_file_t *file;
866
867   SVN_ERR(svn_io_file_open(&file, path,
868                            APR_WRITE
869                              | APR_BUFFERED
870                              | APR_CREATE
871                              | APR_EXCL,
872                            APR_OS_DEFAULT, result_pool));
873   *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
874
875   return SVN_NO_ERROR;
876 }
877
878
879 svn_error_t *
880 svn_stream_open_unique(svn_stream_t **stream,
881                        const char **temp_path,
882                        const char *dirpath,
883                        svn_io_file_del_t delete_when,
884                        apr_pool_t *result_pool,
885                        apr_pool_t *scratch_pool)
886 {
887   apr_file_t *file;
888
889   SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath,
890                                    delete_when, result_pool, scratch_pool));
891   *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
892
893   return SVN_NO_ERROR;
894 }
895
896
897 svn_stream_t *
898 svn_stream_from_aprfile2(apr_file_t *file,
899                          svn_boolean_t disown,
900                          apr_pool_t *pool)
901 {
902   struct baton_apr *baton;
903   svn_stream_t *stream;
904
905   if (file == NULL)
906     return svn_stream_empty(pool);
907
908   baton = apr_palloc(pool, sizeof(*baton));
909   baton->file = file;
910   baton->pool = pool;
911   stream = svn_stream_create(baton, pool);
912   svn_stream_set_read(stream, read_handler_apr);
913   svn_stream_set_write(stream, write_handler_apr);
914   svn_stream_set_skip(stream, skip_handler_apr);
915   svn_stream_set_mark(stream, mark_handler_apr);
916   svn_stream_set_seek(stream, seek_handler_apr);
917   svn_stream__set_is_buffered(stream, is_buffered_handler_apr);
918   stream->file = file;
919
920   if (! disown)
921     svn_stream_set_close(stream, close_handler_apr);
922
923   return stream;
924 }
925
926 apr_file_t *
927 svn_stream__aprfile(svn_stream_t *stream)
928 {
929   return stream->file;
930 }
931
932 \f
933 /* Compressed stream support */
934
935 #define ZBUFFER_SIZE 4096       /* The size of the buffer the
936                                    compressed stream uses to read from
937                                    the substream. Basically an
938                                    arbitrary value, picked to be about
939                                    page-sized. */
940
941 struct zbaton {
942   z_stream *in;                 /* compressed stream for reading */
943   z_stream *out;                /* compressed stream for writing */
944   svn_read_fn_t read;           /* substream's read function */
945   svn_write_fn_t write;         /* substream's write function */
946   svn_close_fn_t close;         /* substream's close function */
947   void *read_buffer;            /* buffer   used   for  reading   from
948                                    substream */
949   int read_flush;               /* what flush mode to use while
950                                    reading */
951   apr_pool_t *pool;             /* The pool this baton is allocated
952                                    on */
953   void *subbaton;               /* The substream's baton */
954 };
955
956 /* zlib alloc function. opaque is the pool we need. */
957 static voidpf
958 zalloc(voidpf opaque, uInt items, uInt size)
959 {
960   apr_pool_t *pool = opaque;
961
962   return apr_palloc(pool, items * size);
963 }
964
965 /* zlib free function */
966 static void
967 zfree(voidpf opaque, voidpf address)
968 {
969   /* Empty, since we allocate on the pool */
970 }
971
972 /* Helper function to figure out the sync mode */
973 static svn_error_t *
974 read_helper_gz(svn_read_fn_t read_fn,
975                void *baton,
976                char *buffer,
977                uInt *len, int *zflush)
978 {
979   uInt orig_len = *len;
980
981   /* There's no reason this value should grow bigger than the range of
982      uInt, but Subversion's API requires apr_size_t. */
983   apr_size_t apr_len = (apr_size_t) *len;
984
985   SVN_ERR((*read_fn)(baton, buffer, &apr_len));
986
987   /* Type cast back to uInt type that zlib uses.  On LP64 platforms
988      apr_size_t will be bigger than uInt. */
989   *len = (uInt) apr_len;
990
991   /* I wanted to use Z_FINISH here, but we need to know our buffer is
992      big enough */
993   *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;
994
995   return SVN_NO_ERROR;
996 }
997
998 /* Handle reading from a compressed stream */
999 static svn_error_t *
1000 read_handler_gz(void *baton, char *buffer, apr_size_t *len)
1001 {
1002   struct zbaton *btn = baton;
1003   int zerr;
1004
1005   if (btn->in == NULL)
1006     {
1007       btn->in = apr_palloc(btn->pool, sizeof(z_stream));
1008       btn->in->zalloc = zalloc;
1009       btn->in->zfree = zfree;
1010       btn->in->opaque = btn->pool;
1011       btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);
1012       btn->in->next_in = btn->read_buffer;
1013       btn->in->avail_in = ZBUFFER_SIZE;
1014
1015       SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
1016                              &btn->in->avail_in, &btn->read_flush));
1017
1018       zerr = inflateInit(btn->in);
1019       SVN_ERR(svn_error__wrap_zlib(zerr, "inflateInit", btn->in->msg));
1020     }
1021
1022   btn->in->next_out = (Bytef *) buffer;
1023   btn->in->avail_out = (uInt) *len;
1024
1025   while (btn->in->avail_out > 0)
1026     {
1027       if (btn->in->avail_in <= 0)
1028         {
1029           btn->in->avail_in = ZBUFFER_SIZE;
1030           btn->in->next_in = btn->read_buffer;
1031           SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
1032                                  &btn->in->avail_in, &btn->read_flush));
1033         }
1034
1035       /* Short read means underlying stream has run out. */
1036       if (btn->in->avail_in == 0)
1037         {
1038           *len = 0;
1039           return SVN_NO_ERROR;
1040         }
1041
1042       zerr = inflate(btn->in, btn->read_flush);
1043       if (zerr == Z_STREAM_END)
1044         break;
1045       else if (zerr != Z_OK)
1046         return svn_error_trace(svn_error__wrap_zlib(zerr, "inflate",
1047                                                     btn->in->msg));
1048     }
1049
1050   *len -= btn->in->avail_out;
1051   return SVN_NO_ERROR;
1052 }
1053
1054 /* Compress data and write it to the substream */
1055 static svn_error_t *
1056 write_handler_gz(void *baton, const char *buffer, apr_size_t *len)
1057 {
1058   struct zbaton *btn = baton;
1059   apr_pool_t *subpool;
1060   void *write_buf;
1061   apr_size_t buf_size, write_len;
1062   int zerr;
1063
1064   if (btn->out == NULL)
1065     {
1066       btn->out = apr_palloc(btn->pool, sizeof(z_stream));
1067       btn->out->zalloc = zalloc;
1068       btn->out->zfree = zfree;
1069       btn->out->opaque =  btn->pool;
1070
1071       zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);
1072       SVN_ERR(svn_error__wrap_zlib(zerr, "deflateInit", btn->out->msg));
1073     }
1074
1075   /* The largest buffer we should need is 0.1% larger than the
1076      compressed data, + 12 bytes. This info comes from zlib.h.  */
1077   buf_size = *len + (*len / 1000) + 13;
1078   subpool = svn_pool_create(btn->pool);
1079   write_buf = apr_palloc(subpool, buf_size);
1080
1081   btn->out->next_in = (Bytef *) buffer;  /* Casting away const! */
1082   btn->out->avail_in = (uInt) *len;
1083
1084   while (btn->out->avail_in > 0)
1085     {
1086       btn->out->next_out = write_buf;
1087       btn->out->avail_out = (uInt) buf_size;
1088
1089       zerr = deflate(btn->out, Z_NO_FLUSH);
1090       SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg));
1091       write_len = buf_size - btn->out->avail_out;
1092       if (write_len > 0)
1093         SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len));
1094     }
1095
1096   svn_pool_destroy(subpool);
1097
1098   return SVN_NO_ERROR;
1099 }
1100
1101 /* Handle flushing and closing the stream */
1102 static svn_error_t *
1103 close_handler_gz(void *baton)
1104 {
1105   struct zbaton *btn = baton;
1106   int zerr;
1107
1108   if (btn->in != NULL)
1109     {
1110       zerr = inflateEnd(btn->in);
1111       SVN_ERR(svn_error__wrap_zlib(zerr, "inflateEnd", btn->in->msg));
1112     }
1113
1114   if (btn->out != NULL)
1115     {
1116       void *buf;
1117       apr_size_t write_len;
1118
1119       buf = apr_palloc(btn->pool, ZBUFFER_SIZE);
1120
1121       while (TRUE)
1122         {
1123           btn->out->next_out = buf;
1124           btn->out->avail_out = ZBUFFER_SIZE;
1125
1126           zerr = deflate(btn->out, Z_FINISH);
1127           if (zerr != Z_STREAM_END && zerr != Z_OK)
1128             return svn_error_trace(svn_error__wrap_zlib(zerr, "deflate",
1129                                                         btn->out->msg));
1130           write_len = ZBUFFER_SIZE - btn->out->avail_out;
1131           if (write_len > 0)
1132             SVN_ERR(btn->write(btn->subbaton, buf, &write_len));
1133           if (zerr == Z_STREAM_END)
1134             break;
1135         }
1136
1137       zerr = deflateEnd(btn->out);
1138       SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg));
1139     }
1140
1141   if (btn->close != NULL)
1142     return svn_error_trace(btn->close(btn->subbaton));
1143   else
1144     return SVN_NO_ERROR;
1145 }
1146
1147
1148 svn_stream_t *
1149 svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)
1150 {
1151   struct svn_stream_t *zstream;
1152   struct zbaton *baton;
1153
1154   assert(stream != NULL);
1155
1156   baton = apr_palloc(pool, sizeof(*baton));
1157   baton->in = baton->out = NULL;
1158   baton->read = stream->read_fn;
1159   baton->write = stream->write_fn;
1160   baton->close = stream->close_fn;
1161   baton->subbaton = stream->baton;
1162   baton->pool = pool;
1163   baton->read_buffer = NULL;
1164   baton->read_flush = Z_SYNC_FLUSH;
1165
1166   zstream = svn_stream_create(baton, pool);
1167   svn_stream_set_read(zstream, read_handler_gz);
1168   svn_stream_set_write(zstream, write_handler_gz);
1169   svn_stream_set_close(zstream, close_handler_gz);
1170
1171   return zstream;
1172 }
1173
1174 \f
1175 /* Checksummed stream support */
1176
1177 struct checksum_stream_baton
1178 {
1179   svn_checksum_ctx_t *read_ctx, *write_ctx;
1180   svn_checksum_t **read_checksum;  /* Output value. */
1181   svn_checksum_t **write_checksum;  /* Output value. */
1182   svn_stream_t *proxy;
1183
1184   /* True if more data should be read when closing the stream. */
1185   svn_boolean_t read_more;
1186
1187   /* Pool to allocate read buffer and output values from. */
1188   apr_pool_t *pool;
1189 };
1190
1191 static svn_error_t *
1192 read_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1193 {
1194   struct checksum_stream_baton *btn = baton;
1195   apr_size_t saved_len = *len;
1196
1197   SVN_ERR(svn_stream_read(btn->proxy, buffer, len));
1198
1199   if (btn->read_checksum)
1200     SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1201
1202   if (saved_len != *len)
1203     btn->read_more = FALSE;
1204
1205   return SVN_NO_ERROR;
1206 }
1207
1208
1209 static svn_error_t *
1210 write_handler_checksum(void *baton, const char *buffer, apr_size_t *len)
1211 {
1212   struct checksum_stream_baton *btn = baton;
1213
1214   if (btn->write_checksum && *len > 0)
1215     SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len));
1216
1217   return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1218 }
1219
1220
1221 static svn_error_t *
1222 close_handler_checksum(void *baton)
1223 {
1224   struct checksum_stream_baton *btn = baton;
1225
1226   /* If we're supposed to drain the stream, do so before finalizing the
1227      checksum. */
1228   if (btn->read_more)
1229     {
1230       char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);
1231       apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1232
1233       do
1234         {
1235           SVN_ERR(read_handler_checksum(baton, buf, &len));
1236         }
1237       while (btn->read_more);
1238     }
1239
1240   if (btn->read_ctx)
1241     SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool));
1242
1243   if (btn->write_ctx)
1244     SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool));
1245
1246   return svn_error_trace(svn_stream_close(btn->proxy));
1247 }
1248
1249
1250 svn_stream_t *
1251 svn_stream_checksummed2(svn_stream_t *stream,
1252                         svn_checksum_t **read_checksum,
1253                         svn_checksum_t **write_checksum,
1254                         svn_checksum_kind_t checksum_kind,
1255                         svn_boolean_t read_all,
1256                         apr_pool_t *pool)
1257 {
1258   svn_stream_t *s;
1259   struct checksum_stream_baton *baton;
1260
1261   if (read_checksum == NULL && write_checksum == NULL)
1262     return stream;
1263
1264   baton = apr_palloc(pool, sizeof(*baton));
1265   if (read_checksum)
1266     baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1267   else
1268     baton->read_ctx = NULL;
1269
1270   if (write_checksum)
1271     baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1272   else
1273     baton->write_ctx = NULL;
1274
1275   baton->read_checksum = read_checksum;
1276   baton->write_checksum = write_checksum;
1277   baton->proxy = stream;
1278   baton->read_more = read_all;
1279   baton->pool = pool;
1280
1281   s = svn_stream_create(baton, pool);
1282   svn_stream_set_read(s, read_handler_checksum);
1283   svn_stream_set_write(s, write_handler_checksum);
1284   svn_stream_set_close(s, close_handler_checksum);
1285   return s;
1286 }
1287
1288 struct md5_stream_baton
1289 {
1290   const unsigned char **read_digest;
1291   const unsigned char **write_digest;
1292   svn_checksum_t *read_checksum;
1293   svn_checksum_t *write_checksum;
1294   svn_stream_t *proxy;
1295   apr_pool_t *pool;
1296 };
1297
1298 static svn_error_t *
1299 read_handler_md5(void *baton, char *buffer, apr_size_t *len)
1300 {
1301   struct md5_stream_baton *btn = baton;
1302   return svn_error_trace(svn_stream_read(btn->proxy, buffer, len));
1303 }
1304
1305 static svn_error_t *
1306 skip_handler_md5(void *baton, apr_size_t len)
1307 {
1308   struct md5_stream_baton *btn = baton;
1309   return svn_error_trace(svn_stream_skip(btn->proxy, len));
1310 }
1311
1312 static svn_error_t *
1313 write_handler_md5(void *baton, const char *buffer, apr_size_t *len)
1314 {
1315   struct md5_stream_baton *btn = baton;
1316   return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1317 }
1318
1319 static svn_error_t *
1320 close_handler_md5(void *baton)
1321 {
1322   struct md5_stream_baton *btn = baton;
1323
1324   SVN_ERR(svn_stream_close(btn->proxy));
1325
1326   if (btn->read_digest)
1327     *btn->read_digest
1328       = apr_pmemdup(btn->pool, btn->read_checksum->digest,
1329                     APR_MD5_DIGESTSIZE);
1330
1331   if (btn->write_digest)
1332     *btn->write_digest
1333       = apr_pmemdup(btn->pool, btn->write_checksum->digest,
1334                     APR_MD5_DIGESTSIZE);
1335
1336   return SVN_NO_ERROR;
1337 }
1338
1339
1340 svn_stream_t *
1341 svn_stream_checksummed(svn_stream_t *stream,
1342                        const unsigned char **read_digest,
1343                        const unsigned char **write_digest,
1344                        svn_boolean_t read_all,
1345                        apr_pool_t *pool)
1346 {
1347   svn_stream_t *s;
1348   struct md5_stream_baton *baton;
1349
1350   if (! read_digest && ! write_digest)
1351     return stream;
1352
1353   baton = apr_palloc(pool, sizeof(*baton));
1354   baton->read_digest = read_digest;
1355   baton->write_digest = write_digest;
1356   baton->pool = pool;
1357
1358   /* Set BATON->proxy to a stream that will fill in BATON->read_checksum
1359    * and BATON->write_checksum (if we want them) when it is closed. */
1360   baton->proxy
1361     = svn_stream_checksummed2(stream,
1362                               read_digest ? &baton->read_checksum : NULL,
1363                               write_digest ? &baton->write_checksum : NULL,
1364                               svn_checksum_md5,
1365                               read_all, pool);
1366
1367   /* Create a stream that will forward its read/write/close operations to
1368    * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we
1369    * want them) after it closes BATON->proxy. */
1370   s = svn_stream_create(baton, pool);
1371   svn_stream_set_read(s, read_handler_md5);
1372   svn_stream_set_skip(s, skip_handler_md5);
1373   svn_stream_set_write(s, write_handler_md5);
1374   svn_stream_set_close(s, close_handler_md5);
1375   return s;
1376 }
1377
1378
1379
1380 \f
1381 /* Miscellaneous stream functions. */
1382 struct stringbuf_stream_baton
1383 {
1384   svn_stringbuf_t *str;
1385   apr_size_t amt_read;
1386 };
1387
1388 /* svn_stream_mark_t for streams backed by stringbufs. */
1389 struct stringbuf_stream_mark {
1390     apr_size_t pos;
1391 };
1392
1393 static svn_error_t *
1394 read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
1395 {
1396   struct stringbuf_stream_baton *btn = baton;
1397   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1398
1399   *len = (*len > left_to_read) ? left_to_read : *len;
1400   memcpy(buffer, btn->str->data + btn->amt_read, *len);
1401   btn->amt_read += *len;
1402   return SVN_NO_ERROR;
1403 }
1404
1405 static svn_error_t *
1406 skip_handler_stringbuf(void *baton, apr_size_t len)
1407 {
1408   struct stringbuf_stream_baton *btn = baton;
1409   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1410
1411   len = (len > left_to_read) ? left_to_read : len;
1412   btn->amt_read += len;
1413   return SVN_NO_ERROR;
1414 }
1415
1416 static svn_error_t *
1417 write_handler_stringbuf(void *baton, const char *data, apr_size_t *len)
1418 {
1419   struct stringbuf_stream_baton *btn = baton;
1420
1421   svn_stringbuf_appendbytes(btn->str, data, *len);
1422   return SVN_NO_ERROR;
1423 }
1424
1425 static svn_error_t *
1426 mark_handler_stringbuf(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1427 {
1428   struct stringbuf_stream_baton *btn;
1429   struct stringbuf_stream_mark *stringbuf_stream_mark;
1430
1431   btn = baton;
1432
1433   stringbuf_stream_mark = apr_palloc(pool, sizeof(*stringbuf_stream_mark));
1434   stringbuf_stream_mark->pos = btn->amt_read;
1435   *mark = (svn_stream_mark_t *)stringbuf_stream_mark;
1436   return SVN_NO_ERROR;
1437 }
1438
1439 static svn_error_t *
1440 seek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark)
1441 {
1442   struct stringbuf_stream_baton *btn = baton;
1443
1444   if (mark != NULL)
1445     {
1446       const struct stringbuf_stream_mark *stringbuf_stream_mark;
1447
1448       stringbuf_stream_mark = (const struct stringbuf_stream_mark *)mark;
1449       btn->amt_read = stringbuf_stream_mark->pos;
1450     }
1451   else
1452     btn->amt_read = 0;
1453
1454   return SVN_NO_ERROR;
1455 }
1456
1457 static svn_boolean_t
1458 is_buffered_handler_stringbuf(void *baton)
1459 {
1460   return TRUE;
1461 }
1462
1463 svn_stream_t *
1464 svn_stream_from_stringbuf(svn_stringbuf_t *str,
1465                           apr_pool_t *pool)
1466 {
1467   svn_stream_t *stream;
1468   struct stringbuf_stream_baton *baton;
1469
1470   if (! str)
1471     return svn_stream_empty(pool);
1472
1473   baton = apr_palloc(pool, sizeof(*baton));
1474   baton->str = str;
1475   baton->amt_read = 0;
1476   stream = svn_stream_create(baton, pool);
1477   svn_stream_set_read(stream, read_handler_stringbuf);
1478   svn_stream_set_skip(stream, skip_handler_stringbuf);
1479   svn_stream_set_write(stream, write_handler_stringbuf);
1480   svn_stream_set_mark(stream, mark_handler_stringbuf);
1481   svn_stream_set_seek(stream, seek_handler_stringbuf);
1482   svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf);
1483   return stream;
1484 }
1485
1486 struct string_stream_baton
1487 {
1488   const svn_string_t *str;
1489   apr_size_t amt_read;
1490 };
1491
1492 /* svn_stream_mark_t for streams backed by stringbufs. */
1493 struct string_stream_mark {
1494     apr_size_t pos;
1495 };
1496
1497 static svn_error_t *
1498 read_handler_string(void *baton, char *buffer, apr_size_t *len)
1499 {
1500   struct string_stream_baton *btn = baton;
1501   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1502
1503   *len = (*len > left_to_read) ? left_to_read : *len;
1504   memcpy(buffer, btn->str->data + btn->amt_read, *len);
1505   btn->amt_read += *len;
1506   return SVN_NO_ERROR;
1507 }
1508
1509 static svn_error_t *
1510 mark_handler_string(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1511 {
1512   struct string_stream_baton *btn;
1513   struct string_stream_mark *marker;
1514
1515   btn = baton;
1516
1517   marker = apr_palloc(pool, sizeof(*marker));
1518   marker->pos = btn->amt_read;
1519   *mark = (svn_stream_mark_t *)marker;
1520   return SVN_NO_ERROR;
1521 }
1522
1523 static svn_error_t *
1524 seek_handler_string(void *baton, const svn_stream_mark_t *mark)
1525 {
1526   struct string_stream_baton *btn = baton;
1527
1528   if (mark != NULL)
1529     {
1530       const struct string_stream_mark *marker;
1531
1532       marker = (const struct string_stream_mark *)mark;
1533       btn->amt_read = marker->pos;
1534     }
1535   else
1536     btn->amt_read = 0;
1537
1538   return SVN_NO_ERROR;
1539 }
1540
1541 static svn_error_t *
1542 skip_handler_string(void *baton, apr_size_t len)
1543 {
1544   struct string_stream_baton *btn = baton;
1545   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1546
1547   len = (len > left_to_read) ? left_to_read : len;
1548   btn->amt_read += len;
1549   return SVN_NO_ERROR;
1550 }
1551
1552 static svn_boolean_t
1553 is_buffered_handler_string(void *baton)
1554 {
1555   return TRUE;
1556 }
1557
1558 svn_stream_t *
1559 svn_stream_from_string(const svn_string_t *str,
1560                        apr_pool_t *pool)
1561 {
1562   svn_stream_t *stream;
1563   struct string_stream_baton *baton;
1564
1565   if (! str)
1566     return svn_stream_empty(pool);
1567
1568   baton = apr_palloc(pool, sizeof(*baton));
1569   baton->str = str;
1570   baton->amt_read = 0;
1571   stream = svn_stream_create(baton, pool);
1572   svn_stream_set_read(stream, read_handler_string);
1573   svn_stream_set_mark(stream, mark_handler_string);
1574   svn_stream_set_seek(stream, seek_handler_string);
1575   svn_stream_set_skip(stream, skip_handler_string);
1576   svn_stream__set_is_buffered(stream, is_buffered_handler_string);
1577   return stream;
1578 }
1579
1580
1581 svn_error_t *
1582 svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool)
1583 {
1584   apr_file_t *stdin_file;
1585   apr_status_t apr_err;
1586
1587   apr_err = apr_file_open_stdin(&stdin_file, pool);
1588   if (apr_err)
1589     return svn_error_wrap_apr(apr_err, "Can't open stdin");
1590
1591   *in = svn_stream_from_aprfile2(stdin_file, TRUE, pool);
1592
1593   return SVN_NO_ERROR;
1594 }
1595
1596
1597 svn_error_t *
1598 svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
1599 {
1600   apr_file_t *stdout_file;
1601   apr_status_t apr_err;
1602
1603   apr_err = apr_file_open_stdout(&stdout_file, pool);
1604   if (apr_err)
1605     return svn_error_wrap_apr(apr_err, "Can't open stdout");
1606
1607   *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool);
1608
1609   return SVN_NO_ERROR;
1610 }
1611
1612
1613 svn_error_t *
1614 svn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool)
1615 {
1616   apr_file_t *stderr_file;
1617   apr_status_t apr_err;
1618
1619   apr_err = apr_file_open_stderr(&stderr_file, pool);
1620   if (apr_err)
1621     return svn_error_wrap_apr(apr_err, "Can't open stderr");
1622
1623   *err = svn_stream_from_aprfile2(stderr_file, TRUE, pool);
1624
1625   return SVN_NO_ERROR;
1626 }
1627
1628
1629 svn_error_t *
1630 svn_string_from_stream(svn_string_t **result,
1631                        svn_stream_t *stream,
1632                        apr_pool_t *result_pool,
1633                        apr_pool_t *scratch_pool)
1634 {
1635   svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,
1636                                                       result_pool);
1637   char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
1638
1639   while (1)
1640     {
1641       apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1642
1643       SVN_ERR(svn_stream_read(stream, buffer, &len));
1644       svn_stringbuf_appendbytes(work, buffer, len);
1645
1646       if (len < SVN__STREAM_CHUNK_SIZE)
1647         break;
1648     }
1649
1650   SVN_ERR(svn_stream_close(stream));
1651
1652   *result = apr_palloc(result_pool, sizeof(**result));
1653   (*result)->data = work->data;
1654   (*result)->len = work->len;
1655
1656   return SVN_NO_ERROR;
1657 }
1658
1659
1660 /* These are somewhat arbirary, if we ever get good empirical data as to
1661    actually valid values, feel free to update them. */
1662 #define BUFFER_BLOCK_SIZE 1024
1663 #define BUFFER_MAX_SIZE 100000
1664
1665 svn_stream_t *
1666 svn_stream_buffered(apr_pool_t *result_pool)
1667 {
1668   return svn_stream__from_spillbuf(BUFFER_BLOCK_SIZE, BUFFER_MAX_SIZE,
1669                                    result_pool);
1670 }
1671
1672
1673 \f
1674 /*** Lazyopen Streams ***/
1675
1676 /* Custom baton for lazyopen-style wrapper streams. */
1677 typedef struct lazyopen_baton_t {
1678
1679   /* Callback function and baton for opening the wrapped stream. */
1680   svn_stream_lazyopen_func_t open_func;
1681   void *open_baton;
1682
1683   /* The wrapped stream, or NULL if the stream hasn't yet been
1684      opened. */
1685   svn_stream_t *real_stream;
1686   apr_pool_t *pool;
1687
1688   /* Whether to open the wrapped stream on a close call. */
1689   svn_boolean_t open_on_close;
1690
1691 } lazyopen_baton_t;
1692
1693
1694 /* Use B->open_func/baton to create and set B->real_stream iff it
1695    isn't already set. */
1696 static svn_error_t *
1697 lazyopen_if_unopened(lazyopen_baton_t *b)
1698 {
1699   if (b->real_stream == NULL)
1700     {
1701       svn_stream_t *stream;
1702       apr_pool_t *scratch_pool = svn_pool_create(b->pool);
1703
1704       SVN_ERR(b->open_func(&stream, b->open_baton,
1705                            b->pool, scratch_pool));
1706
1707       svn_pool_destroy(scratch_pool);
1708
1709       b->real_stream = stream;
1710     }
1711
1712   return SVN_NO_ERROR;
1713 }
1714
1715 /* Implements svn_read_fn_t */
1716 static svn_error_t *
1717 read_handler_lazyopen(void *baton,
1718                       char *buffer,
1719                       apr_size_t *len)
1720 {
1721   lazyopen_baton_t *b = baton;
1722
1723   SVN_ERR(lazyopen_if_unopened(b));
1724   SVN_ERR(svn_stream_read(b->real_stream, buffer, len));
1725
1726   return SVN_NO_ERROR;
1727 }
1728
1729 /* Implements svn_stream_skip_fn_t */
1730 static svn_error_t *
1731 skip_handler_lazyopen(void *baton,
1732                       apr_size_t len)
1733 {
1734   lazyopen_baton_t *b = baton;
1735
1736   SVN_ERR(lazyopen_if_unopened(b));
1737   SVN_ERR(svn_stream_skip(b->real_stream, len));
1738
1739   return SVN_NO_ERROR;
1740 }
1741
1742 /* Implements svn_write_fn_t */
1743 static svn_error_t *
1744 write_handler_lazyopen(void *baton,
1745                        const char *data,
1746                        apr_size_t *len)
1747 {
1748   lazyopen_baton_t *b = baton;
1749
1750   SVN_ERR(lazyopen_if_unopened(b));
1751   SVN_ERR(svn_stream_write(b->real_stream, data, len));
1752
1753   return SVN_NO_ERROR;
1754 }
1755
1756 /* Implements svn_close_fn_t */
1757 static svn_error_t *
1758 close_handler_lazyopen(void *baton)
1759 {
1760   lazyopen_baton_t *b = baton;
1761
1762   if (b->open_on_close)
1763     SVN_ERR(lazyopen_if_unopened(b));
1764   if (b->real_stream)
1765     SVN_ERR(svn_stream_close(b->real_stream));
1766
1767   return SVN_NO_ERROR;
1768 }
1769
1770 /* Implements svn_stream_mark_fn_t */
1771 static svn_error_t *
1772 mark_handler_lazyopen(void *baton,
1773                       svn_stream_mark_t **mark,
1774                       apr_pool_t *pool)
1775 {
1776   lazyopen_baton_t *b = baton;
1777
1778   SVN_ERR(lazyopen_if_unopened(b));
1779   SVN_ERR(svn_stream_mark(b->real_stream, mark, pool));
1780
1781   return SVN_NO_ERROR;
1782 }
1783
1784 /* Implements svn_stream_seek_fn_t */
1785 static svn_error_t *
1786 seek_handler_lazyopen(void *baton,
1787                       const svn_stream_mark_t *mark)
1788 {
1789   lazyopen_baton_t *b = baton;
1790
1791   SVN_ERR(lazyopen_if_unopened(b));
1792   SVN_ERR(svn_stream_seek(b->real_stream, mark));
1793
1794   return SVN_NO_ERROR;
1795 }
1796
1797 /* Implements svn_stream__is_buffered_fn_t */
1798 static svn_boolean_t
1799 is_buffered_lazyopen(void *baton)
1800 {
1801   lazyopen_baton_t *b = baton;
1802
1803   /* No lazy open as we cannot handle an open error. */
1804   if (!b->real_stream)
1805     return FALSE;
1806
1807   return svn_stream__is_buffered(b->real_stream);
1808 }
1809
1810 svn_stream_t *
1811 svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,
1812                            void *open_baton,
1813                            svn_boolean_t open_on_close,
1814                            apr_pool_t *result_pool)
1815 {
1816   lazyopen_baton_t *lob = apr_pcalloc(result_pool, sizeof(*lob));
1817   svn_stream_t *stream;
1818
1819   lob->open_func = open_func;
1820   lob->open_baton = open_baton;
1821   lob->real_stream = NULL;
1822   lob->pool = result_pool;
1823   lob->open_on_close = open_on_close;
1824
1825   stream = svn_stream_create(lob, result_pool);
1826   svn_stream_set_read(stream, read_handler_lazyopen);
1827   svn_stream_set_skip(stream, skip_handler_lazyopen);
1828   svn_stream_set_write(stream, write_handler_lazyopen);
1829   svn_stream_set_close(stream, close_handler_lazyopen);
1830   svn_stream_set_mark(stream, mark_handler_lazyopen);
1831   svn_stream_set_seek(stream, seek_handler_lazyopen);
1832   svn_stream__set_is_buffered(stream, is_buffered_lazyopen);
1833
1834   return stream;
1835 }