| /* | 
 |  * ra_loader.c:  logic for loading different RA library implementations | 
 |  * | 
 |  * ==================================================================== | 
 |  * Copyright (c) 2000-2008 CollabNet.  All rights reserved. | 
 |  * | 
 |  * This software is licensed as described in the file COPYING, which | 
 |  * you should have received as part of this distribution.  The terms | 
 |  * are also available at http://subversion.tigris.org/license-1.html. | 
 |  * If newer versions of this license are posted there, you may use a | 
 |  * newer version instead, at your option. | 
 |  * | 
 |  * This software consists of voluntary contributions made by many | 
 |  * individuals.  For exact contribution history, see the revision | 
 |  * history and logs, available at http://subversion.tigris.org/. | 
 |  * ==================================================================== | 
 |  */ | 
 |  | 
 | /* ==================================================================== */ | 
 |  | 
 | /*** Includes. ***/ | 
 | #include <assert.h> | 
 |  | 
 | #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_compat.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_open2 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_LIBSVN_CLIENT_LINKS_RA_NEON) && defined (SVN_LIBSVN_CLIENT_LINKS_RA_SERF) | 
 | #define MUST_CHOOSE_DAV | 
 | #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 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; | 
 | } | 
 |  | 
 | /* -------------------------------------------------------------- */ | 
 |  | 
 | /*** 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 | 
 | }; | 
 |  | 
 |  | 
 | /* -------------------------------------------------------------- */ | 
 |  | 
 | /*** 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_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) | 
 | { | 
 |   svn_ra_session_t *session; | 
 |   const struct ra_lib_defn *defn; | 
 |   const svn_ra__vtable_t *vtable = NULL; | 
 | #ifdef MUST_CHOOSE_DAV | 
 |   svn_config_t *servers = NULL; | 
 |   const char *http_library = "neon"; | 
 |  | 
 |   if (config) | 
 |     { | 
 |       servers = apr_hash_get(config, SVN_CONFIG_CATEGORY_SERVERS, | 
 |                              APR_HASH_KEY_STRING); | 
 |       if (servers) | 
 |         { | 
 |           apr_uri_t repos_URI; | 
 |           apr_status_t apr_err; | 
 |           const char *server_group; | 
 |  | 
 |           apr_err = apr_uri_parse(pool, repos_URL, &repos_URI); | 
 |           if (apr_err != APR_SUCCESS) | 
 |             return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, | 
 |                                      _("Illegal repository URL '%s'"), | 
 |                                      repos_URL); | 
 |           server_group = svn_config_find_group(servers, repos_URI.hostname, | 
 |                                                SVN_CONFIG_SECTION_GROUPS, pool); | 
 |  | 
 |           http_library | 
 |             = svn_config_get_server_setting(servers, | 
 |                                             server_group, | 
 |                                             SVN_CONFIG_OPTION_HTTP_LIBRARY, | 
 |                                             "neon"); | 
 |  | 
 |           if (strcmp(http_library, "neon") != 0 && | 
 |               strcmp(http_library, "serf") != 0) | 
 |             return svn_error_create(SVN_ERR_RA_DAV_INVALID_CONFIG_VALUE, NULL, | 
 |                                     _("Invalid config: unknown HTTP library")); | 
 |         } | 
 |     } | 
 | #endif | 
 |  | 
 |   /* 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 MUST_CHOOSE_DAV | 
 |           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, | 
 |                                    pool)); | 
 |           if (! initfunc) | 
 |             /* Library not found. */ | 
 |             continue; | 
 |  | 
 |           SVN_ERR(initfunc(svn_ra_version(), &vtable, pool)); | 
 |  | 
 |           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(pool, sizeof(*session)); | 
 |   session->vtable = vtable; | 
 |   session->pool = pool; | 
 |  | 
 |   /* Ask the library to open the session. */ | 
 |   SVN_ERR(vtable->open_session(session, repos_URL, callbacks, callback_baton, | 
 |                                config, pool)); | 
 |  | 
 |   *session_p = session; | 
 |   return SVN_NO_ERROR; | 
 | } | 
 |  | 
 | 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_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_root(session, &repos_root, pool)); | 
 |   if (! svn_path_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_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_prop(svn_ra_session_t *session, | 
 |                                     svn_revnum_t rev, | 
 |                                     const char *name, | 
 |                                     const svn_string_t *value, | 
 |                                     apr_pool_t *pool) | 
 | { | 
 |   return session->vtable->change_rev_prop(session, rev, name, 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) | 
 | { | 
 |   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) | 
 | { | 
 |   return session->vtable->rev_prop(session, rev, name, value, pool); | 
 | } | 
 |  | 
 | svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session, | 
 |                                        const svn_delta_editor_t **editor, | 
 |                                        void **edit_baton, | 
 |                                        apr_hash_t *revprop_table, | 
 |                                        svn_commit_callback2_t callback, | 
 |                                        void *callback_baton, | 
 |                                        apr_hash_t *lock_tokens, | 
 |                                        svn_boolean_t keep_locks, | 
 |                                        apr_pool_t *pool) | 
 | { | 
 |   return session->vtable->get_commit_editor(session, editor, edit_baton, | 
 |                                             revprop_table, callback, | 
 |                                             callback_baton, lock_tokens, | 
 |                                             keep_locks, 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); | 
 |   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_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) | 
 | { | 
 |   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) | 
 | { | 
 |   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) | 
 | { | 
 |   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, | 
 |                                   apr_hash_t **mergeinfo, | 
 |                                   const apr_array_header_t *paths, | 
 |                                   svn_revnum_t revision, | 
 |                                   svn_mergeinfo_inheritance_t inherit, | 
 |                                   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 *); | 
 |       assert(*path != '/'); | 
 |     } | 
 |  | 
 |   /* Check server Merge Tracking capability. */ | 
 |   err = svn_ra__assert_mergeinfo_capable_server(session, NULL, pool); | 
 |   if (err) | 
 |     { | 
 |       *mergeinfo = NULL; | 
 |       return err; | 
 |     } | 
 |  | 
 |   return session->vtable->get_mergeinfo(session, mergeinfo, paths, | 
 |                                         revision, inherit, 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) | 
 | { | 
 |   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_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)); | 
 |   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_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) | 
 | { | 
 |   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_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)); | 
 |   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_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) | 
 | { | 
 |   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_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)); | 
 |   assert(svn_path_is_empty(status_target) | 
 |          || svn_path_is_single_path_component(status_target)); | 
 |   *reporter = &reporter_3in2_wrapper; | 
 |   *report_baton = b; | 
 |   return session->vtable->do_status(session, | 
 |                                     &(b->reporter3), &(b->reporter3_baton), | 
 |                                     status_target, revision, | 
 |                                     SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse), | 
 |                                     status_editor, status_baton, pool); | 
 | } | 
 |  | 
 | svn_error_t *svn_ra_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) | 
 | { | 
 |   assert(svn_path_is_empty(diff_target) | 
 |          || svn_path_is_single_path_component(diff_target)); | 
 |   return session->vtable->do_diff(session, | 
 |                                   reporter, report_baton, | 
 |                                   revision, diff_target, | 
 |                                   depth, ignore_ancestry, | 
 |                                   text_deltas, versus_url, diff_editor, | 
 |                                   diff_baton, pool); | 
 | } | 
 |  | 
 | svn_error_t *svn_ra_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)); | 
 |   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) | 
 | { | 
 |   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_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) | 
 | { | 
 |   int i; | 
 |   for (i = 0; i < paths->nelts; i++) | 
 |     { | 
 |       const char *path = APR_ARRAY_IDX(paths, i, const char *); | 
 |       assert(*path != '/'); | 
 |     } | 
 |   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_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; | 
 |   int i; | 
 |  | 
 |   for (i = 0; i < paths->nelts; i++) | 
 |     { | 
 |       const char *path = APR_ARRAY_IDX(paths, i, const char *); | 
 |       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_check_path(svn_ra_session_t *session, | 
 |                                const char *path, | 
 |                                svn_revnum_t revision, | 
 |                                svn_node_kind_t *kind, | 
 |                                apr_pool_t *pool) | 
 | { | 
 |   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) | 
 | { | 
 |   assert(*path != '/'); | 
 |   return session->vtable->stat(session, path, revision, dirent, pool); | 
 | } | 
 |  | 
 | 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_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, | 
 |                                   apr_array_header_t *location_revisions, | 
 |                                   apr_pool_t *pool) | 
 | { | 
 |   svn_error_t *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; | 
 |  | 
 |   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_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; | 
 |  | 
 |   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_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; | 
 |  | 
 |   assert(*path != '/'); | 
 |  | 
 |   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(NULL, path_revs); hi; hi = apr_hash_next(hi)) | 
 |     { | 
 |       const void *path; | 
 |       apr_hash_this(hi, &path, NULL, NULL); | 
 |       assert(*((const char *)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(NULL, path_tokens); hi; hi = apr_hash_next(hi)) | 
 |     { | 
 |       const void *path; | 
 |       apr_hash_this(hi, &path, NULL, NULL); | 
 |       assert(*((const char *)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) | 
 | { | 
 |   assert(*path != '/'); | 
 |   return session->vtable->get_lock(session, lock, path, pool); | 
 | } | 
 |  | 
 | svn_error_t *svn_ra_get_locks(svn_ra_session_t *session, | 
 |                               apr_hash_t **locks, | 
 |                               const char *path, | 
 |                               apr_pool_t *pool) | 
 | { | 
 |   assert(*path != '/'); | 
 |   return session->vtable->get_locks(session, locks, path, 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); | 
 |     } | 
 |  | 
 |   SVN_ERR(err); | 
 |  | 
 |   return SVN_NO_ERROR; | 
 | } | 
 |  | 
 | 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_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)); | 
 |  | 
 |           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; | 
 |  | 
 |           SVN_ERR(check_ra_version((*library)->get_version(), scheme)); | 
 |  | 
 |           return SVN_NO_ERROR; | 
 |         } | 
 |     } | 
 |  | 
 |   /* 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 */ |