]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_delta/editor.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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_complete(svn_editor_t *editor,
395                           svn_editor_cb_complete_t callback,
396                           apr_pool_t *scratch_pool)
397 {
398   editor->funcs.cb_complete = callback;
399   return SVN_NO_ERROR;
400 }
401
402
403 svn_error_t *
404 svn_editor_setcb_abort(svn_editor_t *editor,
405                        svn_editor_cb_abort_t callback,
406                        apr_pool_t *scratch_pool)
407 {
408   editor->funcs.cb_abort = callback;
409   return SVN_NO_ERROR;
410 }
411
412
413 svn_error_t *
414 svn_editor_setcb_many(svn_editor_t *editor,
415                       const svn_editor_cb_many_t *many,
416                       apr_pool_t *scratch_pool)
417 {
418 #define COPY_CALLBACK(NAME) if (many->NAME) editor->funcs.NAME = many->NAME
419
420   COPY_CALLBACK(cb_add_directory);
421   COPY_CALLBACK(cb_add_file);
422   COPY_CALLBACK(cb_add_symlink);
423   COPY_CALLBACK(cb_add_absent);
424   COPY_CALLBACK(cb_alter_directory);
425   COPY_CALLBACK(cb_alter_file);
426   COPY_CALLBACK(cb_alter_symlink);
427   COPY_CALLBACK(cb_delete);
428   COPY_CALLBACK(cb_copy);
429   COPY_CALLBACK(cb_move);
430   COPY_CALLBACK(cb_complete);
431   COPY_CALLBACK(cb_abort);
432
433 #undef COPY_CALLBACK
434
435   return SVN_NO_ERROR;
436 }
437
438
439 static svn_error_t *
440 check_cancel(svn_editor_t *editor)
441 {
442   svn_error_t *err = NULL;
443
444   if (editor->cancel_func)
445     {
446       START_CALLBACK(editor);
447       err = editor->cancel_func(editor->cancel_baton);
448       END_CALLBACK(editor);
449     }
450
451   return svn_error_trace(err);
452 }
453
454
455 svn_error_t *
456 svn_editor_add_directory(svn_editor_t *editor,
457                          const char *relpath,
458                          const apr_array_header_t *children,
459                          apr_hash_t *props,
460                          svn_revnum_t replaces_rev)
461 {
462   svn_error_t *err = SVN_NO_ERROR;
463
464   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
465   SVN_ERR_ASSERT(children != NULL);
466   SVN_ERR_ASSERT(props != NULL);
467   /* ### validate children are just basenames?  */
468   SHOULD_NOT_BE_FINISHED(editor);
469   SHOULD_ALLOW_ADD(editor, relpath);
470   VERIFY_PARENT_MAY_EXIST(editor, relpath);
471   CHECK_UNKNOWN_CHILD(editor, relpath);
472
473   SVN_ERR(check_cancel(editor));
474
475   if (editor->funcs.cb_add_directory)
476     {
477       START_CALLBACK(editor);
478       err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
479                                            props, replaces_rev,
480                                            editor->scratch_pool);
481       END_CALLBACK(editor);
482     }
483
484   MARK_ADDED_DIR(editor, relpath);
485   MARK_PARENT_STABLE(editor, relpath);
486   CLEAR_INCOMPLETE(editor, relpath);
487
488 #ifdef ENABLE_ORDERING_CHECK
489   {
490     int i;
491     for (i = 0; i < children->nelts; i++)
492       {
493         const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
494         const char *child = svn_relpath_join(relpath, child_basename,
495                                              editor->state_pool);
496
497         svn_hash_sets(editor->pending_incomplete_children, child, "");
498       }
499   }
500 #endif
501
502   svn_pool_clear(editor->scratch_pool);
503   return svn_error_trace(err);
504 }
505
506
507 svn_error_t *
508 svn_editor_add_file(svn_editor_t *editor,
509                     const char *relpath,
510                     const svn_checksum_t *checksum,
511                     svn_stream_t *contents,
512                     apr_hash_t *props,
513                     svn_revnum_t replaces_rev)
514 {
515   svn_error_t *err = SVN_NO_ERROR;
516
517   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
518   SVN_ERR_ASSERT(checksum != NULL
519                     && checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
520   SVN_ERR_ASSERT(contents != NULL);
521   SVN_ERR_ASSERT(props != NULL);
522   SHOULD_NOT_BE_FINISHED(editor);
523   SHOULD_ALLOW_ADD(editor, relpath);
524   VERIFY_PARENT_MAY_EXIST(editor, relpath);
525   CHECK_UNKNOWN_CHILD(editor, relpath);
526
527   SVN_ERR(check_cancel(editor));
528
529   if (editor->funcs.cb_add_file)
530     {
531       START_CALLBACK(editor);
532       err = editor->funcs.cb_add_file(editor->baton, relpath,
533                                       checksum, contents, props,
534                                       replaces_rev, editor->scratch_pool);
535       END_CALLBACK(editor);
536     }
537
538   MARK_COMPLETED(editor, relpath);
539   MARK_PARENT_STABLE(editor, relpath);
540   CLEAR_INCOMPLETE(editor, relpath);
541
542   svn_pool_clear(editor->scratch_pool);
543   return svn_error_trace(err);
544 }
545
546
547 svn_error_t *
548 svn_editor_add_symlink(svn_editor_t *editor,
549                        const char *relpath,
550                        const char *target,
551                        apr_hash_t *props,
552                        svn_revnum_t replaces_rev)
553 {
554   svn_error_t *err = SVN_NO_ERROR;
555
556   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
557   SVN_ERR_ASSERT(props != NULL);
558   SHOULD_NOT_BE_FINISHED(editor);
559   SHOULD_ALLOW_ADD(editor, relpath);
560   VERIFY_PARENT_MAY_EXIST(editor, relpath);
561   CHECK_UNKNOWN_CHILD(editor, relpath);
562
563   SVN_ERR(check_cancel(editor));
564
565   if (editor->funcs.cb_add_symlink)
566     {
567       START_CALLBACK(editor);
568       err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props,
569                                          replaces_rev, editor->scratch_pool);
570       END_CALLBACK(editor);
571     }
572
573   MARK_COMPLETED(editor, relpath);
574   MARK_PARENT_STABLE(editor, relpath);
575   CLEAR_INCOMPLETE(editor, relpath);
576
577   svn_pool_clear(editor->scratch_pool);
578   return svn_error_trace(err);
579 }
580
581
582 svn_error_t *
583 svn_editor_add_absent(svn_editor_t *editor,
584                       const char *relpath,
585                       svn_node_kind_t kind,
586                       svn_revnum_t replaces_rev)
587 {
588   svn_error_t *err = SVN_NO_ERROR;
589
590   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
591   SHOULD_NOT_BE_FINISHED(editor);
592   SHOULD_ALLOW_ADD(editor, relpath);
593   VERIFY_PARENT_MAY_EXIST(editor, relpath);
594   CHECK_UNKNOWN_CHILD(editor, relpath);
595
596   SVN_ERR(check_cancel(editor));
597
598   if (editor->funcs.cb_add_absent)
599     {
600       START_CALLBACK(editor);
601       err = editor->funcs.cb_add_absent(editor->baton, relpath, kind,
602                                         replaces_rev, editor->scratch_pool);
603       END_CALLBACK(editor);
604     }
605
606   MARK_COMPLETED(editor, relpath);
607   MARK_PARENT_STABLE(editor, relpath);
608   CLEAR_INCOMPLETE(editor, relpath);
609
610   svn_pool_clear(editor->scratch_pool);
611   return svn_error_trace(err);
612 }
613
614
615 svn_error_t *
616 svn_editor_alter_directory(svn_editor_t *editor,
617                            const char *relpath,
618                            svn_revnum_t revision,
619                            const apr_array_header_t *children,
620                            apr_hash_t *props)
621 {
622   svn_error_t *err = SVN_NO_ERROR;
623
624   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
625   SVN_ERR_ASSERT(children != NULL || props != NULL);
626   /* ### validate children are just basenames?  */
627   SHOULD_NOT_BE_FINISHED(editor);
628   SHOULD_ALLOW_ALTER(editor, relpath);
629   VERIFY_PARENT_MAY_EXIST(editor, relpath);
630
631   SVN_ERR(check_cancel(editor));
632
633   if (editor->funcs.cb_alter_directory)
634     {
635       START_CALLBACK(editor);
636       err = editor->funcs.cb_alter_directory(editor->baton,
637                                              relpath, revision,
638                                              children, props,
639                                              editor->scratch_pool);
640       END_CALLBACK(editor);
641     }
642
643   MARK_COMPLETED(editor, relpath);
644   MARK_PARENT_STABLE(editor, relpath);
645
646 #ifdef ENABLE_ORDERING_CHECK
647   /* ### this is not entirely correct. we probably need to adjust the
648      ### check_unknown_child() function for this scenario.  */
649 #if 0
650   {
651     int i;
652     for (i = 0; i < children->nelts; i++)
653       {
654         const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
655         const char *child = svn_relpath_join(relpath, child_basename,
656                                              editor->state_pool);
657
658         apr_hash_set(editor->pending_incomplete_children, child,
659                      APR_HASH_KEY_STRING, "");
660         /* Perhaps MARK_ALLOW_ADD(editor, child); ? */
661       }
662   }
663 #endif
664 #endif
665
666   svn_pool_clear(editor->scratch_pool);
667   return svn_error_trace(err);
668 }
669
670
671 svn_error_t *
672 svn_editor_alter_file(svn_editor_t *editor,
673                       const char *relpath,
674                       svn_revnum_t revision,
675                       const svn_checksum_t *checksum,
676                       svn_stream_t *contents,
677                       apr_hash_t *props)
678 {
679   svn_error_t *err = SVN_NO_ERROR;
680
681   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
682   SVN_ERR_ASSERT((checksum != NULL && contents != NULL)
683                  || (checksum == NULL && contents == NULL));
684   SVN_ERR_ASSERT(props != NULL || checksum != NULL);
685   if (checksum)
686     SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
687   SHOULD_NOT_BE_FINISHED(editor);
688   SHOULD_ALLOW_ALTER(editor, relpath);
689   VERIFY_PARENT_MAY_EXIST(editor, relpath);
690
691   SVN_ERR(check_cancel(editor));
692
693   if (editor->funcs.cb_alter_file)
694     {
695       START_CALLBACK(editor);
696       err = editor->funcs.cb_alter_file(editor->baton,
697                                         relpath, revision,
698                                         checksum, contents, props,
699                                         editor->scratch_pool);
700       END_CALLBACK(editor);
701     }
702
703   MARK_COMPLETED(editor, relpath);
704   MARK_PARENT_STABLE(editor, relpath);
705
706   svn_pool_clear(editor->scratch_pool);
707   return svn_error_trace(err);
708 }
709
710
711 svn_error_t *
712 svn_editor_alter_symlink(svn_editor_t *editor,
713                          const char *relpath,
714                          svn_revnum_t revision,
715                          const char *target,
716                          apr_hash_t *props)
717 {
718   svn_error_t *err = SVN_NO_ERROR;
719
720   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
721   SVN_ERR_ASSERT(props != NULL || target != NULL);
722   SHOULD_NOT_BE_FINISHED(editor);
723   SHOULD_ALLOW_ALTER(editor, relpath);
724   VERIFY_PARENT_MAY_EXIST(editor, relpath);
725
726   SVN_ERR(check_cancel(editor));
727
728   if (editor->funcs.cb_alter_symlink)
729     {
730       START_CALLBACK(editor);
731       err = editor->funcs.cb_alter_symlink(editor->baton,
732                                            relpath, revision,
733                                            target, props,
734                                            editor->scratch_pool);
735       END_CALLBACK(editor);
736     }
737
738   MARK_COMPLETED(editor, relpath);
739   MARK_PARENT_STABLE(editor, relpath);
740
741   svn_pool_clear(editor->scratch_pool);
742   return svn_error_trace(err);
743 }
744
745
746 svn_error_t *
747 svn_editor_delete(svn_editor_t *editor,
748                   const char *relpath,
749                   svn_revnum_t revision)
750 {
751   svn_error_t *err = SVN_NO_ERROR;
752
753   SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
754   SHOULD_NOT_BE_FINISHED(editor);
755   SHOULD_NOT_BE_COMPLETED(editor, relpath);
756   VERIFY_PARENT_MAY_EXIST(editor, relpath);
757   CHILD_DELETIONS_ALLOWED(editor, relpath);
758
759   SVN_ERR(check_cancel(editor));
760
761   if (editor->funcs.cb_delete)
762     {
763       START_CALLBACK(editor);
764       err = editor->funcs.cb_delete(editor->baton, relpath, revision,
765                                     editor->scratch_pool);
766       END_CALLBACK(editor);
767     }
768
769   MARK_COMPLETED(editor, relpath);
770   MARK_PARENT_STABLE(editor, relpath);
771
772   svn_pool_clear(editor->scratch_pool);
773   return svn_error_trace(err);
774 }
775
776
777 svn_error_t *
778 svn_editor_copy(svn_editor_t *editor,
779                 const char *src_relpath,
780                 svn_revnum_t src_revision,
781                 const char *dst_relpath,
782                 svn_revnum_t replaces_rev)
783 {
784   svn_error_t *err = SVN_NO_ERROR;
785
786   SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
787   SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
788   SHOULD_NOT_BE_FINISHED(editor);
789   SHOULD_ALLOW_ADD(editor, dst_relpath);
790   VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
791   VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
792
793   SVN_ERR(check_cancel(editor));
794
795   if (editor->funcs.cb_copy)
796     {
797       START_CALLBACK(editor);
798       err = editor->funcs.cb_copy(editor->baton, src_relpath, src_revision,
799                                   dst_relpath, replaces_rev,
800                                   editor->scratch_pool);
801       END_CALLBACK(editor);
802     }
803
804   MARK_ALLOW_ALTER(editor, dst_relpath);
805   MARK_PARENT_STABLE(editor, dst_relpath);
806   CLEAR_INCOMPLETE(editor, dst_relpath);
807
808   svn_pool_clear(editor->scratch_pool);
809   return svn_error_trace(err);
810 }
811
812
813 svn_error_t *
814 svn_editor_move(svn_editor_t *editor,
815                 const char *src_relpath,
816                 svn_revnum_t src_revision,
817                 const char *dst_relpath,
818                 svn_revnum_t replaces_rev)
819 {
820   svn_error_t *err = SVN_NO_ERROR;
821
822   SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
823   SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
824   SHOULD_NOT_BE_FINISHED(editor);
825   SHOULD_NOT_BE_COMPLETED(editor, src_relpath);
826   SHOULD_ALLOW_ADD(editor, dst_relpath);
827   VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
828   CHILD_DELETIONS_ALLOWED(editor, src_relpath);
829   VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
830
831   SVN_ERR(check_cancel(editor));
832
833   if (editor->funcs.cb_move)
834     {
835       START_CALLBACK(editor);
836       err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision,
837                                   dst_relpath, replaces_rev,
838                                   editor->scratch_pool);
839       END_CALLBACK(editor);
840     }
841
842   MARK_ALLOW_ADD(editor, src_relpath);
843   MARK_PARENT_STABLE(editor, src_relpath);
844   MARK_ALLOW_ALTER(editor, dst_relpath);
845   MARK_PARENT_STABLE(editor, dst_relpath);
846   CLEAR_INCOMPLETE(editor, dst_relpath);
847
848   svn_pool_clear(editor->scratch_pool);
849   return svn_error_trace(err);
850 }
851
852
853 svn_error_t *
854 svn_editor_complete(svn_editor_t *editor)
855 {
856   svn_error_t *err = SVN_NO_ERROR;
857
858   SHOULD_NOT_BE_FINISHED(editor);
859 #ifdef ENABLE_ORDERING_CHECK
860   SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0);
861 #endif
862
863   if (editor->funcs.cb_complete)
864     {
865       START_CALLBACK(editor);
866       err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool);
867       END_CALLBACK(editor);
868     }
869
870   MARK_FINISHED(editor);
871
872   svn_pool_clear(editor->scratch_pool);
873   return svn_error_trace(err);
874 }
875
876
877 svn_error_t *
878 svn_editor_abort(svn_editor_t *editor)
879 {
880   svn_error_t *err = SVN_NO_ERROR;
881
882   SHOULD_NOT_BE_FINISHED(editor);
883
884   if (editor->funcs.cb_abort)
885     {
886       START_CALLBACK(editor);
887       err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool);
888       END_CALLBACK(editor);
889     }
890
891   MARK_FINISHED(editor);
892
893   svn_pool_clear(editor->scratch_pool);
894   return svn_error_trace(err);
895 }