source: valtobtest/subversion-1.6.2/subversion/libsvn_subr/stream.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

File size: 27.5 KB
Line 
1/*
2 * stream.c:   svn_stream operations
3 *
4 * ====================================================================
5 * Copyright (c) 2000-2004, 2006 CollabNet.  All rights reserved.
6 *
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution.  The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
12 *
13 * This software consists of voluntary contributions made by many
14 * individuals.  For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
17 */
18
19#include "svn_private_config.h"
20
21#include <assert.h>
22#include <stdio.h>
23
24#include <apr.h>
25#include <apr_pools.h>
26#include <apr_strings.h>
27#include <apr_file_io.h>
28#include <apr_errno.h>
29#include <apr_md5.h>
30
31#include <zlib.h>
32
33#include "svn_pools.h"
34#include "svn_io.h"
35#include "svn_error.h"
36#include "svn_string.h"
37#include "svn_utf.h"
38#include "svn_checksum.h"
39#include "svn_path.h"
40
41
42struct svn_stream_t {
43  void *baton;
44  svn_read_fn_t read_fn;
45  svn_write_fn_t write_fn;
46  svn_close_fn_t close_fn;
47};
48
49
50
51/*** Generic streams. ***/
52
53svn_stream_t *
54svn_stream_create(void *baton, apr_pool_t *pool)
55{
56  svn_stream_t *stream;
57
58  stream = apr_palloc(pool, sizeof(*stream));
59  stream->baton = baton;
60  stream->read_fn = NULL;
61  stream->write_fn = NULL;
62  stream->close_fn = NULL;
63  return stream;
64}
65
66
67void
68svn_stream_set_baton(svn_stream_t *stream, void *baton)
69{
70  stream->baton = baton;
71}
72
73
74void
75svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn)
76{
77  stream->read_fn = read_fn;
78}
79
80
81void
82svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
83{
84  stream->write_fn = write_fn;
85}
86
87
88void
89svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
90{
91  stream->close_fn = close_fn;
92}
93
94
95svn_error_t *
96svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len)
97{
98  SVN_ERR_ASSERT(stream->read_fn != NULL);
99  return stream->read_fn(stream->baton, buffer, len);
100}
101
102
103svn_error_t *
104svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)
105{
106  SVN_ERR_ASSERT(stream->write_fn != NULL);
107  return stream->write_fn(stream->baton, data, len);
108}
109
110
111svn_error_t *
112svn_stream_close(svn_stream_t *stream)
113{
114  if (stream->close_fn == NULL)
115    return SVN_NO_ERROR;
116  return stream->close_fn(stream->baton);
117}
118
119
120svn_error_t *
121svn_stream_printf(svn_stream_t *stream,
122                  apr_pool_t *pool,
123                  const char *fmt,
124                  ...)
125{
126  const char *message;
127  va_list ap;
128  apr_size_t len;
129
130  va_start(ap, fmt);
131  message = apr_pvsprintf(pool, fmt, ap);
132  va_end(ap);
133
134  len = strlen(message);
135  return svn_stream_write(stream, message, &len);
136}
137
138
139svn_error_t *
140svn_stream_printf_from_utf8(svn_stream_t *stream,
141                            const char *encoding,
142                            apr_pool_t *pool,
143                            const char *fmt,
144                            ...)
145{
146  const char *message, *translated;
147  va_list ap;
148  apr_size_t len;
149
150  va_start(ap, fmt);
151  message = apr_pvsprintf(pool, fmt, ap);
152  va_end(ap);
153
154  SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,
155                                        pool));
156
157  len = strlen(translated);
158
159  return svn_stream_write(stream, translated, &len);
160}
161
162
163svn_error_t *
164svn_stream_readline(svn_stream_t *stream,
165                    svn_stringbuf_t **stringbuf,
166                    const char *eol,
167                    svn_boolean_t *eof,
168                    apr_pool_t *pool)
169{
170  apr_size_t numbytes;
171  const char *match;
172  char c;
173  /* Since we're reading one character at a time, let's at least
174     optimize for the 90% case.  90% of the time, we can avoid the
175     stringbuf ever having to realloc() itself if we start it out at
176     80 chars.  */
177  svn_stringbuf_t *str = svn_stringbuf_create_ensure(80, pool);
178
179  match = eol;
180  while (*match)
181    {
182      numbytes = 1;
183      SVN_ERR(svn_stream_read(stream, &c, &numbytes));
184      if (numbytes != 1)
185        {
186          /* a 'short' read means the stream has run out. */
187          *eof = TRUE;
188          *stringbuf = str;
189          return SVN_NO_ERROR;
190        }
191
192      if (c == *match)
193        match++;
194      else
195        match = eol;
196
197      svn_stringbuf_appendbytes(str, &c, 1);
198    }
199
200  *eof = FALSE;
201  svn_stringbuf_chop(str, match - eol);
202  *stringbuf = str;
203  return SVN_NO_ERROR;
204}
205
206
207svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
208                              svn_cancel_func_t cancel_func,
209                              void *cancel_baton,
210                              apr_pool_t *scratch_pool)
211{
212  char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
213  svn_error_t *err;
214  svn_error_t *err2;
215
216  /* Read and write chunks until we get a short read, indicating the
217     end of the stream.  (We can't get a short write without an
218     associated error.) */
219  while (1)
220    {
221      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
222
223      if (cancel_func)
224        SVN_ERR(cancel_func(cancel_baton));
225
226      SVN_ERR(svn_stream_read(from, buf, &len));
227      if (len > 0)
228        SVN_ERR(svn_stream_write(to, buf, &len));
229      if (len != SVN__STREAM_CHUNK_SIZE)
230        break;
231    }
232
233  err = svn_stream_close(from);
234  err2 = svn_stream_close(to);
235  if (err)
236    {
237      /* ### it would be nice to compose the two errors in some way */
238      svn_error_clear(err2);  /* note: might be NULL */
239      return err;
240    }
241  return err2;
242}
243
244svn_error_t *svn_stream_copy2(svn_stream_t *from, svn_stream_t *to,
245                              svn_cancel_func_t cancel_func,
246                              void *cancel_baton,
247                              apr_pool_t *scratch_pool)
248{
249  return svn_stream_copy3(svn_stream_disown(from, scratch_pool),
250                          svn_stream_disown(to, scratch_pool),
251                          cancel_func, cancel_baton, scratch_pool);
252}
253
254svn_error_t *svn_stream_copy(svn_stream_t *from, svn_stream_t *to,
255                             apr_pool_t *scratch_pool)
256{
257  return svn_stream_copy3(svn_stream_disown(from, scratch_pool),
258                          svn_stream_disown(to, scratch_pool),
259                          NULL, NULL, scratch_pool);
260}
261
262
263svn_error_t *
264svn_stream_contents_same(svn_boolean_t *same,
265                         svn_stream_t *stream1,
266                         svn_stream_t *stream2,
267                         apr_pool_t *pool)
268{
269  char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
270  char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
271  apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;
272  apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;
273
274  *same = TRUE;  /* assume TRUE, until disproved below */
275  while (bytes_read1 == SVN__STREAM_CHUNK_SIZE
276         && bytes_read2 == SVN__STREAM_CHUNK_SIZE)
277    {
278      SVN_ERR(svn_stream_read(stream1, buf1, &bytes_read1));
279      SVN_ERR(svn_stream_read(stream2, buf2, &bytes_read2));
280
281      if ((bytes_read1 != bytes_read2)
282          || (memcmp(buf1, buf2, bytes_read1)))
283        {
284          *same = FALSE;
285          break;
286        }
287    }
288
289  return SVN_NO_ERROR;
290}
291
292
293
294/*** Generic readable empty stream ***/
295
296static svn_error_t *
297read_handler_empty(void *baton, char *buffer, apr_size_t *len)
298{
299  *len = 0;
300  return SVN_NO_ERROR;
301}
302
303
304static svn_error_t *
305write_handler_empty(void *baton, const char *data, apr_size_t *len)
306{
307  return SVN_NO_ERROR;
308}
309
310
311svn_stream_t *
312svn_stream_empty(apr_pool_t *pool)
313{
314  svn_stream_t *stream;
315
316  stream = svn_stream_create(NULL, pool);
317  svn_stream_set_read(stream, read_handler_empty);
318  svn_stream_set_write(stream, write_handler_empty);
319  return stream;
320}
321
322
323
324
325/*** Ownership detaching stream ***/
326
327static svn_error_t *
328read_handler_disown(void *baton, char *buffer, apr_size_t *len)
329{
330  return svn_stream_read((svn_stream_t *)baton, buffer, len);
331}
332
333static svn_error_t *
334write_handler_disown(void *baton, const char *buffer, apr_size_t *len)
335{
336  return svn_stream_write((svn_stream_t *)baton, buffer, len);
337}
338
339
340svn_stream_t *
341svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
342{
343  svn_stream_t *s = svn_stream_create(stream, pool);
344
345  svn_stream_set_read(s, read_handler_disown);
346  svn_stream_set_write(s, write_handler_disown);
347
348  return s;
349}
350
351
352
353/*** Generic stream for APR files ***/
354struct baton_apr {
355  apr_file_t *file;
356  apr_pool_t *pool;
357};
358
359
360static svn_error_t *
361read_handler_apr(void *baton, char *buffer, apr_size_t *len)
362{
363  struct baton_apr *btn = baton;
364  svn_error_t *err;
365
366  err = svn_io_file_read_full(btn->file, buffer, *len, len, btn->pool);
367  if (err && APR_STATUS_IS_EOF(err->apr_err))
368    {
369      svn_error_clear(err);
370      err = SVN_NO_ERROR;
371    }
372
373  return err;
374}
375
376
377static svn_error_t *
378write_handler_apr(void *baton, const char *data, apr_size_t *len)
379{
380  struct baton_apr *btn = baton;
381
382  return svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
383}
384
385static svn_error_t *
386close_handler_apr(void *baton)
387{
388  struct baton_apr *btn = baton;
389
390  return svn_io_file_close(btn->file, btn->pool);
391}
392
393
394svn_error_t *
395svn_stream_open_readonly(svn_stream_t **stream,
396                         const char *path,
397                         apr_pool_t *result_pool,
398                         apr_pool_t *scratch_pool)
399{
400  apr_file_t *file;
401
402  SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED | APR_BINARY,
403                           APR_OS_DEFAULT, result_pool));
404  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
405
406  return SVN_NO_ERROR;
407}
408
409
410svn_error_t *
411svn_stream_open_writable(svn_stream_t **stream,
412                         const char *path,
413                         apr_pool_t *result_pool,
414                         apr_pool_t *scratch_pool)
415{
416  apr_file_t *file;
417
418  SVN_ERR(svn_io_file_open(&file, path,
419                           APR_WRITE
420                             | APR_BUFFERED
421                             | APR_BINARY
422                             | APR_CREATE
423                             | APR_EXCL,
424                           APR_OS_DEFAULT, result_pool));
425  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
426
427  return SVN_NO_ERROR;
428}
429
430
431svn_error_t *
432svn_stream_open_unique(svn_stream_t **stream,
433                       const char **temp_path,
434                       const char *dirpath,
435                       svn_io_file_del_t delete_when,
436                       apr_pool_t *result_pool,
437                       apr_pool_t *scratch_pool)
438{
439  apr_file_t *file;
440
441  SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath,
442                                   delete_when, result_pool, scratch_pool));
443  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
444
445  return SVN_NO_ERROR;
446}
447
448
449svn_stream_t *
450svn_stream_from_aprfile2(apr_file_t *file,
451                         svn_boolean_t disown,
452                         apr_pool_t *pool)
453{
454  struct baton_apr *baton;
455  svn_stream_t *stream;
456
457  if (file == NULL)
458    return svn_stream_empty(pool);
459
460  baton = apr_palloc(pool, sizeof(*baton));
461  baton->file = file;
462  baton->pool = pool;
463  stream = svn_stream_create(baton, pool);
464  svn_stream_set_read(stream, read_handler_apr);
465  svn_stream_set_write(stream, write_handler_apr);
466
467  if (! disown)
468    svn_stream_set_close(stream, close_handler_apr);
469
470  return stream;
471}
472
473svn_stream_t *
474svn_stream_from_aprfile(apr_file_t *file, apr_pool_t *pool)
475{
476  return svn_stream_from_aprfile2(file, TRUE, pool);
477}
478
479
480
481/* Compressed stream support */
482
483#define ZBUFFER_SIZE 4096       /* The size of the buffer the
484                                   compressed stream uses to read from
485                                   the substream. Basically an
486                                   arbitrary value, picked to be about
487                                   page-sized. */
488
489struct zbaton {
490  z_stream *in;                 /* compressed stream for reading */
491  z_stream *out;                /* compressed stream for writing */
492  svn_read_fn_t read;           /* substream's read function */
493  svn_write_fn_t write;         /* substream's write function */
494  svn_close_fn_t close;         /* substream's close function */
495  void *read_buffer;            /* buffer   used   for  reading   from
496                                   substream */
497  int read_flush;               /* what flush mode to use while
498                                   reading */
499  apr_pool_t *pool;             /* The pool this baton is allocated
500                                   on */
501  void *subbaton;               /* The substream's baton */
502};
503
504/* zlib alloc function. opaque is the pool we need. */
505static voidpf
506zalloc(voidpf opaque, uInt items, uInt size)
507{
508  apr_pool_t *pool = opaque;
509
510  return apr_palloc(pool, items * size);
511}
512
513/* zlib free function */
514static void
515zfree(voidpf opaque, voidpf address)
516{
517  /* Empty, since we allocate on the pool */
518}
519
520/* Converts a zlib error to an svn_error_t. zerr is the error code,
521   function is the function name, and stream is the z_stream we are
522   using.  */
523static svn_error_t *
524zerr_to_svn_error(int zerr, const char *function, z_stream *stream)
525{
526  apr_status_t status;
527  const char *message;
528
529  if (zerr == Z_OK)
530    return SVN_NO_ERROR;
531
532  switch (zerr)
533    {
534    case Z_STREAM_ERROR:
535      status = SVN_ERR_STREAM_MALFORMED_DATA;
536      message = "stream error";
537      break;
538
539    case Z_MEM_ERROR:
540      status = APR_ENOMEM;
541      message = "out of memory";
542      break;
543
544    case Z_BUF_ERROR:
545      status = APR_ENOMEM;
546      message = "buffer error";
547      break;
548
549    case Z_VERSION_ERROR:
550      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
551      message = "version error";
552      break;
553
554    case Z_DATA_ERROR:
555      status = SVN_ERR_STREAM_MALFORMED_DATA;
556      message = "corrupted data";
557      break;
558
559    default:
560      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
561      message = "error";
562      break;
563    }
564
565  if (stream->msg != NULL)
566    return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
567                             message, stream->msg);
568  else
569    return svn_error_createf(status, NULL, "zlib (%s): %s", function,
570                             message);
571}
572
573/* Helper function to figure out the sync mode */
574static svn_error_t *
575read_helper_gz(svn_read_fn_t read_fn,
576               void *baton,
577               char *buffer,
578               uInt *len, int *zflush)
579{
580  uInt orig_len = *len;
581
582  /* There's no reason this value should grow bigger than the range of
583     uInt, but Subversion's API requires apr_size_t. */
584  apr_size_t apr_len = (apr_size_t) *len;
585
586  SVN_ERR((*read_fn)(baton, buffer, &apr_len));
587
588  /* Type cast back to uInt type that zlib uses.  On LP64 platforms
589     apr_size_t will be bigger than uInt. */
590  *len = (uInt) apr_len;
591
592  /* I wanted to use Z_FINISH here, but we need to know our buffer is
593     big enough */
594  *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;
595
596  return SVN_NO_ERROR;
597}
598
599/* Handle reading from a compressed stream */
600static svn_error_t *
601read_handler_gz(void *baton, char *buffer, apr_size_t *len)
602{
603  struct zbaton *btn = baton;
604  int zerr;
605
606  if (btn->in == NULL)
607    {
608      btn->in = apr_palloc(btn->pool, sizeof(z_stream));
609      btn->in->zalloc = zalloc;
610      btn->in->zfree = zfree;
611      btn->in->opaque = btn->pool;
612      btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);
613      btn->in->next_in = btn->read_buffer;
614      btn->in->avail_in = ZBUFFER_SIZE;
615
616      SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
617                             &btn->in->avail_in, &btn->read_flush));
618
619      zerr = inflateInit(btn->in);
620      SVN_ERR(zerr_to_svn_error(zerr, "inflateInit", btn->in));
621    }
622
623  btn->in->next_out = (Bytef *) buffer;
624  btn->in->avail_out = *len;
625
626  while (btn->in->avail_out > 0)
627    {
628      if (btn->in->avail_in <= 0)
629        {
630          btn->in->avail_in = ZBUFFER_SIZE;
631          btn->in->next_in = btn->read_buffer;
632          SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,
633                                 &btn->in->avail_in, &btn->read_flush));
634        }
635
636      zerr = inflate(btn->in, btn->read_flush);
637      if (zerr == Z_STREAM_END)
638        break;
639      else if (zerr != Z_OK)
640        return zerr_to_svn_error(zerr, "inflate", btn->in);
641    }
642
643  *len -= btn->in->avail_out;
644  return SVN_NO_ERROR;
645}
646
647/* Compress data and write it to the substream */
648static svn_error_t *
649write_handler_gz(void *baton, const char *buffer, apr_size_t *len)
650{
651  struct zbaton *btn = baton;
652  apr_pool_t *subpool;
653  void *write_buf;
654  apr_size_t buf_size, write_len;
655  int zerr;
656
657  if (btn->out == NULL)
658    {
659      btn->out = apr_palloc(btn->pool, sizeof(z_stream));
660      btn->out->zalloc = zalloc;
661      btn->out->zfree = zfree;
662      btn->out->opaque =  btn->pool;
663
664      zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);
665      SVN_ERR(zerr_to_svn_error(zerr, "deflateInit", btn->out));
666    }
667
668  /* The largest buffer we should need is 0.1% larger than the
669     compressed data, + 12 bytes. This info comes from zlib.h.  */
670  buf_size = *len + (*len / 1000) + 13;
671  subpool = svn_pool_create(btn->pool);
672  write_buf = apr_palloc(subpool, buf_size);
673
674  btn->out->next_in = (Bytef *) buffer;  /* Casting away const! */
675  btn->out->avail_in = *len;
676
677  while (btn->out->avail_in > 0)
678    {
679      btn->out->next_out = write_buf;
680      btn->out->avail_out = buf_size;
681
682      zerr = deflate(btn->out, Z_NO_FLUSH);
683      SVN_ERR(zerr_to_svn_error(zerr, "deflate", btn->out));
684      write_len = buf_size - btn->out->avail_out;
685      if (write_len > 0)
686        SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len));
687    }
688
689  svn_pool_destroy(subpool);
690
691  return SVN_NO_ERROR;
692}
693
694/* Handle flushing and closing the stream */
695static svn_error_t *
696close_handler_gz(void *baton)
697{
698  struct zbaton *btn = baton;
699  int zerr;
700
701  if (btn->in != NULL)
702    {
703      zerr = inflateEnd(btn->in);
704      SVN_ERR(zerr_to_svn_error(zerr, "inflateEnd", btn->in));
705    }
706
707  if (btn->out != NULL)
708    {
709      void *buf;
710      apr_size_t write_len;
711
712      buf = apr_palloc(btn->pool, ZBUFFER_SIZE);
713
714      while (TRUE)
715        {
716          btn->out->next_out = buf;
717          btn->out->avail_out = ZBUFFER_SIZE;
718
719          zerr = deflate(btn->out, Z_FINISH);
720          if (zerr != Z_STREAM_END && zerr != Z_OK)
721            return zerr_to_svn_error(zerr, "deflate", btn->out);
722          write_len = ZBUFFER_SIZE - btn->out->avail_out;
723          if (write_len > 0)
724            SVN_ERR(btn->write(btn->subbaton, buf, &write_len));
725          if (zerr == Z_STREAM_END)
726            break;
727        }
728
729      zerr = deflateEnd(btn->out);
730      SVN_ERR(zerr_to_svn_error(zerr, "deflateEnd", btn->out));
731    }
732
733  if (btn->close != NULL)
734    return btn->close(btn->subbaton);
735  else
736    return SVN_NO_ERROR;
737}
738
739
740svn_stream_t *
741svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)
742{
743  struct svn_stream_t *zstream;
744  struct zbaton *baton;
745
746  assert(stream != NULL);
747
748  baton = apr_palloc(pool, sizeof(*baton));
749  baton->in = baton->out = NULL;
750  baton->read = stream->read_fn;
751  baton->write = stream->write_fn;
752  baton->close = stream->close_fn;
753  baton->subbaton = stream->baton;
754  baton->pool = pool;
755  baton->read_buffer = NULL;
756  baton->read_flush = Z_SYNC_FLUSH;
757
758  zstream = svn_stream_create(baton, pool);
759  svn_stream_set_read(zstream, read_handler_gz);
760  svn_stream_set_write(zstream, write_handler_gz);
761  svn_stream_set_close(zstream, close_handler_gz);
762
763  return zstream;
764}
765
766
767/* Checksummed stream support */
768
769struct checksum_stream_baton
770{
771  svn_checksum_ctx_t *read_ctx, *write_ctx;
772  svn_checksum_t **read_checksum;  /* Output value. */
773  svn_checksum_t **write_checksum;  /* Output value. */
774  svn_stream_t *proxy;
775
776  /* True if more data should be read when closing the stream. */
777  svn_boolean_t read_more;
778
779  /* Pool to allocate read buffer and output values from. */
780  apr_pool_t *pool;
781};
782
783static svn_error_t *
784read_handler_checksum(void *baton, char *buffer, apr_size_t *len)
785{
786  struct checksum_stream_baton *btn = baton;
787  apr_size_t saved_len = *len;
788
789  SVN_ERR(svn_stream_read(btn->proxy, buffer, len));
790
791  if (btn->read_checksum)
792    SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
793
794  if (saved_len != *len)
795    btn->read_more = FALSE;
796
797  return SVN_NO_ERROR;
798}
799
800
801static svn_error_t *
802write_handler_checksum(void *baton, const char *buffer, apr_size_t *len)
803{
804  struct checksum_stream_baton *btn = baton;
805
806  if (btn->write_checksum && *len > 0)
807    SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len));
808
809  return svn_stream_write(btn->proxy, buffer, len);
810}
811
812
813static svn_error_t *
814close_handler_checksum(void *baton)
815{
816  struct checksum_stream_baton *btn = baton;
817
818  /* If we're supposed to drain the stream, do so before finalizing the
819     checksum. */
820  if (btn->read_more)
821    {
822      char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);
823      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
824
825      do
826        {
827          SVN_ERR(read_handler_checksum(baton, buf, &len));
828        }
829      while (btn->read_more);
830    }
831
832  if (btn->read_ctx)
833    SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool));
834
835  if (btn->write_ctx)
836    SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool));
837
838  return svn_stream_close(btn->proxy);
839}
840
841
842svn_stream_t *
843svn_stream_checksummed2(svn_stream_t *stream,
844                        svn_checksum_t **read_checksum,
845                        svn_checksum_t **write_checksum,
846                        svn_checksum_kind_t checksum_kind,
847                        svn_boolean_t read_all,
848                        apr_pool_t *pool)
849{
850  svn_stream_t *s;
851  struct checksum_stream_baton *baton;
852
853  if (read_checksum == NULL && write_checksum == NULL)
854    return stream;
855
856  baton = apr_palloc(pool, sizeof(*baton));
857  if (read_checksum)
858    baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool);
859  else
860    baton->read_ctx = NULL;
861
862  if (write_checksum)
863    baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool);
864  else
865    baton->write_ctx = NULL;
866
867  baton->read_checksum = read_checksum;
868  baton->write_checksum = write_checksum;
869  baton->proxy = stream;
870  baton->read_more = read_all;
871  baton->pool = pool;
872
873  s = svn_stream_create(baton, pool);
874  svn_stream_set_read(s, read_handler_checksum);
875  svn_stream_set_write(s, write_handler_checksum);
876  svn_stream_set_close(s, close_handler_checksum);
877  return s;
878}
879
880struct md5_stream_baton
881{
882  const unsigned char **read_digest;
883  const unsigned char **write_digest;
884  svn_checksum_t *read_checksum;
885  svn_checksum_t *write_checksum;
886  svn_stream_t *proxy;
887  apr_pool_t *pool;
888};
889
890static svn_error_t *
891read_handler_md5(void *baton, char *buffer, apr_size_t *len)
892{
893  struct md5_stream_baton *btn = baton;
894  return svn_stream_read(btn->proxy, buffer, len);
895}
896
897static svn_error_t *
898write_handler_md5(void *baton, const char *buffer, apr_size_t *len)
899{
900  struct md5_stream_baton *btn = baton;
901  return svn_stream_write(btn->proxy, buffer, len);
902}
903
904static svn_error_t *
905close_handler_md5(void *baton)
906{
907  struct md5_stream_baton *btn = baton;
908
909  SVN_ERR(svn_stream_close(btn->proxy));
910
911  if (btn->read_digest)
912    *btn->read_digest
913      = apr_pmemdup(btn->pool, btn->read_checksum->digest,
914                    APR_MD5_DIGESTSIZE);
915
916  if (btn->write_digest)
917    *btn->write_digest
918      = apr_pmemdup(btn->pool, btn->write_checksum->digest,
919                    APR_MD5_DIGESTSIZE);
920
921  return SVN_NO_ERROR;
922}
923
924
925svn_stream_t *
926svn_stream_checksummed(svn_stream_t *stream,
927                       const unsigned char **read_digest,
928                       const unsigned char **write_digest,
929                       svn_boolean_t read_all,
930                       apr_pool_t *pool)
931{
932  svn_stream_t *s;
933  struct md5_stream_baton *baton;
934
935  if (! read_digest && ! write_digest)
936    return stream;
937
938  baton = apr_palloc(pool, sizeof(*baton));
939  baton->read_digest = read_digest;
940  baton->write_digest = write_digest;
941  baton->pool = pool;
942
943  /* Set BATON->proxy to a stream that will fill in BATON->read_checksum
944   * and BATON->write_checksum (if we want them) when it is closed. */
945  baton->proxy
946    = svn_stream_checksummed2(stream,
947                              read_digest ? &baton->read_checksum : NULL,
948                              write_digest ? &baton->write_checksum : NULL,
949                              svn_checksum_md5,
950                              read_all, pool);
951
952  /* Create a stream that will forward its read/write/close operations to
953   * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we
954   * want them) after it closes BATON->proxy. */
955  s = svn_stream_create(baton, pool);
956  svn_stream_set_read(s, read_handler_md5);
957  svn_stream_set_write(s, write_handler_md5);
958  svn_stream_set_close(s, close_handler_md5);
959  return s;
960}
961
962
963
964
965/* Miscellaneous stream functions. */
966struct stringbuf_stream_baton
967{
968  svn_stringbuf_t *str;
969  apr_size_t amt_read;
970};
971
972static svn_error_t *
973read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
974{
975  struct stringbuf_stream_baton *btn = baton;
976  apr_size_t left_to_read = btn->str->len - btn->amt_read;
977
978  *len = (*len > left_to_read) ? left_to_read : *len;
979  memcpy(buffer, btn->str->data + btn->amt_read, *len);
980  btn->amt_read += *len;
981  return SVN_NO_ERROR;
982}
983
984static svn_error_t *
985write_handler_stringbuf(void *baton, const char *data, apr_size_t *len)
986{
987  struct stringbuf_stream_baton *btn = baton;
988
989  svn_stringbuf_appendbytes(btn->str, data, *len);
990  return SVN_NO_ERROR;
991}
992
993svn_stream_t *
994svn_stream_from_stringbuf(svn_stringbuf_t *str,
995                          apr_pool_t *pool)
996{
997  svn_stream_t *stream;
998  struct stringbuf_stream_baton *baton;
999
1000  if (! str)
1001    return svn_stream_empty(pool);
1002
1003  baton = apr_palloc(pool, sizeof(*baton));
1004  baton->str = str;
1005  baton->amt_read = 0;
1006  stream = svn_stream_create(baton, pool);
1007  svn_stream_set_read(stream, read_handler_stringbuf);
1008  svn_stream_set_write(stream, write_handler_stringbuf);
1009  return stream;
1010}
1011
1012struct string_stream_baton
1013{
1014  const svn_string_t *str;
1015  apr_size_t amt_read;
1016};
1017
1018static svn_error_t *
1019read_handler_string(void *baton, char *buffer, apr_size_t *len)
1020{
1021  struct string_stream_baton *btn = baton;
1022  apr_size_t left_to_read = btn->str->len - btn->amt_read;
1023
1024  *len = (*len > left_to_read) ? left_to_read : *len;
1025  memcpy(buffer, btn->str->data + btn->amt_read, *len);
1026  btn->amt_read += *len;
1027  return SVN_NO_ERROR;
1028}
1029
1030svn_stream_t *
1031svn_stream_from_string(const svn_string_t *str,
1032                       apr_pool_t *pool)
1033{
1034  svn_stream_t *stream;
1035  struct string_stream_baton *baton;
1036
1037  if (! str)
1038    return svn_stream_empty(pool);
1039
1040  baton = apr_palloc(pool, sizeof(*baton));
1041  baton->str = str;
1042  baton->amt_read = 0;
1043  stream = svn_stream_create(baton, pool);
1044  svn_stream_set_read(stream, read_handler_string);
1045  return stream;
1046}
1047
1048
1049svn_error_t *
1050svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
1051{
1052  apr_file_t *stdout_file;
1053  apr_status_t apr_err;
1054
1055  apr_err = apr_file_open_stdout(&stdout_file, pool);
1056  if (apr_err)
1057    return svn_error_wrap_apr(apr_err, "Can't open stdout");
1058
1059  *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool);
1060
1061  return SVN_NO_ERROR;
1062}
1063
1064
1065svn_error_t *
1066svn_string_from_stream(svn_string_t **result,
1067                       svn_stream_t *stream,
1068                       apr_pool_t *result_pool,
1069                       apr_pool_t *scratch_pool)
1070{
1071  svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,
1072                                                      result_pool);
1073  char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
1074
1075  while (1)
1076    {
1077      apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1078
1079      SVN_ERR(svn_stream_read(stream, buffer, &len));
1080      svn_stringbuf_appendbytes(work, buffer, len);
1081
1082      if (len < SVN__STREAM_CHUNK_SIZE)
1083        break;
1084    }
1085
1086  SVN_ERR(svn_stream_close(stream));
1087
1088  *result = apr_palloc(result_pool, sizeof(**result));
1089  (*result)->data = work->data;
1090  (*result)->len = work->len;
1091
1092  return SVN_NO_ERROR;
1093}
Note: See TracBrowser for help on using the repository browser.