]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_delta/editor.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_delta / editor.c
1 /*
2  * editor.c :  editing trees of versioned resources
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include <apr_pools.h>
25
26 #include "svn_types.h"
27 #include "svn_error.h"
28 #include "svn_pools.h"
29 #include "svn_dirent_uri.h"
30
31 #include "private/svn_editor.h"
32
33 #ifdef SVN_DEBUG
34 /* This enables runtime checks of the editor API constraints.  This may
35    introduce additional memory and runtime overhead, and should not be used
36    in production builds.
37
38    ### Remove before release?
39
40    ### Disabled for now.  If I call svn_editor_alter_directory(A) then
41        svn_editor_add_file(A/f) the latter fails on SHOULD_ALLOW_ADD.
42        If I modify svn_editor_alter_directory to MARK_ALLOW_ADD(child)
43        then if I call svn_editor_alter_directory(A) followed by
44        svn_editor_alter_directory(A/B/C) the latter fails on
45        VERIFY_PARENT_MAY_EXIST. */
46 #if 0
47 #define ENABLE_ORDERING_CHECK
48 #endif
49 #endif
50
51
52 struct svn_editor_t
53 {
54   void *baton;
55
56   /* Standard cancellation function. Called before each callback.  */
57   svn_cancel_func_t cancel_func;
58   void *cancel_baton;
59
60   /* Our callback functions match that of the set-many structure, so
61      just use that.  */
62   svn_editor_cb_many_t funcs;
63
64   /* This pool is used as the scratch_pool for all callbacks.  */
65   apr_pool_t *scratch_pool;
66
67 #ifdef ENABLE_ORDERING_CHECK
68   svn_boolean_t within_callback;
69
70   apr_hash_t *pending_incomplete_children;
71   apr_hash_t *completed_nodes;
72   svn_boolean_t finished;
73
74   apr_pool_t *state_pool;
75 #endif
76 };
77
78
79 #ifdef ENABLE_ORDERING_CHECK
80
81 #define START_CALLBACK(editor)                       \
82   do {                                               \
83     svn_editor_t *editor__tmp_e = (editor);          \
84     SVN_ERR_ASSERT(!editor__tmp_e->within_callback); \
85     editor__tmp_e->within_callback = TRUE;           \
86   } while (0)
87 #define END_CALLBACK(editor) ((editor)->within_callback = FALSE)
88
89 /* Marker to indicate no further changes are allowed on this node.  */
90 static const int marker_done = 0;
91 #define MARKER_DONE (&marker_done)
92
93 /* Marker indicating that add_* may be called for this path, or that it
94    can be the destination of a copy or move. For copy/move, the path
95    will switch to MARKER_ALLOW_ALTER, to enable further tweaks.  */
96 static const int marker_allow_add = 0;
97 #define MARKER_ALLOW_ADD (&marker_allow_add)
98
99 /* Marker indicating that alter_* may be called for this path.  */
100 static const int marker_allow_alter = 0;
101 #define MARKER_ALLOW_ALTER (&marker_allow_alter)
102
103 /* Just like MARKER_DONE, but also indicates that the node was created
104    via add_directory(). This allows us to verify that the CHILDREN param
105    was comprehensive.  */
106 static const int marker_added_dir = 0;
107 #define MARKER_ADDED_DIR (&marker_added_dir)
108
109 #define MARK_FINISHED(editor) ((editor)->finished = TRUE)
110 #define SHOULD_NOT_BE_FINISHED(editor)  SVN_ERR_ASSERT(!(editor)->finished)
111
112 #define CLEAR_INCOMPLETE(editor, relpath) \
113   svn_hash_sets((editor)->pending_incomplete_children, relpath, NULL);
114
115 #define MARK_RELPATH(editor, relpath, value) \
116   svn_hash_sets((editor)->completed_nodes, \
117                 apr_pstrdup((editor)->state_pool, relpath), value)
118
119 #define MARK_COMPLETED(editor, relpath) \
120   MARK_RELPATH(editor, relpath, MARKER_DONE)
121 #define SHOULD_NOT_BE_COMPLETED(editor, relpath) \
122   SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, relpath) == NULL)
123
124 #define MARK_ALLOW_ADD(editor, relpath) \
125   MARK_RELPATH(editor, relpath, MARKER_ALLOW_ADD)
126 #define SHOULD_ALLOW_ADD(editor, relpath) \
127   SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ADD, NULL))
128
129 #define MARK_ALLOW_ALTER(editor, relpath) \
130   MARK_RELPATH(editor, relpath, MARKER_ALLOW_ALTER)
131 #define SHOULD_ALLOW_ALTER(editor, relpath) \
132   SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ALTER, NULL))
133
134 #define MARK_ADDED_DIR(editor, relpath) \
135   MARK_RELPATH(editor, relpath, MARKER_ADDED_DIR)
136 #define CHECK_UNKNOWN_CHILD(editor, relpath) \
137   SVN_ERR_ASSERT(check_unknown_child(editor, relpath))
138
139 /* When a child is changed in some way, mark the parent directory as needing
140    to be "stable" (no future structural changes). IOW, only allow "alter" on
141    the parent. Prevents parent-add/delete/move after any child operation.  */
142 #define MARK_PARENT_STABLE(editor, relpath) \
143   mark_parent_stable(editor, relpath)
144
145 /* If the parent is MARKER_ALLOW_ADD, then it has been moved-away, and we
146    know it does not exist. All other cases: it might exist.  */
147 #define VERIFY_PARENT_MAY_EXIST(editor, relpath) \
148   SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, \
149                                svn_relpath_dirname(relpath, \
150                                                    (editor)->scratch_pool)) \
151                  != MARKER_ALLOW_ADD)
152
153 /* If the parent is MARKER_ADDED_DIR, then we should not be deleting
154    children(*). If the parent is MARKER_ALLOW_ADD, then it has been
155    moved-away, so children cannot exist. That leaves MARKER_DONE,
156    MARKER_ALLOW_ALTER, and NULL as possible values. Just assert that
157    we didn't get either of the bad ones.
158
159    (*) if the child as added via add_*(), then it would have been marked
160    as completed and delete/move-away already test against completed nodes.
161    This test is to beware of trying to delete "children" that are not
162    actually (and can't possibly be) present.  */
163 #define CHILD_DELETIONS_ALLOWED(editor, relpath) \
164   SVN_ERR_ASSERT(!allow_either(editor, \
165                                svn_relpath_dirname(relpath, \
166                                                    (editor)->scratch_pool), \
167                                MARKER_ADDED_DIR, MARKER_ALLOW_ADD))
168
169 static svn_boolean_t
170 allow_either(const svn_editor_t *editor,
171              const char *relpath,
172              const void *marker1,
173              const void *marker2)
174 {
175   void *value = svn_hash_gets(editor->completed_nodes, relpath);
176   return value == marker1 || value == marker2;
177 }
178
179 static svn_boolean_t
180 check_unknown_child(const svn_editor_t *editor,
181                     const char *relpath)
182 {
183   const char *parent;
184
185   /* If we already know about the new child, then exit early.  */
186   if (svn_hash_gets(editor->pending_incomplete_children, relpath) != NULL)
187     return TRUE;
188
189   parent = svn_relpath_dirname(relpath, editor->scratch_pool);
190
191   /* Was this parent created via svn_editor_add_directory() ?  */
192   if (svn_hash_gets(editor->completed_nodes, parent)
193       == MARKER_ADDED_DIR)
194     {
195       /* Whoops. This child should have been listed in that add call,
196          and placed into ->pending_incomplete_children.  */
197       return FALSE;
198     }
199
200   /* The parent was not added in this drive.  */
201   return TRUE;
202 }
203
204 static void
205 mark_parent_stable(const svn_editor_t *editor,
206                    const char *relpath)
207 {
208   const char *parent = svn_relpath_dirname(relpath, editor->scratch_pool);
209   const void *marker = svn_hash_gets(editor->completed_nodes, parent);
210
211   /* If RELPATH has already been marked (to disallow adds, or that it
212      has been fully-completed), then do nothing.  */
213   if (marker == MARKER_ALLOW_ALTER
214       || marker == MARKER_DONE
215       || marker == MARKER_ADDED_DIR)
216     return;
217
218   /* If the marker is MARKER_ALLOW_ADD, then that means the parent was
219      moved away. There is no way to work on a child. That should have
220      been tested before we got here by VERIFY_PARENT_MAY_EXIST().  */
221   SVN_ERR_ASSERT_NO_RETURN(marker != MARKER_ALLOW_ADD);
222
223   /* MARKER is NULL. Upgrade it to MARKER_ALLOW_ALTER.  */
224   MARK_RELPATH(editor, parent, MARKER_ALLOW_ALTER);
225 }
226
227 #else
228
229 /* Be wary with the definition of these macros so that we don't
230    end up with "statement with no effect" warnings. Obviously, this
231    depends upon particular usage, which is easy to verify.  */
232
233 #define START_CALLBACK(editor)  /* empty */
234 #define END_CALLBACK(editor)  /* empty */
235
236 #define MARK_FINISHED(editor)  /* empty */
237 #define SHOULD_NOT_BE_FINISHED(editor)  /* empty */
238
239 #define CLEAR_INCOMPLETE(editor, relpath)  /* empty */
240
241 #define MARK_COMPLETED(editor, relpath)  /* empty */
242 #define SHOULD_NOT_BE_COMPLETED(editor, relpath)  /* empty */
243
244 #define MARK_ALLOW_ADD(editor, relpath)  /* empty */
245 #define SHOULD_ALLOW_ADD(editor, relpath)  /* empty */
246
247 #define MARK_ALLOW_ALTER(editor, relpath)  /* empty */
248 #define SHOULD_ALLOW_ALTER(editor, relpath)  /* empty */
249
250 #define MARK_ADDED_DIR(editor, relpath)  /* empty */
251 #define CHECK_UNKNOWN_CHILD(editor, relpath)  /* empty */
252
253 #define MARK_PARENT_STABLE(editor, relpath)  /* empty */
254 #define VERIFY_PARENT_MAY_EXIST(editor, relpath)  /* empty */
255 #define CHILD_DELETIONS_ALLOWED(editor, relpath)  /* empty */
256
257 #endif /* ENABLE_ORDERING_CHECK */
258
259
260 svn_error_t *
261 svn_editor_create(svn_editor_t **editor,
262                   void *editor_baton,
263                   svn_cancel_func_t cancel_func,
264                   void *cancel_baton,
265                   apr_pool_t *result_pool,
266                   apr_pool_t *scratch_pool)
267 {
268   *editor = apr_pcalloc(result_pool, sizeof(**editor));
269
270   (*editor)->baton = editor_baton;
271   (*editor)->cancel_func = cancel_func;
272   (*editor)->cancel_baton = cancel_baton;
273   (*editor)->scratch_pool = svn_pool_create(result_pool);
274
275 #ifdef ENABLE_ORDERING_CHECK
276   (*editor)->pending_incomplete_children = apr_hash_make(result_pool);
277   (*editor)->completed_nodes = apr_hash_make(result_pool);
278   (*editor)->finished = FALSE;
279   (*editor)->state_pool = result_pool;
280 #endif
281
282   return SVN_NO_ERROR;
283 }
284
285
286 void *
287 svn_editor_get_baton(const svn_editor_t *editor)
288 {
289   return editor->baton;
290 }
291
292
293 svn_error_t *
294 svn_editor_setcb_add_directory(svn_editor_t *editor,
295                                svn_editor_cb_add_directory_t callback,
296                                apr_pool_t *scratch_pool)
297 {
298   editor->funcs.cb_add_directory = callback;
299   return SVN_NO_ERROR;
300 }
301
302
303 svn_error_t *
304 svn_editor_setcb_add_file(svn_editor_t *editor,
305                           svn_editor_cb_add_file_t callback,
306                           apr_pool_t *scratch_pool)
307 {
308   editor->funcs.cb_add_file = callback;
309   return SVN_NO_ERROR;
310 }
311
312
313 svn_error_t *
314 svn_editor_setcb_add_symlink(svn_editor_t *editor,
315                              svn_editor_cb_add_symlink_t callback,
316                              apr_pool_t *scratch_pool)
317 {
318   editor->funcs.cb_add_symlink = callback;
319   return SVN_NO_ERROR;
320 }
321
322
323 svn_error_t *
324 svn_editor_setcb_add_absent(svn_editor_t *editor,
325                             svn_editor_cb_add_absent_t callback,
326                             apr_pool_t *scratch_pool)
327 {
328   editor->funcs.cb_add_absent = callback;
329   return SVN_NO_ERROR;
330 }
331
332
333 svn_error_t *
334 svn_editor_setcb_alter_directory(svn_editor_t *editor,
335                                  svn_editor_cb_alter_directory_t callback,
336                                  apr_pool_t *scratch_pool)
337 {
338   editor->funcs.cb_alter_directory = callback;
339   return SVN_NO_ERROR;
340 }
341
342
343 svn_error_t *
344 svn_editor_setcb_alter_file(svn_editor_t *editor,
345                             svn_editor_cb_alter_file_t callback,
346                             apr_pool_t *scratch_pool)
347 {
348   editor->funcs.cb_alter_file = callback;
349   return SVN_NO_ERROR;
350 }
351
352
353 svn_error_t *
354 svn_editor_setcb_alter_symlink(svn_editor_t *editor,
355                                svn_editor_cb_alter_symlink_t callback,
356                                apr_pool_t *scratch_pool)
357 {
358   editor->funcs.cb_alter_symlink = callback;
359   return SVN_NO_ERROR;
360 }
361
362
363 svn_error_t *
364 svn_editor_setcb_delete(svn_editor_t *editor,
365                         svn_editor_cb_delete_t callback,
366                         apr_pool_t *scratch_pool)
367 {
368   editor->funcs.cb_delete = callback;
369   return SVN_NO_ERROR;
370 }
371
372
373 svn_error_t *
374 svn_editor_setcb_copy(svn_editor_t *editor,
375                       svn_editor_cb_copy_t callback,
376                       apr_pool_t *scratch_pool)
377 {
378   editor->funcs.cb_copy = callback;
379   return SVN_NO_ERROR;
380 }
381
382
383 svn_error_t *
384 svn_editor_setcb_move(svn_editor_t *editor,
385                       svn_editor_cb_move_t callback,
386                       apr_pool_t *scratch_pool)
387 {
388   editor->funcs.cb_move = callback;
389   return SVN_NO_ERROR;
390 }
391
392
393 svn_error_t *
394 svn_editor_setcb_rotate(svn_editor_t *editor,
395                         svn_editor_cb_rotate_t callback,
396                         apr_pool_t *scratch_pool)
397 {
398   editor->funcs.cb_rotate = callback;
399   return SVN_NO_ERROR;
400 }
401
402
403 svn_error_t *
404 svn_editor_setcb_complete(svn_editor_t *editor,
405                           svn_editor_cb_complete_t callback,
406                           apr_pool_t *scratch_pool)
407 {
408   editor->funcs.cb_complete = callback;
409   return SVN_NO_ERROR;
410 }
411
412
413 svn_error_t *
414 svn_editor_setcb_abort(svn_editor_t *editor,
415                        svn_editor_cb_abort_t callback,
416                        apr_pool_t *scratch_pool)
417 {
418   editor->funcs.cb_abort = callback;
419   return SVN_NO_ERROR;
420 }
421
422
423 svn_error_t *
424 svn_editor_setcb_many(svn_editor_t *editor,
425                       const svn_editor_cb_many_t *many,
426                       apr_pool_t *scratch_pool)
427 {
428 #define COPY_CALLBACK(NAME) if (many->NAME) editor->funcs.NAME = many->NAME
429
430   COPY_CALLBACK(cb_add_directory);
431   COPY_CALLBACK(cb_add_file);
432   COPY_CALLBACK(cb_add_symlink);
433   COPY_CALLBACK(cb_add_absent);
434   COPY_CALLBACK(cb_alter_directory);
435   COPY_CALLBACK(cb_alter_file);
436   COPY_CALLBACK(cb_alter_symlink);
437   COPY_CALLBACK(cb_delete);
438   COPY_CALLBACK(cb_copy);
439   COPY_CALLBACK(cb_move);
440   COPY_CALLBACK(cb_rotate);
441   COPY_CALLBACK(cb_complete);
442   COPY_CALLBACK(cb_abort);
443
444 #undef COPY_CALLBACK
445
446   return SVN_NO_ERROR;
447 }
448
449
450 static svn_error_t *
451 check_cancel(svn_editor_t *editor)
452 {
453   svn_error_t *err = NULL;
454
455   if (editor->cancel_func)
456     {
457       START_CALLBACK(editor);
458       err = editor->cancel_func(editor->cancel_baton);
459       END_CALLBACK(editor);
460     }
461
462   return svn_error_trace(err);
463 }
464
465
466 svn_error_t *
467 svn_editor_add_directory(svn_editor_t *editor,
468                          const char *relpath,
469                          const apr_array_header_t *children,
470                          apr_hash_t *props,
471                          svn_revnum_t replaces_rev)
472 {
473   svn_error_t *err = SVN_NO_ERROR;
474
475   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
476   SVN_ERR_ASSERT(children != NULL);
477   SVN_ERR_ASSERT(props != NULL);
478   /* ### validate children are just basenames?  */
479   SHOULD_NOT_BE_FINISHED(editor);
480   SHOULD_ALLOW_ADD(editor, relpath);
481   VERIFY_PARENT_MAY_EXIST(editor, relpath);
482   CHECK_UNKNOWN_CHILD(editor, relpath);
483
484   SVN_ERR(check_cancel(editor));
485
486   if (editor->funcs.cb_add_directory)
487     {
488       START_CALLBACK(editor);
489       err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
490                                            props, replaces_rev,
491                                            editor->scratch_pool);
492       END_CALLBACK(editor);
493     }
494
495   MARK_ADDED_DIR(editor, relpath);
496   MARK_PARENT_STABLE(editor, relpath);
497   CLEAR_INCOMPLETE(editor, relpath);
498
499 #ifdef ENABLE_ORDERING_CHECK
500   {
501     int i;
502     for (i = 0; i < children->nelts; i++)
503       {
504         const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
505         const char *child = svn_relpath_join(relpath, child_basename,
506                                              editor->state_pool);
507
508         svn_hash_sets(editor->pending_incomplete_children, child, "");
509       }
510   }
511 #endif
512
513   svn_pool_clear(editor->scratch_pool);
514   return svn_error_trace(err);
515 }
516
517
518 svn_error_t *
519 svn_editor_add_file(svn_editor_t *editor,
520                     const char *relpath,
521                     const svn_checksum_t *checksum,
522                     svn_stream_t *contents,
523                     apr_hash_t *props,
524                     svn_revnum_t replaces_rev)
525 {
526   svn_error_t *err = SVN_NO_ERROR;
527
528   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
529   SVN_ERR_ASSERT(checksum != NULL
530                     && checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
531   SVN_ERR_ASSERT(contents != NULL);
532   SVN_ERR_ASSERT(props != NULL);
533   SHOULD_NOT_BE_FINISHED(editor);
534   SHOULD_ALLOW_ADD(editor, relpath);
535   VERIFY_PARENT_MAY_EXIST(editor, relpath);
536   CHECK_UNKNOWN_CHILD(editor, relpath);
537
538   SVN_ERR(check_cancel(editor));
539
540   if (editor->funcs.cb_add_file)
541     {
542       START_CALLBACK(editor);
543       err = editor->funcs.cb_add_file(editor->baton, relpath,
544                                       checksum, contents, props,
545                                       replaces_rev, editor->scratch_pool);
546       END_CALLBACK(editor);
547     }
548
549   MARK_COMPLETED(editor, relpath);
550   MARK_PARENT_STABLE(editor, relpath);
551   CLEAR_INCOMPLETE(editor, relpath);
552
553   svn_pool_clear(editor->scratch_pool);
554   return svn_error_trace(err);
555 }
556
557
558 svn_error_t *
559 svn_editor_add_symlink(svn_editor_t *editor,
560                        const char *relpath,
561                        const char *target,
562                        apr_hash_t *props,
563                        svn_revnum_t replaces_rev)
564 {
565   svn_error_t *err = SVN_NO_ERROR;
566
567   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
568   SVN_ERR_ASSERT(props != NULL);
569   SHOULD_NOT_BE_FINISHED(editor);
570   SHOULD_ALLOW_ADD(editor, relpath);
571   VERIFY_PARENT_MAY_EXIST(editor, relpath);
572   CHECK_UNKNOWN_CHILD(editor, relpath);
573
574   SVN_ERR(check_cancel(editor));
575
576   if (editor->funcs.cb_add_symlink)
577     {
578       START_CALLBACK(editor);
579       err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props,
580                                          replaces_rev, editor->scratch_pool);
581       END_CALLBACK(editor);
582     }
583
584   MARK_COMPLETED(editor, relpath);
585   MARK_PARENT_STABLE(editor, relpath);
586   CLEAR_INCOMPLETE(editor, relpath);
587
588   svn_pool_clear(editor->scratch_pool);
589   return svn_error_trace(err);
590 }
591
592
593 svn_error_t *
594 svn_editor_add_absent(svn_editor_t *editor,
595                       const char *relpath,
596                       svn_node_kind_t kind,
597                       svn_revnum_t replaces_rev)
598 {
599   svn_error_t *err = SVN_NO_ERROR;
600
601   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
602   SHOULD_NOT_BE_FINISHED(editor);
603   SHOULD_ALLOW_ADD(editor, relpath);
604   VERIFY_PARENT_MAY_EXIST(editor, relpath);
605   CHECK_UNKNOWN_CHILD(editor, relpath);
606
607   SVN_ERR(check_cancel(editor));
608
609   if (editor->funcs.cb_add_absent)
610     {
611       START_CALLBACK(editor);
612       err = editor->funcs.cb_add_absent(editor->baton, relpath, kind,
613                                         replaces_rev, editor->scratch_pool);
614       END_CALLBACK(editor);
615     }
616
617   MARK_COMPLETED(editor, relpath);
618   MARK_PARENT_STABLE(editor, relpath);
619   CLEAR_INCOMPLETE(editor, relpath);
620
621   svn_pool_clear(editor->scratch_pool);
622   return svn_error_trace(err);
623 }
624
625
626 svn_error_t *
627 svn_editor_alter_directory(svn_editor_t *editor,
628                            const char *relpath,
629                            svn_revnum_t revision,
630                            const apr_array_header_t *children,
631                            apr_hash_t *props)
632 {
633   svn_error_t *err = SVN_NO_ERROR;
634
635   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
636   SVN_ERR_ASSERT(children != NULL || props != NULL);
637   /* ### validate children are just basenames?  */
638   SHOULD_NOT_BE_FINISHED(editor);
639   SHOULD_ALLOW_ALTER(editor, relpath);
640   VERIFY_PARENT_MAY_EXIST(editor, relpath);
641
642   SVN_ERR(check_cancel(editor));
643
644   if (editor->funcs.cb_alter_directory)
645     {
646       START_CALLBACK(editor);
647       err = editor->funcs.cb_alter_directory(editor->baton,
648                                              relpath, revision,
649                                              children, props,
650                                              editor->scratch_pool);
651       END_CALLBACK(editor);
652     }
653
654   MARK_COMPLETED(editor, relpath);
655   MARK_PARENT_STABLE(editor, relpath);
656
657 #ifdef ENABLE_ORDERING_CHECK
658   /* ### this is not entirely correct. we probably need to adjust the
659      ### check_unknown_child() function for this scenario.  */
660 #if 0
661   {
662     int i;
663     for (i = 0; i < children->nelts; i++)
664       {
665         const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
666         const char *child = svn_relpath_join(relpath, child_basename,
667                                              editor->state_pool);
668
669         apr_hash_set(editor->pending_incomplete_children, child,
670                      APR_HASH_KEY_STRING, "");
671         /* Perhaps MARK_ALLOW_ADD(editor, child); ? */
672       }
673   }
674 #endif
675 #endif
676
677   svn_pool_clear(editor->scratch_pool);
678   return svn_error_trace(err);
679 }
680
681
682 svn_error_t *
683 svn_editor_alter_file(svn_editor_t *editor,
684                       const char *relpath,
685                       svn_revnum_t revision,
686                       apr_hash_t *props,
687                       const svn_checksum_t *checksum,
688                       svn_stream_t *contents)
689 {
690   svn_error_t *err = SVN_NO_ERROR;
691
692   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
693   SVN_ERR_ASSERT((checksum != NULL && contents != NULL)
694                  || (checksum == NULL && contents == NULL));
695   SVN_ERR_ASSERT(props != NULL || checksum != NULL);
696   if (checksum)
697     SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
698   SHOULD_NOT_BE_FINISHED(editor);
699   SHOULD_ALLOW_ALTER(editor, relpath);
700   VERIFY_PARENT_MAY_EXIST(editor, relpath);
701
702   SVN_ERR(check_cancel(editor));
703
704   if (editor->funcs.cb_alter_file)
705     {
706       START_CALLBACK(editor);
707       err = editor->funcs.cb_alter_file(editor->baton,
708                                         relpath, revision, props,
709                                         checksum, contents,
710                                         editor->scratch_pool);
711       END_CALLBACK(editor);
712     }
713
714   MARK_COMPLETED(editor, relpath);
715   MARK_PARENT_STABLE(editor, relpath);
716
717   svn_pool_clear(editor->scratch_pool);
718   return svn_error_trace(err);
719 }
720
721
722 svn_error_t *
723 svn_editor_alter_symlink(svn_editor_t *editor,
724                          const char *relpath,
725                          svn_revnum_t revision,
726                          apr_hash_t *props,
727                          const char *target)
728 {
729   svn_error_t *err = SVN_NO_ERROR;
730
731   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
732   SVN_ERR_ASSERT(props != NULL || target != NULL);
733   SHOULD_NOT_BE_FINISHED(editor);
734   SHOULD_ALLOW_ALTER(editor, relpath);
735   VERIFY_PARENT_MAY_EXIST(editor, relpath);
736
737   SVN_ERR(check_cancel(editor));
738
739   if (editor->funcs.cb_alter_symlink)
740     {
741       START_CALLBACK(editor);
742       err = editor->funcs.cb_alter_symlink(editor->baton,
743                                            relpath, revision, props,
744                                            target,
745                                            editor->scratch_pool);
746       END_CALLBACK(editor);
747     }
748
749   MARK_COMPLETED(editor, relpath);
750   MARK_PARENT_STABLE(editor, relpath);
751
752   svn_pool_clear(editor->scratch_pool);
753   return svn_error_trace(err);
754 }
755
756
757 svn_error_t *
758 svn_editor_delete(svn_editor_t *editor,
759                   const char *relpath,
760                   svn_revnum_t revision)
761 {
762   svn_error_t *err = SVN_NO_ERROR;
763
764   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
765   SHOULD_NOT_BE_FINISHED(editor);
766   SHOULD_NOT_BE_COMPLETED(editor, relpath);
767   VERIFY_PARENT_MAY_EXIST(editor, relpath);
768   CHILD_DELETIONS_ALLOWED(editor, relpath);
769
770   SVN_ERR(check_cancel(editor));
771
772   if (editor->funcs.cb_delete)
773     {
774       START_CALLBACK(editor);
775       err = editor->funcs.cb_delete(editor->baton, relpath, revision,
776                                     editor->scratch_pool);
777       END_CALLBACK(editor);
778     }
779
780   MARK_COMPLETED(editor, relpath);
781   MARK_PARENT_STABLE(editor, relpath);
782
783   svn_pool_clear(editor->scratch_pool);
784   return svn_error_trace(err);
785 }
786
787
788 svn_error_t *
789 svn_editor_copy(svn_editor_t *editor,
790                 const char *src_relpath,
791                 svn_revnum_t src_revision,
792                 const char *dst_relpath,
793                 svn_revnum_t replaces_rev)
794 {
795   svn_error_t *err = SVN_NO_ERROR;
796
797   SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
798   SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
799   SHOULD_NOT_BE_FINISHED(editor);
800   SHOULD_ALLOW_ADD(editor, dst_relpath);
801   VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
802   VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
803
804   SVN_ERR(check_cancel(editor));
805
806   if (editor->funcs.cb_copy)
807     {
808       START_CALLBACK(editor);
809       err = editor->funcs.cb_copy(editor->baton, src_relpath, src_revision,
810                                   dst_relpath, replaces_rev,
811                                   editor->scratch_pool);
812       END_CALLBACK(editor);
813     }
814
815   MARK_ALLOW_ALTER(editor, dst_relpath);
816   MARK_PARENT_STABLE(editor, dst_relpath);
817   CLEAR_INCOMPLETE(editor, dst_relpath);
818
819   svn_pool_clear(editor->scratch_pool);
820   return svn_error_trace(err);
821 }
822
823
824 svn_error_t *
825 svn_editor_move(svn_editor_t *editor,
826                 const char *src_relpath,
827                 svn_revnum_t src_revision,
828                 const char *dst_relpath,
829                 svn_revnum_t replaces_rev)
830 {
831   svn_error_t *err = SVN_NO_ERROR;
832
833   SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
834   SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
835   SHOULD_NOT_BE_FINISHED(editor);
836   SHOULD_NOT_BE_COMPLETED(editor, src_relpath);
837   SHOULD_ALLOW_ADD(editor, dst_relpath);
838   VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
839   CHILD_DELETIONS_ALLOWED(editor, src_relpath);
840   VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
841
842   SVN_ERR(check_cancel(editor));
843
844   if (editor->funcs.cb_move)
845     {
846       START_CALLBACK(editor);
847       err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision,
848                                   dst_relpath, replaces_rev,
849                                   editor->scratch_pool);
850       END_CALLBACK(editor);
851     }
852
853   MARK_ALLOW_ADD(editor, src_relpath);
854   MARK_PARENT_STABLE(editor, src_relpath);
855   MARK_ALLOW_ALTER(editor, dst_relpath);
856   MARK_PARENT_STABLE(editor, dst_relpath);
857   CLEAR_INCOMPLETE(editor, dst_relpath);
858
859   svn_pool_clear(editor->scratch_pool);
860   return svn_error_trace(err);
861 }
862
863
864 svn_error_t *
865 svn_editor_rotate(svn_editor_t *editor,
866                   const apr_array_header_t *relpaths,
867                   const apr_array_header_t *revisions)
868 {
869   svn_error_t *err = SVN_NO_ERROR;
870
871   SHOULD_NOT_BE_FINISHED(editor);
872 #ifdef ENABLE_ORDERING_CHECK
873   {
874     int i;
875     for (i = 0; i < relpaths->nelts; i++)
876       {
877         const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
878
879         SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
880         SHOULD_NOT_BE_COMPLETED(editor, relpath);
881         VERIFY_PARENT_MAY_EXIST(editor, relpath);
882         CHILD_DELETIONS_ALLOWED(editor, relpath);
883       }
884   }
885 #endif
886
887   SVN_ERR(check_cancel(editor));
888
889   if (editor->funcs.cb_rotate)
890     {
891       START_CALLBACK(editor);
892       err = editor->funcs.cb_rotate(editor->baton, relpaths, revisions,
893                                     editor->scratch_pool);
894       END_CALLBACK(editor);
895     }
896
897 #ifdef ENABLE_ORDERING_CHECK
898   {
899     int i;
900     for (i = 0; i < relpaths->nelts; i++)
901       {
902         const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
903         MARK_ALLOW_ALTER(editor, relpath);
904         MARK_PARENT_STABLE(editor, relpath);
905       }
906   }
907 #endif
908
909   svn_pool_clear(editor->scratch_pool);
910   return svn_error_trace(err);
911 }
912
913
914 svn_error_t *
915 svn_editor_complete(svn_editor_t *editor)
916 {
917   svn_error_t *err = SVN_NO_ERROR;
918
919   SHOULD_NOT_BE_FINISHED(editor);
920 #ifdef ENABLE_ORDERING_CHECK
921   SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0);
922 #endif
923
924   if (editor->funcs.cb_complete)
925     {
926       START_CALLBACK(editor);
927       err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool);
928       END_CALLBACK(editor);
929     }
930
931   MARK_FINISHED(editor);
932
933   svn_pool_clear(editor->scratch_pool);
934   return svn_error_trace(err);
935 }
936
937
938 svn_error_t *
939 svn_editor_abort(svn_editor_t *editor)
940 {
941   svn_error_t *err = SVN_NO_ERROR;
942
943   SHOULD_NOT_BE_FINISHED(editor);
944
945   if (editor->funcs.cb_abort)
946     {
947       START_CALLBACK(editor);
948       err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool);
949       END_CALLBACK(editor);
950     }
951
952   MARK_FINISHED(editor);
953
954   svn_pool_clear(editor->scratch_pool);
955   return svn_error_trace(err);
956 }