Author: artagnon
Date: Sun Jul 25 08:44:21 2010
New Revision: 979002

URL: http://svn.apache.org/viewvc?rev=979002&view=rev
Log:
Add svnrload: currently a heavy WIP, with most of the code imported
from svnrdump

* subversion/svnrload
* subversion/svnrload/load_editor.c: From svnrdump/dump_editor.c
* subversion/svnrload/svnrload.c: From svnrdump/svnrdump.c
* subversion/svnrload/load_editor.h: From svnrdump/dump_editor.h
* subversion/svnrload/parse_dumpstream.c: Functionality to parse a
  dumpstream

Added:
    subversion/branches/svnrload/subversion/svnrload/
    subversion/branches/svnrload/subversion/svnrload/load_editor.c
    subversion/branches/svnrload/subversion/svnrload/load_editor.h
    subversion/branches/svnrload/subversion/svnrload/parse_dumpstream.c
    subversion/branches/svnrload/subversion/svnrload/svnrload.c

Added: subversion/branches/svnrload/subversion/svnrload/load_editor.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svnrload/subversion/svnrload/load_editor.c?rev=979002&view=auto
==============================================================================
--- subversion/branches/svnrload/subversion/svnrload/load_editor.c (added)
+++ subversion/branches/svnrload/subversion/svnrload/load_editor.c Sun Jul 25 
08:44:21 2010
@@ -0,0 +1,780 @@
+/*
+ *  dump_editor.c: The svn_delta_editor_t editor used by svnrdump
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include "svn_hash.h"
+#include "svn_pools.h"
+#include "svn_repos.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "svn_dirent_uri.h"
+
+#include "dump_editor.h"
+
+#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
+
+/* The baton used by the dump editor. */
+struct load_edit_baton {
+  /* The output stream we write the dumpfile to */
+  svn_stream_t *stream;
+
+  /* Pool for per-edit-session allocations */
+  apr_pool_t *pool;
+
+  /* Properties which were modified during change_file_prop
+   * or change_dir_prop. */
+  apr_hash_t *props;
+
+  /* Properties which were deleted during change_file_prop
+   * or change_dir_prop. */
+  apr_hash_t *deleted_props;
+
+  /* Temporary buffer to write property hashes to in human-readable
+   * form. ### Is this really needed? */
+  svn_stringbuf_t *propstring;
+   
+  /* Temporary file to write delta to along with its checksum. */
+  char *delta_abspath;
+
+  /* The checksum of the file the delta is being applied to */
+  const char *base_checksum;
+
+  /* Flags to trigger dumping props and text */
+  svn_boolean_t load_props;
+  svn_boolean_t load_text;
+  svn_boolean_t load_props_pending;
+};
+
+/* Make a directory baton to represent the directory at path (relative
+ * to the edit_baton).
+ *
+ * COPYFROM_PATH/COPYFROM_REV are the path/revision against which this
+ * directory should be compared for changes. If the copyfrom
+ * information is valid, the directory will be compared against its
+ * copy source.
+ *
+ * PARENT_DIR_BATON is the directory baton of this directory's parent,
+ * or NULL if this is the top-level directory of the edit.  ADDED
+ * indicates if this directory is newly added in this revision.
+ * Perform all allocations in POOL.  */
+static struct dir_baton *
+make_dir_baton(const char *path,
+               const char *copyfrom_path,
+               svn_revnum_t copyfrom_rev,
+               void *edit_baton,
+               void *parent_dir_baton,
+               svn_boolean_t added,
+               apr_pool_t *pool)
+{
+  struct load_edit_baton *eb = edit_baton;
+  struct dir_baton *pb = parent_dir_baton;
+  struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
+  const char *abspath;
+
+  /* Disallow a path relative to nothing. */
+  SVN_ERR_ASSERT_NO_RETURN(!path || pb);
+
+  /* Construct the full path of this node. */
+  if (pb)
+    abspath = svn_uri_join("/", path, pool);
+  else
+    abspath = "/";
+
+  /* Remove leading slashes from copyfrom paths. */
+  if (copyfrom_path && strcmp(copyfrom_path, "/"))
+    copyfrom_path = ((*copyfrom_path == '/') ?
+                     copyfrom_path + 1 : copyfrom_path);
+
+  new_db->eb = eb;
+  new_db->parent_dir_baton = pb;
+  new_db->abspath = abspath;
+  new_db->copyfrom_path = copyfrom_path ?
+    apr_pstrdup(pool, copyfrom_path) : NULL;
+  new_db->copyfrom_rev = copyfrom_rev;
+  new_db->added = added;
+  new_db->written_out = FALSE;
+  new_db->deleted_entries = apr_hash_make(pool);
+
+  return new_db;
+}
+
+/* Extract and dump properties stored in edit baton EB, using POOL for
+ * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
+ * Unless LOAD_DATA_TOO is set, only property headers are dumped.
+ */
+static svn_error_t *
+load_props(struct load_edit_baton *eb,
+           svn_boolean_t *trigger_var,
+           svn_boolean_t load_data_too,
+           apr_pool_t *pool)
+{
+  svn_stream_t *propstream;
+
+  if (trigger_var && !*trigger_var)
+    return SVN_NO_ERROR;
+
+  svn_stringbuf_setempty(eb->propstring);
+  propstream = svn_stream_from_stringbuf(eb->propstring, eb->pool);
+  SVN_ERR(svn_hash_write_incremental(eb->props, eb->deleted_props,
+                                     propstream, "PROPS-END", pool));
+  SVN_ERR(svn_stream_close(propstream));
+  
+  /* Prop-delta: true */
+  SVN_ERR(svn_stream_printf(eb->stream, pool,
+                            SVN_REPOS_DUMPFILE_PROP_DELTA
+                            ": true\n"));
+
+  /* Prop-content-length: 193 */
+  SVN_ERR(svn_stream_printf(eb->stream, pool,
+                            SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n", eb->propstring->len));
+
+  if (load_data_too)
+    {
+      /* Content-length: 14 */
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                                ": %" APR_SIZE_T_FMT "\n\n",
+                                eb->propstring->len));
+
+      /* The properties. */
+      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
+                               &(eb->propstring->len)));
+
+      /* No text is going to be dumped. Write a couple of newlines and
+         wait for the next node/ revision. */
+      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+
+      /* Cleanup so that data is never dumped twice. */
+      svn_hash__clear(eb->props, pool);
+      svn_hash__clear(eb->deleted_props, pool);
+      if (trigger_var)
+        *trigger_var = FALSE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/*
+ * Write out a node record for PATH of type KIND under EB->FS_ROOT.
+ * ACTION describes what is happening to the node (see enum
+ * svn_node_action). Write record to writable EB->STREAM, using
+ * EB->BUFFER to write in chunks.
+ *
+ * If the node was itself copied, IS_COPY is TRUE and the
+ * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV.
+ * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this
+ * node is part of a copied subtree.
+ */
+static svn_error_t *
+load_node(struct load_edit_baton *eb,
+          const char *path,    /* an absolute path. */
+          svn_node_kind_t kind,
+          enum svn_node_action action,
+          svn_boolean_t is_copy,
+          const char *copyfrom_path,
+          svn_revnum_t copyfrom_rev,
+          apr_pool_t *pool)
+{
+  /* Remove leading slashes from path and copyfrom_path */
+  if (path && strcmp(path, "/"))
+    path = ((*path == '/') ? path + 1 : path);
+  
+  if (copyfrom_path && strcmp(copyfrom_path, "/"))
+    copyfrom_path = ((*copyfrom_path == '/') ?
+                     copyfrom_path + 1 : copyfrom_path);
+
+  /* Node-path: commons/STATUS */
+  SVN_ERR(svn_stream_printf(eb->stream, pool,
+                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
+
+  /* Node-kind: file */
+  if (kind == svn_node_file)
+    SVN_ERR(svn_stream_printf(eb->stream, pool,
+                              SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
+  else if (kind == svn_node_dir)
+    SVN_ERR(svn_stream_printf(eb->stream, pool,
+                              SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
+
+
+  /* Write the appropriate Node-action header */
+  switch (action)
+    {
+    case svn_node_action_change:
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_ACTION
+                                ": change\n"));
+      break;
+
+    case svn_node_action_replace:
+      if (!is_copy)
+        {
+          /* Node-action: replace */
+          SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                    SVN_REPOS_DUMPFILE_NODE_ACTION
+                                    ": replace\n"));
+
+          eb->load_props_pending = TRUE;
+          break;
+        }
+      /* More complex case: is_copy is true, and copyfrom_path/
+         copyfrom_rev are present: delete the original, and then re-add
+         it */
+
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_ACTION
+                                ": delete\n\n"));
+
+      /* Recurse: Print an additional add-with-history record. */
+      SVN_ERR(load_node(eb, path, kind, svn_node_action_add,
+                        is_copy, copyfrom_path, copyfrom_rev, pool));
+
+      /* We can leave this routine quietly now, don't need to dump any
+         content; that was already done in the second record. */
+      eb->load_props = FALSE;
+      break;
+
+    case svn_node_action_delete:
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_ACTION
+                                ": delete\n"));
+
+      /* We can leave this routine quietly now, don't need to dump
+         any content. */
+      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+      eb->load_props = FALSE;
+      break;
+
+    case svn_node_action_add:
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+
+      if (!is_copy)
+        {
+          /* eb->load_props_pending for files is handled in close_file
+             which is called immediately.  However, directories are not
+             closed until all the work inside them has been done;
+             eb->load_props_pending for directories is handled in all the
+             functions that can possibly be called after add_directory:
+             add_directory, open_directory, delete_entry, close_directory,
+             add_file, open_file. change_dir_prop is a special case. */
+
+          eb->load_props_pending = TRUE;
+          break;
+        }
+
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
+                                ": %ld\n"
+                                SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
+                                ": %s\n",
+                                copyfrom_rev, copyfrom_path));
+
+      /* Ugly hack: If a directory was copied from a previous revision,
+         nothing else can be done, and close_file won't be called to
+         write two blank lines. Write them here otherwise the `svnadmin
+         load` parser will fail. */
+      if (kind == svn_node_dir)
+        SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+
+      break;
+    }
+
+  /* Dump property headers */
+  SVN_ERR(load_props(eb, &(eb->load_props), FALSE, pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+open_root(void *edit_baton,
+          svn_revnum_t base_revision,
+          apr_pool_t *pool,
+          void **root_baton)
+{
+  struct load_edit_baton *eb = edit_baton;
+  /* Allocate a special pool for the edit_baton to avoid pool
+     lifetime issues */
+  eb->pool = svn_pool_create(pool);
+  eb->props = apr_hash_make(eb->pool);
+  eb->deleted_props = apr_hash_make(eb->pool);
+  eb->propstring = svn_stringbuf_create("", eb->pool);
+
+  *root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
+                               edit_baton, NULL, FALSE, pool);
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+delete_entry(const char *path,
+             svn_revnum_t revision,
+             void *parent_baton,
+             apr_pool_t *pool)
+{
+  struct dir_baton *pb = parent_baton;
+  const char *mypath = apr_pstrdup(pool, path);
+
+  /* Some pending properties to dump? */
+  SVN_ERR(load_props(pb->eb, &(pb->eb->load_props_pending), TRUE, pool));
+
+  /* Add this path to the deleted_entries of the parent directory
+     baton. */
+  apr_hash_set(pb->deleted_entries, mypath, APR_HASH_KEY_STRING, pb);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+add_directory(const char *path,
+              void *parent_baton,
+              const char *copyfrom_path,
+              svn_revnum_t copyfrom_rev,
+              apr_pool_t *pool,
+              void **child_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  void *val;
+  struct dir_baton *new_db
+    = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, TRUE, 
pool);
+  svn_boolean_t is_copy;
+
+  /* Some pending properties to dump? */
+  SVN_ERR(load_props(pb->eb, &(pb->eb->load_props_pending), TRUE, pool));
+
+  /* This might be a replacement -- is the path already deleted? */
+  val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+
+  /* Detect an add-with-history */
+  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
+
+  /* Dump the node */
+  SVN_ERR(load_node(pb->eb, path,
+                    svn_node_dir,
+                    val ? svn_node_action_replace : svn_node_action_add,
+                    is_copy,
+                    is_copy ? copyfrom_path : NULL,
+                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+                    pool));
+
+  if (val)
+    /* Delete the path, it's now been dumped */
+    apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
+
+  new_db->written_out = TRUE;
+
+  *child_baton = new_db;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+open_directory(const char *path,
+               void *parent_baton,
+               svn_revnum_t base_revision,
+               apr_pool_t *pool,
+               void **child_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  struct dir_baton *new_db;
+  const char *copyfrom_path = NULL;
+  svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
+
+  /* Some pending properties to dump? */
+  SVN_ERR(load_props(pb->eb, &(pb->eb->load_props_pending), TRUE, pool));
+
+  /* If the parent directory has explicit comparison path and rev,
+     record the same for this one. */
+  if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
+    {
+      copyfrom_path = svn_uri_join(pb->copyfrom_path,
+                                   svn_relpath_basename(path, pool),
+                                   pool);
+      copyfrom_rev = pb->copyfrom_rev;
+    }
+
+  new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
+                          FALSE, pool);
+  *child_baton = new_db;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_directory(void *dir_baton,
+                apr_pool_t *pool)
+{
+  struct dir_baton *db = dir_baton;
+  struct load_edit_baton *eb = db->eb;
+  apr_hash_index_t *hi;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+
+  /* Some pending properties to dump? */
+  SVN_ERR(load_props(eb, &(eb->load_props_pending), TRUE, pool));
+
+  /* Dump the directory entries */
+  for (hi = apr_hash_first(pool, db->deleted_entries); hi;
+       hi = apr_hash_next(hi))
+    {
+      const void *key;
+      const char *path;
+      apr_hash_this(hi, &key, NULL, NULL);
+      path = key;
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(load_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
+                        FALSE, NULL, SVN_INVALID_REVNUM, iterpool));
+    }
+
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+add_file(const char *path,
+         void *parent_baton,
+         const char *copyfrom_path,
+         svn_revnum_t copyfrom_rev,
+         apr_pool_t *pool,
+         void **file_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  void *val;
+  svn_boolean_t is_copy;
+
+  /* Some pending properties to dump? */
+  SVN_ERR(load_props(pb->eb, &(pb->eb->load_props_pending), TRUE, pool));
+
+  /* This might be a replacement -- is the path already deleted? */
+  val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+
+  /* Detect add-with-history. */
+  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
+
+  /* Dump the node. */
+  SVN_ERR(load_node(pb->eb, path,
+                    svn_node_file,
+                    val ? svn_node_action_replace : svn_node_action_add,
+                    is_copy,
+                    is_copy ? copyfrom_path : NULL,
+                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
+                    pool));
+
+  if (val)
+    /* delete the path, it's now been dumped. */
+    apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
+
+  /* Build a nice file baton to pass to change_file_prop and
+     apply_textdelta */
+  *file_baton = pb->eb;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+open_file(const char *path,
+          void *parent_baton,
+          svn_revnum_t ancestor_revision,
+          apr_pool_t *pool,
+          void **file_baton)
+{
+  struct dir_baton *pb = parent_baton;
+  const char *copyfrom_path = NULL;
+  svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
+  apr_array_header_t *compose_path;
+
+  /* Some pending properties to dump? */
+  SVN_ERR(load_props(pb->eb, &(pb->eb->load_props_pending), TRUE, pool));
+
+  compose_path = apr_array_make(pool, 2, sizeof(const char *));
+
+  /* If the parent directory has explicit copyfrom path and rev,
+     record the same for this one. */
+  if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
+    {
+      APR_ARRAY_PUSH(compose_path, const char *) = pb->copyfrom_path;
+      APR_ARRAY_PUSH(compose_path, const char *) =
+        svn_relpath_basename(path, pool);
+      copyfrom_path = svn_path_compose(compose_path, pool);
+      copyfrom_rev = pb->copyfrom_rev;
+    }
+
+  SVN_ERR(load_node(pb->eb, path, svn_node_file, svn_node_action_change,
+                    FALSE, copyfrom_path, copyfrom_rev, pool));
+
+  /* Build a nice file baton to pass to change_file_prop and
+     apply_textdelta */
+  *file_baton = pb->eb;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_dir_prop(void *parent_baton,
+                const char *name,
+                const svn_string_t *value,
+                apr_pool_t *pool)
+{
+  struct dir_baton *db = parent_baton;
+
+  if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
+    return SVN_NO_ERROR;
+
+  if (value)
+    apr_hash_set(db->eb->props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, pool));
+  else
+    apr_hash_set(db->eb->deleted_props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, "");
+
+  if (! db->written_out)
+    {
+      /* If db->written_out is set, it means that the node information
+         corresponding to this directory has already been written: don't
+         do anything; load_props_pending will take care of dumping the
+         props. If it not, dump the node itself before dumping the
+         props. */
+
+      SVN_ERR(load_node(db->eb, db->abspath, svn_node_dir,
+                        svn_node_action_change, FALSE, db->copyfrom_path,
+                        db->copyfrom_rev, pool));
+
+      SVN_ERR(load_props(db->eb, NULL, TRUE, pool));
+      db->written_out = TRUE;
+    }
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_file_prop(void *file_baton,
+                 const char *name,
+                 const svn_string_t *value,
+                 apr_pool_t *pool)
+{
+  struct load_edit_baton *eb = file_baton;
+
+  if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
+    return SVN_NO_ERROR;
+
+  if (value)
+    apr_hash_set(eb->props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, pool));
+  else
+    apr_hash_set(eb->deleted_props, apr_pstrdup(pool, name),
+                 APR_HASH_KEY_STRING, "");
+
+  /* Dump the property headers and wait; close_file might need
+     to write text headers too depending on whether
+     apply_textdelta is called */
+  eb->load_props_pending = TRUE;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+window_handler(svn_txdelta_window_t *window, void *baton)
+{
+  struct handler_baton *hb = baton;
+  struct load_edit_baton *eb = hb->eb;
+  static svn_error_t *err;
+
+  err = hb->apply_handler(window, hb->apply_baton);
+  if (window != NULL && !err)
+    return SVN_NO_ERROR;
+
+  if (err)
+    SVN_ERR(err);
+
+  /* Write information about the filepath to hb->eb */
+  eb->delta_abspath = apr_pstrdup(eb->pool, hb->delta_abspath);
+
+  /* Cleanup */
+  svn_pool_destroy(hb->pool);
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+apply_textdelta(void *file_baton, const char *base_checksum,
+                apr_pool_t *pool,
+                svn_txdelta_window_handler_t *handler,
+                void **handler_baton)
+{
+  struct load_edit_baton *eb = file_baton;
+
+  /* Custom handler_baton allocated in a separate pool */
+  apr_pool_t *handler_pool = svn_pool_create(pool);
+  struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
+  hb->pool = handler_pool;
+  hb->eb = eb;
+
+  /* Use a temporary file to measure the text-content-length */
+  SVN_ERR(svn_stream_open_unique(&(hb->delta_filestream), &hb->delta_abspath,
+                                 NULL, svn_io_file_del_none, hb->pool,
+                                 hb->pool));
+
+  /* Prepare to write the delta to the temporary file. */
+  svn_txdelta_to_svndiff2(&(hb->apply_handler), &(hb->apply_baton),
+                          hb->delta_filestream, 0, hb->pool);
+  eb->load_text = TRUE;
+  eb->base_checksum = apr_pstrdup(pool, base_checksum);
+
+  /* The actual writing takes place when this function has
+     finished. Set handler and handler_baton now so for
+     window_handler() */
+  *handler = window_handler;
+  *handler_baton = hb;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_file(void *file_baton,
+           const char *text_checksum,
+           apr_pool_t *pool)
+{
+  struct load_edit_baton *eb = file_baton;
+  apr_file_t *delta_file;
+  svn_stream_t *delta_filestream;
+  apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
+
+  /* Some pending properties to dump? */
+  SVN_ERR(load_props(eb, &(eb->load_props_pending), FALSE, pool));
+
+  /* The prop headers have already been dumped in load_node; now dump
+     the text headers. */
+  if (eb->load_text)
+    {
+      /* Text-delta: true */
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_TEXT_DELTA
+                                ": true\n"));
+
+      SVN_ERR(svn_io_stat(info, eb->delta_abspath, APR_FINFO_SIZE, pool));
+
+      if (eb->base_checksum)
+        /* Text-delta-base-md5: */
+        SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                  SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
+                                  ": %s\n",
+                                  eb->base_checksum));
+
+      /* Text-content-length: 39 */
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
+                                ": %lu\n",
+                                (unsigned long)info->size));
+
+      /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */      
+      SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
+                                ": %s\n",
+                                text_checksum));
+    }
+
+  /* Content-length: 1549 */
+  /* If both text and props are absent, skip this header */
+  if (eb->load_props || eb->load_props_pending)
+    SVN_ERR(svn_stream_printf(eb->stream, pool,
+                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                              ": %ld\n\n",
+                              (unsigned long)info->size + 
eb->propstring->len));
+  else if (eb->load_text)
+    SVN_ERR(svn_stream_printf(eb->stream, pool,
+                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                              ": %ld\n\n",
+                              (unsigned long)info->size));
+
+  /* Dump the props; the propstring should have already been
+     written in load_node or above */
+  if (eb->load_props || eb->load_props_pending)
+    {
+      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
+                               &(eb->propstring->len)));
+
+      /* Cleanup */
+      eb->load_props = eb->load_props_pending = FALSE;
+      svn_hash__clear(eb->props, pool);
+      svn_hash__clear(eb->deleted_props, pool);
+    }
+
+  /* Dump the text */
+  if (eb->load_text)
+    {
+      /* Open the temporary file, map it to a stream, copy
+         the stream to eb->stream, close and delete the
+         file */
+      SVN_ERR(svn_io_file_open(&delta_file, eb->delta_abspath, APR_READ,
+                               APR_OS_DEFAULT, pool));
+      delta_filestream = svn_stream_from_aprfile2(delta_file, TRUE, pool);
+      SVN_ERR(svn_stream_copy3(delta_filestream, eb->stream, NULL, NULL, 
pool));
+
+      /* Cleanup */
+      SVN_ERR(svn_io_file_close(delta_file, pool));
+      SVN_ERR(svn_stream_close(delta_filestream));
+      SVN_ERR(svn_io_remove_file2(eb->delta_abspath, TRUE, pool));
+      eb->load_text = FALSE;
+    }
+
+  SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_edit(void *edit_baton, apr_pool_t *pool)
+{
+  struct load_edit_baton *eb = edit_baton;
+  svn_pool_destroy(eb->pool);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+get_load_editor(const svn_delta_editor_t **editor,
+                void **edit_baton,
+                svn_stream_t *stream,
+                apr_pool_t *pool)
+{
+  struct load_edit_baton *eb;
+  svn_delta_editor_t *de;
+
+  eb = apr_pcalloc(pool, sizeof(struct load_edit_baton));
+  eb->stream = stream;
+
+  de = svn_delta_default_editor(pool);
+  de->open_root = open_root;
+  de->delete_entry = delete_entry;
+  de->add_directory = add_directory;
+  de->open_directory = open_directory;
+  de->close_directory = close_directory;
+  de->change_dir_prop = change_dir_prop;
+  de->change_file_prop = change_file_prop;
+  de->apply_textdelta = apply_textdelta;
+  de->add_file = add_file;
+  de->open_file = open_file;
+  de->close_file = close_file;
+  de->close_edit = close_edit;
+
+  /* Set the edit_baton and editor. */
+  *edit_baton = eb;
+  *editor = de;
+
+  return SVN_NO_ERROR;
+}

Added: subversion/branches/svnrload/subversion/svnrload/load_editor.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/svnrload/subversion/svnrload/load_editor.h?rev=979002&view=auto
==============================================================================
--- subversion/branches/svnrload/subversion/svnrload/load_editor.h (added)
+++ subversion/branches/svnrload/subversion/svnrload/load_editor.h Sun Jul 25 
08:44:21 2010
@@ -0,0 +1,89 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file load_editor.h
+ * @brief The svn_delta_editor_t editor used by svnrdump
+ */
+
+#ifndef LOAD_EDITOR_H_
+#define LOAD_EDITOR_H_
+
+/**
+ * A directory baton used by all directory-related callback functions
+ * in the dump editor.
+ */
+struct dir_baton
+{
+  struct load_edit_baton *eb;
+  struct dir_baton *parent_dir_baton;
+
+  /* is this directory a new addition to this revision? */
+  svn_boolean_t added;
+
+  /* has this directory been written to the output stream? */
+  svn_boolean_t written_out;
+
+  /* the absolute path to this directory */
+  const char *abspath;
+
+  /* Copyfrom info for the node, if any */
+  const char *copyfrom_path;
+  svn_revnum_t copyfrom_rev;
+
+  /* Hash of paths that need to be deleted, though some -might- be
+     replaced.  Maps const char * paths to this dir_baton. Note that
+     they're full paths, because that's what the editor driver gives
+     us, although they're all really within this directory. */
+  apr_hash_t *deleted_entries;
+};
+
+/**
+ * A handler baton to be used in window_handler().
+ */
+struct handler_baton
+{
+  svn_txdelta_window_handler_t apply_handler;
+  void *apply_baton;
+
+  /* Pool used for temporary allocations during delta application in
+     window_handler() */
+  apr_pool_t *pool;
+
+  /* Information about the path of the temporary file used */
+  const char *delta_abspath;
+  svn_stream_t *delta_filestream;
+
+  /* Global edit baton */
+  struct load_edit_baton *eb;
+};
+
+/**
+ * Get a dump editor @a editor along with a @a edit_baton allocated in
+ * @a pool. The editor will write output to @a stream.
+ */
+svn_error_t *
+get_load_editor(const svn_delta_editor_t **editor,
+                void **edit_baton,
+                svn_stream_t *stream,
+                apr_pool_t *pool);
+
+#endif

Added: subversion/branches/svnrload/subversion/svnrload/parse_dumpstream.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svnrload/subversion/svnrload/parse_dumpstream.c?rev=979002&view=auto
==============================================================================
--- subversion/branches/svnrload/subversion/svnrload/parse_dumpstream.c (added)
+++ subversion/branches/svnrload/subversion/svnrload/parse_dumpstream.c Sun Jul 
25 08:44:21 2010
@@ -0,0 +1,129 @@
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_repos.h"
+#include "svn_io.h"
+
+static svn_error_t *
+new_revision_record(void **revision_baton,
+                   apr_hash_t *headers,
+                   void *parse_baton,
+                   apr_pool_t *pool)
+{
+       printf("new_revision_record called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+new_node_record(void **node_baton,
+                apr_hash_t *headers,
+                void *revision_baton,
+                apr_pool_t *pool)
+{
+       printf("new_node_record called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+uuid_record(const char *uuid,
+            void *parse_baton,
+            apr_pool_t *pool)
+{
+       printf("uuid_record called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+set_revision_property(void *baton,
+                      const char *name,
+                      const svn_string_t *value)
+{
+       printf("set_revision_property called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+set_node_property(void *baton,
+                  const char *name,
+                  const svn_string_t *value)
+{
+       printf("set_node_property called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+remove_node_props(void *baton)
+{
+       printf("remove_node_props called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+set_fulltext(svn_stream_t **stream,
+             void *node_baton)
+{
+       printf("set_fulltext called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_node(void *baton)
+{
+       printf("close_node called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_revision(void *baton)
+{
+       printf("close_revision called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+delete_node_property(void *baton,
+                     const char *name)
+{
+       printf("delete_node_property called");
+       return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+apply_textdelta(svn_txdelta_window_handler_t *handler,
+                void **handler_baton,
+                void *node_baton)
+{
+       printf("apply_textdelta called");
+       return SVN_NO_ERROR;
+}
+
+int main()
+{
+       apr_pool_t *pool;
+       apr_file_t *dumpfile;
+       svn_stream_t *dumpstream;
+       svn_repos_parse_fns2_t *parser;
+
+       if (svn_cmdline_init ("parse_dumpstream", stderr) != EXIT_SUCCESS)
+               return EXIT_FAILURE;
+       pool = svn_pool_create(NULL);
+
+       parser = apr_pcalloc(pool, sizeof(*parser));
+
+       parser->new_revision_record = new_revision_record;
+       parser->new_node_record = new_node_record;
+       parser->uuid_record = uuid_record;
+       parser->set_revision_property = set_revision_property;
+       parser->set_node_property = set_node_property;
+       parser->remove_node_props = remove_node_props;
+       parser->set_fulltext = set_fulltext;
+       parser->close_node = close_node;
+       parser->close_revision = close_revision;
+       parser->delete_node_property = delete_node_property;
+       parser->apply_textdelta = apply_textdelta;
+
+       apr_file_open_stdin(&dumpfile, pool);
+       dumpstream = svn_stream_from_aprfile2(dumpfile, FALSE, pool);
+       svn_repos_parse_dumpstream2(dumpstream, parser, NULL, NULL, NULL, pool);
+       svn_pool_destroy(pool);
+       return 0;
+}

Added: subversion/branches/svnrload/subversion/svnrload/svnrload.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/svnrload/subversion/svnrload/svnrload.c?rev=979002&view=auto
==============================================================================
--- subversion/branches/svnrload/subversion/svnrload/svnrload.c (added)
+++ subversion/branches/svnrload/subversion/svnrload/svnrload.c Sun Jul 25 
08:44:21 2010
@@ -0,0 +1,468 @@
+/*
+ *  svnrload.c: Load a dumpfile to a remote repository.
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_hash.h"
+#include "svn_ra.h"
+#include "svn_repos.h"
+#include "svn_path.h"
+#include "svn_utf.h"
+#include "svn_string.h"
+#include "svn_props.h"
+
+#include "load_editor.h"
+
+enum svn_svnrload__longopt_t
+  {
+    opt_config_dir = SVN_OPT_FIRST_LONGOPT_ID,
+    opt_auth_username,
+    opt_auth_password,
+    opt_non_interactive,
+    opt_auth_nocache,
+    opt_version,
+  };
+
+static const apr_getopt_option_t svnrload__options[] =
+  {
+    {"revision",     'r', 1, "REV1[:REV2] range of revisions to dump"},
+    {"quiet",         'q', 0, "no progress (only errors to stderr"},
+    {"config-dir",    opt_config_dir, 1, "read user configuration files from"
+                                            " directory ARG" },
+    {"username",      opt_auth_username, 1, "specify a username ARG"},
+    {"password",      opt_auth_password, 1, "specify a password ARG"},
+    {"non-interactive", opt_non_interactive, 0, "do no interactive"
+                                                   " prompting"},
+    {"no-auth-cache", opt_auth_nocache, 0, "do not cache authentication"
+                                              " tokens"},
+  
+    {"help",          'h', 0, "display this help"},
+    {"version",       opt_version, 0, "show program version information"},
+
+    {0,                  0,   0, 0}
+  };
+
+/* Baton for the RA replay session. */
+struct replay_baton {
+  /* The editor producing diffs. */
+  const svn_delta_editor_t *editor;
+
+  /* Baton for the editor. */
+  void *edit_baton;
+
+  /* Whether to be quiet. */
+  svn_boolean_t quiet;
+};
+
+static svn_error_t *
+replay_revstart(svn_revnum_t revision,
+                void *replay_baton,
+                const svn_delta_editor_t **editor,
+                void **edit_baton,
+                apr_hash_t *rev_props,
+                apr_pool_t *pool)
+{
+  struct replay_baton *rb = replay_baton;
+  svn_stringbuf_t *propstring;
+  svn_stream_t *stdout_stream;
+  svn_stream_t *revprop_stream;
+
+  svn_stream_for_stdout(&stdout_stream, pool);
+
+  /* Revision-number: 19 */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_REVISION_NUMBER
+                            ": %ld\n", revision));
+  propstring = svn_stringbuf_create_ensure(0, pool);
+  revprop_stream = svn_stream_from_stringbuf(propstring, pool);
+  SVN_ERR(svn_hash_write2(rev_props, revprop_stream, "PROPS-END", pool));
+  SVN_ERR(svn_stream_close(revprop_stream));
+
+  /* Prop-content-length: 13 */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n", propstring->len));
+
+  /* Content-length: 29 */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n\n", propstring->len));
+
+  /* Property data. */
+  SVN_ERR(svn_stream_write(stdout_stream, propstring->data,
+                           &(propstring->len)));
+
+  SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n"));
+  SVN_ERR(svn_stream_close(stdout_stream));
+
+  /* Extract editor and editor_baton from the replay_baton and
+     set them so that the editor callbacks can use them. */
+  *editor = rb->editor;
+  *edit_baton = rb->edit_baton;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+replay_revend(svn_revnum_t revision,
+              void *replay_baton,
+              const svn_delta_editor_t *editor,
+              void *edit_baton,
+              apr_hash_t *rev_props,
+              apr_pool_t *pool)
+{
+  /* No resources left to free. */
+  struct replay_baton *rb = replay_baton;
+  if (! rb->quiet)
+    svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu\n", revision);
+  return SVN_NO_ERROR;
+}
+
+/* Return in *SESSION a new RA session to URL.
+ * Allocate *SESSION and related data structures in POOL.
+ * Use CONFIG_DIR and pass USERNAME, PASSWORD, CONFIG_DIR and
+ * NO_AUTH_CACHE to initialize the authorization baton.*/
+static svn_error_t *
+open_connection(svn_ra_session_t **session,
+                const char *url,
+                svn_boolean_t non_interactive,
+                const char *username,
+                const char *password,
+                const char *config_dir,
+                svn_boolean_t no_auth_cache,
+                apr_pool_t *pool)
+{
+  svn_client_ctx_t *ctx = NULL;
+  svn_config_t *cfg_config;
+
+  SVN_ERR(svn_ra_initialize(pool));
+
+  SVN_ERR(svn_config_ensure(config_dir, pool));
+  SVN_ERR(svn_client_create_context(&ctx, pool));
+
+  SVN_ERR(svn_config_get_config(&(ctx->config), config_dir, pool));
+
+  cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
+                            APR_HASH_KEY_STRING);
+
+  /* Default authentication providers for non-interactive use */
+  SVN_ERR(svn_cmdline_create_auth_baton(&(ctx->auth_baton), non_interactive,
+                                        username, password, config_dir,
+                                        no_auth_cache, FALSE, cfg_config,
+                                        ctx->cancel_func, ctx->cancel_baton,
+                                        pool));
+  SVN_ERR(svn_client_open_ra_session(session, url, ctx, pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+replay_range(svn_ra_session_t *session, const char *url,
+             svn_revnum_t start_revision, svn_revnum_t end_revision,
+             svn_boolean_t quiet, apr_pool_t *pool)
+{
+  const svn_delta_editor_t *load_editor;
+  struct replay_baton *replay_baton;
+  void *load_baton;
+  const char *uuid;
+  svn_stream_t *stdout_stream;
+
+  SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
+
+  SVN_ERR(get_load_editor(&load_editor, &load_baton, stdout_stream, pool));
+
+  replay_baton = apr_pcalloc(pool, sizeof(*replay_baton));
+  replay_baton->editor = load_editor;
+  replay_baton->edit_baton = load_baton;
+  replay_baton->quiet = quiet;
+
+  /* Write the magic header and UUID */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
+                            SVN_REPOS_DUMPFILE_FORMAT_VERSION));
+  SVN_ERR(svn_ra_get_uuid2(session, &uuid, pool));
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid));
+
+  /* Fake revision 0 if necessary */
+  if (start_revision == 0)
+    {
+      apr_hash_t *prophash;
+      svn_stringbuf_t *propstring;
+      svn_stream_t *propstream;
+      SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                                SVN_REPOS_DUMPFILE_REVISION_NUMBER
+                                ": %ld\n", start_revision));
+
+      prophash = apr_hash_make(pool);
+      propstring = svn_stringbuf_create("", pool);
+
+      SVN_ERR(svn_ra_rev_proplist(session, start_revision,
+                                  &prophash, pool));
+
+      propstream = svn_stream_from_stringbuf(propstring, pool);
+      SVN_ERR(svn_hash_write2(prophash, propstream, "PROPS-END", pool));
+      SVN_ERR(svn_stream_close(propstream));
+
+      /* Property-content-length: 14; Content-length: 14 */
+      SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                                SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                                ": %" APR_SIZE_T_FMT "\n",
+                                propstring->len));
+      SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                                SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                                ": %" APR_SIZE_T_FMT "\n\n",
+                                propstring->len));
+      /* The properties */
+      SVN_ERR(svn_stream_write(stdout_stream, propstring->data,
+                               &(propstring->len)));
+      SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n"));
+      if (! quiet)
+        svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu\n", 
start_revision);
+
+      start_revision++;
+    }
+
+  SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
+                              0, TRUE, replay_revstart, replay_revend,
+                              replay_baton, pool));
+  SVN_ERR(svn_stream_close(stdout_stream));
+  return SVN_NO_ERROR;
+}
+
+static const char *
+ensure_appname(const char *progname, apr_pool_t *pool)
+{
+  if (!progname)
+    return "svnrdump";
+  
+  progname = svn_dirent_internal_style(progname, pool);
+  return svn_dirent_basename(progname, NULL);
+}
+
+static svn_error_t *
+usage(const char *progname, apr_pool_t *pool)
+{
+  progname = ensure_appname(progname, pool);
+
+  SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+                              "Type '%s --help' for usage.\n",
+                              progname));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+help(const char *progname, apr_pool_t *pool)
+{
+  apr_size_t i;
+
+  progname = ensure_appname(progname, pool);
+
+  SVN_ERR(svn_cmdline_printf(
+                             pool,
+                             "usage: %s URL [-r LOWER[:UPPER]]\n\n"
+                               "Dump the contents of repository at remote URL "
+                               "to stdout in a 'dumpfile' portable format.\n"
+                               "Dump revisions LOWER rev through UPPER rev.\n"
+                               "LOWER defaults to 0 and UPPER defaults to the "
+                               "highest possible revision if omitted.\n\n"
+                               "Valid options:\n",
+                             progname));
+
+  for (i = 0; svnrload__options[i].name && svnrload__options[i].optch; i++)
+    {
+      const char *optstr;
+      svn_opt_format_option(&optstr, svnrload__options + i, TRUE, pool);
+      SVN_ERR(svn_cmdline_fprintf(stdout, pool, "  %s\n", optstr));
+    }
+  return svn_cmdline_fprintf(stdout, pool, "\n");
+}
+
+static svn_error_t *
+version(const char *progname, apr_pool_t *pool)
+{
+  progname = ensure_appname(progname, pool);
+
+  return svn_opt_print_help3(NULL, progname, TRUE, FALSE, NULL,
+                             NULL, NULL, NULL, NULL, NULL, pool);
+}
+
+
+/** A statement macro, similar to @c SVN_ERR, but returns an integer.
+ *
+ * Evaluate @a expr. If it yields an error, handle that error and
+ * return @c EXIT_FAILURE.
+ */
+#define SVNRLOAD_ERR(expr)                                              \
+  do                                                                    \
+    {                                                                   \
+      svn_error_t *svn_err__temp = (expr);                              \
+      if (svn_err__temp)                                                \
+        {                                                               \
+          svn_handle_error2(svn_err__temp, stderr, FALSE, "svnrdump: "); \
+          svn_error_clear(svn_err__temp);                               \
+          return EXIT_FAILURE;                                          \
+        }                                                               \
+    }                                                                   \
+  while (0)
+
+
+int
+main(int argc, const char **argv)
+{
+  const char *url = NULL;
+  char *revision_cut = NULL;
+  svn_revnum_t start_revision = svn_opt_revision_unspecified;
+  svn_revnum_t end_revision = svn_opt_revision_unspecified;
+  svn_revnum_t latest_revision = svn_opt_revision_unspecified;
+  svn_boolean_t quiet = FALSE;
+  apr_pool_t *pool = NULL;
+  svn_ra_session_t *session = NULL;
+  const char *config_dir = NULL;
+  const char *username = NULL;
+  const char *password = NULL;
+  svn_boolean_t no_auth_cache = FALSE;
+  svn_boolean_t non_interactive = FALSE;
+  apr_getopt_t *os;
+
+  if (svn_cmdline_init ("svnrdump", stderr) != EXIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  pool = svn_pool_create(NULL);
+
+  SVNRLOAD_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
+
+  os->interleave = TRUE; /* Options and arguments can be interleaved */
+
+  while (1)
+    {
+      int opt;
+      const char *opt_arg;
+      apr_status_t status = apr_getopt_long(os, svnrload__options, &opt,
+                                            &opt_arg);
+
+      if (APR_STATUS_IS_EOF(status))
+        break;
+      if (status != APR_SUCCESS)
+        {
+          SVNRLOAD_ERR(usage(argv[0], pool));
+          exit(EXIT_FAILURE);
+        }
+
+      switch(opt)
+        {
+        case 'r':
+          {
+            revision_cut = strchr(opt_arg, ':');
+            if (revision_cut)
+              {
+                start_revision = (svn_revnum_t)strtoul(opt_arg,
+                                                       &revision_cut, 10);
+                end_revision = (svn_revnum_t)strtoul(revision_cut + 1,
+                                                     NULL, 10);
+              }
+            else
+              start_revision = (svn_revnum_t)strtoul(opt_arg, NULL, 10);
+          }
+          break;
+        case 'q':
+          quiet = TRUE;
+          break;
+        case opt_config_dir:
+          config_dir = opt_arg;
+          break;
+        case opt_version:
+          SVNRLOAD_ERR(version(argv[0], pool));
+          exit(EXIT_SUCCESS);
+          break;
+        case 'h':
+          SVNRLOAD_ERR(help(argv[0], pool));
+          exit(EXIT_SUCCESS);
+          break;
+        case opt_auth_username:
+          SVNRLOAD_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool));
+          break;
+        case opt_auth_password:
+          SVNRLOAD_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool));
+          break;
+        case opt_auth_nocache:
+          no_auth_cache = TRUE;
+          break;
+        case opt_non_interactive:
+          non_interactive = TRUE;
+          break;
+        }
+    }
+
+  /* Only continue if the only not option argument is a url, to allow
+     implementing 'svnrdump dump URL' like handling later without breaking
+     backward compatibility */
+  if ((os->ind != os->argc-1)
+      || !svn_path_is_url(os->argv[os->ind]))
+    {
+      SVNRLOAD_ERR(usage(argv[0], pool));
+      exit(EXIT_FAILURE);
+    }
+
+  SVNRLOAD_ERR(svn_utf_cstring_to_utf8(&url, os->argv[os->ind], pool));
+
+  url = svn_uri_canonicalize(os->argv[os->ind], pool);
+
+
+  SVNRLOAD_ERR(open_connection(&session,
+                               url,
+                               non_interactive,
+                               username,
+                               password,
+                               config_dir,
+                               no_auth_cache,
+                               pool));
+
+  /* Have sane start_revision and end_revision defaults if unspecified */
+  SVNRLOAD_ERR(svn_ra_get_latest_revnum(session, &latest_revision, pool));
+  if (start_revision == svn_opt_revision_unspecified)
+    start_revision = 0;
+  if (end_revision == svn_opt_revision_unspecified)
+    end_revision = latest_revision;
+  if (end_revision > latest_revision)
+    {
+      SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
+                                      "Revision %ld does not exist.\n",
+                                      end_revision));
+      exit(EXIT_FAILURE);
+    }
+  if (end_revision < start_revision)
+    {
+      SVN_INT_ERR(svn_cmdline_fprintf(stderr, pool,
+                                      "LOWER cannot be greater "
+                                        "than UPPER.\n"));
+      exit(EXIT_FAILURE);
+    }
+
+  SVNRLOAD_ERR(replay_range(session, url, start_revision, end_revision,
+                            quiet, pool));
+
+  svn_pool_destroy(pool);
+
+  return 0;
+}


Reply via email to