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