1251881Speter/* 2251881Speter * dump_editor.c: The svn_delta_editor_t editor used by svnrdump to 3251881Speter * dump revisions. 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter */ 24251881Speter 25251881Speter#include "svn_hash.h" 26251881Speter#include "svn_pools.h" 27251881Speter#include "svn_repos.h" 28251881Speter#include "svn_path.h" 29251881Speter#include "svn_props.h" 30251881Speter#include "svn_subst.h" 31251881Speter#include "svn_dirent_uri.h" 32251881Speter 33251881Speter#include "private/svn_subr_private.h" 34251881Speter#include "private/svn_dep_compat.h" 35251881Speter#include "private/svn_editor.h" 36251881Speter 37251881Speter#include "svnrdump.h" 38251881Speter#include <assert.h> 39251881Speter 40251881Speter#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r)) 41251881Speter 42251881Speter#if 0 43251881Speter#define LDR_DBG(x) SVN_DBG(x) 44251881Speter#else 45251881Speter#define LDR_DBG(x) while(0) 46251881Speter#endif 47251881Speter 48251881Speter/* A directory baton used by all directory-related callback functions 49251881Speter * in the dump editor. */ 50251881Speterstruct dir_baton 51251881Speter{ 52251881Speter struct dump_edit_baton *eb; 53251881Speter struct dir_baton *parent_dir_baton; 54251881Speter 55251881Speter /* Pool for per-directory allocations */ 56251881Speter apr_pool_t *pool; 57251881Speter 58251881Speter /* is this directory a new addition to this revision? */ 59251881Speter svn_boolean_t added; 60251881Speter 61251881Speter /* has this directory been written to the output stream? */ 62251881Speter svn_boolean_t written_out; 63251881Speter 64251881Speter /* the path to this directory */ 65251881Speter const char *repos_relpath; /* a relpath */ 66251881Speter 67251881Speter /* Copyfrom info for the node, if any. */ 68251881Speter const char *copyfrom_path; /* a relpath */ 69251881Speter svn_revnum_t copyfrom_rev; 70251881Speter 71251881Speter /* Properties which were modified during change_dir_prop. */ 72251881Speter apr_hash_t *props; 73251881Speter 74251881Speter /* Properties which were deleted during change_dir_prop. */ 75251881Speter apr_hash_t *deleted_props; 76251881Speter 77251881Speter /* Hash of paths that need to be deleted, though some -might- be 78251881Speter replaced. Maps const char * paths to this dir_baton. Note that 79251881Speter they're full paths, because that's what the editor driver gives 80251881Speter us, although they're all really within this directory. */ 81251881Speter apr_hash_t *deleted_entries; 82251881Speter 83251881Speter /* Flags to trigger dumping props and record termination newlines. */ 84251881Speter svn_boolean_t dump_props; 85251881Speter svn_boolean_t dump_newlines; 86251881Speter}; 87251881Speter 88251881Speter/* A file baton used by all file-related callback functions in the dump 89251881Speter * editor */ 90251881Speterstruct file_baton 91251881Speter{ 92251881Speter struct dump_edit_baton *eb; 93251881Speter struct dir_baton *parent_dir_baton; 94251881Speter 95251881Speter /* Pool for per-file allocations */ 96251881Speter apr_pool_t *pool; 97251881Speter 98251881Speter /* the path to this file */ 99251881Speter const char *repos_relpath; /* a relpath */ 100251881Speter 101251881Speter /* Properties which were modified during change_file_prop. */ 102251881Speter apr_hash_t *props; 103251881Speter 104251881Speter /* Properties which were deleted during change_file_prop. */ 105251881Speter apr_hash_t *deleted_props; 106251881Speter 107251881Speter /* The checksum of the file the delta is being applied to */ 108251881Speter const char *base_checksum; 109251881Speter 110251881Speter /* Copy state and source information (if any). */ 111251881Speter svn_boolean_t is_copy; 112251881Speter const char *copyfrom_path; 113251881Speter svn_revnum_t copyfrom_rev; 114251881Speter 115251881Speter /* The action associate with this node. */ 116251881Speter enum svn_node_action action; 117251881Speter 118251881Speter /* Flags to trigger dumping props and text. */ 119251881Speter svn_boolean_t dump_text; 120251881Speter svn_boolean_t dump_props; 121251881Speter}; 122251881Speter 123251881Speter/* A handler baton to be used in window_handler(). */ 124251881Speterstruct handler_baton 125251881Speter{ 126251881Speter svn_txdelta_window_handler_t apply_handler; 127251881Speter void *apply_baton; 128251881Speter}; 129251881Speter 130251881Speter/* The baton used by the dump editor. */ 131251881Speterstruct dump_edit_baton { 132251881Speter /* The output stream we write the dumpfile to */ 133251881Speter svn_stream_t *stream; 134251881Speter 135251881Speter /* A backdoor ra session to fetch additional information during the edit. */ 136251881Speter svn_ra_session_t *ra_session; 137251881Speter 138251881Speter /* The repository relpath of the anchor of the editor when driven 139251881Speter via the RA update mechanism; NULL otherwise. (When the editor is 140251881Speter driven via the RA "replay" mechanism instead, the editor is 141251881Speter always anchored at the repository, we don't need to prepend an 142251881Speter anchor path to the dumped node paths, and open_root() doesn't 143251881Speter need to manufacture directory additions.) */ 144251881Speter const char *update_anchor_relpath; 145251881Speter 146251881Speter /* Pool for per-revision allocations */ 147251881Speter apr_pool_t *pool; 148251881Speter 149251881Speter /* Temporary file used for textdelta application along with its 150251881Speter absolute path; these two variables should be allocated in the 151251881Speter per-edit-session pool */ 152251881Speter const char *delta_abspath; 153251881Speter apr_file_t *delta_file; 154251881Speter 155251881Speter /* The revision we're currently dumping. */ 156251881Speter svn_revnum_t current_revision; 157251881Speter 158251881Speter /* The kind (file or directory) and baton of the item whose block of 159251881Speter dump stream data has not been fully completed; NULL if there's no 160251881Speter such item. */ 161251881Speter svn_node_kind_t pending_kind; 162251881Speter void *pending_baton; 163251881Speter}; 164251881Speter 165251881Speter/* Make a directory baton to represent the directory at PATH (relative 166251881Speter * to the EDIT_BATON). 167251881Speter * 168251881Speter * COPYFROM_PATH/COPYFROM_REV are the path/revision against which this 169251881Speter * directory should be compared for changes. If the copyfrom 170251881Speter * information is valid, the directory will be compared against its 171251881Speter * copy source. 172251881Speter * 173251881Speter * PB is the directory baton of this directory's parent, or NULL if 174251881Speter * this is the top-level directory of the edit. ADDED indicates if 175251881Speter * this directory is newly added in this revision. Perform all 176251881Speter * allocations in POOL. */ 177251881Speterstatic struct dir_baton * 178251881Spetermake_dir_baton(const char *path, 179251881Speter const char *copyfrom_path, 180251881Speter svn_revnum_t copyfrom_rev, 181251881Speter void *edit_baton, 182251881Speter struct dir_baton *pb, 183251881Speter svn_boolean_t added, 184251881Speter apr_pool_t *pool) 185251881Speter{ 186251881Speter struct dump_edit_baton *eb = edit_baton; 187251881Speter struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db)); 188251881Speter const char *repos_relpath; 189251881Speter 190251881Speter /* Construct the full path of this node. */ 191251881Speter if (pb) 192251881Speter repos_relpath = svn_relpath_canonicalize(path, pool); 193251881Speter else 194251881Speter repos_relpath = ""; 195251881Speter 196251881Speter /* Strip leading slash from copyfrom_path so that the path is 197251881Speter canonical and svn_relpath_join can be used */ 198251881Speter if (copyfrom_path) 199251881Speter copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool); 200251881Speter 201251881Speter new_db->eb = eb; 202251881Speter new_db->parent_dir_baton = pb; 203251881Speter new_db->pool = pool; 204251881Speter new_db->repos_relpath = repos_relpath; 205251881Speter new_db->copyfrom_path = copyfrom_path 206251881Speter ? svn_relpath_canonicalize(copyfrom_path, pool) 207251881Speter : NULL; 208251881Speter new_db->copyfrom_rev = copyfrom_rev; 209251881Speter new_db->added = added; 210251881Speter new_db->written_out = FALSE; 211251881Speter new_db->props = apr_hash_make(pool); 212251881Speter new_db->deleted_props = apr_hash_make(pool); 213251881Speter new_db->deleted_entries = apr_hash_make(pool); 214251881Speter 215251881Speter return new_db; 216251881Speter} 217251881Speter 218251881Speter/* Make a file baton to represent the directory at PATH (relative to 219251881Speter * PB->eb). PB is the directory baton of this directory's parent, or 220251881Speter * NULL if this is the top-level directory of the edit. Perform all 221251881Speter * allocations in POOL. */ 222251881Speterstatic struct file_baton * 223251881Spetermake_file_baton(const char *path, 224251881Speter struct dir_baton *pb, 225251881Speter apr_pool_t *pool) 226251881Speter{ 227251881Speter struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb)); 228251881Speter 229251881Speter new_fb->eb = pb->eb; 230251881Speter new_fb->parent_dir_baton = pb; 231251881Speter new_fb->pool = pool; 232251881Speter new_fb->repos_relpath = svn_relpath_canonicalize(path, pool); 233251881Speter new_fb->props = apr_hash_make(pool); 234251881Speter new_fb->deleted_props = apr_hash_make(pool); 235251881Speter new_fb->is_copy = FALSE; 236251881Speter new_fb->copyfrom_path = NULL; 237251881Speter new_fb->copyfrom_rev = SVN_INVALID_REVNUM; 238251881Speter new_fb->action = svn_node_action_change; 239251881Speter 240251881Speter return new_fb; 241251881Speter} 242251881Speter 243251881Speter/* Return in *HEADER and *CONTENT the headers and content for PROPS. */ 244251881Speterstatic svn_error_t * 245251881Speterget_props_content(svn_stringbuf_t **header, 246251881Speter svn_stringbuf_t **content, 247251881Speter apr_hash_t *props, 248251881Speter apr_hash_t *deleted_props, 249251881Speter apr_pool_t *result_pool, 250251881Speter apr_pool_t *scratch_pool) 251251881Speter{ 252251881Speter svn_stream_t *content_stream; 253251881Speter apr_hash_t *normal_props; 254251881Speter const char *buf; 255251881Speter 256251881Speter *content = svn_stringbuf_create_empty(result_pool); 257251881Speter *header = svn_stringbuf_create_empty(result_pool); 258251881Speter 259251881Speter content_stream = svn_stream_from_stringbuf(*content, scratch_pool); 260251881Speter 261251881Speter SVN_ERR(svn_rdump__normalize_props(&normal_props, props, scratch_pool)); 262251881Speter SVN_ERR(svn_hash_write_incremental(normal_props, deleted_props, 263251881Speter content_stream, "PROPS-END", 264251881Speter scratch_pool)); 265251881Speter SVN_ERR(svn_stream_close(content_stream)); 266251881Speter 267251881Speter /* Prop-delta: true */ 268251881Speter *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA 269251881Speter ": true\n"); 270251881Speter 271251881Speter /* Prop-content-length: 193 */ 272251881Speter buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH 273251881Speter ": %" APR_SIZE_T_FMT "\n", (*content)->len); 274251881Speter svn_stringbuf_appendcstr(*header, buf); 275251881Speter 276251881Speter return SVN_NO_ERROR; 277251881Speter} 278251881Speter 279251881Speter/* Extract and dump properties stored in PROPS and property deletions 280251881Speter * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to 281251881Speter * FALSE. 282251881Speter * 283251881Speter * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing 284251881Speter * the content block of the property changes; otherwise, dump that to 285251881Speter * the stream, too. 286251881Speter */ 287251881Speterstatic svn_error_t * 288251881Speterdo_dump_props(svn_stringbuf_t **propstring, 289251881Speter svn_stream_t *stream, 290251881Speter apr_hash_t *props, 291251881Speter apr_hash_t *deleted_props, 292251881Speter svn_boolean_t *trigger_var, 293251881Speter apr_pool_t *result_pool, 294251881Speter apr_pool_t *scratch_pool) 295251881Speter{ 296251881Speter svn_stringbuf_t *header; 297251881Speter svn_stringbuf_t *content; 298251881Speter apr_size_t len; 299251881Speter 300251881Speter if (trigger_var && !*trigger_var) 301251881Speter return SVN_NO_ERROR; 302251881Speter 303251881Speter SVN_ERR(get_props_content(&header, &content, props, deleted_props, 304251881Speter result_pool, scratch_pool)); 305251881Speter len = header->len; 306251881Speter SVN_ERR(svn_stream_write(stream, header->data, &len)); 307251881Speter 308251881Speter if (propstring) 309251881Speter { 310251881Speter *propstring = content; 311251881Speter } 312251881Speter else 313251881Speter { 314251881Speter /* Content-length: 14 */ 315251881Speter SVN_ERR(svn_stream_printf(stream, scratch_pool, 316251881Speter SVN_REPOS_DUMPFILE_CONTENT_LENGTH 317251881Speter ": %" APR_SIZE_T_FMT "\n\n", 318251881Speter content->len)); 319251881Speter 320251881Speter len = content->len; 321251881Speter SVN_ERR(svn_stream_write(stream, content->data, &len)); 322251881Speter 323251881Speter /* No text is going to be dumped. Write a couple of newlines and 324251881Speter wait for the next node/ revision. */ 325251881Speter SVN_ERR(svn_stream_puts(stream, "\n\n")); 326251881Speter 327251881Speter /* Cleanup so that data is never dumped twice. */ 328251881Speter apr_hash_clear(props); 329251881Speter apr_hash_clear(deleted_props); 330251881Speter if (trigger_var) 331251881Speter *trigger_var = FALSE; 332251881Speter } 333251881Speter 334251881Speter return SVN_NO_ERROR; 335251881Speter} 336251881Speter 337251881Speterstatic svn_error_t * 338251881Speterdo_dump_newlines(struct dump_edit_baton *eb, 339251881Speter svn_boolean_t *trigger_var, 340251881Speter apr_pool_t *pool) 341251881Speter{ 342251881Speter if (trigger_var && *trigger_var) 343251881Speter { 344251881Speter SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); 345251881Speter *trigger_var = FALSE; 346251881Speter } 347251881Speter return SVN_NO_ERROR; 348251881Speter} 349251881Speter 350251881Speter/* 351251881Speter * Write out a node record for PATH of type KIND under EB->FS_ROOT. 352251881Speter * ACTION describes what is happening to the node (see enum 353251881Speter * svn_node_action). Write record to writable EB->STREAM, using 354251881Speter * EB->BUFFER to write in chunks. 355251881Speter * 356251881Speter * If the node was itself copied, IS_COPY is TRUE and the 357251881Speter * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV. 358251881Speter * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this 359251881Speter * node is part of a copied subtree. 360251881Speter */ 361251881Speterstatic svn_error_t * 362251881Speterdump_node(struct dump_edit_baton *eb, 363251881Speter const char *repos_relpath, 364251881Speter struct dir_baton *db, 365251881Speter struct file_baton *fb, 366251881Speter enum svn_node_action action, 367251881Speter svn_boolean_t is_copy, 368251881Speter const char *copyfrom_path, 369251881Speter svn_revnum_t copyfrom_rev, 370251881Speter apr_pool_t *pool) 371251881Speter{ 372251881Speter const char *node_relpath = repos_relpath; 373251881Speter 374251881Speter assert(svn_relpath_is_canonical(repos_relpath)); 375251881Speter assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path)); 376251881Speter assert(! (db && fb)); 377251881Speter 378251881Speter /* Add the edit root relpath prefix if necessary. */ 379251881Speter if (eb->update_anchor_relpath) 380251881Speter node_relpath = svn_relpath_join(eb->update_anchor_relpath, 381251881Speter node_relpath, pool); 382251881Speter 383251881Speter /* Node-path: ... */ 384251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 385251881Speter SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", 386251881Speter node_relpath)); 387251881Speter 388251881Speter /* Node-kind: "file" | "dir" */ 389251881Speter if (fb) 390251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 391251881Speter SVN_REPOS_DUMPFILE_NODE_KIND ": file\n")); 392251881Speter else if (db) 393251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 394251881Speter SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); 395251881Speter 396251881Speter 397251881Speter /* Write the appropriate Node-action header */ 398251881Speter switch (action) 399251881Speter { 400251881Speter case svn_node_action_change: 401251881Speter /* We are here after a change_file_prop or change_dir_prop. They 402251881Speter set up whatever dump_props they needed to- nothing to 403251881Speter do here but print node action information. 404251881Speter 405251881Speter Node-action: change. */ 406251881Speter SVN_ERR(svn_stream_puts(eb->stream, 407251881Speter SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n")); 408251881Speter break; 409251881Speter 410251881Speter case svn_node_action_replace: 411251881Speter if (is_copy) 412251881Speter { 413251881Speter /* Delete the original, and then re-add the replacement as a 414251881Speter copy using recursive calls into this function. */ 415251881Speter SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete, 416251881Speter FALSE, NULL, SVN_INVALID_REVNUM, pool)); 417251881Speter SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add, 418251881Speter is_copy, copyfrom_path, copyfrom_rev, pool)); 419251881Speter } 420251881Speter else 421251881Speter { 422251881Speter /* Node-action: replace */ 423251881Speter SVN_ERR(svn_stream_puts(eb->stream, 424251881Speter SVN_REPOS_DUMPFILE_NODE_ACTION 425251881Speter ": replace\n")); 426251881Speter 427251881Speter /* Wait for a change_*_prop to be called before dumping 428251881Speter anything */ 429251881Speter if (fb) 430251881Speter fb->dump_props = TRUE; 431251881Speter else if (db) 432251881Speter db->dump_props = TRUE; 433251881Speter } 434251881Speter break; 435251881Speter 436251881Speter case svn_node_action_delete: 437251881Speter /* Node-action: delete */ 438251881Speter SVN_ERR(svn_stream_puts(eb->stream, 439251881Speter SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n")); 440251881Speter 441251881Speter /* We can leave this routine quietly now. Nothing more to do- 442251881Speter print a couple of newlines because we're not dumping props or 443251881Speter text. */ 444251881Speter SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); 445251881Speter 446251881Speter break; 447251881Speter 448251881Speter case svn_node_action_add: 449251881Speter /* Node-action: add */ 450251881Speter SVN_ERR(svn_stream_puts(eb->stream, 451251881Speter SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); 452251881Speter 453251881Speter if (is_copy) 454251881Speter { 455251881Speter /* Node-copyfrom-rev / Node-copyfrom-path */ 456251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 457251881Speter SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV 458251881Speter ": %ld\n" 459251881Speter SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH 460251881Speter ": %s\n", 461251881Speter copyfrom_rev, copyfrom_path)); 462251881Speter 463251881Speter /* Ugly hack: If a directory was copied from a previous 464251881Speter revision, nothing like close_file() will be called to write two 465251881Speter blank lines. If change_dir_prop() is called, props are dumped 466251881Speter (along with the necessary PROPS-END\n\n and we're good. So 467251881Speter set DUMP_NEWLINES here to print the newlines unless 468251881Speter change_dir_prop() is called next otherwise the `svnadmin load` 469251881Speter parser will fail. */ 470251881Speter if (db) 471251881Speter db->dump_newlines = TRUE; 472251881Speter } 473251881Speter else 474251881Speter { 475251881Speter /* fb->dump_props (for files) is handled in close_file() 476251881Speter which is called immediately. 477251881Speter 478251881Speter However, directories are not closed until all the work 479251881Speter inside them has been done; db->dump_props (for directories) 480251881Speter is handled (via dump_pending()) in all the functions that 481251881Speter can possibly be called after add_directory(): 482251881Speter 483251881Speter - add_directory() 484251881Speter - open_directory() 485251881Speter - delete_entry() 486251881Speter - close_directory() 487251881Speter - add_file() 488251881Speter - open_file() 489251881Speter 490251881Speter change_dir_prop() is a special case. */ 491251881Speter if (fb) 492251881Speter fb->dump_props = TRUE; 493251881Speter else if (db) 494251881Speter db->dump_props = TRUE; 495251881Speter } 496251881Speter 497251881Speter break; 498251881Speter } 499251881Speter return SVN_NO_ERROR; 500251881Speter} 501251881Speter 502251881Speterstatic svn_error_t * 503251881Speterdump_mkdir(struct dump_edit_baton *eb, 504251881Speter const char *repos_relpath, 505251881Speter apr_pool_t *pool) 506251881Speter{ 507251881Speter svn_stringbuf_t *prop_header, *prop_content; 508251881Speter apr_size_t len; 509251881Speter const char *buf; 510251881Speter 511251881Speter /* Node-path: ... */ 512251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 513251881Speter SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", 514251881Speter repos_relpath)); 515251881Speter 516251881Speter /* Node-kind: dir */ 517251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 518251881Speter SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); 519251881Speter 520251881Speter /* Node-action: add */ 521251881Speter SVN_ERR(svn_stream_puts(eb->stream, 522251881Speter SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); 523251881Speter 524251881Speter /* Dump the (empty) property block. */ 525251881Speter SVN_ERR(get_props_content(&prop_header, &prop_content, 526251881Speter apr_hash_make(pool), apr_hash_make(pool), 527251881Speter pool, pool)); 528251881Speter len = prop_header->len; 529251881Speter SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len)); 530251881Speter len = prop_content->len; 531251881Speter buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH 532251881Speter ": %" APR_SIZE_T_FMT "\n", len); 533251881Speter SVN_ERR(svn_stream_puts(eb->stream, buf)); 534251881Speter SVN_ERR(svn_stream_puts(eb->stream, "\n")); 535251881Speter SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len)); 536251881Speter 537251881Speter /* Newlines to tie it all off. */ 538251881Speter SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); 539251881Speter 540251881Speter return SVN_NO_ERROR; 541251881Speter} 542251881Speter 543251881Speter/* Dump pending items from the specified node, to allow starting the dump 544251881Speter of a child node */ 545251881Speterstatic svn_error_t * 546251881Speterdump_pending(struct dump_edit_baton *eb, 547251881Speter apr_pool_t *scratch_pool) 548251881Speter{ 549251881Speter if (! eb->pending_baton) 550251881Speter return SVN_NO_ERROR; 551251881Speter 552251881Speter if (eb->pending_kind == svn_node_dir) 553251881Speter { 554251881Speter struct dir_baton *db = eb->pending_baton; 555251881Speter 556251881Speter /* Some pending properties to dump? */ 557251881Speter SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props, 558251881Speter &(db->dump_props), db->pool, scratch_pool)); 559251881Speter 560251881Speter /* Some pending newlines to dump? */ 561251881Speter SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool)); 562251881Speter } 563251881Speter else if (eb->pending_kind == svn_node_file) 564251881Speter { 565251881Speter struct file_baton *fb = eb->pending_baton; 566251881Speter 567251881Speter /* Some pending properties to dump? */ 568251881Speter SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props, 569251881Speter &(fb->dump_props), fb->pool, scratch_pool)); 570251881Speter } 571251881Speter else 572251881Speter abort(); 573251881Speter 574251881Speter /* Anything that was pending is pending no longer. */ 575251881Speter eb->pending_baton = NULL; 576251881Speter eb->pending_kind = svn_node_none; 577251881Speter 578251881Speter return SVN_NO_ERROR; 579251881Speter} 580251881Speter 581251881Speter 582251881Speter 583251881Speter/*** Editor Function Implementations ***/ 584251881Speter 585251881Speterstatic svn_error_t * 586251881Speteropen_root(void *edit_baton, 587251881Speter svn_revnum_t base_revision, 588251881Speter apr_pool_t *pool, 589251881Speter void **root_baton) 590251881Speter{ 591251881Speter struct dump_edit_baton *eb = edit_baton; 592251881Speter struct dir_baton *new_db = NULL; 593251881Speter 594251881Speter /* Clear the per-revision pool after each revision */ 595251881Speter svn_pool_clear(eb->pool); 596251881Speter 597251881Speter LDR_DBG(("open_root %p\n", *root_baton)); 598251881Speter 599251881Speter if (eb->update_anchor_relpath) 600251881Speter { 601251881Speter int i; 602251881Speter const char *parent_path = eb->update_anchor_relpath; 603251881Speter apr_array_header_t *dirs_to_add = 604251881Speter apr_array_make(pool, 4, sizeof(const char *)); 605251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 606251881Speter 607251881Speter while (! svn_path_is_empty(parent_path)) 608251881Speter { 609251881Speter APR_ARRAY_PUSH(dirs_to_add, const char *) = parent_path; 610251881Speter parent_path = svn_relpath_dirname(parent_path, pool); 611251881Speter } 612251881Speter 613251881Speter for (i = dirs_to_add->nelts; i; --i) 614251881Speter { 615251881Speter const char *dir_to_add = 616251881Speter APR_ARRAY_IDX(dirs_to_add, i - 1, const char *); 617251881Speter 618251881Speter svn_pool_clear(iterpool); 619251881Speter 620251881Speter /* For parents of the source directory, we just manufacture 621251881Speter the adds ourselves. */ 622251881Speter if (i > 1) 623251881Speter { 624251881Speter SVN_ERR(dump_mkdir(eb, dir_to_add, iterpool)); 625251881Speter } 626251881Speter else 627251881Speter { 628251881Speter /* ... but for the source directory itself, we'll defer 629251881Speter to letting the typical plumbing handle this task. */ 630251881Speter new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, 631251881Speter edit_baton, NULL, TRUE, pool); 632251881Speter SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db, 633251881Speter NULL, svn_node_action_add, FALSE, 634251881Speter NULL, SVN_INVALID_REVNUM, pool)); 635251881Speter 636251881Speter /* Remember that we've started but not yet finished 637251881Speter handling this directory. */ 638251881Speter new_db->written_out = TRUE; 639251881Speter eb->pending_baton = new_db; 640251881Speter eb->pending_kind = svn_node_dir; 641251881Speter } 642251881Speter } 643251881Speter svn_pool_destroy(iterpool); 644251881Speter } 645251881Speter 646251881Speter if (! new_db) 647251881Speter { 648251881Speter new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, 649251881Speter edit_baton, NULL, FALSE, pool); 650251881Speter } 651251881Speter 652251881Speter *root_baton = new_db; 653251881Speter return SVN_NO_ERROR; 654251881Speter} 655251881Speter 656251881Speterstatic svn_error_t * 657251881Speterdelete_entry(const char *path, 658251881Speter svn_revnum_t revision, 659251881Speter void *parent_baton, 660251881Speter apr_pool_t *pool) 661251881Speter{ 662251881Speter struct dir_baton *pb = parent_baton; 663251881Speter 664251881Speter LDR_DBG(("delete_entry %s\n", path)); 665251881Speter 666251881Speter SVN_ERR(dump_pending(pb->eb, pool)); 667251881Speter 668251881Speter /* We don't dump this deletion immediate. Rather, we add this path 669251881Speter to the deleted_entries of the parent directory baton. That way, 670251881Speter we can tell (later) an addition from a replacement. All the real 671251881Speter deletions get handled in close_directory(). */ 672251881Speter svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path), pb); 673251881Speter 674251881Speter return SVN_NO_ERROR; 675251881Speter} 676251881Speter 677251881Speterstatic svn_error_t * 678251881Speteradd_directory(const char *path, 679251881Speter void *parent_baton, 680251881Speter const char *copyfrom_path, 681251881Speter svn_revnum_t copyfrom_rev, 682251881Speter apr_pool_t *pool, 683251881Speter void **child_baton) 684251881Speter{ 685251881Speter struct dir_baton *pb = parent_baton; 686251881Speter void *val; 687251881Speter struct dir_baton *new_db; 688251881Speter svn_boolean_t is_copy; 689251881Speter 690251881Speter LDR_DBG(("add_directory %s\n", path)); 691251881Speter 692251881Speter SVN_ERR(dump_pending(pb->eb, pool)); 693251881Speter 694251881Speter new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, 695251881Speter pb, TRUE, pb->eb->pool); 696251881Speter 697251881Speter /* This might be a replacement -- is the path already deleted? */ 698251881Speter val = svn_hash_gets(pb->deleted_entries, path); 699251881Speter 700251881Speter /* Detect an add-with-history */ 701251881Speter is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev); 702251881Speter 703251881Speter /* Dump the node */ 704251881Speter SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL, 705251881Speter val ? svn_node_action_replace : svn_node_action_add, 706251881Speter is_copy, 707251881Speter is_copy ? new_db->copyfrom_path : NULL, 708251881Speter is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, 709251881Speter pool)); 710251881Speter 711251881Speter if (val) 712251881Speter /* Delete the path, it's now been dumped */ 713251881Speter svn_hash_sets(pb->deleted_entries, path, NULL); 714251881Speter 715251881Speter /* Remember that we've started, but not yet finished handling this 716251881Speter directory. */ 717251881Speter new_db->written_out = TRUE; 718251881Speter pb->eb->pending_baton = new_db; 719251881Speter pb->eb->pending_kind = svn_node_dir; 720251881Speter 721251881Speter *child_baton = new_db; 722251881Speter return SVN_NO_ERROR; 723251881Speter} 724251881Speter 725251881Speterstatic svn_error_t * 726251881Speteropen_directory(const char *path, 727251881Speter void *parent_baton, 728251881Speter svn_revnum_t base_revision, 729251881Speter apr_pool_t *pool, 730251881Speter void **child_baton) 731251881Speter{ 732251881Speter struct dir_baton *pb = parent_baton; 733251881Speter struct dir_baton *new_db; 734251881Speter const char *copyfrom_path = NULL; 735251881Speter svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; 736251881Speter 737251881Speter LDR_DBG(("open_directory %s\n", path)); 738251881Speter 739251881Speter SVN_ERR(dump_pending(pb->eb, pool)); 740251881Speter 741251881Speter /* If the parent directory has explicit comparison path and rev, 742251881Speter record the same for this one. */ 743251881Speter if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev)) 744251881Speter { 745251881Speter copyfrom_path = svn_relpath_join(pb->copyfrom_path, 746251881Speter svn_relpath_basename(path, NULL), 747251881Speter pb->eb->pool); 748251881Speter copyfrom_rev = pb->copyfrom_rev; 749251881Speter } 750251881Speter 751251881Speter new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, 752251881Speter FALSE, pb->eb->pool); 753251881Speter 754251881Speter *child_baton = new_db; 755251881Speter return SVN_NO_ERROR; 756251881Speter} 757251881Speter 758251881Speterstatic svn_error_t * 759251881Speterclose_directory(void *dir_baton, 760251881Speter apr_pool_t *pool) 761251881Speter{ 762251881Speter struct dir_baton *db = dir_baton; 763251881Speter apr_hash_index_t *hi; 764251881Speter svn_boolean_t this_pending; 765251881Speter 766251881Speter LDR_DBG(("close_directory %p\n", dir_baton)); 767251881Speter 768251881Speter /* Remember if this directory is the one currently pending. */ 769251881Speter this_pending = (db->eb->pending_baton == db); 770251881Speter 771251881Speter SVN_ERR(dump_pending(db->eb, pool)); 772251881Speter 773251881Speter /* If this directory was pending, then dump_pending() should have 774251881Speter taken care of all the props and such. Of course, the only way 775251881Speter that would be the case is if this directory was added/replaced. 776251881Speter 777251881Speter Otherwise, if stuff for this directory has already been written 778251881Speter out (at some point in the past, prior to our handling other 779251881Speter nodes), we might need to generate a second "change" record just 780251881Speter to carry the information we've since learned about the 781251881Speter directory. */ 782251881Speter if ((! this_pending) && (db->dump_props)) 783251881Speter { 784251881Speter SVN_ERR(dump_node(db->eb, db->repos_relpath, db, NULL, 785251881Speter svn_node_action_change, FALSE, 786251881Speter NULL, SVN_INVALID_REVNUM, pool)); 787251881Speter db->eb->pending_baton = db; 788251881Speter db->eb->pending_kind = svn_node_dir; 789251881Speter SVN_ERR(dump_pending(db->eb, pool)); 790251881Speter } 791251881Speter 792251881Speter /* Dump the deleted directory entries */ 793251881Speter for (hi = apr_hash_first(pool, db->deleted_entries); hi; 794251881Speter hi = apr_hash_next(hi)) 795251881Speter { 796251881Speter const char *path = svn__apr_hash_index_key(hi); 797251881Speter 798251881Speter SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete, 799251881Speter FALSE, NULL, SVN_INVALID_REVNUM, pool)); 800251881Speter } 801251881Speter 802251881Speter /* ### should be unnecessary */ 803251881Speter apr_hash_clear(db->deleted_entries); 804251881Speter 805251881Speter return SVN_NO_ERROR; 806251881Speter} 807251881Speter 808251881Speterstatic svn_error_t * 809251881Speteradd_file(const char *path, 810251881Speter void *parent_baton, 811251881Speter const char *copyfrom_path, 812251881Speter svn_revnum_t copyfrom_rev, 813251881Speter apr_pool_t *pool, 814251881Speter void **file_baton) 815251881Speter{ 816251881Speter struct dir_baton *pb = parent_baton; 817251881Speter struct file_baton *fb; 818251881Speter void *val; 819251881Speter 820251881Speter LDR_DBG(("add_file %s\n", path)); 821251881Speter 822251881Speter SVN_ERR(dump_pending(pb->eb, pool)); 823251881Speter 824251881Speter /* Make the file baton. */ 825251881Speter fb = make_file_baton(path, pb, pool); 826251881Speter 827251881Speter /* This might be a replacement -- is the path already deleted? */ 828251881Speter val = svn_hash_gets(pb->deleted_entries, path); 829251881Speter 830251881Speter /* Detect add-with-history. */ 831251881Speter if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev)) 832251881Speter { 833251881Speter fb->copyfrom_path = svn_relpath_canonicalize(copyfrom_path, fb->pool); 834251881Speter fb->copyfrom_rev = copyfrom_rev; 835251881Speter fb->is_copy = TRUE; 836251881Speter } 837251881Speter fb->action = val ? svn_node_action_replace : svn_node_action_add; 838251881Speter 839251881Speter /* Delete the path, it's now been dumped. */ 840251881Speter if (val) 841251881Speter svn_hash_sets(pb->deleted_entries, path, NULL); 842251881Speter 843251881Speter *file_baton = fb; 844251881Speter return SVN_NO_ERROR; 845251881Speter} 846251881Speter 847251881Speterstatic svn_error_t * 848251881Speteropen_file(const char *path, 849251881Speter void *parent_baton, 850251881Speter svn_revnum_t ancestor_revision, 851251881Speter apr_pool_t *pool, 852251881Speter void **file_baton) 853251881Speter{ 854251881Speter struct dir_baton *pb = parent_baton; 855251881Speter struct file_baton *fb; 856251881Speter 857251881Speter LDR_DBG(("open_file %s\n", path)); 858251881Speter 859251881Speter SVN_ERR(dump_pending(pb->eb, pool)); 860251881Speter 861251881Speter /* Make the file baton. */ 862251881Speter fb = make_file_baton(path, pb, pool); 863251881Speter 864251881Speter /* If the parent directory has explicit copyfrom path and rev, 865251881Speter record the same for this one. */ 866251881Speter if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev)) 867251881Speter { 868251881Speter fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path, 869251881Speter svn_relpath_basename(path, NULL), 870251881Speter pb->eb->pool); 871251881Speter fb->copyfrom_rev = pb->copyfrom_rev; 872251881Speter } 873251881Speter 874251881Speter *file_baton = fb; 875251881Speter return SVN_NO_ERROR; 876251881Speter} 877251881Speter 878251881Speterstatic svn_error_t * 879251881Speterchange_dir_prop(void *parent_baton, 880251881Speter const char *name, 881251881Speter const svn_string_t *value, 882251881Speter apr_pool_t *pool) 883251881Speter{ 884251881Speter struct dir_baton *db = parent_baton; 885251881Speter svn_boolean_t this_pending; 886251881Speter 887251881Speter LDR_DBG(("change_dir_prop %p\n", parent_baton)); 888251881Speter 889251881Speter /* This directory is not pending, but something else is, so handle 890251881Speter the "something else". */ 891251881Speter this_pending = (db->eb->pending_baton == db); 892251881Speter if (! this_pending) 893251881Speter SVN_ERR(dump_pending(db->eb, pool)); 894251881Speter 895251881Speter if (svn_property_kind2(name) != svn_prop_regular_kind) 896251881Speter return SVN_NO_ERROR; 897251881Speter 898251881Speter if (value) 899251881Speter svn_hash_sets(db->props, 900251881Speter apr_pstrdup(db->pool, name), 901251881Speter svn_string_dup(value, db->pool)); 902251881Speter else 903251881Speter svn_hash_sets(db->deleted_props, apr_pstrdup(db->pool, name), ""); 904251881Speter 905251881Speter /* Make sure we eventually output the props, and disable printing 906251881Speter a couple of extra newlines */ 907251881Speter db->dump_newlines = FALSE; 908251881Speter db->dump_props = TRUE; 909251881Speter 910251881Speter return SVN_NO_ERROR; 911251881Speter} 912251881Speter 913251881Speterstatic svn_error_t * 914251881Speterchange_file_prop(void *file_baton, 915251881Speter const char *name, 916251881Speter const svn_string_t *value, 917251881Speter apr_pool_t *pool) 918251881Speter{ 919251881Speter struct file_baton *fb = file_baton; 920251881Speter 921251881Speter LDR_DBG(("change_file_prop %p\n", file_baton)); 922251881Speter 923251881Speter if (svn_property_kind2(name) != svn_prop_regular_kind) 924251881Speter return SVN_NO_ERROR; 925251881Speter 926251881Speter if (value) 927251881Speter svn_hash_sets(fb->props, 928251881Speter apr_pstrdup(fb->pool, name), 929251881Speter svn_string_dup(value, fb->pool)); 930251881Speter else 931251881Speter svn_hash_sets(fb->deleted_props, apr_pstrdup(fb->pool, name), ""); 932251881Speter 933251881Speter /* Dump the property headers and wait; close_file might need 934251881Speter to write text headers too depending on whether 935251881Speter apply_textdelta is called */ 936251881Speter fb->dump_props = TRUE; 937251881Speter 938251881Speter return SVN_NO_ERROR; 939251881Speter} 940251881Speter 941251881Speterstatic svn_error_t * 942251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton) 943251881Speter{ 944251881Speter struct handler_baton *hb = baton; 945251881Speter static svn_error_t *err; 946251881Speter 947251881Speter err = hb->apply_handler(window, hb->apply_baton); 948251881Speter if (window != NULL && !err) 949251881Speter return SVN_NO_ERROR; 950251881Speter 951251881Speter if (err) 952251881Speter SVN_ERR(err); 953251881Speter 954251881Speter return SVN_NO_ERROR; 955251881Speter} 956251881Speter 957251881Speterstatic svn_error_t * 958251881Speterapply_textdelta(void *file_baton, const char *base_checksum, 959251881Speter apr_pool_t *pool, 960251881Speter svn_txdelta_window_handler_t *handler, 961251881Speter void **handler_baton) 962251881Speter{ 963251881Speter struct file_baton *fb = file_baton; 964251881Speter struct dump_edit_baton *eb = fb->eb; 965251881Speter struct handler_baton *hb; 966251881Speter svn_stream_t *delta_filestream; 967251881Speter 968251881Speter LDR_DBG(("apply_textdelta %p\n", file_baton)); 969251881Speter 970251881Speter /* This is custom handler_baton, allocated from a separate pool. */ 971251881Speter hb = apr_pcalloc(eb->pool, sizeof(*hb)); 972251881Speter 973251881Speter /* Use a temporary file to measure the Text-content-length */ 974251881Speter delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool); 975251881Speter 976251881Speter /* Prepare to write the delta to the delta_filestream */ 977251881Speter svn_txdelta_to_svndiff3(&(hb->apply_handler), &(hb->apply_baton), 978251881Speter delta_filestream, 0, 979251881Speter SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); 980251881Speter 981251881Speter /* Record that there's text to be dumped, and its base checksum. */ 982251881Speter fb->dump_text = TRUE; 983251881Speter fb->base_checksum = apr_pstrdup(eb->pool, base_checksum); 984251881Speter 985251881Speter /* The actual writing takes place when this function has 986251881Speter finished. Set handler and handler_baton now so for 987251881Speter window_handler() */ 988251881Speter *handler = window_handler; 989251881Speter *handler_baton = hb; 990251881Speter 991251881Speter return SVN_NO_ERROR; 992251881Speter} 993251881Speter 994251881Speterstatic svn_error_t * 995251881Speterclose_file(void *file_baton, 996251881Speter const char *text_checksum, 997251881Speter apr_pool_t *pool) 998251881Speter{ 999251881Speter struct file_baton *fb = file_baton; 1000251881Speter struct dump_edit_baton *eb = fb->eb; 1001251881Speter apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t)); 1002251881Speter svn_stringbuf_t *propstring; 1003251881Speter 1004251881Speter LDR_DBG(("close_file %p\n", file_baton)); 1005251881Speter 1006251881Speter SVN_ERR(dump_pending(eb, pool)); 1007251881Speter 1008251881Speter /* Dump the node. */ 1009251881Speter SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb, 1010251881Speter fb->action, fb->is_copy, fb->copyfrom_path, 1011251881Speter fb->copyfrom_rev, pool)); 1012251881Speter 1013251881Speter /* Some pending properties to dump? We'll dump just the headers for 1014251881Speter now, then dump the actual propchange content only after dumping 1015251881Speter the text headers too (if present). */ 1016251881Speter SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props, 1017251881Speter &(fb->dump_props), pool, pool)); 1018251881Speter 1019251881Speter /* Dump the text headers */ 1020251881Speter if (fb->dump_text) 1021251881Speter { 1022251881Speter apr_status_t err; 1023251881Speter 1024251881Speter /* Text-delta: true */ 1025251881Speter SVN_ERR(svn_stream_puts(eb->stream, 1026251881Speter SVN_REPOS_DUMPFILE_TEXT_DELTA 1027251881Speter ": true\n")); 1028251881Speter 1029251881Speter err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file); 1030251881Speter if (err) 1031251881Speter SVN_ERR(svn_error_wrap_apr(err, NULL)); 1032251881Speter 1033251881Speter if (fb->base_checksum) 1034251881Speter /* Text-delta-base-md5: */ 1035251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 1036251881Speter SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 1037251881Speter ": %s\n", 1038251881Speter fb->base_checksum)); 1039251881Speter 1040251881Speter /* Text-content-length: 39 */ 1041251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 1042251881Speter SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH 1043251881Speter ": %lu\n", 1044251881Speter (unsigned long)info->size)); 1045251881Speter 1046251881Speter /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */ 1047251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 1048251881Speter SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5 1049251881Speter ": %s\n", 1050251881Speter text_checksum)); 1051251881Speter } 1052251881Speter 1053251881Speter /* Content-length: 1549 */ 1054251881Speter /* If both text and props are absent, skip this header */ 1055251881Speter if (fb->dump_props) 1056251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 1057251881Speter SVN_REPOS_DUMPFILE_CONTENT_LENGTH 1058251881Speter ": %ld\n\n", 1059251881Speter (unsigned long)info->size + propstring->len)); 1060251881Speter else if (fb->dump_text) 1061251881Speter SVN_ERR(svn_stream_printf(eb->stream, pool, 1062251881Speter SVN_REPOS_DUMPFILE_CONTENT_LENGTH 1063251881Speter ": %ld\n\n", 1064251881Speter (unsigned long)info->size)); 1065251881Speter 1066251881Speter /* Dump the props now */ 1067251881Speter if (fb->dump_props) 1068251881Speter { 1069251881Speter SVN_ERR(svn_stream_write(eb->stream, propstring->data, 1070251881Speter &(propstring->len))); 1071251881Speter 1072251881Speter /* Cleanup */ 1073251881Speter fb->dump_props = FALSE; 1074251881Speter apr_hash_clear(fb->props); 1075251881Speter apr_hash_clear(fb->deleted_props); 1076251881Speter } 1077251881Speter 1078251881Speter /* Dump the text */ 1079251881Speter if (fb->dump_text) 1080251881Speter { 1081251881Speter /* Seek to the beginning of the delta file, map it to a stream, 1082251881Speter and copy the stream to eb->stream. Then close the stream and 1083251881Speter truncate the file so we can reuse it for the next textdelta 1084251881Speter application. Note that the file isn't created, opened or 1085251881Speter closed here */ 1086251881Speter svn_stream_t *delta_filestream; 1087251881Speter apr_off_t offset = 0; 1088251881Speter 1089251881Speter SVN_ERR(svn_io_file_seek(eb->delta_file, APR_SET, &offset, pool)); 1090251881Speter delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool); 1091251881Speter SVN_ERR(svn_stream_copy3(delta_filestream, eb->stream, NULL, NULL, pool)); 1092251881Speter 1093251881Speter /* Cleanup */ 1094251881Speter SVN_ERR(svn_stream_close(delta_filestream)); 1095251881Speter SVN_ERR(svn_io_file_trunc(eb->delta_file, 0, pool)); 1096251881Speter } 1097251881Speter 1098251881Speter /* Write a couple of blank lines for matching output with `svnadmin 1099251881Speter dump` */ 1100251881Speter SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); 1101251881Speter 1102251881Speter return SVN_NO_ERROR; 1103251881Speter} 1104251881Speter 1105251881Speterstatic svn_error_t * 1106251881Speterclose_edit(void *edit_baton, apr_pool_t *pool) 1107251881Speter{ 1108251881Speter return SVN_NO_ERROR; 1109251881Speter} 1110251881Speter 1111251881Speterstatic svn_error_t * 1112251881Speterfetch_base_func(const char **filename, 1113251881Speter void *baton, 1114251881Speter const char *path, 1115251881Speter svn_revnum_t base_revision, 1116251881Speter apr_pool_t *result_pool, 1117251881Speter apr_pool_t *scratch_pool) 1118251881Speter{ 1119251881Speter struct dump_edit_baton *eb = baton; 1120251881Speter svn_stream_t *fstream; 1121251881Speter svn_error_t *err; 1122251881Speter 1123251881Speter if (path[0] == '/') 1124251881Speter path += 1; 1125251881Speter 1126251881Speter if (! SVN_IS_VALID_REVNUM(base_revision)) 1127251881Speter base_revision = eb->current_revision - 1; 1128251881Speter 1129251881Speter SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL, 1130251881Speter svn_io_file_del_on_pool_cleanup, 1131251881Speter result_pool, scratch_pool)); 1132251881Speter 1133251881Speter err = svn_ra_get_file(eb->ra_session, path, base_revision, 1134251881Speter fstream, NULL, NULL, scratch_pool); 1135251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 1136251881Speter { 1137251881Speter svn_error_clear(err); 1138251881Speter SVN_ERR(svn_stream_close(fstream)); 1139251881Speter 1140251881Speter *filename = NULL; 1141251881Speter return SVN_NO_ERROR; 1142251881Speter } 1143251881Speter else if (err) 1144251881Speter return svn_error_trace(err); 1145251881Speter 1146251881Speter SVN_ERR(svn_stream_close(fstream)); 1147251881Speter 1148251881Speter return SVN_NO_ERROR; 1149251881Speter} 1150251881Speter 1151251881Speterstatic svn_error_t * 1152251881Speterfetch_props_func(apr_hash_t **props, 1153251881Speter void *baton, 1154251881Speter const char *path, 1155251881Speter svn_revnum_t base_revision, 1156251881Speter apr_pool_t *result_pool, 1157251881Speter apr_pool_t *scratch_pool) 1158251881Speter{ 1159251881Speter struct dump_edit_baton *eb = baton; 1160251881Speter svn_node_kind_t node_kind; 1161251881Speter 1162251881Speter if (path[0] == '/') 1163251881Speter path += 1; 1164251881Speter 1165251881Speter if (! SVN_IS_VALID_REVNUM(base_revision)) 1166251881Speter base_revision = eb->current_revision - 1; 1167251881Speter 1168251881Speter SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind, 1169251881Speter scratch_pool)); 1170251881Speter 1171251881Speter if (node_kind == svn_node_file) 1172251881Speter { 1173251881Speter SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision, 1174251881Speter NULL, NULL, props, result_pool)); 1175251881Speter } 1176251881Speter else if (node_kind == svn_node_dir) 1177251881Speter { 1178251881Speter apr_array_header_t *tmp_props; 1179251881Speter 1180251881Speter SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path, 1181251881Speter base_revision, 0 /* Dirent fields */, 1182251881Speter result_pool)); 1183251881Speter tmp_props = svn_prop_hash_to_array(*props, result_pool); 1184251881Speter SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props, 1185251881Speter result_pool)); 1186251881Speter *props = svn_prop_array_to_hash(tmp_props, result_pool); 1187251881Speter } 1188251881Speter else 1189251881Speter { 1190251881Speter *props = apr_hash_make(result_pool); 1191251881Speter } 1192251881Speter 1193251881Speter return SVN_NO_ERROR; 1194251881Speter} 1195251881Speter 1196251881Speterstatic svn_error_t * 1197251881Speterfetch_kind_func(svn_node_kind_t *kind, 1198251881Speter void *baton, 1199251881Speter const char *path, 1200251881Speter svn_revnum_t base_revision, 1201251881Speter apr_pool_t *scratch_pool) 1202251881Speter{ 1203251881Speter struct dump_edit_baton *eb = baton; 1204251881Speter 1205251881Speter if (path[0] == '/') 1206251881Speter path += 1; 1207251881Speter 1208251881Speter if (! SVN_IS_VALID_REVNUM(base_revision)) 1209251881Speter base_revision = eb->current_revision - 1; 1210251881Speter 1211251881Speter SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind, 1212251881Speter scratch_pool)); 1213251881Speter 1214251881Speter return SVN_NO_ERROR; 1215251881Speter} 1216251881Speter 1217251881Spetersvn_error_t * 1218251881Spetersvn_rdump__get_dump_editor(const svn_delta_editor_t **editor, 1219251881Speter void **edit_baton, 1220251881Speter svn_revnum_t revision, 1221251881Speter svn_stream_t *stream, 1222251881Speter svn_ra_session_t *ra_session, 1223251881Speter const char *update_anchor_relpath, 1224251881Speter svn_cancel_func_t cancel_func, 1225251881Speter void *cancel_baton, 1226251881Speter apr_pool_t *pool) 1227251881Speter{ 1228251881Speter struct dump_edit_baton *eb; 1229251881Speter svn_delta_editor_t *de; 1230251881Speter svn_delta_shim_callbacks_t *shim_callbacks = 1231251881Speter svn_delta_shim_callbacks_default(pool); 1232251881Speter 1233251881Speter eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton)); 1234251881Speter eb->stream = stream; 1235251881Speter eb->ra_session = ra_session; 1236251881Speter eb->update_anchor_relpath = update_anchor_relpath; 1237251881Speter eb->current_revision = revision; 1238251881Speter eb->pending_kind = svn_node_none; 1239251881Speter 1240251881Speter /* Create a special per-revision pool */ 1241251881Speter eb->pool = svn_pool_create(pool); 1242251881Speter 1243251881Speter /* Open a unique temporary file for all textdelta applications in 1244251881Speter this edit session. The file is automatically closed and cleaned 1245251881Speter up when the edit session is done. */ 1246251881Speter SVN_ERR(svn_io_open_unique_file3(&(eb->delta_file), &(eb->delta_abspath), 1247251881Speter NULL, svn_io_file_del_on_close, pool, pool)); 1248251881Speter 1249251881Speter de = svn_delta_default_editor(pool); 1250251881Speter de->open_root = open_root; 1251251881Speter de->delete_entry = delete_entry; 1252251881Speter de->add_directory = add_directory; 1253251881Speter de->open_directory = open_directory; 1254251881Speter de->close_directory = close_directory; 1255251881Speter de->change_dir_prop = change_dir_prop; 1256251881Speter de->change_file_prop = change_file_prop; 1257251881Speter de->apply_textdelta = apply_textdelta; 1258251881Speter de->add_file = add_file; 1259251881Speter de->open_file = open_file; 1260251881Speter de->close_file = close_file; 1261251881Speter de->close_edit = close_edit; 1262251881Speter 1263251881Speter /* Set the edit_baton and editor. */ 1264251881Speter *edit_baton = eb; 1265251881Speter *editor = de; 1266251881Speter 1267251881Speter /* Wrap this editor in a cancellation editor. */ 1268251881Speter SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, 1269251881Speter de, eb, editor, edit_baton, pool)); 1270251881Speter 1271251881Speter shim_callbacks->fetch_base_func = fetch_base_func; 1272251881Speter shim_callbacks->fetch_props_func = fetch_props_func; 1273251881Speter shim_callbacks->fetch_kind_func = fetch_kind_func; 1274251881Speter shim_callbacks->fetch_baton = eb; 1275251881Speter 1276251881Speter SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 1277251881Speter NULL, NULL, shim_callbacks, pool, pool)); 1278251881Speter 1279251881Speter return SVN_NO_ERROR; 1280251881Speter} 1281