1251881Speter/*
2251881Speter * stream.c:   svn_stream operations
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include <assert.h>
25251881Speter#include <stdio.h>
26251881Speter
27251881Speter#include <apr.h>
28251881Speter#include <apr_pools.h>
29251881Speter#include <apr_strings.h>
30251881Speter#include <apr_file_io.h>
31251881Speter#include <apr_errno.h>
32251881Speter#include <apr_md5.h>
33251881Speter
34251881Speter#include <zlib.h>
35251881Speter
36251881Speter#include "svn_pools.h"
37251881Speter#include "svn_io.h"
38251881Speter#include "svn_error.h"
39251881Speter#include "svn_string.h"
40251881Speter#include "svn_utf.h"
41251881Speter#include "svn_checksum.h"
42251881Speter#include "svn_path.h"
43251881Speter#include "svn_private_config.h"
44251881Speter#include "private/svn_error_private.h"
45251881Speter#include "private/svn_eol_private.h"
46251881Speter#include "private/svn_io_private.h"
47251881Speter#include "private/svn_subr_private.h"
48251881Speter
49251881Speter
50251881Speterstruct svn_stream_t {
51251881Speter  void *baton;
52251881Speter  svn_read_fn_t read_fn;
53251881Speter  svn_stream_skip_fn_t skip_fn;
54251881Speter  svn_write_fn_t write_fn;
55251881Speter  svn_close_fn_t close_fn;
56251881Speter  svn_stream_mark_fn_t mark_fn;
57251881Speter  svn_stream_seek_fn_t seek_fn;
58251881Speter  svn_stream__is_buffered_fn_t is_buffered_fn;
59253734Speter  apr_file_t *file; /* Maybe NULL */
60251881Speter};
61251881Speter
62251881Speter
63251881Speter/*** Forward declarations. ***/
64251881Speter
65251881Speterstatic svn_error_t *
66251881Speterskip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn);
67251881Speter
68251881Speter
69251881Speter/*** Generic streams. ***/
70251881Speter
71251881Spetersvn_stream_t *
72251881Spetersvn_stream_create(void *baton, apr_pool_t *pool)
73251881Speter{
74251881Speter  svn_stream_t *stream;
75251881Speter
76251881Speter  stream = apr_palloc(pool, sizeof(*stream));
77251881Speter  stream->baton = baton;
78251881Speter  stream->read_fn = NULL;
79251881Speter  stream->skip_fn = NULL;
80251881Speter  stream->write_fn = NULL;
81251881Speter  stream->close_fn = NULL;
82251881Speter  stream->mark_fn = NULL;
83251881Speter  stream->seek_fn = NULL;
84251881Speter  stream->is_buffered_fn = NULL;
85253734Speter  stream->file = NULL;
86251881Speter  return stream;
87251881Speter}
88251881Speter
89251881Speter
90251881Spetervoid
91251881Spetersvn_stream_set_baton(svn_stream_t *stream, void *baton)
92251881Speter{
93251881Speter  stream->baton = baton;
94251881Speter}
95251881Speter
96251881Speter
97251881Spetervoid
98251881Spetersvn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn)
99251881Speter{
100251881Speter  stream->read_fn = read_fn;
101251881Speter}
102251881Speter
103251881Spetervoid
104251881Spetersvn_stream_set_skip(svn_stream_t *stream, svn_stream_skip_fn_t skip_fn)
105251881Speter{
106251881Speter  stream->skip_fn = skip_fn;
107251881Speter}
108251881Speter
109251881Spetervoid
110251881Spetersvn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
111251881Speter{
112251881Speter  stream->write_fn = write_fn;
113251881Speter}
114251881Speter
115251881Spetervoid
116251881Spetersvn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
117251881Speter{
118251881Speter  stream->close_fn = close_fn;
119251881Speter}
120251881Speter
121251881Spetervoid
122251881Spetersvn_stream_set_mark(svn_stream_t *stream, svn_stream_mark_fn_t mark_fn)
123251881Speter{
124251881Speter  stream->mark_fn = mark_fn;
125251881Speter}
126251881Speter
127251881Spetervoid
128251881Spetersvn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn)
129251881Speter{
130251881Speter  stream->seek_fn = seek_fn;
131251881Speter}
132251881Speter
133251881Spetervoid
134251881Spetersvn_stream__set_is_buffered(svn_stream_t *stream,
135251881Speter                            svn_stream__is_buffered_fn_t is_buffered_fn)
136251881Speter{
137251881Speter  stream->is_buffered_fn = is_buffered_fn;
138251881Speter}
139251881Speter
140251881Spetersvn_error_t *
141251881Spetersvn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len)
142251881Speter{
143251881Speter  SVN_ERR_ASSERT(stream->read_fn != NULL);
144251881Speter  return svn_error_trace(stream->read_fn(stream->baton, buffer, len));
145251881Speter}
146251881Speter
147251881Speter
148251881Spetersvn_error_t *
149251881Spetersvn_stream_skip(svn_stream_t *stream, apr_size_t len)
150251881Speter{
151251881Speter  if (stream->skip_fn == NULL)
152251881Speter    return svn_error_trace(
153251881Speter            skip_default_handler(stream->baton, len, stream->read_fn));
154251881Speter
155251881Speter  return svn_error_trace(stream->skip_fn(stream->baton, len));
156251881Speter}
157251881Speter
158251881Speter
159251881Spetersvn_error_t *
160251881Spetersvn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)
161251881Speter{
162251881Speter  SVN_ERR_ASSERT(stream->write_fn != NULL);
163251881Speter  return svn_error_trace(stream->write_fn(stream->baton, data, len));
164251881Speter}
165251881Speter
166251881Speter
167251881Spetersvn_error_t *
168251881Spetersvn_stream_reset(svn_stream_t *stream)
169251881Speter{
170251881Speter  return svn_error_trace(
171251881Speter            svn_stream_seek(stream, NULL));
172251881Speter}
173251881Speter
174251881Spetersvn_boolean_t
175251881Spetersvn_stream_supports_mark(svn_stream_t *stream)
176251881Speter{
177251881Speter  return stream->mark_fn != NULL;
178251881Speter}
179251881Speter
180251881Spetersvn_error_t *
181251881Spetersvn_stream_mark(svn_stream_t *stream, svn_stream_mark_t **mark,
182251881Speter                apr_pool_t *pool)
183251881Speter{
184251881Speter  if (stream->mark_fn == NULL)
185251881Speter    return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
186251881Speter
187251881Speter  return svn_error_trace(stream->mark_fn(stream->baton, mark, pool));
188251881Speter}
189251881Speter
190251881Spetersvn_error_t *
191251881Spetersvn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark)
192251881Speter{
193251881Speter  if (stream->seek_fn == NULL)
194251881Speter    return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
195251881Speter
196251881Speter  return svn_error_trace(stream->seek_fn(stream->baton, mark));
197251881Speter}
198251881Speter
199251881Spetersvn_boolean_t
200251881Spetersvn_stream__is_buffered(svn_stream_t *stream)
201251881Speter{
202251881Speter  if (stream->is_buffered_fn == NULL)
203251881Speter    return FALSE;
204251881Speter
205251881Speter  return stream->is_buffered_fn(stream->baton);
206251881Speter}
207251881Speter
208251881Spetersvn_error_t *
209251881Spetersvn_stream_close(svn_stream_t *stream)
210251881Speter{
211251881Speter  if (stream->close_fn == NULL)
212251881Speter    return SVN_NO_ERROR;
213251881Speter  return svn_error_trace(stream->close_fn(stream->baton));
214251881Speter}
215251881Speter
216251881Spetersvn_error_t *
217251881Spetersvn_stream_puts(svn_stream_t *stream,
218251881Speter                const char *str)
219251881Speter{
220251881Speter  apr_size_t len;
221251881Speter  len = strlen(str);
222251881Speter  return svn_error_trace(svn_stream_write(stream, str, &len));
223251881Speter}
224251881Speter
225251881Spetersvn_error_t *
226251881Spetersvn_stream_printf(svn_stream_t *stream,
227251881Speter                  apr_pool_t *pool,
228251881Speter                  const char *fmt,
229251881Speter                  ...)
230251881Speter{
231251881Speter  const char *message;
232251881Speter  va_list ap;
233251881Speter
234251881Speter  va_start(ap, fmt);
235251881Speter  message = apr_pvsprintf(pool, fmt, ap);
236251881Speter  va_end(ap);
237251881Speter
238251881Speter  return svn_error_trace(svn_stream_puts(stream, message));
239251881Speter}
240251881Speter
241251881Speter
242251881Spetersvn_error_t *
243251881Spetersvn_stream_printf_from_utf8(svn_stream_t *stream,
244251881Speter                            const char *encoding,
245251881Speter                            apr_pool_t *pool,
246251881Speter                            const char *fmt,
247251881Speter                            ...)
248251881Speter{
249251881Speter  const char *message, *translated;
250251881Speter  va_list ap;
251251881Speter
252251881Speter  va_start(ap, fmt);
253251881Speter  message = apr_pvsprintf(pool, fmt, ap);
254251881Speter  va_end(ap);
255251881Speter
256251881Speter  SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,
257251881Speter                                        pool));
258251881Speter
259251881Speter  return svn_error_trace(svn_stream_puts(stream, translated));
260251881Speter}
261251881Speter
262251881Speter/* Size that 90% of the lines we encounter will be not longer than.
263251881Speter   used by stream_readline_bytewise() and stream_readline_chunky().
264251881Speter */
265251881Speter#define LINE_CHUNK_SIZE 80
266251881Speter
267251881Speter/* Guts of svn_stream_readline().
268251881Speter * Returns the line read from STREAM in *STRINGBUF, and indicates
269251881Speter * end-of-file in *EOF.  If DETECT_EOL is TRUE, the end-of-line indicator
270251881Speter * is detected automatically and returned in *EOL.
271251881Speter * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line
272251881Speter * indicator.  STRINGBUF is allocated in POOL. */
273251881Speterstatic svn_error_t *
274251881Speterstream_readline_bytewise(svn_stringbuf_t **stringbuf,
275251881Speter                         svn_boolean_t *eof,
276251881Speter                         const char *eol,
277251881Speter                         svn_stream_t *stream,
278251881Speter                         apr_pool_t *pool)
279251881Speter{
280251881Speter  svn_stringbuf_t *str;
281251881Speter  apr_size_t numbytes;
282251881Speter  const char *match;
283251881Speter  char c;
284251881Speter
285251881Speter  /* Since we're reading one character at a time, let's at least
286251881Speter     optimize for the 90% case.  90% of the time, we can avoid the
287251881Speter     stringbuf ever having to realloc() itself if we start it out at
288251881Speter     80 chars.  */
289251881Speter  str = svn_stringbuf_create_ensure(LINE_CHUNK_SIZE, pool);
290251881Speter
291251881Speter  /* Read into STR up to and including the next EOL sequence. */
292251881Speter  match = eol;
293251881Speter  while (*match)
294251881Speter    {
295251881Speter      numbytes = 1;
296251881Speter      SVN_ERR(svn_stream_read(stream, &c, &numbytes));
297251881Speter      if (numbytes != 1)
298251881Speter        {
299251881Speter          /* a 'short' read means the stream has run out. */
300251881Speter          *eof = TRUE;
301251881Speter          *stringbuf = str;
302251881Speter          return SVN_NO_ERROR;
303251881Speter        }
304251881Speter
305251881Speter      if (c == *match)
306251881Speter        match++;
307251881Speter      else
308251881Speter        match = eol;
309251881Speter
310251881Speter      svn_stringbuf_appendbyte(str, c);
311251881Speter    }
312251881Speter
313251881Speter  *eof = FALSE;
314251881Speter  svn_stringbuf_chop(str, match - eol);
315251881Speter  *stringbuf = str;
316251881Speter
317251881Speter  return SVN_NO_ERROR;
318251881Speter}
319251881Speter
320251881Speterstatic svn_error_t *
321251881Speterstream_readline_chunky(svn_stringbuf_t **stringbuf,
322251881Speter                       svn_boolean_t *eof,
323251881Speter                       const char *eol,
324251881Speter                       svn_stream_t *stream,
325251881Speter                       apr_pool_t *pool)
326251881Speter{
327251881Speter  /* Read larger chunks of data at once into this buffer and scan
328251881Speter   * that for EOL. A good chunk size should be about 80 chars since
329251881Speter   * most text lines will be shorter. However, don't use a much
330251881Speter   * larger value because filling the buffer from the stream takes
331251881Speter   * time as well.
332251881Speter   */
333251881Speter  char buffer[LINE_CHUNK_SIZE+1];
334251881Speter
335251881Speter  /* variables */
336251881Speter  svn_stream_mark_t *mark;
337251881Speter  apr_size_t numbytes;
338251881Speter  const char *eol_pos;
339251881Speter  apr_size_t total_parsed = 0;
340251881Speter
341251881Speter  /* invariant for this call */
342251881Speter  const size_t eol_len = strlen(eol);
343251881Speter
344251881Speter  /* Remember the line start so this plus the line length will be
345251881Speter   * the position to move to at the end of this function.
346251881Speter   */
347251881Speter  SVN_ERR(svn_stream_mark(stream, &mark, pool));
348251881Speter
349251881Speter  /* Read the first chunk. */
350251881Speter  numbytes = LINE_CHUNK_SIZE;
351251881Speter  SVN_ERR(svn_stream_read(stream, buffer, &numbytes));
352251881Speter  buffer[numbytes] = '\0';
353251881Speter
354251881Speter  /* Look for the EOL in this first chunk. If we find it, we are done here.
355251881Speter   */
356251881Speter  eol_pos = strstr(buffer, eol);
357251881Speter  if (eol_pos != NULL)
358251881Speter    {
359251881Speter      *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool);
360251881Speter      total_parsed = eol_pos - buffer + eol_len;
361251881Speter    }
362251881Speter  else if (numbytes < LINE_CHUNK_SIZE)
363251881Speter    {
364251881Speter      /* We hit EOF but not EOL.
365251881Speter       */
366251881Speter      *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool);
367251881Speter      *eof = TRUE;
368251881Speter      return SVN_NO_ERROR;
369251881Speter     }
370251881Speter  else
371251881Speter    {
372251881Speter      /* A larger buffer for the string is needed. */
373251881Speter      svn_stringbuf_t *str;
374251881Speter      str = svn_stringbuf_create_ensure(2*LINE_CHUNK_SIZE, pool);
375251881Speter      svn_stringbuf_appendbytes(str, buffer, numbytes);
376251881Speter      *stringbuf = str;
377251881Speter
378251881Speter      /* Loop reading chunks until an EOL was found. If we hit EOF, fall
379251881Speter       * back to the standard implementation. */
380251881Speter      do
381251881Speter      {
382251881Speter        /* Append the next chunk to the string read so far.
383251881Speter         */
384251881Speter        svn_stringbuf_ensure(str, str->len + LINE_CHUNK_SIZE);
385251881Speter        numbytes = LINE_CHUNK_SIZE;
386251881Speter        SVN_ERR(svn_stream_read(stream, str->data + str->len, &numbytes));
387251881Speter        str->len += numbytes;
388251881Speter        str->data[str->len] = '\0';
389251881Speter
390251881Speter        /* Look for the EOL in the new data plus the last part of the
391251881Speter         * previous chunk because the EOL may span over the boundary
392251881Speter         * between both chunks.
393251881Speter         */
394251881Speter        eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol);
395251881Speter
396251881Speter        if ((numbytes < LINE_CHUNK_SIZE) && (eol_pos == NULL))
397251881Speter        {
398251881Speter          /* We hit EOF instead of EOL. */
399251881Speter          *eof = TRUE;
400251881Speter          return SVN_NO_ERROR;
401251881Speter        }
402251881Speter      }
403251881Speter      while (eol_pos == NULL);
404251881Speter
405251881Speter      /* Number of bytes we actually consumed (i.e. line + EOF).
406251881Speter       * We need to "return" the rest to the stream by moving its
407251881Speter       * read pointer.
408251881Speter       */
409251881Speter      total_parsed = eol_pos - str->data + eol_len;
410251881Speter
411251881Speter      /* Terminate the string at the EOL postion and return it. */
412251881Speter      str->len = eol_pos - str->data;
413251881Speter      str->data[str->len] = 0;
414251881Speter    }
415251881Speter
416251881Speter  /* Move the stream read pointer to the first position behind the EOL.
417251881Speter   */
418251881Speter  SVN_ERR(svn_stream_seek(stream, mark));
419251881Speter  return svn_error_trace(svn_stream_skip(stream, total_parsed));
420251881Speter}
421251881Speter
422251881Speter/* Guts of svn_stream_readline().
423251881Speter * Returns the line read from STREAM in *STRINGBUF, and indicates
424251881Speter * end-of-file in *EOF.  EOL must point to the desired end-of-line
425251881Speter * indicator.  STRINGBUF is allocated in POOL. */
426251881Speterstatic svn_error_t *
427251881Speterstream_readline(svn_stringbuf_t **stringbuf,
428251881Speter                svn_boolean_t *eof,
429251881Speter                const char *eol,
430251881Speter                svn_stream_t *stream,
431251881Speter                apr_pool_t *pool)
432251881Speter{
433251881Speter  *eof = FALSE;
434251881Speter
435251881Speter  /* Often, we operate on APR file or string-based streams and know what
436251881Speter   * EOL we are looking for. Optimize that common case.
437251881Speter   */
438251881Speter  if (svn_stream_supports_mark(stream) &&
439251881Speter      svn_stream__is_buffered(stream))
440251881Speter    {
441251881Speter      /* We can efficiently read chunks speculatively and reposition the
442251881Speter       * stream pointer to the end of the line once we found that.
443251881Speter       */
444251881Speter      SVN_ERR(stream_readline_chunky(stringbuf,
445251881Speter                                     eof,
446251881Speter                                     eol,
447251881Speter                                     stream,
448251881Speter                                     pool));
449251881Speter    }
450251881Speter  else
451251881Speter    {
452251881Speter      /* Use the standard byte-byte implementation.
453251881Speter       */
454251881Speter      SVN_ERR(stream_readline_bytewise(stringbuf,
455251881Speter                                       eof,
456251881Speter                                       eol,
457251881Speter                                       stream,
458251881Speter                                       pool));
459251881Speter    }
460251881Speter
461251881Speter  return SVN_NO_ERROR;
462251881Speter}
463251881Speter
464251881Spetersvn_error_t *
465251881Spetersvn_stream_readline(svn_stream_t *stream,
466251881Speter                    svn_stringbuf_t **stringbuf,
467251881Speter                    const char *eol,
468251881Speter                    svn_boolean_t *eof,
469251881Speter                    apr_pool_t *pool)
470251881Speter{
471251881Speter  return svn_error_trace(stream_readline(stringbuf, eof, eol, stream,
472251881Speter                                         pool));
473251881Speter}
474251881Speter
475251881Spetersvn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
476251881Speter                              svn_cancel_func_t cancel_func,
477251881Speter                              void *cancel_baton,
478251881Speter                              apr_pool_t *scratch_pool)
479251881Speter{
480251881Speter  char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
481251881Speter  svn_error_t *err;
482251881Speter  svn_error_t *err2;
483251881Speter
484251881Speter  /* Read and write chunks until we get a short read, indicating the
485251881Speter     end of the stream.  (We can't get a short write without an
486251881Speter     associated error.) */
487251881Speter  while (1)
488251881Speter    {
489251881Speter      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
490251881Speter
491251881Speter      if (cancel_func)
492251881Speter        {
493251881Speter          err = cancel_func(cancel_baton);
494251881Speter          if (err)
495251881Speter             break;
496251881Speter        }
497251881Speter
498251881Speter      err = svn_stream_read(from, buf, &len);
499251881Speter      if (err)
500251881Speter         break;
501251881Speter
502251881Speter      if (len > 0)
503251881Speter        err = svn_stream_write(to, buf, &len);
504251881Speter
505251881Speter      if (err || (len != SVN__STREAM_CHUNK_SIZE))
506251881Speter          break;
507251881Speter    }
508251881Speter
509251881Speter  err2 = svn_error_compose_create(svn_stream_close(from),
510251881Speter                                  svn_stream_close(to));
511251881Speter
512251881Speter  return svn_error_compose_create(err, err2);
513251881Speter}
514251881Speter
515251881Spetersvn_error_t *
516251881Spetersvn_stream_contents_same2(svn_boolean_t *same,
517251881Speter                          svn_stream_t *stream1,
518251881Speter                          svn_stream_t *stream2,
519251881Speter                          apr_pool_t *pool)
520251881Speter{
521251881Speter  char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
522251881Speter  char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
523251881Speter  apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;
524251881Speter  apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;
525251881Speter  svn_error_t *err = NULL;
526251881Speter
527251881Speter  *same = TRUE;  /* assume TRUE, until disproved below */
528251881Speter  while (bytes_read1 == SVN__STREAM_CHUNK_SIZE
529251881Speter         && bytes_read2 == SVN__STREAM_CHUNK_SIZE)
530251881Speter    {
531251881Speter      err = svn_stream_read(stream1, buf1, &bytes_read1);
532251881Speter      if (err)
533251881Speter        break;
534251881Speter      err = svn_stream_read(stream2, buf2, &bytes_read2);
535251881Speter      if (err)
536251881Speter        break;
537251881Speter
538251881Speter      if ((bytes_read1 != bytes_read2)
539251881Speter          || (memcmp(buf1, buf2, bytes_read1)))
540251881Speter        {
541251881Speter          *same = FALSE;
542251881Speter          break;
543251881Speter        }
544251881Speter    }
545251881Speter
546251881Speter  return svn_error_compose_create(err,
547251881Speter                                  svn_error_compose_create(
548251881Speter                                    svn_stream_close(stream1),
549251881Speter                                    svn_stream_close(stream2)));
550251881Speter}
551251881Speter
552251881Speter
553251881Speter/*** Stream implementation utilities ***/
554251881Speter
555251881Speter/* Skip data from any stream by reading and simply discarding it. */
556251881Speterstatic svn_error_t *
557251881Speterskip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn)
558251881Speter{
559251881Speter  apr_size_t bytes_read = 1;
560251881Speter  char buffer[4096];
561251881Speter  apr_size_t to_read = len;
562251881Speter
563251881Speter  while ((to_read > 0) && (bytes_read > 0))
564251881Speter    {
565251881Speter      bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read;
566251881Speter      SVN_ERR(read_fn(baton, buffer, &bytes_read));
567251881Speter      to_read -= bytes_read;
568251881Speter    }
569251881Speter
570251881Speter  return SVN_NO_ERROR;
571251881Speter}
572251881Speter
573251881Speter
574251881Speter
575251881Speter/*** Generic readable empty stream ***/
576251881Speter
577251881Speterstatic svn_error_t *
578251881Speterread_handler_empty(void *baton, char *buffer, apr_size_t *len)
579251881Speter{
580251881Speter  *len = 0;
581251881Speter  return SVN_NO_ERROR;
582251881Speter}
583251881Speter
584251881Speterstatic svn_error_t *
585251881Speterwrite_handler_empty(void *baton, const char *data, apr_size_t *len)
586251881Speter{
587251881Speter  return SVN_NO_ERROR;
588251881Speter}
589251881Speter
590251881Speterstatic svn_error_t *
591251881Spetermark_handler_empty(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
592251881Speter{
593251881Speter  *mark = NULL; /* Seek to start of stream marker */
594251881Speter  return SVN_NO_ERROR;
595251881Speter}
596251881Speter
597251881Speterstatic svn_error_t *
598251881Speterseek_handler_empty(void *baton, const svn_stream_mark_t *mark)
599251881Speter{
600251881Speter  return SVN_NO_ERROR;
601251881Speter}
602251881Speter
603251881Speterstatic svn_boolean_t
604251881Speteris_buffered_handler_empty(void *baton)
605251881Speter{
606251881Speter  return FALSE;
607251881Speter}
608251881Speter
609251881Speter
610251881Spetersvn_stream_t *
611251881Spetersvn_stream_empty(apr_pool_t *pool)
612251881Speter{
613251881Speter  svn_stream_t *stream;
614251881Speter
615251881Speter  stream = svn_stream_create(NULL, pool);
616251881Speter  svn_stream_set_read(stream, read_handler_empty);
617251881Speter  svn_stream_set_write(stream, write_handler_empty);
618251881Speter  svn_stream_set_mark(stream, mark_handler_empty);
619251881Speter  svn_stream_set_seek(stream, seek_handler_empty);
620251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_empty);
621251881Speter  return stream;
622251881Speter}
623251881Speter
624251881Speter
625251881Speter
626251881Speter/*** Stream duplication support ***/
627251881Speterstruct baton_tee {
628251881Speter  svn_stream_t *out1;
629251881Speter  svn_stream_t *out2;
630251881Speter};
631251881Speter
632251881Speter
633251881Speterstatic svn_error_t *
634251881Speterwrite_handler_tee(void *baton, const char *data, apr_size_t *len)
635251881Speter{
636251881Speter  struct baton_tee *bt = baton;
637251881Speter
638251881Speter  SVN_ERR(svn_stream_write(bt->out1, data, len));
639251881Speter  SVN_ERR(svn_stream_write(bt->out2, data, len));
640251881Speter
641251881Speter  return SVN_NO_ERROR;
642251881Speter}
643251881Speter
644251881Speter
645251881Speterstatic svn_error_t *
646251881Speterclose_handler_tee(void *baton)
647251881Speter{
648251881Speter  struct baton_tee *bt = baton;
649251881Speter
650251881Speter  SVN_ERR(svn_stream_close(bt->out1));
651251881Speter  SVN_ERR(svn_stream_close(bt->out2));
652251881Speter
653251881Speter  return SVN_NO_ERROR;
654251881Speter}
655251881Speter
656251881Speter
657251881Spetersvn_stream_t *
658251881Spetersvn_stream_tee(svn_stream_t *out1,
659251881Speter               svn_stream_t *out2,
660251881Speter               apr_pool_t *pool)
661251881Speter{
662251881Speter  struct baton_tee *baton;
663251881Speter  svn_stream_t *stream;
664251881Speter
665251881Speter  if (out1 == NULL)
666251881Speter    return out2;
667251881Speter
668251881Speter  if (out2 == NULL)
669251881Speter    return out1;
670251881Speter
671251881Speter  baton = apr_palloc(pool, sizeof(*baton));
672251881Speter  baton->out1 = out1;
673251881Speter  baton->out2 = out2;
674251881Speter  stream = svn_stream_create(baton, pool);
675251881Speter  svn_stream_set_write(stream, write_handler_tee);
676251881Speter  svn_stream_set_close(stream, close_handler_tee);
677251881Speter
678251881Speter  return stream;
679251881Speter}
680251881Speter
681251881Speter
682251881Speter
683251881Speter/*** Ownership detaching stream ***/
684251881Speter
685251881Speterstatic svn_error_t *
686251881Speterread_handler_disown(void *baton, char *buffer, apr_size_t *len)
687251881Speter{
688251881Speter  return svn_error_trace(svn_stream_read(baton, buffer, len));
689251881Speter}
690251881Speter
691251881Speterstatic svn_error_t *
692251881Speterskip_handler_disown(void *baton, apr_size_t len)
693251881Speter{
694251881Speter  return svn_error_trace(svn_stream_skip(baton, len));
695251881Speter}
696251881Speter
697251881Speterstatic svn_error_t *
698251881Speterwrite_handler_disown(void *baton, const char *buffer, apr_size_t *len)
699251881Speter{
700251881Speter  return svn_error_trace(svn_stream_write(baton, buffer, len));
701251881Speter}
702251881Speter
703251881Speterstatic svn_error_t *
704251881Spetermark_handler_disown(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
705251881Speter{
706251881Speter  return svn_error_trace(svn_stream_mark(baton, mark, pool));
707251881Speter}
708251881Speter
709251881Speterstatic svn_error_t *
710251881Speterseek_handler_disown(void *baton, const svn_stream_mark_t *mark)
711251881Speter{
712251881Speter  return svn_error_trace(svn_stream_seek(baton, mark));
713251881Speter}
714251881Speter
715251881Speterstatic svn_boolean_t
716251881Speteris_buffered_handler_disown(void *baton)
717251881Speter{
718251881Speter  return svn_stream__is_buffered(baton);
719251881Speter}
720251881Speter
721251881Spetersvn_stream_t *
722251881Spetersvn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
723251881Speter{
724251881Speter  svn_stream_t *s = svn_stream_create(stream, pool);
725251881Speter
726251881Speter  svn_stream_set_read(s, read_handler_disown);
727251881Speter  svn_stream_set_skip(s, skip_handler_disown);
728251881Speter  svn_stream_set_write(s, write_handler_disown);
729251881Speter  svn_stream_set_mark(s, mark_handler_disown);
730251881Speter  svn_stream_set_seek(s, seek_handler_disown);
731251881Speter  svn_stream__set_is_buffered(s, is_buffered_handler_disown);
732251881Speter
733251881Speter  return s;
734251881Speter}
735251881Speter
736251881Speter
737251881Speter
738251881Speter/*** Generic stream for APR files ***/
739251881Speterstruct baton_apr {
740251881Speter  apr_file_t *file;
741251881Speter  apr_pool_t *pool;
742251881Speter};
743251881Speter
744251881Speter/* svn_stream_mark_t for streams backed by APR files. */
745251881Speterstruct mark_apr {
746251881Speter  apr_off_t off;
747251881Speter};
748251881Speter
749251881Speterstatic svn_error_t *
750251881Speterread_handler_apr(void *baton, char *buffer, apr_size_t *len)
751251881Speter{
752251881Speter  struct baton_apr *btn = baton;
753251881Speter  svn_error_t *err;
754251881Speter  svn_boolean_t eof;
755251881Speter
756251881Speter  if (*len == 1)
757251881Speter    {
758251881Speter      err = svn_io_file_getc(buffer, btn->file, btn->pool);
759251881Speter      if (err)
760251881Speter        {
761251881Speter          *len = 0;
762251881Speter          if (APR_STATUS_IS_EOF(err->apr_err))
763251881Speter            {
764251881Speter              svn_error_clear(err);
765251881Speter              err = SVN_NO_ERROR;
766251881Speter            }
767251881Speter        }
768251881Speter    }
769251881Speter  else
770251881Speter    err = svn_io_file_read_full2(btn->file, buffer, *len, len,
771251881Speter                                 &eof, btn->pool);
772251881Speter
773251881Speter  return svn_error_trace(err);
774251881Speter}
775251881Speter
776251881Speterstatic svn_error_t *
777251881Speterskip_handler_apr(void *baton, apr_size_t len)
778251881Speter{
779251881Speter  struct baton_apr *btn = baton;
780251881Speter  apr_off_t offset = len;
781251881Speter
782251881Speter  return svn_error_trace(
783251881Speter            svn_io_file_seek(btn->file, APR_CUR, &offset, btn->pool));
784251881Speter}
785251881Speter
786251881Speterstatic svn_error_t *
787251881Speterwrite_handler_apr(void *baton, const char *data, apr_size_t *len)
788251881Speter{
789251881Speter  struct baton_apr *btn = baton;
790251881Speter  svn_error_t *err;
791251881Speter
792251881Speter  if (*len == 1)
793251881Speter    {
794251881Speter      err = svn_io_file_putc(*data, btn->file, btn->pool);
795251881Speter      if (err)
796251881Speter        *len = 0;
797251881Speter    }
798251881Speter  else
799251881Speter    err = svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
800251881Speter
801251881Speter  return svn_error_trace(err);
802251881Speter}
803251881Speter
804251881Speterstatic svn_error_t *
805251881Speterclose_handler_apr(void *baton)
806251881Speter{
807251881Speter  struct baton_apr *btn = baton;
808251881Speter
809251881Speter  return svn_error_trace(svn_io_file_close(btn->file, btn->pool));
810251881Speter}
811251881Speter
812251881Speterstatic svn_error_t *
813251881Spetermark_handler_apr(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
814251881Speter{
815251881Speter  struct baton_apr *btn = baton;
816251881Speter  struct mark_apr *mark_apr;
817251881Speter
818251881Speter  mark_apr = apr_palloc(pool, sizeof(*mark_apr));
819251881Speter  mark_apr->off = 0;
820251881Speter  SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &mark_apr->off, btn->pool));
821251881Speter  *mark = (svn_stream_mark_t *)mark_apr;
822251881Speter  return SVN_NO_ERROR;
823251881Speter}
824251881Speter
825251881Speterstatic svn_error_t *
826251881Speterseek_handler_apr(void *baton, const svn_stream_mark_t *mark)
827251881Speter{
828251881Speter  struct baton_apr *btn = baton;
829251881Speter  apr_off_t offset = (mark != NULL) ? ((const struct mark_apr *)mark)->off : 0;
830251881Speter
831251881Speter  SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool));
832251881Speter
833251881Speter  return SVN_NO_ERROR;
834251881Speter}
835251881Speter
836251881Speterstatic svn_boolean_t
837251881Speteris_buffered_handler_apr(void *baton)
838251881Speter{
839251881Speter  struct baton_apr *btn = baton;
840251881Speter  return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0;
841251881Speter}
842251881Speter
843251881Spetersvn_error_t *
844251881Spetersvn_stream_open_readonly(svn_stream_t **stream,
845251881Speter                         const char *path,
846251881Speter                         apr_pool_t *result_pool,
847251881Speter                         apr_pool_t *scratch_pool)
848251881Speter{
849251881Speter  apr_file_t *file;
850251881Speter
851251881Speter  SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
852251881Speter                           APR_OS_DEFAULT, result_pool));
853251881Speter  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
854251881Speter
855251881Speter  return SVN_NO_ERROR;
856251881Speter}
857251881Speter
858251881Speter
859251881Spetersvn_error_t *
860251881Spetersvn_stream_open_writable(svn_stream_t **stream,
861251881Speter                         const char *path,
862251881Speter                         apr_pool_t *result_pool,
863251881Speter                         apr_pool_t *scratch_pool)
864251881Speter{
865251881Speter  apr_file_t *file;
866251881Speter
867251881Speter  SVN_ERR(svn_io_file_open(&file, path,
868251881Speter                           APR_WRITE
869251881Speter                             | APR_BUFFERED
870251881Speter                             | APR_CREATE
871251881Speter                             | APR_EXCL,
872251881Speter                           APR_OS_DEFAULT, result_pool));
873251881Speter  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
874251881Speter
875251881Speter  return SVN_NO_ERROR;
876251881Speter}
877251881Speter
878251881Speter
879251881Spetersvn_error_t *
880251881Spetersvn_stream_open_unique(svn_stream_t **stream,
881251881Speter                       const char **temp_path,
882251881Speter                       const char *dirpath,
883251881Speter                       svn_io_file_del_t delete_when,
884251881Speter                       apr_pool_t *result_pool,
885251881Speter                       apr_pool_t *scratch_pool)
886251881Speter{
887251881Speter  apr_file_t *file;
888251881Speter
889251881Speter  SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath,
890251881Speter                                   delete_when, result_pool, scratch_pool));
891251881Speter  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
892251881Speter
893251881Speter  return SVN_NO_ERROR;
894251881Speter}
895251881Speter
896251881Speter
897251881Spetersvn_stream_t *
898251881Spetersvn_stream_from_aprfile2(apr_file_t *file,
899251881Speter                         svn_boolean_t disown,
900251881Speter                         apr_pool_t *pool)
901251881Speter{
902251881Speter  struct baton_apr *baton;
903251881Speter  svn_stream_t *stream;
904251881Speter
905251881Speter  if (file == NULL)
906251881Speter    return svn_stream_empty(pool);
907251881Speter
908251881Speter  baton = apr_palloc(pool, sizeof(*baton));
909251881Speter  baton->file = file;
910251881Speter  baton->pool = pool;
911251881Speter  stream = svn_stream_create(baton, pool);
912251881Speter  svn_stream_set_read(stream, read_handler_apr);
913251881Speter  svn_stream_set_write(stream, write_handler_apr);
914251881Speter  svn_stream_set_skip(stream, skip_handler_apr);
915251881Speter  svn_stream_set_mark(stream, mark_handler_apr);
916251881Speter  svn_stream_set_seek(stream, seek_handler_apr);
917251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_apr);
918253734Speter  stream->file = file;
919251881Speter
920251881Speter  if (! disown)
921251881Speter    svn_stream_set_close(stream, close_handler_apr);
922251881Speter
923251881Speter  return stream;
924251881Speter}
925251881Speter
926253734Speterapr_file_t *
927253734Spetersvn_stream__aprfile(svn_stream_t *stream)
928253734Speter{
929253734Speter  return stream->file;
930253734Speter}
931253734Speter
932251881Speter
933251881Speter/* Compressed stream support */
934251881Speter
935251881Speter#define ZBUFFER_SIZE 4096       /* The size of the buffer the
936251881Speter                                   compressed stream uses to read from
937251881Speter                                   the substream. Basically an
938251881Speter                                   arbitrary value, picked to be about
939251881Speter                                   page-sized. */
940251881Speter
941251881Speterstruct zbaton {
942251881Speter  z_stream *in;                 /* compressed stream for reading */
943251881Speter  z_stream *out;                /* compressed stream for writing */
944251881Speter  svn_read_fn_t read;           /* substream's read function */
945251881Speter  svn_write_fn_t write;         /* substream's write function */
946251881Speter  svn_close_fn_t close;         /* substream's close function */
947251881Speter  void *read_buffer;            /* buffer   used   for  reading   from
948251881Speter                                   substream */
949251881Speter  int read_flush;               /* what flush mode to use while
950251881Speter                                   reading */
951251881Speter  apr_pool_t *pool;             /* The pool this baton is allocated
952251881Speter                                   on */
953251881Speter  void *subbaton;               /* The substream's baton */
954251881Speter};
955251881Speter
956251881Speter/* zlib alloc function. opaque is the pool we need. */
957251881Speterstatic voidpf
958251881Speterzalloc(voidpf opaque, uInt items, uInt size)
959251881Speter{
960251881Speter  apr_pool_t *pool = opaque;
961251881Speter
962251881Speter  return apr_palloc(pool, items * size);
963251881Speter}
964251881Speter
965251881Speter/* zlib free function */
966251881Speterstatic void
967251881Speterzfree(voidpf opaque, voidpf address)
968251881Speter{
969251881Speter  /* Empty, since we allocate on the pool */
970251881Speter}
971251881Speter
972251881Speter/* Helper function to figure out the sync mode */
973251881Speterstatic svn_error_t *
974251881Speterread_helper_gz(svn_read_fn_t read_fn,
975251881Speter               void *baton,
976251881Speter               char *buffer,
977251881Speter               uInt *len, int *zflush)
978251881Speter{
979251881Speter  uInt orig_len = *len;
980251881Speter
981251881Speter  /* There's no reason this value should grow bigger than the range of
982251881Speter     uInt, but Subversion's API requires apr_size_t. */
983251881Speter  apr_size_t apr_len = (apr_size_t) *len;
984251881Speter
985251881Speter  SVN_ERR((*read_fn)(baton, buffer, &apr_len));
986251881Speter
987251881Speter  /* Type cast back to uInt type that zlib uses.  On LP64 platforms
988251881Speter     apr_size_t will be bigger than uInt. */
989251881Speter  *len = (uInt) apr_len;
990251881Speter
991251881Speter  /* I wanted to use Z_FINISH here, but we need to know our buffer is
992251881Speter     big enough */
993251881Speter  *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;
994251881Speter
995251881Speter  return SVN_NO_ERROR;
996251881Speter}
997251881Speter
998251881Speter/* Handle reading from a compressed stream */
999251881Speterstatic svn_error_t *
1000251881Speterread_handler_gz(void *baton, char *buffer, apr_size_t *len)
1001251881Speter{
1002251881Speter  struct zbaton *btn = baton;
1003251881Speter  int zerr;
1004251881Speter
1005251881Speter  if (btn->in == NULL)
1006251881Speter    {
1007251881Speter      btn->in = apr_palloc(btn->pool, sizeof(z_stream));
1008251881Speter      btn->in->zalloc = zalloc;
1009251881Speter      btn->in->zfree = zfree;
1010251881Speter      btn->in->opaque = btn->pool;
1011251881Speter      btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);
1012251881Speter      btn->in->next_in = btn->read_buffer;
1013251881Speter      btn->in->avail_in = ZBUFFER_SIZE;
1014251881Speter
1015251881Speter      SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
1016251881Speter                             &btn->in->avail_in, &btn->read_flush));
1017251881Speter
1018251881Speter      zerr = inflateInit(btn->in);
1019251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "inflateInit", btn->in->msg));
1020251881Speter    }
1021251881Speter
1022251881Speter  btn->in->next_out = (Bytef *) buffer;
1023251881Speter  btn->in->avail_out = (uInt) *len;
1024251881Speter
1025251881Speter  while (btn->in->avail_out > 0)
1026251881Speter    {
1027251881Speter      if (btn->in->avail_in <= 0)
1028251881Speter        {
1029251881Speter          btn->in->avail_in = ZBUFFER_SIZE;
1030251881Speter          btn->in->next_in = btn->read_buffer;
1031251881Speter          SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
1032251881Speter                                 &btn->in->avail_in, &btn->read_flush));
1033251881Speter        }
1034251881Speter
1035251881Speter      /* Short read means underlying stream has run out. */
1036251881Speter      if (btn->in->avail_in == 0)
1037251881Speter        {
1038251881Speter          *len = 0;
1039251881Speter          return SVN_NO_ERROR;
1040251881Speter        }
1041251881Speter
1042251881Speter      zerr = inflate(btn->in, btn->read_flush);
1043251881Speter      if (zerr == Z_STREAM_END)
1044251881Speter        break;
1045251881Speter      else if (zerr != Z_OK)
1046251881Speter        return svn_error_trace(svn_error__wrap_zlib(zerr, "inflate",
1047251881Speter                                                    btn->in->msg));
1048251881Speter    }
1049251881Speter
1050251881Speter  *len -= btn->in->avail_out;
1051251881Speter  return SVN_NO_ERROR;
1052251881Speter}
1053251881Speter
1054251881Speter/* Compress data and write it to the substream */
1055251881Speterstatic svn_error_t *
1056251881Speterwrite_handler_gz(void *baton, const char *buffer, apr_size_t *len)
1057251881Speter{
1058251881Speter  struct zbaton *btn = baton;
1059251881Speter  apr_pool_t *subpool;
1060251881Speter  void *write_buf;
1061251881Speter  apr_size_t buf_size, write_len;
1062251881Speter  int zerr;
1063251881Speter
1064251881Speter  if (btn->out == NULL)
1065251881Speter    {
1066251881Speter      btn->out = apr_palloc(btn->pool, sizeof(z_stream));
1067251881Speter      btn->out->zalloc = zalloc;
1068251881Speter      btn->out->zfree = zfree;
1069251881Speter      btn->out->opaque =  btn->pool;
1070251881Speter
1071251881Speter      zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);
1072251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "deflateInit", btn->out->msg));
1073251881Speter    }
1074251881Speter
1075251881Speter  /* The largest buffer we should need is 0.1% larger than the
1076251881Speter     compressed data, + 12 bytes. This info comes from zlib.h.  */
1077251881Speter  buf_size = *len + (*len / 1000) + 13;
1078251881Speter  subpool = svn_pool_create(btn->pool);
1079251881Speter  write_buf = apr_palloc(subpool, buf_size);
1080251881Speter
1081251881Speter  btn->out->next_in = (Bytef *) buffer;  /* Casting away const! */
1082251881Speter  btn->out->avail_in = (uInt) *len;
1083251881Speter
1084251881Speter  while (btn->out->avail_in > 0)
1085251881Speter    {
1086251881Speter      btn->out->next_out = write_buf;
1087251881Speter      btn->out->avail_out = (uInt) buf_size;
1088251881Speter
1089251881Speter      zerr = deflate(btn->out, Z_NO_FLUSH);
1090251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg));
1091251881Speter      write_len = buf_size - btn->out->avail_out;
1092251881Speter      if (write_len > 0)
1093251881Speter        SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len));
1094251881Speter    }
1095251881Speter
1096251881Speter  svn_pool_destroy(subpool);
1097251881Speter
1098251881Speter  return SVN_NO_ERROR;
1099251881Speter}
1100251881Speter
1101251881Speter/* Handle flushing and closing the stream */
1102251881Speterstatic svn_error_t *
1103251881Speterclose_handler_gz(void *baton)
1104251881Speter{
1105251881Speter  struct zbaton *btn = baton;
1106251881Speter  int zerr;
1107251881Speter
1108251881Speter  if (btn->in != NULL)
1109251881Speter    {
1110251881Speter      zerr = inflateEnd(btn->in);
1111251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "inflateEnd", btn->in->msg));
1112251881Speter    }
1113251881Speter
1114251881Speter  if (btn->out != NULL)
1115251881Speter    {
1116251881Speter      void *buf;
1117251881Speter      apr_size_t write_len;
1118251881Speter
1119251881Speter      buf = apr_palloc(btn->pool, ZBUFFER_SIZE);
1120251881Speter
1121251881Speter      while (TRUE)
1122251881Speter        {
1123251881Speter          btn->out->next_out = buf;
1124251881Speter          btn->out->avail_out = ZBUFFER_SIZE;
1125251881Speter
1126251881Speter          zerr = deflate(btn->out, Z_FINISH);
1127251881Speter          if (zerr != Z_STREAM_END && zerr != Z_OK)
1128251881Speter            return svn_error_trace(svn_error__wrap_zlib(zerr, "deflate",
1129251881Speter                                                        btn->out->msg));
1130251881Speter          write_len = ZBUFFER_SIZE - btn->out->avail_out;
1131251881Speter          if (write_len > 0)
1132251881Speter            SVN_ERR(btn->write(btn->subbaton, buf, &write_len));
1133251881Speter          if (zerr == Z_STREAM_END)
1134251881Speter            break;
1135251881Speter        }
1136251881Speter
1137251881Speter      zerr = deflateEnd(btn->out);
1138251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg));
1139251881Speter    }
1140251881Speter
1141251881Speter  if (btn->close != NULL)
1142251881Speter    return svn_error_trace(btn->close(btn->subbaton));
1143251881Speter  else
1144251881Speter    return SVN_NO_ERROR;
1145251881Speter}
1146251881Speter
1147251881Speter
1148251881Spetersvn_stream_t *
1149251881Spetersvn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)
1150251881Speter{
1151251881Speter  struct svn_stream_t *zstream;
1152251881Speter  struct zbaton *baton;
1153251881Speter
1154251881Speter  assert(stream != NULL);
1155251881Speter
1156251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1157251881Speter  baton->in = baton->out = NULL;
1158251881Speter  baton->read = stream->read_fn;
1159251881Speter  baton->write = stream->write_fn;
1160251881Speter  baton->close = stream->close_fn;
1161251881Speter  baton->subbaton = stream->baton;
1162251881Speter  baton->pool = pool;
1163251881Speter  baton->read_buffer = NULL;
1164251881Speter  baton->read_flush = Z_SYNC_FLUSH;
1165251881Speter
1166251881Speter  zstream = svn_stream_create(baton, pool);
1167251881Speter  svn_stream_set_read(zstream, read_handler_gz);
1168251881Speter  svn_stream_set_write(zstream, write_handler_gz);
1169251881Speter  svn_stream_set_close(zstream, close_handler_gz);
1170251881Speter
1171251881Speter  return zstream;
1172251881Speter}
1173251881Speter
1174251881Speter
1175251881Speter/* Checksummed stream support */
1176251881Speter
1177251881Speterstruct checksum_stream_baton
1178251881Speter{
1179251881Speter  svn_checksum_ctx_t *read_ctx, *write_ctx;
1180251881Speter  svn_checksum_t **read_checksum;  /* Output value. */
1181251881Speter  svn_checksum_t **write_checksum;  /* Output value. */
1182251881Speter  svn_stream_t *proxy;
1183251881Speter
1184251881Speter  /* True if more data should be read when closing the stream. */
1185251881Speter  svn_boolean_t read_more;
1186251881Speter
1187251881Speter  /* Pool to allocate read buffer and output values from. */
1188251881Speter  apr_pool_t *pool;
1189251881Speter};
1190251881Speter
1191251881Speterstatic svn_error_t *
1192251881Speterread_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1193251881Speter{
1194251881Speter  struct checksum_stream_baton *btn = baton;
1195251881Speter  apr_size_t saved_len = *len;
1196251881Speter
1197251881Speter  SVN_ERR(svn_stream_read(btn->proxy, buffer, len));
1198251881Speter
1199251881Speter  if (btn->read_checksum)
1200251881Speter    SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1201251881Speter
1202251881Speter  if (saved_len != *len)
1203251881Speter    btn->read_more = FALSE;
1204251881Speter
1205251881Speter  return SVN_NO_ERROR;
1206251881Speter}
1207251881Speter
1208251881Speter
1209251881Speterstatic svn_error_t *
1210251881Speterwrite_handler_checksum(void *baton, const char *buffer, apr_size_t *len)
1211251881Speter{
1212251881Speter  struct checksum_stream_baton *btn = baton;
1213251881Speter
1214251881Speter  if (btn->write_checksum && *len > 0)
1215251881Speter    SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len));
1216251881Speter
1217251881Speter  return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1218251881Speter}
1219251881Speter
1220251881Speter
1221251881Speterstatic svn_error_t *
1222251881Speterclose_handler_checksum(void *baton)
1223251881Speter{
1224251881Speter  struct checksum_stream_baton *btn = baton;
1225251881Speter
1226251881Speter  /* If we're supposed to drain the stream, do so before finalizing the
1227251881Speter     checksum. */
1228251881Speter  if (btn->read_more)
1229251881Speter    {
1230251881Speter      char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);
1231251881Speter      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1232251881Speter
1233251881Speter      do
1234251881Speter        {
1235251881Speter          SVN_ERR(read_handler_checksum(baton, buf, &len));
1236251881Speter        }
1237251881Speter      while (btn->read_more);
1238251881Speter    }
1239251881Speter
1240251881Speter  if (btn->read_ctx)
1241251881Speter    SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool));
1242251881Speter
1243251881Speter  if (btn->write_ctx)
1244251881Speter    SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool));
1245251881Speter
1246251881Speter  return svn_error_trace(svn_stream_close(btn->proxy));
1247251881Speter}
1248251881Speter
1249251881Speter
1250251881Spetersvn_stream_t *
1251251881Spetersvn_stream_checksummed2(svn_stream_t *stream,
1252251881Speter                        svn_checksum_t **read_checksum,
1253251881Speter                        svn_checksum_t **write_checksum,
1254251881Speter                        svn_checksum_kind_t checksum_kind,
1255251881Speter                        svn_boolean_t read_all,
1256251881Speter                        apr_pool_t *pool)
1257251881Speter{
1258251881Speter  svn_stream_t *s;
1259251881Speter  struct checksum_stream_baton *baton;
1260251881Speter
1261251881Speter  if (read_checksum == NULL && write_checksum == NULL)
1262251881Speter    return stream;
1263251881Speter
1264251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1265251881Speter  if (read_checksum)
1266251881Speter    baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1267251881Speter  else
1268251881Speter    baton->read_ctx = NULL;
1269251881Speter
1270251881Speter  if (write_checksum)
1271251881Speter    baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1272251881Speter  else
1273251881Speter    baton->write_ctx = NULL;
1274251881Speter
1275251881Speter  baton->read_checksum = read_checksum;
1276251881Speter  baton->write_checksum = write_checksum;
1277251881Speter  baton->proxy = stream;
1278251881Speter  baton->read_more = read_all;
1279251881Speter  baton->pool = pool;
1280251881Speter
1281251881Speter  s = svn_stream_create(baton, pool);
1282251881Speter  svn_stream_set_read(s, read_handler_checksum);
1283251881Speter  svn_stream_set_write(s, write_handler_checksum);
1284251881Speter  svn_stream_set_close(s, close_handler_checksum);
1285251881Speter  return s;
1286251881Speter}
1287251881Speter
1288251881Speterstruct md5_stream_baton
1289251881Speter{
1290251881Speter  const unsigned char **read_digest;
1291251881Speter  const unsigned char **write_digest;
1292251881Speter  svn_checksum_t *read_checksum;
1293251881Speter  svn_checksum_t *write_checksum;
1294251881Speter  svn_stream_t *proxy;
1295251881Speter  apr_pool_t *pool;
1296251881Speter};
1297251881Speter
1298251881Speterstatic svn_error_t *
1299251881Speterread_handler_md5(void *baton, char *buffer, apr_size_t *len)
1300251881Speter{
1301251881Speter  struct md5_stream_baton *btn = baton;
1302251881Speter  return svn_error_trace(svn_stream_read(btn->proxy, buffer, len));
1303251881Speter}
1304251881Speter
1305251881Speterstatic svn_error_t *
1306251881Speterskip_handler_md5(void *baton, apr_size_t len)
1307251881Speter{
1308251881Speter  struct md5_stream_baton *btn = baton;
1309251881Speter  return svn_error_trace(svn_stream_skip(btn->proxy, len));
1310251881Speter}
1311251881Speter
1312251881Speterstatic svn_error_t *
1313251881Speterwrite_handler_md5(void *baton, const char *buffer, apr_size_t *len)
1314251881Speter{
1315251881Speter  struct md5_stream_baton *btn = baton;
1316251881Speter  return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1317251881Speter}
1318251881Speter
1319251881Speterstatic svn_error_t *
1320251881Speterclose_handler_md5(void *baton)
1321251881Speter{
1322251881Speter  struct md5_stream_baton *btn = baton;
1323251881Speter
1324251881Speter  SVN_ERR(svn_stream_close(btn->proxy));
1325251881Speter
1326251881Speter  if (btn->read_digest)
1327251881Speter    *btn->read_digest
1328251881Speter      = apr_pmemdup(btn->pool, btn->read_checksum->digest,
1329251881Speter                    APR_MD5_DIGESTSIZE);
1330251881Speter
1331251881Speter  if (btn->write_digest)
1332251881Speter    *btn->write_digest
1333251881Speter      = apr_pmemdup(btn->pool, btn->write_checksum->digest,
1334251881Speter                    APR_MD5_DIGESTSIZE);
1335251881Speter
1336251881Speter  return SVN_NO_ERROR;
1337251881Speter}
1338251881Speter
1339251881Speter
1340251881Spetersvn_stream_t *
1341251881Spetersvn_stream_checksummed(svn_stream_t *stream,
1342251881Speter                       const unsigned char **read_digest,
1343251881Speter                       const unsigned char **write_digest,
1344251881Speter                       svn_boolean_t read_all,
1345251881Speter                       apr_pool_t *pool)
1346251881Speter{
1347251881Speter  svn_stream_t *s;
1348251881Speter  struct md5_stream_baton *baton;
1349251881Speter
1350251881Speter  if (! read_digest && ! write_digest)
1351251881Speter    return stream;
1352251881Speter
1353251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1354251881Speter  baton->read_digest = read_digest;
1355251881Speter  baton->write_digest = write_digest;
1356251881Speter  baton->pool = pool;
1357251881Speter
1358251881Speter  /* Set BATON->proxy to a stream that will fill in BATON->read_checksum
1359251881Speter   * and BATON->write_checksum (if we want them) when it is closed. */
1360251881Speter  baton->proxy
1361251881Speter    = svn_stream_checksummed2(stream,
1362251881Speter                              read_digest ? &baton->read_checksum : NULL,
1363251881Speter                              write_digest ? &baton->write_checksum : NULL,
1364251881Speter                              svn_checksum_md5,
1365251881Speter                              read_all, pool);
1366251881Speter
1367251881Speter  /* Create a stream that will forward its read/write/close operations to
1368251881Speter   * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we
1369251881Speter   * want them) after it closes BATON->proxy. */
1370251881Speter  s = svn_stream_create(baton, pool);
1371251881Speter  svn_stream_set_read(s, read_handler_md5);
1372251881Speter  svn_stream_set_skip(s, skip_handler_md5);
1373251881Speter  svn_stream_set_write(s, write_handler_md5);
1374251881Speter  svn_stream_set_close(s, close_handler_md5);
1375251881Speter  return s;
1376251881Speter}
1377251881Speter
1378251881Speter
1379251881Speter
1380251881Speter
1381251881Speter/* Miscellaneous stream functions. */
1382251881Speterstruct stringbuf_stream_baton
1383251881Speter{
1384251881Speter  svn_stringbuf_t *str;
1385251881Speter  apr_size_t amt_read;
1386251881Speter};
1387251881Speter
1388251881Speter/* svn_stream_mark_t for streams backed by stringbufs. */
1389251881Speterstruct stringbuf_stream_mark {
1390251881Speter    apr_size_t pos;
1391251881Speter};
1392251881Speter
1393251881Speterstatic svn_error_t *
1394251881Speterread_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
1395251881Speter{
1396251881Speter  struct stringbuf_stream_baton *btn = baton;
1397251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1398251881Speter
1399251881Speter  *len = (*len > left_to_read) ? left_to_read : *len;
1400251881Speter  memcpy(buffer, btn->str->data + btn->amt_read, *len);
1401251881Speter  btn->amt_read += *len;
1402251881Speter  return SVN_NO_ERROR;
1403251881Speter}
1404251881Speter
1405251881Speterstatic svn_error_t *
1406251881Speterskip_handler_stringbuf(void *baton, apr_size_t len)
1407251881Speter{
1408251881Speter  struct stringbuf_stream_baton *btn = baton;
1409251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1410251881Speter
1411251881Speter  len = (len > left_to_read) ? left_to_read : len;
1412251881Speter  btn->amt_read += len;
1413251881Speter  return SVN_NO_ERROR;
1414251881Speter}
1415251881Speter
1416251881Speterstatic svn_error_t *
1417251881Speterwrite_handler_stringbuf(void *baton, const char *data, apr_size_t *len)
1418251881Speter{
1419251881Speter  struct stringbuf_stream_baton *btn = baton;
1420251881Speter
1421251881Speter  svn_stringbuf_appendbytes(btn->str, data, *len);
1422251881Speter  return SVN_NO_ERROR;
1423251881Speter}
1424251881Speter
1425251881Speterstatic svn_error_t *
1426251881Spetermark_handler_stringbuf(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1427251881Speter{
1428251881Speter  struct stringbuf_stream_baton *btn;
1429251881Speter  struct stringbuf_stream_mark *stringbuf_stream_mark;
1430251881Speter
1431251881Speter  btn = baton;
1432251881Speter
1433251881Speter  stringbuf_stream_mark = apr_palloc(pool, sizeof(*stringbuf_stream_mark));
1434251881Speter  stringbuf_stream_mark->pos = btn->amt_read;
1435251881Speter  *mark = (svn_stream_mark_t *)stringbuf_stream_mark;
1436251881Speter  return SVN_NO_ERROR;
1437251881Speter}
1438251881Speter
1439251881Speterstatic svn_error_t *
1440251881Speterseek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark)
1441251881Speter{
1442251881Speter  struct stringbuf_stream_baton *btn = baton;
1443251881Speter
1444251881Speter  if (mark != NULL)
1445251881Speter    {
1446251881Speter      const struct stringbuf_stream_mark *stringbuf_stream_mark;
1447251881Speter
1448251881Speter      stringbuf_stream_mark = (const struct stringbuf_stream_mark *)mark;
1449251881Speter      btn->amt_read = stringbuf_stream_mark->pos;
1450251881Speter    }
1451251881Speter  else
1452251881Speter    btn->amt_read = 0;
1453251881Speter
1454251881Speter  return SVN_NO_ERROR;
1455251881Speter}
1456251881Speter
1457251881Speterstatic svn_boolean_t
1458251881Speteris_buffered_handler_stringbuf(void *baton)
1459251881Speter{
1460251881Speter  return TRUE;
1461251881Speter}
1462251881Speter
1463251881Spetersvn_stream_t *
1464251881Spetersvn_stream_from_stringbuf(svn_stringbuf_t *str,
1465251881Speter                          apr_pool_t *pool)
1466251881Speter{
1467251881Speter  svn_stream_t *stream;
1468251881Speter  struct stringbuf_stream_baton *baton;
1469251881Speter
1470251881Speter  if (! str)
1471251881Speter    return svn_stream_empty(pool);
1472251881Speter
1473251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1474251881Speter  baton->str = str;
1475251881Speter  baton->amt_read = 0;
1476251881Speter  stream = svn_stream_create(baton, pool);
1477251881Speter  svn_stream_set_read(stream, read_handler_stringbuf);
1478251881Speter  svn_stream_set_skip(stream, skip_handler_stringbuf);
1479251881Speter  svn_stream_set_write(stream, write_handler_stringbuf);
1480251881Speter  svn_stream_set_mark(stream, mark_handler_stringbuf);
1481251881Speter  svn_stream_set_seek(stream, seek_handler_stringbuf);
1482251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf);
1483251881Speter  return stream;
1484251881Speter}
1485251881Speter
1486251881Speterstruct string_stream_baton
1487251881Speter{
1488251881Speter  const svn_string_t *str;
1489251881Speter  apr_size_t amt_read;
1490251881Speter};
1491251881Speter
1492251881Speter/* svn_stream_mark_t for streams backed by stringbufs. */
1493251881Speterstruct string_stream_mark {
1494251881Speter    apr_size_t pos;
1495251881Speter};
1496251881Speter
1497251881Speterstatic svn_error_t *
1498251881Speterread_handler_string(void *baton, char *buffer, apr_size_t *len)
1499251881Speter{
1500251881Speter  struct string_stream_baton *btn = baton;
1501251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1502251881Speter
1503251881Speter  *len = (*len > left_to_read) ? left_to_read : *len;
1504251881Speter  memcpy(buffer, btn->str->data + btn->amt_read, *len);
1505251881Speter  btn->amt_read += *len;
1506251881Speter  return SVN_NO_ERROR;
1507251881Speter}
1508251881Speter
1509251881Speterstatic svn_error_t *
1510251881Spetermark_handler_string(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1511251881Speter{
1512251881Speter  struct string_stream_baton *btn;
1513251881Speter  struct string_stream_mark *marker;
1514251881Speter
1515251881Speter  btn = baton;
1516251881Speter
1517251881Speter  marker = apr_palloc(pool, sizeof(*marker));
1518251881Speter  marker->pos = btn->amt_read;
1519251881Speter  *mark = (svn_stream_mark_t *)marker;
1520251881Speter  return SVN_NO_ERROR;
1521251881Speter}
1522251881Speter
1523251881Speterstatic svn_error_t *
1524251881Speterseek_handler_string(void *baton, const svn_stream_mark_t *mark)
1525251881Speter{
1526251881Speter  struct string_stream_baton *btn = baton;
1527251881Speter
1528251881Speter  if (mark != NULL)
1529251881Speter    {
1530251881Speter      const struct string_stream_mark *marker;
1531251881Speter
1532251881Speter      marker = (const struct string_stream_mark *)mark;
1533251881Speter      btn->amt_read = marker->pos;
1534251881Speter    }
1535251881Speter  else
1536251881Speter    btn->amt_read = 0;
1537251881Speter
1538251881Speter  return SVN_NO_ERROR;
1539251881Speter}
1540251881Speter
1541251881Speterstatic svn_error_t *
1542251881Speterskip_handler_string(void *baton, apr_size_t len)
1543251881Speter{
1544251881Speter  struct string_stream_baton *btn = baton;
1545251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1546251881Speter
1547251881Speter  len = (len > left_to_read) ? left_to_read : len;
1548251881Speter  btn->amt_read += len;
1549251881Speter  return SVN_NO_ERROR;
1550251881Speter}
1551251881Speter
1552251881Speterstatic svn_boolean_t
1553251881Speteris_buffered_handler_string(void *baton)
1554251881Speter{
1555251881Speter  return TRUE;
1556251881Speter}
1557251881Speter
1558251881Spetersvn_stream_t *
1559251881Spetersvn_stream_from_string(const svn_string_t *str,
1560251881Speter                       apr_pool_t *pool)
1561251881Speter{
1562251881Speter  svn_stream_t *stream;
1563251881Speter  struct string_stream_baton *baton;
1564251881Speter
1565251881Speter  if (! str)
1566251881Speter    return svn_stream_empty(pool);
1567251881Speter
1568251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1569251881Speter  baton->str = str;
1570251881Speter  baton->amt_read = 0;
1571251881Speter  stream = svn_stream_create(baton, pool);
1572251881Speter  svn_stream_set_read(stream, read_handler_string);
1573251881Speter  svn_stream_set_mark(stream, mark_handler_string);
1574251881Speter  svn_stream_set_seek(stream, seek_handler_string);
1575251881Speter  svn_stream_set_skip(stream, skip_handler_string);
1576251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_string);
1577251881Speter  return stream;
1578251881Speter}
1579251881Speter
1580251881Speter
1581251881Spetersvn_error_t *
1582251881Spetersvn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool)
1583251881Speter{
1584251881Speter  apr_file_t *stdin_file;
1585251881Speter  apr_status_t apr_err;
1586251881Speter
1587251881Speter  apr_err = apr_file_open_stdin(&stdin_file, pool);
1588251881Speter  if (apr_err)
1589251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stdin");
1590251881Speter
1591251881Speter  *in = svn_stream_from_aprfile2(stdin_file, TRUE, pool);
1592251881Speter
1593251881Speter  return SVN_NO_ERROR;
1594251881Speter}
1595251881Speter
1596251881Speter
1597251881Spetersvn_error_t *
1598251881Spetersvn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
1599251881Speter{
1600251881Speter  apr_file_t *stdout_file;
1601251881Speter  apr_status_t apr_err;
1602251881Speter
1603251881Speter  apr_err = apr_file_open_stdout(&stdout_file, pool);
1604251881Speter  if (apr_err)
1605251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stdout");
1606251881Speter
1607251881Speter  *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool);
1608251881Speter
1609251881Speter  return SVN_NO_ERROR;
1610251881Speter}
1611251881Speter
1612251881Speter
1613251881Spetersvn_error_t *
1614251881Spetersvn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool)
1615251881Speter{
1616251881Speter  apr_file_t *stderr_file;
1617251881Speter  apr_status_t apr_err;
1618251881Speter
1619251881Speter  apr_err = apr_file_open_stderr(&stderr_file, pool);
1620251881Speter  if (apr_err)
1621251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stderr");
1622251881Speter
1623251881Speter  *err = svn_stream_from_aprfile2(stderr_file, TRUE, pool);
1624251881Speter
1625251881Speter  return SVN_NO_ERROR;
1626251881Speter}
1627251881Speter
1628251881Speter
1629251881Spetersvn_error_t *
1630251881Spetersvn_string_from_stream(svn_string_t **result,
1631251881Speter                       svn_stream_t *stream,
1632251881Speter                       apr_pool_t *result_pool,
1633251881Speter                       apr_pool_t *scratch_pool)
1634251881Speter{
1635251881Speter  svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,
1636251881Speter                                                      result_pool);
1637251881Speter  char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
1638251881Speter
1639251881Speter  while (1)
1640251881Speter    {
1641251881Speter      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1642251881Speter
1643251881Speter      SVN_ERR(svn_stream_read(stream, buffer, &len));
1644251881Speter      svn_stringbuf_appendbytes(work, buffer, len);
1645251881Speter
1646251881Speter      if (len < SVN__STREAM_CHUNK_SIZE)
1647251881Speter        break;
1648251881Speter    }
1649251881Speter
1650251881Speter  SVN_ERR(svn_stream_close(stream));
1651251881Speter
1652251881Speter  *result = apr_palloc(result_pool, sizeof(**result));
1653251881Speter  (*result)->data = work->data;
1654251881Speter  (*result)->len = work->len;
1655251881Speter
1656251881Speter  return SVN_NO_ERROR;
1657251881Speter}
1658251881Speter
1659251881Speter
1660251881Speter/* These are somewhat arbirary, if we ever get good empirical data as to
1661251881Speter   actually valid values, feel free to update them. */
1662251881Speter#define BUFFER_BLOCK_SIZE 1024
1663251881Speter#define BUFFER_MAX_SIZE 100000
1664251881Speter
1665251881Spetersvn_stream_t *
1666251881Spetersvn_stream_buffered(apr_pool_t *result_pool)
1667251881Speter{
1668251881Speter  return svn_stream__from_spillbuf(BUFFER_BLOCK_SIZE, BUFFER_MAX_SIZE,
1669251881Speter                                   result_pool);
1670251881Speter}
1671251881Speter
1672251881Speter
1673251881Speter
1674251881Speter/*** Lazyopen Streams ***/
1675251881Speter
1676251881Speter/* Custom baton for lazyopen-style wrapper streams. */
1677251881Spetertypedef struct lazyopen_baton_t {
1678251881Speter
1679251881Speter  /* Callback function and baton for opening the wrapped stream. */
1680251881Speter  svn_stream_lazyopen_func_t open_func;
1681251881Speter  void *open_baton;
1682251881Speter
1683251881Speter  /* The wrapped stream, or NULL if the stream hasn't yet been
1684251881Speter     opened. */
1685251881Speter  svn_stream_t *real_stream;
1686251881Speter  apr_pool_t *pool;
1687251881Speter
1688251881Speter  /* Whether to open the wrapped stream on a close call. */
1689251881Speter  svn_boolean_t open_on_close;
1690251881Speter
1691251881Speter} lazyopen_baton_t;
1692251881Speter
1693251881Speter
1694251881Speter/* Use B->open_func/baton to create and set B->real_stream iff it
1695251881Speter   isn't already set. */
1696251881Speterstatic svn_error_t *
1697251881Speterlazyopen_if_unopened(lazyopen_baton_t *b)
1698251881Speter{
1699251881Speter  if (b->real_stream == NULL)
1700251881Speter    {
1701251881Speter      svn_stream_t *stream;
1702251881Speter      apr_pool_t *scratch_pool = svn_pool_create(b->pool);
1703251881Speter
1704251881Speter      SVN_ERR(b->open_func(&stream, b->open_baton,
1705251881Speter                           b->pool, scratch_pool));
1706251881Speter
1707251881Speter      svn_pool_destroy(scratch_pool);
1708251881Speter
1709251881Speter      b->real_stream = stream;
1710251881Speter    }
1711251881Speter
1712251881Speter  return SVN_NO_ERROR;
1713251881Speter}
1714251881Speter
1715251881Speter/* Implements svn_read_fn_t */
1716251881Speterstatic svn_error_t *
1717251881Speterread_handler_lazyopen(void *baton,
1718251881Speter                      char *buffer,
1719251881Speter                      apr_size_t *len)
1720251881Speter{
1721251881Speter  lazyopen_baton_t *b = baton;
1722251881Speter
1723251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1724251881Speter  SVN_ERR(svn_stream_read(b->real_stream, buffer, len));
1725251881Speter
1726251881Speter  return SVN_NO_ERROR;
1727251881Speter}
1728251881Speter
1729251881Speter/* Implements svn_stream_skip_fn_t */
1730251881Speterstatic svn_error_t *
1731251881Speterskip_handler_lazyopen(void *baton,
1732251881Speter                      apr_size_t len)
1733251881Speter{
1734251881Speter  lazyopen_baton_t *b = baton;
1735251881Speter
1736251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1737251881Speter  SVN_ERR(svn_stream_skip(b->real_stream, len));
1738251881Speter
1739251881Speter  return SVN_NO_ERROR;
1740251881Speter}
1741251881Speter
1742251881Speter/* Implements svn_write_fn_t */
1743251881Speterstatic svn_error_t *
1744251881Speterwrite_handler_lazyopen(void *baton,
1745251881Speter                       const char *data,
1746251881Speter                       apr_size_t *len)
1747251881Speter{
1748251881Speter  lazyopen_baton_t *b = baton;
1749251881Speter
1750251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1751251881Speter  SVN_ERR(svn_stream_write(b->real_stream, data, len));
1752251881Speter
1753251881Speter  return SVN_NO_ERROR;
1754251881Speter}
1755251881Speter
1756251881Speter/* Implements svn_close_fn_t */
1757251881Speterstatic svn_error_t *
1758251881Speterclose_handler_lazyopen(void *baton)
1759251881Speter{
1760251881Speter  lazyopen_baton_t *b = baton;
1761251881Speter
1762251881Speter  if (b->open_on_close)
1763251881Speter    SVN_ERR(lazyopen_if_unopened(b));
1764251881Speter  if (b->real_stream)
1765251881Speter    SVN_ERR(svn_stream_close(b->real_stream));
1766251881Speter
1767251881Speter  return SVN_NO_ERROR;
1768251881Speter}
1769251881Speter
1770251881Speter/* Implements svn_stream_mark_fn_t */
1771251881Speterstatic svn_error_t *
1772251881Spetermark_handler_lazyopen(void *baton,
1773251881Speter                      svn_stream_mark_t **mark,
1774251881Speter                      apr_pool_t *pool)
1775251881Speter{
1776251881Speter  lazyopen_baton_t *b = baton;
1777251881Speter
1778251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1779251881Speter  SVN_ERR(svn_stream_mark(b->real_stream, mark, pool));
1780251881Speter
1781251881Speter  return SVN_NO_ERROR;
1782251881Speter}
1783251881Speter
1784251881Speter/* Implements svn_stream_seek_fn_t */
1785251881Speterstatic svn_error_t *
1786251881Speterseek_handler_lazyopen(void *baton,
1787251881Speter                      const svn_stream_mark_t *mark)
1788251881Speter{
1789251881Speter  lazyopen_baton_t *b = baton;
1790251881Speter
1791251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1792251881Speter  SVN_ERR(svn_stream_seek(b->real_stream, mark));
1793251881Speter
1794251881Speter  return SVN_NO_ERROR;
1795251881Speter}
1796251881Speter
1797251881Speter/* Implements svn_stream__is_buffered_fn_t */
1798251881Speterstatic svn_boolean_t
1799251881Speteris_buffered_lazyopen(void *baton)
1800251881Speter{
1801251881Speter  lazyopen_baton_t *b = baton;
1802251881Speter
1803251881Speter  /* No lazy open as we cannot handle an open error. */
1804251881Speter  if (!b->real_stream)
1805251881Speter    return FALSE;
1806251881Speter
1807251881Speter  return svn_stream__is_buffered(b->real_stream);
1808251881Speter}
1809251881Speter
1810251881Spetersvn_stream_t *
1811251881Spetersvn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,
1812251881Speter                           void *open_baton,
1813251881Speter                           svn_boolean_t open_on_close,
1814251881Speter                           apr_pool_t *result_pool)
1815251881Speter{
1816251881Speter  lazyopen_baton_t *lob = apr_pcalloc(result_pool, sizeof(*lob));
1817251881Speter  svn_stream_t *stream;
1818251881Speter
1819251881Speter  lob->open_func = open_func;
1820251881Speter  lob->open_baton = open_baton;
1821251881Speter  lob->real_stream = NULL;
1822251881Speter  lob->pool = result_pool;
1823251881Speter  lob->open_on_close = open_on_close;
1824251881Speter
1825251881Speter  stream = svn_stream_create(lob, result_pool);
1826251881Speter  svn_stream_set_read(stream, read_handler_lazyopen);
1827251881Speter  svn_stream_set_skip(stream, skip_handler_lazyopen);
1828251881Speter  svn_stream_set_write(stream, write_handler_lazyopen);
1829251881Speter  svn_stream_set_close(stream, close_handler_lazyopen);
1830251881Speter  svn_stream_set_mark(stream, mark_handler_lazyopen);
1831251881Speter  svn_stream_set_seek(stream, seek_handler_lazyopen);
1832251881Speter  svn_stream__set_is_buffered(stream, is_buffered_lazyopen);
1833251881Speter
1834251881Speter  return stream;
1835251881Speter}
1836