| /* |
| * util.c : utility functions for the libsvn_client library |
| * |
| * ==================================================================== |
| * 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 <apr_strings.h> |
| |
| #include "svn_hash.h" |
| #include "svn_pools.h" |
| #include "svn_error.h" |
| #include "svn_types.h" |
| #include "svn_opt.h" |
| #include "svn_props.h" |
| #include "svn_path.h" |
| #include "svn_wc.h" |
| #include "svn_client.h" |
| |
| #include "private/svn_client_private.h" |
| #include "private/svn_wc_private.h" |
| #include "private/svn_fspath.h" |
| |
| #include "client.h" |
| |
| #include "svn_private_config.h" |
| |
| svn_client__pathrev_t * |
| svn_client__pathrev_create(const char *repos_root_url, |
| const char *repos_uuid, |
| svn_revnum_t rev, |
| const char *url, |
| apr_pool_t *result_pool) |
| { |
| svn_client__pathrev_t *loc = apr_palloc(result_pool, sizeof(*loc)); |
| |
| SVN_ERR_ASSERT_NO_RETURN(svn_path_is_url(repos_root_url)); |
| SVN_ERR_ASSERT_NO_RETURN(svn_path_is_url(url)); |
| |
| loc->repos_root_url = apr_pstrdup(result_pool, repos_root_url); |
| loc->repos_uuid = apr_pstrdup(result_pool, repos_uuid); |
| loc->rev = rev; |
| loc->url = apr_pstrdup(result_pool, url); |
| return loc; |
| } |
| |
| svn_client__pathrev_t * |
| svn_client__pathrev_create_with_relpath(const char *repos_root_url, |
| const char *repos_uuid, |
| svn_revnum_t rev, |
| const char *relpath, |
| apr_pool_t *result_pool) |
| { |
| SVN_ERR_ASSERT_NO_RETURN(svn_relpath_is_canonical(relpath)); |
| |
| return svn_client__pathrev_create( |
| repos_root_url, repos_uuid, rev, |
| svn_path_url_add_component2(repos_root_url, relpath, result_pool), |
| result_pool); |
| } |
| |
| svn_error_t * |
| svn_client__pathrev_create_with_session(svn_client__pathrev_t **pathrev_p, |
| svn_ra_session_t *ra_session, |
| svn_revnum_t rev, |
| const char *url, |
| apr_pool_t *result_pool) |
| { |
| svn_client__pathrev_t *pathrev = apr_palloc(result_pool, sizeof(*pathrev)); |
| |
| SVN_ERR_ASSERT(svn_path_is_url(url)); |
| |
| SVN_ERR(svn_ra_get_repos_root2(ra_session, &pathrev->repos_root_url, |
| result_pool)); |
| SVN_ERR(svn_ra_get_uuid2(ra_session, &pathrev->repos_uuid, result_pool)); |
| pathrev->rev = rev; |
| pathrev->url = apr_pstrdup(result_pool, url); |
| *pathrev_p = pathrev; |
| SVN_ERR_ASSERT(svn_uri__is_ancestor(pathrev->repos_root_url, url)); |
| return SVN_NO_ERROR; |
| } |
| |
| svn_client__pathrev_t * |
| svn_client__pathrev_dup(const svn_client__pathrev_t *pathrev, |
| apr_pool_t *result_pool) |
| { |
| return svn_client__pathrev_create( |
| pathrev->repos_root_url, pathrev->repos_uuid, |
| pathrev->rev, pathrev->url, result_pool); |
| } |
| |
| svn_client__pathrev_t * |
| svn_client__pathrev_join_relpath(const svn_client__pathrev_t *pathrev, |
| const char *relpath, |
| apr_pool_t *result_pool) |
| { |
| return svn_client__pathrev_create( |
| pathrev->repos_root_url, pathrev->repos_uuid, pathrev->rev, |
| svn_path_url_add_component2(pathrev->url, relpath, result_pool), |
| result_pool); |
| } |
| |
| const char * |
| svn_client__pathrev_relpath(const svn_client__pathrev_t *pathrev, |
| apr_pool_t *result_pool) |
| { |
| return svn_uri_skip_ancestor(pathrev->repos_root_url, pathrev->url, |
| result_pool); |
| } |
| |
| const char * |
| svn_client__pathrev_fspath(const svn_client__pathrev_t *pathrev, |
| apr_pool_t *result_pool) |
| { |
| return svn_fspath__canonicalize(svn_uri_skip_ancestor( |
| pathrev->repos_root_url, pathrev->url, |
| result_pool), |
| result_pool); |
| } |
| |
| |
| svn_client_commit_item3_t * |
| svn_client_commit_item3_create(apr_pool_t *pool) |
| { |
| svn_client_commit_item3_t *item = apr_pcalloc(pool, sizeof(*item)); |
| |
| item->revision = SVN_INVALID_REVNUM; |
| item->copyfrom_rev = SVN_INVALID_REVNUM; |
| item->kind = svn_node_unknown; |
| |
| return item; |
| } |
| |
| svn_client_commit_item3_t * |
| svn_client_commit_item3_dup(const svn_client_commit_item3_t *item, |
| apr_pool_t *pool) |
| { |
| svn_client_commit_item3_t *new_item = apr_palloc(pool, sizeof(*new_item)); |
| |
| *new_item = *item; |
| |
| if (new_item->path) |
| new_item->path = apr_pstrdup(pool, new_item->path); |
| |
| if (new_item->url) |
| new_item->url = apr_pstrdup(pool, new_item->url); |
| |
| if (new_item->copyfrom_url) |
| new_item->copyfrom_url = apr_pstrdup(pool, new_item->copyfrom_url); |
| |
| if (new_item->incoming_prop_changes) |
| new_item->incoming_prop_changes = |
| svn_prop_array_dup(new_item->incoming_prop_changes, pool); |
| |
| if (new_item->outgoing_prop_changes) |
| new_item->outgoing_prop_changes = |
| svn_prop_array_dup(new_item->outgoing_prop_changes, pool); |
| |
| if (new_item->session_relpath) |
| new_item->session_relpath = apr_pstrdup(pool, new_item->session_relpath); |
| |
| if (new_item->moved_from_abspath) |
| new_item->moved_from_abspath = apr_pstrdup(pool, |
| new_item->moved_from_abspath); |
| |
| return new_item; |
| } |
| |
| svn_error_t * |
| svn_client__wc_node_get_base(svn_client__pathrev_t **base_p, |
| const char *wc_abspath, |
| svn_wc_context_t *wc_ctx, |
| apr_pool_t *result_pool, |
| apr_pool_t *scratch_pool) |
| { |
| const char *relpath; |
| |
| *base_p = apr_palloc(result_pool, sizeof(**base_p)); |
| |
| SVN_ERR(svn_wc__node_get_base(NULL, |
| &(*base_p)->rev, |
| &relpath, |
| &(*base_p)->repos_root_url, |
| &(*base_p)->repos_uuid, |
| NULL, |
| wc_ctx, wc_abspath, |
| TRUE /* ignore_enoent */, |
| result_pool, scratch_pool)); |
| if ((*base_p)->repos_root_url && relpath) |
| { |
| (*base_p)->url = svn_path_url_add_component2( |
| (*base_p)->repos_root_url, relpath, result_pool); |
| } |
| else |
| { |
| *base_p = NULL; |
| } |
| return SVN_NO_ERROR; |
| } |
| |
| svn_error_t * |
| svn_client__wc_node_get_origin(svn_client__pathrev_t **origin_p, |
| const char *wc_abspath, |
| svn_client_ctx_t *ctx, |
| apr_pool_t *result_pool, |
| apr_pool_t *scratch_pool) |
| { |
| const char *relpath; |
| |
| *origin_p = apr_palloc(result_pool, sizeof(**origin_p)); |
| |
| SVN_ERR(svn_wc__node_get_origin(NULL /* is_copy */, |
| &(*origin_p)->rev, |
| &relpath, |
| &(*origin_p)->repos_root_url, |
| &(*origin_p)->repos_uuid, |
| NULL, NULL, |
| ctx->wc_ctx, wc_abspath, |
| FALSE /* scan_deleted */, |
| result_pool, scratch_pool)); |
| if ((*origin_p)->repos_root_url && relpath) |
| { |
| (*origin_p)->url = svn_path_url_add_component2( |
| (*origin_p)->repos_root_url, relpath, result_pool); |
| } |
| else |
| { |
| *origin_p = NULL; |
| } |
| return SVN_NO_ERROR; |
| } |
| |
| svn_error_t * |
| svn_client_get_repos_root(const char **repos_root, |
| const char **repos_uuid, |
| const char *abspath_or_url, |
| svn_client_ctx_t *ctx, |
| apr_pool_t *result_pool, |
| apr_pool_t *scratch_pool) |
| { |
| svn_ra_session_t *ra_session; |
| |
| /* If PATH_OR_URL is a local path we can fetch the repos root locally. */ |
| if (!svn_path_is_url(abspath_or_url)) |
| { |
| svn_error_t *err; |
| err = svn_wc__node_get_repos_info(NULL, NULL, repos_root, repos_uuid, |
| ctx->wc_ctx, abspath_or_url, |
| result_pool, scratch_pool); |
| |
| if (err) |
| { |
| if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND |
| && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) |
| return svn_error_trace(err); |
| |
| svn_error_clear(err); |
| if (repos_root) |
| *repos_root = NULL; |
| if (repos_uuid) |
| *repos_uuid = NULL; |
| } |
| return SVN_NO_ERROR; |
| } |
| |
| /* If PATH_OR_URL was a URL, we use the RA layer to look it up. */ |
| SVN_ERR(svn_client_open_ra_session2(&ra_session, abspath_or_url, NULL, |
| ctx, scratch_pool, scratch_pool)); |
| |
| if (repos_root) |
| SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool)); |
| if (repos_uuid) |
| SVN_ERR(svn_ra_get_uuid2(ra_session, repos_uuid, result_pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| const svn_opt_revision_t * |
| svn_cl__rev_default_to_head_or_base(const svn_opt_revision_t *revision, |
| const char *path_or_url) |
| { |
| static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; |
| static svn_opt_revision_t base_rev = { svn_opt_revision_base, { 0 } }; |
| |
| if (revision->kind == svn_opt_revision_unspecified) |
| return svn_path_is_url(path_or_url) ? &head_rev : &base_rev; |
| return revision; |
| } |
| |
| const svn_opt_revision_t * |
| svn_cl__rev_default_to_head_or_working(const svn_opt_revision_t *revision, |
| const char *path_or_url) |
| { |
| static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; |
| static svn_opt_revision_t work_rev = { svn_opt_revision_working, { 0 } }; |
| |
| if (revision->kind == svn_opt_revision_unspecified) |
| return svn_path_is_url(path_or_url) ? &head_rev : &work_rev; |
| return revision; |
| } |
| |
| const svn_opt_revision_t * |
| svn_cl__rev_default_to_peg(const svn_opt_revision_t *revision, |
| const svn_opt_revision_t *peg_revision) |
| { |
| if (revision->kind == svn_opt_revision_unspecified) |
| return peg_revision; |
| return revision; |
| } |
| |
| svn_error_t * |
| svn_client__assert_homogeneous_target_type(const apr_array_header_t *targets) |
| { |
| svn_boolean_t wc_present = FALSE, url_present = FALSE; |
| int i; |
| |
| for (i = 0; i < targets->nelts; ++i) |
| { |
| const char *target = APR_ARRAY_IDX(targets, i, const char *); |
| if (! svn_path_is_url(target)) |
| wc_present = TRUE; |
| else |
| url_present = TRUE; |
| if (url_present && wc_present) |
| return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, |
| _("Cannot mix repository and working copy " |
| "targets")); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| struct shim_callbacks_baton |
| { |
| svn_wc_context_t *wc_ctx; |
| apr_hash_t *relpath_map; |
| }; |
| |
| static svn_error_t * |
| fetch_props_func(apr_hash_t **props, |
| void *baton, |
| const char *path, |
| svn_revnum_t base_revision, |
| apr_pool_t *result_pool, |
| apr_pool_t *scratch_pool) |
| { |
| struct shim_callbacks_baton *scb = baton; |
| const char *local_abspath; |
| |
| local_abspath = svn_hash_gets(scb->relpath_map, path); |
| if (!local_abspath) |
| { |
| *props = apr_hash_make(result_pool); |
| return SVN_NO_ERROR; |
| } |
| |
| /* Reads the pristine properties of WORKING, not those of BASE */ |
| SVN_ERR(svn_wc_get_pristine_props(props, scb->wc_ctx, local_abspath, |
| result_pool, scratch_pool)); |
| |
| if (!*props) |
| *props = apr_hash_make(result_pool); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| static svn_error_t * |
| fetch_kind_func(svn_node_kind_t *kind, |
| void *baton, |
| const char *path, |
| svn_revnum_t base_revision, |
| apr_pool_t *scratch_pool) |
| { |
| struct shim_callbacks_baton *scb = baton; |
| const char *local_abspath; |
| |
| local_abspath = svn_hash_gets(scb->relpath_map, path); |
| if (!local_abspath) |
| { |
| *kind = svn_node_unknown; |
| return SVN_NO_ERROR; |
| } |
| /* Reads the WORKING kind. Not the BASE kind */ |
| SVN_ERR(svn_wc_read_kind2(kind, scb->wc_ctx, local_abspath, |
| TRUE, FALSE, scratch_pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| static svn_error_t * |
| fetch_base_func(const char **filename, |
| void *baton, |
| const char *path, |
| svn_revnum_t base_revision, |
| apr_pool_t *result_pool, |
| apr_pool_t *scratch_pool) |
| { |
| struct shim_callbacks_baton *scb = baton; |
| const char *local_abspath; |
| svn_stream_t *pristine_stream; |
| svn_stream_t *temp_stream; |
| svn_error_t *err; |
| |
| local_abspath = svn_hash_gets(scb->relpath_map, path); |
| if (!local_abspath) |
| { |
| *filename = NULL; |
| return SVN_NO_ERROR; |
| } |
| |
| /* Reads the pristine of WORKING, not of BASE */ |
| err = svn_wc_get_pristine_contents2(&pristine_stream, scb->wc_ctx, |
| local_abspath, scratch_pool, |
| scratch_pool); |
| if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) |
| { |
| svn_error_clear(err); |
| *filename = NULL; |
| return SVN_NO_ERROR; |
| } |
| else if (err) |
| return svn_error_trace(err); |
| |
| SVN_ERR(svn_stream_open_unique(&temp_stream, filename, NULL, |
| svn_io_file_del_on_pool_cleanup, |
| result_pool, scratch_pool)); |
| SVN_ERR(svn_stream_copy3(pristine_stream, temp_stream, NULL, NULL, |
| scratch_pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| svn_delta_shim_callbacks_t * |
| svn_client__get_shim_callbacks(svn_wc_context_t *wc_ctx, |
| apr_hash_t *relpath_map, |
| apr_pool_t *result_pool) |
| { |
| svn_delta_shim_callbacks_t *callbacks = |
| svn_delta_shim_callbacks_default(result_pool); |
| struct shim_callbacks_baton *scb = apr_pcalloc(result_pool, sizeof(*scb)); |
| |
| scb->wc_ctx = wc_ctx; |
| if (relpath_map) |
| scb->relpath_map = relpath_map; |
| else |
| scb->relpath_map = apr_hash_make(result_pool); |
| |
| callbacks->fetch_props_func = fetch_props_func; |
| callbacks->fetch_kind_func = fetch_kind_func; |
| callbacks->fetch_base_func = fetch_base_func; |
| callbacks->fetch_baton = scb; |
| |
| return callbacks; |
| } |