util.c revision 251881
1/* 2 * util.c : utility functions for the libsvn_client library 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24#include <apr_pools.h> 25#include <apr_strings.h> 26 27#include "svn_hash.h" 28#include "svn_pools.h" 29#include "svn_error.h" 30#include "svn_types.h" 31#include "svn_opt.h" 32#include "svn_props.h" 33#include "svn_path.h" 34#include "svn_wc.h" 35#include "svn_client.h" 36 37#include "private/svn_client_private.h" 38#include "private/svn_wc_private.h" 39#include "private/svn_fspath.h" 40 41#include "client.h" 42 43#include "svn_private_config.h" 44 45svn_client__pathrev_t * 46svn_client__pathrev_create(const char *repos_root_url, 47 const char *repos_uuid, 48 svn_revnum_t rev, 49 const char *url, 50 apr_pool_t *result_pool) 51{ 52 svn_client__pathrev_t *loc = apr_palloc(result_pool, sizeof(*loc)); 53 54 SVN_ERR_ASSERT_NO_RETURN(svn_path_is_url(repos_root_url)); 55 SVN_ERR_ASSERT_NO_RETURN(svn_path_is_url(url)); 56 57 loc->repos_root_url = apr_pstrdup(result_pool, repos_root_url); 58 loc->repos_uuid = apr_pstrdup(result_pool, repos_uuid); 59 loc->rev = rev; 60 loc->url = apr_pstrdup(result_pool, url); 61 return loc; 62} 63 64svn_client__pathrev_t * 65svn_client__pathrev_create_with_relpath(const char *repos_root_url, 66 const char *repos_uuid, 67 svn_revnum_t rev, 68 const char *relpath, 69 apr_pool_t *result_pool) 70{ 71 SVN_ERR_ASSERT_NO_RETURN(svn_relpath_is_canonical(relpath)); 72 73 return svn_client__pathrev_create( 74 repos_root_url, repos_uuid, rev, 75 svn_path_url_add_component2(repos_root_url, relpath, result_pool), 76 result_pool); 77} 78 79svn_error_t * 80svn_client__pathrev_create_with_session(svn_client__pathrev_t **pathrev_p, 81 svn_ra_session_t *ra_session, 82 svn_revnum_t rev, 83 const char *url, 84 apr_pool_t *result_pool) 85{ 86 svn_client__pathrev_t *pathrev = apr_palloc(result_pool, sizeof(*pathrev)); 87 88 SVN_ERR_ASSERT(svn_path_is_url(url)); 89 90 SVN_ERR(svn_ra_get_repos_root2(ra_session, &pathrev->repos_root_url, 91 result_pool)); 92 SVN_ERR(svn_ra_get_uuid2(ra_session, &pathrev->repos_uuid, result_pool)); 93 pathrev->rev = rev; 94 pathrev->url = apr_pstrdup(result_pool, url); 95 *pathrev_p = pathrev; 96 return SVN_NO_ERROR; 97} 98 99svn_client__pathrev_t * 100svn_client__pathrev_dup(const svn_client__pathrev_t *pathrev, 101 apr_pool_t *result_pool) 102{ 103 return svn_client__pathrev_create( 104 pathrev->repos_root_url, pathrev->repos_uuid, 105 pathrev->rev, pathrev->url, result_pool); 106} 107 108svn_client__pathrev_t * 109svn_client__pathrev_join_relpath(const svn_client__pathrev_t *pathrev, 110 const char *relpath, 111 apr_pool_t *result_pool) 112{ 113 return svn_client__pathrev_create( 114 pathrev->repos_root_url, pathrev->repos_uuid, pathrev->rev, 115 svn_path_url_add_component2(pathrev->url, relpath, result_pool), 116 result_pool); 117} 118 119const char * 120svn_client__pathrev_relpath(const svn_client__pathrev_t *pathrev, 121 apr_pool_t *result_pool) 122{ 123 return svn_uri_skip_ancestor(pathrev->repos_root_url, pathrev->url, 124 result_pool); 125} 126 127const char * 128svn_client__pathrev_fspath(const svn_client__pathrev_t *pathrev, 129 apr_pool_t *result_pool) 130{ 131 return svn_fspath__canonicalize(svn_uri_skip_ancestor( 132 pathrev->repos_root_url, pathrev->url, 133 result_pool), 134 result_pool); 135} 136 137 138svn_client_commit_item3_t * 139svn_client_commit_item3_create(apr_pool_t *pool) 140{ 141 return apr_pcalloc(pool, sizeof(svn_client_commit_item3_t)); 142} 143 144svn_client_commit_item3_t * 145svn_client_commit_item3_dup(const svn_client_commit_item3_t *item, 146 apr_pool_t *pool) 147{ 148 svn_client_commit_item3_t *new_item = apr_palloc(pool, sizeof(*new_item)); 149 150 *new_item = *item; 151 152 if (new_item->path) 153 new_item->path = apr_pstrdup(pool, new_item->path); 154 155 if (new_item->url) 156 new_item->url = apr_pstrdup(pool, new_item->url); 157 158 if (new_item->copyfrom_url) 159 new_item->copyfrom_url = apr_pstrdup(pool, new_item->copyfrom_url); 160 161 if (new_item->incoming_prop_changes) 162 new_item->incoming_prop_changes = 163 svn_prop_array_dup(new_item->incoming_prop_changes, pool); 164 165 if (new_item->outgoing_prop_changes) 166 new_item->outgoing_prop_changes = 167 svn_prop_array_dup(new_item->outgoing_prop_changes, pool); 168 169 return new_item; 170} 171 172svn_error_t * 173svn_client__wc_node_get_base(svn_client__pathrev_t **base_p, 174 const char *wc_abspath, 175 svn_wc_context_t *wc_ctx, 176 apr_pool_t *result_pool, 177 apr_pool_t *scratch_pool) 178{ 179 const char *relpath; 180 181 *base_p = apr_palloc(result_pool, sizeof(**base_p)); 182 183 SVN_ERR(svn_wc__node_get_base(NULL, 184 &(*base_p)->rev, 185 &relpath, 186 &(*base_p)->repos_root_url, 187 &(*base_p)->repos_uuid, 188 NULL, 189 wc_ctx, wc_abspath, 190 TRUE /* ignore_enoent */, 191 TRUE /* show_hidden */, 192 result_pool, scratch_pool)); 193 if ((*base_p)->repos_root_url && relpath) 194 { 195 (*base_p)->url = svn_path_url_add_component2( 196 (*base_p)->repos_root_url, relpath, result_pool); 197 } 198 else 199 { 200 *base_p = NULL; 201 } 202 return SVN_NO_ERROR; 203} 204 205svn_error_t * 206svn_client__wc_node_get_origin(svn_client__pathrev_t **origin_p, 207 const char *wc_abspath, 208 svn_client_ctx_t *ctx, 209 apr_pool_t *result_pool, 210 apr_pool_t *scratch_pool) 211{ 212 const char *relpath; 213 214 *origin_p = apr_palloc(result_pool, sizeof(**origin_p)); 215 216 SVN_ERR(svn_wc__node_get_origin(NULL /* is_copy */, 217 &(*origin_p)->rev, 218 &relpath, 219 &(*origin_p)->repos_root_url, 220 &(*origin_p)->repos_uuid, 221 NULL, ctx->wc_ctx, wc_abspath, 222 FALSE /* scan_deleted */, 223 result_pool, scratch_pool)); 224 if ((*origin_p)->repos_root_url && relpath) 225 { 226 (*origin_p)->url = svn_path_url_add_component2( 227 (*origin_p)->repos_root_url, relpath, result_pool); 228 } 229 else 230 { 231 *origin_p = NULL; 232 } 233 return SVN_NO_ERROR; 234} 235 236svn_error_t * 237svn_client_get_repos_root(const char **repos_root, 238 const char **repos_uuid, 239 const char *abspath_or_url, 240 svn_client_ctx_t *ctx, 241 apr_pool_t *result_pool, 242 apr_pool_t *scratch_pool) 243{ 244 svn_ra_session_t *ra_session; 245 246 /* If PATH_OR_URL is a local path we can fetch the repos root locally. */ 247 if (!svn_path_is_url(abspath_or_url)) 248 { 249 svn_error_t *err; 250 err = svn_wc__node_get_repos_info(NULL, NULL, repos_root, repos_uuid, 251 ctx->wc_ctx, abspath_or_url, 252 result_pool, scratch_pool); 253 254 if (err) 255 { 256 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 257 && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 258 return svn_error_trace(err); 259 260 svn_error_clear(err); 261 if (repos_root) 262 *repos_root = NULL; 263 if (repos_uuid) 264 *repos_uuid = NULL; 265 } 266 return SVN_NO_ERROR; 267 } 268 269 /* If PATH_OR_URL was a URL, we use the RA layer to look it up. */ 270 SVN_ERR(svn_client_open_ra_session2(&ra_session, abspath_or_url, NULL, 271 ctx, scratch_pool, scratch_pool)); 272 273 if (repos_root) 274 SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool)); 275 if (repos_uuid) 276 SVN_ERR(svn_ra_get_uuid2(ra_session, repos_uuid, result_pool)); 277 278 return SVN_NO_ERROR; 279} 280 281const svn_opt_revision_t * 282svn_cl__rev_default_to_head_or_base(const svn_opt_revision_t *revision, 283 const char *path_or_url) 284{ 285 static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; 286 static svn_opt_revision_t base_rev = { svn_opt_revision_base, { 0 } }; 287 288 if (revision->kind == svn_opt_revision_unspecified) 289 return svn_path_is_url(path_or_url) ? &head_rev : &base_rev; 290 return revision; 291} 292 293const svn_opt_revision_t * 294svn_cl__rev_default_to_head_or_working(const svn_opt_revision_t *revision, 295 const char *path_or_url) 296{ 297 static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; 298 static svn_opt_revision_t work_rev = { svn_opt_revision_working, { 0 } }; 299 300 if (revision->kind == svn_opt_revision_unspecified) 301 return svn_path_is_url(path_or_url) ? &head_rev : &work_rev; 302 return revision; 303} 304 305const svn_opt_revision_t * 306svn_cl__rev_default_to_peg(const svn_opt_revision_t *revision, 307 const svn_opt_revision_t *peg_revision) 308{ 309 if (revision->kind == svn_opt_revision_unspecified) 310 return peg_revision; 311 return revision; 312} 313 314svn_error_t * 315svn_client__assert_homogeneous_target_type(const apr_array_header_t *targets) 316{ 317 svn_boolean_t wc_present = FALSE, url_present = FALSE; 318 int i; 319 320 for (i = 0; i < targets->nelts; ++i) 321 { 322 const char *target = APR_ARRAY_IDX(targets, i, const char *); 323 if (! svn_path_is_url(target)) 324 wc_present = TRUE; 325 else 326 url_present = TRUE; 327 if (url_present && wc_present) 328 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 329 _("Cannot mix repository and working copy " 330 "targets")); 331 } 332 333 return SVN_NO_ERROR; 334} 335 336struct shim_callbacks_baton 337{ 338 svn_wc_context_t *wc_ctx; 339 apr_hash_t *relpath_map; 340}; 341 342static svn_error_t * 343fetch_props_func(apr_hash_t **props, 344 void *baton, 345 const char *path, 346 svn_revnum_t base_revision, 347 apr_pool_t *result_pool, 348 apr_pool_t *scratch_pool) 349{ 350 struct shim_callbacks_baton *scb = baton; 351 const char *local_abspath; 352 353 local_abspath = svn_hash_gets(scb->relpath_map, path); 354 if (!local_abspath) 355 { 356 *props = apr_hash_make(result_pool); 357 return SVN_NO_ERROR; 358 } 359 360 /* Reads the pristine properties of WORKING, not those of BASE */ 361 SVN_ERR(svn_wc_get_pristine_props(props, scb->wc_ctx, local_abspath, 362 result_pool, scratch_pool)); 363 364 if (!*props) 365 *props = apr_hash_make(result_pool); 366 367 return SVN_NO_ERROR; 368} 369 370static svn_error_t * 371fetch_kind_func(svn_node_kind_t *kind, 372 void *baton, 373 const char *path, 374 svn_revnum_t base_revision, 375 apr_pool_t *scratch_pool) 376{ 377 struct shim_callbacks_baton *scb = baton; 378 const char *local_abspath; 379 380 local_abspath = svn_hash_gets(scb->relpath_map, path); 381 if (!local_abspath) 382 { 383 *kind = svn_node_unknown; 384 return SVN_NO_ERROR; 385 } 386 /* Reads the WORKING kind. Not the BASE kind */ 387 SVN_ERR(svn_wc_read_kind2(kind, scb->wc_ctx, local_abspath, 388 TRUE, FALSE, scratch_pool)); 389 390 return SVN_NO_ERROR; 391} 392 393static svn_error_t * 394fetch_base_func(const char **filename, 395 void *baton, 396 const char *path, 397 svn_revnum_t base_revision, 398 apr_pool_t *result_pool, 399 apr_pool_t *scratch_pool) 400{ 401 struct shim_callbacks_baton *scb = baton; 402 const char *local_abspath; 403 svn_stream_t *pristine_stream; 404 svn_stream_t *temp_stream; 405 svn_error_t *err; 406 407 local_abspath = svn_hash_gets(scb->relpath_map, path); 408 if (!local_abspath) 409 { 410 *filename = NULL; 411 return SVN_NO_ERROR; 412 } 413 414 /* Reads the pristine of WORKING, not of BASE */ 415 err = svn_wc_get_pristine_contents2(&pristine_stream, scb->wc_ctx, 416 local_abspath, scratch_pool, 417 scratch_pool); 418 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 419 { 420 svn_error_clear(err); 421 *filename = NULL; 422 return SVN_NO_ERROR; 423 } 424 else if (err) 425 return svn_error_trace(err); 426 427 SVN_ERR(svn_stream_open_unique(&temp_stream, filename, NULL, 428 svn_io_file_del_on_pool_cleanup, 429 result_pool, scratch_pool)); 430 SVN_ERR(svn_stream_copy3(pristine_stream, temp_stream, NULL, NULL, 431 scratch_pool)); 432 433 return SVN_NO_ERROR; 434} 435 436svn_delta_shim_callbacks_t * 437svn_client__get_shim_callbacks(svn_wc_context_t *wc_ctx, 438 apr_hash_t *relpath_map, 439 apr_pool_t *result_pool) 440{ 441 svn_delta_shim_callbacks_t *callbacks = 442 svn_delta_shim_callbacks_default(result_pool); 443 struct shim_callbacks_baton *scb = apr_pcalloc(result_pool, sizeof(*scb)); 444 445 scb->wc_ctx = wc_ctx; 446 if (relpath_map) 447 scb->relpath_map = relpath_map; 448 else 449 scb->relpath_map = apr_hash_make(result_pool); 450 451 callbacks->fetch_props_func = fetch_props_func; 452 callbacks->fetch_kind_func = fetch_kind_func; 453 callbacks->fetch_base_func = fetch_base_func; 454 callbacks->fetch_baton = scb; 455 456 return callbacks; 457} 458