1251881Speter/* 2251881Speter * compat.c : Wrappers and callbacks for compatibility. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter#include <stddef.h> 25251881Speter 26251881Speter#include "svn_types.h" 27251881Speter#include "svn_error.h" 28251881Speter#include "svn_delta.h" 29251881Speter#include "svn_sorts.h" 30251881Speter#include "svn_dirent_uri.h" 31251881Speter#include "svn_path.h" 32251881Speter#include "svn_hash.h" 33251881Speter#include "svn_props.h" 34251881Speter#include "svn_pools.h" 35251881Speter 36251881Speter#include "svn_private_config.h" 37251881Speter 38251881Speter#include "private/svn_delta_private.h" 39251881Speter 40251881Speter 41251881Speterstruct file_rev_handler_wrapper_baton { 42251881Speter void *baton; 43251881Speter svn_file_rev_handler_old_t handler; 44251881Speter}; 45251881Speter 46251881Speter/* This implements svn_file_rev_handler_t. */ 47251881Speterstatic svn_error_t * 48251881Speterfile_rev_handler_wrapper(void *baton, 49251881Speter const char *path, 50251881Speter svn_revnum_t rev, 51251881Speter apr_hash_t *rev_props, 52251881Speter svn_boolean_t result_of_merge, 53251881Speter svn_txdelta_window_handler_t *delta_handler, 54251881Speter void **delta_baton, 55251881Speter apr_array_header_t *prop_diffs, 56251881Speter apr_pool_t *pool) 57251881Speter{ 58251881Speter struct file_rev_handler_wrapper_baton *fwb = baton; 59251881Speter 60251881Speter if (fwb->handler) 61251881Speter return fwb->handler(fwb->baton, 62251881Speter path, 63251881Speter rev, 64251881Speter rev_props, 65251881Speter delta_handler, 66251881Speter delta_baton, 67251881Speter prop_diffs, 68251881Speter pool); 69251881Speter 70251881Speter return SVN_NO_ERROR; 71251881Speter} 72251881Speter 73251881Spetervoid 74251881Spetersvn_compat_wrap_file_rev_handler(svn_file_rev_handler_t *handler2, 75251881Speter void **handler2_baton, 76251881Speter svn_file_rev_handler_old_t handler, 77251881Speter void *handler_baton, 78251881Speter apr_pool_t *pool) 79251881Speter{ 80251881Speter struct file_rev_handler_wrapper_baton *fwb = apr_pcalloc(pool, sizeof(*fwb)); 81251881Speter 82251881Speter /* Set the user provided old format callback in the baton. */ 83251881Speter fwb->baton = handler_baton; 84251881Speter fwb->handler = handler; 85251881Speter 86251881Speter *handler2_baton = fwb; 87251881Speter *handler2 = file_rev_handler_wrapper; 88251881Speter} 89251881Speter 90251881Speter 91251881Speter/* The following code maps the calls to a traditional delta editor to an 92251881Speter * Editorv2 editor. It does this by keeping track of a lot of state, and 93251881Speter * then communicating that state to Ev2 upon closure of the file or dir (or 94251881Speter * edit). Note that Ev2 calls add_symlink() and alter_symlink() are not 95251881Speter * present in the delta editor paradigm, so we never call them. 96251881Speter * 97251881Speter * The general idea here is that we have to see *all* the actions on a node's 98251881Speter * parent before we can process that node, which means we need to buffer a 99251881Speter * large amount of information in the dir batons, and then process it in the 100251881Speter * close_directory() handler. 101251881Speter * 102251881Speter * There are a few ways we alter the callback stream. One is when unlocking 103251881Speter * paths. To tell a client a path should be unlocked, the server sends a 104251881Speter * prop-del for the SVN_PROP_ENTRY_LOCK_TOKEN property. This causes problems, 105251881Speter * since the client doesn't have this property in the first place, but the 106251881Speter * deletion has side effects (unlike deleting a non-existent regular property 107251881Speter * would). To solve this, we introduce *another* function into the API, not 108251881Speter * a part of the Ev2 callbacks, but a companion which is used to register 109251881Speter * the unlock of a path. See ev2_change_file_prop() for implemenation 110251881Speter * details. 111251881Speter */ 112251881Speter 113251881Speterstruct ev2_edit_baton 114251881Speter{ 115251881Speter svn_editor_t *editor; 116251881Speter 117251881Speter apr_hash_t *changes; /* REPOS_RELPATH -> struct change_node */ 118251881Speter 119251881Speter apr_array_header_t *path_order; 120251881Speter int paths_processed; 121251881Speter 122251881Speter /* For calculating relpaths from Ev1 copyfrom urls. */ 123251881Speter const char *repos_root; 124251881Speter const char *base_relpath; 125251881Speter 126251881Speter apr_pool_t *edit_pool; 127251881Speter struct svn_delta__extra_baton *exb; 128251881Speter svn_boolean_t closed; 129251881Speter 130251881Speter svn_boolean_t *found_abs_paths; /* Did we strip an incoming '/' from the 131251881Speter paths? */ 132251881Speter 133251881Speter svn_delta_fetch_props_func_t fetch_props_func; 134251881Speter void *fetch_props_baton; 135251881Speter 136251881Speter svn_delta_fetch_base_func_t fetch_base_func; 137251881Speter void *fetch_base_baton; 138251881Speter 139251881Speter svn_delta__unlock_func_t do_unlock; 140251881Speter void *unlock_baton; 141251881Speter}; 142251881Speter 143251881Speterstruct ev2_dir_baton 144251881Speter{ 145251881Speter struct ev2_edit_baton *eb; 146251881Speter const char *path; 147251881Speter svn_revnum_t base_revision; 148251881Speter 149251881Speter const char *copyfrom_relpath; 150251881Speter svn_revnum_t copyfrom_rev; 151251881Speter}; 152251881Speter 153251881Speterstruct ev2_file_baton 154251881Speter{ 155251881Speter struct ev2_edit_baton *eb; 156251881Speter const char *path; 157251881Speter svn_revnum_t base_revision; 158251881Speter const char *delta_base; 159251881Speter}; 160251881Speter 161251881Speterenum restructure_action_t 162251881Speter{ 163251881Speter RESTRUCTURE_NONE = 0, 164251881Speter RESTRUCTURE_ADD, /* add the node, maybe replacing. maybe copy */ 165251881Speter RESTRUCTURE_ADD_ABSENT, /* add an absent node, possibly replacing */ 166251881Speter RESTRUCTURE_DELETE /* delete this node */ 167251881Speter}; 168251881Speter 169251881Speter/* Records everything about how this node is to be changed. */ 170251881Speterstruct change_node 171251881Speter{ 172251881Speter /* what kind of (tree) restructure is occurring at this node? */ 173251881Speter enum restructure_action_t action; 174251881Speter 175251881Speter svn_node_kind_t kind; /* the NEW kind of this node */ 176251881Speter 177251881Speter /* We need two revisions: one to specify the revision we are altering, 178251881Speter and a second to specify the revision to delete/replace. These are 179251881Speter mutually exclusive, but they need to be separate to ensure we don't 180251881Speter confuse the operation on this node. For example, we may delete a 181251881Speter node and replace it we use DELETING for REPLACES_REV, and ignore 182251881Speter the value placed into CHANGING when properties were set/changed 183251881Speter on the new node. Or we simply change a node (setting CHANGING), 184251881Speter and DELETING remains SVN_INVALID_REVNUM, indicating we are not 185251881Speter attempting to replace a node. */ 186251881Speter svn_revnum_t changing; 187251881Speter svn_revnum_t deleting; 188251881Speter 189251881Speter apr_hash_t *props; /* new/final set of props to apply */ 190251881Speter 191251881Speter const char *contents_abspath; /* file containing new fulltext */ 192251881Speter svn_checksum_t *checksum; /* checksum of new fulltext */ 193251881Speter 194251881Speter /* If COPYFROM_PATH is not NULL, then copy PATH@REV to this node. 195251881Speter RESTRUCTURE must be RESTRUCTURE_ADD. */ 196251881Speter const char *copyfrom_path; 197251881Speter svn_revnum_t copyfrom_rev; 198251881Speter 199251881Speter /* Record whether an incoming propchange unlocked this node. */ 200251881Speter svn_boolean_t unlock; 201251881Speter}; 202251881Speter 203251881Speter 204251881Speterstatic struct change_node * 205251881Speterlocate_change(struct ev2_edit_baton *eb, 206251881Speter const char *relpath) 207251881Speter{ 208251881Speter struct change_node *change = svn_hash_gets(eb->changes, relpath); 209251881Speter 210251881Speter if (change != NULL) 211251881Speter return change; 212251881Speter 213251881Speter /* Shift RELPATH into the proper pool, and record the observed order. */ 214251881Speter relpath = apr_pstrdup(eb->edit_pool, relpath); 215251881Speter APR_ARRAY_PUSH(eb->path_order, const char *) = relpath; 216251881Speter 217251881Speter /* Return an empty change. Callers will tweak as needed. */ 218251881Speter change = apr_pcalloc(eb->edit_pool, sizeof(*change)); 219251881Speter change->changing = SVN_INVALID_REVNUM; 220251881Speter change->deleting = SVN_INVALID_REVNUM; 221253734Speter change->kind = svn_node_unknown; 222251881Speter 223251881Speter svn_hash_sets(eb->changes, relpath, change); 224251881Speter 225251881Speter return change; 226251881Speter} 227251881Speter 228251881Speter 229251881Speterstatic svn_error_t * 230251881Speterapply_propedit(struct ev2_edit_baton *eb, 231251881Speter const char *relpath, 232251881Speter svn_node_kind_t kind, 233251881Speter svn_revnum_t base_revision, 234251881Speter const char *name, 235251881Speter const svn_string_t *value, 236251881Speter apr_pool_t *scratch_pool) 237251881Speter{ 238251881Speter struct change_node *change = locate_change(eb, relpath); 239251881Speter 240251881Speter SVN_ERR_ASSERT(change->kind == svn_node_unknown || change->kind == kind); 241251881Speter change->kind = kind; 242251881Speter 243251881Speter /* We're now changing the node. Record the revision. */ 244251881Speter SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing) 245251881Speter || change->changing == base_revision); 246251881Speter change->changing = base_revision; 247251881Speter 248251881Speter if (change->props == NULL) 249251881Speter { 250251881Speter /* Fetch the original set of properties. We'll apply edits to create 251251881Speter the new/target set of properties. 252251881Speter 253251881Speter If this is a copied/moved now, then the original properties come 254251881Speter from there. If the node has been added, it starts with empty props. 255251881Speter Otherwise, we get the properties from BASE. */ 256251881Speter 257251881Speter if (change->copyfrom_path) 258251881Speter SVN_ERR(eb->fetch_props_func(&change->props, 259251881Speter eb->fetch_props_baton, 260251881Speter change->copyfrom_path, 261251881Speter change->copyfrom_rev, 262251881Speter eb->edit_pool, scratch_pool)); 263251881Speter else if (change->action == RESTRUCTURE_ADD) 264251881Speter change->props = apr_hash_make(eb->edit_pool); 265251881Speter else 266251881Speter SVN_ERR(eb->fetch_props_func(&change->props, 267251881Speter eb->fetch_props_baton, 268251881Speter relpath, base_revision, 269251881Speter eb->edit_pool, scratch_pool)); 270251881Speter } 271251881Speter 272251881Speter if (value == NULL) 273251881Speter svn_hash_sets(change->props, name, NULL); 274251881Speter else 275251881Speter svn_hash_sets(change->props, 276251881Speter apr_pstrdup(eb->edit_pool, name), 277251881Speter svn_string_dup(value, eb->edit_pool)); 278251881Speter 279251881Speter return SVN_NO_ERROR; 280251881Speter} 281251881Speter 282251881Speter 283251881Speter/* Find all the paths which are immediate children of PATH and return their 284251881Speter basenames in a list. */ 285251881Speterstatic apr_array_header_t * 286251881Speterget_children(struct ev2_edit_baton *eb, 287251881Speter const char *path, 288251881Speter apr_pool_t *pool) 289251881Speter{ 290251881Speter apr_array_header_t *children = apr_array_make(pool, 1, sizeof(const char *)); 291251881Speter apr_hash_index_t *hi; 292251881Speter 293251881Speter for (hi = apr_hash_first(pool, eb->changes); hi; hi = apr_hash_next(hi)) 294251881Speter { 295251881Speter const char *repos_relpath = svn__apr_hash_index_key(hi); 296251881Speter const char *child; 297251881Speter 298251881Speter /* Find potential children. */ 299251881Speter child = svn_relpath_skip_ancestor(path, repos_relpath); 300251881Speter if (!child || !*child) 301251881Speter continue; 302251881Speter 303251881Speter /* If we have a path separator, it's a deep child, so just ignore it. 304251881Speter ### Is there an API we should be using for this? */ 305251881Speter if (strchr(child, '/') != NULL) 306251881Speter continue; 307251881Speter 308251881Speter APR_ARRAY_PUSH(children, const char *) = child; 309251881Speter } 310251881Speter 311251881Speter return children; 312251881Speter} 313251881Speter 314251881Speter 315251881Speterstatic svn_error_t * 316251881Speterprocess_actions(struct ev2_edit_baton *eb, 317251881Speter const char *repos_relpath, 318251881Speter const struct change_node *change, 319251881Speter apr_pool_t *scratch_pool) 320251881Speter{ 321251881Speter apr_hash_t *props = NULL; 322251881Speter svn_stream_t *contents = NULL; 323251881Speter svn_checksum_t *checksum = NULL; 324251881Speter svn_node_kind_t kind = svn_node_unknown; 325251881Speter 326251881Speter SVN_ERR_ASSERT(change != NULL); 327251881Speter 328251881Speter if (change->unlock) 329251881Speter SVN_ERR(eb->do_unlock(eb->unlock_baton, repos_relpath, scratch_pool)); 330251881Speter 331251881Speter if (change->action == RESTRUCTURE_DELETE) 332251881Speter { 333251881Speter /* If the action was left as RESTRUCTURE_DELETE, then a 334251881Speter replacement is not occurring. Just do the delete and bail. */ 335251881Speter SVN_ERR(svn_editor_delete(eb->editor, repos_relpath, 336251881Speter change->deleting)); 337251881Speter 338251881Speter /* No further work possible on this node. */ 339251881Speter return SVN_NO_ERROR; 340251881Speter } 341251881Speter if (change->action == RESTRUCTURE_ADD_ABSENT) 342251881Speter { 343251881Speter SVN_ERR(svn_editor_add_absent(eb->editor, repos_relpath, 344251881Speter change->kind, change->deleting)); 345251881Speter 346251881Speter /* No further work possible on this node. */ 347251881Speter return SVN_NO_ERROR; 348251881Speter } 349251881Speter 350251881Speter if (change->contents_abspath != NULL) 351251881Speter { 352251881Speter /* We can only set text on files. */ 353251881Speter /* ### validate we aren't overwriting KIND? */ 354251881Speter kind = svn_node_file; 355251881Speter 356251881Speter /* ### the checksum might be in CHANGE->CHECKSUM */ 357251881Speter SVN_ERR(svn_io_file_checksum2(&checksum, change->contents_abspath, 358251881Speter svn_checksum_sha1, scratch_pool)); 359251881Speter SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath, 360251881Speter scratch_pool, scratch_pool)); 361251881Speter } 362251881Speter 363251881Speter if (change->props != NULL) 364251881Speter { 365251881Speter /* ### validate we aren't overwriting KIND? */ 366251881Speter kind = change->kind; 367251881Speter props = change->props; 368251881Speter } 369251881Speter 370251881Speter if (change->action == RESTRUCTURE_ADD) 371251881Speter { 372251881Speter /* An add might be a replace. Grab the revnum we're replacing. */ 373251881Speter svn_revnum_t replaces_rev = change->deleting; 374251881Speter 375251881Speter kind = change->kind; 376251881Speter 377251881Speter if (change->copyfrom_path != NULL) 378251881Speter { 379251881Speter SVN_ERR(svn_editor_copy(eb->editor, change->copyfrom_path, 380251881Speter change->copyfrom_rev, 381251881Speter repos_relpath, replaces_rev)); 382251881Speter /* Fall through to possibly make changes post-copy. */ 383251881Speter } 384251881Speter else 385251881Speter { 386251881Speter /* If no properties were defined, then use an empty set. */ 387251881Speter if (props == NULL) 388251881Speter props = apr_hash_make(scratch_pool); 389251881Speter 390251881Speter if (kind == svn_node_dir) 391251881Speter { 392251881Speter const apr_array_header_t *children; 393251881Speter 394251881Speter children = get_children(eb, repos_relpath, scratch_pool); 395251881Speter SVN_ERR(svn_editor_add_directory(eb->editor, repos_relpath, 396251881Speter children, props, 397251881Speter replaces_rev)); 398251881Speter } 399251881Speter else 400251881Speter { 401251881Speter /* If this file was added, but apply_txdelta() was not 402251881Speter called (ie. no CONTENTS_ABSPATH), then we're adding 403251881Speter an empty file. */ 404251881Speter if (change->contents_abspath == NULL) 405251881Speter { 406251881Speter contents = svn_stream_empty(scratch_pool); 407251881Speter checksum = svn_checksum_empty_checksum(svn_checksum_sha1, 408251881Speter scratch_pool); 409251881Speter } 410251881Speter 411251881Speter SVN_ERR(svn_editor_add_file(eb->editor, repos_relpath, 412251881Speter checksum, contents, props, 413251881Speter replaces_rev)); 414251881Speter } 415251881Speter 416251881Speter /* No further work possible on this node. */ 417251881Speter return SVN_NO_ERROR; 418251881Speter } 419251881Speter } 420251881Speter 421251881Speter#if 0 422251881Speter /* There *should* be work for this node. But it seems that isn't true 423251881Speter in some cases. Future investigation... */ 424251881Speter SVN_ERR_ASSERT(props || contents); 425251881Speter#endif 426251881Speter if (props || contents) 427251881Speter { 428251881Speter /* Changes to properties or content should have indicated the revision 429251881Speter it was intending to change. 430251881Speter 431251881Speter Oop. Not true. The node may be locally-added. */ 432251881Speter#if 0 433251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(change->changing)); 434251881Speter#endif 435251881Speter 436251881Speter /* ### we need to gather up the target set of children */ 437251881Speter 438251881Speter if (kind == svn_node_dir) 439251881Speter SVN_ERR(svn_editor_alter_directory(eb->editor, repos_relpath, 440251881Speter change->changing, NULL, props)); 441251881Speter else 442251881Speter SVN_ERR(svn_editor_alter_file(eb->editor, repos_relpath, 443251881Speter change->changing, props, 444251881Speter checksum, contents)); 445251881Speter } 446251881Speter 447251881Speter return SVN_NO_ERROR; 448251881Speter} 449251881Speter 450251881Speterstatic svn_error_t * 451251881Speterrun_ev2_actions(struct ev2_edit_baton *eb, 452251881Speter apr_pool_t *scratch_pool) 453251881Speter{ 454251881Speter apr_pool_t *iterpool; 455251881Speter 456251881Speter iterpool = svn_pool_create(scratch_pool); 457251881Speter 458251881Speter /* Possibly pick up where we left off. Ocassionally, we do some of these 459251881Speter as part of close_edit() and then some more as part of abort_edit() */ 460251881Speter for (; eb->paths_processed < eb->path_order->nelts; ++eb->paths_processed) 461251881Speter { 462251881Speter const char *repos_relpath = APR_ARRAY_IDX(eb->path_order, 463251881Speter eb->paths_processed, 464251881Speter const char *); 465251881Speter const struct change_node *change = svn_hash_gets(eb->changes, 466251881Speter repos_relpath); 467251881Speter 468251881Speter svn_pool_clear(iterpool); 469251881Speter 470251881Speter SVN_ERR(process_actions(eb, repos_relpath, change, iterpool)); 471251881Speter } 472251881Speter svn_pool_destroy(iterpool); 473251881Speter 474251881Speter return SVN_NO_ERROR; 475251881Speter} 476251881Speter 477251881Speter 478251881Speterstatic const char * 479251881Spetermap_to_repos_relpath(struct ev2_edit_baton *eb, 480251881Speter const char *path_or_url, 481251881Speter apr_pool_t *result_pool) 482251881Speter{ 483251881Speter if (svn_path_is_url(path_or_url)) 484251881Speter { 485251881Speter return svn_uri_skip_ancestor(eb->repos_root, path_or_url, result_pool); 486251881Speter } 487251881Speter else 488251881Speter { 489251881Speter return svn_relpath_join(eb->base_relpath, 490251881Speter path_or_url[0] == '/' 491251881Speter ? path_or_url + 1 : path_or_url, 492251881Speter result_pool); 493251881Speter } 494251881Speter} 495251881Speter 496251881Speter 497251881Speterstatic svn_error_t * 498251881Speterev2_set_target_revision(void *edit_baton, 499251881Speter svn_revnum_t target_revision, 500251881Speter apr_pool_t *scratch_pool) 501251881Speter{ 502251881Speter struct ev2_edit_baton *eb = edit_baton; 503251881Speter 504251881Speter if (eb->exb->target_revision) 505251881Speter SVN_ERR(eb->exb->target_revision(eb->exb->baton, target_revision, 506251881Speter scratch_pool)); 507251881Speter 508251881Speter return SVN_NO_ERROR; 509251881Speter} 510251881Speter 511251881Speterstatic svn_error_t * 512251881Speterev2_open_root(void *edit_baton, 513251881Speter svn_revnum_t base_revision, 514251881Speter apr_pool_t *result_pool, 515251881Speter void **root_baton) 516251881Speter{ 517251881Speter struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db)); 518251881Speter struct ev2_edit_baton *eb = edit_baton; 519251881Speter 520251881Speter db->eb = eb; 521251881Speter db->path = apr_pstrdup(eb->edit_pool, eb->base_relpath); 522251881Speter db->base_revision = base_revision; 523251881Speter 524251881Speter *root_baton = db; 525251881Speter 526251881Speter if (eb->exb->start_edit) 527251881Speter SVN_ERR(eb->exb->start_edit(eb->exb->baton, base_revision)); 528251881Speter 529251881Speter return SVN_NO_ERROR; 530251881Speter} 531251881Speter 532251881Speterstatic svn_error_t * 533251881Speterev2_delete_entry(const char *path, 534251881Speter svn_revnum_t revision, 535251881Speter void *parent_baton, 536251881Speter apr_pool_t *scratch_pool) 537251881Speter{ 538251881Speter struct ev2_dir_baton *pb = parent_baton; 539251881Speter svn_revnum_t base_revision; 540251881Speter const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); 541251881Speter struct change_node *change = locate_change(pb->eb, relpath); 542251881Speter 543251881Speter if (SVN_IS_VALID_REVNUM(revision)) 544251881Speter base_revision = revision; 545251881Speter else 546251881Speter base_revision = pb->base_revision; 547251881Speter 548251881Speter SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE); 549251881Speter change->action = RESTRUCTURE_DELETE; 550251881Speter 551251881Speter SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->deleting) 552251881Speter || change->deleting == base_revision); 553251881Speter change->deleting = base_revision; 554251881Speter 555251881Speter return SVN_NO_ERROR; 556251881Speter} 557251881Speter 558251881Speterstatic svn_error_t * 559251881Speterev2_add_directory(const char *path, 560251881Speter void *parent_baton, 561251881Speter const char *copyfrom_path, 562251881Speter svn_revnum_t copyfrom_revision, 563251881Speter apr_pool_t *result_pool, 564251881Speter void **child_baton) 565251881Speter{ 566251881Speter /* ### fix this? */ 567251881Speter apr_pool_t *scratch_pool = result_pool; 568251881Speter struct ev2_dir_baton *pb = parent_baton; 569251881Speter struct ev2_dir_baton *cb = apr_pcalloc(result_pool, sizeof(*cb)); 570251881Speter const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); 571251881Speter struct change_node *change = locate_change(pb->eb, relpath); 572251881Speter 573251881Speter /* ### assert that RESTRUCTURE is NONE or DELETE? */ 574251881Speter change->action = RESTRUCTURE_ADD; 575251881Speter change->kind = svn_node_dir; 576251881Speter 577251881Speter cb->eb = pb->eb; 578251881Speter cb->path = apr_pstrdup(result_pool, relpath); 579251881Speter cb->base_revision = pb->base_revision; 580251881Speter *child_baton = cb; 581251881Speter 582251881Speter if (!copyfrom_path) 583251881Speter { 584251881Speter if (pb->copyfrom_relpath) 585251881Speter { 586251881Speter const char *name = svn_relpath_basename(relpath, scratch_pool); 587251881Speter cb->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name, 588251881Speter result_pool); 589251881Speter cb->copyfrom_rev = pb->copyfrom_rev; 590251881Speter } 591251881Speter } 592251881Speter else 593251881Speter { 594251881Speter /* A copy */ 595251881Speter 596251881Speter change->copyfrom_path = map_to_repos_relpath(pb->eb, copyfrom_path, 597251881Speter pb->eb->edit_pool); 598251881Speter change->copyfrom_rev = copyfrom_revision; 599251881Speter 600251881Speter cb->copyfrom_relpath = change->copyfrom_path; 601251881Speter cb->copyfrom_rev = change->copyfrom_rev; 602251881Speter } 603251881Speter 604251881Speter return SVN_NO_ERROR; 605251881Speter} 606251881Speter 607251881Speterstatic svn_error_t * 608251881Speterev2_open_directory(const char *path, 609251881Speter void *parent_baton, 610251881Speter svn_revnum_t base_revision, 611251881Speter apr_pool_t *result_pool, 612251881Speter void **child_baton) 613251881Speter{ 614251881Speter /* ### fix this? */ 615251881Speter apr_pool_t *scratch_pool = result_pool; 616251881Speter struct ev2_dir_baton *pb = parent_baton; 617251881Speter struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db)); 618251881Speter const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); 619251881Speter 620251881Speter db->eb = pb->eb; 621251881Speter db->path = apr_pstrdup(result_pool, relpath); 622251881Speter db->base_revision = base_revision; 623251881Speter 624251881Speter if (pb->copyfrom_relpath) 625251881Speter { 626251881Speter /* We are inside a copy. */ 627251881Speter const char *name = svn_relpath_basename(relpath, scratch_pool); 628251881Speter 629251881Speter db->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name, 630251881Speter result_pool); 631251881Speter db->copyfrom_rev = pb->copyfrom_rev; 632251881Speter } 633251881Speter 634251881Speter *child_baton = db; 635251881Speter return SVN_NO_ERROR; 636251881Speter} 637251881Speter 638251881Speterstatic svn_error_t * 639251881Speterev2_change_dir_prop(void *dir_baton, 640251881Speter const char *name, 641251881Speter const svn_string_t *value, 642251881Speter apr_pool_t *scratch_pool) 643251881Speter{ 644251881Speter struct ev2_dir_baton *db = dir_baton; 645251881Speter 646251881Speter SVN_ERR(apply_propedit(db->eb, db->path, svn_node_dir, db->base_revision, 647251881Speter name, value, scratch_pool)); 648251881Speter 649251881Speter return SVN_NO_ERROR; 650251881Speter} 651251881Speter 652251881Speterstatic svn_error_t * 653251881Speterev2_close_directory(void *dir_baton, 654251881Speter apr_pool_t *scratch_pool) 655251881Speter{ 656251881Speter return SVN_NO_ERROR; 657251881Speter} 658251881Speter 659251881Speterstatic svn_error_t * 660251881Speterev2_absent_directory(const char *path, 661251881Speter void *parent_baton, 662251881Speter apr_pool_t *scratch_pool) 663251881Speter{ 664251881Speter struct ev2_dir_baton *pb = parent_baton; 665251881Speter const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); 666251881Speter struct change_node *change = locate_change(pb->eb, relpath); 667251881Speter 668251881Speter /* ### assert that RESTRUCTURE is NONE or DELETE? */ 669251881Speter change->action = RESTRUCTURE_ADD_ABSENT; 670251881Speter change->kind = svn_node_dir; 671251881Speter 672251881Speter return SVN_NO_ERROR; 673251881Speter} 674251881Speter 675251881Speterstatic svn_error_t * 676251881Speterev2_add_file(const char *path, 677251881Speter void *parent_baton, 678251881Speter const char *copyfrom_path, 679251881Speter svn_revnum_t copyfrom_revision, 680251881Speter apr_pool_t *result_pool, 681251881Speter void **file_baton) 682251881Speter{ 683251881Speter /* ### fix this? */ 684251881Speter apr_pool_t *scratch_pool = result_pool; 685251881Speter struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb)); 686251881Speter struct ev2_dir_baton *pb = parent_baton; 687251881Speter const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); 688251881Speter struct change_node *change = locate_change(pb->eb, relpath); 689251881Speter 690251881Speter /* ### assert that RESTRUCTURE is NONE or DELETE? */ 691251881Speter change->action = RESTRUCTURE_ADD; 692251881Speter change->kind = svn_node_file; 693251881Speter 694251881Speter fb->eb = pb->eb; 695251881Speter fb->path = apr_pstrdup(result_pool, relpath); 696251881Speter fb->base_revision = pb->base_revision; 697251881Speter *file_baton = fb; 698251881Speter 699251881Speter if (!copyfrom_path) 700251881Speter { 701251881Speter /* Don't bother fetching the base, as in an add we don't have a base. */ 702251881Speter fb->delta_base = NULL; 703251881Speter } 704251881Speter else 705251881Speter { 706251881Speter /* A copy */ 707251881Speter 708251881Speter change->copyfrom_path = map_to_repos_relpath(fb->eb, copyfrom_path, 709251881Speter fb->eb->edit_pool); 710251881Speter change->copyfrom_rev = copyfrom_revision; 711251881Speter 712251881Speter SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base, 713251881Speter fb->eb->fetch_base_baton, 714251881Speter change->copyfrom_path, 715251881Speter change->copyfrom_rev, 716251881Speter result_pool, scratch_pool)); 717251881Speter } 718251881Speter 719251881Speter return SVN_NO_ERROR; 720251881Speter} 721251881Speter 722251881Speterstatic svn_error_t * 723251881Speterev2_open_file(const char *path, 724251881Speter void *parent_baton, 725251881Speter svn_revnum_t base_revision, 726251881Speter apr_pool_t *result_pool, 727251881Speter void **file_baton) 728251881Speter{ 729251881Speter /* ### fix this? */ 730251881Speter apr_pool_t *scratch_pool = result_pool; 731251881Speter struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb)); 732251881Speter struct ev2_dir_baton *pb = parent_baton; 733251881Speter const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); 734251881Speter 735251881Speter fb->eb = pb->eb; 736251881Speter fb->path = apr_pstrdup(result_pool, relpath); 737251881Speter fb->base_revision = base_revision; 738251881Speter 739251881Speter if (pb->copyfrom_relpath) 740251881Speter { 741251881Speter /* We're in a copied directory, so the delta base is going to be 742251881Speter based up on the copy source. */ 743251881Speter const char *name = svn_relpath_basename(relpath, scratch_pool); 744251881Speter const char *copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, 745251881Speter name, 746251881Speter scratch_pool); 747251881Speter 748251881Speter SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base, 749251881Speter fb->eb->fetch_base_baton, 750251881Speter copyfrom_relpath, pb->copyfrom_rev, 751251881Speter result_pool, scratch_pool)); 752251881Speter } 753251881Speter else 754251881Speter { 755251881Speter SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base, 756251881Speter fb->eb->fetch_base_baton, 757251881Speter relpath, base_revision, 758251881Speter result_pool, scratch_pool)); 759251881Speter } 760251881Speter 761251881Speter *file_baton = fb; 762251881Speter return SVN_NO_ERROR; 763251881Speter} 764251881Speter 765251881Speterstruct handler_baton 766251881Speter{ 767251881Speter svn_txdelta_window_handler_t apply_handler; 768251881Speter void *apply_baton; 769251881Speter 770251881Speter svn_stream_t *source; 771251881Speter 772251881Speter apr_pool_t *pool; 773251881Speter}; 774251881Speter 775251881Speterstatic svn_error_t * 776251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton) 777251881Speter{ 778251881Speter struct handler_baton *hb = baton; 779251881Speter svn_error_t *err; 780251881Speter 781251881Speter err = hb->apply_handler(window, hb->apply_baton); 782251881Speter if (window != NULL && !err) 783251881Speter return SVN_NO_ERROR; 784251881Speter 785251881Speter SVN_ERR(svn_stream_close(hb->source)); 786251881Speter 787251881Speter svn_pool_destroy(hb->pool); 788251881Speter 789251881Speter return svn_error_trace(err); 790251881Speter} 791251881Speter 792251881Speter 793251881Speterstatic svn_error_t * 794251881Speterev2_apply_textdelta(void *file_baton, 795251881Speter const char *base_checksum, 796251881Speter apr_pool_t *result_pool, 797251881Speter svn_txdelta_window_handler_t *handler, 798251881Speter void **handler_baton) 799251881Speter{ 800251881Speter struct ev2_file_baton *fb = file_baton; 801251881Speter apr_pool_t *handler_pool = svn_pool_create(fb->eb->edit_pool); 802251881Speter struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb)); 803251881Speter struct change_node *change; 804251881Speter svn_stream_t *target; 805251881Speter /* ### fix this. for now, we know this has a "short" lifetime. */ 806251881Speter apr_pool_t *scratch_pool = handler_pool; 807251881Speter 808251881Speter change = locate_change(fb->eb, fb->path); 809251881Speter SVN_ERR_ASSERT(change->contents_abspath == NULL); 810251881Speter SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing) 811251881Speter || change->changing == fb->base_revision); 812251881Speter change->changing = fb->base_revision; 813251881Speter 814251881Speter if (! fb->delta_base) 815251881Speter hb->source = svn_stream_empty(handler_pool); 816251881Speter else 817251881Speter SVN_ERR(svn_stream_open_readonly(&hb->source, fb->delta_base, handler_pool, 818251881Speter scratch_pool)); 819251881Speter 820251881Speter SVN_ERR(svn_stream_open_unique(&target, &change->contents_abspath, NULL, 821251881Speter svn_io_file_del_on_pool_cleanup, 822251881Speter fb->eb->edit_pool, scratch_pool)); 823251881Speter 824251881Speter svn_txdelta_apply(hb->source, target, 825251881Speter NULL, NULL, 826251881Speter handler_pool, 827251881Speter &hb->apply_handler, &hb->apply_baton); 828251881Speter 829251881Speter hb->pool = handler_pool; 830251881Speter 831251881Speter *handler_baton = hb; 832251881Speter *handler = window_handler; 833251881Speter 834251881Speter return SVN_NO_ERROR; 835251881Speter} 836251881Speter 837251881Speterstatic svn_error_t * 838251881Speterev2_change_file_prop(void *file_baton, 839251881Speter const char *name, 840251881Speter const svn_string_t *value, 841251881Speter apr_pool_t *scratch_pool) 842251881Speter{ 843251881Speter struct ev2_file_baton *fb = file_baton; 844251881Speter 845251881Speter if (!strcmp(name, SVN_PROP_ENTRY_LOCK_TOKEN) && value == NULL) 846251881Speter { 847251881Speter /* We special case the lock token propery deletion, which is the 848251881Speter server's way of telling the client to unlock the path. */ 849251881Speter 850251881Speter /* ### this duplicates much of apply_propedit(). fix in future. */ 851251881Speter const char *relpath = map_to_repos_relpath(fb->eb, fb->path, 852251881Speter scratch_pool); 853251881Speter struct change_node *change = locate_change(fb->eb, relpath); 854251881Speter 855251881Speter change->unlock = TRUE; 856251881Speter } 857251881Speter 858251881Speter SVN_ERR(apply_propedit(fb->eb, fb->path, svn_node_file, fb->base_revision, 859251881Speter name, value, scratch_pool)); 860251881Speter 861251881Speter return SVN_NO_ERROR; 862251881Speter} 863251881Speter 864251881Speterstatic svn_error_t * 865251881Speterev2_close_file(void *file_baton, 866251881Speter const char *text_checksum, 867251881Speter apr_pool_t *scratch_pool) 868251881Speter{ 869251881Speter return SVN_NO_ERROR; 870251881Speter} 871251881Speter 872251881Speterstatic svn_error_t * 873251881Speterev2_absent_file(const char *path, 874251881Speter void *parent_baton, 875251881Speter apr_pool_t *scratch_pool) 876251881Speter{ 877251881Speter struct ev2_dir_baton *pb = parent_baton; 878251881Speter const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); 879251881Speter struct change_node *change = locate_change(pb->eb, relpath); 880251881Speter 881251881Speter /* ### assert that RESTRUCTURE is NONE or DELETE? */ 882251881Speter change->action = RESTRUCTURE_ADD_ABSENT; 883251881Speter change->kind = svn_node_file; 884251881Speter 885251881Speter return SVN_NO_ERROR; 886251881Speter} 887251881Speter 888251881Speterstatic svn_error_t * 889251881Speterev2_close_edit(void *edit_baton, 890251881Speter apr_pool_t *scratch_pool) 891251881Speter{ 892251881Speter struct ev2_edit_baton *eb = edit_baton; 893251881Speter 894251881Speter SVN_ERR(run_ev2_actions(edit_baton, scratch_pool)); 895251881Speter eb->closed = TRUE; 896251881Speter return svn_error_trace(svn_editor_complete(eb->editor)); 897251881Speter} 898251881Speter 899251881Speterstatic svn_error_t * 900251881Speterev2_abort_edit(void *edit_baton, 901251881Speter apr_pool_t *scratch_pool) 902251881Speter{ 903251881Speter struct ev2_edit_baton *eb = edit_baton; 904251881Speter 905251881Speter SVN_ERR(run_ev2_actions(edit_baton, scratch_pool)); 906251881Speter if (!eb->closed) 907251881Speter return svn_error_trace(svn_editor_abort(eb->editor)); 908251881Speter else 909251881Speter return SVN_NO_ERROR; 910251881Speter} 911251881Speter 912251881Speter/* Return a svn_delta_editor_t * in DEDITOR, with an accompanying baton in 913251881Speter * DEDITOR_BATON, which will drive EDITOR. These will both be 914251881Speter * allocated in RESULT_POOL, which may become large and long-lived; 915251881Speter * SCRATCH_POOL is used for temporary allocations. 916251881Speter * 917251881Speter * The other parameters are as follows: 918251881Speter * - UNLOCK_FUNC / UNLOCK_BATON: A callback / baton which will be called 919251881Speter * when an unlocking action is received. 920251881Speter * - FOUND_ABS_PATHS: A pointer to a boolean flag which will be set if 921251881Speter * this shim determines that it is receiving absolute paths. 922251881Speter * - FETCH_PROPS_FUNC / FETCH_PROPS_BATON: A callback / baton pair which 923251881Speter * will be used by the shim handlers if they need to determine the 924251881Speter * existing properties on a path. 925251881Speter * - FETCH_BASE_FUNC / FETCH_BASE_BATON: A callback / baton pair which will 926251881Speter * be used by the shims handlers if they need to determine the base 927251881Speter * text of a path. It should only be invoked for files. 928251881Speter * - EXB: An 'extra baton' which is used to communicate between the shims. 929251881Speter * Its callbacks should be invoked at the appropriate time by this 930251881Speter * shim. 931251881Speter */ 932251881Spetersvn_error_t * 933251881Spetersvn_delta__delta_from_editor(const svn_delta_editor_t **deditor, 934251881Speter void **dedit_baton, 935251881Speter svn_editor_t *editor, 936251881Speter svn_delta__unlock_func_t unlock_func, 937251881Speter void *unlock_baton, 938251881Speter svn_boolean_t *found_abs_paths, 939251881Speter const char *repos_root, 940251881Speter const char *base_relpath, 941251881Speter svn_delta_fetch_props_func_t fetch_props_func, 942251881Speter void *fetch_props_baton, 943251881Speter svn_delta_fetch_base_func_t fetch_base_func, 944251881Speter void *fetch_base_baton, 945251881Speter struct svn_delta__extra_baton *exb, 946251881Speter apr_pool_t *pool) 947251881Speter{ 948251881Speter /* Static 'cause we don't want it to be on the stack. */ 949251881Speter static svn_delta_editor_t delta_editor = { 950251881Speter ev2_set_target_revision, 951251881Speter ev2_open_root, 952251881Speter ev2_delete_entry, 953251881Speter ev2_add_directory, 954251881Speter ev2_open_directory, 955251881Speter ev2_change_dir_prop, 956251881Speter ev2_close_directory, 957251881Speter ev2_absent_directory, 958251881Speter ev2_add_file, 959251881Speter ev2_open_file, 960251881Speter ev2_apply_textdelta, 961251881Speter ev2_change_file_prop, 962251881Speter ev2_close_file, 963251881Speter ev2_absent_file, 964251881Speter ev2_close_edit, 965251881Speter ev2_abort_edit 966251881Speter }; 967251881Speter struct ev2_edit_baton *eb = apr_pcalloc(pool, sizeof(*eb)); 968251881Speter 969251881Speter if (!base_relpath) 970251881Speter base_relpath = ""; 971251881Speter else if (base_relpath[0] == '/') 972251881Speter base_relpath += 1; 973251881Speter 974251881Speter eb->editor = editor; 975251881Speter eb->changes = apr_hash_make(pool); 976251881Speter eb->path_order = apr_array_make(pool, 1, sizeof(const char *)); 977251881Speter eb->edit_pool = pool; 978251881Speter eb->found_abs_paths = found_abs_paths; 979251881Speter *eb->found_abs_paths = FALSE; 980251881Speter eb->exb = exb; 981251881Speter eb->repos_root = apr_pstrdup(pool, repos_root); 982251881Speter eb->base_relpath = apr_pstrdup(pool, base_relpath); 983251881Speter 984251881Speter eb->fetch_props_func = fetch_props_func; 985251881Speter eb->fetch_props_baton = fetch_props_baton; 986251881Speter 987251881Speter eb->fetch_base_func = fetch_base_func; 988251881Speter eb->fetch_base_baton = fetch_base_baton; 989251881Speter 990251881Speter eb->do_unlock = unlock_func; 991251881Speter eb->unlock_baton = unlock_baton; 992251881Speter 993251881Speter *dedit_baton = eb; 994251881Speter *deditor = &delta_editor; 995251881Speter 996251881Speter return SVN_NO_ERROR; 997251881Speter} 998251881Speter 999251881Speter 1000251881Speter/* ### note the similarity to struct change_node. these structures will 1001251881Speter ### be combined in the future. */ 1002251881Speterstruct operation { 1003251881Speter /* ### leave these two here for now. still used. */ 1004251881Speter svn_revnum_t base_revision; 1005251881Speter void *baton; 1006251881Speter}; 1007251881Speter 1008251881Speterstruct editor_baton 1009251881Speter{ 1010251881Speter const svn_delta_editor_t *deditor; 1011251881Speter void *dedit_baton; 1012251881Speter 1013251881Speter svn_delta_fetch_kind_func_t fetch_kind_func; 1014251881Speter void *fetch_kind_baton; 1015251881Speter 1016251881Speter svn_delta_fetch_props_func_t fetch_props_func; 1017251881Speter void *fetch_props_baton; 1018251881Speter 1019251881Speter struct operation root; 1020251881Speter svn_boolean_t *make_abs_paths; 1021251881Speter const char *repos_root; 1022251881Speter const char *base_relpath; 1023251881Speter 1024251881Speter /* REPOS_RELPATH -> struct change_node * */ 1025251881Speter apr_hash_t *changes; 1026251881Speter 1027251881Speter apr_pool_t *edit_pool; 1028251881Speter}; 1029251881Speter 1030251881Speter 1031251881Speter/* Insert a new change for RELPATH, or return an existing one. */ 1032251881Speterstatic struct change_node * 1033251881Speterinsert_change(const char *relpath, 1034251881Speter apr_hash_t *changes) 1035251881Speter{ 1036251881Speter apr_pool_t *result_pool; 1037251881Speter struct change_node *change; 1038251881Speter 1039251881Speter change = svn_hash_gets(changes, relpath); 1040251881Speter if (change != NULL) 1041251881Speter return change; 1042251881Speter 1043251881Speter result_pool = apr_hash_pool_get(changes); 1044251881Speter 1045251881Speter /* Return an empty change. Callers will tweak as needed. */ 1046251881Speter change = apr_pcalloc(result_pool, sizeof(*change)); 1047251881Speter change->changing = SVN_INVALID_REVNUM; 1048251881Speter change->deleting = SVN_INVALID_REVNUM; 1049251881Speter 1050251881Speter svn_hash_sets(changes, apr_pstrdup(result_pool, relpath), change); 1051251881Speter 1052251881Speter return change; 1053251881Speter} 1054251881Speter 1055251881Speter 1056251881Speter/* This implements svn_editor_cb_add_directory_t */ 1057251881Speterstatic svn_error_t * 1058251881Speteradd_directory_cb(void *baton, 1059251881Speter const char *relpath, 1060251881Speter const apr_array_header_t *children, 1061251881Speter apr_hash_t *props, 1062251881Speter svn_revnum_t replaces_rev, 1063251881Speter apr_pool_t *scratch_pool) 1064251881Speter{ 1065251881Speter struct editor_baton *eb = baton; 1066251881Speter struct change_node *change = insert_change(relpath, eb->changes); 1067251881Speter 1068251881Speter change->action = RESTRUCTURE_ADD; 1069251881Speter change->kind = svn_node_dir; 1070251881Speter change->deleting = replaces_rev; 1071251881Speter change->props = svn_prop_hash_dup(props, eb->edit_pool); 1072251881Speter 1073251881Speter return SVN_NO_ERROR; 1074251881Speter} 1075251881Speter 1076251881Speter/* This implements svn_editor_cb_add_file_t */ 1077251881Speterstatic svn_error_t * 1078251881Speteradd_file_cb(void *baton, 1079251881Speter const char *relpath, 1080251881Speter const svn_checksum_t *checksum, 1081251881Speter svn_stream_t *contents, 1082251881Speter apr_hash_t *props, 1083251881Speter svn_revnum_t replaces_rev, 1084251881Speter apr_pool_t *scratch_pool) 1085251881Speter{ 1086251881Speter struct editor_baton *eb = baton; 1087251881Speter const char *tmp_filename; 1088251881Speter svn_stream_t *tmp_stream; 1089251881Speter svn_checksum_t *md5_checksum; 1090251881Speter struct change_node *change = insert_change(relpath, eb->changes); 1091251881Speter 1092251881Speter /* We may need to re-checksum these contents */ 1093251881Speter if (!(checksum && checksum->kind == svn_checksum_md5)) 1094251881Speter contents = svn_stream_checksummed2(contents, &md5_checksum, NULL, 1095251881Speter svn_checksum_md5, TRUE, scratch_pool); 1096251881Speter else 1097251881Speter md5_checksum = (svn_checksum_t *)checksum; 1098251881Speter 1099251881Speter /* Spool the contents to a tempfile, and provide that to the driver. */ 1100251881Speter SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL, 1101251881Speter svn_io_file_del_on_pool_cleanup, 1102251881Speter eb->edit_pool, scratch_pool)); 1103251881Speter SVN_ERR(svn_stream_copy3(contents, tmp_stream, NULL, NULL, scratch_pool)); 1104251881Speter 1105251881Speter change->action = RESTRUCTURE_ADD; 1106251881Speter change->kind = svn_node_file; 1107251881Speter change->deleting = replaces_rev; 1108251881Speter change->props = svn_prop_hash_dup(props, eb->edit_pool); 1109251881Speter change->contents_abspath = tmp_filename; 1110251881Speter change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool); 1111251881Speter 1112251881Speter return SVN_NO_ERROR; 1113251881Speter} 1114251881Speter 1115251881Speter/* This implements svn_editor_cb_add_symlink_t */ 1116251881Speterstatic svn_error_t * 1117251881Speteradd_symlink_cb(void *baton, 1118251881Speter const char *relpath, 1119251881Speter const char *target, 1120251881Speter apr_hash_t *props, 1121251881Speter svn_revnum_t replaces_rev, 1122251881Speter apr_pool_t *scratch_pool) 1123251881Speter{ 1124251881Speter#if 0 1125251881Speter struct editor_baton *eb = baton; 1126251881Speter struct change_node *change = insert_change(relpath, eb->changes); 1127251881Speter 1128251881Speter change->action = RESTRUCTURE_ADD; 1129251881Speter change->kind = svn_node_symlink; 1130251881Speter change->deleting = replaces_rev; 1131251881Speter change->props = svn_prop_hash_dup(props, eb->edit_pool); 1132251881Speter /* ### target */ 1133251881Speter#endif 1134251881Speter 1135251881Speter SVN__NOT_IMPLEMENTED(); 1136251881Speter} 1137251881Speter 1138251881Speter/* This implements svn_editor_cb_add_absent_t */ 1139251881Speterstatic svn_error_t * 1140251881Speteradd_absent_cb(void *baton, 1141251881Speter const char *relpath, 1142251881Speter svn_node_kind_t kind, 1143251881Speter svn_revnum_t replaces_rev, 1144251881Speter apr_pool_t *scratch_pool) 1145251881Speter{ 1146251881Speter struct editor_baton *eb = baton; 1147251881Speter struct change_node *change = insert_change(relpath, eb->changes); 1148251881Speter 1149251881Speter change->action = RESTRUCTURE_ADD_ABSENT; 1150251881Speter change->kind = kind; 1151251881Speter change->deleting = replaces_rev; 1152251881Speter 1153251881Speter return SVN_NO_ERROR; 1154251881Speter} 1155251881Speter 1156251881Speter/* This implements svn_editor_cb_alter_directory_t */ 1157251881Speterstatic svn_error_t * 1158251881Speteralter_directory_cb(void *baton, 1159251881Speter const char *relpath, 1160251881Speter svn_revnum_t revision, 1161251881Speter const apr_array_header_t *children, 1162251881Speter apr_hash_t *props, 1163251881Speter apr_pool_t *scratch_pool) 1164251881Speter{ 1165251881Speter struct editor_baton *eb = baton; 1166251881Speter struct change_node *change = insert_change(relpath, eb->changes); 1167251881Speter 1168251881Speter /* ### should we verify the kind is truly a directory? */ 1169251881Speter 1170251881Speter /* ### do we need to do anything with CHILDREN? */ 1171251881Speter 1172251881Speter /* Note: this node may already have information in CHANGE as a result 1173251881Speter of an earlier copy/move operation. */ 1174251881Speter change->kind = svn_node_dir; 1175251881Speter change->changing = revision; 1176251881Speter change->props = svn_prop_hash_dup(props, eb->edit_pool); 1177251881Speter 1178251881Speter return SVN_NO_ERROR; 1179251881Speter} 1180251881Speter 1181251881Speter/* This implements svn_editor_cb_alter_file_t */ 1182251881Speterstatic svn_error_t * 1183251881Speteralter_file_cb(void *baton, 1184251881Speter const char *relpath, 1185251881Speter svn_revnum_t revision, 1186251881Speter apr_hash_t *props, 1187251881Speter const svn_checksum_t *checksum, 1188251881Speter svn_stream_t *contents, 1189251881Speter apr_pool_t *scratch_pool) 1190251881Speter{ 1191251881Speter struct editor_baton *eb = baton; 1192251881Speter const char *tmp_filename; 1193251881Speter svn_stream_t *tmp_stream; 1194251881Speter svn_checksum_t *md5_checksum; 1195251881Speter struct change_node *change = insert_change(relpath, eb->changes); 1196251881Speter 1197251881Speter /* ### should we verify the kind is truly a file? */ 1198251881Speter 1199251881Speter if (contents) 1200251881Speter { 1201251881Speter /* We may need to re-checksum these contents */ 1202251881Speter if (!(checksum && checksum->kind == svn_checksum_md5)) 1203251881Speter contents = svn_stream_checksummed2(contents, &md5_checksum, NULL, 1204251881Speter svn_checksum_md5, TRUE, 1205251881Speter scratch_pool); 1206251881Speter else 1207251881Speter md5_checksum = (svn_checksum_t *)checksum; 1208251881Speter 1209251881Speter /* Spool the contents to a tempfile, and provide that to the driver. */ 1210251881Speter SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL, 1211251881Speter svn_io_file_del_on_pool_cleanup, 1212251881Speter eb->edit_pool, scratch_pool)); 1213251881Speter SVN_ERR(svn_stream_copy3(contents, tmp_stream, NULL, NULL, 1214251881Speter scratch_pool)); 1215251881Speter } 1216251881Speter 1217251881Speter /* Note: this node may already have information in CHANGE as a result 1218251881Speter of an earlier copy/move operation. */ 1219251881Speter 1220251881Speter change->kind = svn_node_file; 1221251881Speter change->changing = revision; 1222251881Speter if (props != NULL) 1223251881Speter change->props = svn_prop_hash_dup(props, eb->edit_pool); 1224251881Speter if (contents != NULL) 1225251881Speter { 1226251881Speter change->contents_abspath = tmp_filename; 1227251881Speter change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool); 1228251881Speter } 1229251881Speter 1230251881Speter return SVN_NO_ERROR; 1231251881Speter} 1232251881Speter 1233251881Speter/* This implements svn_editor_cb_alter_symlink_t */ 1234251881Speterstatic svn_error_t * 1235251881Speteralter_symlink_cb(void *baton, 1236251881Speter const char *relpath, 1237251881Speter svn_revnum_t revision, 1238251881Speter apr_hash_t *props, 1239251881Speter const char *target, 1240251881Speter apr_pool_t *scratch_pool) 1241251881Speter{ 1242251881Speter /* ### should we verify the kind is truly a symlink? */ 1243251881Speter 1244251881Speter /* ### do something */ 1245251881Speter 1246251881Speter SVN__NOT_IMPLEMENTED(); 1247251881Speter} 1248251881Speter 1249251881Speter/* This implements svn_editor_cb_delete_t */ 1250251881Speterstatic svn_error_t * 1251251881Speterdelete_cb(void *baton, 1252251881Speter const char *relpath, 1253251881Speter svn_revnum_t revision, 1254251881Speter apr_pool_t *scratch_pool) 1255251881Speter{ 1256251881Speter struct editor_baton *eb = baton; 1257251881Speter struct change_node *change = insert_change(relpath, eb->changes); 1258251881Speter 1259251881Speter change->action = RESTRUCTURE_DELETE; 1260251881Speter /* change->kind = svn_node_unknown; */ 1261251881Speter change->deleting = revision; 1262251881Speter 1263251881Speter return SVN_NO_ERROR; 1264251881Speter} 1265251881Speter 1266251881Speter/* This implements svn_editor_cb_copy_t */ 1267251881Speterstatic svn_error_t * 1268251881Spetercopy_cb(void *baton, 1269251881Speter const char *src_relpath, 1270251881Speter svn_revnum_t src_revision, 1271251881Speter const char *dst_relpath, 1272251881Speter svn_revnum_t replaces_rev, 1273251881Speter apr_pool_t *scratch_pool) 1274251881Speter{ 1275251881Speter struct editor_baton *eb = baton; 1276251881Speter struct change_node *change = insert_change(dst_relpath, eb->changes); 1277251881Speter 1278251881Speter change->action = RESTRUCTURE_ADD; 1279251881Speter /* change->kind = svn_node_unknown; */ 1280251881Speter change->deleting = replaces_rev; 1281251881Speter change->copyfrom_path = apr_pstrdup(eb->edit_pool, src_relpath); 1282251881Speter change->copyfrom_rev = src_revision; 1283251881Speter 1284251881Speter /* We need the source's kind to know whether to call add_directory() 1285251881Speter or add_file() later on. */ 1286251881Speter SVN_ERR(eb->fetch_kind_func(&change->kind, eb->fetch_kind_baton, 1287251881Speter change->copyfrom_path, 1288251881Speter change->copyfrom_rev, 1289251881Speter scratch_pool)); 1290251881Speter 1291251881Speter /* Note: this node may later have alter_*() called on it. */ 1292251881Speter 1293251881Speter return SVN_NO_ERROR; 1294251881Speter} 1295251881Speter 1296251881Speter/* This implements svn_editor_cb_move_t */ 1297251881Speterstatic svn_error_t * 1298251881Spetermove_cb(void *baton, 1299251881Speter const char *src_relpath, 1300251881Speter svn_revnum_t src_revision, 1301251881Speter const char *dst_relpath, 1302251881Speter svn_revnum_t replaces_rev, 1303251881Speter apr_pool_t *scratch_pool) 1304251881Speter{ 1305251881Speter struct editor_baton *eb = baton; 1306251881Speter struct change_node *change; 1307251881Speter 1308251881Speter /* Remap a move into a DELETE + COPY. */ 1309251881Speter 1310251881Speter change = insert_change(src_relpath, eb->changes); 1311251881Speter change->action = RESTRUCTURE_DELETE; 1312251881Speter /* change->kind = svn_node_unknown; */ 1313251881Speter change->deleting = src_revision; 1314251881Speter 1315251881Speter change = insert_change(dst_relpath, eb->changes); 1316251881Speter change->action = RESTRUCTURE_ADD; 1317251881Speter /* change->kind = svn_node_unknown; */ 1318251881Speter change->deleting = replaces_rev; 1319251881Speter change->copyfrom_path = apr_pstrdup(eb->edit_pool, src_relpath); 1320251881Speter change->copyfrom_rev = src_revision; 1321251881Speter 1322251881Speter /* We need the source's kind to know whether to call add_directory() 1323251881Speter or add_file() later on. */ 1324251881Speter SVN_ERR(eb->fetch_kind_func(&change->kind, eb->fetch_kind_baton, 1325251881Speter change->copyfrom_path, 1326251881Speter change->copyfrom_rev, 1327251881Speter scratch_pool)); 1328251881Speter 1329251881Speter /* Note: this node may later have alter_*() called on it. */ 1330251881Speter 1331251881Speter return SVN_NO_ERROR; 1332251881Speter} 1333251881Speter 1334251881Speter/* This implements svn_editor_cb_rotate_t */ 1335251881Speterstatic svn_error_t * 1336251881Speterrotate_cb(void *baton, 1337251881Speter const apr_array_header_t *relpaths, 1338251881Speter const apr_array_header_t *revisions, 1339251881Speter apr_pool_t *scratch_pool) 1340251881Speter{ 1341251881Speter SVN__NOT_IMPLEMENTED(); 1342251881Speter} 1343251881Speter 1344251881Speter 1345251881Speterstatic int 1346251881Spetercount_components(const char *relpath) 1347251881Speter{ 1348251881Speter int count = 1; 1349251881Speter const char *slash = strchr(relpath, '/'); 1350251881Speter 1351251881Speter while (slash != NULL) 1352251881Speter { 1353251881Speter ++count; 1354251881Speter slash = strchr(slash + 1, '/'); 1355251881Speter } 1356251881Speter return count; 1357251881Speter} 1358251881Speter 1359251881Speter 1360251881Speterstatic int 1361251881Spetersort_deletes_first(const svn_sort__item_t *item1, 1362251881Speter const svn_sort__item_t *item2) 1363251881Speter{ 1364251881Speter const char *relpath1 = item1->key; 1365251881Speter const char *relpath2 = item2->key; 1366251881Speter const struct change_node *change1 = item1->value; 1367251881Speter const struct change_node *change2 = item2->value; 1368251881Speter const char *slash1; 1369251881Speter const char *slash2; 1370251881Speter ptrdiff_t len1; 1371251881Speter ptrdiff_t len2; 1372251881Speter 1373251881Speter /* Force the root to always sort first. Otherwise, it may look like a 1374251881Speter sibling of its children (no slashes), and could get sorted *after* 1375251881Speter any children that get deleted. */ 1376251881Speter if (*relpath1 == '\0') 1377251881Speter return -1; 1378251881Speter if (*relpath2 == '\0') 1379251881Speter return 1; 1380251881Speter 1381251881Speter /* Are these two items siblings? The 'if' statement tests if they are 1382251881Speter siblings in the root directory, or that slashes were found in both 1383251881Speter paths, that the length of the paths to those slashes match, and that 1384251881Speter the path contents up to those slashes also match. */ 1385251881Speter slash1 = strrchr(relpath1, '/'); 1386251881Speter slash2 = strrchr(relpath2, '/'); 1387251881Speter if ((slash1 == NULL && slash2 == NULL) 1388251881Speter || (slash1 != NULL 1389251881Speter && slash2 != NULL 1390251881Speter && (len1 = slash1 - relpath1) == (len2 = slash2 - relpath2) 1391251881Speter && memcmp(relpath1, relpath2, len1) == 0)) 1392251881Speter { 1393251881Speter if (change1->action == RESTRUCTURE_DELETE) 1394251881Speter { 1395251881Speter if (change2->action == RESTRUCTURE_DELETE) 1396251881Speter { 1397251881Speter /* If both items are being deleted, then we don't care about 1398251881Speter the order. State they are equal. */ 1399251881Speter return 0; 1400251881Speter } 1401251881Speter 1402251881Speter /* ITEM1 is being deleted. Sort it before the surviving item. */ 1403251881Speter return -1; 1404251881Speter } 1405251881Speter if (change2->action == RESTRUCTURE_DELETE) 1406251881Speter /* ITEM2 is being deleted. Sort it before the surviving item. */ 1407251881Speter return 1; 1408251881Speter 1409251881Speter /* Normally, we don't care about the ordering of two siblings. However, 1410251881Speter if these siblings are directories, then we need to provide an 1411251881Speter ordering so that the quicksort algorithm will further sort them 1412251881Speter relative to the maybe-directory's children. 1413251881Speter 1414251881Speter Without this additional ordering, we could see that A/B/E and A/B/F 1415251881Speter are equal. And then A/B/E/child is sorted before A/B/F. But since 1416251881Speter E and F are "equal", A/B/E could arrive *after* A/B/F and after the 1417251881Speter A/B/E/child node. */ 1418251881Speter 1419251881Speter /* FALLTHROUGH */ 1420251881Speter } 1421251881Speter 1422251881Speter /* Paths-to-be-deleted with fewer components always sort earlier. 1423251881Speter 1424251881Speter For example, gamma will sort before E/alpha. 1425251881Speter 1426251881Speter Without this test, E/alpha lexicographically sorts before gamma, 1427251881Speter but gamma sorts before E when gamma is to be deleted. This kind of 1428251881Speter ordering would place E/alpha before E. Not good. 1429251881Speter 1430251881Speter With this test, gamma sorts before E/alpha. E and E/alpha are then 1431251881Speter sorted by svn_path_compare_paths() (which places E before E/alpha). */ 1432251881Speter if (change1->action == RESTRUCTURE_DELETE 1433251881Speter || change2->action == RESTRUCTURE_DELETE) 1434251881Speter { 1435251881Speter int count1 = count_components(relpath1); 1436251881Speter int count2 = count_components(relpath2); 1437251881Speter 1438251881Speter if (count1 < count2 && change1->action == RESTRUCTURE_DELETE) 1439251881Speter return -1; 1440251881Speter if (count1 > count2 && change2->action == RESTRUCTURE_DELETE) 1441251881Speter return 1; 1442251881Speter } 1443251881Speter 1444251881Speter /* Use svn_path_compare_paths() to get correct depth-based ordering. */ 1445251881Speter return svn_path_compare_paths(relpath1, relpath2); 1446251881Speter} 1447251881Speter 1448251881Speter 1449251881Speterstatic const apr_array_header_t * 1450251881Speterget_sorted_paths(apr_hash_t *changes, 1451251881Speter const char *base_relpath, 1452251881Speter apr_pool_t *scratch_pool) 1453251881Speter{ 1454251881Speter const apr_array_header_t *items; 1455251881Speter apr_array_header_t *paths; 1456251881Speter int i; 1457251881Speter 1458251881Speter /* Construct a sorted array of svn_sort__item_t structs. Within a given 1459251881Speter directory, nodes that are to be deleted will appear first. */ 1460251881Speter items = svn_sort__hash(changes, sort_deletes_first, scratch_pool); 1461251881Speter 1462251881Speter /* Build a new array with just the paths, trimmed to relative paths for 1463251881Speter the Ev1 drive. */ 1464251881Speter paths = apr_array_make(scratch_pool, items->nelts, sizeof(const char *)); 1465251881Speter for (i = items->nelts; i--; ) 1466251881Speter { 1467251881Speter const svn_sort__item_t *item; 1468251881Speter 1469251881Speter item = &APR_ARRAY_IDX(items, i, const svn_sort__item_t); 1470251881Speter APR_ARRAY_IDX(paths, i, const char *) 1471251881Speter = svn_relpath_skip_ancestor(base_relpath, item->key); 1472251881Speter } 1473251881Speter 1474251881Speter /* We didn't use PUSH, so set the proper number of elements. */ 1475251881Speter paths->nelts = items->nelts; 1476251881Speter 1477251881Speter return paths; 1478251881Speter} 1479251881Speter 1480251881Speter 1481251881Speterstatic svn_error_t * 1482251881Speterdrive_ev1_props(const struct editor_baton *eb, 1483251881Speter const char *repos_relpath, 1484251881Speter const struct change_node *change, 1485251881Speter void *node_baton, 1486251881Speter apr_pool_t *scratch_pool) 1487251881Speter{ 1488251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1489251881Speter apr_hash_t *old_props; 1490251881Speter apr_array_header_t *propdiffs; 1491251881Speter int i; 1492251881Speter 1493251881Speter /* If there are no properties to install, then just exit. */ 1494251881Speter if (change->props == NULL) 1495251881Speter return SVN_NO_ERROR; 1496251881Speter 1497251881Speter if (change->copyfrom_path) 1498251881Speter { 1499251881Speter /* The pristine properties are from the copy/move source. */ 1500251881Speter SVN_ERR(eb->fetch_props_func(&old_props, eb->fetch_props_baton, 1501251881Speter change->copyfrom_path, 1502251881Speter change->copyfrom_rev, 1503251881Speter scratch_pool, iterpool)); 1504251881Speter } 1505251881Speter else if (change->action == RESTRUCTURE_ADD) 1506251881Speter { 1507251881Speter /* Locally-added nodes have no pristine properties. 1508251881Speter 1509251881Speter Note: we can use iterpool; this hash only needs to survive to 1510251881Speter the propdiffs call, and there are no contents to preserve. */ 1511251881Speter old_props = apr_hash_make(iterpool); 1512251881Speter } 1513251881Speter else 1514251881Speter { 1515251881Speter /* Fetch the pristine properties for whatever we're editing. */ 1516251881Speter SVN_ERR(eb->fetch_props_func(&old_props, eb->fetch_props_baton, 1517251881Speter repos_relpath, change->changing, 1518251881Speter scratch_pool, iterpool)); 1519251881Speter } 1520251881Speter 1521251881Speter SVN_ERR(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool)); 1522251881Speter 1523251881Speter for (i = 0; i < propdiffs->nelts; i++) 1524251881Speter { 1525251881Speter /* Note: the array returned by svn_prop_diffs() is an array of 1526251881Speter actual structures, not pointers to them. */ 1527251881Speter const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t); 1528251881Speter 1529251881Speter svn_pool_clear(iterpool); 1530251881Speter 1531251881Speter if (change->kind == svn_node_dir) 1532251881Speter SVN_ERR(eb->deditor->change_dir_prop(node_baton, 1533251881Speter prop->name, prop->value, 1534251881Speter iterpool)); 1535251881Speter else 1536251881Speter SVN_ERR(eb->deditor->change_file_prop(node_baton, 1537251881Speter prop->name, prop->value, 1538251881Speter iterpool)); 1539251881Speter } 1540251881Speter 1541251881Speter /* Handle the funky unlock protocol. Note: only possibly on files. */ 1542251881Speter if (change->unlock) 1543251881Speter { 1544251881Speter SVN_ERR_ASSERT(change->kind == svn_node_file); 1545251881Speter SVN_ERR(eb->deditor->change_file_prop(node_baton, 1546251881Speter SVN_PROP_ENTRY_LOCK_TOKEN, NULL, 1547251881Speter iterpool)); 1548251881Speter } 1549251881Speter 1550251881Speter svn_pool_destroy(iterpool); 1551251881Speter return SVN_NO_ERROR; 1552251881Speter} 1553251881Speter 1554251881Speter 1555251881Speter/* Conforms to svn_delta_path_driver_cb_func_t */ 1556251881Speterstatic svn_error_t * 1557251881Speterapply_change(void **dir_baton, 1558251881Speter void *parent_baton, 1559251881Speter void *callback_baton, 1560251881Speter const char *ev1_relpath, 1561251881Speter apr_pool_t *result_pool) 1562251881Speter{ 1563251881Speter /* ### fix this? */ 1564251881Speter apr_pool_t *scratch_pool = result_pool; 1565251881Speter const struct editor_baton *eb = callback_baton; 1566251881Speter const struct change_node *change; 1567251881Speter const char *relpath; 1568251881Speter void *file_baton = NULL; 1569251881Speter 1570251881Speter /* Typically, we are not creating new directory batons. */ 1571251881Speter *dir_baton = NULL; 1572251881Speter 1573251881Speter relpath = svn_relpath_join(eb->base_relpath, ev1_relpath, scratch_pool); 1574251881Speter change = svn_hash_gets(eb->changes, relpath); 1575251881Speter 1576251881Speter /* The callback should only be called for paths in CHANGES. */ 1577251881Speter SVN_ERR_ASSERT(change != NULL); 1578251881Speter 1579251881Speter /* Are we editing the root of the tree? */ 1580251881Speter if (parent_baton == NULL) 1581251881Speter { 1582251881Speter /* The root was opened in start_edit_func() */ 1583251881Speter *dir_baton = eb->root.baton; 1584251881Speter 1585251881Speter /* Only property edits are allowed on the root. */ 1586251881Speter SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE); 1587251881Speter SVN_ERR(drive_ev1_props(eb, relpath, change, *dir_baton, scratch_pool)); 1588251881Speter 1589251881Speter /* No further action possible for the root. */ 1590251881Speter return SVN_NO_ERROR; 1591251881Speter } 1592251881Speter 1593251881Speter if (change->action == RESTRUCTURE_DELETE) 1594251881Speter { 1595251881Speter SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting, 1596251881Speter parent_baton, scratch_pool)); 1597251881Speter 1598251881Speter /* No futher action possible for this node. */ 1599251881Speter return SVN_NO_ERROR; 1600251881Speter } 1601251881Speter 1602251881Speter /* If we're not deleting this node, then we should know its kind. */ 1603251881Speter SVN_ERR_ASSERT(change->kind != svn_node_unknown); 1604251881Speter 1605251881Speter if (change->action == RESTRUCTURE_ADD_ABSENT) 1606251881Speter { 1607251881Speter if (change->kind == svn_node_dir) 1608251881Speter SVN_ERR(eb->deditor->absent_directory(ev1_relpath, parent_baton, 1609251881Speter scratch_pool)); 1610251881Speter else 1611251881Speter SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton, 1612251881Speter scratch_pool)); 1613251881Speter 1614251881Speter /* No further action possible for this node. */ 1615251881Speter return SVN_NO_ERROR; 1616251881Speter } 1617251881Speter /* RESTRUCTURE_NONE or RESTRUCTURE_ADD */ 1618251881Speter 1619251881Speter if (change->action == RESTRUCTURE_ADD) 1620251881Speter { 1621251881Speter const char *copyfrom_url = NULL; 1622251881Speter svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; 1623251881Speter 1624251881Speter /* Do we have an old node to delete first? */ 1625251881Speter if (SVN_IS_VALID_REVNUM(change->deleting)) 1626251881Speter SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting, 1627251881Speter parent_baton, scratch_pool)); 1628251881Speter 1629251881Speter /* Are we copying the node from somewhere? */ 1630251881Speter if (change->copyfrom_path) 1631251881Speter { 1632251881Speter if (eb->repos_root) 1633251881Speter copyfrom_url = svn_path_url_add_component2(eb->repos_root, 1634251881Speter change->copyfrom_path, 1635251881Speter scratch_pool); 1636251881Speter else 1637253734Speter { 1638253734Speter copyfrom_url = change->copyfrom_path; 1639251881Speter 1640253734Speter /* Make this an FS path by prepending "/" */ 1641253734Speter if (copyfrom_url[0] != '/') 1642253734Speter copyfrom_url = apr_pstrcat(scratch_pool, "/", 1643253734Speter copyfrom_url, NULL); 1644253734Speter } 1645251881Speter 1646251881Speter copyfrom_rev = change->copyfrom_rev; 1647251881Speter } 1648251881Speter 1649251881Speter if (change->kind == svn_node_dir) 1650251881Speter SVN_ERR(eb->deditor->add_directory(ev1_relpath, parent_baton, 1651251881Speter copyfrom_url, copyfrom_rev, 1652251881Speter result_pool, dir_baton)); 1653251881Speter else 1654251881Speter SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton, 1655251881Speter copyfrom_url, copyfrom_rev, 1656251881Speter result_pool, &file_baton)); 1657251881Speter } 1658251881Speter else 1659251881Speter { 1660251881Speter if (change->kind == svn_node_dir) 1661251881Speter SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton, 1662251881Speter change->changing, 1663251881Speter result_pool, dir_baton)); 1664251881Speter else 1665251881Speter SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton, 1666251881Speter change->changing, 1667251881Speter result_pool, &file_baton)); 1668251881Speter } 1669251881Speter 1670251881Speter /* Apply any properties in CHANGE to the node. */ 1671251881Speter if (change->kind == svn_node_dir) 1672251881Speter SVN_ERR(drive_ev1_props(eb, relpath, change, *dir_baton, scratch_pool)); 1673251881Speter else 1674251881Speter SVN_ERR(drive_ev1_props(eb, relpath, change, file_baton, scratch_pool)); 1675251881Speter 1676251881Speter if (change->contents_abspath) 1677251881Speter { 1678251881Speter svn_txdelta_window_handler_t handler; 1679251881Speter void *handler_baton; 1680251881Speter svn_stream_t *contents; 1681251881Speter 1682251881Speter /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the 1683251881Speter ### shim code... */ 1684251881Speter SVN_ERR(eb->deditor->apply_textdelta(file_baton, NULL, scratch_pool, 1685251881Speter &handler, &handler_baton)); 1686251881Speter SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath, 1687251881Speter scratch_pool, scratch_pool)); 1688251881Speter /* ### it would be nice to send a true txdelta here, but whatever. */ 1689251881Speter SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton, 1690251881Speter NULL, scratch_pool)); 1691251881Speter SVN_ERR(svn_stream_close(contents)); 1692251881Speter } 1693251881Speter 1694251881Speter if (file_baton) 1695251881Speter { 1696251881Speter const char *digest = svn_checksum_to_cstring(change->checksum, 1697251881Speter scratch_pool); 1698251881Speter 1699251881Speter SVN_ERR(eb->deditor->close_file(file_baton, digest, scratch_pool)); 1700251881Speter } 1701251881Speter 1702251881Speter return SVN_NO_ERROR; 1703251881Speter} 1704251881Speter 1705251881Speter 1706251881Speterstatic svn_error_t * 1707251881Speterdrive_changes(const struct editor_baton *eb, 1708251881Speter apr_pool_t *scratch_pool) 1709251881Speter{ 1710251881Speter struct change_node *change; 1711251881Speter const apr_array_header_t *paths; 1712251881Speter 1713251881Speter /* If we never opened a root baton, then the caller aborted the editor 1714251881Speter before it even began. There is nothing to do. Bail. */ 1715251881Speter if (eb->root.baton == NULL) 1716251881Speter return SVN_NO_ERROR; 1717251881Speter 1718251881Speter /* We need to make the path driver believe we want to make changes to 1719251881Speter the root. Otherwise, it will attempt an open_root(), which we already 1720251881Speter did in start_edit_func(). We can forge up a change record, if one 1721251881Speter does not already exist. */ 1722251881Speter change = insert_change(eb->base_relpath, eb->changes); 1723251881Speter change->kind = svn_node_dir; 1724251881Speter /* No property changes (tho they might exist from a real change). */ 1725251881Speter 1726251881Speter /* Get a sorted list of Ev1-relative paths. */ 1727251881Speter paths = get_sorted_paths(eb->changes, eb->base_relpath, scratch_pool); 1728251881Speter SVN_ERR(svn_delta_path_driver2(eb->deditor, eb->dedit_baton, paths, 1729251881Speter FALSE, apply_change, (void *)eb, 1730251881Speter scratch_pool)); 1731251881Speter 1732251881Speter return SVN_NO_ERROR; 1733251881Speter} 1734251881Speter 1735251881Speter 1736251881Speter/* This implements svn_editor_cb_complete_t */ 1737251881Speterstatic svn_error_t * 1738251881Spetercomplete_cb(void *baton, 1739251881Speter apr_pool_t *scratch_pool) 1740251881Speter{ 1741251881Speter struct editor_baton *eb = baton; 1742251881Speter svn_error_t *err; 1743251881Speter 1744251881Speter /* Drive the tree we've created. */ 1745251881Speter err = drive_changes(eb, scratch_pool); 1746251881Speter if (!err) 1747251881Speter { 1748251881Speter err = svn_error_compose_create(err, eb->deditor->close_edit( 1749251881Speter eb->dedit_baton, 1750251881Speter scratch_pool)); 1751251881Speter } 1752251881Speter 1753251881Speter if (err) 1754251881Speter svn_error_clear(eb->deditor->abort_edit(eb->dedit_baton, scratch_pool)); 1755251881Speter 1756251881Speter return svn_error_trace(err); 1757251881Speter} 1758251881Speter 1759251881Speter/* This implements svn_editor_cb_abort_t */ 1760251881Speterstatic svn_error_t * 1761251881Speterabort_cb(void *baton, 1762251881Speter apr_pool_t *scratch_pool) 1763251881Speter{ 1764251881Speter struct editor_baton *eb = baton; 1765251881Speter svn_error_t *err; 1766251881Speter svn_error_t *err2; 1767251881Speter 1768251881Speter /* We still need to drive anything we collected in the editor to this 1769251881Speter point. */ 1770251881Speter 1771251881Speter /* Drive the tree we've created. */ 1772251881Speter err = drive_changes(eb, scratch_pool); 1773251881Speter 1774251881Speter err2 = eb->deditor->abort_edit(eb->dedit_baton, scratch_pool); 1775251881Speter 1776251881Speter if (err2) 1777251881Speter { 1778251881Speter if (err) 1779251881Speter svn_error_clear(err2); 1780251881Speter else 1781251881Speter err = err2; 1782251881Speter } 1783251881Speter 1784251881Speter return svn_error_trace(err); 1785251881Speter} 1786251881Speter 1787251881Speterstatic svn_error_t * 1788251881Speterstart_edit_func(void *baton, 1789251881Speter svn_revnum_t base_revision) 1790251881Speter{ 1791251881Speter struct editor_baton *eb = baton; 1792251881Speter 1793251881Speter eb->root.base_revision = base_revision; 1794251881Speter 1795251881Speter /* For some Ev1 editors (such as the repos commit editor), the root must 1796251881Speter be open before can invoke any callbacks. The open_root() call sets up 1797251881Speter stuff (eg. open an FS txn) which will be needed. */ 1798251881Speter SVN_ERR(eb->deditor->open_root(eb->dedit_baton, eb->root.base_revision, 1799251881Speter eb->edit_pool, &eb->root.baton)); 1800251881Speter 1801251881Speter return SVN_NO_ERROR; 1802251881Speter} 1803251881Speter 1804251881Speterstatic svn_error_t * 1805251881Spetertarget_revision_func(void *baton, 1806251881Speter svn_revnum_t target_revision, 1807251881Speter apr_pool_t *scratch_pool) 1808251881Speter{ 1809251881Speter struct editor_baton *eb = baton; 1810251881Speter 1811251881Speter SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision, 1812251881Speter scratch_pool)); 1813251881Speter 1814251881Speter return SVN_NO_ERROR; 1815251881Speter} 1816251881Speter 1817251881Speterstatic svn_error_t * 1818251881Speterdo_unlock(void *baton, 1819251881Speter const char *path, 1820251881Speter apr_pool_t *scratch_pool) 1821251881Speter{ 1822251881Speter struct editor_baton *eb = baton; 1823251881Speter 1824251881Speter { 1825251881Speter /* PATH is REPOS_RELPATH */ 1826251881Speter struct change_node *change = insert_change(path, eb->changes); 1827251881Speter 1828251881Speter /* We will need to propagate a deletion of SVN_PROP_ENTRY_LOCK_TOKEN */ 1829251881Speter change->unlock = TRUE; 1830251881Speter } 1831251881Speter 1832251881Speter return SVN_NO_ERROR; 1833251881Speter} 1834251881Speter 1835251881Speter/* Return an svn_editor_t * in EDITOR_P which will drive 1836251881Speter * DEDITOR/DEDIT_BATON. EDITOR_P is allocated in RESULT_POOL, which may 1837251881Speter * become large and long-lived; SCRATCH_POOL is used for temporary 1838251881Speter * allocations. 1839251881Speter * 1840251881Speter * The other parameters are as follows: 1841251881Speter * - EXB: An 'extra_baton' used for passing information between the coupled 1842251881Speter * shims. This includes actions like 'start edit' and 'set target'. 1843251881Speter * As this shim receives these actions, it provides the extra baton 1844251881Speter * to its caller. 1845251881Speter * - UNLOCK_FUNC / UNLOCK_BATON: A callback / baton pair which a caller 1846251881Speter * can use to notify this shim that a path should be unlocked (in the 1847251881Speter * 'svn lock' sense). As this shim receives this action, it provides 1848251881Speter * this callback / baton to its caller. 1849251881Speter * - SEND_ABS_PATHS: A pointer which will be set prior to this edit (but 1850251881Speter * not necessarily at the invocation of editor_from_delta()),and 1851251881Speter * which indicates whether incoming paths should be expected to 1852251881Speter * be absolute or relative. 1853251881Speter * - CANCEL_FUNC / CANCEL_BATON: The usual; folded into the produced editor. 1854251881Speter * - FETCH_KIND_FUNC / FETCH_KIND_BATON: A callback / baton pair which will 1855251881Speter * be used by the shim handlers if they need to determine the kind of 1856251881Speter * a path. 1857251881Speter * - FETCH_PROPS_FUNC / FETCH_PROPS_BATON: A callback / baton pair which 1858251881Speter * will be used by the shim handlers if they need to determine the 1859251881Speter * existing properties on a path. 1860251881Speter */ 1861251881Spetersvn_error_t * 1862251881Spetersvn_delta__editor_from_delta(svn_editor_t **editor_p, 1863251881Speter struct svn_delta__extra_baton **exb, 1864251881Speter svn_delta__unlock_func_t *unlock_func, 1865251881Speter void **unlock_baton, 1866251881Speter const svn_delta_editor_t *deditor, 1867251881Speter void *dedit_baton, 1868251881Speter svn_boolean_t *send_abs_paths, 1869251881Speter const char *repos_root, 1870251881Speter const char *base_relpath, 1871251881Speter svn_cancel_func_t cancel_func, 1872251881Speter void *cancel_baton, 1873251881Speter svn_delta_fetch_kind_func_t fetch_kind_func, 1874251881Speter void *fetch_kind_baton, 1875251881Speter svn_delta_fetch_props_func_t fetch_props_func, 1876251881Speter void *fetch_props_baton, 1877251881Speter apr_pool_t *result_pool, 1878251881Speter apr_pool_t *scratch_pool) 1879251881Speter{ 1880251881Speter svn_editor_t *editor; 1881251881Speter static const svn_editor_cb_many_t editor_cbs = { 1882251881Speter add_directory_cb, 1883251881Speter add_file_cb, 1884251881Speter add_symlink_cb, 1885251881Speter add_absent_cb, 1886251881Speter alter_directory_cb, 1887251881Speter alter_file_cb, 1888251881Speter alter_symlink_cb, 1889251881Speter delete_cb, 1890251881Speter copy_cb, 1891251881Speter move_cb, 1892251881Speter rotate_cb, 1893251881Speter complete_cb, 1894251881Speter abort_cb 1895251881Speter }; 1896251881Speter struct editor_baton *eb = apr_pcalloc(result_pool, sizeof(*eb)); 1897251881Speter struct svn_delta__extra_baton *extra_baton = apr_pcalloc(result_pool, 1898251881Speter sizeof(*extra_baton)); 1899251881Speter 1900251881Speter if (!base_relpath) 1901251881Speter base_relpath = ""; 1902251881Speter else if (base_relpath[0] == '/') 1903251881Speter base_relpath += 1; 1904251881Speter 1905251881Speter eb->deditor = deditor; 1906251881Speter eb->dedit_baton = dedit_baton; 1907251881Speter eb->edit_pool = result_pool; 1908251881Speter eb->repos_root = apr_pstrdup(result_pool, repos_root); 1909251881Speter eb->base_relpath = apr_pstrdup(result_pool, base_relpath); 1910251881Speter 1911251881Speter eb->changes = apr_hash_make(result_pool); 1912251881Speter 1913251881Speter eb->fetch_kind_func = fetch_kind_func; 1914251881Speter eb->fetch_kind_baton = fetch_kind_baton; 1915251881Speter eb->fetch_props_func = fetch_props_func; 1916251881Speter eb->fetch_props_baton = fetch_props_baton; 1917251881Speter 1918251881Speter eb->root.base_revision = SVN_INVALID_REVNUM; 1919251881Speter 1920251881Speter eb->make_abs_paths = send_abs_paths; 1921251881Speter 1922251881Speter SVN_ERR(svn_editor_create(&editor, eb, cancel_func, cancel_baton, 1923251881Speter result_pool, scratch_pool)); 1924251881Speter SVN_ERR(svn_editor_setcb_many(editor, &editor_cbs, scratch_pool)); 1925251881Speter 1926251881Speter *editor_p = editor; 1927251881Speter 1928251881Speter *unlock_func = do_unlock; 1929251881Speter *unlock_baton = eb; 1930251881Speter 1931251881Speter extra_baton->start_edit = start_edit_func; 1932251881Speter extra_baton->target_revision = target_revision_func; 1933251881Speter extra_baton->baton = eb; 1934251881Speter 1935251881Speter *exb = extra_baton; 1936251881Speter 1937251881Speter return SVN_NO_ERROR; 1938251881Speter} 1939251881Speter 1940251881Spetersvn_delta_shim_callbacks_t * 1941251881Spetersvn_delta_shim_callbacks_default(apr_pool_t *result_pool) 1942251881Speter{ 1943251881Speter svn_delta_shim_callbacks_t *shim_callbacks = apr_pcalloc(result_pool, 1944251881Speter sizeof(*shim_callbacks)); 1945251881Speter return shim_callbacks; 1946251881Speter} 1947251881Speter 1948251881Speter/* To enable editor shims throughout Subversion, ENABLE_EV2_SHIMS should be 1949251881Speter * defined. This can be done manually, or by providing `--enable-ev2-shims' 1950251881Speter * to `configure'. */ 1951251881Speter 1952251881Spetersvn_error_t * 1953251881Spetersvn_editor__insert_shims(const svn_delta_editor_t **deditor_out, 1954251881Speter void **dedit_baton_out, 1955251881Speter const svn_delta_editor_t *deditor_in, 1956251881Speter void *dedit_baton_in, 1957251881Speter const char *repos_root, 1958251881Speter const char *base_relpath, 1959251881Speter svn_delta_shim_callbacks_t *shim_callbacks, 1960251881Speter apr_pool_t *result_pool, 1961251881Speter apr_pool_t *scratch_pool) 1962251881Speter{ 1963251881Speter#ifndef ENABLE_EV2_SHIMS 1964251881Speter /* Shims disabled, just copy the editor and baton directly. */ 1965251881Speter *deditor_out = deditor_in; 1966251881Speter *dedit_baton_out = dedit_baton_in; 1967251881Speter#else 1968251881Speter /* Use our shim APIs to create an intermediate svn_editor_t, and then 1969251881Speter wrap that again back into a svn_delta_editor_t. This introduces 1970251881Speter a lot of overhead. */ 1971251881Speter svn_editor_t *editor; 1972251881Speter 1973251881Speter /* The "extra baton" is a set of functions and a baton which allows the 1974251881Speter shims to communicate additional events to each other. 1975251881Speter svn_delta__editor_from_delta() returns a pointer to this baton, which 1976251881Speter svn_delta__delta_from_editor() should then store. */ 1977251881Speter struct svn_delta__extra_baton *exb; 1978251881Speter 1979251881Speter /* The reason this is a pointer is that we don't know the appropriate 1980251881Speter value until we start receiving paths. So process_actions() sets the 1981251881Speter flag, which drive_tree() later consumes. */ 1982251881Speter svn_boolean_t *found_abs_paths = apr_palloc(result_pool, 1983251881Speter sizeof(*found_abs_paths)); 1984251881Speter 1985251881Speter svn_delta__unlock_func_t unlock_func; 1986251881Speter void *unlock_baton; 1987251881Speter 1988251881Speter SVN_ERR_ASSERT(shim_callbacks->fetch_kind_func != NULL); 1989251881Speter SVN_ERR_ASSERT(shim_callbacks->fetch_props_func != NULL); 1990251881Speter SVN_ERR_ASSERT(shim_callbacks->fetch_base_func != NULL); 1991251881Speter 1992251881Speter SVN_ERR(svn_delta__editor_from_delta(&editor, &exb, 1993251881Speter &unlock_func, &unlock_baton, 1994251881Speter deditor_in, dedit_baton_in, 1995251881Speter found_abs_paths, repos_root, base_relpath, 1996251881Speter NULL, NULL, 1997251881Speter shim_callbacks->fetch_kind_func, 1998251881Speter shim_callbacks->fetch_baton, 1999251881Speter shim_callbacks->fetch_props_func, 2000251881Speter shim_callbacks->fetch_baton, 2001251881Speter result_pool, scratch_pool)); 2002251881Speter SVN_ERR(svn_delta__delta_from_editor(deditor_out, dedit_baton_out, editor, 2003251881Speter unlock_func, unlock_baton, 2004251881Speter found_abs_paths, 2005251881Speter repos_root, base_relpath, 2006251881Speter shim_callbacks->fetch_props_func, 2007251881Speter shim_callbacks->fetch_baton, 2008251881Speter shim_callbacks->fetch_base_func, 2009251881Speter shim_callbacks->fetch_baton, 2010251881Speter exb, result_pool)); 2011251881Speter 2012251881Speter#endif 2013251881Speter return SVN_NO_ERROR; 2014251881Speter} 2015