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
52struct 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.  */
90static 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.  */
96static 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.  */
100static 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.  */
106static 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
169static svn_boolean_t
170allow_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
179static svn_boolean_t
180check_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
204static void
205mark_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
260svn_error_t *
261svn_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
286void *
287svn_editor_get_baton(const svn_editor_t *editor)
288{
289  return editor->baton;
290}
291
292
293svn_error_t *
294svn_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
303svn_error_t *
304svn_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
313svn_error_t *
314svn_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
323svn_error_t *
324svn_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
333svn_error_t *
334svn_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
343svn_error_t *
344svn_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
353svn_error_t *
354svn_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
363svn_error_t *
364svn_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
373svn_error_t *
374svn_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
383svn_error_t *
384svn_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
393svn_error_t *
394svn_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
403svn_error_t *
404svn_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
413svn_error_t *
414svn_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
439static svn_error_t *
440check_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
455svn_error_t *
456svn_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
507svn_error_t *
508svn_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
547svn_error_t *
548svn_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
582svn_error_t *
583svn_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
615svn_error_t *
616svn_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
671svn_error_t *
672svn_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
711svn_error_t *
712svn_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
746svn_error_t *
747svn_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
777svn_error_t *
778svn_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
813svn_error_t *
814svn_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
853svn_error_t *
854svn_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
877svn_error_t *
878svn_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}
896