2 * dump_editor.c: The svn_delta_editor_t editor used by svnrdump to
5 * ====================================================================
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
22 * ====================================================================
26 #include "svn_pools.h"
27 #include "svn_repos.h"
29 #include "svn_props.h"
30 #include "svn_subst.h"
31 #include "svn_dirent_uri.h"
33 #include "svn17_compat.h"
34 #include "dump_editor.h"
36 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
39 #define LDR_DBG(x) SVN_DBG(x)
41 #define LDR_DBG(x) while(0)
44 /* The baton used by the dump editor. */
45 struct dump_edit_baton
{
46 /* The output stream we write the dumpfile to */
49 /* Pool for per-revision allocations */
52 /* Properties which were modified during change_file_prop
53 * or change_dir_prop. */
56 /* Properties which were deleted during change_file_prop
57 * or change_dir_prop. */
58 apr_hash_t
*deleted_props
;
60 /* Temporary buffer to write property hashes to in human-readable
61 * form. ### Is this really needed? */
62 svn_stringbuf_t
*propstring
;
64 /* Temporary file used for textdelta application along with its
65 absolute path; these two variables should be allocated in the
66 per-edit-session pool */
67 const char *delta_abspath
;
68 apr_file_t
*delta_file
;
70 /* The checksum of the file the delta is being applied to */
71 const char *base_checksum
;
73 /* Flags to trigger dumping props and text */
74 svn_boolean_t dump_text
;
75 svn_boolean_t dump_props
;
76 svn_boolean_t dump_newlines
;
79 /* Normalize the line ending style of the values of properties in PROPS
80 * that "need translation" (according to svn_prop_needs_translation(),
81 * currently all svn:* props) so that they contain only LF (\n) line endings.
84 normalize_props(apr_hash_t
*props
,
88 const char *key
, *cstring
;
89 const svn_string_t
*value
;
91 for (hi
= apr_hash_first(pool
, props
); hi
; hi
= apr_hash_next(hi
))
93 key
= svn__apr_hash_index_key(hi
);
94 value
= svn__apr_hash_index_val(hi
);
96 if (svn_prop_needs_translation(key
))
98 SVN_ERR(svn_subst_translate_cstring2(value
->data
, &cstring
,
102 value
= svn_string_create(cstring
, pool
);
103 apr_hash_set(props
, key
, APR_HASH_KEY_STRING
, value
);
109 /* Make a directory baton to represent the directory at path (relative
110 * to the edit_baton).
112 * COPYFROM_PATH/COPYFROM_REV are the path/revision against which this
113 * directory should be compared for changes. If the copyfrom
114 * information is valid, the directory will be compared against its
117 * PARENT_DIR_BATON is the directory baton of this directory's parent,
118 * or NULL if this is the top-level directory of the edit. ADDED
119 * indicates if this directory is newly added in this revision.
120 * Perform all allocations in POOL. */
121 static struct dir_baton
*
122 make_dir_baton(const char *path
,
123 const char *copyfrom_path
,
124 svn_revnum_t copyfrom_rev
,
126 void *parent_dir_baton
,
130 struct dump_edit_baton
*eb
= edit_baton
;
131 struct dir_baton
*pb
= parent_dir_baton
;
132 struct dir_baton
*new_db
= apr_pcalloc(pool
, sizeof(*new_db
));
135 /* Construct the full path of this node. */
137 abspath
= svn_uri_join("/", path
, pool
);
141 /* Strip leading slash from copyfrom_path so that the path is
142 canonical and svn_relpath_join can be used */
144 copyfrom_path
= ((*copyfrom_path
== '/') ?
145 copyfrom_path
+ 1 : copyfrom_path
);
148 new_db
->parent_dir_baton
= pb
;
149 new_db
->abspath
= abspath
;
150 new_db
->copyfrom_path
= copyfrom_path
;
151 new_db
->copyfrom_rev
= copyfrom_rev
;
152 new_db
->added
= added
;
153 new_db
->written_out
= FALSE
;
154 new_db
->deleted_entries
= apr_hash_make(pool
);
159 /* Extract and dump properties stored in edit baton EB, using POOL for
160 * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
161 * Unless DUMP_DATA_TOO is set, only property headers are dumped.
164 dump_props(struct dump_edit_baton
*eb
,
165 svn_boolean_t
*trigger_var
,
166 svn_boolean_t dump_data_too
,
169 svn_stream_t
*propstream
;
171 if (trigger_var
&& !*trigger_var
)
174 SVN_ERR(normalize_props(eb
->props
, eb
->pool
));
175 svn_stringbuf_setempty(eb
->propstring
);
176 propstream
= svn_stream_from_stringbuf(eb
->propstring
, eb
->pool
);
177 SVN_ERR(svn_hash_write_incremental(eb
->props
, eb
->deleted_props
,
178 propstream
, "PROPS-END", pool
));
179 SVN_ERR(svn_stream_close(propstream
));
181 /* Prop-delta: true */
182 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
183 SVN_REPOS_DUMPFILE_PROP_DELTA
186 /* Prop-content-length: 193 */
187 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
188 SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
189 ": %" APR_SIZE_T_FMT
"\n", eb
->propstring
->len
));
193 /* Content-length: 14 */
194 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
195 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
196 ": %" APR_SIZE_T_FMT
"\n\n",
197 eb
->propstring
->len
));
199 /* The properties. */
200 SVN_ERR(svn_stream_write(eb
->stream
, eb
->propstring
->data
,
201 &(eb
->propstring
->len
)));
203 /* No text is going to be dumped. Write a couple of newlines and
204 wait for the next node/ revision. */
205 SVN_ERR(svn_stream_printf(eb
->stream
, pool
, "\n\n"));
207 /* Cleanup so that data is never dumped twice. */
208 apr_hash_clear(eb
->props
);
209 apr_hash_clear(eb
->deleted_props
);
211 *trigger_var
= FALSE
;
218 dump_newlines(struct dump_edit_baton
*eb
,
219 svn_boolean_t
*trigger_var
,
222 if (trigger_var
&& *trigger_var
)
224 SVN_ERR(svn_stream_printf(eb
->stream
, pool
, "\n\n"));
225 *trigger_var
= FALSE
;
231 * Write out a node record for PATH of type KIND under EB->FS_ROOT.
232 * ACTION describes what is happening to the node (see enum
233 * svn_node_action). Write record to writable EB->STREAM, using
234 * EB->BUFFER to write in chunks.
236 * If the node was itself copied, IS_COPY is TRUE and the
237 * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV.
238 * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this
239 * node is part of a copied subtree.
242 dump_node(struct dump_edit_baton
*eb
,
243 const char *path
, /* an absolute path. */
244 svn_node_kind_t kind
,
245 enum svn_node_action action
,
246 svn_boolean_t is_copy
,
247 const char *copyfrom_path
,
248 svn_revnum_t copyfrom_rev
,
251 /* Remove leading slashes from path and copyfrom_path */
253 path
= ((*path
== '/') ? path
+ 1 : path
);
256 copyfrom_path
= ((*copyfrom_path
== '/') ?
257 copyfrom_path
+ 1 : copyfrom_path
);
259 /* Node-path: commons/STATUS */
260 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
261 SVN_REPOS_DUMPFILE_NODE_PATH
": %s\n", path
));
263 /* Node-kind: file */
264 if (kind
== svn_node_file
)
265 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
266 SVN_REPOS_DUMPFILE_NODE_KIND
": file\n"));
267 else if (kind
== svn_node_dir
)
268 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
269 SVN_REPOS_DUMPFILE_NODE_KIND
": dir\n"));
272 /* Write the appropriate Node-action header */
275 case svn_node_action_change
:
276 /* We are here after a change_file_prop or change_dir_prop. They
277 set up whatever dump_props they needed to- nothing to
278 do here but print node action information */
279 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
280 SVN_REPOS_DUMPFILE_NODE_ACTION
284 case svn_node_action_replace
:
287 /* Node-action: replace */
288 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
289 SVN_REPOS_DUMPFILE_NODE_ACTION
292 /* Wait for a change_*_prop to be called before dumping
294 eb
->dump_props
= TRUE
;
297 /* More complex case: is_copy is true, and copyfrom_path/
298 copyfrom_rev are present: delete the original, and then re-add
301 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
302 SVN_REPOS_DUMPFILE_NODE_ACTION
305 /* Recurse: Print an additional add-with-history record. */
306 SVN_ERR(dump_node(eb
, path
, kind
, svn_node_action_add
,
307 is_copy
, copyfrom_path
, copyfrom_rev
, pool
));
309 /* We can leave this routine quietly now, don't need to dump any
310 content; that was already done in the second record. */
313 case svn_node_action_delete
:
314 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
315 SVN_REPOS_DUMPFILE_NODE_ACTION
318 /* We can leave this routine quietly now. Nothing more to do-
319 print a couple of newlines because we're not dumping props or
321 SVN_ERR(svn_stream_printf(eb
->stream
, pool
, "\n\n"));
324 case svn_node_action_add
:
325 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
326 SVN_REPOS_DUMPFILE_NODE_ACTION
": add\n"));
330 /* eb->dump_props for files is handled in close_file
331 which is called immediately. However, directories are not
332 closed until all the work inside them has been done;
333 eb->dump_props for directories is handled in all the
334 functions that can possibly be called after add_directory:
335 add_directory, open_directory, delete_entry, close_directory,
336 add_file, open_file. change_dir_prop is a special case. */
338 /* Wait for a change_*_prop to be called before dumping
340 eb
->dump_props
= TRUE
;
344 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
345 SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
347 SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
349 copyfrom_rev
, copyfrom_path
));
351 /* Ugly hack: If a directory was copied from a previous
352 revision, nothing like close_file will be called to write two
353 blank lines. If change_dir_prop is called, props are dumped
354 (along with the necessary PROPS-END\n\n and we're good. So
355 set a dump_newlines here to print the newlines unless
356 change_dir_prop is called next otherwise the `svnadmin load`
358 if (kind
== svn_node_dir
)
359 eb
->dump_newlines
= TRUE
;
367 open_root(void *edit_baton
,
368 svn_revnum_t base_revision
,
372 struct dump_edit_baton
*eb
= edit_baton
;
374 /* Clear the per-revision pool after each revision */
375 svn_pool_clear(eb
->pool
);
377 eb
->props
= apr_hash_make(eb
->pool
);
378 eb
->deleted_props
= apr_hash_make(eb
->pool
);
379 eb
->propstring
= svn_stringbuf_create("", eb
->pool
);
381 *root_baton
= make_dir_baton(NULL
, NULL
, SVN_INVALID_REVNUM
,
382 edit_baton
, NULL
, FALSE
, eb
->pool
);
383 LDR_DBG(("open_root %p\n", *root_baton
));
389 delete_entry(const char *path
,
390 svn_revnum_t revision
,
394 struct dir_baton
*pb
= parent_baton
;
396 LDR_DBG(("delete_entry %s\n", path
));
398 /* Some pending properties to dump? */
399 SVN_ERR(dump_props(pb
->eb
, &(pb
->eb
->dump_props
), TRUE
, pool
));
401 /* Some pending newlines to dump? */
402 SVN_ERR(dump_newlines(pb
->eb
, &(pb
->eb
->dump_newlines
), pool
));
404 /* Add this path to the deleted_entries of the parent directory
406 apr_hash_set(pb
->deleted_entries
, apr_pstrdup(pb
->eb
->pool
, path
),
407 APR_HASH_KEY_STRING
, pb
);
413 add_directory(const char *path
,
415 const char *copyfrom_path
,
416 svn_revnum_t copyfrom_rev
,
420 struct dir_baton
*pb
= parent_baton
;
422 struct dir_baton
*new_db
;
423 svn_boolean_t is_copy
;
425 LDR_DBG(("add_directory %s\n", path
));
427 new_db
= make_dir_baton(path
, copyfrom_path
, copyfrom_rev
, pb
->eb
,
428 pb
, TRUE
, pb
->eb
->pool
);
430 /* Some pending properties to dump? */
431 SVN_ERR(dump_props(pb
->eb
, &(pb
->eb
->dump_props
), TRUE
, pool
));
433 /* Some pending newlines to dump? */
434 SVN_ERR(dump_newlines(pb
->eb
, &(pb
->eb
->dump_newlines
), pool
));
436 /* This might be a replacement -- is the path already deleted? */
437 val
= apr_hash_get(pb
->deleted_entries
, path
, APR_HASH_KEY_STRING
);
439 /* Detect an add-with-history */
440 is_copy
= ARE_VALID_COPY_ARGS(copyfrom_path
, copyfrom_rev
);
443 SVN_ERR(dump_node(pb
->eb
, path
,
445 val
? svn_node_action_replace
: svn_node_action_add
,
447 is_copy
? copyfrom_path
: NULL
,
448 is_copy
? copyfrom_rev
: SVN_INVALID_REVNUM
,
452 /* Delete the path, it's now been dumped */
453 apr_hash_set(pb
->deleted_entries
, path
, APR_HASH_KEY_STRING
, NULL
);
455 new_db
->written_out
= TRUE
;
457 *child_baton
= new_db
;
462 open_directory(const char *path
,
464 svn_revnum_t base_revision
,
468 struct dir_baton
*pb
= parent_baton
;
469 struct dir_baton
*new_db
;
470 const char *copyfrom_path
= NULL
;
471 svn_revnum_t copyfrom_rev
= SVN_INVALID_REVNUM
;
473 LDR_DBG(("open_directory %s\n", path
));
475 /* Some pending properties to dump? */
476 SVN_ERR(dump_props(pb
->eb
, &(pb
->eb
->dump_props
), TRUE
, pool
));
478 /* Some pending newlines to dump? */
479 SVN_ERR(dump_newlines(pb
->eb
, &(pb
->eb
->dump_newlines
), pool
));
481 /* If the parent directory has explicit comparison path and rev,
482 record the same for this one. */
483 if (pb
&& ARE_VALID_COPY_ARGS(pb
->copyfrom_path
, pb
->copyfrom_rev
))
485 copyfrom_path
= svn_uri_join(pb
->copyfrom_path
,
486 svn_relpath_basename(path
, NULL
),
488 copyfrom_rev
= pb
->copyfrom_rev
;
491 new_db
= make_dir_baton(path
, copyfrom_path
, copyfrom_rev
, pb
->eb
, pb
,
492 FALSE
, pb
->eb
->pool
);
493 *child_baton
= new_db
;
498 close_directory(void *dir_baton
,
501 struct dir_baton
*db
= dir_baton
;
502 struct dump_edit_baton
*eb
= db
->eb
;
503 apr_hash_index_t
*hi
;
505 LDR_DBG(("close_directory %p\n", dir_baton
));
507 /* Some pending properties to dump? */
508 SVN_ERR(dump_props(eb
, &(eb
->dump_props
), TRUE
, pool
));
510 /* Some pending newlines to dump? */
511 SVN_ERR(dump_newlines(eb
, &(eb
->dump_newlines
), pool
));
513 /* Dump the deleted directory entries */
514 for (hi
= apr_hash_first(pool
, db
->deleted_entries
); hi
;
515 hi
= apr_hash_next(hi
))
519 apr_hash_this(hi
, &key
, NULL
, NULL
);
522 SVN_ERR(dump_node(db
->eb
, path
, svn_node_unknown
, svn_node_action_delete
,
523 FALSE
, NULL
, SVN_INVALID_REVNUM
, pool
));
526 apr_hash_clear(db
->deleted_entries
);
531 add_file(const char *path
,
533 const char *copyfrom_path
,
534 svn_revnum_t copyfrom_rev
,
538 struct dir_baton
*pb
= parent_baton
;
540 svn_boolean_t is_copy
;
542 LDR_DBG(("add_file %s\n", path
));
544 /* Some pending properties to dump? */
545 SVN_ERR(dump_props(pb
->eb
, &(pb
->eb
->dump_props
), TRUE
, pool
));
547 /* Some pending newlines to dump? */
548 SVN_ERR(dump_newlines(pb
->eb
, &(pb
->eb
->dump_newlines
), pool
));
550 /* This might be a replacement -- is the path already deleted? */
551 val
= apr_hash_get(pb
->deleted_entries
, path
, APR_HASH_KEY_STRING
);
553 /* Detect add-with-history. */
554 is_copy
= ARE_VALID_COPY_ARGS(copyfrom_path
, copyfrom_rev
);
557 SVN_ERR(dump_node(pb
->eb
, path
,
559 val
? svn_node_action_replace
: svn_node_action_add
,
561 is_copy
? copyfrom_path
: NULL
,
562 is_copy
? copyfrom_rev
: SVN_INVALID_REVNUM
,
566 /* delete the path, it's now been dumped. */
567 apr_hash_set(pb
->deleted_entries
, path
, APR_HASH_KEY_STRING
, NULL
);
569 /* Build a nice file baton to pass to change_file_prop and
571 *file_baton
= pb
->eb
;
577 open_file(const char *path
,
579 svn_revnum_t ancestor_revision
,
583 struct dir_baton
*pb
= parent_baton
;
584 const char *copyfrom_path
= NULL
;
585 svn_revnum_t copyfrom_rev
= SVN_INVALID_REVNUM
;
587 LDR_DBG(("open_file %s\n", path
));
589 /* Some pending properties to dump? */
590 SVN_ERR(dump_props(pb
->eb
, &(pb
->eb
->dump_props
), TRUE
, pool
));
592 /* Some pending newlines to dump? */
593 SVN_ERR(dump_newlines(pb
->eb
, &(pb
->eb
->dump_newlines
), pool
));
595 /* If the parent directory has explicit copyfrom path and rev,
596 record the same for this one. */
597 if (pb
&& ARE_VALID_COPY_ARGS(pb
->copyfrom_path
, pb
->copyfrom_rev
))
599 copyfrom_path
= svn_relpath_join(pb
->copyfrom_path
,
600 svn_relpath_basename(path
, NULL
),
602 copyfrom_rev
= pb
->copyfrom_rev
;
605 SVN_ERR(dump_node(pb
->eb
, path
, svn_node_file
, svn_node_action_change
,
606 FALSE
, copyfrom_path
, copyfrom_rev
, pool
));
608 /* Build a nice file baton to pass to change_file_prop and
610 *file_baton
= pb
->eb
;
616 change_dir_prop(void *parent_baton
,
618 const svn_string_t
*value
,
621 struct dir_baton
*db
= parent_baton
;
623 LDR_DBG(("change_dir_prop %p\n", parent_baton
));
625 if (svn_property_kind(NULL
, name
) != svn_prop_regular_kind
)
629 apr_hash_set(db
->eb
->props
, apr_pstrdup(db
->eb
->pool
, name
),
630 APR_HASH_KEY_STRING
, svn_string_dup(value
, db
->eb
->pool
));
632 apr_hash_set(db
->eb
->deleted_props
, apr_pstrdup(db
->eb
->pool
, name
),
633 APR_HASH_KEY_STRING
, "");
635 if (! db
->written_out
)
637 /* If db->written_out is set, it means that the node information
638 corresponding to this directory has already been written: don't
639 do anything; dump_props will take care of dumping the
640 props. If it not, dump the node itself before dumping the
643 SVN_ERR(dump_node(db
->eb
, db
->abspath
, svn_node_dir
,
644 svn_node_action_change
, FALSE
, db
->copyfrom_path
,
645 db
->copyfrom_rev
, pool
));
646 db
->written_out
= TRUE
;
649 /* Dump props whether or not the directory has been written
650 out. Then disable printing a couple of extra newlines */
651 SVN_ERR(dump_props(db
->eb
, NULL
, TRUE
, pool
));
652 db
->eb
->dump_newlines
= FALSE
;
658 change_file_prop(void *file_baton
,
660 const svn_string_t
*value
,
663 struct dump_edit_baton
*eb
= file_baton
;
665 LDR_DBG(("change_file_prop %p\n", file_baton
));
667 if (svn_property_kind(NULL
, name
) != svn_prop_regular_kind
)
671 apr_hash_set(eb
->props
, apr_pstrdup(eb
->pool
, name
),
672 APR_HASH_KEY_STRING
, svn_string_dup(value
, eb
->pool
));
674 apr_hash_set(eb
->deleted_props
, apr_pstrdup(eb
->pool
, name
),
675 APR_HASH_KEY_STRING
, "");
677 /* Dump the property headers and wait; close_file might need
678 to write text headers too depending on whether
679 apply_textdelta is called */
680 eb
->dump_props
= TRUE
;
686 window_handler(svn_txdelta_window_t
*window
, void *baton
)
688 struct handler_baton
*hb
= baton
;
689 static svn_error_t
*err
;
691 err
= hb
->apply_handler(window
, hb
->apply_baton
);
692 if (window
!= NULL
&& !err
)
702 apply_textdelta(void *file_baton
, const char *base_checksum
,
704 svn_txdelta_window_handler_t
*handler
,
705 void **handler_baton
)
707 struct dump_edit_baton
*eb
= file_baton
;
709 /* Custom handler_baton allocated in a separate pool */
710 struct handler_baton
*hb
;
711 svn_stream_t
*delta_filestream
;
713 hb
= apr_pcalloc(eb
->pool
, sizeof(*hb
));
715 LDR_DBG(("apply_textdelta %p\n", file_baton
));
717 /* Use a temporary file to measure the text-content-length */
718 delta_filestream
= svn_stream_from_aprfile2(eb
->delta_file
, TRUE
, pool
);
720 /* Prepare to write the delta to the delta_filestream */
721 svn_txdelta_to_svndiff2(&(hb
->apply_handler
), &(hb
->apply_baton
),
722 delta_filestream
, 0, pool
);
724 eb
->dump_text
= TRUE
;
725 eb
->base_checksum
= apr_pstrdup(eb
->pool
, base_checksum
);
726 svn_stream_close(delta_filestream
);
728 /* The actual writing takes place when this function has
729 finished. Set handler and handler_baton now so for
731 *handler
= window_handler
;
738 close_file(void *file_baton
,
739 const char *text_checksum
,
742 struct dump_edit_baton
*eb
= file_baton
;
743 svn_stream_t
*delta_filestream
;
744 apr_finfo_t
*info
= apr_pcalloc(pool
, sizeof(apr_finfo_t
));
748 LDR_DBG(("close_file %p\n", file_baton
));
750 /* Some pending properties to dump? Dump just the headers- dump the
751 props only after dumping the text headers too (if present) */
752 SVN_ERR(dump_props(eb
, &(eb
->dump_props
), FALSE
, pool
));
754 /* Dump the text headers */
757 /* Text-delta: true */
758 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
759 SVN_REPOS_DUMPFILE_TEXT_DELTA
762 err
= apr_file_info_get(info
, APR_FINFO_SIZE
, eb
->delta_file
);
764 SVN_ERR(svn_error_wrap_apr(err
, NULL
));
766 if (eb
->base_checksum
)
767 /* Text-delta-base-md5: */
768 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
769 SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
773 /* Text-content-length: 39 */
774 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
775 SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
777 (unsigned long)info
->size
));
779 /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */
780 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
781 SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
786 /* Content-length: 1549 */
787 /* If both text and props are absent, skip this header */
789 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
790 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
792 (unsigned long)info
->size
+ eb
->propstring
->len
));
793 else if (eb
->dump_text
)
794 SVN_ERR(svn_stream_printf(eb
->stream
, pool
,
795 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
797 (unsigned long)info
->size
));
799 /* Dump the props now */
802 SVN_ERR(svn_stream_write(eb
->stream
, eb
->propstring
->data
,
803 &(eb
->propstring
->len
)));
806 eb
->dump_props
= FALSE
;
807 apr_hash_clear(eb
->props
);
808 apr_hash_clear(eb
->deleted_props
);
814 /* Seek to the beginning of the delta file, map it to a stream,
815 and copy the stream to eb->stream. Then close the stream and
816 truncate the file so we can reuse it for the next textdelta
817 application. Note that the file isn't created, opened or
820 SVN_ERR(svn_io_file_seek(eb
->delta_file
, APR_SET
, &offset
, pool
));
821 delta_filestream
= svn_stream_from_aprfile2(eb
->delta_file
, TRUE
, pool
);
822 SVN_ERR(svn_stream_copy3(delta_filestream
, eb
->stream
, NULL
, NULL
, pool
));
825 SVN_ERR(svn_stream_close(delta_filestream
));
826 SVN_ERR(svn_io_file_trunc(eb
->delta_file
, 0, pool
));
827 eb
->dump_text
= FALSE
;
830 /* Write a couple of blank lines for matching output with `svnadmin
832 SVN_ERR(svn_stream_printf(eb
->stream
, pool
, "\n\n"));
838 close_edit(void *edit_baton
, apr_pool_t
*pool
)
844 get_dump_editor(const svn_delta_editor_t
**editor
,
846 svn_stream_t
*stream
,
847 svn_cancel_func_t cancel_func
,
851 struct dump_edit_baton
*eb
;
852 svn_delta_editor_t
*de
;
854 eb
= apr_pcalloc(pool
, sizeof(struct dump_edit_baton
));
857 /* Create a special per-revision pool */
858 eb
->pool
= svn_pool_create(pool
);
860 /* Open a unique temporary file for all textdelta applications in
861 this edit session. The file is automatically closed and cleaned
862 up when the edit session is done. */
863 SVN_ERR(svn_io_open_unique_file3(&(eb
->delta_file
), &(eb
->delta_abspath
),
864 NULL
, svn_io_file_del_on_close
, pool
, pool
));
866 de
= svn_delta_default_editor(pool
);
867 de
->open_root
= open_root
;
868 de
->delete_entry
= delete_entry
;
869 de
->add_directory
= add_directory
;
870 de
->open_directory
= open_directory
;
871 de
->close_directory
= close_directory
;
872 de
->change_dir_prop
= change_dir_prop
;
873 de
->change_file_prop
= change_file_prop
;
874 de
->apply_textdelta
= apply_textdelta
;
875 de
->add_file
= add_file
;
876 de
->open_file
= open_file
;
877 de
->close_file
= close_file
;
878 de
->close_edit
= close_edit
;
880 /* Set the edit_baton and editor. */
884 /* Wrap this editor in a cancellation editor. */
885 return svn_delta_get_cancellation_editor(cancel_func
, cancel_baton
,
886 de
, eb
, editor
, edit_baton
, pool
);