util.c revision 299742
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 svn_client_commit_item3_t *item = apr_pcalloc(pool, sizeof(*item)); 142 143 item->revision = SVN_INVALID_REVNUM; 144 item->copyfrom_rev = SVN_INVALID_REVNUM; 145 item->kind = svn_node_unknown; 146 147 return item; 148} 149 150svn_client_commit_item3_t * 151svn_client_commit_item3_dup(const svn_client_commit_item3_t *item, 152 apr_pool_t *pool) 153{ 154 svn_client_commit_item3_t *new_item = apr_palloc(pool, sizeof(*new_item)); 155 156 *new_item = *item; 157 158 if (new_item->path) 159 new_item->path = apr_pstrdup(pool, new_item->path); 160 161 if (new_item->url) 162 new_item->url = apr_pstrdup(pool, new_item->url); 163 164 if (new_item->copyfrom_url) 165 new_item->copyfrom_url = apr_pstrdup(pool, new_item->copyfrom_url); 166 167 if (new_item->incoming_prop_changes) 168 new_item->incoming_prop_changes = 169 svn_prop_array_dup(new_item->incoming_prop_changes, pool); 170 171 if (new_item->outgoing_prop_changes) 172 new_item->outgoing_prop_changes = 173 svn_prop_array_dup(new_item->outgoing_prop_changes, pool); 174 175 if (new_item->session_relpath) 176 new_item->session_relpath = apr_pstrdup(pool, new_item->session_relpath); 177 178 if (new_item->moved_from_abspath) 179 new_item->moved_from_abspath = apr_pstrdup(pool, 180 new_item->moved_from_abspath); 181 182 return new_item; 183} 184 185svn_error_t * 186svn_client__wc_node_get_base(svn_client__pathrev_t **base_p, 187 const char *wc_abspath, 188 svn_wc_context_t *wc_ctx, 189 apr_pool_t *result_pool, 190 apr_pool_t *scratch_pool) 191{ 192 const char *relpath; 193 194 *base_p = apr_palloc(result_pool, sizeof(**base_p)); 195 196 SVN_ERR(svn_wc__node_get_base(NULL, 197 &(*base_p)->rev, 198 &relpath, 199 &(*base_p)->repos_root_url, 200 &(*base_p)->repos_uuid, 201 NULL, 202 wc_ctx, wc_abspath, 203 TRUE /* ignore_enoent */, 204 result_pool, scratch_pool)); 205 if ((*base_p)->repos_root_url && relpath) 206 { 207 (*base_p)->url = svn_path_url_add_component2( 208 (*base_p)->repos_root_url, relpath, result_pool); 209 } 210 else 211 { 212 *base_p = NULL; 213 } 214 return SVN_NO_ERROR; 215} 216 217svn_error_t * 218svn_client__wc_node_get_origin(svn_client__pathrev_t **origin_p, 219 const char *wc_abspath, 220 svn_client_ctx_t *ctx, 221 apr_pool_t *result_pool, 222 apr_pool_t *scratch_pool) 223{ 224 const char *relpath; 225 226 *origin_p = apr_palloc(result_pool, sizeof(**origin_p)); 227 228 SVN_ERR(svn_wc__node_get_origin(NULL /* is_copy */, 229 &(*origin_p)->rev, 230 &relpath, 231 &(*origin_p)->repos_root_url, 232 &(*origin_p)->repos_uuid, 233 NULL, NULL, 234 ctx->wc_ctx, wc_abspath, 235 FALSE /* scan_deleted */, 236 result_pool, scratch_pool)); 237 if ((*origin_p)->repos_root_url && relpath) 238 { 239 (*origin_p)->url = svn_path_url_add_component2( 240 (*origin_p)->repos_root_url, relpath, result_pool); 241 } 242 else 243 { 244 *origin_p = NULL; 245 } 246 return SVN_NO_ERROR; 247} 248 249svn_error_t * 250svn_client_get_repos_root(const char **repos_root, 251 const char **repos_uuid, 252 const char *abspath_or_url, 253 svn_client_ctx_t *ctx, 254 apr_pool_t *result_pool, 255 apr_pool_t *scratch_pool) 256{ 257 svn_ra_session_t *ra_session; 258 259 /* If PATH_OR_URL is a local path we can fetch the repos root locally. */ 260 if (!svn_path_is_url(abspath_or_url)) 261 { 262 svn_error_t *err; 263 err = svn_wc__node_get_repos_info(NULL, NULL, repos_root, repos_uuid, 264 ctx->wc_ctx, abspath_or_url, 265 result_pool, scratch_pool); 266 267 if (err) 268 { 269 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 270 && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 271 return svn_error_trace(err); 272 273 svn_error_clear(err); 274 if (repos_root) 275 *repos_root = NULL; 276 if (repos_uuid) 277 *repos_uuid = NULL; 278 } 279 return SVN_NO_ERROR; 280 } 281 282 /* If PATH_OR_URL was a URL, we use the RA layer to look it up. */ 283 SVN_ERR(svn_client_open_ra_session2(&ra_session, abspath_or_url, NULL, 284 ctx, scratch_pool, scratch_pool)); 285 286 if (repos_root) 287 SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool)); 288 if (repos_uuid) 289 SVN_ERR(svn_ra_get_uuid2(ra_session, repos_uuid, result_pool)); 290 291 return SVN_NO_ERROR; 292} 293 294const svn_opt_revision_t * 295svn_cl__rev_default_to_head_or_base(const svn_opt_revision_t *revision, 296 const char *path_or_url) 297{ 298 static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; 299 static svn_opt_revision_t base_rev = { svn_opt_revision_base, { 0 } }; 300 301 if (revision->kind == svn_opt_revision_unspecified) 302 return svn_path_is_url(path_or_url) ? &head_rev : &base_rev; 303 return revision; 304} 305 306const svn_opt_revision_t * 307svn_cl__rev_default_to_head_or_working(const svn_opt_revision_t *revision, 308 const char *path_or_url) 309{ 310 static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; 311 static svn_opt_revision_t work_rev = { svn_opt_revision_working, { 0 } }; 312 313 if (revision->kind == svn_opt_revision_unspecified) 314 return svn_path_is_url(path_or_url) ? &head_rev : &work_rev; 315 return revision; 316} 317 318const svn_opt_revision_t * 319svn_cl__rev_default_to_peg(const svn_opt_revision_t *revision, 320 const svn_opt_revision_t *peg_revision) 321{ 322 if (revision->kind == svn_opt_revision_unspecified) 323 return peg_revision; 324 return revision; 325} 326 327svn_error_t * 328svn_client__assert_homogeneous_target_type(const apr_array_header_t *targets) 329{ 330 svn_boolean_t wc_present = FALSE, url_present = FALSE; 331 int i; 332 333 for (i = 0; i < targets->nelts; ++i) 334 { 335 const char *target = APR_ARRAY_IDX(targets, i, const char *); 336 if (! svn_path_is_url(target)) 337 wc_present = TRUE; 338 else 339 url_present = TRUE; 340 if (url_present && wc_present) 341 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 342 _("Cannot mix repository and working copy " 343 "targets")); 344 } 345 346 return SVN_NO_ERROR; 347} 348 349struct shim_callbacks_baton 350{ 351 svn_wc_context_t *wc_ctx; 352 apr_hash_t *relpath_map; 353}; 354 355static svn_error_t * 356fetch_props_func(apr_hash_t **props, 357 void *baton, 358 const char *path, 359 svn_revnum_t base_revision, 360 apr_pool_t *result_pool, 361 apr_pool_t *scratch_pool) 362{ 363 struct shim_callbacks_baton *scb = baton; 364 const char *local_abspath; 365 366 local_abspath = svn_hash_gets(scb->relpath_map, path); 367 if (!local_abspath) 368 { 369 *props = apr_hash_make(result_pool); 370 return SVN_NO_ERROR; 371 } 372 373 /* Reads the pristine properties of WORKING, not those of BASE */ 374 SVN_ERR(svn_wc_get_pristine_props(props, scb->wc_ctx, local_abspath, 375 result_pool, scratch_pool)); 376 377 if (!*props) 378 *props = apr_hash_make(result_pool); 379 380 return SVN_NO_ERROR; 381} 382 383static svn_error_t * 384fetch_kind_func(svn_node_kind_t *kind, 385 void *baton, 386 const char *path, 387 svn_revnum_t base_revision, 388 apr_pool_t *scratch_pool) 389{ 390 struct shim_callbacks_baton *scb = baton; 391 const char *local_abspath; 392 393 local_abspath = svn_hash_gets(scb->relpath_map, path); 394 if (!local_abspath) 395 { 396 *kind = svn_node_unknown; 397 return SVN_NO_ERROR; 398 } 399 /* Reads the WORKING kind. Not the BASE kind */ 400 SVN_ERR(svn_wc_read_kind2(kind, scb->wc_ctx, local_abspath, 401 TRUE, FALSE, scratch_pool)); 402 403 return SVN_NO_ERROR; 404} 405 406static svn_error_t * 407fetch_base_func(const char **filename, 408 void *baton, 409 const char *path, 410 svn_revnum_t base_revision, 411 apr_pool_t *result_pool, 412 apr_pool_t *scratch_pool) 413{ 414 struct shim_callbacks_baton *scb = baton; 415 const char *local_abspath; 416 svn_stream_t *pristine_stream; 417 svn_stream_t *temp_stream; 418 svn_error_t *err; 419 420 local_abspath = svn_hash_gets(scb->relpath_map, path); 421 if (!local_abspath) 422 { 423 *filename = NULL; 424 return SVN_NO_ERROR; 425 } 426 427 /* Reads the pristine of WORKING, not of BASE */ 428 err = svn_wc_get_pristine_contents2(&pristine_stream, scb->wc_ctx, 429 local_abspath, scratch_pool, 430 scratch_pool); 431 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 432 { 433 svn_error_clear(err); 434 *filename = NULL; 435 return SVN_NO_ERROR; 436 } 437 else if (err) 438 return svn_error_trace(err); 439 440 SVN_ERR(svn_stream_open_unique(&temp_stream, filename, NULL, 441 svn_io_file_del_on_pool_cleanup, 442 result_pool, scratch_pool)); 443 SVN_ERR(svn_stream_copy3(pristine_stream, temp_stream, NULL, NULL, 444 scratch_pool)); 445 446 return SVN_NO_ERROR; 447} 448 449svn_delta_shim_callbacks_t * 450svn_client__get_shim_callbacks(svn_wc_context_t *wc_ctx, 451 apr_hash_t *relpath_map, 452 apr_pool_t *result_pool) 453{ 454 svn_delta_shim_callbacks_t *callbacks = 455 svn_delta_shim_callbacks_default(result_pool); 456 struct shim_callbacks_baton *scb = apr_pcalloc(result_pool, sizeof(*scb)); 457 458 scb->wc_ctx = wc_ctx; 459 if (relpath_map) 460 scb->relpath_map = relpath_map; 461 else 462 scb->relpath_map = apr_hash_make(result_pool); 463 464 callbacks->fetch_props_func = fetch_props_func; 465 callbacks->fetch_kind_func = fetch_kind_func; 466 callbacks->fetch_base_func = fetch_base_func; 467 callbacks->fetch_baton = scb; 468 469 return callbacks; 470} 471