OSDN Git Service

Success build TortoiseMerge.
[tortoisegit/TortoiseGitJp.git] / src / TortoiseMerge / libsvn_diff / stream.c
diff --git a/src/TortoiseMerge/libsvn_diff/stream.c b/src/TortoiseMerge/libsvn_diff/stream.c
new file mode 100644 (file)
index 0000000..3c1967e
--- /dev/null
@@ -0,0 +1,1093 @@
+/*\r
+ * stream.c:   svn_stream operations\r
+ *\r
+ * ====================================================================\r
+ * Copyright (c) 2000-2004, 2006 CollabNet.  All rights reserved.\r
+ *\r
+ * This software is licensed as described in the file COPYING, which\r
+ * you should have received as part of this distribution.  The terms\r
+ * are also available at http://subversion.tigris.org/license-1.html.\r
+ * If newer versions of this license are posted there, you may use a\r
+ * newer version instead, at your option.\r
+ *\r
+ * This software consists of voluntary contributions made by many\r
+ * individuals.  For exact contribution history, see the revision\r
+ * history and logs, available at http://subversion.tigris.org/.\r
+ * ====================================================================\r
+ */\r
+\r
+//#include "svn_private_config.h"\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+\r
+#include <apr.h>\r
+#include <apr_pools.h>\r
+#include <apr_strings.h>\r
+#include <apr_file_io.h>\r
+#include <apr_errno.h>\r
+#include <apr_md5.h>\r
+\r
+#include <zlib.h>\r
+\r
+#include "svn_pools.h"\r
+#include "svn_io.h"\r
+#include "svn_error.h"\r
+#include "svn_string.h"\r
+#include "svn_utf.h"\r
+#include "svn_checksum.h"\r
+#include "svn_path.h"\r
+\r
+\r
+struct svn_stream_t {\r
+  void *baton;\r
+  svn_read_fn_t read_fn;\r
+  svn_write_fn_t write_fn;\r
+  svn_close_fn_t close_fn;\r
+};\r
+\r
+\r
+\f\r
+/*** Generic streams. ***/\r
+\r
+svn_stream_t *\r
+svn_stream_create(void *baton, apr_pool_t *pool)\r
+{\r
+  svn_stream_t *stream;\r
+\r
+  stream = apr_palloc(pool, sizeof(*stream));\r
+  stream->baton = baton;\r
+  stream->read_fn = NULL;\r
+  stream->write_fn = NULL;\r
+  stream->close_fn = NULL;\r
+  return stream;\r
+}\r
+\r
+\r
+void\r
+svn_stream_set_baton(svn_stream_t *stream, void *baton)\r
+{\r
+  stream->baton = baton;\r
+}\r
+\r
+\r
+void\r
+svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn)\r
+{\r
+  stream->read_fn = read_fn;\r
+}\r
+\r
+\r
+void\r
+svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)\r
+{\r
+  stream->write_fn = write_fn;\r
+}\r
+\r
+\r
+void\r
+svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)\r
+{\r
+  stream->close_fn = close_fn;\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len)\r
+{\r
+  SVN_ERR_ASSERT(stream->read_fn != NULL);\r
+  return stream->read_fn(stream->baton, buffer, len);\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)\r
+{\r
+  SVN_ERR_ASSERT(stream->write_fn != NULL);\r
+  return stream->write_fn(stream->baton, data, len);\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_close(svn_stream_t *stream)\r
+{\r
+  if (stream->close_fn == NULL)\r
+    return SVN_NO_ERROR;\r
+  return stream->close_fn(stream->baton);\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_printf(svn_stream_t *stream,\r
+                  apr_pool_t *pool,\r
+                  const char *fmt,\r
+                  ...)\r
+{\r
+  const char *message;\r
+  va_list ap;\r
+  apr_size_t len;\r
+\r
+  va_start(ap, fmt);\r
+  message = apr_pvsprintf(pool, fmt, ap);\r
+  va_end(ap);\r
+\r
+  len = strlen(message);\r
+  return svn_stream_write(stream, message, &len);\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_printf_from_utf8(svn_stream_t *stream,\r
+                            const char *encoding,\r
+                            apr_pool_t *pool,\r
+                            const char *fmt,\r
+                            ...)\r
+{\r
+  const char *message, *translated;\r
+  va_list ap;\r
+  apr_size_t len;\r
+\r
+  va_start(ap, fmt);\r
+  message = apr_pvsprintf(pool, fmt, ap);\r
+  va_end(ap);\r
+\r
+  SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,\r
+                                        pool));\r
+\r
+  len = strlen(translated);\r
+\r
+  return svn_stream_write(stream, translated, &len);\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_readline(svn_stream_t *stream,\r
+                    svn_stringbuf_t **stringbuf,\r
+                    const char *eol,\r
+                    svn_boolean_t *eof,\r
+                    apr_pool_t *pool)\r
+{\r
+  apr_size_t numbytes;\r
+  const char *match;\r
+  char c;\r
+  /* Since we're reading one character at a time, let's at least\r
+     optimize for the 90% case.  90% of the time, we can avoid the\r
+     stringbuf ever having to realloc() itself if we start it out at\r
+     80 chars.  */\r
+  svn_stringbuf_t *str = svn_stringbuf_create_ensure(80, pool);\r
+\r
+  match = eol;\r
+  while (*match)\r
+    {\r
+      numbytes = 1;\r
+      SVN_ERR(svn_stream_read(stream, &c, &numbytes));\r
+      if (numbytes != 1)\r
+        {\r
+          /* a 'short' read means the stream has run out. */\r
+          *eof = TRUE;\r
+          *stringbuf = str;\r
+          return SVN_NO_ERROR;\r
+        }\r
+\r
+      if (c == *match)\r
+        match++;\r
+      else\r
+        match = eol;\r
+\r
+      svn_stringbuf_appendbytes(str, &c, 1);\r
+    }\r
+\r
+  *eof = FALSE;\r
+  svn_stringbuf_chop(str, match - eol);\r
+  *stringbuf = str;\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,\r
+                              svn_cancel_func_t cancel_func,\r
+                              void *cancel_baton,\r
+                              apr_pool_t *scratch_pool)\r
+{\r
+  char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);\r
+  svn_error_t *err;\r
+  svn_error_t *err2;\r
+\r
+  /* Read and write chunks until we get a short read, indicating the\r
+     end of the stream.  (We can't get a short write without an\r
+     associated error.) */\r
+  while (1)\r
+    {\r
+      apr_size_t len = SVN__STREAM_CHUNK_SIZE;\r
+\r
+      if (cancel_func)\r
+        SVN_ERR(cancel_func(cancel_baton));\r
+\r
+      SVN_ERR(svn_stream_read(from, buf, &len));\r
+      if (len > 0)\r
+        SVN_ERR(svn_stream_write(to, buf, &len));\r
+      if (len != SVN__STREAM_CHUNK_SIZE)\r
+        break;\r
+    }\r
+\r
+  err = svn_stream_close(from);\r
+  err2 = svn_stream_close(to);\r
+  if (err)\r
+    {\r
+      /* ### it would be nice to compose the two errors in some way */\r
+      svn_error_clear(err2);  /* note: might be NULL */\r
+      return err;\r
+    }\r
+  return err2;\r
+}\r
+\r
+svn_error_t *svn_stream_copy2(svn_stream_t *from, svn_stream_t *to,\r
+                              svn_cancel_func_t cancel_func,\r
+                              void *cancel_baton,\r
+                              apr_pool_t *scratch_pool)\r
+{\r
+  return svn_stream_copy3(svn_stream_disown(from, scratch_pool),\r
+                          svn_stream_disown(to, scratch_pool),\r
+                          cancel_func, cancel_baton, scratch_pool);\r
+}\r
+\r
+svn_error_t *svn_stream_copy(svn_stream_t *from, svn_stream_t *to,\r
+                             apr_pool_t *scratch_pool)\r
+{\r
+  return svn_stream_copy3(svn_stream_disown(from, scratch_pool),\r
+                          svn_stream_disown(to, scratch_pool),\r
+                          NULL, NULL, scratch_pool);\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_contents_same(svn_boolean_t *same,\r
+                         svn_stream_t *stream1,\r
+                         svn_stream_t *stream2,\r
+                         apr_pool_t *pool)\r
+{\r
+  char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);\r
+  char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);\r
+  apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;\r
+  apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;\r
+\r
+  *same = TRUE;  /* assume TRUE, until disproved below */\r
+  while (bytes_read1 == SVN__STREAM_CHUNK_SIZE\r
+         && bytes_read2 == SVN__STREAM_CHUNK_SIZE)\r
+    {\r
+      SVN_ERR(svn_stream_read(stream1, buf1, &bytes_read1));\r
+      SVN_ERR(svn_stream_read(stream2, buf2, &bytes_read2));\r
+\r
+      if ((bytes_read1 != bytes_read2)\r
+          || (memcmp(buf1, buf2, bytes_read1)))\r
+        {\r
+          *same = FALSE;\r
+          break;\r
+        }\r
+    }\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+\f\r
+/*** Generic readable empty stream ***/\r
+\r
+static svn_error_t *\r
+read_handler_empty(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  *len = 0;\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+static svn_error_t *\r
+write_handler_empty(void *baton, const char *data, apr_size_t *len)\r
+{\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_stream_t *\r
+svn_stream_empty(apr_pool_t *pool)\r
+{\r
+  svn_stream_t *stream;\r
+\r
+  stream = svn_stream_create(NULL, pool);\r
+  svn_stream_set_read(stream, read_handler_empty);\r
+  svn_stream_set_write(stream, write_handler_empty);\r
+  return stream;\r
+}\r
+\r
+\r
+\r
+\f\r
+/*** Ownership detaching stream ***/\r
+\r
+static svn_error_t *\r
+read_handler_disown(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  return svn_stream_read((svn_stream_t *)baton, buffer, len);\r
+}\r
+\r
+static svn_error_t *\r
+write_handler_disown(void *baton, const char *buffer, apr_size_t *len)\r
+{\r
+  return svn_stream_write((svn_stream_t *)baton, buffer, len);\r
+}\r
+\r
+\r
+svn_stream_t *\r
+svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)\r
+{\r
+  svn_stream_t *s = svn_stream_create(stream, pool);\r
+\r
+  svn_stream_set_read(s, read_handler_disown);\r
+  svn_stream_set_write(s, write_handler_disown);\r
+\r
+  return s;\r
+}\r
+\r
+\r
+\f\r
+/*** Generic stream for APR files ***/\r
+struct baton_apr {\r
+  apr_file_t *file;\r
+  apr_pool_t *pool;\r
+};\r
+\r
+\r
+static svn_error_t *\r
+read_handler_apr(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  struct baton_apr *btn = baton;\r
+  svn_error_t *err;\r
+\r
+  err = svn_io_file_read_full(btn->file, buffer, *len, len, btn->pool);\r
+  if (err && APR_STATUS_IS_EOF(err->apr_err))\r
+    {\r
+      svn_error_clear(err);\r
+      err = SVN_NO_ERROR;\r
+    }\r
+\r
+  return err;\r
+}\r
+\r
+\r
+static svn_error_t *\r
+write_handler_apr(void *baton, const char *data, apr_size_t *len)\r
+{\r
+  struct baton_apr *btn = baton;\r
+\r
+  return svn_io_file_write_full(btn->file, data, *len, len, btn->pool);\r
+}\r
+\r
+static svn_error_t *\r
+close_handler_apr(void *baton)\r
+{\r
+  struct baton_apr *btn = baton;\r
+\r
+  return svn_io_file_close(btn->file, btn->pool);\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_open_readonly(svn_stream_t **stream,\r
+                         const char *path,\r
+                         apr_pool_t *result_pool,\r
+                         apr_pool_t *scratch_pool)\r
+{\r
+  apr_file_t *file;\r
+\r
+  SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED | APR_BINARY,\r
+                           APR_OS_DEFAULT, result_pool));\r
+  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_open_writable(svn_stream_t **stream,\r
+                         const char *path,\r
+                         apr_pool_t *result_pool,\r
+                         apr_pool_t *scratch_pool)\r
+{\r
+  apr_file_t *file;\r
+\r
+  SVN_ERR(svn_io_file_open(&file, path,\r
+                           APR_WRITE\r
+                             | APR_BUFFERED\r
+                             | APR_BINARY\r
+                             | APR_CREATE\r
+                             | APR_EXCL,\r
+                           APR_OS_DEFAULT, result_pool));\r
+  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_open_unique(svn_stream_t **stream,\r
+                       const char **temp_path,\r
+                       const char *dirpath,\r
+                       svn_io_file_del_t delete_when,\r
+                       apr_pool_t *result_pool,\r
+                       apr_pool_t *scratch_pool)\r
+{\r
+  apr_file_t *file;\r
+\r
+  SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath,\r
+                                   delete_when, result_pool, scratch_pool));\r
+  *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_stream_t *\r
+svn_stream_from_aprfile2(apr_file_t *file,\r
+                         svn_boolean_t disown,\r
+                         apr_pool_t *pool)\r
+{\r
+  struct baton_apr *baton;\r
+  svn_stream_t *stream;\r
+\r
+  if (file == NULL)\r
+    return svn_stream_empty(pool);\r
+\r
+  baton = apr_palloc(pool, sizeof(*baton));\r
+  baton->file = file;\r
+  baton->pool = pool;\r
+  stream = svn_stream_create(baton, pool);\r
+  svn_stream_set_read(stream, read_handler_apr);\r
+  svn_stream_set_write(stream, write_handler_apr);\r
+\r
+  if (! disown)\r
+    svn_stream_set_close(stream, close_handler_apr);\r
+\r
+  return stream;\r
+}\r
+\r
+svn_stream_t *\r
+svn_stream_from_aprfile(apr_file_t *file, apr_pool_t *pool)\r
+{\r
+  return svn_stream_from_aprfile2(file, TRUE, pool);\r
+}\r
+\r
+\r
+\f\r
+/* Compressed stream support */\r
+\r
+#define ZBUFFER_SIZE 4096       /* The size of the buffer the\r
+                                   compressed stream uses to read from\r
+                                   the substream. Basically an\r
+                                   arbitrary value, picked to be about\r
+                                   page-sized. */\r
+\r
+struct zbaton {\r
+  z_stream *in;                 /* compressed stream for reading */\r
+  z_stream *out;                /* compressed stream for writing */\r
+  svn_read_fn_t read;           /* substream's read function */\r
+  svn_write_fn_t write;         /* substream's write function */\r
+  svn_close_fn_t close;         /* substream's close function */\r
+  void *read_buffer;            /* buffer   used   for  reading   from\r
+                                   substream */\r
+  int read_flush;               /* what flush mode to use while\r
+                                   reading */\r
+  apr_pool_t *pool;             /* The pool this baton is allocated\r
+                                   on */\r
+  void *subbaton;               /* The substream's baton */\r
+};\r
+\r
+/* zlib alloc function. opaque is the pool we need. */\r
+static voidpf\r
+zalloc(voidpf opaque, uInt items, uInt size)\r
+{\r
+  apr_pool_t *pool = opaque;\r
+\r
+  return apr_palloc(pool, items * size);\r
+}\r
+\r
+/* zlib free function */\r
+static void\r
+zfree(voidpf opaque, voidpf address)\r
+{\r
+  /* Empty, since we allocate on the pool */\r
+}\r
+\r
+/* Converts a zlib error to an svn_error_t. zerr is the error code,\r
+   function is the function name, and stream is the z_stream we are\r
+   using.  */\r
+static svn_error_t *\r
+zerr_to_svn_error(int zerr, const char *function, z_stream *stream)\r
+{\r
+  apr_status_t status;\r
+  const char *message;\r
+\r
+  if (zerr == Z_OK)\r
+    return SVN_NO_ERROR;\r
+\r
+  switch (zerr)\r
+    {\r
+    case Z_STREAM_ERROR:\r
+      status = SVN_ERR_STREAM_MALFORMED_DATA;\r
+      message = "stream error";\r
+      break;\r
+\r
+    case Z_MEM_ERROR:\r
+      status = APR_ENOMEM;\r
+      message = "out of memory";\r
+      break;\r
+\r
+    case Z_BUF_ERROR:\r
+      status = APR_ENOMEM;\r
+      message = "buffer error";\r
+      break;\r
+\r
+    case Z_VERSION_ERROR:\r
+      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;\r
+      message = "version error";\r
+      break;\r
+\r
+    case Z_DATA_ERROR:\r
+      status = SVN_ERR_STREAM_MALFORMED_DATA;\r
+      message = "corrupted data";\r
+      break;\r
+\r
+    default:\r
+      status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;\r
+      message = "error";\r
+      break;\r
+    }\r
+\r
+  if (stream->msg != NULL)\r
+    return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,\r
+                             message, stream->msg);\r
+  else\r
+    return svn_error_createf(status, NULL, "zlib (%s): %s", function,\r
+                             message);\r
+}\r
+\r
+/* Helper function to figure out the sync mode */\r
+static svn_error_t *\r
+read_helper_gz(svn_read_fn_t read_fn,\r
+               void *baton,\r
+               char *buffer,\r
+               uInt *len, int *zflush)\r
+{\r
+  uInt orig_len = *len;\r
+\r
+  /* There's no reason this value should grow bigger than the range of\r
+     uInt, but Subversion's API requires apr_size_t. */\r
+  apr_size_t apr_len = (apr_size_t) *len;\r
+\r
+  SVN_ERR((*read_fn)(baton, buffer, &apr_len));\r
+\r
+  /* Type cast back to uInt type that zlib uses.  On LP64 platforms\r
+     apr_size_t will be bigger than uInt. */\r
+  *len = (uInt) apr_len;\r
+\r
+  /* I wanted to use Z_FINISH here, but we need to know our buffer is\r
+     big enough */\r
+  *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+/* Handle reading from a compressed stream */\r
+static svn_error_t *\r
+read_handler_gz(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  struct zbaton *btn = baton;\r
+  int zerr;\r
+\r
+  if (btn->in == NULL)\r
+    {\r
+      btn->in = apr_palloc(btn->pool, sizeof(z_stream));\r
+      btn->in->zalloc = zalloc;\r
+      btn->in->zfree = zfree;\r
+      btn->in->opaque = btn->pool;\r
+      btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);\r
+      btn->in->next_in = btn->read_buffer;\r
+      btn->in->avail_in = ZBUFFER_SIZE;\r
+\r
+      SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,\r
+                             &btn->in->avail_in, &btn->read_flush));\r
+\r
+      zerr = inflateInit(btn->in);\r
+      SVN_ERR(zerr_to_svn_error(zerr, "inflateInit", btn->in));\r
+    }\r
+\r
+  btn->in->next_out = (Bytef *) buffer;\r
+  btn->in->avail_out = *len;\r
+\r
+  while (btn->in->avail_out > 0)\r
+    {\r
+      if (btn->in->avail_in <= 0)\r
+        {\r
+          btn->in->avail_in = ZBUFFER_SIZE;\r
+          btn->in->next_in = btn->read_buffer;\r
+          SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer,\r
+                                 &btn->in->avail_in, &btn->read_flush));\r
+        }\r
+\r
+      zerr = inflate(btn->in, btn->read_flush);\r
+      if (zerr == Z_STREAM_END)\r
+        break;\r
+      else if (zerr != Z_OK)\r
+        return zerr_to_svn_error(zerr, "inflate", btn->in);\r
+    }\r
+\r
+  *len -= btn->in->avail_out;\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+/* Compress data and write it to the substream */\r
+static svn_error_t *\r
+write_handler_gz(void *baton, const char *buffer, apr_size_t *len)\r
+{\r
+  struct zbaton *btn = baton;\r
+  apr_pool_t *subpool;\r
+  void *write_buf;\r
+  apr_size_t buf_size, write_len;\r
+  int zerr;\r
+\r
+  if (btn->out == NULL)\r
+    {\r
+      btn->out = apr_palloc(btn->pool, sizeof(z_stream));\r
+      btn->out->zalloc = zalloc;\r
+      btn->out->zfree = zfree;\r
+      btn->out->opaque =  btn->pool;\r
+\r
+      zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);\r
+      SVN_ERR(zerr_to_svn_error(zerr, "deflateInit", btn->out));\r
+    }\r
+\r
+  /* The largest buffer we should need is 0.1% larger than the\r
+     compressed data, + 12 bytes. This info comes from zlib.h.  */\r
+  buf_size = *len + (*len / 1000) + 13;\r
+  subpool = svn_pool_create(btn->pool);\r
+  write_buf = apr_palloc(subpool, buf_size);\r
+\r
+  btn->out->next_in = (Bytef *) buffer;  /* Casting away const! */\r
+  btn->out->avail_in = *len;\r
+\r
+  while (btn->out->avail_in > 0)\r
+    {\r
+      btn->out->next_out = write_buf;\r
+      btn->out->avail_out = buf_size;\r
+\r
+      zerr = deflate(btn->out, Z_NO_FLUSH);\r
+      SVN_ERR(zerr_to_svn_error(zerr, "deflate", btn->out));\r
+      write_len = buf_size - btn->out->avail_out;\r
+      if (write_len > 0)\r
+        SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len));\r
+    }\r
+\r
+  svn_pool_destroy(subpool);\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+/* Handle flushing and closing the stream */\r
+static svn_error_t *\r
+close_handler_gz(void *baton)\r
+{\r
+  struct zbaton *btn = baton;\r
+  int zerr;\r
+\r
+  if (btn->in != NULL)\r
+    {\r
+      zerr = inflateEnd(btn->in);\r
+      SVN_ERR(zerr_to_svn_error(zerr, "inflateEnd", btn->in));\r
+    }\r
+\r
+  if (btn->out != NULL)\r
+    {\r
+      void *buf;\r
+      apr_size_t write_len;\r
+\r
+      buf = apr_palloc(btn->pool, ZBUFFER_SIZE);\r
+\r
+      while (TRUE)\r
+        {\r
+          btn->out->next_out = buf;\r
+          btn->out->avail_out = ZBUFFER_SIZE;\r
+\r
+          zerr = deflate(btn->out, Z_FINISH);\r
+          if (zerr != Z_STREAM_END && zerr != Z_OK)\r
+            return zerr_to_svn_error(zerr, "deflate", btn->out);\r
+          write_len = ZBUFFER_SIZE - btn->out->avail_out;\r
+          if (write_len > 0)\r
+            SVN_ERR(btn->write(btn->subbaton, buf, &write_len));\r
+          if (zerr == Z_STREAM_END)\r
+            break;\r
+        }\r
+\r
+      zerr = deflateEnd(btn->out);\r
+      SVN_ERR(zerr_to_svn_error(zerr, "deflateEnd", btn->out));\r
+    }\r
+\r
+  if (btn->close != NULL)\r
+    return btn->close(btn->subbaton);\r
+  else\r
+    return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_stream_t *\r
+svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)\r
+{\r
+  struct svn_stream_t *zstream;\r
+  struct zbaton *baton;\r
+\r
+  assert(stream != NULL);\r
+\r
+  baton = apr_palloc(pool, sizeof(*baton));\r
+  baton->in = baton->out = NULL;\r
+  baton->read = stream->read_fn;\r
+  baton->write = stream->write_fn;\r
+  baton->close = stream->close_fn;\r
+  baton->subbaton = stream->baton;\r
+  baton->pool = pool;\r
+  baton->read_buffer = NULL;\r
+  baton->read_flush = Z_SYNC_FLUSH;\r
+\r
+  zstream = svn_stream_create(baton, pool);\r
+  svn_stream_set_read(zstream, read_handler_gz);\r
+  svn_stream_set_write(zstream, write_handler_gz);\r
+  svn_stream_set_close(zstream, close_handler_gz);\r
+\r
+  return zstream;\r
+}\r
+\r
+\f\r
+/* Checksummed stream support */\r
+\r
+struct checksum_stream_baton\r
+{\r
+  svn_checksum_ctx_t *read_ctx, *write_ctx;\r
+  svn_checksum_t **read_checksum;  /* Output value. */\r
+  svn_checksum_t **write_checksum;  /* Output value. */\r
+  svn_stream_t *proxy;\r
+\r
+  /* True if more data should be read when closing the stream. */\r
+  svn_boolean_t read_more;\r
+\r
+  /* Pool to allocate read buffer and output values from. */\r
+  apr_pool_t *pool;\r
+};\r
+\r
+static svn_error_t *\r
+read_handler_checksum(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  struct checksum_stream_baton *btn = baton;\r
+  apr_size_t saved_len = *len;\r
+\r
+  SVN_ERR(svn_stream_read(btn->proxy, buffer, len));\r
+\r
+  if (btn->read_checksum)\r
+    SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));\r
+\r
+  if (saved_len != *len)\r
+    btn->read_more = FALSE;\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+static svn_error_t *\r
+write_handler_checksum(void *baton, const char *buffer, apr_size_t *len)\r
+{\r
+  struct checksum_stream_baton *btn = baton;\r
+\r
+  if (btn->write_checksum && *len > 0)\r
+    SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len));\r
+\r
+  return svn_stream_write(btn->proxy, buffer, len);\r
+}\r
+\r
+\r
+static svn_error_t *\r
+close_handler_checksum(void *baton)\r
+{\r
+  struct checksum_stream_baton *btn = baton;\r
+\r
+  /* If we're supposed to drain the stream, do so before finalizing the\r
+     checksum. */\r
+  if (btn->read_more)\r
+    {\r
+      char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);\r
+      apr_size_t len = SVN__STREAM_CHUNK_SIZE;\r
+\r
+      do\r
+        {\r
+          SVN_ERR(read_handler_checksum(baton, buf, &len));\r
+        }\r
+      while (btn->read_more);\r
+    }\r
+\r
+  if (btn->read_ctx)\r
+    SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool));\r
+\r
+  if (btn->write_ctx)\r
+    SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool));\r
+\r
+  return svn_stream_close(btn->proxy);\r
+}\r
+\r
+\r
+svn_stream_t *\r
+svn_stream_checksummed2(svn_stream_t *stream,\r
+                        svn_checksum_t **read_checksum,\r
+                        svn_checksum_t **write_checksum,\r
+                        svn_checksum_kind_t checksum_kind,\r
+                        svn_boolean_t read_all,\r
+                        apr_pool_t *pool)\r
+{\r
+  svn_stream_t *s;\r
+  struct checksum_stream_baton *baton;\r
+\r
+  if (read_checksum == NULL && write_checksum == NULL)\r
+    return stream;\r
+\r
+  baton = apr_palloc(pool, sizeof(*baton));\r
+  if (read_checksum)\r
+    baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool);\r
+  else\r
+    baton->read_ctx = NULL;\r
+\r
+  if (write_checksum)\r
+    baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool);\r
+  else\r
+    baton->write_ctx = NULL;\r
+\r
+  baton->read_checksum = read_checksum;\r
+  baton->write_checksum = write_checksum;\r
+  baton->proxy = stream;\r
+  baton->read_more = read_all;\r
+  baton->pool = pool;\r
+\r
+  s = svn_stream_create(baton, pool);\r
+  svn_stream_set_read(s, read_handler_checksum);\r
+  svn_stream_set_write(s, write_handler_checksum);\r
+  svn_stream_set_close(s, close_handler_checksum);\r
+  return s;\r
+}\r
+\r
+struct md5_stream_baton\r
+{\r
+  const unsigned char **read_digest;\r
+  const unsigned char **write_digest;\r
+  svn_checksum_t *read_checksum;\r
+  svn_checksum_t *write_checksum;\r
+  svn_stream_t *proxy;\r
+  apr_pool_t *pool;\r
+};\r
+\r
+static svn_error_t *\r
+read_handler_md5(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  struct md5_stream_baton *btn = baton;\r
+  return svn_stream_read(btn->proxy, buffer, len);\r
+}\r
+\r
+static svn_error_t *\r
+write_handler_md5(void *baton, const char *buffer, apr_size_t *len)\r
+{\r
+  struct md5_stream_baton *btn = baton;\r
+  return svn_stream_write(btn->proxy, buffer, len);\r
+}\r
+\r
+static svn_error_t *\r
+close_handler_md5(void *baton)\r
+{\r
+  struct md5_stream_baton *btn = baton;\r
+\r
+  SVN_ERR(svn_stream_close(btn->proxy));\r
+\r
+  if (btn->read_digest)\r
+    *btn->read_digest\r
+      = apr_pmemdup(btn->pool, btn->read_checksum->digest,\r
+                    APR_MD5_DIGESTSIZE);\r
+\r
+  if (btn->write_digest)\r
+    *btn->write_digest\r
+      = apr_pmemdup(btn->pool, btn->write_checksum->digest,\r
+                    APR_MD5_DIGESTSIZE);\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_stream_t *\r
+svn_stream_checksummed(svn_stream_t *stream,\r
+                       const unsigned char **read_digest,\r
+                       const unsigned char **write_digest,\r
+                       svn_boolean_t read_all,\r
+                       apr_pool_t *pool)\r
+{\r
+  svn_stream_t *s;\r
+  struct md5_stream_baton *baton;\r
+\r
+  if (! read_digest && ! write_digest)\r
+    return stream;\r
+\r
+  baton = apr_palloc(pool, sizeof(*baton));\r
+  baton->read_digest = read_digest;\r
+  baton->write_digest = write_digest;\r
+  baton->pool = pool;\r
+\r
+  /* Set BATON->proxy to a stream that will fill in BATON->read_checksum\r
+   * and BATON->write_checksum (if we want them) when it is closed. */\r
+  baton->proxy\r
+    = svn_stream_checksummed2(stream,\r
+                              read_digest ? &baton->read_checksum : NULL,\r
+                              write_digest ? &baton->write_checksum : NULL,\r
+                              svn_checksum_md5,\r
+                              read_all, pool);\r
+\r
+  /* Create a stream that will forward its read/write/close operations to\r
+   * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we\r
+   * want them) after it closes BATON->proxy. */\r
+  s = svn_stream_create(baton, pool);\r
+  svn_stream_set_read(s, read_handler_md5);\r
+  svn_stream_set_write(s, write_handler_md5);\r
+  svn_stream_set_close(s, close_handler_md5);\r
+  return s;\r
+}\r
+\r
+\r
+\r
+\f\r
+/* Miscellaneous stream functions. */\r
+struct stringbuf_stream_baton\r
+{\r
+  svn_stringbuf_t *str;\r
+  apr_size_t amt_read;\r
+};\r
+\r
+static svn_error_t *\r
+read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  struct stringbuf_stream_baton *btn = baton;\r
+  apr_size_t left_to_read = btn->str->len - btn->amt_read;\r
+\r
+  *len = (*len > left_to_read) ? left_to_read : *len;\r
+  memcpy(buffer, btn->str->data + btn->amt_read, *len);\r
+  btn->amt_read += *len;\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+static svn_error_t *\r
+write_handler_stringbuf(void *baton, const char *data, apr_size_t *len)\r
+{\r
+  struct stringbuf_stream_baton *btn = baton;\r
+\r
+  svn_stringbuf_appendbytes(btn->str, data, *len);\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+svn_stream_t *\r
+svn_stream_from_stringbuf(svn_stringbuf_t *str,\r
+                          apr_pool_t *pool)\r
+{\r
+  svn_stream_t *stream;\r
+  struct stringbuf_stream_baton *baton;\r
+\r
+  if (! str)\r
+    return svn_stream_empty(pool);\r
+\r
+  baton = apr_palloc(pool, sizeof(*baton));\r
+  baton->str = str;\r
+  baton->amt_read = 0;\r
+  stream = svn_stream_create(baton, pool);\r
+  svn_stream_set_read(stream, read_handler_stringbuf);\r
+  svn_stream_set_write(stream, write_handler_stringbuf);\r
+  return stream;\r
+}\r
+\r
+struct string_stream_baton\r
+{\r
+  const svn_string_t *str;\r
+  apr_size_t amt_read;\r
+};\r
+\r
+static svn_error_t *\r
+read_handler_string(void *baton, char *buffer, apr_size_t *len)\r
+{\r
+  struct string_stream_baton *btn = baton;\r
+  apr_size_t left_to_read = btn->str->len - btn->amt_read;\r
+\r
+  *len = (*len > left_to_read) ? left_to_read : *len;\r
+  memcpy(buffer, btn->str->data + btn->amt_read, *len);\r
+  btn->amt_read += *len;\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+svn_stream_t *\r
+svn_stream_from_string(const svn_string_t *str,\r
+                       apr_pool_t *pool)\r
+{\r
+  svn_stream_t *stream;\r
+  struct string_stream_baton *baton;\r
+\r
+  if (! str)\r
+    return svn_stream_empty(pool);\r
+\r
+  baton = apr_palloc(pool, sizeof(*baton));\r
+  baton->str = str;\r
+  baton->amt_read = 0;\r
+  stream = svn_stream_create(baton, pool);\r
+  svn_stream_set_read(stream, read_handler_string);\r
+  return stream;\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)\r
+{\r
+  apr_file_t *stdout_file;\r
+  apr_status_t apr_err;\r
+\r
+  apr_err = apr_file_open_stdout(&stdout_file, pool);\r
+  if (apr_err)\r
+    return svn_error_wrap_apr(apr_err, "Can't open stdout");\r
+\r
+  *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool);\r
+\r
+  return SVN_NO_ERROR;\r
+}\r
+\r
+\r
+svn_error_t *\r
+svn_string_from_stream(svn_string_t **result,\r
+                       svn_stream_t *stream,\r
+                       apr_pool_t *result_pool,\r
+                       apr_pool_t *scratch_pool)\r
+{\r
+  svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,\r
+                                                      result_pool);\r
+  char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);\r
+\r
+  while (1)\r
+    {\r
+      apr_size_t len = SVN__STREAM_CHUNK_SIZE;\r
+\r
+      SVN_ERR(svn_stream_read(stream, buffer, &len));\r
+      svn_stringbuf_appendbytes(work, buffer, len);\r
+\r
+      if (len < SVN__STREAM_CHUNK_SIZE)\r
+        break;\r
+    }\r
+\r
+  SVN_ERR(svn_stream_close(stream));\r
+\r
+  *result = apr_palloc(result_pool, sizeof(**result));\r
+  (*result)->data = work->data;\r
+  (*result)->len = work->len;\r
+\r
+  return SVN_NO_ERROR;\r
+}\r