summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra
diff options
context:
space:
mode:
authorLorry <lorry@roadtrain.codethink.co.uk>2012-08-22 14:29:52 +0100
committerLorry <lorry@roadtrain.codethink.co.uk>2012-08-22 14:29:52 +0100
commitf1bdf13786f0752c0846cf36f0d91e4fc6747929 (patch)
tree4223b2035bf2240d681a53822808b3c7f687b905 /subversion/libsvn_ra
downloadsubversion-tarball-f1bdf13786f0752c0846cf36f0d91e4fc6747929.tar.gz
Tarball conversion
Diffstat (limited to 'subversion/libsvn_ra')
-rw-r--r--subversion/libsvn_ra/compat.c872
-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.c419
-rw-r--r--subversion/libsvn_ra/ra_loader.c1406
-rw-r--r--subversion/libsvn_ra/ra_loader.h458
-rw-r--r--subversion/libsvn_ra/util.c242
-rw-r--r--subversion/libsvn_ra/wrapper_template.h506
8 files changed, 4103 insertions, 0 deletions
diff --git a/subversion/libsvn_ra/compat.c b/subversion/libsvn_ra/compat.c
new file mode 100644
index 0000000..ec3ff9d
--- /dev/null
+++ b/subversion/libsvn_ra/compat.c
@@ -0,0 +1,872 @@
+/*
+ * 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_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 "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 = apr_hash_get(changed_paths, path, APR_HASH_KEY_STRING);
+ 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;
+ int 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 = apr_hash_get(changed_paths, ch_path, len);
+ 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;
+
+ /* 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. */
+ qsort(location_revisions->elts, location_revisions->nelts,
+ location_revisions->elt_size, compare_revisions);
+ oldest_requested = APR_ARRAY_IDX(location_revisions, 0, svn_revnum_t);
+ youngest_requested = APR_ARRAY_IDX(location_revisions,
+ 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, 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 < location_revisions->nelts; i++)
+ {
+ svn_revnum_t rev = APR_ARRAY_IDX(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;
+ apr_hash_index_t *hi;
+
+ 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 = apr_hash_make(lmb->pool);
+ for (hi = apr_hash_first(pool, log_entry->revprops); hi;
+ hi = apr_hash_next(hi))
+ {
+ void *val;
+ const void *key;
+
+ apr_hash_this(hi, &key, NULL, &val);
+ apr_hash_set(rev->props, apr_pstrdup(lmb->pool, key), APR_HASH_KEY_STRING,
+ svn_string_dup(val, 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. */
+ svn_txdelta(&delta_stream, last_stream, stream, 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;
+}
diff --git a/subversion/libsvn_ra/debug_reporter.c b/subversion/libsvn_ra/debug_reporter.c
new file mode 100644
index 0000000..56248ac
--- /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_printf(rb->out, pool, "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_printf(rb->out, pool, "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..6fd2442
--- /dev/null
+++ b/subversion/libsvn_ra/deprecated.c
@@ -0,0 +1,419 @@
+/*
+ * 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_ra.h"
+#include "svn_path.h"
+#include "svn_compat.h"
+#include "svn_props.h"
+#include "svn_pools.h"
+
+#include "ra_loader.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 callback,
+ void *callback_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)
+ apr_hash_set(revprop_table, SVN_PROP_REVISION_LOG,
+ APR_HASH_KEY_STRING,
+ svn_string_create(log_msg, pool));
+ return svn_ra_get_commit_editor3(session, editor, edit_baton, revprop_table,
+ callback, callback_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_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 */
+ update_editor, update_baton,
+ 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, switch_editor, switch_baton,
+ 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);
+}
diff --git a/subversion/libsvn_ra/ra_loader.c b/subversion/libsvn_ra/ra_loader.c
new file mode 100644
index 0000000..b796a80
--- /dev/null
+++ b/subversion/libsvn_ra/ra_loader.c
@@ -0,0 +1,1406 @@
+/*
+ * 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_version.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_config.h"
+#include "ra_loader.h"
+
+#include "private/svn_ra_private.h"
+#include "svn_private_config.h"
+
+
+/* ### This file maps URL schemes to particular RA libraries.
+ ### Currently, the only pair of RA libraries which support the same
+ ### protocols are neon and serf. svn_ra_open3 makes the assumption
+ ### that this is the case; that their 'schemes' fields are both
+ ### dav_schemes; and that "neon" is listed first.
+
+ ### Users can choose which dav library to use with the http-library
+ ### preference in .subversion/servers; however, it is ignored by
+ ### any code which uses the pre-1.2 API svn_ra_get_ra_library
+ ### instead of svn_ra_open. */
+
+#if defined(SVN_HAVE_NEON) && defined(SVN_HAVE_SERF)
+#define CHOOSABLE_DAV_MODULE
+#endif
+
+
+/* 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[] = {
+ {
+ "neon",
+ dav_schemes,
+#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_NEON
+ svn_ra_neon__init,
+ svn_ra_dav_init
+#endif
+ },
+
+ {
+ "svn",
+ svn_schemes,
+#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SVN
+ svn_ra_svn__init,
+ svn_ra_svn_init
+#endif
+ },
+
+ {
+ "local",
+ local_schemes,
+#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
+ svn_ra_local__init,
+ svn_ra_local_init
+#endif
+ },
+
+ {
+ "serf",
+ dav_schemes,
+#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SERF
+ svn_ra_serf__init,
+ svn_ra_serf_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-%d.so.0",
+ ra_name, SVN_VER_MAJOR);
+ 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 DEFN may support URL, return the scheme. Else, return NULL. */
+static const char *
+has_scheme_of(const struct ra_lib_defn *defn, const char *url)
+{
+ const char * const *schemes;
+ apr_size_t len;
+
+ for (schemes = defn->schemes; *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)
+{
+ 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);
+ svn_ra_session_t *session;
+ const struct ra_lib_defn *defn;
+ const svn_ra__vtable_t *vtable = NULL;
+ svn_config_t *servers = NULL;
+ const char *server_group;
+ apr_uri_t repos_URI;
+ apr_status_t apr_err;
+#ifdef CHOOSABLE_DAV_MODULE
+ const char *http_library = DEFAULT_HTTP_LIBRARY;
+#endif
+ /* Auth caching parameters. */
+ svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS;
+ svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS;
+ const char *store_plaintext_passwords
+ = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS;
+ svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP;
+ const char *store_pp_plaintext
+ = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT;
+ const char *corrected_url;
+
+ /* 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)
+ {
+ /* The 'store-passwords' and 'store-auth-creds' parameters used to
+ * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility,
+ * if values for these parameters have already been set by our
+ * callers, we use those values as defaults.
+ *
+ * Note that we can only catch the case where users explicitly set
+ * "store-passwords = no" or 'store-auth-creds = no".
+ *
+ * However, since the default value for both these options is
+ * currently (and has always been) "yes", users won't know
+ * the difference if they set "store-passwords = yes" or
+ * "store-auth-creds = yes" -- they'll get the expected behaviour.
+ */
+
+ if (svn_auth_get_parameter(callbacks->auth_baton,
+ SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL)
+ store_passwords = FALSE;
+
+ if (svn_auth_get_parameter(callbacks->auth_baton,
+ SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL)
+ store_auth_creds = FALSE;
+ }
+
+ if (config)
+ {
+ /* Grab the 'servers' config. */
+ servers = apr_hash_get(config, SVN_CONFIG_CATEGORY_SERVERS,
+ APR_HASH_KEY_STRING);
+ if (servers)
+ {
+ /* First, look in the global section. */
+
+ SVN_ERR(svn_config_get_bool
+ (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL,
+ SVN_CONFIG_OPTION_STORE_PASSWORDS,
+ store_passwords));
+
+ SVN_ERR(svn_config_get_yes_no_ask
+ (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL,
+ SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
+ SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS));
+
+ SVN_ERR(svn_config_get_bool
+ (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL,
+ SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
+ store_pp));
+
+ SVN_ERR(svn_config_get_yes_no_ask
+ (servers, &store_pp_plaintext,
+ SVN_CONFIG_SECTION_GLOBAL,
+ SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
+ SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT));
+
+ SVN_ERR(svn_config_get_bool
+ (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL,
+ SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
+ store_auth_creds));
+
+ /* 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);
+
+ if (server_group)
+ {
+ /* Override global auth caching parameters with the ones
+ * for the server group, if any. */
+ SVN_ERR(svn_config_get_bool(servers, &store_auth_creds,
+ server_group,
+ SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
+ store_auth_creds));
+
+ SVN_ERR(svn_config_get_bool(servers, &store_passwords,
+ server_group,
+ SVN_CONFIG_OPTION_STORE_PASSWORDS,
+ store_passwords));
+
+ SVN_ERR(svn_config_get_yes_no_ask
+ (servers, &store_plaintext_passwords, server_group,
+ SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
+ store_plaintext_passwords));
+
+ SVN_ERR(svn_config_get_bool
+ (servers, &store_pp,
+ server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
+ store_pp));
+
+ SVN_ERR(svn_config_get_yes_no_ask
+ (servers, &store_pp_plaintext, server_group,
+ SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
+ store_pp_plaintext));
+ }
+#ifdef CHOOSABLE_DAV_MODULE
+ /* 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, "neon") != 0 &&
+ strcmp(http_library, "serf") != 0)
+ return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
+ _("Invalid config: unknown HTTP library "
+ "'%s'"),
+ http_library);
+#endif
+ }
+ }
+
+ if (callbacks->auth_baton)
+ {
+ /* Save auth caching parameters in the auth parameter hash. */
+ if (! store_passwords)
+ svn_auth_set_parameter(callbacks->auth_baton,
+ SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
+
+ svn_auth_set_parameter(callbacks->auth_baton,
+ SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS,
+ store_plaintext_passwords);
+
+ if (! store_pp)
+ svn_auth_set_parameter(callbacks->auth_baton,
+ SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP,
+ "");
+
+ svn_auth_set_parameter(callbacks->auth_baton,
+ SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
+ store_pp_plaintext);
+
+ if (! store_auth_creds)
+ svn_auth_set_parameter(callbacks->auth_baton,
+ SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
+ }
+
+ /* Find the library. */
+ for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
+ {
+ const char *scheme;
+
+ if ((scheme = has_scheme_of(defn, 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,
+ sesspool));
+ if (! initfunc)
+ /* Library not found. */
+ continue;
+
+ SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool));
+
+ SVN_ERR(check_ra_version(vtable->get_version(), scheme));
+
+ 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->vtable = vtable;
+ session->pool = sesspool;
+
+ /* Ask the library to open the session. */
+ SVN_ERR_W(vtable->open_session(session, &corrected_url, repos_URL,
+ callbacks, callback_baton, config, sesspool),
+ apr_psprintf(pool, "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)
+ {
+ if (! svn_path_is_url(corrected_url))
+ {
+ /* RFC1945 and RFC2616 state that the Location header's
+ value (from whence this CORRECTED_URL ultimately comes),
+ if present, must be an absolute URI. But some Apache
+ versions (those older than 2.2.11, it seems) transmit
+ only the path portion of the URI. See issue #3775 for
+ details. */
+ apr_uri_t corrected_URI = repos_URI;
+ corrected_URI.path = (char *)corrected_url;
+ corrected_url = apr_uri_unparse(pool, &corrected_URI, 0);
+ }
+ *corrected_url_p = svn_uri_canonicalize(corrected_url, pool);
+ svn_pool_destroy(sesspool);
+ return SVN_NO_ERROR;
+ }
+
+ /* 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);
+ return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
+ _("Repository UUID '%s' doesn't match "
+ "expected UUID '%s'"),
+ repository_uuid, uuid);
+ }
+ }
+
+ *session_p = 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));
+ if (strcmp(sess_url, url) == 0)
+ {
+ *rel_path = "";
+ }
+ else
+ {
+ *rel_path = svn_uri__is_child(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));
+ if (strcmp(root_url, url) == 0)
+ {
+ *rel_path = "";
+ }
+ else
+ {
+ *rel_path = svn_uri__is_child(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);
+}
+
+struct ccw_baton
+{
+ svn_commit_callback2_t original_callback;
+ void *original_baton;
+
+ svn_ra_session_t *session;
+};
+
+/* Wrapper which populates the repos_root field of the commit_info struct */
+static svn_error_t *
+commit_callback_wrapper(const svn_commit_info_t *commit_info,
+ void *baton,
+ apr_pool_t *pool)
+{
+ struct ccw_baton *ccwb = baton;
+ svn_commit_info_t *ci = svn_commit_info_dup(commit_info, pool);
+
+ SVN_ERR(svn_ra_get_repos_root2(ccwb->session, &ci->repos_root, pool));
+
+ return ccwb->original_callback(ci, ccwb->original_baton, 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 callback,
+ void *callback_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *pool)
+{
+ /* Allocate this in a pool, since the callback will be called long after
+ this function as returned. */
+ struct ccw_baton *ccwb = apr_palloc(pool, sizeof(*ccwb));
+
+ ccwb->original_callback = callback;
+ ccwb->original_baton = callback_baton;
+ ccwb->session = session;
+
+ return session->vtable->get_commit_editor(session, editor, edit_baton,
+ revprop_table,
+ callback
+ ? commit_callback_wrapper
+ : NULL,
+ callback ? ccwb : NULL,
+ 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(*path != '/');
+ return session->vtable->get_file(session, path, revision, stream,
+ fetched_rev, props, 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_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(*path != '/');
+ return session->vtable->get_dir(session, dirents, fetched_rev, props,
+ path, revision, dirent_fields, 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(*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_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)
+{
+ 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,
+ update_editor, update_baton,
+ 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)
+{
+ 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, switch_editor,
+ switch_baton, 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)
+{
+ 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(*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(*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_ERR_ASSERT(*path != '/');
+ return session->vtable->stat(session, path, revision, dirent, pool);
+}
+
+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(*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(*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(*path != '/');
+
+ if (include_merged_revisions)
+ SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, 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))
+ {
+ 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 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 = svn__apr_hash_index_key(hi);
+
+ SVN_ERR_ASSERT(*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 = svn__apr_hash_index_key(hi);
+
+ SVN_ERR_ASSERT(*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(*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(*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)
+{
+ return session->vtable->replay(session, revision, low_water_mark,
+ text_deltas, editor, edit_baton, pool);
+}
+
+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 =
+ session->vtable->replay_range(session, start_revision, end_revision,
+ low_water_mark, text_deltas,
+ revstart_func, revfinish_func,
+ replay_baton, pool);
+
+ if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
+ {
+ apr_pool_t *subpool = svn_pool_create(pool);
+ svn_revnum_t rev;
+
+ svn_error_clear(err);
+ err = SVN_NO_ERROR;
+
+ 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(subpool);
+
+ SVN_ERR(svn_ra_rev_proplist(session, rev, &rev_props, subpool));
+
+ SVN_ERR(revstart_func(rev, replay_baton,
+ &editor, &edit_baton,
+ rev_props,
+ subpool));
+ SVN_ERR(svn_ra_replay(session, rev, low_water_mark,
+ text_deltas, editor, edit_baton,
+ subpool));
+ SVN_ERR(revfinish_func(rev, replay_baton,
+ editor, edit_baton,
+ rev_props,
+ subpool));
+ }
+ svn_pool_destroy(subpool);
+ }
+
+ return err;
+}
+
+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(*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 /* serf */
+ || err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) /* neon */
+ {
+ 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_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());
+ 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("", pool);
+ return svn_ra_print_modules(*descriptions, pool);
+}
+
+
+/* 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, 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 = apr_hash_get(ht, scheme, APR_HASH_KEY_STRING);
+
+ /* 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_CLIENT_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_CLIENT_LINKS_RA_NEON */
+
+#ifndef SVN_LIBSVN_CLIENT_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_CLIENT_LINKS_RA_SVN */
+
+#ifndef SVN_LIBSVN_CLIENT_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_CLIENT_LINKS_RA_LOCAL */
+
+#ifndef SVN_LIBSVN_CLIENT_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_CLIENT_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..ddb2581
--- /dev/null
+++ b/subversion/libsvn_ra/ra_loader.h
@@ -0,0 +1,458 @@
+/**
+ * @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"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 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)(void);
+
+ /* 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 *repos_URL,
+ const svn_ra_callbacks2_t *callbacks,
+ void *callback_baton,
+ apr_hash_t *config,
+ apr_pool_t *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_update2(). */
+ 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,
+ const svn_delta_editor_t *update_editor,
+ void *update_baton,
+ apr_pool_t *pool);
+ /* See svn_ra_do_switch2(). */
+ 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,
+ const svn_delta_editor_t *switch_editor,
+ void *switch_baton,
+ apr_pool_t *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);
+
+} svn_ra__vtable_t;
+
+/* The RA session object. */
+struct svn_ra_session_t {
+ const svn_ra__vtable_t *vtable;
+
+ /* 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_neon__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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/subversion/libsvn_ra/util.c b/subversion/libsvn_ra/util.c
new file mode 100644
index 0000000..47e4865
--- /dev/null
+++ b/subversion/libsvn_ra/util.c
@@ -0,0 +1,242 @@
+/*
+ * 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"
+
+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)
+ {
+ 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 is more important,
+ so dummy up the session's URL and chuck this error. */
+ svn_error_clear(err);
+ path_or_url = "<repository>";
+ }
+ }
+ 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;
+}
+
+/* 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..e5426dc
--- /dev/null
+++ b/subversion/libsvn_ra/wrapper_template.h
@@ -0,0 +1,506 @@
+/**
+ * @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, config, 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, /* no copyfrom args */
+ editor, update_baton, 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, editor, switch_baton, 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;
+}