--- /dev/null
+/*\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