]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/stream.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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_poll.h>
33 #include <apr_portable.h>
34
35 #include <zlib.h>
36
37 #include "svn_pools.h"
38 #include "svn_io.h"
39 #include "svn_error.h"
40 #include "svn_string.h"
41 #include "svn_utf.h"
42 #include "svn_checksum.h"
43 #include "svn_path.h"
44 #include "svn_private_config.h"
45 #include "private/svn_atomic.h"
46 #include "private/svn_error_private.h"
47 #include "private/svn_eol_private.h"
48 #include "private/svn_io_private.h"
49 #include "private/svn_subr_private.h"
50 #include "private/svn_utf_private.h"
51
52
53 struct svn_stream_t {
54   void *baton;
55   svn_read_fn_t read_fn;
56   svn_read_fn_t read_full_fn;
57   svn_stream_skip_fn_t skip_fn;
58   svn_write_fn_t write_fn;
59   svn_close_fn_t close_fn;
60   svn_stream_mark_fn_t mark_fn;
61   svn_stream_seek_fn_t seek_fn;
62   svn_stream_data_available_fn_t data_available_fn;
63   svn_stream__is_buffered_fn_t is_buffered_fn;
64   apr_file_t *file; /* Maybe NULL */
65 };
66
67 \f
68 /*** Forward declarations. ***/
69
70 static svn_error_t *
71 skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn);
72
73 \f
74 /*** Generic streams. ***/
75
76 svn_stream_t *
77 svn_stream_create(void *baton, apr_pool_t *pool)
78 {
79   svn_stream_t *stream;
80
81   stream = apr_pcalloc(pool, sizeof(*stream));
82   stream->baton = baton;
83   return stream;
84 }
85
86
87 void
88 svn_stream_set_baton(svn_stream_t *stream, void *baton)
89 {
90   stream->baton = baton;
91 }
92
93
94 void
95 svn_stream_set_read2(svn_stream_t *stream,
96                      svn_read_fn_t read_fn,
97                      svn_read_fn_t read_full_fn)
98 {
99   stream->read_fn = read_fn;
100   stream->read_full_fn = read_full_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_data_available(svn_stream_t *stream,
135                               svn_stream_data_available_fn_t data_available_fn)
136 {
137   stream->data_available_fn = data_available_fn;
138 }
139
140 void
141 svn_stream__set_is_buffered(svn_stream_t *stream,
142                             svn_stream__is_buffered_fn_t is_buffered_fn)
143 {
144   stream->is_buffered_fn = is_buffered_fn;
145 }
146
147 /* Standard implementation for svn_stream_read_full() based on
148    multiple svn_stream_read2() calls (in separate function to make
149    it more likely for svn_stream_read_full to be inlined) */
150 static svn_error_t *
151 full_read_fallback(svn_stream_t *stream, char *buffer, apr_size_t *len)
152 {
153   apr_size_t remaining = *len;
154   while (remaining > 0)
155     {
156       apr_size_t length = remaining;
157       SVN_ERR(svn_stream_read2(stream, buffer, &length));
158
159       if (length == 0)
160         {
161           *len -= remaining;
162           return SVN_NO_ERROR;
163         }
164
165       remaining -= length;
166       buffer += length;
167     }
168
169   return SVN_NO_ERROR;
170 }
171
172 svn_boolean_t
173 svn_stream_supports_partial_read(svn_stream_t *stream)
174 {
175   return stream->read_fn != NULL;
176 }
177
178 svn_error_t *
179 svn_stream_read2(svn_stream_t *stream, char *buffer, apr_size_t *len)
180 {
181   if (stream->read_fn == NULL)
182     return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
183
184   return svn_error_trace(stream->read_fn(stream->baton, buffer, len));
185 }
186
187 svn_error_t *
188 svn_stream_read_full(svn_stream_t *stream, char *buffer, apr_size_t *len)
189 {
190   if (stream->read_full_fn == NULL)
191     return svn_error_trace(full_read_fallback(stream, buffer, len));
192
193   return svn_error_trace(stream->read_full_fn(stream->baton, buffer, len));
194 }
195
196 svn_error_t *
197 svn_stream_skip(svn_stream_t *stream, apr_size_t len)
198 {
199   if (stream->skip_fn == NULL)
200     return svn_error_trace(
201             skip_default_handler(stream->baton, len, stream->read_full_fn));
202
203   return svn_error_trace(stream->skip_fn(stream->baton, len));
204 }
205
206
207 svn_error_t *
208 svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)
209 {
210   if (stream->write_fn == NULL)
211     return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
212
213   return svn_error_trace(stream->write_fn(stream->baton, data, len));
214 }
215
216
217 svn_error_t *
218 svn_stream_reset(svn_stream_t *stream)
219 {
220   return svn_error_trace(
221             svn_stream_seek(stream, NULL));
222 }
223
224 svn_boolean_t
225 svn_stream_supports_mark(svn_stream_t *stream)
226 {
227   return stream->mark_fn != NULL;
228 }
229
230 svn_error_t *
231 svn_stream_mark(svn_stream_t *stream, svn_stream_mark_t **mark,
232                 apr_pool_t *pool)
233 {
234   if (stream->mark_fn == NULL)
235     return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
236
237   return svn_error_trace(stream->mark_fn(stream->baton, mark, pool));
238 }
239
240 svn_error_t *
241 svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark)
242 {
243   if (stream->seek_fn == NULL)
244     return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
245
246   return svn_error_trace(stream->seek_fn(stream->baton, mark));
247 }
248
249 svn_error_t *
250 svn_stream_data_available(svn_stream_t *stream,
251                           svn_boolean_t *data_available)
252 {
253   if (stream->data_available_fn == NULL)
254     return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
255
256   return svn_error_trace(stream->data_available_fn(stream->baton,
257                                                    data_available));
258 }
259
260 svn_boolean_t
261 svn_stream__is_buffered(svn_stream_t *stream)
262 {
263   if (stream->is_buffered_fn == NULL)
264     return FALSE;
265
266   return stream->is_buffered_fn(stream->baton);
267 }
268
269 svn_error_t *
270 svn_stream_close(svn_stream_t *stream)
271 {
272   if (stream->close_fn == NULL)
273     return SVN_NO_ERROR;
274   return svn_error_trace(stream->close_fn(stream->baton));
275 }
276
277 svn_error_t *
278 svn_stream_puts(svn_stream_t *stream,
279                 const char *str)
280 {
281   apr_size_t len;
282   len = strlen(str);
283   return svn_error_trace(svn_stream_write(stream, str, &len));
284 }
285
286 svn_error_t *
287 svn_stream_printf(svn_stream_t *stream,
288                   apr_pool_t *pool,
289                   const char *fmt,
290                   ...)
291 {
292   const char *message;
293   va_list ap;
294
295   va_start(ap, fmt);
296   message = apr_pvsprintf(pool, fmt, ap);
297   va_end(ap);
298
299   return svn_error_trace(svn_stream_puts(stream, message));
300 }
301
302
303 svn_error_t *
304 svn_stream_printf_from_utf8(svn_stream_t *stream,
305                             const char *encoding,
306                             apr_pool_t *pool,
307                             const char *fmt,
308                             ...)
309 {
310   const char *message, *translated;
311   va_list ap;
312
313   va_start(ap, fmt);
314   message = apr_pvsprintf(pool, fmt, ap);
315   va_end(ap);
316
317   SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,
318                                         pool));
319
320   return svn_error_trace(svn_stream_puts(stream, translated));
321 }
322
323 /* Guts of svn_stream_readline().
324  * Returns the line read from STREAM in *STRINGBUF, and indicates
325  * end-of-file in *EOF.  If DETECT_EOL is TRUE, the end-of-line indicator
326  * is detected automatically and returned in *EOL.
327  * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line
328  * indicator.  STRINGBUF is allocated in POOL. */
329 static svn_error_t *
330 stream_readline_bytewise(svn_stringbuf_t **stringbuf,
331                          svn_boolean_t *eof,
332                          const char *eol,
333                          svn_stream_t *stream,
334                          apr_pool_t *pool)
335 {
336   svn_stringbuf_t *str;
337   apr_size_t numbytes;
338   const char *match;
339   char c;
340
341   /* Since we're reading one character at a time, let's at least
342      optimize for the 90% case.  90% of the time, we can avoid the
343      stringbuf ever having to realloc() itself if we start it out at
344      80 chars.  */
345   str = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool);
346
347   /* Read into STR up to and including the next EOL sequence. */
348   match = eol;
349   while (*match)
350     {
351       numbytes = 1;
352       SVN_ERR(svn_stream_read_full(stream, &c, &numbytes));
353       if (numbytes != 1)
354         {
355           /* a 'short' read means the stream has run out. */
356           *eof = TRUE;
357           *stringbuf = str;
358           return SVN_NO_ERROR;
359         }
360
361       if (c == *match)
362         match++;
363       else
364         match = eol;
365
366       svn_stringbuf_appendbyte(str, c);
367     }
368
369   *eof = FALSE;
370   svn_stringbuf_chop(str, match - eol);
371   *stringbuf = str;
372
373   return SVN_NO_ERROR;
374 }
375
376 static svn_error_t *
377 stream_readline_chunky(svn_stringbuf_t **stringbuf,
378                        svn_boolean_t *eof,
379                        const char *eol,
380                        svn_stream_t *stream,
381                        apr_pool_t *pool)
382 {
383   /* Read larger chunks of data at once into this buffer and scan
384    * that for EOL. A good chunk size should be about 80 chars since
385    * most text lines will be shorter. However, don't use a much
386    * larger value because filling the buffer from the stream takes
387    * time as well.
388    */
389   char buffer[SVN__LINE_CHUNK_SIZE+1];
390
391   /* variables */
392   svn_stream_mark_t *mark;
393   apr_size_t numbytes;
394   const char *eol_pos;
395   apr_size_t total_parsed = 0;
396
397   /* invariant for this call */
398   const size_t eol_len = strlen(eol);
399
400   /* Remember the line start so this plus the line length will be
401    * the position to move to at the end of this function.
402    */
403   SVN_ERR(svn_stream_mark(stream, &mark, pool));
404
405   /* Read the first chunk. */
406   numbytes = SVN__LINE_CHUNK_SIZE;
407   SVN_ERR(svn_stream_read_full(stream, buffer, &numbytes));
408   buffer[numbytes] = '\0';
409
410   /* Look for the EOL in this first chunk. If we find it, we are done here.
411    */
412   eol_pos = strstr(buffer, eol);
413   if (eol_pos != NULL)
414     {
415       *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool);
416       total_parsed = eol_pos - buffer + eol_len;
417     }
418   else if (numbytes < SVN__LINE_CHUNK_SIZE)
419     {
420       /* We hit EOF but not EOL.
421        */
422       *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool);
423       *eof = TRUE;
424       return SVN_NO_ERROR;
425      }
426   else
427     {
428       /* A larger buffer for the string is needed. */
429       svn_stringbuf_t *str;
430       str = svn_stringbuf_create_ensure(2*SVN__LINE_CHUNK_SIZE, pool);
431       svn_stringbuf_appendbytes(str, buffer, numbytes);
432       *stringbuf = str;
433
434       /* Loop reading chunks until an EOL was found. If we hit EOF, fall
435        * back to the standard implementation. */
436       do
437       {
438         /* Append the next chunk to the string read so far.
439          */
440         svn_stringbuf_ensure(str, str->len + SVN__LINE_CHUNK_SIZE);
441         numbytes = SVN__LINE_CHUNK_SIZE;
442         SVN_ERR(svn_stream_read_full(stream, str->data + str->len, &numbytes));
443         str->len += numbytes;
444         str->data[str->len] = '\0';
445
446         /* Look for the EOL in the new data plus the last part of the
447          * previous chunk because the EOL may span over the boundary
448          * between both chunks.
449          */
450         eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol);
451
452         if ((numbytes < SVN__LINE_CHUNK_SIZE) && (eol_pos == NULL))
453         {
454           /* We hit EOF instead of EOL. */
455           *eof = TRUE;
456           return SVN_NO_ERROR;
457         }
458       }
459       while (eol_pos == NULL);
460
461       /* Number of bytes we actually consumed (i.e. line + EOF).
462        * We need to "return" the rest to the stream by moving its
463        * read pointer.
464        */
465       total_parsed = eol_pos - str->data + eol_len;
466
467       /* Terminate the string at the EOL postion and return it. */
468       str->len = eol_pos - str->data;
469       str->data[str->len] = 0;
470     }
471
472   /* Move the stream read pointer to the first position behind the EOL.
473    */
474   SVN_ERR(svn_stream_seek(stream, mark));
475   return svn_error_trace(svn_stream_skip(stream, total_parsed));
476 }
477
478 /* Guts of svn_stream_readline().
479  * Returns the line read from STREAM in *STRINGBUF, and indicates
480  * end-of-file in *EOF.  EOL must point to the desired end-of-line
481  * indicator.  STRINGBUF is allocated in POOL. */
482 static svn_error_t *
483 stream_readline(svn_stringbuf_t **stringbuf,
484                 svn_boolean_t *eof,
485                 const char *eol,
486                 svn_stream_t *stream,
487                 apr_pool_t *pool)
488 {
489   *eof = FALSE;
490
491   /* Often, we operate on APR file or string-based streams and know what
492    * EOL we are looking for. Optimize that common case.
493    */
494   if (svn_stream_supports_mark(stream) &&
495       svn_stream__is_buffered(stream))
496     {
497       /* We can efficiently read chunks speculatively and reposition the
498        * stream pointer to the end of the line once we found that.
499        */
500       SVN_ERR(stream_readline_chunky(stringbuf,
501                                      eof,
502                                      eol,
503                                      stream,
504                                      pool));
505     }
506   else
507     {
508       /* Use the standard byte-byte implementation.
509        */
510       SVN_ERR(stream_readline_bytewise(stringbuf,
511                                        eof,
512                                        eol,
513                                        stream,
514                                        pool));
515     }
516
517   return SVN_NO_ERROR;
518 }
519
520 svn_error_t *
521 svn_stream_readline(svn_stream_t *stream,
522                     svn_stringbuf_t **stringbuf,
523                     const char *eol,
524                     svn_boolean_t *eof,
525                     apr_pool_t *pool)
526 {
527   return svn_error_trace(stream_readline(stringbuf, eof, eol, stream,
528                                          pool));
529 }
530
531 svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
532                               svn_cancel_func_t cancel_func,
533                               void *cancel_baton,
534                               apr_pool_t *scratch_pool)
535 {
536   char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
537   svn_error_t *err;
538   svn_error_t *err2;
539
540   /* Read and write chunks until we get a short read, indicating the
541      end of the stream.  (We can't get a short write without an
542      associated error.) */
543   while (1)
544     {
545       apr_size_t len = SVN__STREAM_CHUNK_SIZE;
546
547       if (cancel_func)
548         {
549           err = cancel_func(cancel_baton);
550           if (err)
551              break;
552         }
553
554       err = svn_stream_read_full(from, buf, &len);
555       if (err)
556          break;
557
558       if (len > 0)
559         err = svn_stream_write(to, buf, &len);
560
561       if (err || (len != SVN__STREAM_CHUNK_SIZE))
562           break;
563     }
564
565   err2 = svn_error_compose_create(svn_stream_close(from),
566                                   svn_stream_close(to));
567
568   return svn_error_compose_create(err, err2);
569 }
570
571 svn_error_t *
572 svn_stream_contents_same2(svn_boolean_t *same,
573                           svn_stream_t *stream1,
574                           svn_stream_t *stream2,
575                           apr_pool_t *pool)
576 {
577   char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
578   char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
579   apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;
580   apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;
581   svn_error_t *err = NULL;
582
583   *same = TRUE;  /* assume TRUE, until disproved below */
584   while (bytes_read1 == SVN__STREAM_CHUNK_SIZE
585          && bytes_read2 == SVN__STREAM_CHUNK_SIZE)
586     {
587       err = svn_stream_read_full(stream1, buf1, &bytes_read1);
588       if (err)
589         break;
590       err = svn_stream_read_full(stream2, buf2, &bytes_read2);
591       if (err)
592         break;
593
594       if ((bytes_read1 != bytes_read2)
595           || (memcmp(buf1, buf2, bytes_read1)))
596         {
597           *same = FALSE;
598           break;
599         }
600     }
601
602   return svn_error_compose_create(err,
603                                   svn_error_compose_create(
604                                     svn_stream_close(stream1),
605                                     svn_stream_close(stream2)));
606 }
607
608 \f
609 /*** Stream implementation utilities ***/
610
611 /* Skip data from any stream by reading and simply discarding it. */
612 static svn_error_t *
613 skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn)
614 {
615   apr_size_t bytes_read = 1;
616   char buffer[4096];
617   apr_size_t to_read = len;
618
619   while ((to_read > 0) && (bytes_read > 0))
620     {
621       bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read;
622       SVN_ERR(read_full_fn(baton, buffer, &bytes_read));
623       to_read -= bytes_read;
624     }
625
626   return SVN_NO_ERROR;
627 }
628
629
630 \f
631 /*** Generic readable empty stream ***/
632
633 static svn_error_t *
634 read_handler_empty(void *baton, char *buffer, apr_size_t *len)
635 {
636   *len = 0;
637   return SVN_NO_ERROR;
638 }
639
640 static svn_error_t *
641 write_handler_empty(void *baton, const char *data, apr_size_t *len)
642 {
643   return SVN_NO_ERROR;
644 }
645
646 static svn_error_t *
647 mark_handler_empty(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
648 {
649   *mark = NULL; /* Seek to start of stream marker */
650   return SVN_NO_ERROR;
651 }
652
653 static svn_error_t *
654 seek_handler_empty(void *baton, const svn_stream_mark_t *mark)
655 {
656   return SVN_NO_ERROR;
657 }
658
659 static svn_boolean_t
660 is_buffered_handler_empty(void *baton)
661 {
662   return FALSE;
663 }
664
665
666 svn_stream_t *
667 svn_stream_empty(apr_pool_t *pool)
668 {
669   svn_stream_t *stream;
670
671   stream = svn_stream_create(NULL, pool);
672   svn_stream_set_read2(stream, read_handler_empty, read_handler_empty);
673   svn_stream_set_write(stream, write_handler_empty);
674   svn_stream_set_mark(stream, mark_handler_empty);
675   svn_stream_set_seek(stream, seek_handler_empty);
676   svn_stream__set_is_buffered(stream, is_buffered_handler_empty);
677   return stream;
678 }
679
680
681 \f
682 /*** Stream duplication support ***/
683 struct baton_tee {
684   svn_stream_t *out1;
685   svn_stream_t *out2;
686 };
687
688
689 static svn_error_t *
690 write_handler_tee(void *baton, const char *data, apr_size_t *len)
691 {
692   struct baton_tee *bt = baton;
693
694   SVN_ERR(svn_stream_write(bt->out1, data, len));
695   SVN_ERR(svn_stream_write(bt->out2, data, len));
696
697   return SVN_NO_ERROR;
698 }
699
700
701 static svn_error_t *
702 close_handler_tee(void *baton)
703 {
704   struct baton_tee *bt = baton;
705
706   SVN_ERR(svn_stream_close(bt->out1));
707   SVN_ERR(svn_stream_close(bt->out2));
708
709   return SVN_NO_ERROR;
710 }
711
712
713 svn_stream_t *
714 svn_stream_tee(svn_stream_t *out1,
715                svn_stream_t *out2,
716                apr_pool_t *pool)
717 {
718   struct baton_tee *baton;
719   svn_stream_t *stream;
720
721   if (out1 == NULL)
722     return out2;
723
724   if (out2 == NULL)
725     return out1;
726
727   baton = apr_palloc(pool, sizeof(*baton));
728   baton->out1 = out1;
729   baton->out2 = out2;
730   stream = svn_stream_create(baton, pool);
731   svn_stream_set_write(stream, write_handler_tee);
732   svn_stream_set_close(stream, close_handler_tee);
733
734   return stream;
735 }
736
737
738 \f
739 /*** Ownership detaching stream ***/
740
741 static svn_error_t *
742 read_handler_disown(void *baton, char *buffer, apr_size_t *len)
743 {
744   return svn_error_trace(svn_stream_read2(baton, buffer, len));
745 }
746
747 static svn_error_t *
748 read_full_handler_disown(void *baton, char *buffer, apr_size_t *len)
749 {
750   return svn_error_trace(svn_stream_read_full(baton, buffer, len));
751 }
752
753 static svn_error_t *
754 skip_handler_disown(void *baton, apr_size_t len)
755 {
756   return svn_error_trace(svn_stream_skip(baton, len));
757 }
758
759 static svn_error_t *
760 write_handler_disown(void *baton, const char *buffer, apr_size_t *len)
761 {
762   return svn_error_trace(svn_stream_write(baton, buffer, len));
763 }
764
765 static svn_error_t *
766 mark_handler_disown(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
767 {
768   return svn_error_trace(svn_stream_mark(baton, mark, pool));
769 }
770
771 static svn_error_t *
772 seek_handler_disown(void *baton, const svn_stream_mark_t *mark)
773 {
774   return svn_error_trace(svn_stream_seek(baton, mark));
775 }
776
777 static svn_error_t *
778 data_available_disown(void *baton, svn_boolean_t *data_available)
779 {
780   return svn_error_trace(svn_stream_data_available(baton, data_available));
781 }
782
783 static svn_boolean_t
784 is_buffered_handler_disown(void *baton)
785 {
786   return svn_stream__is_buffered(baton);
787 }
788
789 svn_stream_t *
790 svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
791 {
792   svn_stream_t *s = svn_stream_create(stream, pool);
793
794   svn_stream_set_read2(s, read_handler_disown, read_full_handler_disown);
795   svn_stream_set_skip(s, skip_handler_disown);
796   svn_stream_set_write(s, write_handler_disown);
797   svn_stream_set_mark(s, mark_handler_disown);
798   svn_stream_set_seek(s, seek_handler_disown);
799   svn_stream_set_data_available(s, data_available_disown);
800   svn_stream__set_is_buffered(s, is_buffered_handler_disown);
801
802   return s;
803 }
804
805
806 \f
807 /*** Generic stream for APR files ***/
808 struct baton_apr {
809   apr_file_t *file;
810   apr_pool_t *pool;
811 };
812
813 /* svn_stream_mark_t for streams backed by APR files. */
814 struct mark_apr {
815   apr_off_t off;
816 };
817
818 static svn_error_t *
819 read_handler_apr(void *baton, char *buffer, apr_size_t *len)
820 {
821   struct baton_apr *btn = baton;
822   svn_error_t *err;
823
824   if (*len == 1)
825     {
826       err = svn_io_file_getc(buffer, btn->file, btn->pool);
827       if (err)
828         {
829           *len = 0;
830           if (APR_STATUS_IS_EOF(err->apr_err))
831             {
832               svn_error_clear(err);
833               err = SVN_NO_ERROR;
834             }
835         }
836     }
837   else
838     {
839       err = svn_io_file_read(btn->file, buffer, len, btn->pool);
840       if (err && APR_STATUS_IS_EOF(err->apr_err))
841         {
842           svn_error_clear(err);
843           err = NULL;
844         }
845     }
846
847   return svn_error_trace(err);
848 }
849
850 static svn_error_t *
851 read_full_handler_apr(void *baton, char *buffer, apr_size_t *len)
852 {
853   struct baton_apr *btn = baton;
854   svn_error_t *err;
855   svn_boolean_t eof;
856
857   if (*len == 1)
858     {
859       err = svn_io_file_getc(buffer, btn->file, btn->pool);
860       if (err)
861         {
862           *len = 0;
863           if (APR_STATUS_IS_EOF(err->apr_err))
864             {
865               svn_error_clear(err);
866               err = SVN_NO_ERROR;
867             }
868         }
869     }
870   else
871     err = svn_io_file_read_full2(btn->file, buffer, *len, len,
872                                  &eof, btn->pool);
873
874   return svn_error_trace(err);
875 }
876
877 static svn_error_t *
878 skip_handler_apr(void *baton, apr_size_t len)
879 {
880   struct baton_apr *btn = baton;
881   apr_off_t offset = len;
882
883   return svn_error_trace(
884             svn_io_file_seek(btn->file, APR_CUR, &offset, btn->pool));
885 }
886
887 static svn_error_t *
888 write_handler_apr(void *baton, const char *data, apr_size_t *len)
889 {
890   struct baton_apr *btn = baton;
891   svn_error_t *err;
892
893   if (*len == 1)
894     {
895       err = svn_io_file_putc(*data, btn->file, btn->pool);
896       if (err)
897         *len = 0;
898     }
899   else
900     err = svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
901
902   return svn_error_trace(err);
903 }
904
905 static svn_error_t *
906 close_handler_apr(void *baton)
907 {
908   struct baton_apr *btn = baton;
909
910   return svn_error_trace(svn_io_file_close(btn->file, btn->pool));
911 }
912
913 static svn_error_t *
914 mark_handler_apr(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
915 {
916   struct baton_apr *btn = baton;
917   struct mark_apr *mark_apr;
918
919   mark_apr = apr_palloc(pool, sizeof(*mark_apr));
920   mark_apr->off = 0;
921   SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &mark_apr->off, btn->pool));
922   *mark = (svn_stream_mark_t *)mark_apr;
923   return SVN_NO_ERROR;
924 }
925
926 static svn_error_t *
927 seek_handler_apr(void *baton, const svn_stream_mark_t *mark)
928 {
929   struct baton_apr *btn = baton;
930   apr_off_t offset = (mark != NULL) ? ((const struct mark_apr *)mark)->off : 0;
931
932   SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool));
933
934   return SVN_NO_ERROR;
935 }
936
937 static svn_error_t *
938 data_available_handler_apr(void *baton, svn_boolean_t *data_available)
939 {
940   struct baton_apr *btn = baton;
941   apr_status_t status;
942 #if !defined(WIN32) || APR_FILES_AS_SOCKETS
943   apr_pollfd_t pfd;
944   int n;
945
946   pfd.desc_type = APR_POLL_FILE;
947   pfd.desc.f = btn->file;
948   pfd.p = btn->pool; /* If we had a scratch pool... Luckily apr doesn't
949                         store anything in this pool at this time */
950   pfd.reqevents = APR_POLLIN;
951
952   status = apr_poll(&pfd, 1, &n, 0);
953
954   if (status == APR_SUCCESS)
955     {
956       *data_available = (n > 0);
957       return SVN_NO_ERROR;
958     }
959   else if (APR_STATUS_IS_EOF(status) || APR_STATUS_IS_TIMEUP(status))
960     {
961       *data_available = FALSE;
962       return SVN_NO_ERROR;
963     }
964   else
965     {
966       return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED,
967                               svn_error_wrap_apr(
968                                   status,
969                                   _("Polling for available data on filestream "
970                                     "failed")),
971                               NULL);
972     }
973 #else
974   HANDLE h;
975   DWORD dwAvail;
976   status = apr_os_file_get(&h, btn->file);
977
978   if (status)
979     return svn_error_wrap_apr(status, NULL);
980
981   if (PeekNamedPipe(h, NULL, 0, NULL, &dwAvail, NULL))
982     {
983       *data_available = (dwAvail > 0);
984       return SVN_NO_ERROR;
985     }
986
987   return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED,
988                           svn_error_wrap_apr(apr_get_os_error(), NULL),
989                           _("Windows doesn't support polling on files"));
990 #endif
991 }
992
993 static svn_boolean_t
994 is_buffered_handler_apr(void *baton)
995 {
996   struct baton_apr *btn = baton;
997   return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0;
998 }
999
1000 svn_error_t *
1001 svn_stream_open_readonly(svn_stream_t **stream,
1002                          const char *path,
1003                          apr_pool_t *result_pool,
1004                          apr_pool_t *scratch_pool)
1005 {
1006   apr_file_t *file;
1007
1008   SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
1009                            APR_OS_DEFAULT, result_pool));
1010   *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1011
1012   return SVN_NO_ERROR;
1013 }
1014
1015
1016 svn_error_t *
1017 svn_stream_open_writable(svn_stream_t **stream,
1018                          const char *path,
1019                          apr_pool_t *result_pool,
1020                          apr_pool_t *scratch_pool)
1021 {
1022   apr_file_t *file;
1023
1024   SVN_ERR(svn_io_file_open(&file, path,
1025                            APR_WRITE
1026                              | APR_BUFFERED
1027                              | APR_CREATE
1028                              | APR_EXCL,
1029                            APR_OS_DEFAULT, result_pool));
1030   *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1031
1032   return SVN_NO_ERROR;
1033 }
1034
1035
1036 svn_error_t *
1037 svn_stream_open_unique(svn_stream_t **stream,
1038                        const char **temp_path,
1039                        const char *dirpath,
1040                        svn_io_file_del_t delete_when,
1041                        apr_pool_t *result_pool,
1042                        apr_pool_t *scratch_pool)
1043 {
1044   apr_file_t *file;
1045
1046   SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath,
1047                                    delete_when, result_pool, scratch_pool));
1048   *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1049
1050   return SVN_NO_ERROR;
1051 }
1052
1053
1054 /* Helper function that creates a stream from an APR file. */
1055 static svn_stream_t *
1056 make_stream_from_apr_file(apr_file_t *file,
1057                           svn_boolean_t disown,
1058                           svn_boolean_t supports_seek,
1059                           apr_pool_t *pool)
1060 {
1061   struct baton_apr *baton;
1062   svn_stream_t *stream;
1063
1064   if (file == NULL)
1065     return svn_stream_empty(pool);
1066
1067   baton = apr_palloc(pool, sizeof(*baton));
1068   baton->file = file;
1069   baton->pool = pool;
1070   stream = svn_stream_create(baton, pool);
1071   svn_stream_set_read2(stream, read_handler_apr, read_full_handler_apr);
1072   svn_stream_set_write(stream, write_handler_apr);
1073
1074   if (supports_seek)
1075     {
1076       svn_stream_set_skip(stream, skip_handler_apr);
1077       svn_stream_set_mark(stream, mark_handler_apr);
1078       svn_stream_set_seek(stream, seek_handler_apr);
1079     }
1080
1081   svn_stream_set_data_available(stream, data_available_handler_apr);
1082   svn_stream__set_is_buffered(stream, is_buffered_handler_apr);
1083   stream->file = file;
1084
1085   if (! disown)
1086     svn_stream_set_close(stream, close_handler_apr);
1087
1088   return stream;
1089 }
1090
1091 svn_stream_t *
1092 svn_stream_from_aprfile2(apr_file_t *file,
1093                          svn_boolean_t disown,
1094                          apr_pool_t *pool)
1095 {
1096   return make_stream_from_apr_file(file, disown, TRUE, pool);
1097 }
1098
1099 apr_file_t *
1100 svn_stream__aprfile(svn_stream_t *stream)
1101 {
1102   return stream->file;
1103 }
1104
1105 \f
1106 /* Compressed stream support */
1107
1108 #define ZBUFFER_SIZE 4096       /* The size of the buffer the
1109                                    compressed stream uses to read from
1110                                    the substream. Basically an
1111                                    arbitrary value, picked to be about
1112                                    page-sized. */
1113
1114 struct zbaton {
1115   z_stream *in;                 /* compressed stream for reading */
1116   z_stream *out;                /* compressed stream for writing */
1117   void *substream;              /* The substream */
1118   void *read_buffer;            /* buffer   used   for  reading   from
1119                                    substream */
1120   int read_flush;               /* what flush mode to use while
1121                                    reading */
1122   apr_pool_t *pool;             /* The pool this baton is allocated
1123                                    on */
1124 };
1125
1126 /* zlib alloc function. opaque is the pool we need. */
1127 static voidpf
1128 zalloc(voidpf opaque, uInt items, uInt size)
1129 {
1130   apr_pool_t *pool = opaque;
1131
1132   return apr_palloc(pool, items * size);
1133 }
1134
1135 /* zlib free function */
1136 static void
1137 zfree(voidpf opaque, voidpf address)
1138 {
1139   /* Empty, since we allocate on the pool */
1140 }
1141
1142 /* Helper function to figure out the sync mode */
1143 static svn_error_t *
1144 read_helper_gz(svn_stream_t *substream,
1145                char *buffer,
1146                uInt *len, int *zflush)
1147 {
1148   uInt orig_len = *len;
1149
1150   /* There's no reason this value should grow bigger than the range of
1151      uInt, but Subversion's API requires apr_size_t. */
1152   apr_size_t apr_len = (apr_size_t) *len;
1153
1154   SVN_ERR(svn_stream_read_full(substream, buffer, &apr_len));
1155
1156   /* Type cast back to uInt type that zlib uses.  On LP64 platforms
1157      apr_size_t will be bigger than uInt. */
1158   *len = (uInt) apr_len;
1159
1160   /* I wanted to use Z_FINISH here, but we need to know our buffer is
1161      big enough */
1162   *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;
1163
1164   return SVN_NO_ERROR;
1165 }
1166
1167 /* Handle reading from a compressed stream */
1168 static svn_error_t *
1169 read_handler_gz(void *baton, char *buffer, apr_size_t *len)
1170 {
1171   struct zbaton *btn = baton;
1172   int zerr;
1173
1174   if (btn->in == NULL)
1175     {
1176       btn->in = apr_palloc(btn->pool, sizeof(z_stream));
1177       btn->in->zalloc = zalloc;
1178       btn->in->zfree = zfree;
1179       btn->in->opaque = btn->pool;
1180       btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);
1181       btn->in->next_in = btn->read_buffer;
1182       btn->in->avail_in = ZBUFFER_SIZE;
1183
1184       SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer,
1185                              &btn->in->avail_in, &btn->read_flush));
1186
1187       zerr = inflateInit(btn->in);
1188       SVN_ERR(svn_error__wrap_zlib(zerr, "inflateInit", btn->in->msg));
1189     }
1190
1191   btn->in->next_out = (Bytef *) buffer;
1192   btn->in->avail_out = (uInt) *len;
1193
1194   while (btn->in->avail_out > 0)
1195     {
1196       if (btn->in->avail_in <= 0)
1197         {
1198           btn->in->avail_in = ZBUFFER_SIZE;
1199           btn->in->next_in = btn->read_buffer;
1200           SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer,
1201                                  &btn->in->avail_in, &btn->read_flush));
1202         }
1203
1204       /* Short read means underlying stream has run out. */
1205       if (btn->in->avail_in == 0)
1206         {
1207           *len = 0;
1208           return SVN_NO_ERROR;
1209         }
1210
1211       zerr = inflate(btn->in, btn->read_flush);
1212       if (zerr == Z_STREAM_END)
1213         break;
1214       else if (zerr != Z_OK)
1215         return svn_error_trace(svn_error__wrap_zlib(zerr, "inflate",
1216                                                     btn->in->msg));
1217     }
1218
1219   *len -= btn->in->avail_out;
1220   return SVN_NO_ERROR;
1221 }
1222
1223 /* Compress data and write it to the substream */
1224 static svn_error_t *
1225 write_handler_gz(void *baton, const char *buffer, apr_size_t *len)
1226 {
1227   struct zbaton *btn = baton;
1228   apr_pool_t *subpool;
1229   void *write_buf;
1230   apr_size_t buf_size, write_len;
1231   int zerr;
1232
1233   if (btn->out == NULL)
1234     {
1235       btn->out = apr_palloc(btn->pool, sizeof(z_stream));
1236       btn->out->zalloc = zalloc;
1237       btn->out->zfree = zfree;
1238       btn->out->opaque =  btn->pool;
1239
1240       zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);
1241       SVN_ERR(svn_error__wrap_zlib(zerr, "deflateInit", btn->out->msg));
1242     }
1243
1244   /* The largest buffer we should need is 0.1% larger than the
1245      compressed data, + 12 bytes. This info comes from zlib.h.  */
1246   buf_size = *len + (*len / 1000) + 13;
1247   subpool = svn_pool_create(btn->pool);
1248   write_buf = apr_palloc(subpool, buf_size);
1249
1250   btn->out->next_in = (Bytef *) buffer;  /* Casting away const! */
1251   btn->out->avail_in = (uInt) *len;
1252
1253   while (btn->out->avail_in > 0)
1254     {
1255       btn->out->next_out = write_buf;
1256       btn->out->avail_out = (uInt) buf_size;
1257
1258       zerr = deflate(btn->out, Z_NO_FLUSH);
1259       SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg));
1260       write_len = buf_size - btn->out->avail_out;
1261       if (write_len > 0)
1262         SVN_ERR(svn_stream_write(btn->substream, write_buf, &write_len));
1263     }
1264
1265   svn_pool_destroy(subpool);
1266
1267   return SVN_NO_ERROR;
1268 }
1269
1270 /* Handle flushing and closing the stream */
1271 static svn_error_t *
1272 close_handler_gz(void *baton)
1273 {
1274   struct zbaton *btn = baton;
1275   int zerr;
1276
1277   if (btn->in != NULL)
1278     {
1279       zerr = inflateEnd(btn->in);
1280       SVN_ERR(svn_error__wrap_zlib(zerr, "inflateEnd", btn->in->msg));
1281     }
1282
1283   if (btn->out != NULL)
1284     {
1285       void *buf;
1286       apr_size_t write_len;
1287
1288       buf = apr_palloc(btn->pool, ZBUFFER_SIZE);
1289
1290       while (TRUE)
1291         {
1292           btn->out->next_out = buf;
1293           btn->out->avail_out = ZBUFFER_SIZE;
1294
1295           zerr = deflate(btn->out, Z_FINISH);
1296           if (zerr != Z_STREAM_END && zerr != Z_OK)
1297             return svn_error_trace(svn_error__wrap_zlib(zerr, "deflate",
1298                                                         btn->out->msg));
1299           write_len = ZBUFFER_SIZE - btn->out->avail_out;
1300           if (write_len > 0)
1301             SVN_ERR(svn_stream_write(btn->substream, buf, &write_len));
1302           if (zerr == Z_STREAM_END)
1303             break;
1304         }
1305
1306       zerr = deflateEnd(btn->out);
1307       SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg));
1308     }
1309
1310   return svn_error_trace(svn_stream_close(btn->substream));
1311 }
1312
1313
1314 svn_stream_t *
1315 svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)
1316 {
1317   struct svn_stream_t *zstream;
1318   struct zbaton *baton;
1319
1320   assert(stream != NULL);
1321
1322   baton = apr_palloc(pool, sizeof(*baton));
1323   baton->in = baton->out = NULL;
1324   baton->substream = stream;
1325   baton->pool = pool;
1326   baton->read_buffer = NULL;
1327   baton->read_flush = Z_SYNC_FLUSH;
1328
1329   zstream = svn_stream_create(baton, pool);
1330   svn_stream_set_read2(zstream, NULL /* only full read support */,
1331                        read_handler_gz);
1332   svn_stream_set_write(zstream, write_handler_gz);
1333   svn_stream_set_close(zstream, close_handler_gz);
1334
1335   return zstream;
1336 }
1337
1338 \f
1339 /* Checksummed stream support */
1340
1341 struct checksum_stream_baton
1342 {
1343   svn_checksum_ctx_t *read_ctx, *write_ctx;
1344   svn_checksum_t **read_checksum;  /* Output value. */
1345   svn_checksum_t **write_checksum;  /* Output value. */
1346   svn_stream_t *proxy;
1347
1348   /* True if more data should be read when closing the stream. */
1349   svn_boolean_t read_more;
1350
1351   /* Pool to allocate read buffer and output values from. */
1352   apr_pool_t *pool;
1353 };
1354
1355 static svn_error_t *
1356 read_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1357 {
1358   struct checksum_stream_baton *btn = baton;
1359
1360   SVN_ERR(svn_stream_read2(btn->proxy, buffer, len));
1361
1362   if (btn->read_checksum)
1363     SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1364
1365   return SVN_NO_ERROR;
1366 }
1367
1368 static svn_error_t *
1369 read_full_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1370 {
1371   struct checksum_stream_baton *btn = baton;
1372   apr_size_t saved_len = *len;
1373
1374   SVN_ERR(svn_stream_read_full(btn->proxy, buffer, len));
1375
1376   if (btn->read_checksum)
1377     SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1378
1379   if (saved_len != *len)
1380     btn->read_more = FALSE;
1381
1382   return SVN_NO_ERROR;
1383 }
1384
1385
1386 static svn_error_t *
1387 write_handler_checksum(void *baton, const char *buffer, apr_size_t *len)
1388 {
1389   struct checksum_stream_baton *btn = baton;
1390
1391   if (btn->write_checksum && *len > 0)
1392     SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len));
1393
1394   return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1395 }
1396
1397 static svn_error_t *
1398 data_available_handler_checksum(void *baton, svn_boolean_t *data_available)
1399 {
1400   struct checksum_stream_baton *btn = baton;
1401
1402   return svn_error_trace(svn_stream_data_available(btn->proxy,
1403                                                    data_available));
1404 }
1405
1406 static svn_error_t *
1407 close_handler_checksum(void *baton)
1408 {
1409   struct checksum_stream_baton *btn = baton;
1410
1411   /* If we're supposed to drain the stream, do so before finalizing the
1412      checksum. */
1413   if (btn->read_more)
1414     {
1415       char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);
1416       apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1417
1418       do
1419         {
1420           SVN_ERR(read_full_handler_checksum(baton, buf, &len));
1421         }
1422       while (btn->read_more);
1423     }
1424
1425   if (btn->read_ctx)
1426     SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool));
1427
1428   if (btn->write_ctx)
1429     SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool));
1430
1431   return svn_error_trace(svn_stream_close(btn->proxy));
1432 }
1433
1434
1435 svn_stream_t *
1436 svn_stream_checksummed2(svn_stream_t *stream,
1437                         svn_checksum_t **read_checksum,
1438                         svn_checksum_t **write_checksum,
1439                         svn_checksum_kind_t checksum_kind,
1440                         svn_boolean_t read_all,
1441                         apr_pool_t *pool)
1442 {
1443   svn_stream_t *s;
1444   struct checksum_stream_baton *baton;
1445
1446   if (read_checksum == NULL && write_checksum == NULL)
1447     return stream;
1448
1449   baton = apr_palloc(pool, sizeof(*baton));
1450   if (read_checksum)
1451     baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1452   else
1453     baton->read_ctx = NULL;
1454
1455   if (write_checksum)
1456     baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1457   else
1458     baton->write_ctx = NULL;
1459
1460   baton->read_checksum = read_checksum;
1461   baton->write_checksum = write_checksum;
1462   baton->proxy = stream;
1463   baton->read_more = read_all;
1464   baton->pool = pool;
1465
1466   s = svn_stream_create(baton, pool);
1467   svn_stream_set_read2(s, read_handler_checksum, read_full_handler_checksum);
1468   svn_stream_set_write(s, write_handler_checksum);
1469   svn_stream_set_data_available(s, data_available_handler_checksum);
1470   svn_stream_set_close(s, close_handler_checksum);
1471   return s;
1472 }
1473
1474 /* Miscellaneous stream functions. */
1475
1476 svn_error_t *
1477 svn_stringbuf_from_stream(svn_stringbuf_t **str,
1478                           svn_stream_t *stream,
1479                           apr_size_t len_hint,
1480                           apr_pool_t *result_pool)
1481 {
1482 #define MIN_READ_SIZE 64
1483
1484   apr_size_t to_read = 0;
1485   svn_stringbuf_t *text
1486     = svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE,
1487                                   result_pool);
1488
1489   do
1490     {
1491       to_read = text->blocksize - 1 - text->len;
1492       SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &to_read));
1493       text->len += to_read;
1494
1495       if (to_read && text->blocksize < text->len + MIN_READ_SIZE)
1496         svn_stringbuf_ensure(text, text->blocksize * 2);
1497     }
1498   while (to_read);
1499
1500   text->data[text->len] = '\0';
1501   *str = text;
1502
1503   return SVN_NO_ERROR;
1504 }
1505
1506 struct stringbuf_stream_baton
1507 {
1508   svn_stringbuf_t *str;
1509   apr_size_t amt_read;
1510 };
1511
1512 /* svn_stream_mark_t for streams backed by stringbufs. */
1513 struct stringbuf_stream_mark {
1514     apr_size_t pos;
1515 };
1516
1517 static svn_error_t *
1518 read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
1519 {
1520   struct stringbuf_stream_baton *btn = baton;
1521   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1522
1523   *len = (*len > left_to_read) ? left_to_read : *len;
1524   memcpy(buffer, btn->str->data + btn->amt_read, *len);
1525   btn->amt_read += *len;
1526   return SVN_NO_ERROR;
1527 }
1528
1529 static svn_error_t *
1530 skip_handler_stringbuf(void *baton, apr_size_t len)
1531 {
1532   struct stringbuf_stream_baton *btn = baton;
1533   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1534
1535   len = (len > left_to_read) ? left_to_read : len;
1536   btn->amt_read += len;
1537   return SVN_NO_ERROR;
1538 }
1539
1540 static svn_error_t *
1541 write_handler_stringbuf(void *baton, const char *data, apr_size_t *len)
1542 {
1543   struct stringbuf_stream_baton *btn = baton;
1544
1545   svn_stringbuf_appendbytes(btn->str, data, *len);
1546   return SVN_NO_ERROR;
1547 }
1548
1549 static svn_error_t *
1550 mark_handler_stringbuf(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1551 {
1552   struct stringbuf_stream_baton *btn;
1553   struct stringbuf_stream_mark *stringbuf_stream_mark;
1554
1555   btn = baton;
1556
1557   stringbuf_stream_mark = apr_palloc(pool, sizeof(*stringbuf_stream_mark));
1558   stringbuf_stream_mark->pos = btn->amt_read;
1559   *mark = (svn_stream_mark_t *)stringbuf_stream_mark;
1560   return SVN_NO_ERROR;
1561 }
1562
1563 static svn_error_t *
1564 seek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark)
1565 {
1566   struct stringbuf_stream_baton *btn = baton;
1567
1568   if (mark != NULL)
1569     {
1570       const struct stringbuf_stream_mark *stringbuf_stream_mark;
1571
1572       stringbuf_stream_mark = (const struct stringbuf_stream_mark *)mark;
1573       btn->amt_read = stringbuf_stream_mark->pos;
1574     }
1575   else
1576     btn->amt_read = 0;
1577
1578   return SVN_NO_ERROR;
1579 }
1580
1581 static svn_error_t *
1582 data_available_handler_stringbuf(void *baton, svn_boolean_t *data_available)
1583 {
1584   struct stringbuf_stream_baton *btn = baton;
1585
1586   *data_available = ((btn->str->len - btn->amt_read) > 0);
1587   return SVN_NO_ERROR;
1588 }
1589
1590 static svn_boolean_t
1591 is_buffered_handler_stringbuf(void *baton)
1592 {
1593   return TRUE;
1594 }
1595
1596 svn_stream_t *
1597 svn_stream_from_stringbuf(svn_stringbuf_t *str,
1598                           apr_pool_t *pool)
1599 {
1600   svn_stream_t *stream;
1601   struct stringbuf_stream_baton *baton;
1602
1603   if (! str)
1604     return svn_stream_empty(pool);
1605
1606   baton = apr_palloc(pool, sizeof(*baton));
1607   baton->str = str;
1608   baton->amt_read = 0;
1609   stream = svn_stream_create(baton, pool);
1610   svn_stream_set_read2(stream, read_handler_stringbuf, read_handler_stringbuf);
1611   svn_stream_set_skip(stream, skip_handler_stringbuf);
1612   svn_stream_set_write(stream, write_handler_stringbuf);
1613   svn_stream_set_mark(stream, mark_handler_stringbuf);
1614   svn_stream_set_seek(stream, seek_handler_stringbuf);
1615   svn_stream_set_data_available(stream, data_available_handler_stringbuf);
1616   svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf);
1617   return stream;
1618 }
1619
1620 struct string_stream_baton
1621 {
1622   const svn_string_t *str;
1623   apr_size_t amt_read;
1624 };
1625
1626 /* svn_stream_mark_t for streams backed by stringbufs. */
1627 struct string_stream_mark {
1628     apr_size_t pos;
1629 };
1630
1631 static svn_error_t *
1632 read_handler_string(void *baton, char *buffer, apr_size_t *len)
1633 {
1634   struct string_stream_baton *btn = baton;
1635   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1636
1637   *len = (*len > left_to_read) ? left_to_read : *len;
1638   memcpy(buffer, btn->str->data + btn->amt_read, *len);
1639   btn->amt_read += *len;
1640   return SVN_NO_ERROR;
1641 }
1642
1643 static svn_error_t *
1644 mark_handler_string(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1645 {
1646   struct string_stream_baton *btn;
1647   struct string_stream_mark *marker;
1648
1649   btn = baton;
1650
1651   marker = apr_palloc(pool, sizeof(*marker));
1652   marker->pos = btn->amt_read;
1653   *mark = (svn_stream_mark_t *)marker;
1654   return SVN_NO_ERROR;
1655 }
1656
1657 static svn_error_t *
1658 seek_handler_string(void *baton, const svn_stream_mark_t *mark)
1659 {
1660   struct string_stream_baton *btn = baton;
1661
1662   if (mark != NULL)
1663     {
1664       const struct string_stream_mark *marker;
1665
1666       marker = (const struct string_stream_mark *)mark;
1667       btn->amt_read = marker->pos;
1668     }
1669   else
1670     btn->amt_read = 0;
1671
1672   return SVN_NO_ERROR;
1673 }
1674
1675 static svn_error_t *
1676 skip_handler_string(void *baton, apr_size_t len)
1677 {
1678   struct string_stream_baton *btn = baton;
1679   apr_size_t left_to_read = btn->str->len - btn->amt_read;
1680
1681   len = (len > left_to_read) ? left_to_read : len;
1682   btn->amt_read += len;
1683   return SVN_NO_ERROR;
1684 }
1685
1686 static svn_error_t *
1687 data_available_handler_string(void *baton, svn_boolean_t *data_available)
1688 {
1689   struct string_stream_baton *btn = baton;
1690
1691   *data_available = ((btn->str->len - btn->amt_read) > 0);
1692   return SVN_NO_ERROR;
1693 }
1694
1695 static svn_boolean_t
1696 is_buffered_handler_string(void *baton)
1697 {
1698   return TRUE;
1699 }
1700
1701 svn_stream_t *
1702 svn_stream_from_string(const svn_string_t *str,
1703                        apr_pool_t *pool)
1704 {
1705   svn_stream_t *stream;
1706   struct string_stream_baton *baton;
1707
1708   if (! str)
1709     return svn_stream_empty(pool);
1710
1711   baton = apr_palloc(pool, sizeof(*baton));
1712   baton->str = str;
1713   baton->amt_read = 0;
1714   stream = svn_stream_create(baton, pool);
1715   svn_stream_set_read2(stream, read_handler_string, read_handler_string);
1716   svn_stream_set_mark(stream, mark_handler_string);
1717   svn_stream_set_seek(stream, seek_handler_string);
1718   svn_stream_set_skip(stream, skip_handler_string);
1719   svn_stream_set_data_available(stream, data_available_handler_string);
1720   svn_stream__set_is_buffered(stream, is_buffered_handler_string);
1721   return stream;
1722 }
1723
1724
1725 svn_error_t *
1726 svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool)
1727 {
1728   apr_file_t *stdin_file;
1729   apr_status_t apr_err;
1730
1731   apr_err = apr_file_open_stdin(&stdin_file, pool);
1732   if (apr_err)
1733     return svn_error_wrap_apr(apr_err, "Can't open stdin");
1734
1735   /* STDIN may or may not support positioning requests, but generally
1736      it does not, or the behavior is implementation-specific.  Hence,
1737      we cannot safely advertise mark(), seek() and non-default skip()
1738      support. */
1739   *in = make_stream_from_apr_file(stdin_file, TRUE, FALSE, pool);
1740
1741   return SVN_NO_ERROR;
1742 }
1743
1744
1745 svn_error_t *
1746 svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
1747 {
1748   apr_file_t *stdout_file;
1749   apr_status_t apr_err;
1750
1751   apr_err = apr_file_open_stdout(&stdout_file, pool);
1752   if (apr_err)
1753     return svn_error_wrap_apr(apr_err, "Can't open stdout");
1754
1755   /* STDOUT may or may not support positioning requests, but generally
1756      it does not, or the behavior is implementation-specific.  Hence,
1757      we cannot safely advertise mark(), seek() and non-default skip()
1758      support. */
1759   *out = make_stream_from_apr_file(stdout_file, TRUE, FALSE, pool);
1760
1761   return SVN_NO_ERROR;
1762 }
1763
1764
1765 svn_error_t *
1766 svn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool)
1767 {
1768   apr_file_t *stderr_file;
1769   apr_status_t apr_err;
1770
1771   apr_err = apr_file_open_stderr(&stderr_file, pool);
1772   if (apr_err)
1773     return svn_error_wrap_apr(apr_err, "Can't open stderr");
1774
1775   /* STDERR may or may not support positioning requests, but generally
1776      it does not, or the behavior is implementation-specific.  Hence,
1777      we cannot safely advertise mark(), seek() and non-default skip()
1778      support. */
1779   *err = make_stream_from_apr_file(stderr_file, TRUE, FALSE, pool);
1780
1781   return SVN_NO_ERROR;
1782 }
1783
1784
1785 svn_error_t *
1786 svn_string_from_stream(svn_string_t **result,
1787                        svn_stream_t *stream,
1788                        apr_pool_t *result_pool,
1789                        apr_pool_t *scratch_pool)
1790 {
1791   svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,
1792                                                       result_pool);
1793   char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
1794
1795   while (1)
1796     {
1797       apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1798
1799       SVN_ERR(svn_stream_read_full(stream, buffer, &len));
1800       svn_stringbuf_appendbytes(work, buffer, len);
1801
1802       if (len < SVN__STREAM_CHUNK_SIZE)
1803         break;
1804     }
1805
1806   SVN_ERR(svn_stream_close(stream));
1807
1808   *result = apr_palloc(result_pool, sizeof(**result));
1809   (*result)->data = work->data;
1810   (*result)->len = work->len;
1811
1812   return SVN_NO_ERROR;
1813 }
1814
1815
1816 /* These are somewhat arbitrary, if we ever get good empirical data as to
1817    actually valid values, feel free to update them. */
1818 #define BUFFER_BLOCK_SIZE 1024
1819 #define BUFFER_MAX_SIZE 100000
1820
1821 svn_stream_t *
1822 svn_stream_buffered(apr_pool_t *result_pool)
1823 {
1824   return svn_stream__from_spillbuf(svn_spillbuf__create(BUFFER_BLOCK_SIZE,
1825                                                         BUFFER_MAX_SIZE,
1826                                                         result_pool),
1827                                    result_pool);
1828 }
1829
1830
1831 \f
1832 /*** Lazyopen Streams ***/
1833
1834 /* Custom baton for lazyopen-style wrapper streams. */
1835 typedef struct lazyopen_baton_t {
1836
1837   /* Callback function and baton for opening the wrapped stream. */
1838   svn_stream_lazyopen_func_t open_func;
1839   void *open_baton;
1840
1841   /* The wrapped stream, or NULL if the stream hasn't yet been
1842      opened. */
1843   svn_stream_t *real_stream;
1844   apr_pool_t *pool;
1845
1846   /* Whether to open the wrapped stream on a close call. */
1847   svn_boolean_t open_on_close;
1848
1849 } lazyopen_baton_t;
1850
1851
1852 /* Use B->open_func/baton to create and set B->real_stream iff it
1853    isn't already set. */
1854 static svn_error_t *
1855 lazyopen_if_unopened(lazyopen_baton_t *b)
1856 {
1857   if (b->real_stream == NULL)
1858     {
1859       svn_stream_t *stream;
1860       apr_pool_t *scratch_pool = svn_pool_create(b->pool);
1861
1862       SVN_ERR(b->open_func(&stream, b->open_baton,
1863                            b->pool, scratch_pool));
1864
1865       svn_pool_destroy(scratch_pool);
1866
1867       b->real_stream = stream;
1868     }
1869
1870   return SVN_NO_ERROR;
1871 }
1872
1873 /* Implements svn_read_fn_t */
1874 static svn_error_t *
1875 read_handler_lazyopen(void *baton,
1876                       char *buffer,
1877                       apr_size_t *len)
1878 {
1879   lazyopen_baton_t *b = baton;
1880
1881   SVN_ERR(lazyopen_if_unopened(b));
1882   SVN_ERR(svn_stream_read2(b->real_stream, buffer, len));
1883
1884   return SVN_NO_ERROR;
1885 }
1886
1887 /* Implements svn_read_fn_t */
1888 static svn_error_t *
1889 read_full_handler_lazyopen(void *baton,
1890                       char *buffer,
1891                       apr_size_t *len)
1892 {
1893   lazyopen_baton_t *b = baton;
1894
1895   SVN_ERR(lazyopen_if_unopened(b));
1896   SVN_ERR(svn_stream_read_full(b->real_stream, buffer, len));
1897
1898   return SVN_NO_ERROR;
1899 }
1900
1901 /* Implements svn_stream_skip_fn_t */
1902 static svn_error_t *
1903 skip_handler_lazyopen(void *baton,
1904                       apr_size_t len)
1905 {
1906   lazyopen_baton_t *b = baton;
1907
1908   SVN_ERR(lazyopen_if_unopened(b));
1909   SVN_ERR(svn_stream_skip(b->real_stream, len));
1910
1911   return SVN_NO_ERROR;
1912 }
1913
1914 /* Implements svn_write_fn_t */
1915 static svn_error_t *
1916 write_handler_lazyopen(void *baton,
1917                        const char *data,
1918                        apr_size_t *len)
1919 {
1920   lazyopen_baton_t *b = baton;
1921
1922   SVN_ERR(lazyopen_if_unopened(b));
1923   SVN_ERR(svn_stream_write(b->real_stream, data, len));
1924
1925   return SVN_NO_ERROR;
1926 }
1927
1928 /* Implements svn_close_fn_t */
1929 static svn_error_t *
1930 close_handler_lazyopen(void *baton)
1931 {
1932   lazyopen_baton_t *b = baton;
1933
1934   if (b->open_on_close)
1935     SVN_ERR(lazyopen_if_unopened(b));
1936   if (b->real_stream)
1937     SVN_ERR(svn_stream_close(b->real_stream));
1938
1939   return SVN_NO_ERROR;
1940 }
1941
1942 /* Implements svn_stream_mark_fn_t */
1943 static svn_error_t *
1944 mark_handler_lazyopen(void *baton,
1945                       svn_stream_mark_t **mark,
1946                       apr_pool_t *pool)
1947 {
1948   lazyopen_baton_t *b = baton;
1949
1950   SVN_ERR(lazyopen_if_unopened(b));
1951   SVN_ERR(svn_stream_mark(b->real_stream, mark, pool));
1952
1953   return SVN_NO_ERROR;
1954 }
1955
1956 /* Implements svn_stream_seek_fn_t */
1957 static svn_error_t *
1958 seek_handler_lazyopen(void *baton,
1959                       const svn_stream_mark_t *mark)
1960 {
1961   lazyopen_baton_t *b = baton;
1962
1963   SVN_ERR(lazyopen_if_unopened(b));
1964   SVN_ERR(svn_stream_seek(b->real_stream, mark));
1965
1966   return SVN_NO_ERROR;
1967 }
1968
1969 static svn_error_t *
1970 data_available_handler_lazyopen(void *baton,
1971                                 svn_boolean_t *data_available)
1972 {
1973   lazyopen_baton_t *b = baton;
1974
1975   SVN_ERR(lazyopen_if_unopened(b));
1976   return svn_error_trace(svn_stream_data_available(b->real_stream,
1977                                                    data_available));
1978 }
1979
1980 /* Implements svn_stream__is_buffered_fn_t */
1981 static svn_boolean_t
1982 is_buffered_lazyopen(void *baton)
1983 {
1984   lazyopen_baton_t *b = baton;
1985
1986   /* No lazy open as we cannot handle an open error. */
1987   if (!b->real_stream)
1988     return FALSE;
1989
1990   return svn_stream__is_buffered(b->real_stream);
1991 }
1992
1993 svn_stream_t *
1994 svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,
1995                            void *open_baton,
1996                            svn_boolean_t open_on_close,
1997                            apr_pool_t *result_pool)
1998 {
1999   lazyopen_baton_t *lob = apr_pcalloc(result_pool, sizeof(*lob));
2000   svn_stream_t *stream;
2001
2002   lob->open_func = open_func;
2003   lob->open_baton = open_baton;
2004   lob->real_stream = NULL;
2005   lob->pool = result_pool;
2006   lob->open_on_close = open_on_close;
2007
2008   stream = svn_stream_create(lob, result_pool);
2009   svn_stream_set_read2(stream, read_handler_lazyopen,
2010                        read_full_handler_lazyopen);
2011   svn_stream_set_skip(stream, skip_handler_lazyopen);
2012   svn_stream_set_write(stream, write_handler_lazyopen);
2013   svn_stream_set_close(stream, close_handler_lazyopen);
2014   svn_stream_set_mark(stream, mark_handler_lazyopen);
2015   svn_stream_set_seek(stream, seek_handler_lazyopen);
2016   svn_stream_set_data_available(stream, data_available_handler_lazyopen);
2017   svn_stream__set_is_buffered(stream, is_buffered_lazyopen);
2018
2019   return stream;
2020 }
2021
2022 /* Baton for install streams */
2023 struct install_baton_t
2024 {
2025   struct baton_apr baton_apr;
2026   const char *tmp_path;
2027 };
2028
2029 #ifdef WIN32
2030
2031 /* Create and open a tempfile in DIRECTORY. Return its handle and path */
2032 static svn_error_t *
2033 create_tempfile(HANDLE *hFile,
2034                 const char **file_path,
2035                 const char *directory,
2036                 apr_pool_t *result_pool,
2037                 apr_pool_t *scratch_pool)
2038 {
2039   const char *unique_name;
2040   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2041   static svn_atomic_t tempname_counter;
2042   int baseNr = (GetTickCount() << 11) + 13 * svn_atomic_inc(&tempname_counter)
2043                + GetCurrentProcessId();
2044   int i = 0;
2045   HANDLE h;
2046
2047   /* Shares common idea with io.c's temp_file_create */
2048
2049   do
2050     {
2051       apr_uint32_t unique_nr;
2052       WCHAR *w_name;
2053
2054       /* Generate a number that should be unique for this application and
2055          usually for the entire computer to reduce the number of cycles
2056          through this loop. (A bit of calculation is much cheaper than
2057          disk io) */
2058       unique_nr = baseNr + 7 * i++;
2059
2060
2061       svn_pool_clear(iterpool);
2062       unique_name = svn_dirent_join(directory,
2063                                     apr_psprintf(iterpool, "svn-%X",
2064                                                  unique_nr),
2065                                     iterpool);
2066
2067       SVN_ERR(svn_io__utf8_to_unicode_longpath(&w_name, unique_name,
2068                                                iterpool));
2069
2070       /* Create a completely not-sharable file to avoid indexers, and other
2071          filesystem watchers locking the file while we are still writing.
2072
2073          We need DELETE privileges to move the file. */
2074       h = CreateFileW(w_name, GENERIC_WRITE | DELETE, 0 /* share */,
2075                       NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
2076
2077       if (h == INVALID_HANDLE_VALUE)
2078         {
2079           apr_status_t status = apr_get_os_error();
2080           if (i > 1000)
2081             return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
2082                            svn_error_wrap_apr(status, NULL),
2083                            _("Unable to make name in '%s'"),
2084                            svn_dirent_local_style(directory, scratch_pool));
2085
2086           if (!APR_STATUS_IS_EEXIST(status) && !APR_STATUS_IS_EACCES(status))
2087             return svn_error_wrap_apr(status, NULL);
2088         }
2089     }
2090   while (h == INVALID_HANDLE_VALUE);
2091
2092   *hFile = h;
2093   *file_path = apr_pstrdup(result_pool, unique_name);
2094   svn_pool_destroy(iterpool);
2095
2096   return SVN_NO_ERROR;
2097 }
2098
2099 /* Implements svn_close_fn_t */
2100 static svn_error_t *
2101 install_close(void *baton)
2102 {
2103   struct install_baton_t *ib = baton;
2104
2105   /* Flush the data cached in APR, but don't close the file yet */
2106   SVN_ERR(svn_io_file_flush(ib->baton_apr.file, ib->baton_apr.pool));
2107
2108   return SVN_NO_ERROR;
2109 }
2110
2111 #endif /* WIN32 */
2112
2113 svn_error_t *
2114 svn_stream__create_for_install(svn_stream_t **install_stream,
2115                                const char *tmp_abspath,
2116                                apr_pool_t *result_pool,
2117                                apr_pool_t *scratch_pool)
2118 {
2119   apr_file_t *file;
2120   struct install_baton_t *ib;
2121   const char *tmp_path;
2122
2123 #ifdef WIN32
2124   HANDLE hInstall;
2125   apr_status_t status;
2126
2127   SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2128
2129   SVN_ERR(create_tempfile(&hInstall, &tmp_path, tmp_abspath,
2130                           scratch_pool, scratch_pool));
2131
2132   /* Wrap as a standard APR file to allow sharing implementation.
2133
2134      But do note that some file functions (such as retrieving the name)
2135      don't work on this wrapper. */
2136   /* ### Buffered, or not? */
2137   status = apr_os_file_put(&file, &hInstall,
2138                            APR_WRITE | APR_BINARY | APR_BUFFERED,
2139                            result_pool);
2140
2141   if (status)
2142     {
2143       CloseHandle(hInstall);
2144       return svn_error_wrap_apr(status, NULL);
2145     }
2146
2147   tmp_path = svn_dirent_internal_style(tmp_path, result_pool);
2148 #else
2149
2150   SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2151
2152   SVN_ERR(svn_io_open_unique_file3(&file, &tmp_path, tmp_abspath,
2153                                    svn_io_file_del_none,
2154                                    result_pool, scratch_pool));
2155 #endif
2156   *install_stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
2157
2158   ib = apr_pcalloc(result_pool, sizeof(*ib));
2159   ib->baton_apr = *(struct baton_apr*)(*install_stream)->baton;
2160
2161   assert((void*)&ib->baton_apr == (void*)ib); /* baton pointer is the same */
2162
2163   (*install_stream)->baton = ib;
2164
2165   ib->tmp_path = tmp_path;
2166
2167 #ifdef WIN32
2168   /* Don't close the file on stream close; flush instead */
2169   svn_stream_set_close(*install_stream, install_close);
2170 #else
2171   /* ### Install pool cleanup handler for tempfile? */
2172 #endif
2173
2174   return SVN_NO_ERROR;
2175 }
2176
2177 svn_error_t *
2178 svn_stream__install_stream(svn_stream_t *install_stream,
2179                            const char *final_abspath,
2180                            svn_boolean_t make_parents,
2181                            apr_pool_t *scratch_pool)
2182 {
2183   struct install_baton_t *ib = install_stream->baton;
2184   svn_error_t *err;
2185
2186   SVN_ERR_ASSERT(svn_dirent_is_absolute(final_abspath));
2187 #ifdef WIN32
2188   err = svn_io__win_rename_open_file(ib->baton_apr.file,  ib->tmp_path,
2189                                      final_abspath, scratch_pool);
2190   if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2191     {
2192       svn_error_t *err2;
2193
2194       err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2195                                                     scratch_pool),
2196                                          scratch_pool);
2197
2198       if (err2)
2199         return svn_error_trace(svn_error_compose_create(err, err2));
2200       else
2201         svn_error_clear(err);
2202
2203       err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path,
2204                                          final_abspath, scratch_pool);
2205     }
2206
2207   /* ### rhuijben: I wouldn't be surprised if we later find out that we
2208                    have to fall back to close+rename on some specific
2209                    error values here, to support some non standard NAS
2210                    and filesystem scenarios. */
2211   if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
2212     {
2213       /* Rename open files is not supported on this platform: fallback to
2214          svn_io_file_rename2(). */
2215       svn_error_clear(err);
2216       err = SVN_NO_ERROR;
2217
2218       SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2219     }
2220   else
2221     {
2222       return svn_error_compose_create(err,
2223                                       svn_io_file_close(ib->baton_apr.file,
2224                                                         scratch_pool));
2225     }
2226 #endif
2227
2228   err = svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool);
2229
2230   /* A missing directory is too common to not cover here. */
2231   if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2232     {
2233       svn_error_t *err2;
2234
2235       err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2236                                                             scratch_pool),
2237                                          scratch_pool);
2238
2239       if (err2)
2240         /* Creating directory didn't work: Return all errors */
2241         return svn_error_trace(svn_error_compose_create(err, err2));
2242       else
2243         /* We could create a directory: retry install */
2244         svn_error_clear(err);
2245
2246       SVN_ERR(svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool));
2247     }
2248   else
2249     SVN_ERR(err);
2250
2251   return SVN_NO_ERROR;
2252 }
2253
2254 svn_error_t *
2255 svn_stream__install_get_info(apr_finfo_t *finfo,
2256                              svn_stream_t *install_stream,
2257                              apr_int32_t wanted,
2258                              apr_pool_t *scratch_pool)
2259 {
2260   struct install_baton_t *ib = install_stream->baton;
2261
2262 #ifdef WIN32
2263   /* On WIN32 the file is still open, so we can obtain the information
2264      from the handle without race conditions */
2265   apr_status_t status;
2266
2267   status = apr_file_info_get(finfo, wanted, ib->baton_apr.file);
2268
2269   if (status)
2270     return svn_error_wrap_apr(status, NULL);
2271 #else
2272   SVN_ERR(svn_io_stat(finfo, ib->tmp_path, wanted, scratch_pool));
2273 #endif
2274
2275   return SVN_NO_ERROR;
2276 }
2277
2278 svn_error_t *
2279 svn_stream__install_delete(svn_stream_t *install_stream,
2280                            apr_pool_t *scratch_pool)
2281 {
2282   struct install_baton_t *ib = install_stream->baton;
2283
2284 #ifdef WIN32
2285   svn_error_t *err;
2286
2287   /* Mark the file as delete on close to avoid having to reopen
2288      the file as part of the delete handling. */
2289   err = svn_io__win_delete_file_on_close(ib->baton_apr.file,  ib->tmp_path,
2290                                          scratch_pool);
2291   if (err == SVN_NO_ERROR)
2292     {
2293       SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2294       return SVN_NO_ERROR; /* File is already gone */
2295     }
2296
2297   /* Deleting file on close may be unsupported, so ignore errors and
2298      fallback to svn_io_remove_file2(). */
2299   svn_error_clear(err);
2300   SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2301 #endif
2302
2303   return svn_error_trace(svn_io_remove_file2(ib->tmp_path, FALSE,
2304                                              scratch_pool));
2305 }