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
50struct 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
63/*** Forward declarations. ***/
64
65static svn_error_t *
66skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn);
67
68
69/*** Generic streams. ***/
70
71svn_stream_t *
72svn_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
90void
91svn_stream_set_baton(svn_stream_t *stream, void *baton)
92{
93  stream->baton = baton;
94}
95
96
97void
98svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn)
99{
100  stream->read_fn = read_fn;
101}
102
103void
104svn_stream_set_skip(svn_stream_t *stream, svn_stream_skip_fn_t skip_fn)
105{
106  stream->skip_fn = skip_fn;
107}
108
109void
110svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
111{
112  stream->write_fn = write_fn;
113}
114
115void
116svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
117{
118  stream->close_fn = close_fn;
119}
120
121void
122svn_stream_set_mark(svn_stream_t *stream, svn_stream_mark_fn_t mark_fn)
123{
124  stream->mark_fn = mark_fn;
125}
126
127void
128svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn)
129{
130  stream->seek_fn = seek_fn;
131}
132
133void
134svn_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
140svn_error_t *
141svn_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
148svn_error_t *
149svn_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
159svn_error_t *
160svn_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
167svn_error_t *
168svn_stream_reset(svn_stream_t *stream)
169{
170  return svn_error_trace(
171            svn_stream_seek(stream, NULL));
172}
173
174svn_boolean_t
175svn_stream_supports_mark(svn_stream_t *stream)
176{
177  return stream->mark_fn != NULL;
178}
179
180svn_error_t *
181svn_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
190svn_error_t *
191svn_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
199svn_boolean_t
200svn_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
208svn_error_t *
209svn_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
216svn_error_t *
217svn_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
225svn_error_t *
226svn_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
242svn_error_t *
243svn_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. */
273static svn_error_t *
274stream_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
320static svn_error_t *
321stream_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. */
426static svn_error_t *
427stream_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
464svn_error_t *
465svn_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
475svn_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
515svn_error_t *
516svn_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
553/*** Stream implementation utilities ***/
554
555/* Skip data from any stream by reading and simply discarding it. */
556static svn_error_t *
557skip_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
575/*** Generic readable empty stream ***/
576
577static svn_error_t *
578read_handler_empty(void *baton, char *buffer, apr_size_t *len)
579{
580  *len = 0;
581  return SVN_NO_ERROR;
582}
583
584static svn_error_t *
585write_handler_empty(void *baton, const char *data, apr_size_t *len)
586{
587  return SVN_NO_ERROR;
588}
589
590static svn_error_t *
591mark_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
597static svn_error_t *
598seek_handler_empty(void *baton, const svn_stream_mark_t *mark)
599{
600  return SVN_NO_ERROR;
601}
602
603static svn_boolean_t
604is_buffered_handler_empty(void *baton)
605{
606  return FALSE;
607}
608
609
610svn_stream_t *
611svn_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
626/*** Stream duplication support ***/
627struct baton_tee {
628  svn_stream_t *out1;
629  svn_stream_t *out2;
630};
631
632
633static svn_error_t *
634write_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
645static svn_error_t *
646close_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
657svn_stream_t *
658svn_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
683/*** Ownership detaching stream ***/
684
685static svn_error_t *
686read_handler_disown(void *baton, char *buffer, apr_size_t *len)
687{
688  return svn_error_trace(svn_stream_read(baton, buffer, len));
689}
690
691static svn_error_t *
692skip_handler_disown(void *baton, apr_size_t len)
693{
694  return svn_error_trace(svn_stream_skip(baton, len));
695}
696
697static svn_error_t *
698write_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
703static svn_error_t *
704mark_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
709static svn_error_t *
710seek_handler_disown(void *baton, const svn_stream_mark_t *mark)
711{
712  return svn_error_trace(svn_stream_seek(baton, mark));
713}
714
715static svn_boolean_t
716is_buffered_handler_disown(void *baton)
717{
718  return svn_stream__is_buffered(baton);
719}
720
721svn_stream_t *
722svn_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
738/*** Generic stream for APR files ***/
739struct 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. */
745struct mark_apr {
746  apr_off_t off;
747};
748
749static svn_error_t *
750read_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
776static svn_error_t *
777skip_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
786static svn_error_t *
787write_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
804static svn_error_t *
805close_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
812static svn_error_t *
813mark_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
825static svn_error_t *
826seek_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
836static svn_boolean_t
837is_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
843svn_error_t *
844svn_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
859svn_error_t *
860svn_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
879svn_error_t *
880svn_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
897svn_stream_t *
898svn_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
926apr_file_t *
927svn_stream__aprfile(svn_stream_t *stream)
928{
929  return stream->file;
930}
931
932
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
941struct 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. */
957static voidpf
958zalloc(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 */
966static void
967zfree(voidpf opaque, voidpf address)
968{
969  /* Empty, since we allocate on the pool */
970}
971
972/* Helper function to figure out the sync mode */
973static svn_error_t *
974read_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 */
999static svn_error_t *
1000read_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 */
1055static svn_error_t *
1056write_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 */
1102static svn_error_t *
1103close_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
1148svn_stream_t *
1149svn_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
1175/* Checksummed stream support */
1176
1177struct 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
1191static svn_error_t *
1192read_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
1209static svn_error_t *
1210write_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
1221static svn_error_t *
1222close_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
1250svn_stream_t *
1251svn_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
1288struct 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
1298static svn_error_t *
1299read_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
1305static svn_error_t *
1306skip_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
1312static svn_error_t *
1313write_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
1319static svn_error_t *
1320close_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
1340svn_stream_t *
1341svn_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
1381/* Miscellaneous stream functions. */
1382struct 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. */
1389struct stringbuf_stream_mark {
1390    apr_size_t pos;
1391};
1392
1393static svn_error_t *
1394read_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
1405static svn_error_t *
1406skip_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
1416static svn_error_t *
1417write_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
1425static svn_error_t *
1426mark_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
1439static svn_error_t *
1440seek_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
1457static svn_boolean_t
1458is_buffered_handler_stringbuf(void *baton)
1459{
1460  return TRUE;
1461}
1462
1463svn_stream_t *
1464svn_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
1486struct 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. */
1493struct string_stream_mark {
1494    apr_size_t pos;
1495};
1496
1497static svn_error_t *
1498read_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
1509static svn_error_t *
1510mark_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
1523static svn_error_t *
1524seek_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
1541static svn_error_t *
1542skip_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
1552static svn_boolean_t
1553is_buffered_handler_string(void *baton)
1554{
1555  return TRUE;
1556}
1557
1558svn_stream_t *
1559svn_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
1581svn_error_t *
1582svn_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
1597svn_error_t *
1598svn_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
1613svn_error_t *
1614svn_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
1629svn_error_t *
1630svn_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
1665svn_stream_t *
1666svn_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
1674/*** Lazyopen Streams ***/
1675
1676/* Custom baton for lazyopen-style wrapper streams. */
1677typedef 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. */
1696static svn_error_t *
1697lazyopen_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 */
1716static svn_error_t *
1717read_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 */
1730static svn_error_t *
1731skip_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 */
1743static svn_error_t *
1744write_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 */
1757static svn_error_t *
1758close_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 */
1771static svn_error_t *
1772mark_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 */
1785static svn_error_t *
1786seek_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 */
1798static svn_boolean_t
1799is_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
1810svn_stream_t *
1811svn_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}
1836