Add test suite
[svnrdump.git] / dump_editor.c
blob72ad04fb41d0345eb2ffa6c7e2f354dc2ed767f9
1 /*
2 * dump_editor.c: The svn_delta_editor_t editor used by svnrdump to
3 * dump revisions.
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
21 * under the License.
22 * ====================================================================
25 #include "svn_hash.h"
26 #include "svn_pools.h"
27 #include "svn_repos.h"
28 #include "svn_path.h"
29 #include "svn_props.h"
30 #include "svn_dirent_uri.h"
32 #include "svn17_compat.h"
33 #include "dump_editor.h"
35 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
37 /* The baton used by the dump editor. */
38 struct dump_edit_baton {
39 /* The output stream we write the dumpfile to */
40 svn_stream_t *stream;
42 /* Pool for per-edit-session allocations */
43 apr_pool_t *pool;
45 /* Properties which were modified during change_file_prop
46 * or change_dir_prop. */
47 apr_hash_t *props;
49 /* Properties which were deleted during change_file_prop
50 * or change_dir_prop. */
51 apr_hash_t *deleted_props;
53 /* Temporary buffer to write property hashes to in human-readable
54 * form. ### Is this really needed? */
55 svn_stringbuf_t *propstring;
57 /* Temporary file to write delta to along with its checksum. */
58 char *delta_abspath;
60 /* The checksum of the file the delta is being applied to */
61 const char *base_checksum;
63 /* Flags to trigger dumping props and text */
64 svn_boolean_t dump_props;
65 svn_boolean_t dump_text;
66 svn_boolean_t dump_props_pending;
69 /* Make a directory baton to represent the directory at path (relative
70 * to the edit_baton).
72 * COPYFROM_PATH/COPYFROM_REV are the path/revision against which this
73 * directory should be compared for changes. If the copyfrom
74 * information is valid, the directory will be compared against its
75 * copy source.
77 * PARENT_DIR_BATON is the directory baton of this directory's parent,
78 * or NULL if this is the top-level directory of the edit. ADDED
79 * indicates if this directory is newly added in this revision.
80 * Perform all allocations in POOL. */
81 static struct dir_baton *
82 make_dir_baton(const char *path,
83 const char *copyfrom_path,
84 svn_revnum_t copyfrom_rev,
85 void *edit_baton,
86 void *parent_dir_baton,
87 svn_boolean_t added,
88 apr_pool_t *pool)
90 struct dump_edit_baton *eb = edit_baton;
91 struct dir_baton *pb = parent_dir_baton;
92 struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
93 const char *abspath;
95 /* Disallow a path relative to nothing. */
96 SVN_ERR_ASSERT_NO_RETURN(!path || pb);
98 /* Construct the full path of this node. */
99 if (pb)
100 abspath = svn_uri_join("/", path, pool);
101 else
102 abspath = "/";
104 /* Remove leading slashes from copyfrom paths. */
105 if (copyfrom_path && strcmp(copyfrom_path, "/"))
106 copyfrom_path = ((*copyfrom_path == '/') ?
107 copyfrom_path + 1 : copyfrom_path);
109 new_db->eb = eb;
110 new_db->parent_dir_baton = pb;
111 new_db->abspath = abspath;
112 new_db->copyfrom_path = copyfrom_path ?
113 apr_pstrdup(pool, copyfrom_path) : NULL;
114 new_db->copyfrom_rev = copyfrom_rev;
115 new_db->added = added;
116 new_db->written_out = FALSE;
117 new_db->deleted_entries = apr_hash_make(pool);
119 return new_db;
122 /* Extract and dump properties stored in edit baton EB, using POOL for
123 * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
124 * Unless DUMP_DATA_TOO is set, only property headers are dumped.
126 static svn_error_t *
127 dump_props(struct dump_edit_baton *eb,
128 svn_boolean_t *trigger_var,
129 svn_boolean_t dump_data_too,
130 apr_pool_t *pool)
132 svn_stream_t *propstream;
134 if (trigger_var && !*trigger_var)
135 return SVN_NO_ERROR;
137 svn_stringbuf_setempty(eb->propstring);
138 propstream = svn_stream_from_stringbuf(eb->propstring, eb->pool);
139 SVN_ERR(svn_hash_write_incremental(eb->props, eb->deleted_props,
140 propstream, "PROPS-END", pool));
141 SVN_ERR(svn_stream_close(propstream));
143 /* Prop-delta: true */
144 SVN_ERR(svn_stream_printf(eb->stream, pool,
145 SVN_REPOS_DUMPFILE_PROP_DELTA
146 ": true\n"));
148 /* Prop-content-length: 193 */
149 SVN_ERR(svn_stream_printf(eb->stream, pool,
150 SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
151 ": %" APR_SIZE_T_FMT "\n", eb->propstring->len));
153 if (dump_data_too)
155 /* Content-length: 14 */
156 SVN_ERR(svn_stream_printf(eb->stream, pool,
157 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
158 ": %" APR_SIZE_T_FMT "\n\n",
159 eb->propstring->len));
161 /* The properties. */
162 SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
163 &(eb->propstring->len)));
165 /* No text is going to be dumped. Write a couple of newlines and
166 wait for the next node/ revision. */
167 SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
169 /* Cleanup so that data is never dumped twice. */
170 apr_hash_clear(eb->props);
171 apr_hash_clear(eb->deleted_props);
172 if (trigger_var)
173 *trigger_var = FALSE;
176 return SVN_NO_ERROR;
180 * Write out a node record for PATH of type KIND under EB->FS_ROOT.
181 * ACTION describes what is happening to the node (see enum
182 * svn_node_action). Write record to writable EB->STREAM, using
183 * EB->BUFFER to write in chunks.
185 * If the node was itself copied, IS_COPY is TRUE and the
186 * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV.
187 * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this
188 * node is part of a copied subtree.
190 static svn_error_t *
191 dump_node(struct dump_edit_baton *eb,
192 const char *path, /* an absolute path. */
193 svn_node_kind_t kind,
194 enum svn_node_action action,
195 svn_boolean_t is_copy,
196 const char *copyfrom_path,
197 svn_revnum_t copyfrom_rev,
198 apr_pool_t *pool)
200 /* Remove leading slashes from path and copyfrom_path */
201 if (path && strcmp(path, "/"))
202 path = ((*path == '/') ? path + 1 : path);
204 if (copyfrom_path && strcmp(copyfrom_path, "/"))
205 copyfrom_path = ((*copyfrom_path == '/') ?
206 copyfrom_path + 1 : copyfrom_path);
208 /* Node-path: commons/STATUS */
209 SVN_ERR(svn_stream_printf(eb->stream, pool,
210 SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
212 /* Node-kind: file */
213 if (kind == svn_node_file)
214 SVN_ERR(svn_stream_printf(eb->stream, pool,
215 SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
216 else if (kind == svn_node_dir)
217 SVN_ERR(svn_stream_printf(eb->stream, pool,
218 SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
221 /* Write the appropriate Node-action header */
222 switch (action)
224 case svn_node_action_change:
225 SVN_ERR(svn_stream_printf(eb->stream, pool,
226 SVN_REPOS_DUMPFILE_NODE_ACTION
227 ": change\n"));
228 break;
230 case svn_node_action_replace:
231 if (!is_copy)
233 /* Node-action: replace */
234 SVN_ERR(svn_stream_printf(eb->stream, pool,
235 SVN_REPOS_DUMPFILE_NODE_ACTION
236 ": replace\n"));
238 eb->dump_props_pending = TRUE;
239 break;
241 /* More complex case: is_copy is true, and copyfrom_path/
242 copyfrom_rev are present: delete the original, and then re-add
243 it */
245 SVN_ERR(svn_stream_printf(eb->stream, pool,
246 SVN_REPOS_DUMPFILE_NODE_ACTION
247 ": delete\n\n"));
249 /* Recurse: Print an additional add-with-history record. */
250 SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
251 is_copy, copyfrom_path, copyfrom_rev, pool));
253 /* We can leave this routine quietly now, don't need to dump any
254 content; that was already done in the second record. */
255 eb->dump_props = FALSE;
256 break;
258 case svn_node_action_delete:
259 SVN_ERR(svn_stream_printf(eb->stream, pool,
260 SVN_REPOS_DUMPFILE_NODE_ACTION
261 ": delete\n"));
263 /* We can leave this routine quietly now, don't need to dump
264 any content. */
265 SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
266 eb->dump_props = FALSE;
267 break;
269 case svn_node_action_add:
270 SVN_ERR(svn_stream_printf(eb->stream, pool,
271 SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
273 if (!is_copy)
275 /* eb->dump_props_pending for files is handled in close_file
276 which is called immediately. However, directories are not
277 closed until all the work inside them has been done;
278 eb->dump_props_pending for directories is handled in all the
279 functions that can possibly be called after add_directory:
280 add_directory, open_directory, delete_entry, close_directory,
281 add_file, open_file. change_dir_prop is a special case. */
283 eb->dump_props_pending = TRUE;
284 break;
287 SVN_ERR(svn_stream_printf(eb->stream, pool,
288 SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
289 ": %ld\n"
290 SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
291 ": %s\n",
292 copyfrom_rev, copyfrom_path));
294 /* Ugly hack: If a directory was copied from a previous revision,
295 nothing else can be done, and close_file won't be called to
296 write two blank lines. Write them here otherwise the `svnadmin
297 load` parser will fail. */
298 if (kind == svn_node_dir)
299 SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
301 break;
304 /* Dump property headers */
305 SVN_ERR(dump_props(eb, &(eb->dump_props), FALSE, pool));
307 return SVN_NO_ERROR;
310 static svn_error_t *
311 open_root(void *edit_baton,
312 svn_revnum_t base_revision,
313 apr_pool_t *pool,
314 void **root_baton)
316 struct dump_edit_baton *eb = edit_baton;
317 /* Allocate a special pool for the edit_baton to avoid pool
318 lifetime issues */
319 eb->pool = svn_pool_create(pool);
320 eb->props = apr_hash_make(eb->pool);
321 eb->deleted_props = apr_hash_make(eb->pool);
322 eb->propstring = svn_stringbuf_create("", eb->pool);
324 *root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
325 edit_baton, NULL, FALSE, pool);
326 return SVN_NO_ERROR;
329 static svn_error_t *
330 delete_entry(const char *path,
331 svn_revnum_t revision,
332 void *parent_baton,
333 apr_pool_t *pool)
335 struct dir_baton *pb = parent_baton;
336 const char *mypath = apr_pstrdup(pool, path);
338 /* Some pending properties to dump? */
339 SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
341 /* Add this path to the deleted_entries of the parent directory
342 baton. */
343 apr_hash_set(pb->deleted_entries, mypath, APR_HASH_KEY_STRING, pb);
345 return SVN_NO_ERROR;
348 static svn_error_t *
349 add_directory(const char *path,
350 void *parent_baton,
351 const char *copyfrom_path,
352 svn_revnum_t copyfrom_rev,
353 apr_pool_t *pool,
354 void **child_baton)
356 struct dir_baton *pb = parent_baton;
357 void *val;
358 struct dir_baton *new_db
359 = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, TRUE, pool);
360 svn_boolean_t is_copy;
362 /* Some pending properties to dump? */
363 SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
365 /* This might be a replacement -- is the path already deleted? */
366 val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
368 /* Detect an add-with-history */
369 is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
371 /* Dump the node */
372 SVN_ERR(dump_node(pb->eb, path,
373 svn_node_dir,
374 val ? svn_node_action_replace : svn_node_action_add,
375 is_copy,
376 is_copy ? copyfrom_path : NULL,
377 is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
378 pool));
380 if (val)
381 /* Delete the path, it's now been dumped */
382 apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
384 new_db->written_out = TRUE;
386 *child_baton = new_db;
387 return SVN_NO_ERROR;
390 static svn_error_t *
391 open_directory(const char *path,
392 void *parent_baton,
393 svn_revnum_t base_revision,
394 apr_pool_t *pool,
395 void **child_baton)
397 struct dir_baton *pb = parent_baton;
398 struct dir_baton *new_db;
399 const char *copyfrom_path = NULL;
400 svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
402 /* Some pending properties to dump? */
403 SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
405 /* If the parent directory has explicit comparison path and rev,
406 record the same for this one. */
407 if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
409 copyfrom_path = svn_uri_join(pb->copyfrom_path,
410 svn_relpath_basename(path, pool),
411 pool);
412 copyfrom_rev = pb->copyfrom_rev;
415 new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
416 FALSE, pool);
417 *child_baton = new_db;
418 return SVN_NO_ERROR;
421 static svn_error_t *
422 close_directory(void *dir_baton,
423 apr_pool_t *pool)
425 struct dir_baton *db = dir_baton;
426 struct dump_edit_baton *eb = db->eb;
427 apr_hash_index_t *hi;
428 apr_pool_t *iterpool = svn_pool_create(pool);
430 /* Some pending properties to dump? */
431 SVN_ERR(dump_props(eb, &(eb->dump_props_pending), TRUE, pool));
433 /* Dump the directory entries */
434 for (hi = apr_hash_first(pool, db->deleted_entries); hi;
435 hi = apr_hash_next(hi))
437 const void *key;
438 const char *path;
439 apr_hash_this(hi, &key, NULL, NULL);
440 path = key;
442 svn_pool_clear(iterpool);
444 SVN_ERR(dump_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
445 FALSE, NULL, SVN_INVALID_REVNUM, iterpool));
448 svn_pool_destroy(iterpool);
449 return SVN_NO_ERROR;
452 static svn_error_t *
453 add_file(const char *path,
454 void *parent_baton,
455 const char *copyfrom_path,
456 svn_revnum_t copyfrom_rev,
457 apr_pool_t *pool,
458 void **file_baton)
460 struct dir_baton *pb = parent_baton;
461 void *val;
462 svn_boolean_t is_copy;
464 /* Some pending properties to dump? */
465 SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
467 /* This might be a replacement -- is the path already deleted? */
468 val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
470 /* Detect add-with-history. */
471 is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
473 /* Dump the node. */
474 SVN_ERR(dump_node(pb->eb, path,
475 svn_node_file,
476 val ? svn_node_action_replace : svn_node_action_add,
477 is_copy,
478 is_copy ? copyfrom_path : NULL,
479 is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
480 pool));
482 if (val)
483 /* delete the path, it's now been dumped. */
484 apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
486 /* Build a nice file baton to pass to change_file_prop and
487 apply_textdelta */
488 *file_baton = pb->eb;
490 return SVN_NO_ERROR;
493 static svn_error_t *
494 open_file(const char *path,
495 void *parent_baton,
496 svn_revnum_t ancestor_revision,
497 apr_pool_t *pool,
498 void **file_baton)
500 struct dir_baton *pb = parent_baton;
501 const char *copyfrom_path = NULL;
502 svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
503 apr_array_header_t *compose_path;
505 /* Some pending properties to dump? */
506 SVN_ERR(dump_props(pb->eb, &(pb->eb->dump_props_pending), TRUE, pool));
508 compose_path = apr_array_make(pool, 2, sizeof(const char *));
510 /* If the parent directory has explicit copyfrom path and rev,
511 record the same for this one. */
512 if (pb && ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
514 APR_ARRAY_PUSH(compose_path, const char *) = pb->copyfrom_path;
515 APR_ARRAY_PUSH(compose_path, const char *) =
516 svn_relpath_basename(path, pool);
517 copyfrom_path = svn_path_compose(compose_path, pool);
518 copyfrom_rev = pb->copyfrom_rev;
521 SVN_ERR(dump_node(pb->eb, path, svn_node_file, svn_node_action_change,
522 FALSE, copyfrom_path, copyfrom_rev, pool));
524 /* Build a nice file baton to pass to change_file_prop and
525 apply_textdelta */
526 *file_baton = pb->eb;
528 return SVN_NO_ERROR;
531 static svn_error_t *
532 change_dir_prop(void *parent_baton,
533 const char *name,
534 const svn_string_t *value,
535 apr_pool_t *pool)
537 struct dir_baton *db = parent_baton;
539 if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
540 return SVN_NO_ERROR;
542 if (value)
543 apr_hash_set(db->eb->props, apr_pstrdup(pool, name),
544 APR_HASH_KEY_STRING, svn_string_dup(value, pool));
545 else
546 apr_hash_set(db->eb->deleted_props, apr_pstrdup(pool, name),
547 APR_HASH_KEY_STRING, "");
549 if (! db->written_out)
551 /* If db->written_out is set, it means that the node information
552 corresponding to this directory has already been written: don't
553 do anything; dump_props_pending will take care of dumping the
554 props. If it not, dump the node itself before dumping the
555 props. */
557 SVN_ERR(dump_node(db->eb, db->abspath, svn_node_dir,
558 svn_node_action_change, FALSE, db->copyfrom_path,
559 db->copyfrom_rev, pool));
561 SVN_ERR(dump_props(db->eb, NULL, TRUE, pool));
562 db->written_out = TRUE;
564 return SVN_NO_ERROR;
567 static svn_error_t *
568 change_file_prop(void *file_baton,
569 const char *name,
570 const svn_string_t *value,
571 apr_pool_t *pool)
573 struct dump_edit_baton *eb = file_baton;
575 if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
576 return SVN_NO_ERROR;
578 if (value)
579 apr_hash_set(eb->props, apr_pstrdup(pool, name),
580 APR_HASH_KEY_STRING, svn_string_dup(value, pool));
581 else
582 apr_hash_set(eb->deleted_props, apr_pstrdup(pool, name),
583 APR_HASH_KEY_STRING, "");
585 /* Dump the property headers and wait; close_file might need
586 to write text headers too depending on whether
587 apply_textdelta is called */
588 eb->dump_props_pending = TRUE;
590 return SVN_NO_ERROR;
593 static svn_error_t *
594 window_handler(svn_txdelta_window_t *window, void *baton)
596 struct handler_baton *hb = baton;
597 struct dump_edit_baton *eb = hb->eb;
598 static svn_error_t *err;
600 err = hb->apply_handler(window, hb->apply_baton);
601 if (window != NULL && !err)
602 return SVN_NO_ERROR;
604 if (err)
605 SVN_ERR(err);
607 /* Write information about the filepath to hb->eb */
608 eb->delta_abspath = apr_pstrdup(eb->pool, hb->delta_abspath);
610 /* Cleanup */
611 svn_pool_destroy(hb->pool);
612 return SVN_NO_ERROR;
615 static svn_error_t *
616 apply_textdelta(void *file_baton, const char *base_checksum,
617 apr_pool_t *pool,
618 svn_txdelta_window_handler_t *handler,
619 void **handler_baton)
621 struct dump_edit_baton *eb = file_baton;
623 /* Custom handler_baton allocated in a separate pool */
624 apr_pool_t *handler_pool = svn_pool_create(pool);
625 struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
626 hb->pool = handler_pool;
627 hb->eb = eb;
629 /* Use a temporary file to measure the text-content-length */
630 SVN_ERR(svn_stream_open_unique(&(hb->delta_filestream), &hb->delta_abspath,
631 NULL, svn_io_file_del_none, hb->pool,
632 hb->pool));
634 /* Prepare to write the delta to the temporary file. */
635 svn_txdelta_to_svndiff2(&(hb->apply_handler), &(hb->apply_baton),
636 hb->delta_filestream, 0, hb->pool);
637 eb->dump_text = TRUE;
638 eb->base_checksum = apr_pstrdup(pool, base_checksum);
640 /* The actual writing takes place when this function has
641 finished. Set handler and handler_baton now so for
642 window_handler() */
643 *handler = window_handler;
644 *handler_baton = hb;
646 return SVN_NO_ERROR;
649 static svn_error_t *
650 close_file(void *file_baton,
651 const char *text_checksum,
652 apr_pool_t *pool)
654 struct dump_edit_baton *eb = file_baton;
655 apr_file_t *delta_file;
656 svn_stream_t *delta_filestream;
657 apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
659 /* Some pending properties to dump? */
660 SVN_ERR(dump_props(eb, &(eb->dump_props_pending), FALSE, pool));
662 /* The prop headers have already been dumped in dump_node; now dump
663 the text headers. */
664 if (eb->dump_text)
666 /* Text-delta: true */
667 SVN_ERR(svn_stream_printf(eb->stream, pool,
668 SVN_REPOS_DUMPFILE_TEXT_DELTA
669 ": true\n"));
671 SVN_ERR(svn_io_stat(info, eb->delta_abspath, APR_FINFO_SIZE, pool));
673 if (eb->base_checksum)
674 /* Text-delta-base-md5: */
675 SVN_ERR(svn_stream_printf(eb->stream, pool,
676 SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
677 ": %s\n",
678 eb->base_checksum));
680 /* Text-content-length: 39 */
681 SVN_ERR(svn_stream_printf(eb->stream, pool,
682 SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
683 ": %lu\n",
684 (unsigned long)info->size));
686 /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */
687 SVN_ERR(svn_stream_printf(eb->stream, pool,
688 SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
689 ": %s\n",
690 text_checksum));
693 /* Content-length: 1549 */
694 /* If both text and props are absent, skip this header */
695 if (eb->dump_props || eb->dump_props_pending)
696 SVN_ERR(svn_stream_printf(eb->stream, pool,
697 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
698 ": %ld\n\n",
699 (unsigned long)info->size + eb->propstring->len));
700 else if (eb->dump_text)
701 SVN_ERR(svn_stream_printf(eb->stream, pool,
702 SVN_REPOS_DUMPFILE_CONTENT_LENGTH
703 ": %ld\n\n",
704 (unsigned long)info->size));
706 /* Dump the props; the propstring should have already been
707 written in dump_node or above */
708 if (eb->dump_props || eb->dump_props_pending)
710 SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
711 &(eb->propstring->len)));
713 /* Cleanup */
714 eb->dump_props = eb->dump_props_pending = FALSE;
715 apr_hash_clear(eb->props);
716 apr_hash_clear(eb->deleted_props);
719 /* Dump the text */
720 if (eb->dump_text)
722 /* Open the temporary file, map it to a stream, copy
723 the stream to eb->stream, close and delete the
724 file */
725 SVN_ERR(svn_io_file_open(&delta_file, eb->delta_abspath, APR_READ,
726 APR_OS_DEFAULT, pool));
727 delta_filestream = svn_stream_from_aprfile2(delta_file, TRUE, pool);
728 SVN_ERR(svn_stream_copy3(delta_filestream, eb->stream, NULL, NULL, pool));
730 /* Cleanup */
731 SVN_ERR(svn_io_file_close(delta_file, pool));
732 SVN_ERR(svn_stream_close(delta_filestream));
733 SVN_ERR(svn_io_remove_file2(eb->delta_abspath, TRUE, pool));
734 eb->dump_text = FALSE;
737 SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
739 return SVN_NO_ERROR;
742 static svn_error_t *
743 close_edit(void *edit_baton, apr_pool_t *pool)
745 struct dump_edit_baton *eb = edit_baton;
746 svn_pool_destroy(eb->pool);
748 return SVN_NO_ERROR;
751 svn_error_t *
752 get_dump_editor(const svn_delta_editor_t **editor,
753 void **edit_baton,
754 svn_stream_t *stream,
755 apr_pool_t *pool)
757 struct dump_edit_baton *eb;
758 svn_delta_editor_t *de;
760 eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
761 eb->stream = stream;
763 de = svn_delta_default_editor(pool);
764 de->open_root = open_root;
765 de->delete_entry = delete_entry;
766 de->add_directory = add_directory;
767 de->open_directory = open_directory;
768 de->close_directory = close_directory;
769 de->change_dir_prop = change_dir_prop;
770 de->change_file_prop = change_file_prop;
771 de->apply_textdelta = apply_textdelta;
772 de->add_file = add_file;
773 de->open_file = open_file;
774 de->close_file = close_file;
775 de->close_edit = close_edit;
777 /* Set the edit_baton and editor. */
778 *edit_baton = eb;
779 *editor = de;
781 return SVN_NO_ERROR;