summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra
diff options
context:
space:
mode:
authorJames McCoy <jamessan@debian.org>2018-07-31 22:26:52 -0400
committerJames McCoy <jamessan@debian.org>2018-07-31 22:26:52 -0400
commite20a507113ff1126aeb4a97b806390ea377fe292 (patch)
tree0260b3a40387d7f994fbadaf22f1e9d3c080b09f /subversion/libsvn_ra
parentc64debffb81d2fa17e9a72af7199ccf88b3cc556 (diff)
New upstream version 1.10.2
Diffstat (limited to 'subversion/libsvn_ra')
-rw-r--r--subversion/libsvn_ra/compat.c954
-rw-r--r--subversion/libsvn_ra/debug_reporter.c151
-rw-r--r--subversion/libsvn_ra/debug_reporter.h49
-rw-r--r--subversion/libsvn_ra/deprecated.c509
-rw-r--r--subversion/libsvn_ra/deprecated.h60
-rw-r--r--subversion/libsvn_ra/editor.c339
-rw-r--r--subversion/libsvn_ra/libsvn_ra.pc.in12
-rw-r--r--subversion/libsvn_ra/ra_loader.c1598
-rw-r--r--subversion/libsvn_ra/ra_loader.h599
-rw-r--r--subversion/libsvn_ra/util.c280
-rw-r--r--subversion/libsvn_ra/wrapper_template.h514
11 files changed, 5065 insertions, 0 deletions
diff --git a/subversion/libsvn_ra/compat.c b/subversion/libsvn_ra/compat.c
new file mode 100644
index 0000000..f7307bf
--- /dev/null
+++ b/subversion/libsvn_ra/compat.c
@@ -0,0 +1,954 @@
+/*
+ * compat.c: compatibility compliance logic
+ *
+ * ====================================================================
+ * 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 <apr_pools.h>
+
+#include "svn_hash.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_sorts.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_ra.h"
+#include "svn_io.h"
+#include "svn_compat.h"
+#include "svn_props.h"
+
+#include "private/svn_fspath.h"
+#include "private/svn_sorts_private.h"
+#include "ra_loader.h"
+#include "svn_private_config.h"
+
+
+
+/* This is just like svn_sort_compare_revisions, save that it sorts
+ the revisions in *ascending* order. */
+static int
+compare_revisions(const void *a, const void *b)
+{
+ svn_revnum_t a_rev = *(const svn_revnum_t *)a;
+ svn_revnum_t b_rev = *(const svn_revnum_t *)b;
+ if (a_rev == b_rev)
+ return 0;
+ return a_rev < b_rev ? -1 : 1;
+}
+
+/* Given the CHANGED_PATHS and REVISION from an instance of a
+ svn_log_message_receiver_t function, determine at which location
+ PATH may be expected in the next log message, and set *PREV_PATH_P
+ to that value. KIND is the node kind of PATH. Set *ACTION_P to a
+ character describing the change that caused this revision (as
+ listed in svn_log_changed_path_t) and set *COPYFROM_REV_P to the
+ revision PATH was copied from, or SVN_INVALID_REVNUM if it was not
+ copied. ACTION_P and COPYFROM_REV_P may be NULL, in which case
+ they are not used. Perform all allocations in POOL.
+
+ This is useful for tracking the various changes in location a
+ particular resource has undergone when performing an RA->get_logs()
+ operation on that resource.
+*/
+static svn_error_t *
+prev_log_path(const char **prev_path_p,
+ char *action_p,
+ svn_revnum_t *copyfrom_rev_p,
+ apr_hash_t *changed_paths,
+ const char *path,
+ svn_node_kind_t kind,
+ svn_revnum_t revision,
+ apr_pool_t *pool)
+{
+ svn_log_changed_path_t *change;
+ const char *prev_path = NULL;
+
+ /* It's impossible to find the predecessor path of a NULL path. */
+ SVN_ERR_ASSERT(path);
+
+ /* Initialize our return values for the action and copyfrom_rev in
+ case we have an unhandled case later on. */
+ if (action_p)
+ *action_p = 'M';
+ if (copyfrom_rev_p)
+ *copyfrom_rev_p = SVN_INVALID_REVNUM;
+
+ if (changed_paths)
+ {
+ /* See if PATH was explicitly changed in this revision. */
+ change = svn_hash_gets(changed_paths, path);
+ if (change)
+ {
+ /* If PATH was not newly added in this revision, then it may or may
+ not have also been part of a moved subtree. In this case, set a
+ default previous path, but still look through the parents of this
+ path for a possible copy event. */
+ if (change->action != 'A' && change->action != 'R')
+ {
+ prev_path = path;
+ }
+ else
+ {
+ /* PATH is new in this revision. This means it cannot have been
+ part of a copied subtree. */
+ if (change->copyfrom_path)
+ prev_path = apr_pstrdup(pool, change->copyfrom_path);
+ else
+ prev_path = NULL;
+
+ *prev_path_p = prev_path;
+ if (action_p)
+ *action_p = change->action;
+ if (copyfrom_rev_p)
+ *copyfrom_rev_p = change->copyfrom_rev;
+ return SVN_NO_ERROR;
+ }
+ }
+
+ if (apr_hash_count(changed_paths))
+ {
+ /* The path was not explicitly changed in this revision. The
+ fact that we're hearing about this revision implies, then,
+ that the path was a child of some copied directory. We need
+ to find that directory, and effectively "re-base" our path on
+ that directory's copyfrom_path. */
+ int i;
+ apr_array_header_t *paths;
+
+ /* Build a sorted list of the changed paths. */
+ paths = svn_sort__hash(changed_paths,
+ svn_sort_compare_items_as_paths, pool);
+
+ /* Now, walk the list of paths backwards, looking a parent of
+ our path that has copyfrom information. */
+ for (i = paths->nelts; i > 0; i--)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(paths,
+ i - 1, svn_sort__item_t);
+ const char *ch_path = item.key;
+ size_t len = strlen(ch_path);
+
+ /* See if our path is the child of this change path. If
+ not, keep looking. */
+ if (! ((strncmp(ch_path, path, len) == 0) && (path[len] == '/')))
+ continue;
+
+ /* Okay, our path *is* a child of this change path. If
+ this change was copied, we just need to apply the
+ portion of our path that is relative to this change's
+ path, to the change's copyfrom path. Otherwise, this
+ change isn't really interesting to us, and our search
+ continues. */
+ change = item.value;
+ if (change->copyfrom_path)
+ {
+ if (action_p)
+ *action_p = change->action;
+ if (copyfrom_rev_p)
+ *copyfrom_rev_p = change->copyfrom_rev;
+ prev_path = svn_fspath__join(change->copyfrom_path,
+ path + len + 1, pool);
+ break;
+ }
+ }
+ }
+ }
+
+ /* If we didn't find what we expected to find, return an error.
+ (Because directories bubble-up, we get a bunch of logs we might
+ not want. Be forgiving in that case.) */
+ if (! prev_path)
+ {
+ if (kind == svn_node_dir)
+ prev_path = apr_pstrdup(pool, path);
+ else
+ return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("Missing changed-path information for "
+ "'%s' in revision %ld"),
+ svn_dirent_local_style(path, pool), revision);
+ }
+
+ *prev_path_p = prev_path;
+ return SVN_NO_ERROR;
+}
+
+
+/* Set *FS_PATH_P to the absolute filesystem path associated with the
+ URL built from SESSION's URL and REL_PATH (which is relative to
+ session's URL. Use POOL for allocations. */
+static svn_error_t *
+get_fs_path(const char **fs_path_p,
+ svn_ra_session_t *session,
+ const char *rel_path,
+ apr_pool_t *pool)
+{
+ const char *url, *fs_path;
+
+ SVN_ERR(svn_ra_get_session_url(session, &url, pool));
+ SVN_ERR(svn_ra_get_path_relative_to_root(session, &fs_path, url, pool));
+ *fs_path_p = svn_fspath__canonicalize(svn_relpath_join(fs_path,
+ rel_path, pool),
+ pool);
+ return SVN_NO_ERROR;
+}
+
+
+
+/*** Fallback implementation of svn_ra_get_locations(). ***/
+
+
+/* ### This is to support 1.0 servers. */
+struct log_receiver_baton
+{
+ /* The kind of the path we're tracing. */
+ svn_node_kind_t kind;
+
+ /* The path at which we are trying to find our versioned resource in
+ the log output. */
+ const char *last_path;
+
+ /* Input revisions and output hash; the whole point of this little game. */
+ svn_revnum_t peg_revision;
+ apr_array_header_t *location_revisions;
+ const char *peg_path;
+ apr_hash_t *locations;
+
+ /* A pool from which to allocate stuff stored in this baton. */
+ apr_pool_t *pool;
+};
+
+
+/* Implements svn_log_entry_receiver_t; helper for slow_get_locations.
+ As input, takes log_receiver_baton (defined above) and attempts to
+ "fill in" locations in the baton over the course of many
+ iterations. */
+static svn_error_t *
+log_receiver(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ struct log_receiver_baton *lrb = baton;
+ apr_pool_t *hash_pool = apr_hash_pool_get(lrb->locations);
+ const char *current_path = lrb->last_path;
+ const char *prev_path;
+
+ /* No paths were changed in this revision. Nothing to do. */
+ if (! log_entry->changed_paths2)
+ return SVN_NO_ERROR;
+
+ /* If we've run off the end of the path's history, there's nothing
+ to do. (This should never happen with a properly functioning
+ server, since we'd get no more log messages after the one where
+ path was created. But a malfunctioning server shouldn't cause us
+ to trigger an assertion failure.) */
+ if (! current_path)
+ return SVN_NO_ERROR;
+
+ /* If we haven't found our peg path yet, and we are now looking at a
+ revision equal to or older than the peg revision, then our
+ "current" path is our peg path. */
+ if ((! lrb->peg_path) && (log_entry->revision <= lrb->peg_revision))
+ lrb->peg_path = apr_pstrdup(lrb->pool, current_path);
+
+ /* Determine the paths for any of the revisions for which we haven't
+ gotten paths already. */
+ while (lrb->location_revisions->nelts)
+ {
+ svn_revnum_t next = APR_ARRAY_IDX(lrb->location_revisions,
+ lrb->location_revisions->nelts - 1,
+ svn_revnum_t);
+ if (log_entry->revision <= next)
+ {
+ apr_hash_set(lrb->locations,
+ apr_pmemdup(hash_pool, &next, sizeof(next)),
+ sizeof(next),
+ apr_pstrdup(hash_pool, current_path));
+ apr_array_pop(lrb->location_revisions);
+ }
+ else
+ break;
+ }
+
+ /* Figure out at which repository path our object of interest lived
+ in the previous revision. */
+ SVN_ERR(prev_log_path(&prev_path, NULL, NULL, log_entry->changed_paths2,
+ current_path, lrb->kind, log_entry->revision, pool));
+
+ /* Squirrel away our "next place to look" path (suffer the strcmp
+ hit to save on allocations). */
+ if (! prev_path)
+ lrb->last_path = NULL;
+ else if (strcmp(prev_path, current_path) != 0)
+ lrb->last_path = apr_pstrdup(lrb->pool, prev_path);
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra__locations_from_log(svn_ra_session_t *session,
+ apr_hash_t **locations_p,
+ const char *path,
+ svn_revnum_t peg_revision,
+ const apr_array_header_t *location_revisions,
+ apr_pool_t *pool)
+{
+ apr_hash_t *locations = apr_hash_make(pool);
+ struct log_receiver_baton lrb = { 0 };
+ apr_array_header_t *targets;
+ svn_revnum_t youngest_requested, oldest_requested, youngest, oldest;
+ svn_node_kind_t kind;
+ const char *fs_path;
+ apr_array_header_t *sorted_location_revisions;
+
+ /* Fetch the absolute FS path associated with PATH. */
+ SVN_ERR(get_fs_path(&fs_path, session, path, pool));
+
+ /* Sanity check: verify that the peg-object exists in repos. */
+ SVN_ERR(svn_ra_check_path(session, path, peg_revision, &kind, pool));
+ if (kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("Path '%s' doesn't exist in revision %ld"),
+ fs_path, peg_revision);
+
+ /* Easy out: no location revisions. */
+ if (! location_revisions->nelts)
+ {
+ *locations_p = locations;
+ return SVN_NO_ERROR;
+ }
+
+ /* Figure out the youngest and oldest revs (amongst the set of
+ requested revisions + the peg revision) so we can avoid
+ unnecessary log parsing. */
+ sorted_location_revisions = apr_array_copy(pool, location_revisions);
+ svn_sort__array(sorted_location_revisions, compare_revisions);
+ oldest_requested = APR_ARRAY_IDX(sorted_location_revisions, 0, svn_revnum_t);
+ youngest_requested = APR_ARRAY_IDX(sorted_location_revisions,
+ sorted_location_revisions->nelts - 1,
+ svn_revnum_t);
+ youngest = peg_revision;
+ youngest = (oldest_requested > youngest) ? oldest_requested : youngest;
+ youngest = (youngest_requested > youngest) ? youngest_requested : youngest;
+ oldest = peg_revision;
+ oldest = (oldest_requested < oldest) ? oldest_requested : oldest;
+ oldest = (youngest_requested < oldest) ? youngest_requested : oldest;
+
+ /* Populate most of our log receiver baton structure. */
+ lrb.kind = kind;
+ lrb.last_path = fs_path;
+ lrb.location_revisions = apr_array_copy(pool, sorted_location_revisions);
+ lrb.peg_revision = peg_revision;
+ lrb.peg_path = NULL;
+ lrb.locations = locations;
+ lrb.pool = pool;
+
+ /* Let the RA layer drive our log information handler, which will do
+ the work of finding the actual locations for our resource.
+ Notice that we always run on the youngest rev of the 3 inputs. */
+ targets = apr_array_make(pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(targets, const char *) = path;
+ SVN_ERR(svn_ra_get_log2(session, targets, youngest, oldest, 0,
+ TRUE, FALSE, FALSE,
+ apr_array_make(pool, 0, sizeof(const char *)),
+ log_receiver, &lrb, pool));
+
+ /* If the received log information did not cover any of the
+ requested revisions, use the last known path. (This normally
+ just means that FS_PATH was not modified between the requested
+ revision and OLDEST. If the file was created at some point after
+ OLDEST, then lrb.last_path should be NULL.) */
+ if (! lrb.peg_path)
+ lrb.peg_path = lrb.last_path;
+ if (lrb.last_path)
+ {
+ int i;
+ for (i = 0; i < sorted_location_revisions->nelts; i++)
+ {
+ svn_revnum_t rev = APR_ARRAY_IDX(sorted_location_revisions, i,
+ svn_revnum_t);
+ if (! apr_hash_get(locations, &rev, sizeof(rev)))
+ apr_hash_set(locations, apr_pmemdup(pool, &rev, sizeof(rev)),
+ sizeof(rev), apr_pstrdup(pool, lrb.last_path));
+ }
+ }
+
+ /* Check that we got the peg path. */
+ if (! lrb.peg_path)
+ return svn_error_createf
+ (APR_EGENERAL, NULL,
+ _("Unable to find repository location for '%s' in revision %ld"),
+ fs_path, peg_revision);
+
+ /* Sanity check: make sure that our calculated peg path is the same
+ as what we expected it to be. */
+ if (strcmp(fs_path, lrb.peg_path) != 0)
+ return svn_error_createf
+ (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("'%s' in revision %ld is an unrelated object"),
+ fs_path, youngest);
+
+ *locations_p = locations;
+ return SVN_NO_ERROR;
+}
+
+
+
+
+/*** Fallback implementation of svn_ra_get_location_segments(). ***/
+
+struct gls_log_receiver_baton {
+ /* The kind of the path we're tracing. */
+ svn_node_kind_t kind;
+
+ /* Are we finished (and just listening to log entries because our
+ caller won't shut up?). */
+ svn_boolean_t done;
+
+ /* The path at which we are trying to find our versioned resource in
+ the log output. */
+ const char *last_path;
+
+ /* Input data. */
+ svn_revnum_t start_rev;
+
+ /* Output intermediate state and callback/baton. */
+ svn_revnum_t range_end;
+ svn_location_segment_receiver_t receiver;
+ void *receiver_baton;
+
+ /* A pool from which to allocate stuff stored in this baton. */
+ apr_pool_t *pool;
+};
+
+/* Build a node location segment object from PATH, RANGE_START, and
+ RANGE_END, and pass it off to RECEIVER/RECEIVER_BATON. */
+static svn_error_t *
+maybe_crop_and_send_segment(const char *path,
+ svn_revnum_t start_rev,
+ svn_revnum_t range_start,
+ svn_revnum_t range_end,
+ svn_location_segment_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
+{
+ svn_location_segment_t *segment = apr_pcalloc(pool, sizeof(*segment));
+ segment->path = path ? ((*path == '/') ? path + 1 : path) : NULL;
+ segment->range_start = range_start;
+ segment->range_end = range_end;
+ if (segment->range_start <= start_rev)
+ {
+ if (segment->range_end > start_rev)
+ segment->range_end = start_rev;
+ return receiver(segment, receiver_baton, pool);
+ }
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+gls_log_receiver(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ struct gls_log_receiver_baton *lrb = baton;
+ const char *current_path = lrb->last_path;
+ const char *prev_path;
+ svn_revnum_t copyfrom_rev;
+
+ /* If we're done, ignore this invocation. */
+ if (lrb->done)
+ return SVN_NO_ERROR;
+
+ /* Figure out at which repository path our object of interest lived
+ in the previous revision, and if its current location is the
+ result of copy since then. */
+ SVN_ERR(prev_log_path(&prev_path, NULL, &copyfrom_rev,
+ log_entry->changed_paths2, current_path,
+ lrb->kind, log_entry->revision, pool));
+
+ /* If we've run off the end of the path's history, we need to report
+ our final segment (and then, we're done). */
+ if (! prev_path)
+ {
+ lrb->done = TRUE;
+ return maybe_crop_and_send_segment(current_path, lrb->start_rev,
+ log_entry->revision, lrb->range_end,
+ lrb->receiver, lrb->receiver_baton,
+ pool);
+ }
+
+ /* If there was a copy operation of interest... */
+ if (SVN_IS_VALID_REVNUM(copyfrom_rev))
+ {
+ /* ...then report the segment between this revision and the
+ last-reported revision. */
+ SVN_ERR(maybe_crop_and_send_segment(current_path, lrb->start_rev,
+ log_entry->revision, lrb->range_end,
+ lrb->receiver, lrb->receiver_baton,
+ pool));
+ lrb->range_end = log_entry->revision - 1;
+
+ /* And if there was a revision gap, we need to report that, too. */
+ if (log_entry->revision - copyfrom_rev > 1)
+ {
+ SVN_ERR(maybe_crop_and_send_segment(NULL, lrb->start_rev,
+ copyfrom_rev + 1, lrb->range_end,
+ lrb->receiver,
+ lrb->receiver_baton, pool));
+ lrb->range_end = copyfrom_rev;
+ }
+
+ /* Update our state variables. */
+ lrb->last_path = apr_pstrdup(lrb->pool, prev_path);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra__location_segments_from_log(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_location_segment_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
+{
+ struct gls_log_receiver_baton lrb = { 0 };
+ apr_array_header_t *targets;
+ svn_node_kind_t kind;
+ svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
+ const char *fs_path;
+
+ /* Fetch the absolute FS path associated with PATH. */
+ SVN_ERR(get_fs_path(&fs_path, session, path, pool));
+
+ /* If PEG_REVISION is invalid, it means HEAD. If START_REV is
+ invalid, it means HEAD. If END_REV is SVN_INVALID_REVNUM, we'll
+ use 0. */
+ if (! SVN_IS_VALID_REVNUM(peg_revision))
+ {
+ SVN_ERR(svn_ra_get_latest_revnum(session, &youngest_rev, pool));
+ peg_revision = youngest_rev;
+ }
+ if (! SVN_IS_VALID_REVNUM(start_rev))
+ {
+ if (SVN_IS_VALID_REVNUM(youngest_rev))
+ start_rev = youngest_rev;
+ else
+ SVN_ERR(svn_ra_get_latest_revnum(session, &start_rev, pool));
+ }
+ if (! SVN_IS_VALID_REVNUM(end_rev))
+ {
+ end_rev = 0;
+ }
+
+ /* The API demands a certain ordering of our revision inputs. Enforce it. */
+ SVN_ERR_ASSERT((peg_revision >= start_rev) && (start_rev >= end_rev));
+
+ /* Sanity check: verify that the peg-object exists in repos. */
+ SVN_ERR(svn_ra_check_path(session, path, peg_revision, &kind, pool));
+ if (kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("Path '%s' doesn't exist in revision %ld"),
+ fs_path, start_rev);
+
+ /* Populate most of our log receiver baton structure. */
+ lrb.kind = kind;
+ lrb.last_path = fs_path;
+ lrb.done = FALSE;
+ lrb.start_rev = start_rev;
+ lrb.range_end = start_rev;
+ lrb.receiver = receiver;
+ lrb.receiver_baton = receiver_baton;
+ lrb.pool = pool;
+
+ /* Let the RA layer drive our log information handler, which will do
+ the work of finding the actual locations for our resource.
+ Notice that we always run on the youngest rev of the 3 inputs. */
+ targets = apr_array_make(pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(targets, const char *) = path;
+ SVN_ERR(svn_ra_get_log2(session, targets, peg_revision, end_rev, 0,
+ TRUE, FALSE, FALSE,
+ apr_array_make(pool, 0, sizeof(const char *)),
+ gls_log_receiver, &lrb, pool));
+
+ /* If we didn't finish, we need to do so with a final segment send. */
+ if (! lrb.done)
+ SVN_ERR(maybe_crop_and_send_segment(lrb.last_path, start_rev,
+ end_rev, lrb.range_end,
+ receiver, receiver_baton, pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+
+/*** Fallback implementation of svn_ra_get_file_revs(). ***/
+
+/* The metadata associated with a particular revision. */
+struct rev
+{
+ svn_revnum_t revision; /* the revision number */
+ const char *path; /* the absolute repository path */
+ apr_hash_t *props; /* the revprops for this revision */
+ struct rev *next; /* the next revision */
+};
+
+/* File revs log message baton. */
+struct fr_log_message_baton {
+ const char *path; /* The path to be processed */
+ struct rev *eldest; /* The eldest revision processed */
+ char action; /* The action associated with the eldest */
+ svn_revnum_t copyrev; /* The revision the eldest was copied from */
+ apr_pool_t *pool;
+};
+
+/* Callback for log messages: implements svn_log_entry_receiver_t and
+ accumulates revision metadata into a chronologically ordered list stored in
+ the baton. */
+static svn_error_t *
+fr_log_message_receiver(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ struct fr_log_message_baton *lmb = baton;
+ struct rev *rev;
+
+ rev = apr_palloc(lmb->pool, sizeof(*rev));
+ rev->revision = log_entry->revision;
+ rev->path = lmb->path;
+ rev->next = lmb->eldest;
+ lmb->eldest = rev;
+
+ /* Duplicate log_entry revprops into rev->props */
+ rev->props = svn_prop_hash_dup(log_entry->revprops, lmb->pool);
+
+ return prev_log_path(&lmb->path, &lmb->action,
+ &lmb->copyrev, log_entry->changed_paths2,
+ lmb->path, svn_node_file, log_entry->revision,
+ lmb->pool);
+}
+
+svn_error_t *
+svn_ra__file_revs_from_log(svn_ra_session_t *ra_session,
+ const char *path,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ svn_file_rev_handler_t handler,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ svn_node_kind_t kind;
+ const char *repos_url, *session_url, *fs_path;
+ apr_array_header_t *condensed_targets;
+ struct fr_log_message_baton lmb;
+ struct rev *rev;
+ apr_hash_t *last_props;
+ svn_stream_t *last_stream;
+ apr_pool_t *currpool, *lastpool;
+
+ /* Fetch the absolute FS path associated with PATH. */
+ SVN_ERR(get_fs_path(&fs_path, ra_session, path, pool));
+
+ /* Check to make sure we're dealing with a file. */
+ SVN_ERR(svn_ra_check_path(ra_session, path, end, &kind, pool));
+ if (kind == svn_node_dir)
+ return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+ _("'%s' is not a file"), fs_path);
+
+ condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(condensed_targets, const char *) = path;
+
+ lmb.path = fs_path;
+ lmb.eldest = NULL;
+ lmb.pool = pool;
+
+ /* Accumulate revision metadata by walking the revisions
+ backwards; this allows us to follow moves/copies
+ correctly. */
+ SVN_ERR(svn_ra_get_log2(ra_session,
+ condensed_targets,
+ end, start, 0, /* no limit */
+ TRUE, FALSE, FALSE,
+ NULL, fr_log_message_receiver, &lmb,
+ pool));
+
+ /* Reparent the session while we go back through the history. */
+ SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, pool));
+ SVN_ERR(svn_ra_reparent(ra_session, repos_url, pool));
+
+ currpool = svn_pool_create(pool);
+ lastpool = svn_pool_create(pool);
+
+ /* We want the first txdelta to be against the empty file. */
+ last_props = apr_hash_make(lastpool);
+ last_stream = svn_stream_empty(lastpool);
+
+ /* Walk the revision list in chronological order, downloading each fulltext,
+ diffing it with its predecessor, and calling the file_revs handler for
+ each one. Use two iteration pools rather than one, because the diff
+ routines need to look at a sliding window of revisions. Two pools gives
+ us a ring buffer of sorts. */
+ for (rev = lmb.eldest; rev; rev = rev->next)
+ {
+ const char *temp_path;
+ apr_pool_t *tmppool;
+ apr_hash_t *props;
+ apr_file_t *file;
+ svn_stream_t *stream;
+ apr_array_header_t *prop_diffs;
+ svn_txdelta_stream_t *delta_stream;
+ svn_txdelta_window_handler_t delta_handler = NULL;
+ void *delta_baton = NULL;
+
+ svn_pool_clear(currpool);
+
+ /* Get the contents of the file from the repository, and put them in
+ a temporary local file. */
+ SVN_ERR(svn_stream_open_unique(&stream, &temp_path, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ currpool, currpool));
+ SVN_ERR(svn_ra_get_file(ra_session, rev->path + 1, rev->revision,
+ stream, NULL, &props, currpool));
+ SVN_ERR(svn_stream_close(stream));
+
+ /* Open up a stream to the local file. */
+ SVN_ERR(svn_io_file_open(&file, temp_path, APR_READ, APR_OS_DEFAULT,
+ currpool));
+ stream = svn_stream_from_aprfile2(file, FALSE, currpool);
+
+ /* Calculate the property diff */
+ SVN_ERR(svn_prop_diffs(&prop_diffs, props, last_props, lastpool));
+
+ /* Call the file_rev handler */
+ SVN_ERR(handler(handler_baton, rev->path, rev->revision, rev->props,
+ FALSE, /* merged revision */
+ &delta_handler, &delta_baton, prop_diffs, lastpool));
+
+ /* Compute and send delta if client asked for it. */
+ if (delta_handler)
+ {
+ /* Get the content delta. Don't calculate checksums as we don't
+ * use them. */
+ svn_txdelta2(&delta_stream, last_stream, stream, FALSE, lastpool);
+
+ /* And send. */
+ SVN_ERR(svn_txdelta_send_txstream(delta_stream, delta_handler,
+ delta_baton, lastpool));
+ }
+
+ /* Switch the pools and data for the next iteration */
+ tmppool = currpool;
+ currpool = lastpool;
+ lastpool = tmppool;
+
+ SVN_ERR(svn_stream_close(last_stream));
+ last_stream = stream;
+ last_props = props;
+ }
+
+ SVN_ERR(svn_stream_close(last_stream));
+ svn_pool_destroy(currpool);
+ svn_pool_destroy(lastpool);
+
+ /* Reparent the session back to the original URL. */
+ return svn_ra_reparent(ra_session, session_url, pool);
+}
+
+
+/*** Fallback implementation of svn_ra_get_deleted_rev(). ***/
+
+/* svn_ra_get_log2() receiver_baton for svn_ra__get_deleted_rev_from_log(). */
+typedef struct log_path_del_rev_t
+{
+ /* Absolute repository path. */
+ const char *path;
+
+ /* Revision PATH was first deleted or replaced. */
+ svn_revnum_t revision_deleted;
+} log_path_del_rev_t;
+
+/* A svn_log_entry_receiver_t callback for finding the revision
+ ((log_path_del_rev_t *)BATON)->PATH was first deleted or replaced.
+ Stores that revision in ((log_path_del_rev_t *)BATON)->REVISION_DELETED.
+ */
+static svn_error_t *
+log_path_del_receiver(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ log_path_del_rev_t *b = baton;
+ apr_hash_index_t *hi;
+
+ /* No paths were changed in this revision. Nothing to do. */
+ if (! log_entry->changed_paths2)
+ return SVN_NO_ERROR;
+
+ for (hi = apr_hash_first(pool, log_entry->changed_paths2);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ void *val;
+ char *path;
+ svn_log_changed_path_t *log_item;
+
+ apr_hash_this(hi, (void *) &path, NULL, &val);
+ log_item = val;
+ if (svn_path_compare_paths(b->path, path) == 0
+ && (log_item->action == 'D' || log_item->action == 'R'))
+ {
+ /* Found the first deletion or replacement, we are done. */
+ b->revision_deleted = log_entry->revision;
+ break;
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra__get_deleted_rev_from_log(svn_ra_session_t *session,
+ const char *rel_deleted_path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t *revision_deleted,
+ apr_pool_t *pool)
+{
+ const char *fs_path;
+ log_path_del_rev_t log_path_deleted_baton;
+
+ /* Fetch the absolute FS path associated with PATH. */
+ SVN_ERR(get_fs_path(&fs_path, session, rel_deleted_path, pool));
+
+ if (!SVN_IS_VALID_REVNUM(peg_revision))
+ return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("Invalid peg revision %ld"), peg_revision);
+ if (!SVN_IS_VALID_REVNUM(end_revision))
+ return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("Invalid end revision %ld"), end_revision);
+ if (end_revision <= peg_revision)
+ return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("Peg revision must precede end revision"));
+
+ log_path_deleted_baton.path = fs_path;
+ log_path_deleted_baton.revision_deleted = SVN_INVALID_REVNUM;
+
+ /* Examine the logs of SESSION's URL to find when DELETED_PATH was first
+ deleted or replaced. */
+ SVN_ERR(svn_ra_get_log2(session, NULL, peg_revision, end_revision, 0,
+ TRUE, TRUE, FALSE,
+ apr_array_make(pool, 0, sizeof(char *)),
+ log_path_del_receiver, &log_path_deleted_baton,
+ pool));
+ *revision_deleted = log_path_deleted_baton.revision_deleted;
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra__get_inherited_props_walk(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ apr_array_header_t **inherited_props,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *repos_root_url;
+ const char *session_url;
+ const char *parent_url;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ *inherited_props =
+ apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *));
+
+ /* Walk to the root of the repository getting inherited
+ props for PATH. */
+ SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, scratch_pool));
+ SVN_ERR(svn_ra_get_session_url(session, &session_url, scratch_pool));
+ parent_url = session_url;
+
+ while (strcmp(repos_root_url, parent_url))
+ {
+ apr_hash_index_t *hi;
+ apr_hash_t *parent_props;
+ apr_hash_t *final_hash = apr_hash_make(result_pool);
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+ parent_url = svn_uri_dirname(parent_url, scratch_pool);
+ SVN_ERR(svn_ra_reparent(session, parent_url, iterpool));
+ err = session->vtable->get_dir(session, NULL, NULL,
+ &parent_props, "",
+ revision, SVN_DIRENT_ALL,
+ iterpool);
+
+ /* If the user doesn't have read access to a parent path then
+ skip, but allow them to inherit from further up. */
+ if (err)
+ {
+ if ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED)
+ || (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN))
+ {
+ svn_error_clear(err);
+ continue;
+ }
+ else
+ {
+ return svn_error_trace(err);
+ }
+ }
+
+ for (hi = apr_hash_first(scratch_pool, parent_props);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *name = apr_hash_this_key(hi);
+ apr_ssize_t klen = apr_hash_this_key_len(hi);
+ svn_string_t *value = apr_hash_this_val(hi);
+
+ if (svn_property_kind2(name) == svn_prop_regular_kind)
+ {
+ name = apr_pstrdup(result_pool, name);
+ value = svn_string_dup(value, result_pool);
+ apr_hash_set(final_hash, name, klen, value);
+ }
+ }
+
+ if (apr_hash_count(final_hash))
+ {
+ svn_prop_inherited_item_t *new_iprop =
+ apr_palloc(result_pool, sizeof(*new_iprop));
+ new_iprop->path_or_url = svn_uri_skip_ancestor(repos_root_url,
+ parent_url,
+ result_pool);
+ new_iprop->prop_hash = final_hash;
+ svn_sort__array_insert(*inherited_props, &new_iprop, 0);
+ }
+ }
+
+ /* Reparent session back to original URL. */
+ SVN_ERR(svn_ra_reparent(session, session_url, scratch_pool));
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra/debug_reporter.c b/subversion/libsvn_ra/debug_reporter.c
new file mode 100644
index 0000000..00ec029
--- /dev/null
+++ b/subversion/libsvn_ra/debug_reporter.c
@@ -0,0 +1,151 @@
+/*
+ * debug_reporter.c : An reporter that writes the operations it does to stderr.
+ *
+ * ====================================================================
+ * 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 "debug_reporter.h"
+
+struct report_baton
+{
+ const svn_ra_reporter3_t *wrapped_reporter;
+ void *wrapped_report_baton;
+
+ svn_stream_t *out;
+};
+
+#define BOOLEAN_TO_WORD(condition) ((condition) ? "True" : "False")
+
+
+/*** Wrappers. ***/
+
+static svn_error_t *
+set_path(void *report_baton,
+ const char *path,
+ svn_revnum_t revision,
+ svn_depth_t depth,
+ svn_boolean_t start_empty,
+ const char *lock_token,
+ apr_pool_t *pool)
+{
+ struct report_baton *rb = report_baton;
+ SVN_ERR(svn_stream_printf(rb->out, pool, "set_path(%s, %ld, %s, %s, %s)\n",
+ path, revision, svn_depth_to_word(depth),
+ BOOLEAN_TO_WORD(start_empty), lock_token));
+ SVN_ERR(rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path,
+ revision, depth,
+ start_empty, lock_token, pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+delete_path(void *report_baton,
+ const char *path,
+ apr_pool_t *pool)
+{
+ struct report_baton *rb = report_baton;
+ SVN_ERR(svn_stream_printf(rb->out, pool, "delete_path(%s)\n", path));
+ SVN_ERR(rb->wrapped_reporter->delete_path(rb->wrapped_report_baton,
+ path, pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+link_path(void *report_baton,
+ const char *path,
+ const char *url,
+ svn_revnum_t revision,
+ svn_depth_t depth,
+ svn_boolean_t start_empty,
+ const char *lock_token,
+ apr_pool_t *pool)
+{
+ struct report_baton *rb = report_baton;
+ SVN_ERR(svn_stream_printf(rb->out, pool,
+ "link_path(%s, %s, %ld, %s, %s, %s)\n",
+ path, url, revision, svn_depth_to_word(depth),
+ BOOLEAN_TO_WORD(start_empty), lock_token));
+ SVN_ERR(rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url,
+ revision, depth, start_empty,
+ lock_token, pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+finish_report(void *report_baton,
+ apr_pool_t *pool)
+{
+ struct report_baton *rb = report_baton;
+ SVN_ERR(svn_stream_puts(rb->out, "finish_report()\n"));
+ SVN_ERR(rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+abort_report(void *report_baton,
+ apr_pool_t *pool)
+{
+ struct report_baton *rb = report_baton;
+ SVN_ERR(svn_stream_puts(rb->out, "abort_report()\n"));
+ SVN_ERR(rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool));
+ return SVN_NO_ERROR;
+}
+
+
+/*** Public interfaces. ***/
+svn_error_t *
+svn_ra__get_debug_reporter(const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ const svn_ra_reporter3_t *wrapped_reporter,
+ void *wrapped_report_baton,
+ apr_pool_t *pool)
+{
+ svn_ra_reporter3_t *tree_reporter;
+ struct report_baton *rb;
+ apr_file_t *errfp;
+ svn_stream_t *out;
+
+ apr_status_t apr_err = apr_file_open_stderr(&errfp, pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err, "Problem opening stderr");
+
+ out = svn_stream_from_aprfile2(errfp, TRUE, pool);
+
+ /* ### svn_delta_default_editor() */
+ tree_reporter = apr_palloc(pool, sizeof(*tree_reporter));
+ rb = apr_palloc(pool, sizeof(*rb));
+
+ tree_reporter->set_path = set_path;
+ tree_reporter->delete_path = delete_path;
+ tree_reporter->link_path = link_path;
+ tree_reporter->finish_report = finish_report;
+ tree_reporter->abort_report = abort_report;
+
+ rb->wrapped_reporter = wrapped_reporter;
+ rb->wrapped_report_baton = wrapped_report_baton;
+ rb->out = out;
+
+ *reporter = tree_reporter;
+ *report_baton = rb;
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra/debug_reporter.h b/subversion/libsvn_ra/debug_reporter.h
new file mode 100644
index 0000000..1728139
--- /dev/null
+++ b/subversion/libsvn_ra/debug_reporter.h
@@ -0,0 +1,49 @@
+/*
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+
+#ifndef SVN_DEBUG_REPORTER_H
+#define SVN_DEBUG_REPORTER_H
+
+#include "svn_ra.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Return a debug reporter that wraps @a wrapped_reporter.
+ *
+ * The debug reporter simply prints an indication of what callbacks are being
+ * called to @c stderr, and is only intended for use in debugging subversion
+ * reporters.
+ */
+svn_error_t *
+svn_ra__get_debug_reporter(const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ const svn_ra_reporter3_t *wrapped_reporter,
+ void *wrapped_report_baton,
+ apr_pool_t *pool);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_DEBUG_REPORTER_H */
diff --git a/subversion/libsvn_ra/deprecated.c b/subversion/libsvn_ra/deprecated.c
new file mode 100644
index 0000000..b7e717e
--- /dev/null
+++ b/subversion/libsvn_ra/deprecated.c
@@ -0,0 +1,509 @@
+/*
+ * deprecated.c: holding file for all deprecated APIs.
+ * "we can't lose 'em, but we can shun 'em!"
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* We define this here to remove any further warnings about the usage of
+ deprecated functions in this file. */
+#define SVN_DEPRECATED
+
+#include "svn_hash.h"
+#include "svn_ra.h"
+#include "svn_path.h"
+#include "svn_compat.h"
+#include "svn_props.h"
+#include "svn_pools.h"
+
+#include "ra_loader.h"
+#include "deprecated.h"
+
+#include "svn_private_config.h"
+
+
+
+
+/*** From ra_loader.c ***/
+/*** Compatibility Wrappers ***/
+
+/* Wrap @c svn_ra_reporter3_t in an interface that looks like
+ @c svn_ra_reporter2_t, for compatibility with functions that take
+ the latter. This shields the ra-specific implementations from
+ worrying about what kind of reporter they're dealing with.
+
+ This code does not live in wrapper_template.h because that file is
+ about the big changeover from a vtable-style to function-style
+ interface, and does not contain the post-changeover interfaces
+ that we are compatiblizing here.
+
+ This code looks like it duplicates code in libsvn_wc/adm_crawler.c,
+ but in fact it does not. That code makes old things look like new
+ things; this code makes a new thing look like an old thing. */
+
+/* Baton for abovementioned wrapping. */
+struct reporter_3in2_baton {
+ const svn_ra_reporter3_t *reporter3;
+ void *reporter3_baton;
+};
+
+/* Wrap the corresponding svn_ra_reporter3_t field in an
+ svn_ra_reporter2_t interface. @a report_baton is a
+ @c reporter_3in2_baton_t *. */
+static svn_error_t *
+set_path(void *report_baton,
+ const char *path,
+ svn_revnum_t revision,
+ svn_boolean_t start_empty,
+ const char *lock_token,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = report_baton;
+ return b->reporter3->set_path(b->reporter3_baton,
+ path, revision, svn_depth_infinity,
+ start_empty, lock_token, pool);
+}
+
+/* Wrap the corresponding svn_ra_reporter3_t field in an
+ svn_ra_reporter2_t interface. @a report_baton is a
+ @c reporter_3in2_baton_t *. */
+static svn_error_t *
+delete_path(void *report_baton,
+ const char *path,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = report_baton;
+ return b->reporter3->delete_path(b->reporter3_baton, path, pool);
+}
+
+/* Wrap the corresponding svn_ra_reporter3_t field in an
+ svn_ra_reporter2_t interface. @a report_baton is a
+ @c reporter_3in2_baton_t *. */
+static svn_error_t *
+link_path(void *report_baton,
+ const char *path,
+ const char *url,
+ svn_revnum_t revision,
+ svn_boolean_t start_empty,
+ const char *lock_token,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = report_baton;
+ return b->reporter3->link_path(b->reporter3_baton,
+ path, url, revision, svn_depth_infinity,
+ start_empty, lock_token, pool);
+
+}
+
+/* Wrap the corresponding svn_ra_reporter3_t field in an
+ svn_ra_reporter2_t interface. @a report_baton is a
+ @c reporter_3in2_baton_t *. */
+static svn_error_t *
+finish_report(void *report_baton,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = report_baton;
+ return b->reporter3->finish_report(b->reporter3_baton, pool);
+}
+
+/* Wrap the corresponding svn_ra_reporter3_t field in an
+ svn_ra_reporter2_t interface. @a report_baton is a
+ @c reporter_3in2_baton_t *. */
+static svn_error_t *
+abort_report(void *report_baton,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = report_baton;
+ return b->reporter3->abort_report(b->reporter3_baton, pool);
+}
+
+/* Wrap svn_ra_reporter3_t calls in an svn_ra_reporter2_t interface.
+
+ Note: For calls where the prototypes are exactly the same, we could
+ avoid the pass-through overhead by using the function in the
+ reporter returned from session->vtable->do_foo. But the code would
+ get a lot less readable, and the only benefit would be to shave a
+ few instructions in a network-bound operation anyway. So in
+ delete_path(), finish_report(), and abort_report(), we cheerfully
+ pass through to identical functions. */
+static svn_ra_reporter2_t reporter_3in2_wrapper = {
+ set_path,
+ delete_path,
+ link_path,
+ finish_report,
+ abort_report
+};
+
+svn_error_t *svn_ra_open3(svn_ra_session_t **session_p,
+ const char *repos_URL,
+ const char *uuid,
+ const svn_ra_callbacks2_t *callbacks,
+ void *callback_baton,
+ apr_hash_t *config,
+ apr_pool_t *pool)
+{
+ return svn_ra_open4(session_p, NULL, repos_URL, uuid,
+ callbacks, callback_baton, config, pool);
+}
+
+svn_error_t *svn_ra_open2(svn_ra_session_t **session_p,
+ const char *repos_URL,
+ const svn_ra_callbacks2_t *callbacks,
+ void *callback_baton,
+ apr_hash_t *config,
+ apr_pool_t *pool)
+{
+ return svn_ra_open3(session_p, repos_URL, NULL,
+ callbacks, callback_baton, config, pool);
+}
+
+svn_error_t *svn_ra_open(svn_ra_session_t **session_p,
+ const char *repos_URL,
+ const svn_ra_callbacks_t *callbacks,
+ void *callback_baton,
+ apr_hash_t *config,
+ apr_pool_t *pool)
+{
+ /* Deprecated function. Copy the contents of the svn_ra_callbacks_t
+ to a new svn_ra_callbacks2_t and call svn_ra_open2(). */
+ svn_ra_callbacks2_t *callbacks2;
+ SVN_ERR(svn_ra_create_callbacks(&callbacks2, pool));
+ callbacks2->open_tmp_file = callbacks->open_tmp_file;
+ callbacks2->auth_baton = callbacks->auth_baton;
+ callbacks2->get_wc_prop = callbacks->get_wc_prop;
+ callbacks2->set_wc_prop = callbacks->set_wc_prop;
+ callbacks2->push_wc_prop = callbacks->push_wc_prop;
+ callbacks2->invalidate_wc_props = callbacks->invalidate_wc_props;
+ callbacks2->progress_func = NULL;
+ callbacks2->progress_baton = NULL;
+ return svn_ra_open2(session_p, repos_URL,
+ callbacks2, callback_baton,
+ config, pool);
+}
+
+svn_error_t *svn_ra_change_rev_prop(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ return svn_ra_change_rev_prop2(session, rev, name, NULL, value, pool);
+}
+
+svn_error_t *svn_ra_get_commit_editor2(svn_ra_session_t *session,
+ const svn_delta_editor_t **editor,
+ void **edit_baton,
+ const char *log_msg,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *pool)
+{
+ apr_hash_t *revprop_table = apr_hash_make(pool);
+ if (log_msg)
+ svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
+ svn_string_create(log_msg, pool));
+ return svn_ra_get_commit_editor3(session, editor, edit_baton, revprop_table,
+ commit_callback, commit_baton,
+ lock_tokens, keep_locks, pool);
+}
+
+svn_error_t *svn_ra_get_commit_editor(svn_ra_session_t *session,
+ const svn_delta_editor_t **editor,
+ void **edit_baton,
+ const char *log_msg,
+ svn_commit_callback_t callback,
+ void *callback_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *pool)
+{
+ svn_commit_callback2_t callback2;
+ void *callback2_baton;
+
+ svn_compat_wrap_commit_callback(&callback2, &callback2_baton,
+ callback, callback_baton,
+ pool);
+
+ return svn_ra_get_commit_editor2(session, editor, edit_baton,
+ log_msg, callback2,
+ callback2_baton, lock_tokens,
+ keep_locks, pool);
+}
+
+svn_error_t *svn_ra_do_diff2(svn_ra_session_t *session,
+ const svn_ra_reporter2_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision,
+ const char *diff_target,
+ svn_boolean_t recurse,
+ svn_boolean_t ignore_ancestry,
+ svn_boolean_t text_deltas,
+ const char *versus_url,
+ const svn_delta_editor_t *diff_editor,
+ void *diff_baton,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = apr_palloc(pool, sizeof(*b));
+ SVN_ERR_ASSERT(svn_path_is_empty(diff_target)
+ || svn_path_is_single_path_component(diff_target));
+ *reporter = &reporter_3in2_wrapper;
+ *report_baton = b;
+ return session->vtable->do_diff(session,
+ &(b->reporter3), &(b->reporter3_baton),
+ revision, diff_target,
+ SVN_DEPTH_INFINITY_OR_FILES(recurse),
+ ignore_ancestry, text_deltas, versus_url,
+ diff_editor, diff_baton, pool);
+}
+
+svn_error_t *svn_ra_do_diff(svn_ra_session_t *session,
+ const svn_ra_reporter2_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision,
+ const char *diff_target,
+ svn_boolean_t recurse,
+ svn_boolean_t ignore_ancestry,
+ const char *versus_url,
+ const svn_delta_editor_t *diff_editor,
+ void *diff_baton,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_path_is_empty(diff_target)
+ || svn_path_is_single_path_component(diff_target));
+ return svn_ra_do_diff2(session, reporter, report_baton, revision,
+ diff_target, recurse, ignore_ancestry, TRUE,
+ versus_url, diff_editor, diff_baton, pool);
+}
+
+svn_error_t *svn_ra_get_log(svn_ra_session_t *session,
+ const apr_array_header_t *paths,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ int limit,
+ svn_boolean_t discover_changed_paths,
+ svn_boolean_t strict_node_history,
+ svn_log_message_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
+{
+ svn_log_entry_receiver_t receiver2;
+ void *receiver2_baton;
+
+ if (paths)
+ {
+ int i;
+ for (i = 0; i < paths->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(paths, i, const char *);
+ SVN_ERR_ASSERT(*path != '/');
+ }
+ }
+
+ svn_compat_wrap_log_receiver(&receiver2, &receiver2_baton,
+ receiver, receiver_baton,
+ pool);
+
+ return svn_ra_get_log2(session, paths, start, end, limit,
+ discover_changed_paths, strict_node_history,
+ FALSE, svn_compat_log_revprops_in(pool),
+ receiver2, receiver2_baton, pool);
+}
+
+svn_error_t *svn_ra_get_file_revs(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ svn_ra_file_rev_handler_t handler,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ svn_file_rev_handler_t handler2;
+ void *handler2_baton;
+
+ SVN_ERR_ASSERT(*path != '/');
+
+ svn_compat_wrap_file_rev_handler(&handler2, &handler2_baton,
+ handler, handler_baton,
+ pool);
+
+ return svn_ra_get_file_revs2(session, path, start, end, FALSE, handler2,
+ handler2_baton, pool);
+}
+
+svn_error_t *
+svn_ra_do_update2(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_update_to,
+ const char *update_target,
+ svn_depth_t depth,
+ svn_boolean_t send_copyfrom_args,
+ const svn_delta_editor_t *update_editor,
+ void *update_baton,
+ apr_pool_t *pool)
+{
+ return svn_error_trace(
+ svn_ra_do_update3(session,
+ reporter, report_baton,
+ revision_to_update_to, update_target,
+ depth,
+ send_copyfrom_args,
+ FALSE /* ignore_ancestry */,
+ update_editor, update_baton,
+ pool, pool));
+}
+
+svn_error_t *svn_ra_do_update(svn_ra_session_t *session,
+ const svn_ra_reporter2_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_update_to,
+ const char *update_target,
+ svn_boolean_t recurse,
+ const svn_delta_editor_t *update_editor,
+ void *update_baton,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = apr_palloc(pool, sizeof(*b));
+ SVN_ERR_ASSERT(svn_path_is_empty(update_target)
+ || svn_path_is_single_path_component(update_target));
+ *reporter = &reporter_3in2_wrapper;
+ *report_baton = b;
+ return session->vtable->do_update(session,
+ &(b->reporter3), &(b->reporter3_baton),
+ revision_to_update_to, update_target,
+ SVN_DEPTH_INFINITY_OR_FILES(recurse),
+ FALSE, /* no copyfrom args */
+ FALSE /* ignore_ancestry */,
+ update_editor, update_baton,
+ pool, pool);
+}
+
+
+svn_error_t *
+svn_ra_do_switch2(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_switch_to,
+ const char *switch_target,
+ svn_depth_t depth,
+ const char *switch_url,
+ const svn_delta_editor_t *switch_editor,
+ void *switch_baton,
+ apr_pool_t *pool)
+{
+ return svn_error_trace(
+ svn_ra_do_switch3(session,
+ reporter, report_baton,
+ revision_to_switch_to, switch_target,
+ depth,
+ switch_url,
+ FALSE /* send_copyfrom_args */,
+ TRUE /* ignore_ancestry */,
+ switch_editor, switch_baton,
+ pool, pool));
+}
+
+svn_error_t *svn_ra_do_switch(svn_ra_session_t *session,
+ const svn_ra_reporter2_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_switch_to,
+ const char *switch_target,
+ svn_boolean_t recurse,
+ const char *switch_url,
+ const svn_delta_editor_t *switch_editor,
+ void *switch_baton,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = apr_palloc(pool, sizeof(*b));
+ SVN_ERR_ASSERT(svn_path_is_empty(switch_target)
+ || svn_path_is_single_path_component(switch_target));
+ *reporter = &reporter_3in2_wrapper;
+ *report_baton = b;
+ return session->vtable->do_switch(session,
+ &(b->reporter3), &(b->reporter3_baton),
+ revision_to_switch_to, switch_target,
+ SVN_DEPTH_INFINITY_OR_FILES(recurse),
+ switch_url,
+ FALSE /* send_copyfrom_args */,
+ TRUE /* ignore_ancestry */,
+ switch_editor, switch_baton,
+ pool, pool);
+}
+
+svn_error_t *svn_ra_do_status(svn_ra_session_t *session,
+ const svn_ra_reporter2_t **reporter,
+ void **report_baton,
+ const char *status_target,
+ svn_revnum_t revision,
+ svn_boolean_t recurse,
+ const svn_delta_editor_t *status_editor,
+ void *status_baton,
+ apr_pool_t *pool)
+{
+ struct reporter_3in2_baton *b = apr_palloc(pool, sizeof(*b));
+ SVN_ERR_ASSERT(svn_path_is_empty(status_target)
+ || svn_path_is_single_path_component(status_target));
+ *reporter = &reporter_3in2_wrapper;
+ *report_baton = b;
+ return session->vtable->do_status(session,
+ &(b->reporter3), &(b->reporter3_baton),
+ status_target, revision,
+ SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse),
+ status_editor, status_baton, pool);
+}
+
+svn_error_t *svn_ra_get_dir(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ apr_hash_t **dirents,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(*path != '/');
+ return session->vtable->get_dir(session, dirents, fetched_rev, props,
+ path, revision, SVN_DIRENT_ALL, pool);
+}
+
+svn_error_t *
+svn_ra_local__deprecated_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ return svn_error_trace(svn_ra_local_init(abi_version, pool, hash));
+}
+
+svn_error_t *
+svn_ra_svn__deprecated_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ return svn_error_trace(svn_ra_svn_init(abi_version, pool, hash));
+}
+
+svn_error_t *
+svn_ra_serf__deprecated_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ return svn_error_trace(svn_ra_serf_init(abi_version, pool, hash));
+}
diff --git a/subversion/libsvn_ra/deprecated.h b/subversion/libsvn_ra/deprecated.h
new file mode 100644
index 0000000..205de16
--- /dev/null
+++ b/subversion/libsvn_ra/deprecated.h
@@ -0,0 +1,60 @@
+/**
+ * @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
+ */
+
+
+
+#ifndef DEPRECATED_H
+#define DEPRECATED_H
+
+#include <apr_hash.h>
+
+#include "private/svn_editor.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Non-deprecated wrapper around svn_ra_local_init. */
+svn_error_t *
+svn_ra_local__deprecated_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash);
+
+/* Non-deprecated wrapper around svn_ra_svn_init. */
+svn_error_t *
+svn_ra_svn__deprecated_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash);
+
+/* Non-deprecated wrapper around svn_ra_serf_init. */
+svn_error_t *
+svn_ra_serf__deprecated_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DEPRECATED_H */
diff --git a/subversion/libsvn_ra/editor.c b/subversion/libsvn_ra/editor.c
new file mode 100644
index 0000000..ac969ca
--- /dev/null
+++ b/subversion/libsvn_ra/editor.c
@@ -0,0 +1,339 @@
+/*
+ * editor.c: compatibility editors
+ *
+ * ====================================================================
+ * 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 <apr_pools.h>
+
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_ra.h"
+#include "svn_delta.h"
+#include "svn_dirent_uri.h"
+
+#include "private/svn_ra_private.h"
+#include "private/svn_delta_private.h"
+#include "private/svn_editor.h"
+
+#include "ra_loader.h"
+#include "svn_private_config.h"
+
+
+struct fp_baton {
+ svn_ra__provide_props_cb_t provide_props_cb;
+ void *cb_baton;
+};
+
+struct fb_baton {
+ svn_ra__provide_base_cb_t provide_base_cb;
+ void *cb_baton;
+};
+
+/* The shims currently want a callback that provides props for a given
+ REPOS_RELPATH at a given BASE_REVISION. However, the RA Ev2 interface
+ has a callback that provides properties for the REPOS_RELPATH from any
+ revision, which is returned along with the properties.
+
+ This is a little shim to map between the prototypes. The base revision
+ for the properties is discarded, and the requested revision (from the
+ shim code) is ignored.
+
+ The shim code needs to be updated to allow for an RA-style callback
+ to fetch properties. */
+static svn_error_t *
+fetch_props(apr_hash_t **props,
+ void *baton,
+ const char *repos_relpath,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct fp_baton *fpb = baton;
+ svn_revnum_t unused_revision;
+
+ /* Ignored: BASE_REVISION. */
+
+ return svn_error_trace(fpb->provide_props_cb(props, &unused_revision,
+ fpb->cb_baton,
+ repos_relpath,
+ result_pool, scratch_pool));
+}
+
+/* See note above regarding BASE_REVISION.
+ This also pulls down the entire contents of the file stream from the
+ RA layer and stores them in a local file, returning the path.
+*/
+static svn_error_t *
+fetch_base(const char **filename,
+ void *baton,
+ const char *repos_relpath,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct fb_baton *fbb = baton;
+ svn_revnum_t unused_revision;
+ svn_stream_t *contents;
+ svn_stream_t *file_stream;
+ const char *tmp_filename;
+
+ /* Ignored: BASE_REVISION. */
+
+ SVN_ERR(fbb->provide_base_cb(&contents, &unused_revision, fbb->cb_baton,
+ repos_relpath, result_pool, scratch_pool));
+
+ SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
+
+ *filename = apr_pstrdup(result_pool, tmp_filename);
+
+
+
+ return SVN_NO_ERROR;
+}
+
+
+
+svn_error_t *
+svn_ra__use_commit_shim(svn_editor_t **editor,
+ svn_ra_session_t *session,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
+ void *cb_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const svn_delta_editor_t *deditor;
+ void *dedit_baton;
+ struct svn_delta__extra_baton *exb;
+ svn_delta__unlock_func_t unlock_func;
+ void *unlock_baton;
+ const char *repos_root;
+ const char *session_url;
+ const char *base_relpath;
+ svn_boolean_t *found_abs_paths;
+ struct fp_baton *fpb;
+
+ /* NOTE: PROVIDE_BASE_CB is currently unused by this shim. In the future,
+ we can pass it to the underlying Ev2/Ev1 shim to produce better
+ apply_txdelta drives (ie. against a base rather than <empty>). */
+
+ /* Fetch the RA provider's Ev1 commit editor. */
+ SVN_ERR(session->vtable->get_commit_editor(session, &deditor, &dedit_baton,
+ revprop_table,
+ commit_callback, commit_baton,
+ lock_tokens, keep_locks,
+ result_pool));
+
+ /* Get or calculate the appropriate repos root and base relpath. */
+ SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, scratch_pool));
+ SVN_ERR(svn_ra_get_session_url(session, &session_url, scratch_pool));
+ base_relpath = svn_uri_skip_ancestor(repos_root, session_url, scratch_pool);
+
+ /* We will assume that when the underlying Ev1 editor is finally driven
+ by the shim, that we will not need to prepend "/" to the paths. Place
+ this on the heap because it is examined much later. Set to FALSE. */
+ found_abs_paths = apr_pcalloc(result_pool, sizeof(*found_abs_paths));
+
+ /* The PROVIDE_PROPS_CB callback does not match what the shims want.
+ Let's jigger things around a little bit here. */
+ fpb = apr_palloc(result_pool, sizeof(*fpb));
+ fpb->provide_props_cb = provide_props_cb;
+ fpb->cb_baton = cb_baton;
+
+ /* Create the Ev2 editor from the Ev1 editor provided by the RA layer.
+
+ Note: GET_COPYSRC_KIND_CB is compatible in type/semantics with the
+ shim's FETCH_KIND_FUNC parameter. */
+ SVN_ERR(svn_delta__editor_from_delta(editor, &exb,
+ &unlock_func, &unlock_baton,
+ deditor, dedit_baton,
+ found_abs_paths,
+ repos_root, base_relpath,
+ cancel_func, cancel_baton,
+ get_copysrc_kind_cb, cb_baton,
+ fetch_props, fpb,
+ result_pool, scratch_pool));
+
+ /* Note: UNLOCK_FUNC and UNLOCK_BATON are unused during commit drives.
+ We can safely drop them on the floor. */
+
+ /* Since we're (currently) just wrapping an existing Ev1 editor, we have
+ to call any start_edit handler it may provide (the shim uses this to
+ invoke Ev1's open_root callback). We've got a couple of options to do
+ so: Implement a wrapper editor and call the start_edit callback upon
+ the first invocation of any of the underlying editor's functions; or,
+ just assume our consumer is going to eventually use the editor it is
+ asking for, and call the start edit callback now. For simplicity's
+ sake, we do the latter. */
+ if (exb->start_edit)
+ {
+ /* Most commit drives pass SVN_INVALID_REVNUM for the revision.
+ All calls to svn_delta_path_driver() pass SVN_INVALID_REVNUM,
+ so this is fine for any commits done via that function.
+
+ Notably, the PROPSET command passes a specific revision. Before
+ PROPSET can use the RA Ev2 interface, we may need to make this
+ revision a parameter.
+ ### what are the exact semantics? what is the meaning of the
+ ### revision passed to the Ev1->open_root() callback? */
+ SVN_ERR(exb->start_edit(exb->baton, SVN_INVALID_REVNUM));
+ }
+
+ /* Note: EXB also contains a TARGET_REVISION function, but that is not
+ used during commit operations. We can safely ignore it. (ie. it is
+ in EXB for use by paired-shims) */
+
+ return SVN_NO_ERROR;
+}
+
+
+struct wrapped_replay_baton_t {
+ svn_ra__replay_revstart_ev2_callback_t revstart_func;
+ svn_ra__replay_revfinish_ev2_callback_t revfinish_func;
+ void *replay_baton;
+
+ svn_ra_session_t *session;
+
+ svn_ra__provide_base_cb_t provide_base_cb;
+ svn_ra__provide_props_cb_t provide_props_cb;
+ void *cb_baton;
+
+ /* This will be populated by the revstart wrapper. */
+ svn_editor_t *editor;
+};
+
+static svn_error_t *
+revstart_func_wrapper(svn_revnum_t revision,
+ void *replay_baton,
+ const svn_delta_editor_t **deditor,
+ void **dedit_baton,
+ apr_hash_t *rev_props,
+ apr_pool_t *result_pool)
+{
+ struct wrapped_replay_baton_t *wrb = replay_baton;
+ const char *repos_root;
+ const char *session_url;
+ const char *base_relpath;
+ svn_boolean_t *found_abs_paths;
+ struct fp_baton *fpb;
+ struct svn_delta__extra_baton *exb;
+
+ /* Get the Ev2 editor from the original revstart func. */
+ SVN_ERR(wrb->revstart_func(revision, wrb->replay_baton, &wrb->editor,
+ rev_props, result_pool));
+
+ /* Get or calculate the appropriate repos root and base relpath. */
+ SVN_ERR(svn_ra_get_repos_root2(wrb->session, &repos_root, result_pool));
+ SVN_ERR(svn_ra_get_session_url(wrb->session, &session_url, result_pool));
+ base_relpath = svn_uri_skip_ancestor(repos_root, session_url, result_pool);
+
+ /* We will assume that when the underlying Ev1 editor is finally driven
+ by the shim, that we will not need to prepend "/" to the paths. Place
+ this on the heap because it is examined much later. Set to FALSE. */
+ found_abs_paths = apr_pcalloc(result_pool, sizeof(*found_abs_paths));
+
+ /* The PROVIDE_PROPS_CB callback does not match what the shims want.
+ Let's jigger things around a little bit here. */
+ fpb = apr_palloc(result_pool, sizeof(*fpb));
+ fpb->provide_props_cb = wrb->provide_props_cb;
+ fpb->cb_baton = wrb->cb_baton;
+
+ /* Create the extra baton. */
+ exb = apr_pcalloc(result_pool, sizeof(*exb));
+
+ /* Create the Ev1 editor from the Ev2 editor provided by the RA layer.
+
+ Note: GET_COPYSRC_KIND_CB is compatible in type/semantics with the
+ shim's FETCH_KIND_FUNC parameter. */
+ SVN_ERR(svn_delta__delta_from_editor(deditor, dedit_baton, wrb->editor,
+ NULL, NULL,
+ found_abs_paths,
+ repos_root, base_relpath,
+ fetch_props, wrb->cb_baton,
+ fetch_base, wrb->cb_baton,
+ exb, result_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+revfinish_func_wrapper(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 wrapped_replay_baton_t *wrb = replay_baton;
+
+ SVN_ERR(wrb->revfinish_func(revision, replay_baton, wrb->editor, rev_props,
+ pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra__use_replay_range_shim(svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t send_deltas,
+ svn_ra__replay_revstart_ev2_callback_t revstart_func,
+ svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
+ void *replay_baton,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ void *cb_baton,
+ apr_pool_t *scratch_pool)
+{
+ /* The basic strategy here is to wrap the callback start and finish
+ functions to appropriately return an Ev1 editor which is itself wrapped
+ from the Ev2 one the provided callbacks will give us. */
+
+ struct wrapped_replay_baton_t *wrb = apr_pcalloc(scratch_pool, sizeof(*wrb));
+
+ wrb->revstart_func = revstart_func;
+ wrb->revfinish_func = revfinish_func;
+ wrb->replay_baton = replay_baton;
+ wrb->session = session;
+
+ wrb->provide_base_cb = provide_base_cb;
+ wrb->provide_props_cb = provide_props_cb;
+ wrb->cb_baton = cb_baton;
+
+ return svn_error_trace(svn_ra_replay_range(session, start_revision,
+ end_revision, low_water_mark,
+ send_deltas,
+ revstart_func_wrapper,
+ revfinish_func_wrapper,
+ wrb, scratch_pool));
+}
diff --git a/subversion/libsvn_ra/libsvn_ra.pc.in b/subversion/libsvn_ra/libsvn_ra.pc.in
new file mode 100644
index 0000000..b7ef131
--- /dev/null
+++ b/subversion/libsvn_ra/libsvn_ra.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsvn_ra
+Description: Subversion General Repository Access Library
+Version: @PACKAGE_VERSION@
+Requires: apr-@SVN_APR_MAJOR_VERSION@
+Requires.private: libsvn_delta libsvn_subr
+Libs: -L${libdir} -lsvn_ra
+Cflags: -I${includedir}
diff --git a/subversion/libsvn_ra/ra_loader.c b/subversion/libsvn_ra/ra_loader.c
new file mode 100644
index 0000000..ac07545
--- /dev/null
+++ b/subversion/libsvn_ra/ra_loader.c
@@ -0,0 +1,1598 @@
+/*
+ * ra_loader.c: logic for loading different RA library implementations
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+/*** Includes. ***/
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+
+#include <apr.h>
+#include <apr_strings.h>
+#include <apr_pools.h>
+#include <apr_hash.h>
+#include <apr_uri.h>
+
+#include "svn_hash.h"
+#include "svn_version.h"
+#include "svn_time.h"
+#include "svn_types.h"
+#include "svn_error.h"
+#include "svn_error_codes.h"
+#include "svn_pools.h"
+#include "svn_delta.h"
+#include "svn_ra.h"
+#include "svn_xml.h"
+#include "svn_path.h"
+#include "svn_dso.h"
+#include "svn_props.h"
+#include "svn_sorts.h"
+
+#include "svn_config.h"
+#include "ra_loader.h"
+#include "deprecated.h"
+
+#include "private/svn_auth_private.h"
+#include "private/svn_ra_private.h"
+#include "svn_private_config.h"
+
+
+
+
+/* These are the URI schemes that the respective libraries *may* support.
+ * The schemes actually supported may be a subset of the schemes listed below.
+ * This can't be determine until the library is loaded.
+ * (Currently, this applies to the https scheme, which is only
+ * available if SSL is supported.) */
+static const char * const dav_schemes[] = { "http", "https", NULL };
+static const char * const svn_schemes[] = { "svn", NULL };
+static const char * const local_schemes[] = { "file", NULL };
+
+static const struct ra_lib_defn {
+ /* the name of this RA library (e.g. "neon" or "local") */
+ const char *ra_name;
+
+ const char * const *schemes;
+ /* the initialization function if linked in; otherwise, NULL */
+ svn_ra__init_func_t initfunc;
+ svn_ra_init_func_t compat_initfunc;
+} ra_libraries[] = {
+ {
+ "svn",
+ svn_schemes,
+#ifdef SVN_LIBSVN_RA_LINKS_RA_SVN
+ svn_ra_svn__init,
+ svn_ra_svn__deprecated_init
+#endif
+ },
+
+ {
+ "local",
+ local_schemes,
+#ifdef SVN_LIBSVN_RA_LINKS_RA_LOCAL
+ svn_ra_local__init,
+ svn_ra_local__deprecated_init
+#endif
+ },
+
+ {
+ "serf",
+ dav_schemes,
+#ifdef SVN_LIBSVN_RA_LINKS_RA_SERF
+ svn_ra_serf__init,
+ svn_ra_serf__deprecated_init
+#endif
+ },
+
+ /* ADD NEW RA IMPLEMENTATIONS HERE (as they're written) */
+
+ /* sentinel */
+ { NULL }
+};
+
+/* Ensure that the RA library NAME is loaded.
+ *
+ * If FUNC is non-NULL, set *FUNC to the address of the svn_ra_NAME__init
+ * function of the library.
+ *
+ * If COMPAT_FUNC is non-NULL, set *COMPAT_FUNC to the address of the
+ * svn_ra_NAME_init compatibility init function of the library.
+ *
+ * ### todo: Any RA libraries implemented from this point forward
+ * ### don't really need an svn_ra_NAME_init compatibility function.
+ * ### Currently, load_ra_module() will error if no such function is
+ * ### found, but it might be more friendly to simply set *COMPAT_FUNC
+ * ### to null (assuming COMPAT_FUNC itself is non-null).
+ */
+static svn_error_t *
+load_ra_module(svn_ra__init_func_t *func,
+ svn_ra_init_func_t *compat_func,
+ const char *ra_name, apr_pool_t *pool)
+{
+ if (func)
+ *func = NULL;
+ if (compat_func)
+ *compat_func = NULL;
+
+#if defined(SVN_USE_DSO) && APR_HAS_DSO
+ {
+ apr_dso_handle_t *dso;
+ apr_dso_handle_sym_t symbol;
+ const char *libname;
+ const char *funcname;
+ const char *compat_funcname;
+ apr_status_t status;
+
+ libname = apr_psprintf(pool, "libsvn_ra_%s-" SVN_DSO_SUFFIX_FMT,
+ ra_name, SVN_VER_MAJOR, SVN_SOVERSION);
+ funcname = apr_psprintf(pool, "svn_ra_%s__init", ra_name);
+ compat_funcname = apr_psprintf(pool, "svn_ra_%s_init", ra_name);
+
+ /* find/load the specified library */
+ SVN_ERR(svn_dso_load(&dso, libname));
+ if (! dso)
+ return SVN_NO_ERROR;
+
+ /* find the initialization routines */
+ if (func)
+ {
+ status = apr_dso_sym(&symbol, dso, funcname);
+ if (status)
+ {
+ return svn_error_wrap_apr(status,
+ _("'%s' does not define '%s()'"),
+ libname, funcname);
+ }
+
+ *func = (svn_ra__init_func_t) symbol;
+ }
+
+ if (compat_func)
+ {
+ status = apr_dso_sym(&symbol, dso, compat_funcname);
+ if (status)
+ {
+ return svn_error_wrap_apr(status,
+ _("'%s' does not define '%s()'"),
+ libname, compat_funcname);
+ }
+
+ *compat_func = (svn_ra_init_func_t) symbol;
+ }
+ }
+#endif /* APR_HAS_DSO */
+
+ return SVN_NO_ERROR;
+}
+
+/* If SCHEMES contains URL, return the scheme. Else, return NULL. */
+static const char *
+has_scheme_of(const char * const *schemes, const char *url)
+{
+ apr_size_t len;
+
+ for ( ; *schemes != NULL; ++schemes)
+ {
+ const char *scheme = *schemes;
+ len = strlen(scheme);
+ /* Case-insensitive comparison, per RFC 2396 section 3.1. Allow
+ URL to contain a trailing "+foo" section in the scheme, since
+ that's how we specify tunnel schemes in ra_svn. */
+ if (strncasecmp(scheme, url, len) == 0 &&
+ (url[len] == ':' || url[len] == '+'))
+ return scheme;
+ }
+
+ return NULL;
+}
+
+/* Return an error if RA_VERSION doesn't match the version of this library.
+ Use SCHEME in the error message to describe the library that was loaded. */
+static svn_error_t *
+check_ra_version(const svn_version_t *ra_version, const char *scheme)
+{
+ const svn_version_t *my_version = svn_ra_version();
+ if (!svn_ver_equal(my_version, ra_version))
+ return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
+ _("Mismatched RA version for '%s':"
+ " found %d.%d.%d%s,"
+ " expected %d.%d.%d%s"),
+ scheme,
+ my_version->major, my_version->minor,
+ my_version->patch, my_version->tag,
+ ra_version->major, ra_version->minor,
+ ra_version->patch, ra_version->tag);
+
+ return SVN_NO_ERROR;
+}
+
+/* -------------------------------------------------------------- */
+
+/*** Public Interfaces ***/
+
+svn_error_t *svn_ra_initialize(apr_pool_t *pool)
+{
+#if defined(SVN_USE_DSO) && APR_HAS_DSO
+ /* Ensure that DSO subsystem is initialized early as possible if
+ we're going to use it. */
+ SVN_ERR(svn_dso_initialize2());
+#endif
+ return SVN_NO_ERROR;
+}
+
+/* Please note: the implementation of svn_ra_create_callbacks is
+ * duplicated in libsvn_ra/wrapper_template.h:compat_open() . This
+ * duplication is intentional, is there to avoid a circular
+ * dependancy, and is justified in great length in the code of
+ * compat_open() in libsvn_ra/wrapper_template.h. If you modify the
+ * implementation of svn_ra_create_callbacks(), be sure to keep the
+ * code in wrapper_template.h:compat_open() in sync with your
+ * changes. */
+svn_error_t *
+svn_ra_create_callbacks(svn_ra_callbacks2_t **callbacks,
+ apr_pool_t *pool)
+{
+ *callbacks = apr_pcalloc(pool, sizeof(**callbacks));
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *svn_ra_open4(svn_ra_session_t **session_p,
+ const char **corrected_url_p,
+ const char *repos_URL,
+ const char *uuid,
+ const svn_ra_callbacks2_t *callbacks,
+ void *callback_baton,
+ apr_hash_t *config,
+ apr_pool_t *pool)
+{
+ apr_pool_t *sesspool = svn_pool_create(pool);
+ apr_pool_t *scratch_pool = svn_pool_create(sesspool);
+ svn_ra_session_t *session;
+ const struct ra_lib_defn *defn;
+ const svn_ra__vtable_t *vtable = NULL;
+ apr_uri_t repos_URI;
+ apr_status_t apr_err;
+ svn_error_t *err;
+#ifdef CHOOSABLE_DAV_MODULE
+ const char *http_library = DEFAULT_HTTP_LIBRARY;
+#endif
+ svn_auth_baton_t *auth_baton;
+
+ /* Initialize the return variable. */
+ *session_p = NULL;
+
+ apr_err = apr_uri_parse(sesspool, repos_URL, &repos_URI);
+ /* ### Should apr_uri_parse leave hostname NULL? It doesn't
+ * for "file:///" URLs, only for bogus URLs like "bogus".
+ * If this is the right behavior for apr_uri_parse, maybe we
+ * should have a svn_uri_parse wrapper. */
+ if (apr_err != APR_SUCCESS || repos_URI.hostname == NULL)
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("Illegal repository URL '%s'"),
+ repos_URL);
+
+ if (callbacks->auth_baton)
+ SVN_ERR(svn_auth__make_session_auth(&auth_baton,
+ callbacks->auth_baton, config,
+ repos_URI.hostname,
+ sesspool, scratch_pool));
+ else
+ auth_baton = NULL;
+
+#ifdef CHOOSABLE_DAV_MODULE
+ if (config)
+ {
+ svn_config_t *servers = NULL;
+ const char *server_group = NULL;
+
+ /* Grab the 'servers' config. */
+ servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS);
+ if (servers)
+ {
+ /* First, look in the global section. */
+
+ /* Find out where we're about to connect to, and
+ * try to pick a server group based on the destination. */
+ server_group = svn_config_find_group(servers, repos_URI.hostname,
+ SVN_CONFIG_SECTION_GROUPS,
+ sesspool);
+
+ /* Now, which DAV-based RA method do we want to use today? */
+ http_library
+ = svn_config_get_server_setting(servers,
+ server_group, /* NULL is OK */
+ SVN_CONFIG_OPTION_HTTP_LIBRARY,
+ DEFAULT_HTTP_LIBRARY);
+
+ if (strcmp(http_library, "serf") != 0)
+ return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
+ _("Invalid config: unknown HTTP library "
+ "'%s'"),
+ http_library);
+ }
+ }
+#endif
+
+ /* Find the library. */
+ for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
+ {
+ const char *scheme;
+
+ if ((scheme = has_scheme_of(defn->schemes, repos_URL)))
+ {
+ svn_ra__init_func_t initfunc = defn->initfunc;
+
+#ifdef CHOOSABLE_DAV_MODULE
+ if (defn->schemes == dav_schemes
+ && strcmp(defn->ra_name, http_library) != 0)
+ continue;
+#endif
+
+ if (! initfunc)
+ SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
+ scratch_pool));
+ if (! initfunc)
+ /* Library not found. */
+ continue;
+
+ SVN_ERR(initfunc(svn_ra_version(), &vtable, scratch_pool));
+
+ SVN_ERR(check_ra_version(vtable->get_version(), scheme));
+
+ if (! has_scheme_of(vtable->get_schemes(scratch_pool), repos_URL))
+ /* Library doesn't support the scheme at runtime. */
+ continue;
+
+
+ break;
+ }
+ }
+
+ if (vtable == NULL)
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("Unrecognized URL scheme for '%s'"),
+ repos_URL);
+
+ /* Create the session object. */
+ session = apr_pcalloc(sesspool, sizeof(*session));
+ session->cancel_func = callbacks->cancel_func;
+ session->cancel_baton = callback_baton;
+ session->vtable = vtable;
+ session->pool = sesspool;
+
+ /* Ask the library to open the session. */
+ err = vtable->open_session(session, corrected_url_p,
+ repos_URL,
+ callbacks, callback_baton, auth_baton,
+ config, sesspool, scratch_pool);
+
+ if (err)
+ {
+ svn_pool_destroy(sesspool); /* Includes scratch_pool */
+ if (err->apr_err == SVN_ERR_RA_SESSION_URL_MISMATCH)
+ return svn_error_trace(err);
+
+ return svn_error_createf(
+ SVN_ERR_RA_CANNOT_CREATE_SESSION, err,
+ _("Unable to connect to a repository at URL '%s'"),
+ repos_URL);
+ }
+
+ /* If the session open stuff detected a server-provided URL
+ correction (a 301 or 302 redirect response during the initial
+ OPTIONS request), then kill the session so the caller can decide
+ what to do. */
+ if (corrected_url_p && *corrected_url_p)
+ {
+ /* *session_p = NULL; */
+ *corrected_url_p = apr_pstrdup(pool, *corrected_url_p);
+ svn_pool_destroy(sesspool); /* Includes scratch_pool */
+ return SVN_NO_ERROR;
+ }
+
+ if (vtable->set_svn_ra_open)
+ SVN_ERR(vtable->set_svn_ra_open(session, svn_ra_open4));
+
+ /* Check the UUID. */
+ if (uuid)
+ {
+ const char *repository_uuid;
+
+ SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool));
+ if (strcmp(uuid, repository_uuid) != 0)
+ {
+ /* Duplicate the uuid as it is allocated in sesspool */
+ repository_uuid = apr_pstrdup(pool, repository_uuid);
+ svn_pool_destroy(sesspool); /* includes scratch_pool */
+ return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
+ _("Repository UUID '%s' doesn't match "
+ "expected UUID '%s'"),
+ repository_uuid, uuid);
+ }
+ }
+
+ svn_pool_destroy(scratch_pool);
+ *session_p = session;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra__dup_session(svn_ra_session_t **new_session,
+ svn_ra_session_t *old_session,
+ const char *session_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_session_t *session;
+
+ if (session_url)
+ {
+ const char *dummy;
+
+ /* This verifies in new_session_url is in the repository */
+ SVN_ERR(svn_ra_get_path_relative_to_root(old_session,
+ &dummy,
+ session_url,
+ scratch_pool));
+ }
+ else
+ SVN_ERR(svn_ra_get_session_url(old_session, &session_url, scratch_pool));
+
+ /* Create the session object. */
+ session = apr_pcalloc(result_pool, sizeof(*session));
+ session->cancel_func = old_session->cancel_func;
+ session->cancel_baton = old_session->cancel_baton;
+ session->vtable = old_session->vtable;
+ session->pool = result_pool;
+
+ SVN_ERR(old_session->vtable->dup_session(session,
+ old_session,
+ session_url,
+ result_pool,
+ scratch_pool));
+
+ if (session->vtable->set_svn_ra_open)
+ SVN_ERR(session->vtable->set_svn_ra_open(session, svn_ra_open4));
+
+ *new_session = session;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *svn_ra_reparent(svn_ra_session_t *session,
+ const char *url,
+ apr_pool_t *pool)
+{
+ const char *repos_root;
+
+ /* Make sure the new URL is in the same repository, so that the
+ implementations don't have to do it. */
+ SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, pool));
+ if (! svn_uri__is_ancestor(repos_root, url))
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("'%s' isn't in the same repository as '%s'"),
+ url, repos_root);
+
+ return session->vtable->reparent(session, url, pool);
+}
+
+svn_error_t *svn_ra_get_session_url(svn_ra_session_t *session,
+ const char **url,
+ apr_pool_t *pool)
+{
+ return session->vtable->get_session_url(session, url, pool);
+}
+
+svn_error_t *svn_ra_get_path_relative_to_session(svn_ra_session_t *session,
+ const char **rel_path,
+ const char *url,
+ apr_pool_t *pool)
+{
+ const char *sess_url;
+
+ SVN_ERR(session->vtable->get_session_url(session, &sess_url, pool));
+ *rel_path = svn_uri_skip_ancestor(sess_url, url, pool);
+ if (! *rel_path)
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("'%s' isn't a child of session URL '%s'"),
+ url, sess_url);
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *svn_ra_get_path_relative_to_root(svn_ra_session_t *session,
+ const char **rel_path,
+ const char *url,
+ apr_pool_t *pool)
+{
+ const char *root_url;
+
+ SVN_ERR(session->vtable->get_repos_root(session, &root_url, pool));
+ *rel_path = svn_uri_skip_ancestor(root_url, url, pool);
+ if (! *rel_path)
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("'%s' isn't a child of repository root "
+ "URL '%s'"),
+ url, root_url);
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *svn_ra_get_latest_revnum(svn_ra_session_t *session,
+ svn_revnum_t *latest_revnum,
+ apr_pool_t *pool)
+{
+ return session->vtable->get_latest_revnum(session, latest_revnum, pool);
+}
+
+svn_error_t *svn_ra_get_dated_revision(svn_ra_session_t *session,
+ svn_revnum_t *revision,
+ apr_time_t tm,
+ apr_pool_t *pool)
+{
+ return session->vtable->get_dated_revision(session, revision, tm, pool);
+}
+
+svn_error_t *svn_ra_change_rev_prop2(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ const char *name,
+ const svn_string_t *const *old_value_p,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
+
+ /* If an old value was specified, make sure the server supports
+ * specifying it. */
+ if (old_value_p)
+ {
+ svn_boolean_t has_atomic_revprops;
+
+ SVN_ERR(svn_ra_has_capability(session, &has_atomic_revprops,
+ SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+ pool));
+
+ if (!has_atomic_revprops)
+ /* API violation. (Should be an ASSERT, but gstein talked me
+ * out of it.) */
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Specifying 'old_value_p' is not allowed when "
+ "the '%s' capability is not advertised, and "
+ "could indicate a bug in your client"),
+ SVN_RA_CAPABILITY_ATOMIC_REVPROPS);
+ }
+
+ return session->vtable->change_rev_prop(session, rev, name,
+ old_value_p, value, pool);
+}
+
+svn_error_t *svn_ra_rev_proplist(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ apr_hash_t **props,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
+ return session->vtable->rev_proplist(session, rev, props, pool);
+}
+
+svn_error_t *svn_ra_rev_prop(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ const char *name,
+ svn_string_t **value,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
+ return session->vtable->rev_prop(session, rev, name, value, pool);
+}
+
+svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session,
+ const svn_delta_editor_t **editor,
+ void **edit_baton,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *pool)
+{
+ return session->vtable->get_commit_editor(session, editor, edit_baton,
+ revprop_table,
+ commit_callback, commit_baton,
+ lock_tokens, keep_locks, pool);
+}
+
+svn_error_t *svn_ra_get_file(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ svn_stream_t *stream,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ return session->vtable->get_file(session, path, revision, stream,
+ fetched_rev, props, pool);
+}
+
+svn_error_t *svn_ra_get_dir2(svn_ra_session_t *session,
+ apr_hash_t **dirents,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ const char *path,
+ svn_revnum_t revision,
+ apr_uint32_t dirent_fields,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ return session->vtable->get_dir(session, dirents, fetched_rev, props,
+ path, revision, dirent_fields, pool);
+}
+
+svn_error_t *
+svn_ra_list(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ const apr_array_header_t *patterns,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_ra_dirent_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ if (!session->vtable->list)
+ return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+
+ SVN_ERR(svn_ra__assert_capable_server(session, SVN_RA_CAPABILITY_LIST,
+ NULL, scratch_pool));
+
+ return session->vtable->list(session, path, revision, patterns, depth,
+ dirent_fields, receiver, receiver_baton,
+ scratch_pool);
+}
+
+svn_error_t *svn_ra_get_mergeinfo(svn_ra_session_t *session,
+ svn_mergeinfo_catalog_t *catalog,
+ const apr_array_header_t *paths,
+ svn_revnum_t revision,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t include_descendants,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+ int i;
+
+ /* Validate path format. */
+ for (i = 0; i < paths->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(paths, i, const char *);
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ }
+
+ /* Check server Merge Tracking capability. */
+ err = svn_ra__assert_mergeinfo_capable_server(session, NULL, pool);
+ if (err)
+ {
+ *catalog = NULL;
+ return err;
+ }
+
+ return session->vtable->get_mergeinfo(session, catalog, paths,
+ revision, inherit,
+ include_descendants, pool);
+}
+
+svn_error_t *
+svn_ra_do_update3(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_update_to,
+ const char *update_target,
+ svn_depth_t depth,
+ svn_boolean_t send_copyfrom_args,
+ svn_boolean_t ignore_ancestry,
+ const svn_delta_editor_t *update_editor,
+ void *update_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR_ASSERT(svn_path_is_empty(update_target)
+ || svn_path_is_single_path_component(update_target));
+ return session->vtable->do_update(session,
+ reporter, report_baton,
+ revision_to_update_to, update_target,
+ depth, send_copyfrom_args,
+ ignore_ancestry,
+ update_editor, update_baton,
+ result_pool, scratch_pool);
+}
+
+svn_error_t *
+svn_ra_do_switch3(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_switch_to,
+ const char *switch_target,
+ svn_depth_t depth,
+ const char *switch_url,
+ svn_boolean_t send_copyfrom_args,
+ svn_boolean_t ignore_ancestry,
+ const svn_delta_editor_t *switch_editor,
+ void *switch_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR_ASSERT(svn_path_is_empty(switch_target)
+ || svn_path_is_single_path_component(switch_target));
+ return session->vtable->do_switch(session,
+ reporter, report_baton,
+ revision_to_switch_to, switch_target,
+ depth, switch_url,
+ send_copyfrom_args,
+ ignore_ancestry,
+ switch_editor,
+ switch_baton,
+ result_pool, scratch_pool);
+}
+
+svn_error_t *svn_ra_do_status2(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ const char *status_target,
+ svn_revnum_t revision,
+ svn_depth_t depth,
+ const svn_delta_editor_t *status_editor,
+ void *status_baton,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_path_is_empty(status_target)
+ || svn_path_is_single_path_component(status_target));
+ return session->vtable->do_status(session,
+ reporter, report_baton,
+ status_target, revision, depth,
+ status_editor, status_baton, pool);
+}
+
+svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision,
+ const char *diff_target,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_boolean_t text_deltas,
+ const char *versus_url,
+ const svn_delta_editor_t *diff_editor,
+ void *diff_baton,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_path_is_empty(diff_target)
+ || svn_path_is_single_path_component(diff_target));
+ return session->vtable->do_diff(session,
+ reporter, report_baton,
+ revision, diff_target,
+ depth, ignore_ancestry,
+ text_deltas, versus_url, diff_editor,
+ diff_baton, pool);
+}
+
+svn_error_t *svn_ra_get_log2(svn_ra_session_t *session,
+ const apr_array_header_t *paths,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ int limit,
+ svn_boolean_t discover_changed_paths,
+ svn_boolean_t strict_node_history,
+ svn_boolean_t include_merged_revisions,
+ const apr_array_header_t *revprops,
+ svn_log_entry_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
+{
+ if (paths)
+ {
+ int i;
+ for (i = 0; i < paths->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(paths, i, const char *);
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ }
+ }
+
+ if (include_merged_revisions)
+ SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
+
+ return session->vtable->get_log(session, paths, start, end, limit,
+ discover_changed_paths, strict_node_history,
+ include_merged_revisions, revprops,
+ receiver, receiver_baton, pool);
+}
+
+svn_error_t *svn_ra_check_path(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ svn_node_kind_t *kind,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ return session->vtable->check_path(session, path, revision, kind, pool);
+}
+
+svn_error_t *svn_ra_stat(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ svn_dirent_t **dirent,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ err = session->vtable->stat(session, path, revision, dirent, pool);
+
+ /* svnserve before 1.2 doesn't support the above, so fall back on
+ a far less efficient, but still correct method. */
+ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
+ {
+ /* ### TODO: Find out if we can somehow move this code in libsvn_ra_svn.
+ */
+ apr_pool_t *scratch_pool = svn_pool_create(pool);
+ svn_node_kind_t kind;
+
+ svn_error_clear(err);
+
+ SVN_ERR(svn_ra_check_path(session, path, revision, &kind, scratch_pool));
+
+ if (kind != svn_node_none)
+ {
+ const char *repos_root_url;
+ const char *session_url;
+
+ SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url,
+ scratch_pool));
+ SVN_ERR(svn_ra_get_session_url(session, &session_url,
+ scratch_pool));
+
+ if (!svn_path_is_empty(path))
+ session_url = svn_path_url_add_component2(session_url, path,
+ scratch_pool);
+
+ if (strcmp(session_url, repos_root_url) != 0)
+ {
+ svn_ra_session_t *parent_session;
+ apr_hash_t *parent_ents;
+ const char *parent_url, *base_name;
+
+ /* Open another session to the path's parent. This server
+ doesn't support svn_ra_reparent anyway, so don't try it. */
+ svn_uri_split(&parent_url, &base_name, session_url,
+ scratch_pool);
+
+ SVN_ERR(svn_ra__dup_session(&parent_session, session, parent_url,
+ scratch_pool, scratch_pool));
+
+ /* Get all parent's entries, no props. */
+ SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL,
+ NULL, "", revision, SVN_DIRENT_ALL,
+ scratch_pool));
+
+ /* Get the relevant entry. */
+ *dirent = svn_hash_gets(parent_ents, base_name);
+
+ if (*dirent)
+ *dirent = svn_dirent_dup(*dirent, pool);
+ }
+ else
+ {
+ apr_hash_t *props;
+ const svn_string_t *val;
+
+ /* We can't get the directory entry for the repository root,
+ but we can still get the information we want.
+ The created-rev of the repository root must, by definition,
+ be rev. */
+ *dirent = apr_pcalloc(pool, sizeof(**dirent));
+ (*dirent)->kind = kind;
+ (*dirent)->size = SVN_INVALID_FILESIZE;
+
+ SVN_ERR(svn_ra_get_dir2(session, NULL, NULL, &props,
+ "", revision, 0 /* no dirent fields */,
+ scratch_pool));
+ (*dirent)->has_props = (apr_hash_count(props) != 0);
+
+ (*dirent)->created_rev = revision;
+
+ SVN_ERR(svn_ra_rev_proplist(session, revision, &props,
+ scratch_pool));
+
+ val = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
+ if (val)
+ SVN_ERR(svn_time_from_cstring(&(*dirent)->time, val->data,
+ scratch_pool));
+
+ val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR);
+ (*dirent)->last_author = val ? apr_pstrdup(pool, val->data)
+ : NULL;
+ }
+ }
+ else
+ *dirent = NULL;
+
+ svn_pool_clear(scratch_pool);
+ }
+ else
+ SVN_ERR(err);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *svn_ra_get_uuid2(svn_ra_session_t *session,
+ const char **uuid,
+ apr_pool_t *pool)
+{
+ SVN_ERR(session->vtable->get_uuid(session, uuid, pool));
+ *uuid = *uuid ? apr_pstrdup(pool, *uuid) : NULL;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *svn_ra_get_uuid(svn_ra_session_t *session,
+ const char **uuid,
+ apr_pool_t *pool)
+{
+ return session->vtable->get_uuid(session, uuid, pool);
+}
+
+svn_error_t *svn_ra_get_repos_root2(svn_ra_session_t *session,
+ const char **url,
+ apr_pool_t *pool)
+{
+ SVN_ERR(session->vtable->get_repos_root(session, url, pool));
+ *url = *url ? apr_pstrdup(pool, *url) : NULL;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *svn_ra_get_repos_root(svn_ra_session_t *session,
+ const char **url,
+ apr_pool_t *pool)
+{
+ return session->vtable->get_repos_root(session, url, pool);
+}
+
+svn_error_t *svn_ra_get_locations(svn_ra_session_t *session,
+ apr_hash_t **locations,
+ const char *path,
+ svn_revnum_t peg_revision,
+ const apr_array_header_t *location_revisions,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(peg_revision));
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ err = session->vtable->get_locations(session, locations, path,
+ peg_revision, location_revisions, pool);
+ if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
+ {
+ svn_error_clear(err);
+
+ /* Do it the slow way, using get-logs, for older servers. */
+ err = svn_ra__locations_from_log(session, locations, path,
+ peg_revision, location_revisions,
+ pool);
+ }
+ return err;
+}
+
+svn_error_t *
+svn_ra_get_location_segments(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_location_segment_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ err = session->vtable->get_location_segments(session, path, peg_revision,
+ start_rev, end_rev,
+ receiver, receiver_baton, pool);
+ if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
+ {
+ svn_error_clear(err);
+
+ /* Do it the slow way, using get-logs, for older servers. */
+ err = svn_ra__location_segments_from_log(session, path,
+ peg_revision, start_rev,
+ end_rev, receiver,
+ receiver_baton, pool);
+ }
+ return err;
+}
+
+svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ svn_boolean_t include_merged_revisions,
+ svn_file_rev_handler_t handler,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+
+ if (include_merged_revisions)
+ SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
+
+ if (start > end || !SVN_IS_VALID_REVNUM(start))
+ SVN_ERR(
+ svn_ra__assert_capable_server(session,
+ SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
+ NULL,
+ pool));
+
+ err = session->vtable->get_file_revs(session, path, start, end,
+ include_merged_revisions,
+ handler, handler_baton, pool);
+ if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
+ && !include_merged_revisions)
+ {
+ svn_error_clear(err);
+
+ /* Do it the slow way, using get-logs, for older servers. */
+ err = svn_ra__file_revs_from_log(session, path, start, end,
+ handler, handler_baton, pool);
+ }
+ return svn_error_trace(err);
+}
+
+svn_error_t *svn_ra_lock(svn_ra_session_t *session,
+ apr_hash_t *path_revs,
+ const char *comment,
+ svn_boolean_t steal_lock,
+ svn_ra_lock_callback_t lock_func,
+ void *lock_baton,
+ apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ }
+
+ if (comment && ! svn_xml_is_xml_safe(comment, strlen(comment)))
+ return svn_error_create
+ (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
+ _("Lock comment contains illegal characters"));
+
+ return session->vtable->lock(session, path_revs, comment, steal_lock,
+ lock_func, lock_baton, pool);
+}
+
+svn_error_t *svn_ra_unlock(svn_ra_session_t *session,
+ apr_hash_t *path_tokens,
+ svn_boolean_t break_lock,
+ svn_ra_lock_callback_t lock_func,
+ void *lock_baton,
+ apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ }
+
+ return session->vtable->unlock(session, path_tokens, break_lock,
+ lock_func, lock_baton, pool);
+}
+
+svn_error_t *svn_ra_get_lock(svn_ra_session_t *session,
+ svn_lock_t **lock,
+ const char *path,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ return session->vtable->get_lock(session, lock, path, pool);
+}
+
+svn_error_t *svn_ra_get_locks2(svn_ra_session_t *session,
+ apr_hash_t **locks,
+ const char *path,
+ svn_depth_t depth,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+ SVN_ERR_ASSERT((depth == svn_depth_empty) ||
+ (depth == svn_depth_files) ||
+ (depth == svn_depth_immediates) ||
+ (depth == svn_depth_infinity));
+ return session->vtable->get_locks(session, locks, path, depth, pool);
+}
+
+svn_error_t *svn_ra_get_locks(svn_ra_session_t *session,
+ apr_hash_t **locks,
+ const char *path,
+ apr_pool_t *pool)
+{
+ return svn_ra_get_locks2(session, locks, path, svn_depth_infinity, pool);
+}
+
+svn_error_t *svn_ra_replay(svn_ra_session_t *session,
+ svn_revnum_t revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t text_deltas,
+ const svn_delta_editor_t *editor,
+ void *edit_baton,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)
+ && SVN_IS_VALID_REVNUM(low_water_mark));
+ return session->vtable->replay(session, revision, low_water_mark,
+ text_deltas, editor, edit_baton, pool);
+}
+
+svn_error_t *
+svn_ra__replay_ev2(svn_ra_session_t *session,
+ svn_revnum_t revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t send_deltas,
+ svn_editor_t *editor,
+ apr_pool_t *scratch_pool)
+{
+ SVN__NOT_IMPLEMENTED();
+}
+
+static svn_error_t *
+replay_range_from_replays(svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t text_deltas,
+ svn_ra_replay_revstart_callback_t revstart_func,
+ svn_ra_replay_revfinish_callback_t revfinish_func,
+ void *replay_baton,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ svn_revnum_t rev;
+
+ for (rev = start_revision ; rev <= end_revision ; rev++)
+ {
+ const svn_delta_editor_t *editor;
+ void *edit_baton;
+ apr_hash_t *rev_props;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_ra_rev_proplist(session, rev, &rev_props, iterpool));
+
+ SVN_ERR(revstart_func(rev, replay_baton,
+ &editor, &edit_baton,
+ rev_props,
+ iterpool));
+ SVN_ERR(svn_ra_replay(session, rev, low_water_mark,
+ text_deltas, editor, edit_baton,
+ iterpool));
+ SVN_ERR(revfinish_func(rev, replay_baton,
+ editor, edit_baton,
+ rev_props,
+ iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_replay_range(svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t text_deltas,
+ svn_ra_replay_revstart_callback_t revstart_func,
+ svn_ra_replay_revfinish_callback_t revfinish_func,
+ void *replay_baton,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_revision)
+ && SVN_IS_VALID_REVNUM(end_revision)
+ && start_revision <= end_revision
+ && SVN_IS_VALID_REVNUM(low_water_mark));
+
+ err =
+ session->vtable->replay_range(session, start_revision, end_revision,
+ low_water_mark, text_deltas,
+ revstart_func, revfinish_func,
+ replay_baton, pool);
+
+ if (!err || (err && (err->apr_err != SVN_ERR_RA_NOT_IMPLEMENTED)))
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ return svn_error_trace(replay_range_from_replays(session, start_revision,
+ end_revision,
+ low_water_mark,
+ text_deltas,
+ revstart_func,
+ revfinish_func,
+ replay_baton, pool));
+}
+
+svn_error_t *
+svn_ra__replay_range_ev2(svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t send_deltas,
+ svn_ra__replay_revstart_ev2_callback_t revstart_func,
+ svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
+ void *replay_baton,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
+ void *cb_baton,
+ apr_pool_t *scratch_pool)
+{
+ if (session->vtable->replay_range_ev2 == NULL)
+ {
+ /* The specific RA layer does not have an implementation. Use our
+ default shim over the normal replay editor. */
+
+ /* This will call the Ev1 replay range handler with modified
+ callbacks. */
+ return svn_error_trace(svn_ra__use_replay_range_shim(
+ session,
+ start_revision,
+ end_revision,
+ low_water_mark,
+ send_deltas,
+ revstart_func,
+ revfinish_func,
+ replay_baton,
+ provide_base_cb,
+ provide_props_cb,
+ cb_baton,
+ scratch_pool));
+ }
+
+ return svn_error_trace(session->vtable->replay_range_ev2(
+ session, start_revision, end_revision,
+ low_water_mark, send_deltas, revstart_func,
+ revfinish_func, replay_baton, scratch_pool));
+}
+
+svn_error_t *svn_ra_has_capability(svn_ra_session_t *session,
+ svn_boolean_t *has,
+ const char *capability,
+ apr_pool_t *pool)
+{
+ return session->vtable->has_capability(session, has, capability, pool);
+}
+
+svn_error_t *
+svn_ra_get_deleted_rev(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t *revision_deleted,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ /* Path must be relative. */
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+
+ if (!SVN_IS_VALID_REVNUM(peg_revision))
+ return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("Invalid peg revision %ld"), peg_revision);
+ if (!SVN_IS_VALID_REVNUM(end_revision))
+ return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("Invalid end revision %ld"), end_revision);
+ if (end_revision <= peg_revision)
+ return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("Peg revision must precede end revision"));
+ err = session->vtable->get_deleted_rev(session, path,
+ peg_revision,
+ end_revision,
+ revision_deleted,
+ pool);
+ if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
+ {
+ svn_error_clear(err);
+
+ /* Do it the slow way, using get-logs, for older servers. */
+ err = svn_ra__get_deleted_rev_from_log(session, path, peg_revision,
+ end_revision, revision_deleted,
+ pool);
+ }
+ return err;
+}
+
+svn_error_t *
+svn_ra_get_inherited_props(svn_ra_session_t *session,
+ apr_array_header_t **iprops,
+ const char *path,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ /* Path must be relative. */
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
+
+ err = session->vtable->get_inherited_props(session, iprops, path,
+ revision, result_pool,
+ scratch_pool);
+
+ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
+ {
+ svn_error_clear(err);
+
+ /* Fallback for legacy servers. */
+ SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops,
+ result_pool, scratch_pool));
+ }
+ else
+ SVN_ERR(err);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra__get_commit_ev2(svn_editor_t **editor,
+ svn_ra_session_t *session,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
+ void *cb_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if (session->vtable->get_commit_ev2 == NULL)
+ {
+ /* The specific RA layer does not have an implementation. Use our
+ default shim over the normal commit editor. */
+
+ return svn_error_trace(svn_ra__use_commit_shim(
+ editor,
+ session,
+ revprop_table,
+ commit_callback, commit_baton,
+ lock_tokens,
+ keep_locks,
+ provide_base_cb,
+ provide_props_cb,
+ get_copysrc_kind_cb,
+ cb_baton,
+ session->cancel_func, session->cancel_baton,
+ result_pool, scratch_pool));
+ }
+
+ /* Note: no need to remap the callback for Ev2. RA layers providing this
+ vtable entry should completely fill in commit_info. */
+
+ return svn_error_trace(session->vtable->get_commit_ev2(
+ editor,
+ session,
+ revprop_table,
+ commit_callback, commit_baton,
+ lock_tokens,
+ keep_locks,
+ provide_base_cb,
+ provide_props_cb,
+ get_copysrc_kind_cb,
+ cb_baton,
+ session->cancel_func, session->cancel_baton,
+ result_pool, scratch_pool));
+}
+
+
+svn_error_t *
+svn_ra_print_modules(svn_stringbuf_t *output,
+ apr_pool_t *pool)
+{
+ const struct ra_lib_defn *defn;
+ const char * const *schemes;
+ svn_ra__init_func_t initfunc;
+ const svn_ra__vtable_t *vtable;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
+ {
+ char *line;
+
+ svn_pool_clear(iterpool);
+
+ initfunc = defn->initfunc;
+ if (! initfunc)
+ SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
+ iterpool));
+
+ if (initfunc)
+ {
+ SVN_ERR(initfunc(svn_ra_version(), &vtable, iterpool));
+
+ SVN_ERR(check_ra_version(vtable->get_version(), defn->ra_name));
+
+ /* Note: if you change the formatting of the description,
+ bear in mind that ra_svn's description has multiple lines when
+ built with SASL. */
+ line = apr_psprintf(iterpool, "* ra_%s : %s\n",
+ defn->ra_name,
+ vtable->get_description(iterpool));
+ svn_stringbuf_appendcstr(output, line);
+
+ for (schemes = vtable->get_schemes(iterpool); *schemes != NULL;
+ ++schemes)
+ {
+ line = apr_psprintf(iterpool, _(" - handles '%s' scheme\n"),
+ *schemes);
+ svn_stringbuf_appendcstr(output, line);
+ }
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_print_ra_libraries(svn_stringbuf_t **descriptions,
+ void *ra_baton,
+ apr_pool_t *pool)
+{
+ *descriptions = svn_stringbuf_create_empty(pool);
+ return svn_ra_print_modules(*descriptions, pool);
+}
+
+
+svn_error_t *
+svn_ra__register_editor_shim_callbacks(svn_ra_session_t *session,
+ svn_delta_shim_callbacks_t *callbacks)
+{
+ SVN_ERR(session->vtable->register_editor_shim_callbacks(session, callbacks));
+ return SVN_NO_ERROR;
+}
+
+
+/* Return the library version number. */
+const svn_version_t *
+svn_ra_version(void)
+{
+ SVN_VERSION_BODY;
+}
+
+
+/*** Compatibility Interfaces **/
+svn_error_t *
+svn_ra_init_ra_libs(void **ra_baton,
+ apr_pool_t *pool)
+{
+ *ra_baton = pool;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_get_ra_library(svn_ra_plugin_t **library,
+ void *ra_baton,
+ const char *url,
+ apr_pool_t *pool)
+{
+ const struct ra_lib_defn *defn;
+ apr_pool_t *load_pool = ra_baton;
+ apr_hash_t *ht = apr_hash_make(pool);
+
+ /* Figure out which RA library key matches URL. */
+ for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
+ {
+ const char *scheme;
+ if ((scheme = has_scheme_of(defn->schemes, url)))
+ {
+ svn_ra_init_func_t compat_initfunc = defn->compat_initfunc;
+
+ if (! compat_initfunc)
+ {
+ SVN_ERR(load_ra_module
+ (NULL, &compat_initfunc, defn->ra_name, load_pool));
+ }
+ if (! compat_initfunc)
+ {
+ continue;
+ }
+
+ SVN_ERR(compat_initfunc(SVN_RA_ABI_VERSION, load_pool, ht));
+
+ *library = svn_hash_gets(ht, scheme);
+
+ /* The library may support just a subset of the schemes listed,
+ so we have to check here too. */
+ if (! *library)
+ break;
+
+ return check_ra_version((*library)->get_version(), scheme);
+ }
+ }
+
+ /* Couldn't find a match... */
+ *library = NULL;
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("Unrecognized URL scheme '%s'"), url);
+}
+
+/* For each libsvn_ra_foo library that is not linked in, provide a default
+ implementation for svn_ra_foo_init which returns a "not implemented"
+ error. */
+
+#ifndef SVN_LIBSVN_RA_LINKS_RA_NEON
+svn_error_t *
+svn_ra_dav_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
+}
+#endif /* ! SVN_LIBSVN_RA_LINKS_RA_NEON */
+
+#ifndef SVN_LIBSVN_RA_LINKS_RA_SVN
+svn_error_t *
+svn_ra_svn_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
+}
+#endif /* ! SVN_LIBSVN_RA_LINKS_RA_SVN */
+
+#ifndef SVN_LIBSVN_RA_LINKS_RA_LOCAL
+svn_error_t *
+svn_ra_local_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
+}
+#endif /* ! SVN_LIBSVN_RA_LINKS_RA_LOCAL */
+
+#ifndef SVN_LIBSVN_RA_LINKS_RA_SERF
+svn_error_t *
+svn_ra_serf_init(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
+}
+#endif /* ! SVN_LIBSVN_RA_LINKS_RA_SERF */
diff --git a/subversion/libsvn_ra/ra_loader.h b/subversion/libsvn_ra/ra_loader.h
new file mode 100644
index 0000000..c20b5bf
--- /dev/null
+++ b/subversion/libsvn_ra/ra_loader.h
@@ -0,0 +1,599 @@
+/**
+ * @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 ra_loader.h
+ * @brief structures related to repository access, private to libsvn_ra and the
+ * RA implementation libraries.
+ */
+
+
+
+#ifndef LIBSVN_RA_RA_LOADER_H
+#define LIBSVN_RA_RA_LOADER_H
+
+#include "svn_ra.h"
+
+#include "private/svn_ra_private.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Prototype of most recent version of svn_ra_openX() api, optionally
+ handed to the ra api to allow opening other ra sessions. */
+typedef svn_error_t * (*svn_ra__open_func_t)(svn_ra_session_t **session_p,
+ const char **corrected_url,
+ const char *repos_URL,
+ const char *uuid,
+ const svn_ra_callbacks2_t *callbacks,
+ void *callback_baton,
+ apr_hash_t *config,
+ apr_pool_t *pool);
+
+/* The RA layer vtable. */
+typedef struct svn_ra__vtable_t {
+ /* This field should always remain first in the vtable. */
+ const svn_version_t *(*get_version)(void);
+
+ /* Return a short description of the RA implementation, as a localized
+ * string. */
+ const char *(*get_description)(apr_pool_t *pool);
+
+ /* Return a list of actual URI schemes supported by this implementation.
+ * The returned array is NULL-terminated. */
+ const char * const *(*get_schemes)(apr_pool_t *pool);
+
+ /* Implementations of the public API functions. */
+
+ /* See svn_ra_open4(). */
+ /* All fields in SESSION, except priv, have been initialized by the
+ time this is called. SESSION->priv may be set by this function. */
+ svn_error_t *(*open_session)(svn_ra_session_t *session,
+ const char **corrected_url,
+ const char *session_URL,
+ const svn_ra_callbacks2_t *callbacks,
+ void *callback_baton,
+ svn_auth_baton_t *auth_baton,
+ apr_hash_t *config,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+ /* Backs svn_ra_dup_session */
+ svn_error_t * (*dup_session)(svn_ra_session_t *new_session,
+ svn_ra_session_t *old_session,
+ const char *new_session_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+ /* See svn_ra_reparent(). */
+ /* URL is guaranteed to have what get_repos_root() returns as a prefix. */
+ svn_error_t *(*reparent)(svn_ra_session_t *session,
+ const char *url,
+ apr_pool_t *pool);
+ /* See svn_ra_get_session_url(). */
+ svn_error_t *(*get_session_url)(svn_ra_session_t *session,
+ const char **url,
+ apr_pool_t *pool);
+ /* See svn_ra_get_latest_revnum(). */
+ svn_error_t *(*get_latest_revnum)(svn_ra_session_t *session,
+ svn_revnum_t *latest_revnum,
+ apr_pool_t *pool);
+ /* See svn_ra_get_dated_revision(). */
+ svn_error_t *(*get_dated_revision)(svn_ra_session_t *session,
+ svn_revnum_t *revision,
+ apr_time_t tm,
+ apr_pool_t *pool);
+ /* See svn_ra_change_rev_prop2(). */
+ svn_error_t *(*change_rev_prop)(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ const char *name,
+ const svn_string_t *const *old_value_p,
+ const svn_string_t *value,
+ apr_pool_t *pool);
+
+ /* See svn_ra_rev_proplist(). */
+ svn_error_t *(*rev_proplist)(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ apr_hash_t **props,
+ apr_pool_t *pool);
+ /* See svn_ra_rev_prop(). */
+ svn_error_t *(*rev_prop)(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ const char *name,
+ svn_string_t **value,
+ apr_pool_t *pool);
+ /* See svn_ra_get_commit_editor3(). */
+ svn_error_t *(*get_commit_editor)(svn_ra_session_t *session,
+ const svn_delta_editor_t **editor,
+ void **edit_baton,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t callback,
+ void *callback_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *pool);
+ /* See svn_ra_get_file(). */
+ svn_error_t *(*get_file)(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ svn_stream_t *stream,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ apr_pool_t *pool);
+ /* See svn_ra_get_dir2(). */
+ svn_error_t *(*get_dir)(svn_ra_session_t *session,
+ apr_hash_t **dirents,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ const char *path,
+ svn_revnum_t revision,
+ apr_uint32_t dirent_fields,
+ apr_pool_t *pool);
+ /* See svn_ra_get_mergeinfo(). */
+ svn_error_t *(*get_mergeinfo)(svn_ra_session_t *session,
+ svn_mergeinfo_catalog_t *mergeinfo,
+ const apr_array_header_t *paths,
+ svn_revnum_t revision,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t include_merged_revisions,
+ apr_pool_t *pool);
+ /* See svn_ra_do_update3(). */
+ svn_error_t *(*do_update)(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_update_to,
+ const char *update_target,
+ svn_depth_t depth,
+ svn_boolean_t send_copyfrom_args,
+ svn_boolean_t ignore_ancestry,
+ const svn_delta_editor_t *update_editor,
+ void *update_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+ /* See svn_ra_do_switch3(). */
+ svn_error_t *(*do_switch)(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_switch_to,
+ const char *switch_target,
+ svn_depth_t depth,
+ const char *switch_url,
+ svn_boolean_t send_copyfrom_args,
+ svn_boolean_t ignore_ancestry,
+ const svn_delta_editor_t *switch_editor,
+ void *switch_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+ /* See svn_ra_do_status2(). */
+ svn_error_t *(*do_status)(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ const char *status_target,
+ svn_revnum_t revision,
+ svn_depth_t depth,
+ const svn_delta_editor_t *status_editor,
+ void *status_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_do_diff3(). */
+ svn_error_t *(*do_diff)(svn_ra_session_t *session,
+ const svn_ra_reporter3_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision,
+ const char *diff_target,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_boolean_t text_deltas,
+ const char *versus_url,
+ const svn_delta_editor_t *diff_editor,
+ void *diff_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_get_log2(). */
+ svn_error_t *(*get_log)(svn_ra_session_t *session,
+ const apr_array_header_t *paths,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ int limit,
+ svn_boolean_t discover_changed_paths,
+ svn_boolean_t strict_node_history,
+ svn_boolean_t include_merged_revisions,
+ const apr_array_header_t *revprops,
+ svn_log_entry_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_check_path(). */
+ svn_error_t *(*check_path)(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ svn_node_kind_t *kind,
+ apr_pool_t *pool);
+ /* See svn_ra_stat(). */
+ svn_error_t *(*stat)(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ svn_dirent_t **dirent,
+ apr_pool_t *pool);
+ /* See svn_ra_get_uuid2(). */
+ svn_error_t *(*get_uuid)(svn_ra_session_t *session,
+ const char **uuid,
+ apr_pool_t *pool);
+ /* See svn_ra_get_repos_root2(). */
+ svn_error_t *(*get_repos_root)(svn_ra_session_t *session,
+ const char **url,
+ apr_pool_t *pool);
+ /* See svn_ra_get_locations(). */
+ svn_error_t *(*get_locations)(svn_ra_session_t *session,
+ apr_hash_t **locations,
+ const char *path,
+ svn_revnum_t peg_revision,
+ const apr_array_header_t *location_revisions,
+ apr_pool_t *pool);
+ /* See svn_ra_get_location_segments(). */
+ svn_error_t *(*get_location_segments)(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_location_segment_receiver_t rcvr,
+ void *receiver_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_get_file_revs2(). */
+ svn_error_t *(*get_file_revs)(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ svn_boolean_t include_merged_revisions,
+ svn_file_rev_handler_t handler,
+ void *handler_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_lock(). */
+ svn_error_t *(*lock)(svn_ra_session_t *session,
+ apr_hash_t *path_revs,
+ const char *comment,
+ svn_boolean_t force,
+ svn_ra_lock_callback_t lock_func,
+ void *lock_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_unlock(). */
+ svn_error_t *(*unlock)(svn_ra_session_t *session,
+ apr_hash_t *path_tokens,
+ svn_boolean_t force,
+ svn_ra_lock_callback_t lock_func,
+ void *lock_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_get_lock(). */
+ svn_error_t *(*get_lock)(svn_ra_session_t *session,
+ svn_lock_t **lock,
+ const char *path,
+ apr_pool_t *pool);
+ /* See svn_ra_get_locks2(). */
+ svn_error_t *(*get_locks)(svn_ra_session_t *session,
+ apr_hash_t **locks,
+ const char *path,
+ svn_depth_t depth,
+ apr_pool_t *pool);
+ /* See svn_ra_replay(). */
+ svn_error_t *(*replay)(svn_ra_session_t *session,
+ svn_revnum_t revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t text_deltas,
+ const svn_delta_editor_t *editor,
+ void *edit_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_has_capability(). */
+ svn_error_t *(*has_capability)(svn_ra_session_t *session,
+ svn_boolean_t *has,
+ const char *capability,
+ apr_pool_t *pool);
+ /* See svn_ra_replay_range(). */
+ svn_error_t *
+ (*replay_range)(svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t text_deltas,
+ svn_ra_replay_revstart_callback_t revstart_func,
+ svn_ra_replay_revfinish_callback_t revfinish_func,
+ void *replay_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_get_deleted_rev(). */
+ svn_error_t *(*get_deleted_rev)(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t *revision_deleted,
+ apr_pool_t *pool);
+ /* See svn_ra_get_inherited_props(). */
+ svn_error_t *(*get_inherited_props)(svn_ra_session_t *session,
+ apr_array_header_t **iprops,
+ const char *path,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+ /* If not NULL, receives a pointer to svn_ra_open, to alllow opening
+ a new ra session from inside the ra layer without a circular
+ library dependency*/
+ svn_error_t *(*set_svn_ra_open)(svn_ra_session_t *session,
+ svn_ra__open_func_t func);
+
+ /* See svn_ra_list(). */
+ svn_error_t *(*list)(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ const apr_array_header_t *patterns,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_ra_dirent_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *scratch_pool);
+
+ /* Experimental support below here */
+
+ /* See svn_ra__register_editor_shim_callbacks() */
+ svn_error_t *(*register_editor_shim_callbacks)(svn_ra_session_t *session,
+ svn_delta_shim_callbacks_t *callbacks);
+ /* See svn_ra__get_commit_ev2() */
+ svn_error_t *(*get_commit_ev2)(
+ svn_editor_t **editor,
+ svn_ra_session_t *session,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t callback,
+ void *callback_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
+ void *cb_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+ /* See svn_ra__replay_range_ev2() */
+ svn_error_t *(*replay_range_ev2)(
+ svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t send_deltas,
+ svn_ra__replay_revstart_ev2_callback_t revstart_func,
+ svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
+ void *replay_baton,
+ apr_pool_t *scratch_pool);
+
+} svn_ra__vtable_t;
+
+/* The RA session object. */
+struct svn_ra_session_t {
+ const svn_ra__vtable_t *vtable;
+
+ /* Cancellation handlers consumers may want to use. */
+ svn_cancel_func_t cancel_func;
+ void *cancel_baton;
+
+ /* Pool used to manage this session. */
+ apr_pool_t *pool;
+
+ /* Private data for the RA implementation. */
+ void *priv;
+};
+
+/* Each libsvn_ra_foo defines a function named svn_ra_foo__init of this type.
+ *
+ * The LOADER_VERSION parameter must remain first in the list, and the
+ * function must use the C calling convention on all platforms, so that
+ * the init functions can safely read the version parameter.
+ *
+ * POOL will be available as long as this module is being used.
+ *
+ * ### need to force this to be __cdecl on Windows... how??
+ */
+typedef svn_error_t *
+(*svn_ra__init_func_t)(const svn_version_t *loader_version,
+ const svn_ra__vtable_t **vtable,
+ apr_pool_t *pool);
+
+/* Declarations of the init functions for the available RA libraries. */
+svn_error_t *svn_ra_local__init(const svn_version_t *loader_version,
+ const svn_ra__vtable_t **vtable,
+ apr_pool_t *pool);
+svn_error_t *svn_ra_svn__init(const svn_version_t *loader_version,
+ const svn_ra__vtable_t **vtable,
+ apr_pool_t *pool);
+svn_error_t *svn_ra_serf__init(const svn_version_t *loader_version,
+ const svn_ra__vtable_t **vtable,
+ apr_pool_t *pool);
+
+
+
+/*** Compat Functions ***/
+
+/**
+ * Set *LOCATIONS to the locations (at the repository revisions
+ * LOCATION_REVISIONS) of the file identified by PATH in PEG_REVISION.
+ * PATH is relative to the URL to which SESSION was opened.
+ * LOCATION_REVISIONS is an array of svn_revnum_t's. *LOCATIONS will
+ * be a mapping from the revisions to their appropriate absolute
+ * paths. If the file doesn't exist in a location_revision, that
+ * revision will be ignored.
+ *
+ * Use POOL for all allocations.
+ *
+ * NOTE: This function uses the RA get_log interfaces to do its work,
+ * as a fallback mechanism for servers which don't support the native
+ * get_locations API.
+ */
+svn_error_t *
+svn_ra__locations_from_log(svn_ra_session_t *session,
+ apr_hash_t **locations_p,
+ const char *path,
+ svn_revnum_t peg_revision,
+ const apr_array_header_t *location_revisions,
+ apr_pool_t *pool);
+
+/**
+ * Call RECEIVER (with RECEIVER_BATON) for each segment in the
+ * location history of PATH in START_REV, working backwards in time
+ * from START_REV to END_REV.
+ *
+ * END_REV may be SVN_INVALID_REVNUM to indicate that you want to
+ * trace the history of the object to its origin.
+ *
+ * START_REV may be SVN_INVALID_REVNUM to indicate that you want to
+ * trace the history of the object beginning in the HEAD revision.
+ * Otherwise, START_REV must be younger than END_REV (unless END_REV
+ * is SVN_INVALID_REVNUM).
+ *
+ * Use POOL for all allocations.
+ *
+ * NOTE: This function uses the RA get_log interfaces to do its work,
+ * as a fallback mechanism for servers which don't support the native
+ * get_location_segments API.
+ */
+svn_error_t *
+svn_ra__location_segments_from_log(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_location_segment_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool);
+
+/**
+ * Retrieve a subset of the interesting revisions of a file PATH
+ * as seen in revision END (see svn_fs_history_prev() for a
+ * definition of "interesting revisions"). Invoke HANDLER with
+ * @a handler_baton as its first argument for each such revision.
+ * @a session is an open RA session. Use POOL for all allocations.
+ *
+ * If there is an interesting revision of the file that is less than or
+ * equal to START, the iteration will begin at that revision.
+ * Else, the iteration will begin at the first revision of the file in
+ * the repository, which has to be less than or equal to END. Note
+ * that if the function succeeds, HANDLER will have been called at
+ * least once.
+ *
+ * In a series of calls to HANDLER, the file contents for the first
+ * interesting revision will be provided as a text delta against the
+ * empty file. In the following calls, the delta will be against the
+ * fulltext contents for the previous call.
+ *
+ * NOTE: This function uses the RA get_log interfaces to do its work,
+ * as a fallback mechanism for servers which don't support the native
+ * get_location_segments API.
+ */
+svn_error_t *
+svn_ra__file_revs_from_log(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ svn_file_rev_handler_t handler,
+ void *handler_baton,
+ apr_pool_t *pool);
+
+
+/**
+ * Given a path REL_DELETED_PATH, relative to the URL of SESSION, which
+ * exists at PEG_REVISION, and an END_REVISION > PEG_REVISION at which
+ * REL_DELETED_PATH no longer exists, set *REVISION_DELETED to the revision
+ * REL_DELETED_PATH was first deleted or replaced, within the inclusive
+ * revision range defined by PEG_REVISION and END_REVISION.
+ *
+ * If REL_DELETED_PATH does not exist at PEG_REVISION or was not deleted prior
+ * to END_REVISION within the specified range, then set *REVISION_DELETED to
+ * SVN_INVALID_REVNUM. If PEG_REVISION or END_REVISION are invalid or if
+ * END_REVISION <= PEG_REVISION, then return SVN_ERR_CLIENT_BAD_REVISION.
+ *
+ * Use POOL for all allocations.
+ *
+ * NOTE: This function uses the RA get_log interfaces to do its work,
+ * as a fallback mechanism for servers which don't support the native
+ * get_deleted_rev API.
+ */
+svn_error_t *
+svn_ra__get_deleted_rev_from_log(svn_ra_session_t *session,
+ const char *rel_deleted_path,
+ svn_revnum_t peg_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t *revision_deleted,
+ apr_pool_t *pool);
+
+
+/**
+ * Fallback logic for svn_ra_get_inherited_props() when that API
+ * need to find PATH's inherited properties on a legacy server that
+ * doesn't have the SVN_RA_CAPABILITY_INHERITED_PROPS capability.
+ *
+ * All arguments are as per svn_ra_get_inherited_props().
+ */
+svn_error_t *
+svn_ra__get_inherited_props_walk(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ apr_array_header_t **inherited_props,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Utility function to provide a shim between a returned Ev2 and an RA
+ provider's Ev1-based commit editor.
+
+ See svn_ra__get_commit_ev2() for parameter semantics. */
+svn_error_t *
+svn_ra__use_commit_shim(svn_editor_t **editor,
+ svn_ra_session_t *session,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t callback,
+ void *callback_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
+ void *cb_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Utility function to provide a shim between a returned Ev2 and an RA
+ provider's Ev1-based commit editor.
+
+ See svn_ra__replay_range_ev2() for parameter semantics. */
+svn_error_t *
+svn_ra__use_replay_range_shim(svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t send_deltas,
+ svn_ra__replay_revstart_ev2_callback_t revstart_func,
+ svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
+ void *replay_baton,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ void *cb_baton,
+ apr_pool_t *scratch_pool);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/subversion/libsvn_ra/util.c b/subversion/libsvn_ra/util.c
new file mode 100644
index 0000000..d9a4520
--- /dev/null
+++ b/subversion/libsvn_ra/util.c
@@ -0,0 +1,280 @@
+/*
+ * util.c: Repository access utility routines.
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+/*** Includes. ***/
+#include <apr_pools.h>
+#include <apr_network_io.h>
+
+#include "svn_types.h"
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_error_codes.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_ra.h"
+
+#include "svn_private_config.h"
+#include "private/svn_ra_private.h"
+
+static const char *
+get_path(const char *path_or_url,
+ svn_ra_session_t *ra_session,
+ apr_pool_t *pool)
+{
+ if (path_or_url == NULL)
+ {
+ svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url,
+ pool);
+ if (err)
+ {
+ /* The SVN_ERR_UNSUPPORTED_FEATURE error in the caller is more
+ important, so dummy up the session's URL and chuck this error. */
+ svn_error_clear(err);
+ return _("<repository>");
+ }
+ }
+ return path_or_url;
+}
+
+svn_error_t *
+svn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session,
+ const char *path_or_url,
+ apr_pool_t *pool)
+{
+ svn_boolean_t mergeinfo_capable;
+ SVN_ERR(svn_ra_has_capability(ra_session, &mergeinfo_capable,
+ SVN_RA_CAPABILITY_MERGEINFO, pool));
+ if (! mergeinfo_capable)
+ {
+ path_or_url = get_path(path_or_url, ra_session, pool);
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Retrieval of mergeinfo unsupported by '%s'"),
+ svn_path_is_url(path_or_url)
+ ? path_or_url
+ : svn_dirent_local_style(path_or_url, pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra__assert_capable_server(svn_ra_session_t *ra_session,
+ const char *capability,
+ const char *path_or_url,
+ apr_pool_t *pool)
+{
+ if (!strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO))
+ return svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url,
+ pool);
+
+ else
+ {
+ svn_boolean_t has;
+ SVN_ERR(svn_ra_has_capability(ra_session, &has, capability, pool));
+ if (! has)
+ {
+ path_or_url = get_path(path_or_url, ra_session, pool);
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("The '%s' feature is not supported by '%s'"),
+ capability,
+ svn_path_is_url(path_or_url)
+ ? path_or_url
+ : svn_dirent_local_style(path_or_url,
+ pool));
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Does ERR mean "the current value of the revprop isn't equal to
+ the *OLD_VALUE_P you gave me"?
+ */
+static svn_boolean_t is_atomicity_error(svn_error_t *err)
+{
+ return svn_error_find_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) != NULL;
+}
+
+svn_error_t *
+svn_ra__release_operational_lock(svn_ra_session_t *session,
+ const char *lock_revprop_name,
+ const svn_string_t *mylocktoken,
+ apr_pool_t *scratch_pool)
+{
+ svn_string_t *reposlocktoken;
+ svn_boolean_t be_atomic;
+
+ SVN_ERR(svn_ra_has_capability(session, &be_atomic,
+ SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+ scratch_pool));
+ SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name,
+ &reposlocktoken, scratch_pool));
+ if (reposlocktoken && svn_string_compare(reposlocktoken, mylocktoken))
+ {
+ svn_error_t *err;
+
+ err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name,
+ be_atomic ? &mylocktoken : NULL, NULL,
+ scratch_pool);
+ if (is_atomicity_error(err))
+ {
+ return svn_error_createf(err->apr_err, err,
+ _("Lock was stolen by '%s'; unable to "
+ "remove it"), reposlocktoken->data);
+ }
+ else
+ SVN_ERR(err);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra__get_operational_lock(const svn_string_t **lock_string_p,
+ const svn_string_t **stolen_lock_p,
+ svn_ra_session_t *session,
+ const char *lock_revprop_name,
+ svn_boolean_t steal_lock,
+ int num_retries,
+ svn_ra__lock_retry_func_t retry_func,
+ void *retry_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool)
+{
+ char hostname_str[APRMAXHOSTLEN + 1] = { 0 };
+ svn_string_t *mylocktoken, *reposlocktoken;
+ apr_status_t apr_err;
+ svn_boolean_t be_atomic;
+ apr_pool_t *subpool;
+ int i;
+
+ *lock_string_p = NULL;
+ if (stolen_lock_p)
+ *stolen_lock_p = NULL;
+
+ SVN_ERR(svn_ra_has_capability(session, &be_atomic,
+ SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool));
+
+ /* We build a lock token from the local hostname and a UUID. */
+ apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err,
+ _("Unable to determine local hostname"));
+ mylocktoken = svn_string_createf(pool, "%s:%s", hostname_str,
+ svn_uuid_generate(pool));
+
+ /* Ye Olde Retry Loope */
+ subpool = svn_pool_create(pool);
+
+ for (i = 0; i < num_retries; ++i)
+ {
+ svn_error_t *err;
+ const svn_string_t *unset = NULL;
+
+ svn_pool_clear(subpool);
+
+ /* Check for cancellation. If we're cancelled, don't leave a
+ stray lock behind! */
+ if (cancel_func)
+ {
+ err = cancel_func(cancel_baton);
+ if (err && err->apr_err == SVN_ERR_CANCELLED)
+ return svn_error_compose_create(
+ svn_ra__release_operational_lock(session,
+ lock_revprop_name,
+ mylocktoken,
+ subpool),
+ err);
+ SVN_ERR(err);
+ }
+
+ /* Ask the repository for the value of the LOCK_REVPROP_NAME. */
+ SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name,
+ &reposlocktoken, subpool));
+
+ /* Did we get a value from the repository? We'll check to see
+ if it matches our token. If so, we call it success. If not
+ and we're told to steal locks, we remember the existing lock
+ token and fall through to the locking code; othewise, we
+ sleep and retry. */
+ if (reposlocktoken)
+ {
+ if (svn_string_compare(reposlocktoken, mylocktoken))
+ {
+ *lock_string_p = mylocktoken;
+ return SVN_NO_ERROR;
+ }
+ else if (! steal_lock)
+ {
+ if (retry_func)
+ SVN_ERR(retry_func(retry_baton, reposlocktoken, subpool));
+ apr_sleep(apr_time_from_sec(1));
+ continue;
+ }
+ else
+ {
+ if (stolen_lock_p)
+ *stolen_lock_p = svn_string_dup(reposlocktoken, pool);
+ unset = reposlocktoken;
+ }
+ }
+
+ /* No lock value in the repository, or we plan to steal it?
+ Well, if we've got a spare iteration, we'll try to set the
+ lock. (We use the spare iteration to verify that we still
+ have the lock after setting it.) */
+ if (i < num_retries - 1)
+ {
+ /* Except in the very last iteration, try to set the lock. */
+ err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name,
+ be_atomic ? &unset : NULL,
+ mylocktoken, subpool);
+
+ if (be_atomic && err && is_atomicity_error(err))
+ {
+ /* Someone else has the lock. No problem, we'll loop again. */
+ svn_error_clear(err);
+ }
+ else if (be_atomic && err == SVN_NO_ERROR)
+ {
+ /* Yay! We have the lock! However, for compatibility
+ with concurrent processes that don't support
+ atomicity, loop anyway to double-check that they
+ haven't overwritten our lock.
+ */
+ continue;
+ }
+ else
+ {
+ /* We have a genuine error, or aren't atomic and need
+ to loop. */
+ SVN_ERR(err);
+ }
+ }
+ }
+
+ return svn_error_createf(APR_EINVAL, NULL,
+ _("Couldn't get lock on destination repos "
+ "after %d attempts"), i);
+}
diff --git a/subversion/libsvn_ra/wrapper_template.h b/subversion/libsvn_ra/wrapper_template.h
new file mode 100644
index 0000000..0585ded
--- /dev/null
+++ b/subversion/libsvn_ra/wrapper_template.h
@@ -0,0 +1,514 @@
+/**
+ * @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
+ */
+
+#include <apr_pools.h>
+#include <apr_hash.h>
+#include <apr_time.h>
+
+#include "svn_types.h"
+#include "svn_string.h"
+#include "svn_props.h"
+#include "svn_compat.h"
+
+/* This file is a template for a compatibility wrapper for an RA library.
+ * It contains an svn_ra_plugin_t and wrappers for all of its functions,
+ * implemented in terms of svn_ra__vtable_t functions. It also contains
+ * the implementations of an svn_ra_FOO_init for the FOO RA library.
+ *
+ * A file in the RA library includes this file, providing the
+ * following macros before inclusion:
+ *
+ * NAME The library name, e.g. "ra_local".
+ * DESCRIPTION The short library description as a string constant.
+ * VTBL The name of an svn_ra_vtable_t object for the library.
+ * INITFUNC The init function for the library, e.g. svn_ra_local__init.
+ * COMPAT_INITFUNC The compatibility init function, e.g. svn_ra_local_init.
+ */
+
+/* Check that all our "arguments" are defined. */
+#if ! defined(NAME) || ! defined(DESCRIPTION) || ! defined(VTBL) \
+ || ! defined(INITFUNC) || ! defined(COMPAT_INITFUNC)
+#error Missing define for RA compatibility wrapper.
+#endif
+
+
+static svn_error_t *compat_open(void **session_baton,
+ const char *repos_URL,
+ const svn_ra_callbacks_t *callbacks,
+ void *callback_baton,
+ apr_hash_t *config,
+ apr_pool_t *pool)
+{
+ /* Here, we should be calling svn_ra_create_callbacks to initialize
+ * the svn_ra_callbacks2_t structure. However, doing that
+ * introduces a circular dependancy between libsvn_ra and
+ * libsvn_ra_{local,neon,serf,svn}, which include
+ * wrapper_template.h. In turn, circular dependancies break the
+ * build on win32 (and possibly other systems).
+ *
+ * In order to avoid this happening at all, the code of
+ * svn_ra_create_callbacks is duplicated here. This is evil, but
+ * the alternative (creating a new ra_util library) would be massive
+ * overkill for the time being. Just be sure to keep the following
+ * line and the code of svn_ra_create_callbacks in sync. */
+ apr_pool_t *sesspool = svn_pool_create(pool);
+ svn_ra_callbacks2_t *callbacks2 = apr_pcalloc(sesspool,
+ sizeof(*callbacks2));
+
+ svn_ra_session_t *sess = apr_pcalloc(sesspool, sizeof(*sess));
+ const char *session_url;
+
+ sess->vtable = &VTBL;
+ sess->pool = sesspool;
+
+ callbacks2->open_tmp_file = callbacks->open_tmp_file;
+ callbacks2->auth_baton = callbacks->auth_baton;
+ callbacks2->get_wc_prop = callbacks->get_wc_prop;
+ callbacks2->set_wc_prop = callbacks->set_wc_prop;
+ callbacks2->push_wc_prop = callbacks->push_wc_prop;
+ callbacks2->invalidate_wc_props = callbacks->invalidate_wc_props;
+ callbacks2->progress_func = NULL;
+ callbacks2->progress_baton = NULL;
+
+ SVN_ERR(VTBL.open_session(sess, &session_url, repos_URL,
+ callbacks2, callback_baton,
+ callbacks ? callbacks->auth_baton : NULL,
+ config, sesspool, sesspool));
+
+ if (strcmp(repos_URL, session_url) != 0)
+ {
+ svn_pool_destroy(sesspool);
+ return svn_error_createf(SVN_ERR_RA_SESSION_URL_MISMATCH, NULL,
+ _("Session URL '%s' does not match requested "
+ " URL '%s', and redirection was disallowed."),
+ session_url, repos_URL);
+ }
+
+ *session_baton = sess;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *compat_get_latest_revnum(void *session_baton,
+ svn_revnum_t *latest_revnum,
+ apr_pool_t *pool)
+{
+ return VTBL.get_latest_revnum(session_baton, latest_revnum, pool);
+}
+
+static svn_error_t *compat_get_dated_revision(void *session_baton,
+ svn_revnum_t *revision,
+ apr_time_t tm,
+ apr_pool_t *pool)
+{
+ return VTBL.get_dated_revision(session_baton, revision, tm, pool);
+}
+
+static svn_error_t *compat_change_rev_prop(void *session_baton,
+ svn_revnum_t rev,
+ const char *propname,
+ const svn_string_t *value,
+ apr_pool_t *pool)
+{
+ return VTBL.change_rev_prop(session_baton, rev, propname, NULL, value, pool);
+}
+
+static svn_error_t *compat_rev_proplist(void *session_baton,
+ svn_revnum_t rev,
+ apr_hash_t **props,
+ apr_pool_t *pool)
+{
+ return VTBL.rev_proplist(session_baton, rev, props, pool);
+}
+
+static svn_error_t *compat_rev_prop(void *session_baton,
+ svn_revnum_t rev,
+ const char *propname,
+ svn_string_t **value,
+ apr_pool_t *pool)
+{
+ return VTBL.rev_prop(session_baton, rev, propname, value, pool);
+}
+
+static svn_error_t *compat_get_commit_editor(void *session_baton,
+ const svn_delta_editor_t
+ **editor,
+ void **edit_baton,
+ const char *log_msg,
+ svn_commit_callback_t callback,
+ void *callback_baton,
+ apr_pool_t *pool)
+{
+ svn_commit_callback2_t callback2;
+ void *callback2_baton;
+ apr_hash_t *revprop_table = apr_hash_make(pool);
+
+ svn_compat_wrap_commit_callback(&callback2, &callback2_baton,
+ callback, callback_baton,
+ pool);
+ apr_hash_set(revprop_table, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING,
+ svn_string_create(log_msg, pool));
+ return VTBL.get_commit_editor(session_baton, editor, edit_baton,
+ revprop_table, callback2, callback2_baton,
+ NULL, TRUE, pool);
+}
+
+static svn_error_t *compat_get_file(void *session_baton,
+ const char *path,
+ svn_revnum_t revision,
+ svn_stream_t *stream,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ apr_pool_t *pool)
+{
+ return VTBL.get_file(session_baton, path, revision, stream, fetched_rev,
+ props, pool);
+}
+
+static svn_error_t *compat_get_dir(void *session_baton,
+ const char *path,
+ svn_revnum_t revision,
+ apr_hash_t **dirents,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ apr_pool_t *pool)
+{
+ return VTBL.get_dir(session_baton, dirents, fetched_rev, props,
+ path, revision, SVN_DIRENT_ALL, pool);
+}
+
+/** Reporter compat code. **/
+
+struct compat_report_baton {
+ const svn_ra_reporter3_t *reporter;
+ void *baton;
+};
+
+static svn_error_t *compat_set_path(void *report_baton,
+ const char *path,
+ svn_revnum_t revision,
+ svn_boolean_t start_empty,
+ apr_pool_t *pool)
+{
+ struct compat_report_baton *crb = report_baton;
+
+ return crb->reporter->set_path(crb->baton, path, revision,
+ svn_depth_infinity, start_empty,
+ NULL, pool);
+}
+
+static svn_error_t *compat_delete_path(void *report_baton,
+ const char *path,
+ apr_pool_t *pool)
+{
+ struct compat_report_baton *crb = report_baton;
+
+ return crb->reporter->delete_path(crb->baton, path, pool);
+}
+
+static svn_error_t *compat_link_path(void *report_baton,
+ const char *path,
+ const char *url,
+ svn_revnum_t revision,
+ svn_boolean_t start_empty,
+ apr_pool_t *pool)
+{
+ struct compat_report_baton *crb = report_baton;
+
+ return crb->reporter->link_path(crb->baton, path, url, revision,
+ svn_depth_infinity, start_empty,
+ NULL, pool);
+}
+
+static svn_error_t *compat_finish_report(void *report_baton,
+ apr_pool_t *pool)
+{
+ struct compat_report_baton *crb = report_baton;
+
+ return crb->reporter->finish_report(crb->baton, pool);
+}
+
+static svn_error_t *compat_abort_report(void *report_baton,
+ apr_pool_t *pool)
+{
+ struct compat_report_baton *crb = report_baton;
+
+ return crb->reporter->abort_report(crb->baton, pool);
+}
+
+static const svn_ra_reporter_t compat_reporter = {
+ compat_set_path,
+ compat_delete_path,
+ compat_link_path,
+ compat_finish_report,
+ compat_abort_report
+};
+
+static void compat_wrap_reporter(const svn_ra_reporter_t **reporter,
+ void **baton,
+ const svn_ra_reporter3_t *wrapped,
+ void *wrapped_baton,
+ apr_pool_t *pool)
+{
+ struct compat_report_baton *crb = apr_palloc(pool, sizeof(*crb));
+ crb->reporter = wrapped;
+ crb->baton = wrapped_baton;
+
+ *reporter = &compat_reporter;
+ *baton = crb;
+}
+
+static svn_error_t *compat_do_update(void *session_baton,
+ const svn_ra_reporter_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_update_to,
+ const char *update_target,
+ svn_boolean_t recurse,
+ const svn_delta_editor_t *editor,
+ void *update_baton,
+ apr_pool_t *pool)
+{
+ const svn_ra_reporter3_t *reporter3;
+ void *baton3;
+ svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
+
+ SVN_ERR(VTBL.do_update(session_baton, &reporter3, &baton3,
+ revision_to_update_to, update_target, depth,
+ FALSE /* send_copyfrom_args */,
+ FALSE /* ignore_ancestry */,
+ editor, update_baton,
+ pool, pool));
+ compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *compat_do_switch(void *session_baton,
+ const svn_ra_reporter_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision_to_switch_to,
+ const char *switch_target,
+ svn_boolean_t recurse,
+ const char *switch_url,
+ const svn_delta_editor_t *editor,
+ void *switch_baton,
+ apr_pool_t *pool)
+{
+ const svn_ra_reporter3_t *reporter3;
+ void *baton3;
+ svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
+
+ SVN_ERR(VTBL.do_switch(session_baton, &reporter3, &baton3,
+ revision_to_switch_to, switch_target, depth,
+ switch_url,
+ FALSE /* send_copyfrom_args */,
+ TRUE /* ignore_ancestry */,
+ editor, switch_baton,
+ pool /* result_pool */, pool /* scratch_pool */));
+
+ compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *compat_do_status(void *session_baton,
+ const svn_ra_reporter_t **reporter,
+ void **report_baton,
+ const char *status_target,
+ svn_revnum_t revision,
+ svn_boolean_t recurse,
+ const svn_delta_editor_t *editor,
+ void *status_baton,
+ apr_pool_t *pool)
+{
+ const svn_ra_reporter3_t *reporter3;
+ void *baton3;
+ svn_depth_t depth = SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse);
+
+ SVN_ERR(VTBL.do_status(session_baton, &reporter3, &baton3, status_target,
+ revision, depth, editor, status_baton, pool));
+
+ compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *compat_do_diff(void *session_baton,
+ const svn_ra_reporter_t **reporter,
+ void **report_baton,
+ svn_revnum_t revision,
+ const char *diff_target,
+ svn_boolean_t recurse,
+ svn_boolean_t ignore_ancestry,
+ const char *versus_url,
+ const svn_delta_editor_t *diff_editor,
+ void *diff_baton,
+ apr_pool_t *pool)
+{
+ const svn_ra_reporter3_t *reporter3;
+ void *baton3;
+ svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
+
+ SVN_ERR(VTBL.do_diff(session_baton, &reporter3, &baton3, revision,
+ diff_target, depth, ignore_ancestry, TRUE,
+ versus_url, diff_editor, diff_baton, pool));
+
+ compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *compat_get_log(void *session_baton,
+ const apr_array_header_t *paths,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ svn_boolean_t discover_changed_paths,
+ svn_boolean_t strict_node_history,
+ svn_log_message_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *pool)
+{
+ svn_log_entry_receiver_t receiver2;
+ void *receiver2_baton;
+
+ svn_compat_wrap_log_receiver(&receiver2, &receiver2_baton,
+ receiver, receiver_baton,
+ pool);
+
+ return VTBL.get_log(session_baton, paths, start, end, 0, /* limit */
+ discover_changed_paths, strict_node_history,
+ FALSE, /* include_merged_revisions */
+ svn_compat_log_revprops_in(pool), /* revprops */
+ receiver2, receiver2_baton, pool);
+}
+
+static svn_error_t *compat_check_path(void *session_baton,
+ const char *path,
+ svn_revnum_t revision,
+ svn_node_kind_t *kind,
+ apr_pool_t *pool)
+{
+ return VTBL.check_path(session_baton, path, revision, kind, pool);
+}
+
+static svn_error_t *compat_get_uuid(void *session_baton,
+ const char **uuid,
+ apr_pool_t *pool)
+{
+ return VTBL.get_uuid(session_baton, uuid, pool);
+}
+
+static svn_error_t *compat_get_repos_root(void *session_baton,
+ const char **url,
+ apr_pool_t *pool)
+{
+ return VTBL.get_repos_root(session_baton, url, pool);
+}
+
+static svn_error_t *compat_get_locations(void *session_baton,
+ apr_hash_t **locations,
+ const char *path,
+ svn_revnum_t peg_revision,
+ apr_array_header_t *location_revs,
+ apr_pool_t *pool)
+{
+ return VTBL.get_locations(session_baton, locations, path, peg_revision,
+ location_revs, pool);
+}
+
+static svn_error_t *compat_get_file_revs(void *session_baton,
+ const char *path,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ svn_ra_file_rev_handler_t handler,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ svn_file_rev_handler_t handler2;
+ void *handler2_baton;
+
+ svn_compat_wrap_file_rev_handler(&handler2, &handler2_baton,
+ handler, handler_baton,
+ pool);
+
+ return VTBL.get_file_revs(session_baton, path, start, end,
+ FALSE, /* include merged revisions */
+ handler2, handler2_baton, pool);
+}
+
+static const svn_version_t *compat_get_version(void)
+{
+ return VTBL.get_version();
+}
+
+
+static const svn_ra_plugin_t compat_plugin = {
+ NAME,
+ DESCRIPTION,
+ compat_open,
+ compat_get_latest_revnum,
+ compat_get_dated_revision,
+ compat_change_rev_prop,
+ compat_rev_proplist,
+ compat_rev_prop,
+ compat_get_commit_editor,
+ compat_get_file,
+ compat_get_dir,
+ compat_do_update,
+ compat_do_switch,
+ compat_do_status,
+ compat_do_diff,
+ compat_get_log,
+ compat_check_path,
+ compat_get_uuid,
+ compat_get_repos_root,
+ compat_get_locations,
+ compat_get_file_revs,
+ compat_get_version
+};
+
+svn_error_t *
+COMPAT_INITFUNC(int abi_version,
+ apr_pool_t *pool,
+ apr_hash_t *hash)
+{
+ const svn_ra__vtable_t *vtable;
+ const char * const * schemes;
+
+ if (abi_version < 1
+ || abi_version > SVN_RA_ABI_VERSION)
+ return svn_error_createf(SVN_ERR_RA_UNSUPPORTED_ABI_VERSION, NULL,
+ _("Unsupported RA plugin ABI version (%d) "
+ "for %s"), abi_version, NAME);
+
+ /* We call the new init function so it can check library dependencies or
+ do other initialization things. We fake the loader version, since we
+ rely on the ABI version check instead. */
+ SVN_ERR(INITFUNC(VTBL.get_version(), &vtable, pool));
+
+ schemes = VTBL.get_schemes(pool);
+
+ for (; *schemes != NULL; ++schemes)
+ apr_hash_set(hash, *schemes, APR_HASH_KEY_STRING, &compat_plugin);
+
+ return SVN_NO_ERROR;
+}