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>
32299742Sdim#include <apr_poll.h>
33299742Sdim#include <apr_portable.h>
34251881Speter
35251881Speter#include <zlib.h>
36251881Speter
37251881Speter#include "svn_pools.h"
38251881Speter#include "svn_io.h"
39251881Speter#include "svn_error.h"
40251881Speter#include "svn_string.h"
41251881Speter#include "svn_utf.h"
42251881Speter#include "svn_checksum.h"
43251881Speter#include "svn_path.h"
44251881Speter#include "svn_private_config.h"
45299742Sdim#include "private/svn_atomic.h"
46251881Speter#include "private/svn_error_private.h"
47251881Speter#include "private/svn_eol_private.h"
48251881Speter#include "private/svn_io_private.h"
49251881Speter#include "private/svn_subr_private.h"
50299742Sdim#include "private/svn_utf_private.h"
51251881Speter
52251881Speter
53251881Speterstruct svn_stream_t {
54251881Speter  void *baton;
55251881Speter  svn_read_fn_t read_fn;
56299742Sdim  svn_read_fn_t read_full_fn;
57251881Speter  svn_stream_skip_fn_t skip_fn;
58251881Speter  svn_write_fn_t write_fn;
59251881Speter  svn_close_fn_t close_fn;
60251881Speter  svn_stream_mark_fn_t mark_fn;
61251881Speter  svn_stream_seek_fn_t seek_fn;
62299742Sdim  svn_stream_data_available_fn_t data_available_fn;
63251881Speter  svn_stream__is_buffered_fn_t is_buffered_fn;
64253734Speter  apr_file_t *file; /* Maybe NULL */
65251881Speter};
66251881Speter
67251881Speter
68251881Speter/*** Forward declarations. ***/
69251881Speter
70251881Speterstatic svn_error_t *
71299742Sdimskip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn);
72251881Speter
73251881Speter
74251881Speter/*** Generic streams. ***/
75251881Speter
76251881Spetersvn_stream_t *
77251881Spetersvn_stream_create(void *baton, apr_pool_t *pool)
78251881Speter{
79251881Speter  svn_stream_t *stream;
80251881Speter
81299742Sdim  stream = apr_pcalloc(pool, sizeof(*stream));
82251881Speter  stream->baton = baton;
83251881Speter  return stream;
84251881Speter}
85251881Speter
86251881Speter
87251881Spetervoid
88251881Spetersvn_stream_set_baton(svn_stream_t *stream, void *baton)
89251881Speter{
90251881Speter  stream->baton = baton;
91251881Speter}
92251881Speter
93251881Speter
94251881Spetervoid
95299742Sdimsvn_stream_set_read2(svn_stream_t *stream,
96299742Sdim                     svn_read_fn_t read_fn,
97299742Sdim                     svn_read_fn_t read_full_fn)
98251881Speter{
99251881Speter  stream->read_fn = read_fn;
100299742Sdim  stream->read_full_fn = read_full_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
134299742Sdimsvn_stream_set_data_available(svn_stream_t *stream,
135299742Sdim                              svn_stream_data_available_fn_t data_available_fn)
136299742Sdim{
137299742Sdim  stream->data_available_fn = data_available_fn;
138299742Sdim}
139299742Sdim
140299742Sdimvoid
141251881Spetersvn_stream__set_is_buffered(svn_stream_t *stream,
142251881Speter                            svn_stream__is_buffered_fn_t is_buffered_fn)
143251881Speter{
144251881Speter  stream->is_buffered_fn = is_buffered_fn;
145251881Speter}
146251881Speter
147299742Sdim/* Standard implementation for svn_stream_read_full() based on
148299742Sdim   multiple svn_stream_read2() calls (in separate function to make
149299742Sdim   it more likely for svn_stream_read_full to be inlined) */
150299742Sdimstatic svn_error_t *
151299742Sdimfull_read_fallback(svn_stream_t *stream, char *buffer, apr_size_t *len)
152299742Sdim{
153299742Sdim  apr_size_t remaining = *len;
154299742Sdim  while (remaining > 0)
155299742Sdim    {
156299742Sdim      apr_size_t length = remaining;
157299742Sdim      SVN_ERR(svn_stream_read2(stream, buffer, &length));
158299742Sdim
159299742Sdim      if (length == 0)
160299742Sdim        {
161299742Sdim          *len -= remaining;
162299742Sdim          return SVN_NO_ERROR;
163299742Sdim        }
164299742Sdim
165299742Sdim      remaining -= length;
166299742Sdim      buffer += length;
167299742Sdim    }
168299742Sdim
169299742Sdim  return SVN_NO_ERROR;
170299742Sdim}
171299742Sdim
172299742Sdimsvn_boolean_t
173299742Sdimsvn_stream_supports_partial_read(svn_stream_t *stream)
174299742Sdim{
175299742Sdim  return stream->read_fn != NULL;
176299742Sdim}
177299742Sdim
178251881Spetersvn_error_t *
179299742Sdimsvn_stream_read2(svn_stream_t *stream, char *buffer, apr_size_t *len)
180251881Speter{
181299742Sdim  if (stream->read_fn == NULL)
182299742Sdim    return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
183299742Sdim
184251881Speter  return svn_error_trace(stream->read_fn(stream->baton, buffer, len));
185251881Speter}
186251881Speter
187299742Sdimsvn_error_t *
188299742Sdimsvn_stream_read_full(svn_stream_t *stream, char *buffer, apr_size_t *len)
189299742Sdim{
190299742Sdim  if (stream->read_full_fn == NULL)
191299742Sdim    return svn_error_trace(full_read_fallback(stream, buffer, len));
192251881Speter
193299742Sdim  return svn_error_trace(stream->read_full_fn(stream->baton, buffer, len));
194299742Sdim}
195299742Sdim
196251881Spetersvn_error_t *
197251881Spetersvn_stream_skip(svn_stream_t *stream, apr_size_t len)
198251881Speter{
199251881Speter  if (stream->skip_fn == NULL)
200251881Speter    return svn_error_trace(
201299742Sdim            skip_default_handler(stream->baton, len, stream->read_full_fn));
202251881Speter
203251881Speter  return svn_error_trace(stream->skip_fn(stream->baton, len));
204251881Speter}
205251881Speter
206251881Speter
207251881Spetersvn_error_t *
208251881Spetersvn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)
209251881Speter{
210299742Sdim  if (stream->write_fn == NULL)
211299742Sdim    return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
212299742Sdim
213251881Speter  return svn_error_trace(stream->write_fn(stream->baton, data, len));
214251881Speter}
215251881Speter
216251881Speter
217251881Spetersvn_error_t *
218251881Spetersvn_stream_reset(svn_stream_t *stream)
219251881Speter{
220251881Speter  return svn_error_trace(
221251881Speter            svn_stream_seek(stream, NULL));
222251881Speter}
223251881Speter
224251881Spetersvn_boolean_t
225251881Spetersvn_stream_supports_mark(svn_stream_t *stream)
226251881Speter{
227251881Speter  return stream->mark_fn != NULL;
228251881Speter}
229251881Speter
230251881Spetersvn_error_t *
231251881Spetersvn_stream_mark(svn_stream_t *stream, svn_stream_mark_t **mark,
232251881Speter                apr_pool_t *pool)
233251881Speter{
234251881Speter  if (stream->mark_fn == NULL)
235251881Speter    return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
236251881Speter
237251881Speter  return svn_error_trace(stream->mark_fn(stream->baton, mark, pool));
238251881Speter}
239251881Speter
240251881Spetersvn_error_t *
241251881Spetersvn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark)
242251881Speter{
243251881Speter  if (stream->seek_fn == NULL)
244251881Speter    return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
245251881Speter
246251881Speter  return svn_error_trace(stream->seek_fn(stream->baton, mark));
247251881Speter}
248251881Speter
249299742Sdimsvn_error_t *
250299742Sdimsvn_stream_data_available(svn_stream_t *stream,
251299742Sdim                          svn_boolean_t *data_available)
252299742Sdim{
253299742Sdim  if (stream->data_available_fn == NULL)
254299742Sdim    return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
255299742Sdim
256299742Sdim  return svn_error_trace(stream->data_available_fn(stream->baton,
257299742Sdim                                                   data_available));
258299742Sdim}
259299742Sdim
260251881Spetersvn_boolean_t
261251881Spetersvn_stream__is_buffered(svn_stream_t *stream)
262251881Speter{
263251881Speter  if (stream->is_buffered_fn == NULL)
264251881Speter    return FALSE;
265251881Speter
266251881Speter  return stream->is_buffered_fn(stream->baton);
267251881Speter}
268251881Speter
269251881Spetersvn_error_t *
270251881Spetersvn_stream_close(svn_stream_t *stream)
271251881Speter{
272251881Speter  if (stream->close_fn == NULL)
273251881Speter    return SVN_NO_ERROR;
274251881Speter  return svn_error_trace(stream->close_fn(stream->baton));
275251881Speter}
276251881Speter
277251881Spetersvn_error_t *
278251881Spetersvn_stream_puts(svn_stream_t *stream,
279251881Speter                const char *str)
280251881Speter{
281251881Speter  apr_size_t len;
282251881Speter  len = strlen(str);
283251881Speter  return svn_error_trace(svn_stream_write(stream, str, &len));
284251881Speter}
285251881Speter
286251881Spetersvn_error_t *
287251881Spetersvn_stream_printf(svn_stream_t *stream,
288251881Speter                  apr_pool_t *pool,
289251881Speter                  const char *fmt,
290251881Speter                  ...)
291251881Speter{
292251881Speter  const char *message;
293251881Speter  va_list ap;
294251881Speter
295251881Speter  va_start(ap, fmt);
296251881Speter  message = apr_pvsprintf(pool, fmt, ap);
297251881Speter  va_end(ap);
298251881Speter
299251881Speter  return svn_error_trace(svn_stream_puts(stream, message));
300251881Speter}
301251881Speter
302251881Speter
303251881Spetersvn_error_t *
304251881Spetersvn_stream_printf_from_utf8(svn_stream_t *stream,
305251881Speter                            const char *encoding,
306251881Speter                            apr_pool_t *pool,
307251881Speter                            const char *fmt,
308251881Speter                            ...)
309251881Speter{
310251881Speter  const char *message, *translated;
311251881Speter  va_list ap;
312251881Speter
313251881Speter  va_start(ap, fmt);
314251881Speter  message = apr_pvsprintf(pool, fmt, ap);
315251881Speter  va_end(ap);
316251881Speter
317251881Speter  SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,
318251881Speter                                        pool));
319251881Speter
320251881Speter  return svn_error_trace(svn_stream_puts(stream, translated));
321251881Speter}
322251881Speter
323251881Speter/* Guts of svn_stream_readline().
324251881Speter * Returns the line read from STREAM in *STRINGBUF, and indicates
325251881Speter * end-of-file in *EOF.  If DETECT_EOL is TRUE, the end-of-line indicator
326251881Speter * is detected automatically and returned in *EOL.
327251881Speter * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line
328251881Speter * indicator.  STRINGBUF is allocated in POOL. */
329251881Speterstatic svn_error_t *
330251881Speterstream_readline_bytewise(svn_stringbuf_t **stringbuf,
331251881Speter                         svn_boolean_t *eof,
332251881Speter                         const char *eol,
333251881Speter                         svn_stream_t *stream,
334251881Speter                         apr_pool_t *pool)
335251881Speter{
336251881Speter  svn_stringbuf_t *str;
337251881Speter  apr_size_t numbytes;
338251881Speter  const char *match;
339251881Speter  char c;
340251881Speter
341251881Speter  /* Since we're reading one character at a time, let's at least
342251881Speter     optimize for the 90% case.  90% of the time, we can avoid the
343251881Speter     stringbuf ever having to realloc() itself if we start it out at
344251881Speter     80 chars.  */
345299742Sdim  str = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool);
346251881Speter
347251881Speter  /* Read into STR up to and including the next EOL sequence. */
348251881Speter  match = eol;
349251881Speter  while (*match)
350251881Speter    {
351251881Speter      numbytes = 1;
352299742Sdim      SVN_ERR(svn_stream_read_full(stream, &c, &numbytes));
353251881Speter      if (numbytes != 1)
354251881Speter        {
355251881Speter          /* a 'short' read means the stream has run out. */
356251881Speter          *eof = TRUE;
357251881Speter          *stringbuf = str;
358251881Speter          return SVN_NO_ERROR;
359251881Speter        }
360251881Speter
361251881Speter      if (c == *match)
362251881Speter        match++;
363251881Speter      else
364251881Speter        match = eol;
365251881Speter
366251881Speter      svn_stringbuf_appendbyte(str, c);
367251881Speter    }
368251881Speter
369251881Speter  *eof = FALSE;
370251881Speter  svn_stringbuf_chop(str, match - eol);
371251881Speter  *stringbuf = str;
372251881Speter
373251881Speter  return SVN_NO_ERROR;
374251881Speter}
375251881Speter
376251881Speterstatic svn_error_t *
377251881Speterstream_readline_chunky(svn_stringbuf_t **stringbuf,
378251881Speter                       svn_boolean_t *eof,
379251881Speter                       const char *eol,
380251881Speter                       svn_stream_t *stream,
381251881Speter                       apr_pool_t *pool)
382251881Speter{
383251881Speter  /* Read larger chunks of data at once into this buffer and scan
384251881Speter   * that for EOL. A good chunk size should be about 80 chars since
385251881Speter   * most text lines will be shorter. However, don't use a much
386251881Speter   * larger value because filling the buffer from the stream takes
387251881Speter   * time as well.
388251881Speter   */
389299742Sdim  char buffer[SVN__LINE_CHUNK_SIZE+1];
390251881Speter
391251881Speter  /* variables */
392251881Speter  svn_stream_mark_t *mark;
393251881Speter  apr_size_t numbytes;
394251881Speter  const char *eol_pos;
395251881Speter  apr_size_t total_parsed = 0;
396251881Speter
397251881Speter  /* invariant for this call */
398251881Speter  const size_t eol_len = strlen(eol);
399251881Speter
400251881Speter  /* Remember the line start so this plus the line length will be
401251881Speter   * the position to move to at the end of this function.
402251881Speter   */
403251881Speter  SVN_ERR(svn_stream_mark(stream, &mark, pool));
404251881Speter
405251881Speter  /* Read the first chunk. */
406299742Sdim  numbytes = SVN__LINE_CHUNK_SIZE;
407299742Sdim  SVN_ERR(svn_stream_read_full(stream, buffer, &numbytes));
408251881Speter  buffer[numbytes] = '\0';
409251881Speter
410251881Speter  /* Look for the EOL in this first chunk. If we find it, we are done here.
411251881Speter   */
412251881Speter  eol_pos = strstr(buffer, eol);
413251881Speter  if (eol_pos != NULL)
414251881Speter    {
415251881Speter      *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool);
416251881Speter      total_parsed = eol_pos - buffer + eol_len;
417251881Speter    }
418299742Sdim  else if (numbytes < SVN__LINE_CHUNK_SIZE)
419251881Speter    {
420251881Speter      /* We hit EOF but not EOL.
421251881Speter       */
422251881Speter      *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool);
423251881Speter      *eof = TRUE;
424251881Speter      return SVN_NO_ERROR;
425251881Speter     }
426251881Speter  else
427251881Speter    {
428251881Speter      /* A larger buffer for the string is needed. */
429251881Speter      svn_stringbuf_t *str;
430299742Sdim      str = svn_stringbuf_create_ensure(2*SVN__LINE_CHUNK_SIZE, pool);
431251881Speter      svn_stringbuf_appendbytes(str, buffer, numbytes);
432251881Speter      *stringbuf = str;
433251881Speter
434251881Speter      /* Loop reading chunks until an EOL was found. If we hit EOF, fall
435251881Speter       * back to the standard implementation. */
436251881Speter      do
437251881Speter      {
438251881Speter        /* Append the next chunk to the string read so far.
439251881Speter         */
440299742Sdim        svn_stringbuf_ensure(str, str->len + SVN__LINE_CHUNK_SIZE);
441299742Sdim        numbytes = SVN__LINE_CHUNK_SIZE;
442299742Sdim        SVN_ERR(svn_stream_read_full(stream, str->data + str->len, &numbytes));
443251881Speter        str->len += numbytes;
444251881Speter        str->data[str->len] = '\0';
445251881Speter
446251881Speter        /* Look for the EOL in the new data plus the last part of the
447251881Speter         * previous chunk because the EOL may span over the boundary
448251881Speter         * between both chunks.
449251881Speter         */
450251881Speter        eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol);
451251881Speter
452299742Sdim        if ((numbytes < SVN__LINE_CHUNK_SIZE) && (eol_pos == NULL))
453251881Speter        {
454251881Speter          /* We hit EOF instead of EOL. */
455251881Speter          *eof = TRUE;
456251881Speter          return SVN_NO_ERROR;
457251881Speter        }
458251881Speter      }
459251881Speter      while (eol_pos == NULL);
460251881Speter
461251881Speter      /* Number of bytes we actually consumed (i.e. line + EOF).
462251881Speter       * We need to "return" the rest to the stream by moving its
463251881Speter       * read pointer.
464251881Speter       */
465251881Speter      total_parsed = eol_pos - str->data + eol_len;
466251881Speter
467251881Speter      /* Terminate the string at the EOL postion and return it. */
468251881Speter      str->len = eol_pos - str->data;
469251881Speter      str->data[str->len] = 0;
470251881Speter    }
471251881Speter
472251881Speter  /* Move the stream read pointer to the first position behind the EOL.
473251881Speter   */
474251881Speter  SVN_ERR(svn_stream_seek(stream, mark));
475251881Speter  return svn_error_trace(svn_stream_skip(stream, total_parsed));
476251881Speter}
477251881Speter
478251881Speter/* Guts of svn_stream_readline().
479251881Speter * Returns the line read from STREAM in *STRINGBUF, and indicates
480251881Speter * end-of-file in *EOF.  EOL must point to the desired end-of-line
481251881Speter * indicator.  STRINGBUF is allocated in POOL. */
482251881Speterstatic svn_error_t *
483251881Speterstream_readline(svn_stringbuf_t **stringbuf,
484251881Speter                svn_boolean_t *eof,
485251881Speter                const char *eol,
486251881Speter                svn_stream_t *stream,
487251881Speter                apr_pool_t *pool)
488251881Speter{
489251881Speter  *eof = FALSE;
490251881Speter
491251881Speter  /* Often, we operate on APR file or string-based streams and know what
492251881Speter   * EOL we are looking for. Optimize that common case.
493251881Speter   */
494251881Speter  if (svn_stream_supports_mark(stream) &&
495251881Speter      svn_stream__is_buffered(stream))
496251881Speter    {
497251881Speter      /* We can efficiently read chunks speculatively and reposition the
498251881Speter       * stream pointer to the end of the line once we found that.
499251881Speter       */
500251881Speter      SVN_ERR(stream_readline_chunky(stringbuf,
501251881Speter                                     eof,
502251881Speter                                     eol,
503251881Speter                                     stream,
504251881Speter                                     pool));
505251881Speter    }
506251881Speter  else
507251881Speter    {
508251881Speter      /* Use the standard byte-byte implementation.
509251881Speter       */
510251881Speter      SVN_ERR(stream_readline_bytewise(stringbuf,
511251881Speter                                       eof,
512251881Speter                                       eol,
513251881Speter                                       stream,
514251881Speter                                       pool));
515251881Speter    }
516251881Speter
517251881Speter  return SVN_NO_ERROR;
518251881Speter}
519251881Speter
520251881Spetersvn_error_t *
521251881Spetersvn_stream_readline(svn_stream_t *stream,
522251881Speter                    svn_stringbuf_t **stringbuf,
523251881Speter                    const char *eol,
524251881Speter                    svn_boolean_t *eof,
525251881Speter                    apr_pool_t *pool)
526251881Speter{
527251881Speter  return svn_error_trace(stream_readline(stringbuf, eof, eol, stream,
528251881Speter                                         pool));
529251881Speter}
530251881Speter
531251881Spetersvn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
532251881Speter                              svn_cancel_func_t cancel_func,
533251881Speter                              void *cancel_baton,
534251881Speter                              apr_pool_t *scratch_pool)
535251881Speter{
536251881Speter  char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
537251881Speter  svn_error_t *err;
538251881Speter  svn_error_t *err2;
539251881Speter
540251881Speter  /* Read and write chunks until we get a short read, indicating the
541251881Speter     end of the stream.  (We can't get a short write without an
542251881Speter     associated error.) */
543251881Speter  while (1)
544251881Speter    {
545251881Speter      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
546251881Speter
547251881Speter      if (cancel_func)
548251881Speter        {
549251881Speter          err = cancel_func(cancel_baton);
550251881Speter          if (err)
551251881Speter             break;
552251881Speter        }
553251881Speter
554299742Sdim      err = svn_stream_read_full(from, buf, &len);
555251881Speter      if (err)
556251881Speter         break;
557251881Speter
558251881Speter      if (len > 0)
559251881Speter        err = svn_stream_write(to, buf, &len);
560251881Speter
561251881Speter      if (err || (len != SVN__STREAM_CHUNK_SIZE))
562251881Speter          break;
563251881Speter    }
564251881Speter
565251881Speter  err2 = svn_error_compose_create(svn_stream_close(from),
566251881Speter                                  svn_stream_close(to));
567251881Speter
568251881Speter  return svn_error_compose_create(err, err2);
569251881Speter}
570251881Speter
571251881Spetersvn_error_t *
572251881Spetersvn_stream_contents_same2(svn_boolean_t *same,
573251881Speter                          svn_stream_t *stream1,
574251881Speter                          svn_stream_t *stream2,
575251881Speter                          apr_pool_t *pool)
576251881Speter{
577251881Speter  char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
578251881Speter  char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
579251881Speter  apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;
580251881Speter  apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;
581251881Speter  svn_error_t *err = NULL;
582251881Speter
583251881Speter  *same = TRUE;  /* assume TRUE, until disproved below */
584251881Speter  while (bytes_read1 == SVN__STREAM_CHUNK_SIZE
585251881Speter         && bytes_read2 == SVN__STREAM_CHUNK_SIZE)
586251881Speter    {
587299742Sdim      err = svn_stream_read_full(stream1, buf1, &bytes_read1);
588251881Speter      if (err)
589251881Speter        break;
590299742Sdim      err = svn_stream_read_full(stream2, buf2, &bytes_read2);
591251881Speter      if (err)
592251881Speter        break;
593251881Speter
594251881Speter      if ((bytes_read1 != bytes_read2)
595251881Speter          || (memcmp(buf1, buf2, bytes_read1)))
596251881Speter        {
597251881Speter          *same = FALSE;
598251881Speter          break;
599251881Speter        }
600251881Speter    }
601251881Speter
602251881Speter  return svn_error_compose_create(err,
603251881Speter                                  svn_error_compose_create(
604251881Speter                                    svn_stream_close(stream1),
605251881Speter                                    svn_stream_close(stream2)));
606251881Speter}
607251881Speter
608251881Speter
609251881Speter/*** Stream implementation utilities ***/
610251881Speter
611251881Speter/* Skip data from any stream by reading and simply discarding it. */
612251881Speterstatic svn_error_t *
613299742Sdimskip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn)
614251881Speter{
615251881Speter  apr_size_t bytes_read = 1;
616251881Speter  char buffer[4096];
617251881Speter  apr_size_t to_read = len;
618251881Speter
619251881Speter  while ((to_read > 0) && (bytes_read > 0))
620251881Speter    {
621251881Speter      bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read;
622299742Sdim      SVN_ERR(read_full_fn(baton, buffer, &bytes_read));
623251881Speter      to_read -= bytes_read;
624251881Speter    }
625251881Speter
626251881Speter  return SVN_NO_ERROR;
627251881Speter}
628251881Speter
629251881Speter
630251881Speter
631251881Speter/*** Generic readable empty stream ***/
632251881Speter
633251881Speterstatic svn_error_t *
634251881Speterread_handler_empty(void *baton, char *buffer, apr_size_t *len)
635251881Speter{
636251881Speter  *len = 0;
637251881Speter  return SVN_NO_ERROR;
638251881Speter}
639251881Speter
640251881Speterstatic svn_error_t *
641251881Speterwrite_handler_empty(void *baton, const char *data, apr_size_t *len)
642251881Speter{
643251881Speter  return SVN_NO_ERROR;
644251881Speter}
645251881Speter
646251881Speterstatic svn_error_t *
647251881Spetermark_handler_empty(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
648251881Speter{
649251881Speter  *mark = NULL; /* Seek to start of stream marker */
650251881Speter  return SVN_NO_ERROR;
651251881Speter}
652251881Speter
653251881Speterstatic svn_error_t *
654251881Speterseek_handler_empty(void *baton, const svn_stream_mark_t *mark)
655251881Speter{
656251881Speter  return SVN_NO_ERROR;
657251881Speter}
658251881Speter
659251881Speterstatic svn_boolean_t
660251881Speteris_buffered_handler_empty(void *baton)
661251881Speter{
662251881Speter  return FALSE;
663251881Speter}
664251881Speter
665251881Speter
666251881Spetersvn_stream_t *
667251881Spetersvn_stream_empty(apr_pool_t *pool)
668251881Speter{
669251881Speter  svn_stream_t *stream;
670251881Speter
671251881Speter  stream = svn_stream_create(NULL, pool);
672299742Sdim  svn_stream_set_read2(stream, read_handler_empty, read_handler_empty);
673251881Speter  svn_stream_set_write(stream, write_handler_empty);
674251881Speter  svn_stream_set_mark(stream, mark_handler_empty);
675251881Speter  svn_stream_set_seek(stream, seek_handler_empty);
676251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_empty);
677251881Speter  return stream;
678251881Speter}
679251881Speter
680251881Speter
681251881Speter
682251881Speter/*** Stream duplication support ***/
683251881Speterstruct baton_tee {
684251881Speter  svn_stream_t *out1;
685251881Speter  svn_stream_t *out2;
686251881Speter};
687251881Speter
688251881Speter
689251881Speterstatic svn_error_t *
690251881Speterwrite_handler_tee(void *baton, const char *data, apr_size_t *len)
691251881Speter{
692251881Speter  struct baton_tee *bt = baton;
693251881Speter
694251881Speter  SVN_ERR(svn_stream_write(bt->out1, data, len));
695251881Speter  SVN_ERR(svn_stream_write(bt->out2, data, len));
696251881Speter
697251881Speter  return SVN_NO_ERROR;
698251881Speter}
699251881Speter
700251881Speter
701251881Speterstatic svn_error_t *
702251881Speterclose_handler_tee(void *baton)
703251881Speter{
704251881Speter  struct baton_tee *bt = baton;
705251881Speter
706251881Speter  SVN_ERR(svn_stream_close(bt->out1));
707251881Speter  SVN_ERR(svn_stream_close(bt->out2));
708251881Speter
709251881Speter  return SVN_NO_ERROR;
710251881Speter}
711251881Speter
712251881Speter
713251881Spetersvn_stream_t *
714251881Spetersvn_stream_tee(svn_stream_t *out1,
715251881Speter               svn_stream_t *out2,
716251881Speter               apr_pool_t *pool)
717251881Speter{
718251881Speter  struct baton_tee *baton;
719251881Speter  svn_stream_t *stream;
720251881Speter
721251881Speter  if (out1 == NULL)
722251881Speter    return out2;
723251881Speter
724251881Speter  if (out2 == NULL)
725251881Speter    return out1;
726251881Speter
727251881Speter  baton = apr_palloc(pool, sizeof(*baton));
728251881Speter  baton->out1 = out1;
729251881Speter  baton->out2 = out2;
730251881Speter  stream = svn_stream_create(baton, pool);
731251881Speter  svn_stream_set_write(stream, write_handler_tee);
732251881Speter  svn_stream_set_close(stream, close_handler_tee);
733251881Speter
734251881Speter  return stream;
735251881Speter}
736251881Speter
737251881Speter
738251881Speter
739251881Speter/*** Ownership detaching stream ***/
740251881Speter
741251881Speterstatic svn_error_t *
742251881Speterread_handler_disown(void *baton, char *buffer, apr_size_t *len)
743251881Speter{
744299742Sdim  return svn_error_trace(svn_stream_read2(baton, buffer, len));
745251881Speter}
746251881Speter
747251881Speterstatic svn_error_t *
748299742Sdimread_full_handler_disown(void *baton, char *buffer, apr_size_t *len)
749299742Sdim{
750299742Sdim  return svn_error_trace(svn_stream_read_full(baton, buffer, len));
751299742Sdim}
752299742Sdim
753299742Sdimstatic svn_error_t *
754251881Speterskip_handler_disown(void *baton, apr_size_t len)
755251881Speter{
756251881Speter  return svn_error_trace(svn_stream_skip(baton, len));
757251881Speter}
758251881Speter
759251881Speterstatic svn_error_t *
760251881Speterwrite_handler_disown(void *baton, const char *buffer, apr_size_t *len)
761251881Speter{
762251881Speter  return svn_error_trace(svn_stream_write(baton, buffer, len));
763251881Speter}
764251881Speter
765251881Speterstatic svn_error_t *
766251881Spetermark_handler_disown(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
767251881Speter{
768251881Speter  return svn_error_trace(svn_stream_mark(baton, mark, pool));
769251881Speter}
770251881Speter
771251881Speterstatic svn_error_t *
772251881Speterseek_handler_disown(void *baton, const svn_stream_mark_t *mark)
773251881Speter{
774251881Speter  return svn_error_trace(svn_stream_seek(baton, mark));
775251881Speter}
776251881Speter
777299742Sdimstatic svn_error_t *
778299742Sdimdata_available_disown(void *baton, svn_boolean_t *data_available)
779299742Sdim{
780299742Sdim  return svn_error_trace(svn_stream_data_available(baton, data_available));
781299742Sdim}
782299742Sdim
783251881Speterstatic svn_boolean_t
784251881Speteris_buffered_handler_disown(void *baton)
785251881Speter{
786251881Speter  return svn_stream__is_buffered(baton);
787251881Speter}
788251881Speter
789251881Spetersvn_stream_t *
790251881Spetersvn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
791251881Speter{
792251881Speter  svn_stream_t *s = svn_stream_create(stream, pool);
793251881Speter
794299742Sdim  svn_stream_set_read2(s, read_handler_disown, read_full_handler_disown);
795251881Speter  svn_stream_set_skip(s, skip_handler_disown);
796251881Speter  svn_stream_set_write(s, write_handler_disown);
797251881Speter  svn_stream_set_mark(s, mark_handler_disown);
798251881Speter  svn_stream_set_seek(s, seek_handler_disown);
799299742Sdim  svn_stream_set_data_available(s, data_available_disown);
800251881Speter  svn_stream__set_is_buffered(s, is_buffered_handler_disown);
801251881Speter
802251881Speter  return s;
803251881Speter}
804251881Speter
805251881Speter
806251881Speter
807251881Speter/*** Generic stream for APR files ***/
808251881Speterstruct baton_apr {
809251881Speter  apr_file_t *file;
810251881Speter  apr_pool_t *pool;
811251881Speter};
812251881Speter
813251881Speter/* svn_stream_mark_t for streams backed by APR files. */
814251881Speterstruct mark_apr {
815251881Speter  apr_off_t off;
816251881Speter};
817251881Speter
818251881Speterstatic svn_error_t *
819251881Speterread_handler_apr(void *baton, char *buffer, apr_size_t *len)
820251881Speter{
821251881Speter  struct baton_apr *btn = baton;
822251881Speter  svn_error_t *err;
823299742Sdim
824299742Sdim  if (*len == 1)
825299742Sdim    {
826299742Sdim      err = svn_io_file_getc(buffer, btn->file, btn->pool);
827299742Sdim      if (err)
828299742Sdim        {
829299742Sdim          *len = 0;
830299742Sdim          if (APR_STATUS_IS_EOF(err->apr_err))
831299742Sdim            {
832299742Sdim              svn_error_clear(err);
833299742Sdim              err = SVN_NO_ERROR;
834299742Sdim            }
835299742Sdim        }
836299742Sdim    }
837299742Sdim  else
838299742Sdim    {
839299742Sdim      err = svn_io_file_read(btn->file, buffer, len, btn->pool);
840299742Sdim      if (err && APR_STATUS_IS_EOF(err->apr_err))
841299742Sdim        {
842299742Sdim          svn_error_clear(err);
843299742Sdim          err = NULL;
844299742Sdim        }
845299742Sdim    }
846299742Sdim
847299742Sdim  return svn_error_trace(err);
848299742Sdim}
849299742Sdim
850299742Sdimstatic svn_error_t *
851299742Sdimread_full_handler_apr(void *baton, char *buffer, apr_size_t *len)
852299742Sdim{
853299742Sdim  struct baton_apr *btn = baton;
854299742Sdim  svn_error_t *err;
855251881Speter  svn_boolean_t eof;
856251881Speter
857251881Speter  if (*len == 1)
858251881Speter    {
859251881Speter      err = svn_io_file_getc(buffer, btn->file, btn->pool);
860251881Speter      if (err)
861251881Speter        {
862251881Speter          *len = 0;
863251881Speter          if (APR_STATUS_IS_EOF(err->apr_err))
864251881Speter            {
865251881Speter              svn_error_clear(err);
866251881Speter              err = SVN_NO_ERROR;
867251881Speter            }
868251881Speter        }
869251881Speter    }
870251881Speter  else
871251881Speter    err = svn_io_file_read_full2(btn->file, buffer, *len, len,
872251881Speter                                 &eof, btn->pool);
873251881Speter
874251881Speter  return svn_error_trace(err);
875251881Speter}
876251881Speter
877251881Speterstatic svn_error_t *
878251881Speterskip_handler_apr(void *baton, apr_size_t len)
879251881Speter{
880251881Speter  struct baton_apr *btn = baton;
881251881Speter  apr_off_t offset = len;
882251881Speter
883251881Speter  return svn_error_trace(
884251881Speter            svn_io_file_seek(btn->file, APR_CUR, &offset, btn->pool));
885251881Speter}
886251881Speter
887251881Speterstatic svn_error_t *
888251881Speterwrite_handler_apr(void *baton, const char *data, apr_size_t *len)
889251881Speter{
890251881Speter  struct baton_apr *btn = baton;
891251881Speter  svn_error_t *err;
892251881Speter
893251881Speter  if (*len == 1)
894251881Speter    {
895251881Speter      err = svn_io_file_putc(*data, btn->file, btn->pool);
896251881Speter      if (err)
897251881Speter        *len = 0;
898251881Speter    }
899251881Speter  else
900251881Speter    err = svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
901251881Speter
902251881Speter  return svn_error_trace(err);
903251881Speter}
904251881Speter
905251881Speterstatic svn_error_t *
906251881Speterclose_handler_apr(void *baton)
907251881Speter{
908251881Speter  struct baton_apr *btn = baton;
909251881Speter
910251881Speter  return svn_error_trace(svn_io_file_close(btn->file, btn->pool));
911251881Speter}
912251881Speter
913251881Speterstatic svn_error_t *
914251881Spetermark_handler_apr(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
915251881Speter{
916251881Speter  struct baton_apr *btn = baton;
917251881Speter  struct mark_apr *mark_apr;
918251881Speter
919251881Speter  mark_apr = apr_palloc(pool, sizeof(*mark_apr));
920251881Speter  mark_apr->off = 0;
921251881Speter  SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &mark_apr->off, btn->pool));
922251881Speter  *mark = (svn_stream_mark_t *)mark_apr;
923251881Speter  return SVN_NO_ERROR;
924251881Speter}
925251881Speter
926251881Speterstatic svn_error_t *
927251881Speterseek_handler_apr(void *baton, const svn_stream_mark_t *mark)
928251881Speter{
929251881Speter  struct baton_apr *btn = baton;
930251881Speter  apr_off_t offset = (mark != NULL) ? ((const struct mark_apr *)mark)->off : 0;
931251881Speter
932251881Speter  SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool));
933251881Speter
934251881Speter  return SVN_NO_ERROR;
935251881Speter}
936251881Speter
937299742Sdimstatic svn_error_t *
938299742Sdimdata_available_handler_apr(void *baton, svn_boolean_t *data_available)
939299742Sdim{
940299742Sdim  struct baton_apr *btn = baton;
941299742Sdim  apr_status_t status;
942299742Sdim#if !defined(WIN32) || APR_FILES_AS_SOCKETS
943299742Sdim  apr_pollfd_t pfd;
944299742Sdim  int n;
945299742Sdim
946299742Sdim  pfd.desc_type = APR_POLL_FILE;
947299742Sdim  pfd.desc.f = btn->file;
948299742Sdim  pfd.p = btn->pool; /* If we had a scratch pool... Luckily apr doesn't
949299742Sdim                        store anything in this pool at this time */
950299742Sdim  pfd.reqevents = APR_POLLIN;
951299742Sdim
952299742Sdim  status = apr_poll(&pfd, 1, &n, 0);
953299742Sdim
954299742Sdim  if (status == APR_SUCCESS)
955299742Sdim    {
956299742Sdim      *data_available = (n > 0);
957299742Sdim      return SVN_NO_ERROR;
958299742Sdim    }
959299742Sdim  else if (APR_STATUS_IS_EOF(status) || APR_STATUS_IS_TIMEUP(status))
960299742Sdim    {
961299742Sdim      *data_available = FALSE;
962299742Sdim      return SVN_NO_ERROR;
963299742Sdim    }
964299742Sdim  else
965299742Sdim    {
966299742Sdim      return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED,
967299742Sdim                              svn_error_wrap_apr(
968299742Sdim                                  status,
969299742Sdim                                  _("Polling for available data on filestream "
970299742Sdim                                    "failed")),
971299742Sdim                              NULL);
972299742Sdim    }
973299742Sdim#else
974299742Sdim  HANDLE h;
975299742Sdim  DWORD dwAvail;
976299742Sdim  status = apr_os_file_get(&h, btn->file);
977299742Sdim
978299742Sdim  if (status)
979299742Sdim    return svn_error_wrap_apr(status, NULL);
980299742Sdim
981299742Sdim  if (PeekNamedPipe(h, NULL, 0, NULL, &dwAvail, NULL))
982299742Sdim    {
983299742Sdim      *data_available = (dwAvail > 0);
984299742Sdim      return SVN_NO_ERROR;
985299742Sdim    }
986299742Sdim
987299742Sdim  return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED,
988299742Sdim                          svn_error_wrap_apr(apr_get_os_error(), NULL),
989299742Sdim                          _("Windows doesn't support polling on files"));
990299742Sdim#endif
991299742Sdim}
992299742Sdim
993251881Speterstatic svn_boolean_t
994251881Speteris_buffered_handler_apr(void *baton)
995251881Speter{
996251881Speter  struct baton_apr *btn = baton;
997251881Speter  return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0;
998251881Speter}
999251881Speter
1000251881Spetersvn_error_t *
1001251881Spetersvn_stream_open_readonly(svn_stream_t **stream,
1002251881Speter                         const char *path,
1003251881Speter                         apr_pool_t *result_pool,
1004251881Speter                         apr_pool_t *scratch_pool)
1005251881Speter{
1006251881Speter  apr_file_t *file;
1007251881Speter
1008251881Speter  SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
1009251881Speter                           APR_OS_DEFAULT, result_pool));
1010251881Speter  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1011251881Speter
1012251881Speter  return SVN_NO_ERROR;
1013251881Speter}
1014251881Speter
1015251881Speter
1016251881Spetersvn_error_t *
1017251881Spetersvn_stream_open_writable(svn_stream_t **stream,
1018251881Speter                         const char *path,
1019251881Speter                         apr_pool_t *result_pool,
1020251881Speter                         apr_pool_t *scratch_pool)
1021251881Speter{
1022251881Speter  apr_file_t *file;
1023251881Speter
1024251881Speter  SVN_ERR(svn_io_file_open(&file, path,
1025251881Speter                           APR_WRITE
1026251881Speter                             | APR_BUFFERED
1027251881Speter                             | APR_CREATE
1028251881Speter                             | APR_EXCL,
1029251881Speter                           APR_OS_DEFAULT, result_pool));
1030251881Speter  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1031251881Speter
1032251881Speter  return SVN_NO_ERROR;
1033251881Speter}
1034251881Speter
1035251881Speter
1036251881Spetersvn_error_t *
1037251881Spetersvn_stream_open_unique(svn_stream_t **stream,
1038251881Speter                       const char **temp_path,
1039251881Speter                       const char *dirpath,
1040251881Speter                       svn_io_file_del_t delete_when,
1041251881Speter                       apr_pool_t *result_pool,
1042251881Speter                       apr_pool_t *scratch_pool)
1043251881Speter{
1044251881Speter  apr_file_t *file;
1045251881Speter
1046251881Speter  SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath,
1047251881Speter                                   delete_when, result_pool, scratch_pool));
1048251881Speter  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1049251881Speter
1050251881Speter  return SVN_NO_ERROR;
1051251881Speter}
1052251881Speter
1053251881Speter
1054299742Sdim/* Helper function that creates a stream from an APR file. */
1055299742Sdimstatic svn_stream_t *
1056299742Sdimmake_stream_from_apr_file(apr_file_t *file,
1057299742Sdim                          svn_boolean_t disown,
1058299742Sdim                          svn_boolean_t supports_seek,
1059299742Sdim                          apr_pool_t *pool)
1060251881Speter{
1061251881Speter  struct baton_apr *baton;
1062251881Speter  svn_stream_t *stream;
1063251881Speter
1064251881Speter  if (file == NULL)
1065251881Speter    return svn_stream_empty(pool);
1066251881Speter
1067251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1068251881Speter  baton->file = file;
1069251881Speter  baton->pool = pool;
1070251881Speter  stream = svn_stream_create(baton, pool);
1071299742Sdim  svn_stream_set_read2(stream, read_handler_apr, read_full_handler_apr);
1072251881Speter  svn_stream_set_write(stream, write_handler_apr);
1073299742Sdim
1074299742Sdim  if (supports_seek)
1075299742Sdim    {
1076299742Sdim      svn_stream_set_skip(stream, skip_handler_apr);
1077299742Sdim      svn_stream_set_mark(stream, mark_handler_apr);
1078299742Sdim      svn_stream_set_seek(stream, seek_handler_apr);
1079299742Sdim    }
1080299742Sdim
1081299742Sdim  svn_stream_set_data_available(stream, data_available_handler_apr);
1082251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_apr);
1083253734Speter  stream->file = file;
1084251881Speter
1085251881Speter  if (! disown)
1086251881Speter    svn_stream_set_close(stream, close_handler_apr);
1087251881Speter
1088251881Speter  return stream;
1089251881Speter}
1090251881Speter
1091299742Sdimsvn_stream_t *
1092299742Sdimsvn_stream_from_aprfile2(apr_file_t *file,
1093299742Sdim                         svn_boolean_t disown,
1094299742Sdim                         apr_pool_t *pool)
1095299742Sdim{
1096299742Sdim  return make_stream_from_apr_file(file, disown, TRUE, pool);
1097299742Sdim}
1098299742Sdim
1099253734Speterapr_file_t *
1100253734Spetersvn_stream__aprfile(svn_stream_t *stream)
1101253734Speter{
1102253734Speter  return stream->file;
1103253734Speter}
1104253734Speter
1105251881Speter
1106251881Speter/* Compressed stream support */
1107251881Speter
1108251881Speter#define ZBUFFER_SIZE 4096       /* The size of the buffer the
1109251881Speter                                   compressed stream uses to read from
1110251881Speter                                   the substream. Basically an
1111251881Speter                                   arbitrary value, picked to be about
1112251881Speter                                   page-sized. */
1113251881Speter
1114251881Speterstruct zbaton {
1115251881Speter  z_stream *in;                 /* compressed stream for reading */
1116251881Speter  z_stream *out;                /* compressed stream for writing */
1117299742Sdim  void *substream;              /* The substream */
1118251881Speter  void *read_buffer;            /* buffer   used   for  reading   from
1119251881Speter                                   substream */
1120251881Speter  int read_flush;               /* what flush mode to use while
1121251881Speter                                   reading */
1122251881Speter  apr_pool_t *pool;             /* The pool this baton is allocated
1123251881Speter                                   on */
1124251881Speter};
1125251881Speter
1126251881Speter/* zlib alloc function. opaque is the pool we need. */
1127251881Speterstatic voidpf
1128251881Speterzalloc(voidpf opaque, uInt items, uInt size)
1129251881Speter{
1130251881Speter  apr_pool_t *pool = opaque;
1131251881Speter
1132251881Speter  return apr_palloc(pool, items * size);
1133251881Speter}
1134251881Speter
1135251881Speter/* zlib free function */
1136251881Speterstatic void
1137251881Speterzfree(voidpf opaque, voidpf address)
1138251881Speter{
1139251881Speter  /* Empty, since we allocate on the pool */
1140251881Speter}
1141251881Speter
1142251881Speter/* Helper function to figure out the sync mode */
1143251881Speterstatic svn_error_t *
1144299742Sdimread_helper_gz(svn_stream_t *substream,
1145251881Speter               char *buffer,
1146251881Speter               uInt *len, int *zflush)
1147251881Speter{
1148251881Speter  uInt orig_len = *len;
1149251881Speter
1150251881Speter  /* There's no reason this value should grow bigger than the range of
1151251881Speter     uInt, but Subversion's API requires apr_size_t. */
1152251881Speter  apr_size_t apr_len = (apr_size_t) *len;
1153251881Speter
1154299742Sdim  SVN_ERR(svn_stream_read_full(substream, buffer, &apr_len));
1155251881Speter
1156251881Speter  /* Type cast back to uInt type that zlib uses.  On LP64 platforms
1157251881Speter     apr_size_t will be bigger than uInt. */
1158251881Speter  *len = (uInt) apr_len;
1159251881Speter
1160251881Speter  /* I wanted to use Z_FINISH here, but we need to know our buffer is
1161251881Speter     big enough */
1162251881Speter  *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;
1163251881Speter
1164251881Speter  return SVN_NO_ERROR;
1165251881Speter}
1166251881Speter
1167251881Speter/* Handle reading from a compressed stream */
1168251881Speterstatic svn_error_t *
1169251881Speterread_handler_gz(void *baton, char *buffer, apr_size_t *len)
1170251881Speter{
1171251881Speter  struct zbaton *btn = baton;
1172251881Speter  int zerr;
1173251881Speter
1174251881Speter  if (btn->in == NULL)
1175251881Speter    {
1176251881Speter      btn->in = apr_palloc(btn->pool, sizeof(z_stream));
1177251881Speter      btn->in->zalloc = zalloc;
1178251881Speter      btn->in->zfree = zfree;
1179251881Speter      btn->in->opaque = btn->pool;
1180251881Speter      btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);
1181251881Speter      btn->in->next_in = btn->read_buffer;
1182251881Speter      btn->in->avail_in = ZBUFFER_SIZE;
1183251881Speter
1184299742Sdim      SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer,
1185251881Speter                             &btn->in->avail_in, &btn->read_flush));
1186251881Speter
1187251881Speter      zerr = inflateInit(btn->in);
1188251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "inflateInit", btn->in->msg));
1189251881Speter    }
1190251881Speter
1191251881Speter  btn->in->next_out = (Bytef *) buffer;
1192251881Speter  btn->in->avail_out = (uInt) *len;
1193251881Speter
1194251881Speter  while (btn->in->avail_out > 0)
1195251881Speter    {
1196251881Speter      if (btn->in->avail_in <= 0)
1197251881Speter        {
1198251881Speter          btn->in->avail_in = ZBUFFER_SIZE;
1199251881Speter          btn->in->next_in = btn->read_buffer;
1200299742Sdim          SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer,
1201251881Speter                                 &btn->in->avail_in, &btn->read_flush));
1202251881Speter        }
1203251881Speter
1204251881Speter      /* Short read means underlying stream has run out. */
1205251881Speter      if (btn->in->avail_in == 0)
1206251881Speter        {
1207251881Speter          *len = 0;
1208251881Speter          return SVN_NO_ERROR;
1209251881Speter        }
1210251881Speter
1211251881Speter      zerr = inflate(btn->in, btn->read_flush);
1212251881Speter      if (zerr == Z_STREAM_END)
1213251881Speter        break;
1214251881Speter      else if (zerr != Z_OK)
1215251881Speter        return svn_error_trace(svn_error__wrap_zlib(zerr, "inflate",
1216251881Speter                                                    btn->in->msg));
1217251881Speter    }
1218251881Speter
1219251881Speter  *len -= btn->in->avail_out;
1220251881Speter  return SVN_NO_ERROR;
1221251881Speter}
1222251881Speter
1223251881Speter/* Compress data and write it to the substream */
1224251881Speterstatic svn_error_t *
1225251881Speterwrite_handler_gz(void *baton, const char *buffer, apr_size_t *len)
1226251881Speter{
1227251881Speter  struct zbaton *btn = baton;
1228251881Speter  apr_pool_t *subpool;
1229251881Speter  void *write_buf;
1230251881Speter  apr_size_t buf_size, write_len;
1231251881Speter  int zerr;
1232251881Speter
1233251881Speter  if (btn->out == NULL)
1234251881Speter    {
1235251881Speter      btn->out = apr_palloc(btn->pool, sizeof(z_stream));
1236251881Speter      btn->out->zalloc = zalloc;
1237251881Speter      btn->out->zfree = zfree;
1238251881Speter      btn->out->opaque =  btn->pool;
1239251881Speter
1240251881Speter      zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);
1241251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "deflateInit", btn->out->msg));
1242251881Speter    }
1243251881Speter
1244251881Speter  /* The largest buffer we should need is 0.1% larger than the
1245251881Speter     compressed data, + 12 bytes. This info comes from zlib.h.  */
1246251881Speter  buf_size = *len + (*len / 1000) + 13;
1247251881Speter  subpool = svn_pool_create(btn->pool);
1248251881Speter  write_buf = apr_palloc(subpool, buf_size);
1249251881Speter
1250251881Speter  btn->out->next_in = (Bytef *) buffer;  /* Casting away const! */
1251251881Speter  btn->out->avail_in = (uInt) *len;
1252251881Speter
1253251881Speter  while (btn->out->avail_in > 0)
1254251881Speter    {
1255251881Speter      btn->out->next_out = write_buf;
1256251881Speter      btn->out->avail_out = (uInt) buf_size;
1257251881Speter
1258251881Speter      zerr = deflate(btn->out, Z_NO_FLUSH);
1259251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg));
1260251881Speter      write_len = buf_size - btn->out->avail_out;
1261251881Speter      if (write_len > 0)
1262299742Sdim        SVN_ERR(svn_stream_write(btn->substream, write_buf, &write_len));
1263251881Speter    }
1264251881Speter
1265251881Speter  svn_pool_destroy(subpool);
1266251881Speter
1267251881Speter  return SVN_NO_ERROR;
1268251881Speter}
1269251881Speter
1270251881Speter/* Handle flushing and closing the stream */
1271251881Speterstatic svn_error_t *
1272251881Speterclose_handler_gz(void *baton)
1273251881Speter{
1274251881Speter  struct zbaton *btn = baton;
1275251881Speter  int zerr;
1276251881Speter
1277251881Speter  if (btn->in != NULL)
1278251881Speter    {
1279251881Speter      zerr = inflateEnd(btn->in);
1280251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "inflateEnd", btn->in->msg));
1281251881Speter    }
1282251881Speter
1283251881Speter  if (btn->out != NULL)
1284251881Speter    {
1285251881Speter      void *buf;
1286251881Speter      apr_size_t write_len;
1287251881Speter
1288251881Speter      buf = apr_palloc(btn->pool, ZBUFFER_SIZE);
1289251881Speter
1290251881Speter      while (TRUE)
1291251881Speter        {
1292251881Speter          btn->out->next_out = buf;
1293251881Speter          btn->out->avail_out = ZBUFFER_SIZE;
1294251881Speter
1295251881Speter          zerr = deflate(btn->out, Z_FINISH);
1296251881Speter          if (zerr != Z_STREAM_END && zerr != Z_OK)
1297251881Speter            return svn_error_trace(svn_error__wrap_zlib(zerr, "deflate",
1298251881Speter                                                        btn->out->msg));
1299251881Speter          write_len = ZBUFFER_SIZE - btn->out->avail_out;
1300251881Speter          if (write_len > 0)
1301299742Sdim            SVN_ERR(svn_stream_write(btn->substream, buf, &write_len));
1302251881Speter          if (zerr == Z_STREAM_END)
1303251881Speter            break;
1304251881Speter        }
1305251881Speter
1306251881Speter      zerr = deflateEnd(btn->out);
1307251881Speter      SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg));
1308251881Speter    }
1309251881Speter
1310299742Sdim  return svn_error_trace(svn_stream_close(btn->substream));
1311251881Speter}
1312251881Speter
1313251881Speter
1314251881Spetersvn_stream_t *
1315251881Spetersvn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)
1316251881Speter{
1317251881Speter  struct svn_stream_t *zstream;
1318251881Speter  struct zbaton *baton;
1319251881Speter
1320251881Speter  assert(stream != NULL);
1321251881Speter
1322251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1323251881Speter  baton->in = baton->out = NULL;
1324299742Sdim  baton->substream = stream;
1325251881Speter  baton->pool = pool;
1326251881Speter  baton->read_buffer = NULL;
1327251881Speter  baton->read_flush = Z_SYNC_FLUSH;
1328251881Speter
1329251881Speter  zstream = svn_stream_create(baton, pool);
1330299742Sdim  svn_stream_set_read2(zstream, NULL /* only full read support */,
1331299742Sdim                       read_handler_gz);
1332251881Speter  svn_stream_set_write(zstream, write_handler_gz);
1333251881Speter  svn_stream_set_close(zstream, close_handler_gz);
1334251881Speter
1335251881Speter  return zstream;
1336251881Speter}
1337251881Speter
1338251881Speter
1339251881Speter/* Checksummed stream support */
1340251881Speter
1341251881Speterstruct checksum_stream_baton
1342251881Speter{
1343251881Speter  svn_checksum_ctx_t *read_ctx, *write_ctx;
1344251881Speter  svn_checksum_t **read_checksum;  /* Output value. */
1345251881Speter  svn_checksum_t **write_checksum;  /* Output value. */
1346251881Speter  svn_stream_t *proxy;
1347251881Speter
1348251881Speter  /* True if more data should be read when closing the stream. */
1349251881Speter  svn_boolean_t read_more;
1350251881Speter
1351251881Speter  /* Pool to allocate read buffer and output values from. */
1352251881Speter  apr_pool_t *pool;
1353251881Speter};
1354251881Speter
1355251881Speterstatic svn_error_t *
1356251881Speterread_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1357251881Speter{
1358251881Speter  struct checksum_stream_baton *btn = baton;
1359299742Sdim
1360299742Sdim  SVN_ERR(svn_stream_read2(btn->proxy, buffer, len));
1361299742Sdim
1362299742Sdim  if (btn->read_checksum)
1363299742Sdim    SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1364299742Sdim
1365299742Sdim  return SVN_NO_ERROR;
1366299742Sdim}
1367299742Sdim
1368299742Sdimstatic svn_error_t *
1369299742Sdimread_full_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1370299742Sdim{
1371299742Sdim  struct checksum_stream_baton *btn = baton;
1372251881Speter  apr_size_t saved_len = *len;
1373251881Speter
1374299742Sdim  SVN_ERR(svn_stream_read_full(btn->proxy, buffer, len));
1375251881Speter
1376251881Speter  if (btn->read_checksum)
1377251881Speter    SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1378251881Speter
1379251881Speter  if (saved_len != *len)
1380251881Speter    btn->read_more = FALSE;
1381251881Speter
1382251881Speter  return SVN_NO_ERROR;
1383251881Speter}
1384251881Speter
1385251881Speter
1386251881Speterstatic svn_error_t *
1387251881Speterwrite_handler_checksum(void *baton, const char *buffer, apr_size_t *len)
1388251881Speter{
1389251881Speter  struct checksum_stream_baton *btn = baton;
1390251881Speter
1391251881Speter  if (btn->write_checksum && *len > 0)
1392251881Speter    SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len));
1393251881Speter
1394251881Speter  return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1395251881Speter}
1396251881Speter
1397299742Sdimstatic svn_error_t *
1398299742Sdimdata_available_handler_checksum(void *baton, svn_boolean_t *data_available)
1399299742Sdim{
1400299742Sdim  struct checksum_stream_baton *btn = baton;
1401251881Speter
1402299742Sdim  return svn_error_trace(svn_stream_data_available(btn->proxy,
1403299742Sdim                                                   data_available));
1404299742Sdim}
1405299742Sdim
1406251881Speterstatic svn_error_t *
1407251881Speterclose_handler_checksum(void *baton)
1408251881Speter{
1409251881Speter  struct checksum_stream_baton *btn = baton;
1410251881Speter
1411251881Speter  /* If we're supposed to drain the stream, do so before finalizing the
1412251881Speter     checksum. */
1413251881Speter  if (btn->read_more)
1414251881Speter    {
1415251881Speter      char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);
1416251881Speter      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1417251881Speter
1418251881Speter      do
1419251881Speter        {
1420299742Sdim          SVN_ERR(read_full_handler_checksum(baton, buf, &len));
1421251881Speter        }
1422251881Speter      while (btn->read_more);
1423251881Speter    }
1424251881Speter
1425251881Speter  if (btn->read_ctx)
1426251881Speter    SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool));
1427251881Speter
1428251881Speter  if (btn->write_ctx)
1429251881Speter    SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool));
1430251881Speter
1431251881Speter  return svn_error_trace(svn_stream_close(btn->proxy));
1432251881Speter}
1433251881Speter
1434251881Speter
1435251881Spetersvn_stream_t *
1436251881Spetersvn_stream_checksummed2(svn_stream_t *stream,
1437251881Speter                        svn_checksum_t **read_checksum,
1438251881Speter                        svn_checksum_t **write_checksum,
1439251881Speter                        svn_checksum_kind_t checksum_kind,
1440251881Speter                        svn_boolean_t read_all,
1441251881Speter                        apr_pool_t *pool)
1442251881Speter{
1443251881Speter  svn_stream_t *s;
1444251881Speter  struct checksum_stream_baton *baton;
1445251881Speter
1446251881Speter  if (read_checksum == NULL && write_checksum == NULL)
1447251881Speter    return stream;
1448251881Speter
1449251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1450251881Speter  if (read_checksum)
1451251881Speter    baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1452251881Speter  else
1453251881Speter    baton->read_ctx = NULL;
1454251881Speter
1455251881Speter  if (write_checksum)
1456251881Speter    baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1457251881Speter  else
1458251881Speter    baton->write_ctx = NULL;
1459251881Speter
1460251881Speter  baton->read_checksum = read_checksum;
1461251881Speter  baton->write_checksum = write_checksum;
1462251881Speter  baton->proxy = stream;
1463251881Speter  baton->read_more = read_all;
1464251881Speter  baton->pool = pool;
1465251881Speter
1466251881Speter  s = svn_stream_create(baton, pool);
1467299742Sdim  svn_stream_set_read2(s, read_handler_checksum, read_full_handler_checksum);
1468251881Speter  svn_stream_set_write(s, write_handler_checksum);
1469299742Sdim  svn_stream_set_data_available(s, data_available_handler_checksum);
1470251881Speter  svn_stream_set_close(s, close_handler_checksum);
1471251881Speter  return s;
1472251881Speter}
1473251881Speter
1474299742Sdim/* Miscellaneous stream functions. */
1475251881Speter
1476299742Sdimsvn_error_t *
1477299742Sdimsvn_stringbuf_from_stream(svn_stringbuf_t **str,
1478299742Sdim                          svn_stream_t *stream,
1479299742Sdim                          apr_size_t len_hint,
1480299742Sdim                          apr_pool_t *result_pool)
1481251881Speter{
1482299742Sdim#define MIN_READ_SIZE 64
1483251881Speter
1484299742Sdim  apr_size_t to_read = 0;
1485299742Sdim  svn_stringbuf_t *text
1486299742Sdim    = svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE,
1487299742Sdim                                  result_pool);
1488251881Speter
1489299742Sdim  do
1490299742Sdim    {
1491299742Sdim      to_read = text->blocksize - 1 - text->len;
1492299742Sdim      SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &to_read));
1493299742Sdim      text->len += to_read;
1494251881Speter
1495299742Sdim      if (to_read && text->blocksize < text->len + MIN_READ_SIZE)
1496299742Sdim        svn_stringbuf_ensure(text, text->blocksize * 2);
1497299742Sdim    }
1498299742Sdim  while (to_read);
1499251881Speter
1500299742Sdim  text->data[text->len] = '\0';
1501299742Sdim  *str = text;
1502251881Speter
1503251881Speter  return SVN_NO_ERROR;
1504251881Speter}
1505251881Speter
1506251881Speterstruct stringbuf_stream_baton
1507251881Speter{
1508251881Speter  svn_stringbuf_t *str;
1509251881Speter  apr_size_t amt_read;
1510251881Speter};
1511251881Speter
1512251881Speter/* svn_stream_mark_t for streams backed by stringbufs. */
1513251881Speterstruct stringbuf_stream_mark {
1514251881Speter    apr_size_t pos;
1515251881Speter};
1516251881Speter
1517251881Speterstatic svn_error_t *
1518251881Speterread_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
1519251881Speter{
1520251881Speter  struct stringbuf_stream_baton *btn = baton;
1521251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1522251881Speter
1523251881Speter  *len = (*len > left_to_read) ? left_to_read : *len;
1524251881Speter  memcpy(buffer, btn->str->data + btn->amt_read, *len);
1525251881Speter  btn->amt_read += *len;
1526251881Speter  return SVN_NO_ERROR;
1527251881Speter}
1528251881Speter
1529251881Speterstatic svn_error_t *
1530251881Speterskip_handler_stringbuf(void *baton, apr_size_t len)
1531251881Speter{
1532251881Speter  struct stringbuf_stream_baton *btn = baton;
1533251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1534251881Speter
1535251881Speter  len = (len > left_to_read) ? left_to_read : len;
1536251881Speter  btn->amt_read += len;
1537251881Speter  return SVN_NO_ERROR;
1538251881Speter}
1539251881Speter
1540251881Speterstatic svn_error_t *
1541251881Speterwrite_handler_stringbuf(void *baton, const char *data, apr_size_t *len)
1542251881Speter{
1543251881Speter  struct stringbuf_stream_baton *btn = baton;
1544251881Speter
1545251881Speter  svn_stringbuf_appendbytes(btn->str, data, *len);
1546251881Speter  return SVN_NO_ERROR;
1547251881Speter}
1548251881Speter
1549251881Speterstatic svn_error_t *
1550251881Spetermark_handler_stringbuf(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1551251881Speter{
1552251881Speter  struct stringbuf_stream_baton *btn;
1553251881Speter  struct stringbuf_stream_mark *stringbuf_stream_mark;
1554251881Speter
1555251881Speter  btn = baton;
1556251881Speter
1557251881Speter  stringbuf_stream_mark = apr_palloc(pool, sizeof(*stringbuf_stream_mark));
1558251881Speter  stringbuf_stream_mark->pos = btn->amt_read;
1559251881Speter  *mark = (svn_stream_mark_t *)stringbuf_stream_mark;
1560251881Speter  return SVN_NO_ERROR;
1561251881Speter}
1562251881Speter
1563251881Speterstatic svn_error_t *
1564251881Speterseek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark)
1565251881Speter{
1566251881Speter  struct stringbuf_stream_baton *btn = baton;
1567251881Speter
1568251881Speter  if (mark != NULL)
1569251881Speter    {
1570251881Speter      const struct stringbuf_stream_mark *stringbuf_stream_mark;
1571251881Speter
1572251881Speter      stringbuf_stream_mark = (const struct stringbuf_stream_mark *)mark;
1573251881Speter      btn->amt_read = stringbuf_stream_mark->pos;
1574251881Speter    }
1575251881Speter  else
1576251881Speter    btn->amt_read = 0;
1577251881Speter
1578251881Speter  return SVN_NO_ERROR;
1579251881Speter}
1580251881Speter
1581299742Sdimstatic svn_error_t *
1582299742Sdimdata_available_handler_stringbuf(void *baton, svn_boolean_t *data_available)
1583299742Sdim{
1584299742Sdim  struct stringbuf_stream_baton *btn = baton;
1585299742Sdim
1586299742Sdim  *data_available = ((btn->str->len - btn->amt_read) > 0);
1587299742Sdim  return SVN_NO_ERROR;
1588299742Sdim}
1589299742Sdim
1590251881Speterstatic svn_boolean_t
1591251881Speteris_buffered_handler_stringbuf(void *baton)
1592251881Speter{
1593251881Speter  return TRUE;
1594251881Speter}
1595251881Speter
1596251881Spetersvn_stream_t *
1597251881Spetersvn_stream_from_stringbuf(svn_stringbuf_t *str,
1598251881Speter                          apr_pool_t *pool)
1599251881Speter{
1600251881Speter  svn_stream_t *stream;
1601251881Speter  struct stringbuf_stream_baton *baton;
1602251881Speter
1603251881Speter  if (! str)
1604251881Speter    return svn_stream_empty(pool);
1605251881Speter
1606251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1607251881Speter  baton->str = str;
1608251881Speter  baton->amt_read = 0;
1609251881Speter  stream = svn_stream_create(baton, pool);
1610299742Sdim  svn_stream_set_read2(stream, read_handler_stringbuf, read_handler_stringbuf);
1611251881Speter  svn_stream_set_skip(stream, skip_handler_stringbuf);
1612251881Speter  svn_stream_set_write(stream, write_handler_stringbuf);
1613251881Speter  svn_stream_set_mark(stream, mark_handler_stringbuf);
1614251881Speter  svn_stream_set_seek(stream, seek_handler_stringbuf);
1615299742Sdim  svn_stream_set_data_available(stream, data_available_handler_stringbuf);
1616251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf);
1617251881Speter  return stream;
1618251881Speter}
1619251881Speter
1620251881Speterstruct string_stream_baton
1621251881Speter{
1622251881Speter  const svn_string_t *str;
1623251881Speter  apr_size_t amt_read;
1624251881Speter};
1625251881Speter
1626251881Speter/* svn_stream_mark_t for streams backed by stringbufs. */
1627251881Speterstruct string_stream_mark {
1628251881Speter    apr_size_t pos;
1629251881Speter};
1630251881Speter
1631251881Speterstatic svn_error_t *
1632251881Speterread_handler_string(void *baton, char *buffer, apr_size_t *len)
1633251881Speter{
1634251881Speter  struct string_stream_baton *btn = baton;
1635251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1636251881Speter
1637251881Speter  *len = (*len > left_to_read) ? left_to_read : *len;
1638251881Speter  memcpy(buffer, btn->str->data + btn->amt_read, *len);
1639251881Speter  btn->amt_read += *len;
1640251881Speter  return SVN_NO_ERROR;
1641251881Speter}
1642251881Speter
1643251881Speterstatic svn_error_t *
1644251881Spetermark_handler_string(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1645251881Speter{
1646251881Speter  struct string_stream_baton *btn;
1647251881Speter  struct string_stream_mark *marker;
1648251881Speter
1649251881Speter  btn = baton;
1650251881Speter
1651251881Speter  marker = apr_palloc(pool, sizeof(*marker));
1652251881Speter  marker->pos = btn->amt_read;
1653251881Speter  *mark = (svn_stream_mark_t *)marker;
1654251881Speter  return SVN_NO_ERROR;
1655251881Speter}
1656251881Speter
1657251881Speterstatic svn_error_t *
1658251881Speterseek_handler_string(void *baton, const svn_stream_mark_t *mark)
1659251881Speter{
1660251881Speter  struct string_stream_baton *btn = baton;
1661251881Speter
1662251881Speter  if (mark != NULL)
1663251881Speter    {
1664251881Speter      const struct string_stream_mark *marker;
1665251881Speter
1666251881Speter      marker = (const struct string_stream_mark *)mark;
1667251881Speter      btn->amt_read = marker->pos;
1668251881Speter    }
1669251881Speter  else
1670251881Speter    btn->amt_read = 0;
1671251881Speter
1672251881Speter  return SVN_NO_ERROR;
1673251881Speter}
1674251881Speter
1675251881Speterstatic svn_error_t *
1676251881Speterskip_handler_string(void *baton, apr_size_t len)
1677251881Speter{
1678251881Speter  struct string_stream_baton *btn = baton;
1679251881Speter  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1680251881Speter
1681251881Speter  len = (len > left_to_read) ? left_to_read : len;
1682251881Speter  btn->amt_read += len;
1683251881Speter  return SVN_NO_ERROR;
1684251881Speter}
1685251881Speter
1686299742Sdimstatic svn_error_t *
1687299742Sdimdata_available_handler_string(void *baton, svn_boolean_t *data_available)
1688299742Sdim{
1689299742Sdim  struct string_stream_baton *btn = baton;
1690299742Sdim
1691299742Sdim  *data_available = ((btn->str->len - btn->amt_read) > 0);
1692299742Sdim  return SVN_NO_ERROR;
1693299742Sdim}
1694299742Sdim
1695251881Speterstatic svn_boolean_t
1696251881Speteris_buffered_handler_string(void *baton)
1697251881Speter{
1698251881Speter  return TRUE;
1699251881Speter}
1700251881Speter
1701251881Spetersvn_stream_t *
1702251881Spetersvn_stream_from_string(const svn_string_t *str,
1703251881Speter                       apr_pool_t *pool)
1704251881Speter{
1705251881Speter  svn_stream_t *stream;
1706251881Speter  struct string_stream_baton *baton;
1707251881Speter
1708251881Speter  if (! str)
1709251881Speter    return svn_stream_empty(pool);
1710251881Speter
1711251881Speter  baton = apr_palloc(pool, sizeof(*baton));
1712251881Speter  baton->str = str;
1713251881Speter  baton->amt_read = 0;
1714251881Speter  stream = svn_stream_create(baton, pool);
1715299742Sdim  svn_stream_set_read2(stream, read_handler_string, read_handler_string);
1716251881Speter  svn_stream_set_mark(stream, mark_handler_string);
1717251881Speter  svn_stream_set_seek(stream, seek_handler_string);
1718251881Speter  svn_stream_set_skip(stream, skip_handler_string);
1719299742Sdim  svn_stream_set_data_available(stream, data_available_handler_string);
1720251881Speter  svn_stream__set_is_buffered(stream, is_buffered_handler_string);
1721251881Speter  return stream;
1722251881Speter}
1723251881Speter
1724251881Speter
1725251881Spetersvn_error_t *
1726251881Spetersvn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool)
1727251881Speter{
1728251881Speter  apr_file_t *stdin_file;
1729251881Speter  apr_status_t apr_err;
1730251881Speter
1731251881Speter  apr_err = apr_file_open_stdin(&stdin_file, pool);
1732251881Speter  if (apr_err)
1733251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stdin");
1734251881Speter
1735299742Sdim  /* STDIN may or may not support positioning requests, but generally
1736299742Sdim     it does not, or the behavior is implementation-specific.  Hence,
1737299742Sdim     we cannot safely advertise mark(), seek() and non-default skip()
1738299742Sdim     support. */
1739299742Sdim  *in = make_stream_from_apr_file(stdin_file, TRUE, FALSE, pool);
1740251881Speter
1741251881Speter  return SVN_NO_ERROR;
1742251881Speter}
1743251881Speter
1744251881Speter
1745251881Spetersvn_error_t *
1746251881Spetersvn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
1747251881Speter{
1748251881Speter  apr_file_t *stdout_file;
1749251881Speter  apr_status_t apr_err;
1750251881Speter
1751251881Speter  apr_err = apr_file_open_stdout(&stdout_file, pool);
1752251881Speter  if (apr_err)
1753251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stdout");
1754251881Speter
1755299742Sdim  /* STDOUT may or may not support positioning requests, but generally
1756299742Sdim     it does not, or the behavior is implementation-specific.  Hence,
1757299742Sdim     we cannot safely advertise mark(), seek() and non-default skip()
1758299742Sdim     support. */
1759299742Sdim  *out = make_stream_from_apr_file(stdout_file, TRUE, FALSE, pool);
1760251881Speter
1761251881Speter  return SVN_NO_ERROR;
1762251881Speter}
1763251881Speter
1764251881Speter
1765251881Spetersvn_error_t *
1766251881Spetersvn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool)
1767251881Speter{
1768251881Speter  apr_file_t *stderr_file;
1769251881Speter  apr_status_t apr_err;
1770251881Speter
1771251881Speter  apr_err = apr_file_open_stderr(&stderr_file, pool);
1772251881Speter  if (apr_err)
1773251881Speter    return svn_error_wrap_apr(apr_err, "Can't open stderr");
1774251881Speter
1775299742Sdim  /* STDERR may or may not support positioning requests, but generally
1776299742Sdim     it does not, or the behavior is implementation-specific.  Hence,
1777299742Sdim     we cannot safely advertise mark(), seek() and non-default skip()
1778299742Sdim     support. */
1779299742Sdim  *err = make_stream_from_apr_file(stderr_file, TRUE, FALSE, pool);
1780251881Speter
1781251881Speter  return SVN_NO_ERROR;
1782251881Speter}
1783251881Speter
1784251881Speter
1785251881Spetersvn_error_t *
1786251881Spetersvn_string_from_stream(svn_string_t **result,
1787251881Speter                       svn_stream_t *stream,
1788251881Speter                       apr_pool_t *result_pool,
1789251881Speter                       apr_pool_t *scratch_pool)
1790251881Speter{
1791251881Speter  svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,
1792251881Speter                                                      result_pool);
1793251881Speter  char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
1794251881Speter
1795251881Speter  while (1)
1796251881Speter    {
1797251881Speter      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1798251881Speter
1799299742Sdim      SVN_ERR(svn_stream_read_full(stream, buffer, &len));
1800251881Speter      svn_stringbuf_appendbytes(work, buffer, len);
1801251881Speter
1802251881Speter      if (len < SVN__STREAM_CHUNK_SIZE)
1803251881Speter        break;
1804251881Speter    }
1805251881Speter
1806251881Speter  SVN_ERR(svn_stream_close(stream));
1807251881Speter
1808251881Speter  *result = apr_palloc(result_pool, sizeof(**result));
1809251881Speter  (*result)->data = work->data;
1810251881Speter  (*result)->len = work->len;
1811251881Speter
1812251881Speter  return SVN_NO_ERROR;
1813251881Speter}
1814251881Speter
1815251881Speter
1816299742Sdim/* These are somewhat arbitrary, if we ever get good empirical data as to
1817251881Speter   actually valid values, feel free to update them. */
1818251881Speter#define BUFFER_BLOCK_SIZE 1024
1819251881Speter#define BUFFER_MAX_SIZE 100000
1820251881Speter
1821251881Spetersvn_stream_t *
1822251881Spetersvn_stream_buffered(apr_pool_t *result_pool)
1823251881Speter{
1824299742Sdim  return svn_stream__from_spillbuf(svn_spillbuf__create(BUFFER_BLOCK_SIZE,
1825299742Sdim                                                        BUFFER_MAX_SIZE,
1826299742Sdim                                                        result_pool),
1827251881Speter                                   result_pool);
1828251881Speter}
1829251881Speter
1830251881Speter
1831251881Speter
1832251881Speter/*** Lazyopen Streams ***/
1833251881Speter
1834251881Speter/* Custom baton for lazyopen-style wrapper streams. */
1835251881Spetertypedef struct lazyopen_baton_t {
1836251881Speter
1837251881Speter  /* Callback function and baton for opening the wrapped stream. */
1838251881Speter  svn_stream_lazyopen_func_t open_func;
1839251881Speter  void *open_baton;
1840251881Speter
1841251881Speter  /* The wrapped stream, or NULL if the stream hasn't yet been
1842251881Speter     opened. */
1843251881Speter  svn_stream_t *real_stream;
1844251881Speter  apr_pool_t *pool;
1845251881Speter
1846251881Speter  /* Whether to open the wrapped stream on a close call. */
1847251881Speter  svn_boolean_t open_on_close;
1848251881Speter
1849251881Speter} lazyopen_baton_t;
1850251881Speter
1851251881Speter
1852251881Speter/* Use B->open_func/baton to create and set B->real_stream iff it
1853251881Speter   isn't already set. */
1854251881Speterstatic svn_error_t *
1855251881Speterlazyopen_if_unopened(lazyopen_baton_t *b)
1856251881Speter{
1857251881Speter  if (b->real_stream == NULL)
1858251881Speter    {
1859251881Speter      svn_stream_t *stream;
1860251881Speter      apr_pool_t *scratch_pool = svn_pool_create(b->pool);
1861251881Speter
1862251881Speter      SVN_ERR(b->open_func(&stream, b->open_baton,
1863251881Speter                           b->pool, scratch_pool));
1864251881Speter
1865251881Speter      svn_pool_destroy(scratch_pool);
1866251881Speter
1867251881Speter      b->real_stream = stream;
1868251881Speter    }
1869251881Speter
1870251881Speter  return SVN_NO_ERROR;
1871251881Speter}
1872251881Speter
1873251881Speter/* Implements svn_read_fn_t */
1874251881Speterstatic svn_error_t *
1875251881Speterread_handler_lazyopen(void *baton,
1876251881Speter                      char *buffer,
1877251881Speter                      apr_size_t *len)
1878251881Speter{
1879251881Speter  lazyopen_baton_t *b = baton;
1880251881Speter
1881251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1882299742Sdim  SVN_ERR(svn_stream_read2(b->real_stream, buffer, len));
1883251881Speter
1884251881Speter  return SVN_NO_ERROR;
1885251881Speter}
1886251881Speter
1887299742Sdim/* Implements svn_read_fn_t */
1888299742Sdimstatic svn_error_t *
1889299742Sdimread_full_handler_lazyopen(void *baton,
1890299742Sdim                      char *buffer,
1891299742Sdim                      apr_size_t *len)
1892299742Sdim{
1893299742Sdim  lazyopen_baton_t *b = baton;
1894299742Sdim
1895299742Sdim  SVN_ERR(lazyopen_if_unopened(b));
1896299742Sdim  SVN_ERR(svn_stream_read_full(b->real_stream, buffer, len));
1897299742Sdim
1898299742Sdim  return SVN_NO_ERROR;
1899299742Sdim}
1900299742Sdim
1901251881Speter/* Implements svn_stream_skip_fn_t */
1902251881Speterstatic svn_error_t *
1903251881Speterskip_handler_lazyopen(void *baton,
1904251881Speter                      apr_size_t len)
1905251881Speter{
1906251881Speter  lazyopen_baton_t *b = baton;
1907251881Speter
1908251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1909251881Speter  SVN_ERR(svn_stream_skip(b->real_stream, len));
1910251881Speter
1911251881Speter  return SVN_NO_ERROR;
1912251881Speter}
1913251881Speter
1914251881Speter/* Implements svn_write_fn_t */
1915251881Speterstatic svn_error_t *
1916251881Speterwrite_handler_lazyopen(void *baton,
1917251881Speter                       const char *data,
1918251881Speter                       apr_size_t *len)
1919251881Speter{
1920251881Speter  lazyopen_baton_t *b = baton;
1921251881Speter
1922251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1923251881Speter  SVN_ERR(svn_stream_write(b->real_stream, data, len));
1924251881Speter
1925251881Speter  return SVN_NO_ERROR;
1926251881Speter}
1927251881Speter
1928251881Speter/* Implements svn_close_fn_t */
1929251881Speterstatic svn_error_t *
1930251881Speterclose_handler_lazyopen(void *baton)
1931251881Speter{
1932251881Speter  lazyopen_baton_t *b = baton;
1933251881Speter
1934251881Speter  if (b->open_on_close)
1935251881Speter    SVN_ERR(lazyopen_if_unopened(b));
1936251881Speter  if (b->real_stream)
1937251881Speter    SVN_ERR(svn_stream_close(b->real_stream));
1938251881Speter
1939251881Speter  return SVN_NO_ERROR;
1940251881Speter}
1941251881Speter
1942251881Speter/* Implements svn_stream_mark_fn_t */
1943251881Speterstatic svn_error_t *
1944251881Spetermark_handler_lazyopen(void *baton,
1945251881Speter                      svn_stream_mark_t **mark,
1946251881Speter                      apr_pool_t *pool)
1947251881Speter{
1948251881Speter  lazyopen_baton_t *b = baton;
1949251881Speter
1950251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1951251881Speter  SVN_ERR(svn_stream_mark(b->real_stream, mark, pool));
1952251881Speter
1953251881Speter  return SVN_NO_ERROR;
1954251881Speter}
1955251881Speter
1956251881Speter/* Implements svn_stream_seek_fn_t */
1957251881Speterstatic svn_error_t *
1958251881Speterseek_handler_lazyopen(void *baton,
1959251881Speter                      const svn_stream_mark_t *mark)
1960251881Speter{
1961251881Speter  lazyopen_baton_t *b = baton;
1962251881Speter
1963251881Speter  SVN_ERR(lazyopen_if_unopened(b));
1964251881Speter  SVN_ERR(svn_stream_seek(b->real_stream, mark));
1965251881Speter
1966251881Speter  return SVN_NO_ERROR;
1967251881Speter}
1968251881Speter
1969299742Sdimstatic svn_error_t *
1970299742Sdimdata_available_handler_lazyopen(void *baton,
1971299742Sdim                                svn_boolean_t *data_available)
1972299742Sdim{
1973299742Sdim  lazyopen_baton_t *b = baton;
1974299742Sdim
1975299742Sdim  SVN_ERR(lazyopen_if_unopened(b));
1976299742Sdim  return svn_error_trace(svn_stream_data_available(b->real_stream,
1977299742Sdim                                                   data_available));
1978299742Sdim}
1979299742Sdim
1980251881Speter/* Implements svn_stream__is_buffered_fn_t */
1981251881Speterstatic svn_boolean_t
1982251881Speteris_buffered_lazyopen(void *baton)
1983251881Speter{
1984251881Speter  lazyopen_baton_t *b = baton;
1985251881Speter
1986251881Speter  /* No lazy open as we cannot handle an open error. */
1987251881Speter  if (!b->real_stream)
1988251881Speter    return FALSE;
1989251881Speter
1990251881Speter  return svn_stream__is_buffered(b->real_stream);
1991251881Speter}
1992251881Speter
1993251881Spetersvn_stream_t *
1994251881Spetersvn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,
1995251881Speter                           void *open_baton,
1996251881Speter                           svn_boolean_t open_on_close,
1997251881Speter                           apr_pool_t *result_pool)
1998251881Speter{
1999251881Speter  lazyopen_baton_t *lob = apr_pcalloc(result_pool, sizeof(*lob));
2000251881Speter  svn_stream_t *stream;
2001251881Speter
2002251881Speter  lob->open_func = open_func;
2003251881Speter  lob->open_baton = open_baton;
2004251881Speter  lob->real_stream = NULL;
2005251881Speter  lob->pool = result_pool;
2006251881Speter  lob->open_on_close = open_on_close;
2007251881Speter
2008251881Speter  stream = svn_stream_create(lob, result_pool);
2009299742Sdim  svn_stream_set_read2(stream, read_handler_lazyopen,
2010299742Sdim                       read_full_handler_lazyopen);
2011251881Speter  svn_stream_set_skip(stream, skip_handler_lazyopen);
2012251881Speter  svn_stream_set_write(stream, write_handler_lazyopen);
2013251881Speter  svn_stream_set_close(stream, close_handler_lazyopen);
2014251881Speter  svn_stream_set_mark(stream, mark_handler_lazyopen);
2015251881Speter  svn_stream_set_seek(stream, seek_handler_lazyopen);
2016299742Sdim  svn_stream_set_data_available(stream, data_available_handler_lazyopen);
2017251881Speter  svn_stream__set_is_buffered(stream, is_buffered_lazyopen);
2018251881Speter
2019251881Speter  return stream;
2020251881Speter}
2021299742Sdim
2022299742Sdim/* Baton for install streams */
2023299742Sdimstruct install_baton_t
2024299742Sdim{
2025299742Sdim  struct baton_apr baton_apr;
2026299742Sdim  const char *tmp_path;
2027299742Sdim};
2028299742Sdim
2029299742Sdim#ifdef WIN32
2030299742Sdim
2031299742Sdim/* Create and open a tempfile in DIRECTORY. Return its handle and path */
2032299742Sdimstatic svn_error_t *
2033299742Sdimcreate_tempfile(HANDLE *hFile,
2034299742Sdim                const char **file_path,
2035299742Sdim                const char *directory,
2036299742Sdim                apr_pool_t *result_pool,
2037299742Sdim                apr_pool_t *scratch_pool)
2038299742Sdim{
2039299742Sdim  const char *unique_name;
2040299742Sdim  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2041299742Sdim  static svn_atomic_t tempname_counter;
2042299742Sdim  int baseNr = (GetTickCount() << 11) + 13 * svn_atomic_inc(&tempname_counter)
2043299742Sdim               + GetCurrentProcessId();
2044299742Sdim  int i = 0;
2045299742Sdim  HANDLE h;
2046299742Sdim
2047299742Sdim  /* Shares common idea with io.c's temp_file_create */
2048299742Sdim
2049299742Sdim  do
2050299742Sdim    {
2051299742Sdim      apr_uint32_t unique_nr;
2052299742Sdim      WCHAR *w_name;
2053299742Sdim
2054299742Sdim      /* Generate a number that should be unique for this application and
2055299742Sdim         usually for the entire computer to reduce the number of cycles
2056299742Sdim         through this loop. (A bit of calculation is much cheaper than
2057299742Sdim         disk io) */
2058299742Sdim      unique_nr = baseNr + 7 * i++;
2059299742Sdim
2060299742Sdim
2061299742Sdim      svn_pool_clear(iterpool);
2062299742Sdim      unique_name = svn_dirent_join(directory,
2063299742Sdim                                    apr_psprintf(iterpool, "svn-%X",
2064299742Sdim                                                 unique_nr),
2065299742Sdim                                    iterpool);
2066299742Sdim
2067299742Sdim      SVN_ERR(svn_io__utf8_to_unicode_longpath(&w_name, unique_name,
2068299742Sdim                                               iterpool));
2069299742Sdim
2070299742Sdim      /* Create a completely not-sharable file to avoid indexers, and other
2071299742Sdim         filesystem watchers locking the file while we are still writing.
2072299742Sdim
2073299742Sdim         We need DELETE privileges to move the file. */
2074299742Sdim      h = CreateFileW(w_name, GENERIC_WRITE | DELETE, 0 /* share */,
2075299742Sdim                      NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
2076299742Sdim
2077299742Sdim      if (h == INVALID_HANDLE_VALUE)
2078299742Sdim        {
2079299742Sdim          apr_status_t status = apr_get_os_error();
2080299742Sdim          if (i > 1000)
2081299742Sdim            return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
2082299742Sdim                           svn_error_wrap_apr(status, NULL),
2083299742Sdim                           _("Unable to make name in '%s'"),
2084299742Sdim                           svn_dirent_local_style(directory, scratch_pool));
2085299742Sdim
2086299742Sdim          if (!APR_STATUS_IS_EEXIST(status) && !APR_STATUS_IS_EACCES(status))
2087299742Sdim            return svn_error_wrap_apr(status, NULL);
2088299742Sdim        }
2089299742Sdim    }
2090299742Sdim  while (h == INVALID_HANDLE_VALUE);
2091299742Sdim
2092299742Sdim  *hFile = h;
2093299742Sdim  *file_path = apr_pstrdup(result_pool, unique_name);
2094299742Sdim  svn_pool_destroy(iterpool);
2095299742Sdim
2096299742Sdim  return SVN_NO_ERROR;
2097299742Sdim}
2098299742Sdim
2099299742Sdim/* Implements svn_close_fn_t */
2100299742Sdimstatic svn_error_t *
2101299742Sdiminstall_close(void *baton)
2102299742Sdim{
2103299742Sdim  struct install_baton_t *ib = baton;
2104299742Sdim
2105299742Sdim  /* Flush the data cached in APR, but don't close the file yet */
2106299742Sdim  SVN_ERR(svn_io_file_flush(ib->baton_apr.file, ib->baton_apr.pool));
2107299742Sdim
2108299742Sdim  return SVN_NO_ERROR;
2109299742Sdim}
2110299742Sdim
2111299742Sdim#endif /* WIN32 */
2112299742Sdim
2113299742Sdimsvn_error_t *
2114299742Sdimsvn_stream__create_for_install(svn_stream_t **install_stream,
2115299742Sdim                               const char *tmp_abspath,
2116299742Sdim                               apr_pool_t *result_pool,
2117299742Sdim                               apr_pool_t *scratch_pool)
2118299742Sdim{
2119299742Sdim  apr_file_t *file;
2120299742Sdim  struct install_baton_t *ib;
2121299742Sdim  const char *tmp_path;
2122299742Sdim
2123299742Sdim#ifdef WIN32
2124299742Sdim  HANDLE hInstall;
2125299742Sdim  apr_status_t status;
2126299742Sdim
2127299742Sdim  SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2128299742Sdim
2129299742Sdim  SVN_ERR(create_tempfile(&hInstall, &tmp_path, tmp_abspath,
2130299742Sdim                          scratch_pool, scratch_pool));
2131299742Sdim
2132299742Sdim  /* Wrap as a standard APR file to allow sharing implementation.
2133299742Sdim
2134299742Sdim     But do note that some file functions (such as retrieving the name)
2135299742Sdim     don't work on this wrapper. */
2136299742Sdim  /* ### Buffered, or not? */
2137299742Sdim  status = apr_os_file_put(&file, &hInstall,
2138299742Sdim                           APR_WRITE | APR_BINARY | APR_BUFFERED,
2139299742Sdim                           result_pool);
2140299742Sdim
2141299742Sdim  if (status)
2142299742Sdim    {
2143299742Sdim      CloseHandle(hInstall);
2144299742Sdim      return svn_error_wrap_apr(status, NULL);
2145299742Sdim    }
2146299742Sdim
2147299742Sdim  tmp_path = svn_dirent_internal_style(tmp_path, result_pool);
2148299742Sdim#else
2149299742Sdim
2150299742Sdim  SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2151299742Sdim
2152299742Sdim  SVN_ERR(svn_io_open_unique_file3(&file, &tmp_path, tmp_abspath,
2153299742Sdim                                   svn_io_file_del_none,
2154299742Sdim                                   result_pool, scratch_pool));
2155299742Sdim#endif
2156299742Sdim  *install_stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
2157299742Sdim
2158299742Sdim  ib = apr_pcalloc(result_pool, sizeof(*ib));
2159299742Sdim  ib->baton_apr = *(struct baton_apr*)(*install_stream)->baton;
2160299742Sdim
2161299742Sdim  assert((void*)&ib->baton_apr == (void*)ib); /* baton pointer is the same */
2162299742Sdim
2163299742Sdim  (*install_stream)->baton = ib;
2164299742Sdim
2165299742Sdim  ib->tmp_path = tmp_path;
2166299742Sdim
2167299742Sdim#ifdef WIN32
2168299742Sdim  /* Don't close the file on stream close; flush instead */
2169299742Sdim  svn_stream_set_close(*install_stream, install_close);
2170299742Sdim#else
2171299742Sdim  /* ### Install pool cleanup handler for tempfile? */
2172299742Sdim#endif
2173299742Sdim
2174299742Sdim  return SVN_NO_ERROR;
2175299742Sdim}
2176299742Sdim
2177299742Sdimsvn_error_t *
2178299742Sdimsvn_stream__install_stream(svn_stream_t *install_stream,
2179299742Sdim                           const char *final_abspath,
2180299742Sdim                           svn_boolean_t make_parents,
2181299742Sdim                           apr_pool_t *scratch_pool)
2182299742Sdim{
2183299742Sdim  struct install_baton_t *ib = install_stream->baton;
2184299742Sdim  svn_error_t *err;
2185299742Sdim
2186299742Sdim  SVN_ERR_ASSERT(svn_dirent_is_absolute(final_abspath));
2187299742Sdim#ifdef WIN32
2188299742Sdim  err = svn_io__win_rename_open_file(ib->baton_apr.file,  ib->tmp_path,
2189299742Sdim                                     final_abspath, scratch_pool);
2190299742Sdim  if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2191299742Sdim    {
2192299742Sdim      svn_error_t *err2;
2193299742Sdim
2194299742Sdim      err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2195299742Sdim                                                    scratch_pool),
2196299742Sdim                                         scratch_pool);
2197299742Sdim
2198299742Sdim      if (err2)
2199299742Sdim        return svn_error_trace(svn_error_compose_create(err, err2));
2200299742Sdim      else
2201299742Sdim        svn_error_clear(err);
2202299742Sdim
2203299742Sdim      err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path,
2204299742Sdim                                         final_abspath, scratch_pool);
2205299742Sdim    }
2206299742Sdim
2207299742Sdim  /* ### rhuijben: I wouldn't be surprised if we later find out that we
2208299742Sdim                   have to fall back to close+rename on some specific
2209299742Sdim                   error values here, to support some non standard NAS
2210299742Sdim                   and filesystem scenarios. */
2211299742Sdim  if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
2212299742Sdim    {
2213299742Sdim      /* Rename open files is not supported on this platform: fallback to
2214299742Sdim         svn_io_file_rename2(). */
2215299742Sdim      svn_error_clear(err);
2216299742Sdim      err = SVN_NO_ERROR;
2217299742Sdim
2218299742Sdim      SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2219299742Sdim    }
2220299742Sdim  else
2221299742Sdim    {
2222299742Sdim      return svn_error_compose_create(err,
2223299742Sdim                                      svn_io_file_close(ib->baton_apr.file,
2224299742Sdim                                                        scratch_pool));
2225299742Sdim    }
2226299742Sdim#endif
2227299742Sdim
2228299742Sdim  err = svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool);
2229299742Sdim
2230299742Sdim  /* A missing directory is too common to not cover here. */
2231299742Sdim  if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2232299742Sdim    {
2233299742Sdim      svn_error_t *err2;
2234299742Sdim
2235299742Sdim      err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2236299742Sdim                                                            scratch_pool),
2237299742Sdim                                         scratch_pool);
2238299742Sdim
2239299742Sdim      if (err2)
2240299742Sdim        /* Creating directory didn't work: Return all errors */
2241299742Sdim        return svn_error_trace(svn_error_compose_create(err, err2));
2242299742Sdim      else
2243299742Sdim        /* We could create a directory: retry install */
2244299742Sdim        svn_error_clear(err);
2245299742Sdim
2246299742Sdim      SVN_ERR(svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool));
2247299742Sdim    }
2248299742Sdim  else
2249299742Sdim    SVN_ERR(err);
2250299742Sdim
2251299742Sdim  return SVN_NO_ERROR;
2252299742Sdim}
2253299742Sdim
2254299742Sdimsvn_error_t *
2255299742Sdimsvn_stream__install_get_info(apr_finfo_t *finfo,
2256299742Sdim                             svn_stream_t *install_stream,
2257299742Sdim                             apr_int32_t wanted,
2258299742Sdim                             apr_pool_t *scratch_pool)
2259299742Sdim{
2260299742Sdim  struct install_baton_t *ib = install_stream->baton;
2261299742Sdim
2262299742Sdim#ifdef WIN32
2263299742Sdim  /* On WIN32 the file is still open, so we can obtain the information
2264299742Sdim     from the handle without race conditions */
2265299742Sdim  apr_status_t status;
2266299742Sdim
2267299742Sdim  status = apr_file_info_get(finfo, wanted, ib->baton_apr.file);
2268299742Sdim
2269299742Sdim  if (status)
2270299742Sdim    return svn_error_wrap_apr(status, NULL);
2271299742Sdim#else
2272299742Sdim  SVN_ERR(svn_io_stat(finfo, ib->tmp_path, wanted, scratch_pool));
2273299742Sdim#endif
2274299742Sdim
2275299742Sdim  return SVN_NO_ERROR;
2276299742Sdim}
2277299742Sdim
2278299742Sdimsvn_error_t *
2279299742Sdimsvn_stream__install_delete(svn_stream_t *install_stream,
2280299742Sdim                           apr_pool_t *scratch_pool)
2281299742Sdim{
2282299742Sdim  struct install_baton_t *ib = install_stream->baton;
2283299742Sdim
2284299742Sdim#ifdef WIN32
2285299742Sdim  svn_error_t *err;
2286299742Sdim
2287299742Sdim  /* Mark the file as delete on close to avoid having to reopen
2288299742Sdim     the file as part of the delete handling. */
2289299742Sdim  err = svn_io__win_delete_file_on_close(ib->baton_apr.file,  ib->tmp_path,
2290299742Sdim                                         scratch_pool);
2291299742Sdim  if (err == SVN_NO_ERROR)
2292299742Sdim    {
2293299742Sdim      SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2294299742Sdim      return SVN_NO_ERROR; /* File is already gone */
2295299742Sdim    }
2296299742Sdim
2297299742Sdim  /* Deleting file on close may be unsupported, so ignore errors and
2298299742Sdim     fallback to svn_io_remove_file2(). */
2299299742Sdim  svn_error_clear(err);
2300299742Sdim  SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2301299742Sdim#endif
2302299742Sdim
2303299742Sdim  return svn_error_trace(svn_io_remove_file2(ib->tmp_path, FALSE,
2304299742Sdim                                             scratch_pool));
2305299742Sdim}
2306