1251881Speter/*
2251881Speter * compat.c :  Wrappers and callbacks for compatibility.
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include <stddef.h>
25251881Speter
26251881Speter#include "svn_types.h"
27251881Speter#include "svn_error.h"
28251881Speter#include "svn_delta.h"
29251881Speter#include "svn_sorts.h"
30251881Speter#include "svn_dirent_uri.h"
31251881Speter#include "svn_path.h"
32251881Speter#include "svn_hash.h"
33251881Speter#include "svn_props.h"
34251881Speter#include "svn_pools.h"
35251881Speter
36251881Speter#include "svn_private_config.h"
37251881Speter
38251881Speter#include "private/svn_delta_private.h"
39251881Speter
40251881Speter
41251881Speterstruct file_rev_handler_wrapper_baton {
42251881Speter  void *baton;
43251881Speter  svn_file_rev_handler_old_t handler;
44251881Speter};
45251881Speter
46251881Speter/* This implements svn_file_rev_handler_t. */
47251881Speterstatic svn_error_t *
48251881Speterfile_rev_handler_wrapper(void *baton,
49251881Speter                         const char *path,
50251881Speter                         svn_revnum_t rev,
51251881Speter                         apr_hash_t *rev_props,
52251881Speter                         svn_boolean_t result_of_merge,
53251881Speter                         svn_txdelta_window_handler_t *delta_handler,
54251881Speter                         void **delta_baton,
55251881Speter                         apr_array_header_t *prop_diffs,
56251881Speter                         apr_pool_t *pool)
57251881Speter{
58251881Speter  struct file_rev_handler_wrapper_baton *fwb = baton;
59251881Speter
60251881Speter  if (fwb->handler)
61251881Speter    return fwb->handler(fwb->baton,
62251881Speter                        path,
63251881Speter                        rev,
64251881Speter                        rev_props,
65251881Speter                        delta_handler,
66251881Speter                        delta_baton,
67251881Speter                        prop_diffs,
68251881Speter                        pool);
69251881Speter
70251881Speter  return SVN_NO_ERROR;
71251881Speter}
72251881Speter
73251881Spetervoid
74251881Spetersvn_compat_wrap_file_rev_handler(svn_file_rev_handler_t *handler2,
75251881Speter                                 void **handler2_baton,
76251881Speter                                 svn_file_rev_handler_old_t handler,
77251881Speter                                 void *handler_baton,
78251881Speter                                 apr_pool_t *pool)
79251881Speter{
80251881Speter  struct file_rev_handler_wrapper_baton *fwb = apr_pcalloc(pool, sizeof(*fwb));
81251881Speter
82251881Speter  /* Set the user provided old format callback in the baton. */
83251881Speter  fwb->baton = handler_baton;
84251881Speter  fwb->handler = handler;
85251881Speter
86251881Speter  *handler2_baton = fwb;
87251881Speter  *handler2 = file_rev_handler_wrapper;
88251881Speter}
89251881Speter
90251881Speter
91251881Speter/* The following code maps the calls to a traditional delta editor to an
92251881Speter * Editorv2 editor.  It does this by keeping track of a lot of state, and
93251881Speter * then communicating that state to Ev2 upon closure of the file or dir (or
94251881Speter * edit).  Note that Ev2 calls add_symlink() and alter_symlink() are not
95251881Speter * present in the delta editor paradigm, so we never call them.
96251881Speter *
97251881Speter * The general idea here is that we have to see *all* the actions on a node's
98251881Speter * parent before we can process that node, which means we need to buffer a
99251881Speter * large amount of information in the dir batons, and then process it in the
100251881Speter * close_directory() handler.
101251881Speter *
102251881Speter * There are a few ways we alter the callback stream.  One is when unlocking
103251881Speter * paths.  To tell a client a path should be unlocked, the server sends a
104251881Speter * prop-del for the SVN_PROP_ENTRY_LOCK_TOKEN property.  This causes problems,
105251881Speter * since the client doesn't have this property in the first place, but the
106251881Speter * deletion has side effects (unlike deleting a non-existent regular property
107251881Speter * would).  To solve this, we introduce *another* function into the API, not
108251881Speter * a part of the Ev2 callbacks, but a companion which is used to register
109251881Speter * the unlock of a path.  See ev2_change_file_prop() for implemenation
110251881Speter * details.
111251881Speter */
112251881Speter
113251881Speterstruct ev2_edit_baton
114251881Speter{
115251881Speter  svn_editor_t *editor;
116251881Speter
117251881Speter  apr_hash_t *changes;  /* REPOS_RELPATH -> struct change_node  */
118251881Speter
119251881Speter  apr_array_header_t *path_order;
120251881Speter  int paths_processed;
121251881Speter
122251881Speter  /* For calculating relpaths from Ev1 copyfrom urls. */
123251881Speter  const char *repos_root;
124251881Speter  const char *base_relpath;
125251881Speter
126251881Speter  apr_pool_t *edit_pool;
127251881Speter  struct svn_delta__extra_baton *exb;
128251881Speter  svn_boolean_t closed;
129251881Speter
130251881Speter  svn_boolean_t *found_abs_paths; /* Did we strip an incoming '/' from the
131251881Speter                                     paths?  */
132251881Speter
133251881Speter  svn_delta_fetch_props_func_t fetch_props_func;
134251881Speter  void *fetch_props_baton;
135251881Speter
136251881Speter  svn_delta_fetch_base_func_t fetch_base_func;
137251881Speter  void *fetch_base_baton;
138251881Speter
139251881Speter  svn_delta__unlock_func_t do_unlock;
140251881Speter  void *unlock_baton;
141251881Speter};
142251881Speter
143251881Speterstruct ev2_dir_baton
144251881Speter{
145251881Speter  struct ev2_edit_baton *eb;
146251881Speter  const char *path;
147251881Speter  svn_revnum_t base_revision;
148251881Speter
149251881Speter  const char *copyfrom_relpath;
150251881Speter  svn_revnum_t copyfrom_rev;
151251881Speter};
152251881Speter
153251881Speterstruct ev2_file_baton
154251881Speter{
155251881Speter  struct ev2_edit_baton *eb;
156251881Speter  const char *path;
157251881Speter  svn_revnum_t base_revision;
158251881Speter  const char *delta_base;
159251881Speter};
160251881Speter
161251881Speterenum restructure_action_t
162251881Speter{
163251881Speter  RESTRUCTURE_NONE = 0,
164251881Speter  RESTRUCTURE_ADD,         /* add the node, maybe replacing. maybe copy  */
165251881Speter  RESTRUCTURE_ADD_ABSENT,  /* add an absent node, possibly replacing  */
166251881Speter  RESTRUCTURE_DELETE       /* delete this node  */
167251881Speter};
168251881Speter
169251881Speter/* Records everything about how this node is to be changed.  */
170251881Speterstruct change_node
171251881Speter{
172251881Speter  /* what kind of (tree) restructure is occurring at this node?  */
173251881Speter  enum restructure_action_t action;
174251881Speter
175251881Speter  svn_node_kind_t kind;  /* the NEW kind of this node  */
176251881Speter
177251881Speter  /* We need two revisions: one to specify the revision we are altering,
178251881Speter     and a second to specify the revision to delete/replace. These are
179251881Speter     mutually exclusive, but they need to be separate to ensure we don't
180251881Speter     confuse the operation on this node. For example, we may delete a
181251881Speter     node and replace it we use DELETING for REPLACES_REV, and ignore
182251881Speter     the value placed into CHANGING when properties were set/changed
183251881Speter     on the new node. Or we simply change a node (setting CHANGING),
184251881Speter     and DELETING remains SVN_INVALID_REVNUM, indicating we are not
185251881Speter     attempting to replace a node.  */
186251881Speter  svn_revnum_t changing;
187251881Speter  svn_revnum_t deleting;
188251881Speter
189251881Speter  apr_hash_t *props;  /* new/final set of props to apply  */
190251881Speter
191251881Speter  const char *contents_abspath;  /* file containing new fulltext  */
192251881Speter  svn_checksum_t *checksum;  /* checksum of new fulltext  */
193251881Speter
194251881Speter  /* If COPYFROM_PATH is not NULL, then copy PATH@REV to this node.
195251881Speter     RESTRUCTURE must be RESTRUCTURE_ADD.  */
196251881Speter  const char *copyfrom_path;
197251881Speter  svn_revnum_t copyfrom_rev;
198251881Speter
199251881Speter  /* Record whether an incoming propchange unlocked this node.  */
200251881Speter  svn_boolean_t unlock;
201251881Speter};
202251881Speter
203251881Speter
204251881Speterstatic struct change_node *
205251881Speterlocate_change(struct ev2_edit_baton *eb,
206251881Speter              const char *relpath)
207251881Speter{
208251881Speter  struct change_node *change = svn_hash_gets(eb->changes, relpath);
209251881Speter
210251881Speter  if (change != NULL)
211251881Speter    return change;
212251881Speter
213251881Speter  /* Shift RELPATH into the proper pool, and record the observed order.  */
214251881Speter  relpath = apr_pstrdup(eb->edit_pool, relpath);
215251881Speter  APR_ARRAY_PUSH(eb->path_order, const char *) = relpath;
216251881Speter
217251881Speter  /* Return an empty change. Callers will tweak as needed.  */
218251881Speter  change = apr_pcalloc(eb->edit_pool, sizeof(*change));
219251881Speter  change->changing = SVN_INVALID_REVNUM;
220251881Speter  change->deleting = SVN_INVALID_REVNUM;
221253734Speter  change->kind = svn_node_unknown;
222251881Speter
223251881Speter  svn_hash_sets(eb->changes, relpath, change);
224251881Speter
225251881Speter  return change;
226251881Speter}
227251881Speter
228251881Speter
229251881Speterstatic svn_error_t *
230251881Speterapply_propedit(struct ev2_edit_baton *eb,
231251881Speter               const char *relpath,
232251881Speter               svn_node_kind_t kind,
233251881Speter               svn_revnum_t base_revision,
234251881Speter               const char *name,
235251881Speter               const svn_string_t *value,
236251881Speter               apr_pool_t *scratch_pool)
237251881Speter{
238251881Speter  struct change_node *change = locate_change(eb, relpath);
239251881Speter
240251881Speter  SVN_ERR_ASSERT(change->kind == svn_node_unknown || change->kind == kind);
241251881Speter  change->kind = kind;
242251881Speter
243251881Speter  /* We're now changing the node. Record the revision.  */
244251881Speter  SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing)
245251881Speter                 || change->changing == base_revision);
246251881Speter  change->changing = base_revision;
247251881Speter
248251881Speter  if (change->props == NULL)
249251881Speter    {
250251881Speter      /* Fetch the original set of properties. We'll apply edits to create
251251881Speter         the new/target set of properties.
252251881Speter
253251881Speter         If this is a copied/moved now, then the original properties come
254251881Speter         from there. If the node has been added, it starts with empty props.
255251881Speter         Otherwise, we get the properties from BASE.  */
256251881Speter
257251881Speter      if (change->copyfrom_path)
258251881Speter        SVN_ERR(eb->fetch_props_func(&change->props,
259251881Speter                                     eb->fetch_props_baton,
260251881Speter                                     change->copyfrom_path,
261251881Speter                                     change->copyfrom_rev,
262251881Speter                                     eb->edit_pool, scratch_pool));
263251881Speter      else if (change->action == RESTRUCTURE_ADD)
264251881Speter        change->props = apr_hash_make(eb->edit_pool);
265251881Speter      else
266251881Speter        SVN_ERR(eb->fetch_props_func(&change->props,
267251881Speter                                     eb->fetch_props_baton,
268251881Speter                                     relpath, base_revision,
269251881Speter                                     eb->edit_pool, scratch_pool));
270251881Speter    }
271251881Speter
272251881Speter  if (value == NULL)
273251881Speter    svn_hash_sets(change->props, name, NULL);
274251881Speter  else
275251881Speter    svn_hash_sets(change->props,
276251881Speter                  apr_pstrdup(eb->edit_pool, name),
277251881Speter                  svn_string_dup(value, eb->edit_pool));
278251881Speter
279251881Speter  return SVN_NO_ERROR;
280251881Speter}
281251881Speter
282251881Speter
283251881Speter/* Find all the paths which are immediate children of PATH and return their
284251881Speter   basenames in a list. */
285251881Speterstatic apr_array_header_t *
286251881Speterget_children(struct ev2_edit_baton *eb,
287251881Speter             const char *path,
288251881Speter             apr_pool_t *pool)
289251881Speter{
290251881Speter  apr_array_header_t *children = apr_array_make(pool, 1, sizeof(const char *));
291251881Speter  apr_hash_index_t *hi;
292251881Speter
293251881Speter  for (hi = apr_hash_first(pool, eb->changes); hi; hi = apr_hash_next(hi))
294251881Speter    {
295251881Speter      const char *repos_relpath = svn__apr_hash_index_key(hi);
296251881Speter      const char *child;
297251881Speter
298251881Speter      /* Find potential children. */
299251881Speter      child = svn_relpath_skip_ancestor(path, repos_relpath);
300251881Speter      if (!child || !*child)
301251881Speter        continue;
302251881Speter
303251881Speter      /* If we have a path separator, it's a deep child, so just ignore it.
304251881Speter         ### Is there an API we should be using for this? */
305251881Speter      if (strchr(child, '/') != NULL)
306251881Speter        continue;
307251881Speter
308251881Speter      APR_ARRAY_PUSH(children, const char *) = child;
309251881Speter    }
310251881Speter
311251881Speter  return children;
312251881Speter}
313251881Speter
314251881Speter
315251881Speterstatic svn_error_t *
316251881Speterprocess_actions(struct ev2_edit_baton *eb,
317251881Speter                const char *repos_relpath,
318251881Speter                const struct change_node *change,
319251881Speter                apr_pool_t *scratch_pool)
320251881Speter{
321251881Speter  apr_hash_t *props = NULL;
322251881Speter  svn_stream_t *contents = NULL;
323251881Speter  svn_checksum_t *checksum = NULL;
324251881Speter  svn_node_kind_t kind = svn_node_unknown;
325251881Speter
326251881Speter  SVN_ERR_ASSERT(change != NULL);
327251881Speter
328251881Speter  if (change->unlock)
329251881Speter    SVN_ERR(eb->do_unlock(eb->unlock_baton, repos_relpath, scratch_pool));
330251881Speter
331251881Speter  if (change->action == RESTRUCTURE_DELETE)
332251881Speter    {
333251881Speter      /* If the action was left as RESTRUCTURE_DELETE, then a
334251881Speter         replacement is not occurring. Just do the delete and bail.  */
335251881Speter      SVN_ERR(svn_editor_delete(eb->editor, repos_relpath,
336251881Speter                                change->deleting));
337251881Speter
338251881Speter      /* No further work possible on this node.  */
339251881Speter      return SVN_NO_ERROR;
340251881Speter    }
341251881Speter  if (change->action == RESTRUCTURE_ADD_ABSENT)
342251881Speter    {
343251881Speter      SVN_ERR(svn_editor_add_absent(eb->editor, repos_relpath,
344251881Speter                                    change->kind, change->deleting));
345251881Speter
346251881Speter      /* No further work possible on this node.  */
347251881Speter      return SVN_NO_ERROR;
348251881Speter    }
349251881Speter
350251881Speter  if (change->contents_abspath != NULL)
351251881Speter    {
352251881Speter      /* We can only set text on files. */
353251881Speter      /* ### validate we aren't overwriting KIND?  */
354251881Speter      kind = svn_node_file;
355251881Speter
356251881Speter      /* ### the checksum might be in CHANGE->CHECKSUM  */
357251881Speter      SVN_ERR(svn_io_file_checksum2(&checksum, change->contents_abspath,
358251881Speter                                    svn_checksum_sha1, scratch_pool));
359251881Speter      SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath,
360251881Speter                                       scratch_pool, scratch_pool));
361251881Speter    }
362251881Speter
363251881Speter  if (change->props != NULL)
364251881Speter    {
365251881Speter      /* ### validate we aren't overwriting KIND?  */
366251881Speter      kind = change->kind;
367251881Speter      props = change->props;
368251881Speter    }
369251881Speter
370251881Speter  if (change->action == RESTRUCTURE_ADD)
371251881Speter    {
372251881Speter      /* An add might be a replace. Grab the revnum we're replacing.  */
373251881Speter      svn_revnum_t replaces_rev = change->deleting;
374251881Speter
375251881Speter      kind = change->kind;
376251881Speter
377251881Speter      if (change->copyfrom_path != NULL)
378251881Speter        {
379251881Speter          SVN_ERR(svn_editor_copy(eb->editor, change->copyfrom_path,
380251881Speter                                  change->copyfrom_rev,
381251881Speter                                  repos_relpath, replaces_rev));
382251881Speter          /* Fall through to possibly make changes post-copy.  */
383251881Speter        }
384251881Speter      else
385251881Speter        {
386251881Speter          /* If no properties were defined, then use an empty set.  */
387251881Speter          if (props == NULL)
388251881Speter            props = apr_hash_make(scratch_pool);
389251881Speter
390251881Speter          if (kind == svn_node_dir)
391251881Speter            {
392251881Speter              const apr_array_header_t *children;
393251881Speter
394251881Speter              children = get_children(eb, repos_relpath, scratch_pool);
395251881Speter              SVN_ERR(svn_editor_add_directory(eb->editor, repos_relpath,
396251881Speter                                               children, props,
397251881Speter                                               replaces_rev));
398251881Speter            }
399251881Speter          else
400251881Speter            {
401251881Speter              /* If this file was added, but apply_txdelta() was not
402251881Speter                 called (ie. no CONTENTS_ABSPATH), then we're adding
403251881Speter                 an empty file.  */
404251881Speter              if (change->contents_abspath == NULL)
405251881Speter                {
406251881Speter                  contents = svn_stream_empty(scratch_pool);
407251881Speter                  checksum = svn_checksum_empty_checksum(svn_checksum_sha1,
408251881Speter                                                         scratch_pool);
409251881Speter                }
410251881Speter
411251881Speter              SVN_ERR(svn_editor_add_file(eb->editor, repos_relpath,
412251881Speter                                          checksum, contents, props,
413251881Speter                                          replaces_rev));
414251881Speter            }
415251881Speter
416251881Speter          /* No further work possible on this node.  */
417251881Speter          return SVN_NO_ERROR;
418251881Speter        }
419251881Speter    }
420251881Speter
421251881Speter#if 0
422251881Speter  /* There *should* be work for this node. But it seems that isn't true
423251881Speter     in some cases. Future investigation...  */
424251881Speter  SVN_ERR_ASSERT(props || contents);
425251881Speter#endif
426251881Speter  if (props || contents)
427251881Speter    {
428251881Speter      /* Changes to properties or content should have indicated the revision
429251881Speter         it was intending to change.
430251881Speter
431251881Speter         Oop. Not true. The node may be locally-added.  */
432251881Speter#if 0
433251881Speter      SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(change->changing));
434251881Speter#endif
435251881Speter
436251881Speter      /* ### we need to gather up the target set of children  */
437251881Speter
438251881Speter      if (kind == svn_node_dir)
439251881Speter        SVN_ERR(svn_editor_alter_directory(eb->editor, repos_relpath,
440251881Speter                                           change->changing, NULL, props));
441251881Speter      else
442251881Speter        SVN_ERR(svn_editor_alter_file(eb->editor, repos_relpath,
443251881Speter                                      change->changing, props,
444251881Speter                                      checksum, contents));
445251881Speter    }
446251881Speter
447251881Speter  return SVN_NO_ERROR;
448251881Speter}
449251881Speter
450251881Speterstatic svn_error_t *
451251881Speterrun_ev2_actions(struct ev2_edit_baton *eb,
452251881Speter                apr_pool_t *scratch_pool)
453251881Speter{
454251881Speter  apr_pool_t *iterpool;
455251881Speter
456251881Speter  iterpool = svn_pool_create(scratch_pool);
457251881Speter
458251881Speter  /* Possibly pick up where we left off. Ocassionally, we do some of these
459251881Speter     as part of close_edit() and then some more as part of abort_edit()  */
460251881Speter  for (; eb->paths_processed < eb->path_order->nelts; ++eb->paths_processed)
461251881Speter    {
462251881Speter      const char *repos_relpath = APR_ARRAY_IDX(eb->path_order,
463251881Speter                                                eb->paths_processed,
464251881Speter                                                const char *);
465251881Speter      const struct change_node *change = svn_hash_gets(eb->changes,
466251881Speter                                                       repos_relpath);
467251881Speter
468251881Speter      svn_pool_clear(iterpool);
469251881Speter
470251881Speter      SVN_ERR(process_actions(eb, repos_relpath, change, iterpool));
471251881Speter    }
472251881Speter  svn_pool_destroy(iterpool);
473251881Speter
474251881Speter  return SVN_NO_ERROR;
475251881Speter}
476251881Speter
477251881Speter
478251881Speterstatic const char *
479251881Spetermap_to_repos_relpath(struct ev2_edit_baton *eb,
480251881Speter                     const char *path_or_url,
481251881Speter                     apr_pool_t *result_pool)
482251881Speter{
483251881Speter  if (svn_path_is_url(path_or_url))
484251881Speter    {
485251881Speter      return svn_uri_skip_ancestor(eb->repos_root, path_or_url, result_pool);
486251881Speter    }
487251881Speter  else
488251881Speter    {
489251881Speter      return svn_relpath_join(eb->base_relpath,
490251881Speter                              path_or_url[0] == '/'
491251881Speter                                    ? path_or_url + 1 : path_or_url,
492251881Speter                              result_pool);
493251881Speter    }
494251881Speter}
495251881Speter
496251881Speter
497251881Speterstatic svn_error_t *
498251881Speterev2_set_target_revision(void *edit_baton,
499251881Speter                        svn_revnum_t target_revision,
500251881Speter                        apr_pool_t *scratch_pool)
501251881Speter{
502251881Speter  struct ev2_edit_baton *eb = edit_baton;
503251881Speter
504251881Speter  if (eb->exb->target_revision)
505251881Speter    SVN_ERR(eb->exb->target_revision(eb->exb->baton, target_revision,
506251881Speter                                     scratch_pool));
507251881Speter
508251881Speter  return SVN_NO_ERROR;
509251881Speter}
510251881Speter
511251881Speterstatic svn_error_t *
512251881Speterev2_open_root(void *edit_baton,
513251881Speter              svn_revnum_t base_revision,
514251881Speter              apr_pool_t *result_pool,
515251881Speter              void **root_baton)
516251881Speter{
517251881Speter  struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
518251881Speter  struct ev2_edit_baton *eb = edit_baton;
519251881Speter
520251881Speter  db->eb = eb;
521251881Speter  db->path = apr_pstrdup(eb->edit_pool, eb->base_relpath);
522251881Speter  db->base_revision = base_revision;
523251881Speter
524251881Speter  *root_baton = db;
525251881Speter
526251881Speter  if (eb->exb->start_edit)
527251881Speter    SVN_ERR(eb->exb->start_edit(eb->exb->baton, base_revision));
528251881Speter
529251881Speter  return SVN_NO_ERROR;
530251881Speter}
531251881Speter
532251881Speterstatic svn_error_t *
533251881Speterev2_delete_entry(const char *path,
534251881Speter                 svn_revnum_t revision,
535251881Speter                 void *parent_baton,
536251881Speter                 apr_pool_t *scratch_pool)
537251881Speter{
538251881Speter  struct ev2_dir_baton *pb = parent_baton;
539251881Speter  svn_revnum_t base_revision;
540251881Speter  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
541251881Speter  struct change_node *change = locate_change(pb->eb, relpath);
542251881Speter
543251881Speter  if (SVN_IS_VALID_REVNUM(revision))
544251881Speter    base_revision = revision;
545251881Speter  else
546251881Speter    base_revision = pb->base_revision;
547251881Speter
548251881Speter  SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
549251881Speter  change->action = RESTRUCTURE_DELETE;
550251881Speter
551251881Speter  SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->deleting)
552251881Speter                 || change->deleting == base_revision);
553251881Speter  change->deleting = base_revision;
554251881Speter
555251881Speter  return SVN_NO_ERROR;
556251881Speter}
557251881Speter
558251881Speterstatic svn_error_t *
559251881Speterev2_add_directory(const char *path,
560251881Speter                  void *parent_baton,
561251881Speter                  const char *copyfrom_path,
562251881Speter                  svn_revnum_t copyfrom_revision,
563251881Speter                  apr_pool_t *result_pool,
564251881Speter                  void **child_baton)
565251881Speter{
566251881Speter  /* ### fix this?  */
567251881Speter  apr_pool_t *scratch_pool = result_pool;
568251881Speter  struct ev2_dir_baton *pb = parent_baton;
569251881Speter  struct ev2_dir_baton *cb = apr_pcalloc(result_pool, sizeof(*cb));
570251881Speter  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
571251881Speter  struct change_node *change = locate_change(pb->eb, relpath);
572251881Speter
573251881Speter  /* ### assert that RESTRUCTURE is NONE or DELETE?  */
574251881Speter  change->action = RESTRUCTURE_ADD;
575251881Speter  change->kind = svn_node_dir;
576251881Speter
577251881Speter  cb->eb = pb->eb;
578251881Speter  cb->path = apr_pstrdup(result_pool, relpath);
579251881Speter  cb->base_revision = pb->base_revision;
580251881Speter  *child_baton = cb;
581251881Speter
582251881Speter  if (!copyfrom_path)
583251881Speter    {
584251881Speter      if (pb->copyfrom_relpath)
585251881Speter        {
586251881Speter          const char *name = svn_relpath_basename(relpath, scratch_pool);
587251881Speter          cb->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name,
588251881Speter                                                  result_pool);
589251881Speter          cb->copyfrom_rev = pb->copyfrom_rev;
590251881Speter        }
591251881Speter    }
592251881Speter  else
593251881Speter    {
594251881Speter      /* A copy */
595251881Speter
596251881Speter      change->copyfrom_path = map_to_repos_relpath(pb->eb, copyfrom_path,
597251881Speter                                                   pb->eb->edit_pool);
598251881Speter      change->copyfrom_rev = copyfrom_revision;
599251881Speter
600251881Speter      cb->copyfrom_relpath = change->copyfrom_path;
601251881Speter      cb->copyfrom_rev = change->copyfrom_rev;
602251881Speter    }
603251881Speter
604251881Speter  return SVN_NO_ERROR;
605251881Speter}
606251881Speter
607251881Speterstatic svn_error_t *
608251881Speterev2_open_directory(const char *path,
609251881Speter                   void *parent_baton,
610251881Speter                   svn_revnum_t base_revision,
611251881Speter                   apr_pool_t *result_pool,
612251881Speter                   void **child_baton)
613251881Speter{
614251881Speter  /* ### fix this?  */
615251881Speter  apr_pool_t *scratch_pool = result_pool;
616251881Speter  struct ev2_dir_baton *pb = parent_baton;
617251881Speter  struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
618251881Speter  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
619251881Speter
620251881Speter  db->eb = pb->eb;
621251881Speter  db->path = apr_pstrdup(result_pool, relpath);
622251881Speter  db->base_revision = base_revision;
623251881Speter
624251881Speter  if (pb->copyfrom_relpath)
625251881Speter    {
626251881Speter      /* We are inside a copy. */
627251881Speter      const char *name = svn_relpath_basename(relpath, scratch_pool);
628251881Speter
629251881Speter      db->copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath, name,
630251881Speter                                              result_pool);
631251881Speter      db->copyfrom_rev = pb->copyfrom_rev;
632251881Speter    }
633251881Speter
634251881Speter  *child_baton = db;
635251881Speter  return SVN_NO_ERROR;
636251881Speter}
637251881Speter
638251881Speterstatic svn_error_t *
639251881Speterev2_change_dir_prop(void *dir_baton,
640251881Speter                    const char *name,
641251881Speter                    const svn_string_t *value,
642251881Speter                    apr_pool_t *scratch_pool)
643251881Speter{
644251881Speter  struct ev2_dir_baton *db = dir_baton;
645251881Speter
646251881Speter  SVN_ERR(apply_propedit(db->eb, db->path, svn_node_dir, db->base_revision,
647251881Speter                         name, value, scratch_pool));
648251881Speter
649251881Speter  return SVN_NO_ERROR;
650251881Speter}
651251881Speter
652251881Speterstatic svn_error_t *
653251881Speterev2_close_directory(void *dir_baton,
654251881Speter                    apr_pool_t *scratch_pool)
655251881Speter{
656251881Speter  return SVN_NO_ERROR;
657251881Speter}
658251881Speter
659251881Speterstatic svn_error_t *
660251881Speterev2_absent_directory(const char *path,
661251881Speter                     void *parent_baton,
662251881Speter                     apr_pool_t *scratch_pool)
663251881Speter{
664251881Speter  struct ev2_dir_baton *pb = parent_baton;
665251881Speter  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
666251881Speter  struct change_node *change = locate_change(pb->eb, relpath);
667251881Speter
668251881Speter  /* ### assert that RESTRUCTURE is NONE or DELETE?  */
669251881Speter  change->action = RESTRUCTURE_ADD_ABSENT;
670251881Speter  change->kind = svn_node_dir;
671251881Speter
672251881Speter  return SVN_NO_ERROR;
673251881Speter}
674251881Speter
675251881Speterstatic svn_error_t *
676251881Speterev2_add_file(const char *path,
677251881Speter             void *parent_baton,
678251881Speter             const char *copyfrom_path,
679251881Speter             svn_revnum_t copyfrom_revision,
680251881Speter             apr_pool_t *result_pool,
681251881Speter             void **file_baton)
682251881Speter{
683251881Speter  /* ### fix this?  */
684251881Speter  apr_pool_t *scratch_pool = result_pool;
685251881Speter  struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
686251881Speter  struct ev2_dir_baton *pb = parent_baton;
687251881Speter  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
688251881Speter  struct change_node *change = locate_change(pb->eb, relpath);
689251881Speter
690251881Speter  /* ### assert that RESTRUCTURE is NONE or DELETE?  */
691251881Speter  change->action = RESTRUCTURE_ADD;
692251881Speter  change->kind = svn_node_file;
693251881Speter
694251881Speter  fb->eb = pb->eb;
695251881Speter  fb->path = apr_pstrdup(result_pool, relpath);
696251881Speter  fb->base_revision = pb->base_revision;
697251881Speter  *file_baton = fb;
698251881Speter
699251881Speter  if (!copyfrom_path)
700251881Speter    {
701251881Speter      /* Don't bother fetching the base, as in an add we don't have a base. */
702251881Speter      fb->delta_base = NULL;
703251881Speter    }
704251881Speter  else
705251881Speter    {
706251881Speter      /* A copy */
707251881Speter
708251881Speter      change->copyfrom_path = map_to_repos_relpath(fb->eb, copyfrom_path,
709251881Speter                                                   fb->eb->edit_pool);
710251881Speter      change->copyfrom_rev = copyfrom_revision;
711251881Speter
712251881Speter      SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
713251881Speter                                      fb->eb->fetch_base_baton,
714251881Speter                                      change->copyfrom_path,
715251881Speter                                      change->copyfrom_rev,
716251881Speter                                      result_pool, scratch_pool));
717251881Speter    }
718251881Speter
719251881Speter  return SVN_NO_ERROR;
720251881Speter}
721251881Speter
722251881Speterstatic svn_error_t *
723251881Speterev2_open_file(const char *path,
724251881Speter              void *parent_baton,
725251881Speter              svn_revnum_t base_revision,
726251881Speter              apr_pool_t *result_pool,
727251881Speter              void **file_baton)
728251881Speter{
729251881Speter  /* ### fix this?  */
730251881Speter  apr_pool_t *scratch_pool = result_pool;
731251881Speter  struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
732251881Speter  struct ev2_dir_baton *pb = parent_baton;
733251881Speter  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
734251881Speter
735251881Speter  fb->eb = pb->eb;
736251881Speter  fb->path = apr_pstrdup(result_pool, relpath);
737251881Speter  fb->base_revision = base_revision;
738251881Speter
739251881Speter  if (pb->copyfrom_relpath)
740251881Speter    {
741251881Speter      /* We're in a copied directory, so the delta base is going to be
742251881Speter         based up on the copy source. */
743251881Speter      const char *name = svn_relpath_basename(relpath, scratch_pool);
744251881Speter      const char *copyfrom_relpath = svn_relpath_join(pb->copyfrom_relpath,
745251881Speter                                                      name,
746251881Speter                                                      scratch_pool);
747251881Speter
748251881Speter      SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
749251881Speter                                      fb->eb->fetch_base_baton,
750251881Speter                                      copyfrom_relpath, pb->copyfrom_rev,
751251881Speter                                      result_pool, scratch_pool));
752251881Speter    }
753251881Speter  else
754251881Speter    {
755251881Speter      SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
756251881Speter                                      fb->eb->fetch_base_baton,
757251881Speter                                      relpath, base_revision,
758251881Speter                                      result_pool, scratch_pool));
759251881Speter    }
760251881Speter
761251881Speter  *file_baton = fb;
762251881Speter  return SVN_NO_ERROR;
763251881Speter}
764251881Speter
765251881Speterstruct handler_baton
766251881Speter{
767251881Speter  svn_txdelta_window_handler_t apply_handler;
768251881Speter  void *apply_baton;
769251881Speter
770251881Speter  svn_stream_t *source;
771251881Speter
772251881Speter  apr_pool_t *pool;
773251881Speter};
774251881Speter
775251881Speterstatic svn_error_t *
776251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton)
777251881Speter{
778251881Speter  struct handler_baton *hb = baton;
779251881Speter  svn_error_t *err;
780251881Speter
781251881Speter  err = hb->apply_handler(window, hb->apply_baton);
782251881Speter  if (window != NULL && !err)
783251881Speter    return SVN_NO_ERROR;
784251881Speter
785251881Speter  SVN_ERR(svn_stream_close(hb->source));
786251881Speter
787251881Speter  svn_pool_destroy(hb->pool);
788251881Speter
789251881Speter  return svn_error_trace(err);
790251881Speter}
791251881Speter
792251881Speter
793251881Speterstatic svn_error_t *
794251881Speterev2_apply_textdelta(void *file_baton,
795251881Speter                    const char *base_checksum,
796251881Speter                    apr_pool_t *result_pool,
797251881Speter                    svn_txdelta_window_handler_t *handler,
798251881Speter                    void **handler_baton)
799251881Speter{
800251881Speter  struct ev2_file_baton *fb = file_baton;
801251881Speter  apr_pool_t *handler_pool = svn_pool_create(fb->eb->edit_pool);
802251881Speter  struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
803251881Speter  struct change_node *change;
804251881Speter  svn_stream_t *target;
805251881Speter  /* ### fix this. for now, we know this has a "short" lifetime.  */
806251881Speter  apr_pool_t *scratch_pool = handler_pool;
807251881Speter
808251881Speter  change = locate_change(fb->eb, fb->path);
809251881Speter  SVN_ERR_ASSERT(change->contents_abspath == NULL);
810251881Speter  SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing)
811251881Speter                 || change->changing == fb->base_revision);
812251881Speter  change->changing = fb->base_revision;
813251881Speter
814251881Speter  if (! fb->delta_base)
815251881Speter    hb->source = svn_stream_empty(handler_pool);
816251881Speter  else
817251881Speter    SVN_ERR(svn_stream_open_readonly(&hb->source, fb->delta_base, handler_pool,
818251881Speter                                     scratch_pool));
819251881Speter
820251881Speter  SVN_ERR(svn_stream_open_unique(&target, &change->contents_abspath, NULL,
821251881Speter                                 svn_io_file_del_on_pool_cleanup,
822251881Speter                                 fb->eb->edit_pool, scratch_pool));
823251881Speter
824251881Speter  svn_txdelta_apply(hb->source, target,
825251881Speter                    NULL, NULL,
826251881Speter                    handler_pool,
827251881Speter                    &hb->apply_handler, &hb->apply_baton);
828251881Speter
829251881Speter  hb->pool = handler_pool;
830251881Speter
831251881Speter  *handler_baton = hb;
832251881Speter  *handler = window_handler;
833251881Speter
834251881Speter  return SVN_NO_ERROR;
835251881Speter}
836251881Speter
837251881Speterstatic svn_error_t *
838251881Speterev2_change_file_prop(void *file_baton,
839251881Speter                     const char *name,
840251881Speter                     const svn_string_t *value,
841251881Speter                     apr_pool_t *scratch_pool)
842251881Speter{
843251881Speter  struct ev2_file_baton *fb = file_baton;
844251881Speter
845251881Speter  if (!strcmp(name, SVN_PROP_ENTRY_LOCK_TOKEN) && value == NULL)
846251881Speter    {
847251881Speter      /* We special case the lock token propery deletion, which is the
848251881Speter         server's way of telling the client to unlock the path. */
849251881Speter
850251881Speter      /* ### this duplicates much of apply_propedit(). fix in future.  */
851251881Speter      const char *relpath = map_to_repos_relpath(fb->eb, fb->path,
852251881Speter                                                 scratch_pool);
853251881Speter      struct change_node *change = locate_change(fb->eb, relpath);
854251881Speter
855251881Speter      change->unlock = TRUE;
856251881Speter    }
857251881Speter
858251881Speter  SVN_ERR(apply_propedit(fb->eb, fb->path, svn_node_file, fb->base_revision,
859251881Speter                         name, value, scratch_pool));
860251881Speter
861251881Speter  return SVN_NO_ERROR;
862251881Speter}
863251881Speter
864251881Speterstatic svn_error_t *
865251881Speterev2_close_file(void *file_baton,
866251881Speter               const char *text_checksum,
867251881Speter               apr_pool_t *scratch_pool)
868251881Speter{
869251881Speter  return SVN_NO_ERROR;
870251881Speter}
871251881Speter
872251881Speterstatic svn_error_t *
873251881Speterev2_absent_file(const char *path,
874251881Speter                void *parent_baton,
875251881Speter                apr_pool_t *scratch_pool)
876251881Speter{
877251881Speter  struct ev2_dir_baton *pb = parent_baton;
878251881Speter  const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool);
879251881Speter  struct change_node *change = locate_change(pb->eb, relpath);
880251881Speter
881251881Speter  /* ### assert that RESTRUCTURE is NONE or DELETE?  */
882251881Speter  change->action = RESTRUCTURE_ADD_ABSENT;
883251881Speter  change->kind = svn_node_file;
884251881Speter
885251881Speter  return SVN_NO_ERROR;
886251881Speter}
887251881Speter
888251881Speterstatic svn_error_t *
889251881Speterev2_close_edit(void *edit_baton,
890251881Speter               apr_pool_t *scratch_pool)
891251881Speter{
892251881Speter  struct ev2_edit_baton *eb = edit_baton;
893251881Speter
894251881Speter  SVN_ERR(run_ev2_actions(edit_baton, scratch_pool));
895251881Speter  eb->closed = TRUE;
896251881Speter  return svn_error_trace(svn_editor_complete(eb->editor));
897251881Speter}
898251881Speter
899251881Speterstatic svn_error_t *
900251881Speterev2_abort_edit(void *edit_baton,
901251881Speter               apr_pool_t *scratch_pool)
902251881Speter{
903251881Speter  struct ev2_edit_baton *eb = edit_baton;
904251881Speter
905251881Speter  SVN_ERR(run_ev2_actions(edit_baton, scratch_pool));
906251881Speter  if (!eb->closed)
907251881Speter    return svn_error_trace(svn_editor_abort(eb->editor));
908251881Speter  else
909251881Speter    return SVN_NO_ERROR;
910251881Speter}
911251881Speter
912251881Speter/* Return a svn_delta_editor_t * in DEDITOR, with an accompanying baton in
913251881Speter * DEDITOR_BATON, which will drive EDITOR.  These will both be
914251881Speter * allocated in RESULT_POOL, which may become large and long-lived;
915251881Speter * SCRATCH_POOL is used for temporary allocations.
916251881Speter *
917251881Speter * The other parameters are as follows:
918251881Speter *  - UNLOCK_FUNC / UNLOCK_BATON: A callback / baton which will be called
919251881Speter *         when an unlocking action is received.
920251881Speter *  - FOUND_ABS_PATHS: A pointer to a boolean flag which will be set if
921251881Speter *         this shim determines that it is receiving absolute paths.
922251881Speter *  - FETCH_PROPS_FUNC / FETCH_PROPS_BATON: A callback / baton pair which
923251881Speter *         will be used by the shim handlers if they need to determine the
924251881Speter *         existing properties on a  path.
925251881Speter *  - FETCH_BASE_FUNC / FETCH_BASE_BATON: A callback / baton pair which will
926251881Speter *         be used by the shims handlers if they need to determine the base
927251881Speter *         text of a path.  It should only be invoked for files.
928251881Speter *  - EXB: An 'extra baton' which is used to communicate between the shims.
929251881Speter *         Its callbacks should be invoked at the appropriate time by this
930251881Speter *         shim.
931251881Speter */
932251881Spetersvn_error_t *
933251881Spetersvn_delta__delta_from_editor(const svn_delta_editor_t **deditor,
934251881Speter                  void **dedit_baton,
935251881Speter                  svn_editor_t *editor,
936251881Speter                  svn_delta__unlock_func_t unlock_func,
937251881Speter                  void *unlock_baton,
938251881Speter                  svn_boolean_t *found_abs_paths,
939251881Speter                  const char *repos_root,
940251881Speter                  const char *base_relpath,
941251881Speter                  svn_delta_fetch_props_func_t fetch_props_func,
942251881Speter                  void *fetch_props_baton,
943251881Speter                  svn_delta_fetch_base_func_t fetch_base_func,
944251881Speter                  void *fetch_base_baton,
945251881Speter                  struct svn_delta__extra_baton *exb,
946251881Speter                  apr_pool_t *pool)
947251881Speter{
948251881Speter  /* Static 'cause we don't want it to be on the stack. */
949251881Speter  static svn_delta_editor_t delta_editor = {
950251881Speter      ev2_set_target_revision,
951251881Speter      ev2_open_root,
952251881Speter      ev2_delete_entry,
953251881Speter      ev2_add_directory,
954251881Speter      ev2_open_directory,
955251881Speter      ev2_change_dir_prop,
956251881Speter      ev2_close_directory,
957251881Speter      ev2_absent_directory,
958251881Speter      ev2_add_file,
959251881Speter      ev2_open_file,
960251881Speter      ev2_apply_textdelta,
961251881Speter      ev2_change_file_prop,
962251881Speter      ev2_close_file,
963251881Speter      ev2_absent_file,
964251881Speter      ev2_close_edit,
965251881Speter      ev2_abort_edit
966251881Speter    };
967251881Speter  struct ev2_edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
968251881Speter
969251881Speter  if (!base_relpath)
970251881Speter    base_relpath = "";
971251881Speter  else if (base_relpath[0] == '/')
972251881Speter    base_relpath += 1;
973251881Speter
974251881Speter  eb->editor = editor;
975251881Speter  eb->changes = apr_hash_make(pool);
976251881Speter  eb->path_order = apr_array_make(pool, 1, sizeof(const char *));
977251881Speter  eb->edit_pool = pool;
978251881Speter  eb->found_abs_paths = found_abs_paths;
979251881Speter  *eb->found_abs_paths = FALSE;
980251881Speter  eb->exb = exb;
981251881Speter  eb->repos_root = apr_pstrdup(pool, repos_root);
982251881Speter  eb->base_relpath = apr_pstrdup(pool, base_relpath);
983251881Speter
984251881Speter  eb->fetch_props_func = fetch_props_func;
985251881Speter  eb->fetch_props_baton = fetch_props_baton;
986251881Speter
987251881Speter  eb->fetch_base_func = fetch_base_func;
988251881Speter  eb->fetch_base_baton = fetch_base_baton;
989251881Speter
990251881Speter  eb->do_unlock = unlock_func;
991251881Speter  eb->unlock_baton = unlock_baton;
992251881Speter
993251881Speter  *dedit_baton = eb;
994251881Speter  *deditor = &delta_editor;
995251881Speter
996251881Speter  return SVN_NO_ERROR;
997251881Speter}
998251881Speter
999251881Speter
1000251881Speter/* ### note the similarity to struct change_node. these structures will
1001251881Speter   ### be combined in the future.  */
1002251881Speterstruct operation {
1003251881Speter  /* ### leave these two here for now. still used.  */
1004251881Speter  svn_revnum_t base_revision;
1005251881Speter  void *baton;
1006251881Speter};
1007251881Speter
1008251881Speterstruct editor_baton
1009251881Speter{
1010251881Speter  const svn_delta_editor_t *deditor;
1011251881Speter  void *dedit_baton;
1012251881Speter
1013251881Speter  svn_delta_fetch_kind_func_t fetch_kind_func;
1014251881Speter  void *fetch_kind_baton;
1015251881Speter
1016251881Speter  svn_delta_fetch_props_func_t fetch_props_func;
1017251881Speter  void *fetch_props_baton;
1018251881Speter
1019251881Speter  struct operation root;
1020251881Speter  svn_boolean_t *make_abs_paths;
1021251881Speter  const char *repos_root;
1022251881Speter  const char *base_relpath;
1023251881Speter
1024251881Speter  /* REPOS_RELPATH -> struct change_node *  */
1025251881Speter  apr_hash_t *changes;
1026251881Speter
1027251881Speter  apr_pool_t *edit_pool;
1028251881Speter};
1029251881Speter
1030251881Speter
1031251881Speter/* Insert a new change for RELPATH, or return an existing one.  */
1032251881Speterstatic struct change_node *
1033251881Speterinsert_change(const char *relpath,
1034251881Speter              apr_hash_t *changes)
1035251881Speter{
1036251881Speter  apr_pool_t *result_pool;
1037251881Speter  struct change_node *change;
1038251881Speter
1039251881Speter  change = svn_hash_gets(changes, relpath);
1040251881Speter  if (change != NULL)
1041251881Speter    return change;
1042251881Speter
1043251881Speter  result_pool = apr_hash_pool_get(changes);
1044251881Speter
1045251881Speter  /* Return an empty change. Callers will tweak as needed.  */
1046251881Speter  change = apr_pcalloc(result_pool, sizeof(*change));
1047251881Speter  change->changing = SVN_INVALID_REVNUM;
1048251881Speter  change->deleting = SVN_INVALID_REVNUM;
1049251881Speter
1050251881Speter  svn_hash_sets(changes, apr_pstrdup(result_pool, relpath), change);
1051251881Speter
1052251881Speter  return change;
1053251881Speter}
1054251881Speter
1055251881Speter
1056251881Speter/* This implements svn_editor_cb_add_directory_t */
1057251881Speterstatic svn_error_t *
1058251881Speteradd_directory_cb(void *baton,
1059251881Speter                 const char *relpath,
1060251881Speter                 const apr_array_header_t *children,
1061251881Speter                 apr_hash_t *props,
1062251881Speter                 svn_revnum_t replaces_rev,
1063251881Speter                 apr_pool_t *scratch_pool)
1064251881Speter{
1065251881Speter  struct editor_baton *eb = baton;
1066251881Speter  struct change_node *change = insert_change(relpath, eb->changes);
1067251881Speter
1068251881Speter  change->action = RESTRUCTURE_ADD;
1069251881Speter  change->kind = svn_node_dir;
1070251881Speter  change->deleting = replaces_rev;
1071251881Speter  change->props = svn_prop_hash_dup(props, eb->edit_pool);
1072251881Speter
1073251881Speter  return SVN_NO_ERROR;
1074251881Speter}
1075251881Speter
1076251881Speter/* This implements svn_editor_cb_add_file_t */
1077251881Speterstatic svn_error_t *
1078251881Speteradd_file_cb(void *baton,
1079251881Speter            const char *relpath,
1080251881Speter            const svn_checksum_t *checksum,
1081251881Speter            svn_stream_t *contents,
1082251881Speter            apr_hash_t *props,
1083251881Speter            svn_revnum_t replaces_rev,
1084251881Speter            apr_pool_t *scratch_pool)
1085251881Speter{
1086251881Speter  struct editor_baton *eb = baton;
1087251881Speter  const char *tmp_filename;
1088251881Speter  svn_stream_t *tmp_stream;
1089251881Speter  svn_checksum_t *md5_checksum;
1090251881Speter  struct change_node *change = insert_change(relpath, eb->changes);
1091251881Speter
1092251881Speter  /* We may need to re-checksum these contents */
1093251881Speter  if (!(checksum && checksum->kind == svn_checksum_md5))
1094251881Speter    contents = svn_stream_checksummed2(contents, &md5_checksum, NULL,
1095251881Speter                                       svn_checksum_md5, TRUE, scratch_pool);
1096251881Speter  else
1097251881Speter    md5_checksum = (svn_checksum_t *)checksum;
1098251881Speter
1099251881Speter  /* Spool the contents to a tempfile, and provide that to the driver. */
1100251881Speter  SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL,
1101251881Speter                                 svn_io_file_del_on_pool_cleanup,
1102251881Speter                                 eb->edit_pool, scratch_pool));
1103251881Speter  SVN_ERR(svn_stream_copy3(contents, tmp_stream, NULL, NULL, scratch_pool));
1104251881Speter
1105251881Speter  change->action = RESTRUCTURE_ADD;
1106251881Speter  change->kind = svn_node_file;
1107251881Speter  change->deleting = replaces_rev;
1108251881Speter  change->props = svn_prop_hash_dup(props, eb->edit_pool);
1109251881Speter  change->contents_abspath = tmp_filename;
1110251881Speter  change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool);
1111251881Speter
1112251881Speter  return SVN_NO_ERROR;
1113251881Speter}
1114251881Speter
1115251881Speter/* This implements svn_editor_cb_add_symlink_t */
1116251881Speterstatic svn_error_t *
1117251881Speteradd_symlink_cb(void *baton,
1118251881Speter               const char *relpath,
1119251881Speter               const char *target,
1120251881Speter               apr_hash_t *props,
1121251881Speter               svn_revnum_t replaces_rev,
1122251881Speter               apr_pool_t *scratch_pool)
1123251881Speter{
1124251881Speter#if 0
1125251881Speter  struct editor_baton *eb = baton;
1126251881Speter  struct change_node *change = insert_change(relpath, eb->changes);
1127251881Speter
1128251881Speter  change->action = RESTRUCTURE_ADD;
1129251881Speter  change->kind = svn_node_symlink;
1130251881Speter  change->deleting = replaces_rev;
1131251881Speter  change->props = svn_prop_hash_dup(props, eb->edit_pool);
1132251881Speter  /* ### target  */
1133251881Speter#endif
1134251881Speter
1135251881Speter  SVN__NOT_IMPLEMENTED();
1136251881Speter}
1137251881Speter
1138251881Speter/* This implements svn_editor_cb_add_absent_t */
1139251881Speterstatic svn_error_t *
1140251881Speteradd_absent_cb(void *baton,
1141251881Speter              const char *relpath,
1142251881Speter              svn_node_kind_t kind,
1143251881Speter              svn_revnum_t replaces_rev,
1144251881Speter              apr_pool_t *scratch_pool)
1145251881Speter{
1146251881Speter  struct editor_baton *eb = baton;
1147251881Speter  struct change_node *change = insert_change(relpath, eb->changes);
1148251881Speter
1149251881Speter  change->action = RESTRUCTURE_ADD_ABSENT;
1150251881Speter  change->kind = kind;
1151251881Speter  change->deleting = replaces_rev;
1152251881Speter
1153251881Speter  return SVN_NO_ERROR;
1154251881Speter}
1155251881Speter
1156251881Speter/* This implements svn_editor_cb_alter_directory_t */
1157251881Speterstatic svn_error_t *
1158251881Speteralter_directory_cb(void *baton,
1159251881Speter                   const char *relpath,
1160251881Speter                   svn_revnum_t revision,
1161251881Speter                   const apr_array_header_t *children,
1162251881Speter                   apr_hash_t *props,
1163251881Speter                   apr_pool_t *scratch_pool)
1164251881Speter{
1165251881Speter  struct editor_baton *eb = baton;
1166251881Speter  struct change_node *change = insert_change(relpath, eb->changes);
1167251881Speter
1168251881Speter  /* ### should we verify the kind is truly a directory?  */
1169251881Speter
1170251881Speter  /* ### do we need to do anything with CHILDREN?  */
1171251881Speter
1172251881Speter  /* Note: this node may already have information in CHANGE as a result
1173251881Speter     of an earlier copy/move operation.  */
1174251881Speter  change->kind = svn_node_dir;
1175251881Speter  change->changing = revision;
1176251881Speter  change->props = svn_prop_hash_dup(props, eb->edit_pool);
1177251881Speter
1178251881Speter  return SVN_NO_ERROR;
1179251881Speter}
1180251881Speter
1181251881Speter/* This implements svn_editor_cb_alter_file_t */
1182251881Speterstatic svn_error_t *
1183251881Speteralter_file_cb(void *baton,
1184251881Speter              const char *relpath,
1185251881Speter              svn_revnum_t revision,
1186251881Speter              apr_hash_t *props,
1187251881Speter              const svn_checksum_t *checksum,
1188251881Speter              svn_stream_t *contents,
1189251881Speter              apr_pool_t *scratch_pool)
1190251881Speter{
1191251881Speter  struct editor_baton *eb = baton;
1192251881Speter  const char *tmp_filename;
1193251881Speter  svn_stream_t *tmp_stream;
1194251881Speter  svn_checksum_t *md5_checksum;
1195251881Speter  struct change_node *change = insert_change(relpath, eb->changes);
1196251881Speter
1197251881Speter  /* ### should we verify the kind is truly a file?  */
1198251881Speter
1199251881Speter  if (contents)
1200251881Speter    {
1201251881Speter      /* We may need to re-checksum these contents */
1202251881Speter      if (!(checksum && checksum->kind == svn_checksum_md5))
1203251881Speter        contents = svn_stream_checksummed2(contents, &md5_checksum, NULL,
1204251881Speter                                           svn_checksum_md5, TRUE,
1205251881Speter                                           scratch_pool);
1206251881Speter      else
1207251881Speter        md5_checksum = (svn_checksum_t *)checksum;
1208251881Speter
1209251881Speter      /* Spool the contents to a tempfile, and provide that to the driver. */
1210251881Speter      SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL,
1211251881Speter                                     svn_io_file_del_on_pool_cleanup,
1212251881Speter                                     eb->edit_pool, scratch_pool));
1213251881Speter      SVN_ERR(svn_stream_copy3(contents, tmp_stream, NULL, NULL,
1214251881Speter                               scratch_pool));
1215251881Speter    }
1216251881Speter
1217251881Speter  /* Note: this node may already have information in CHANGE as a result
1218251881Speter     of an earlier copy/move operation.  */
1219251881Speter
1220251881Speter  change->kind = svn_node_file;
1221251881Speter  change->changing = revision;
1222251881Speter  if (props != NULL)
1223251881Speter    change->props = svn_prop_hash_dup(props, eb->edit_pool);
1224251881Speter  if (contents != NULL)
1225251881Speter    {
1226251881Speter      change->contents_abspath = tmp_filename;
1227251881Speter      change->checksum = svn_checksum_dup(md5_checksum, eb->edit_pool);
1228251881Speter    }
1229251881Speter
1230251881Speter  return SVN_NO_ERROR;
1231251881Speter}
1232251881Speter
1233251881Speter/* This implements svn_editor_cb_alter_symlink_t */
1234251881Speterstatic svn_error_t *
1235251881Speteralter_symlink_cb(void *baton,
1236251881Speter                 const char *relpath,
1237251881Speter                 svn_revnum_t revision,
1238251881Speter                 apr_hash_t *props,
1239251881Speter                 const char *target,
1240251881Speter                 apr_pool_t *scratch_pool)
1241251881Speter{
1242251881Speter  /* ### should we verify the kind is truly a symlink?  */
1243251881Speter
1244251881Speter  /* ### do something  */
1245251881Speter
1246251881Speter  SVN__NOT_IMPLEMENTED();
1247251881Speter}
1248251881Speter
1249251881Speter/* This implements svn_editor_cb_delete_t */
1250251881Speterstatic svn_error_t *
1251251881Speterdelete_cb(void *baton,
1252251881Speter          const char *relpath,
1253251881Speter          svn_revnum_t revision,
1254251881Speter          apr_pool_t *scratch_pool)
1255251881Speter{
1256251881Speter  struct editor_baton *eb = baton;
1257251881Speter  struct change_node *change = insert_change(relpath, eb->changes);
1258251881Speter
1259251881Speter  change->action = RESTRUCTURE_DELETE;
1260251881Speter  /* change->kind = svn_node_unknown;  */
1261251881Speter  change->deleting = revision;
1262251881Speter
1263251881Speter  return SVN_NO_ERROR;
1264251881Speter}
1265251881Speter
1266251881Speter/* This implements svn_editor_cb_copy_t */
1267251881Speterstatic svn_error_t *
1268251881Spetercopy_cb(void *baton,
1269251881Speter        const char *src_relpath,
1270251881Speter        svn_revnum_t src_revision,
1271251881Speter        const char *dst_relpath,
1272251881Speter        svn_revnum_t replaces_rev,
1273251881Speter        apr_pool_t *scratch_pool)
1274251881Speter{
1275251881Speter  struct editor_baton *eb = baton;
1276251881Speter  struct change_node *change = insert_change(dst_relpath, eb->changes);
1277251881Speter
1278251881Speter  change->action = RESTRUCTURE_ADD;
1279251881Speter  /* change->kind = svn_node_unknown;  */
1280251881Speter  change->deleting = replaces_rev;
1281251881Speter  change->copyfrom_path = apr_pstrdup(eb->edit_pool, src_relpath);
1282251881Speter  change->copyfrom_rev = src_revision;
1283251881Speter
1284251881Speter  /* We need the source's kind to know whether to call add_directory()
1285251881Speter     or add_file() later on.  */
1286251881Speter  SVN_ERR(eb->fetch_kind_func(&change->kind, eb->fetch_kind_baton,
1287251881Speter                              change->copyfrom_path,
1288251881Speter                              change->copyfrom_rev,
1289251881Speter                              scratch_pool));
1290251881Speter
1291251881Speter  /* Note: this node may later have alter_*() called on it.  */
1292251881Speter
1293251881Speter  return SVN_NO_ERROR;
1294251881Speter}
1295251881Speter
1296251881Speter/* This implements svn_editor_cb_move_t */
1297251881Speterstatic svn_error_t *
1298251881Spetermove_cb(void *baton,
1299251881Speter        const char *src_relpath,
1300251881Speter        svn_revnum_t src_revision,
1301251881Speter        const char *dst_relpath,
1302251881Speter        svn_revnum_t replaces_rev,
1303251881Speter        apr_pool_t *scratch_pool)
1304251881Speter{
1305251881Speter  struct editor_baton *eb = baton;
1306251881Speter  struct change_node *change;
1307251881Speter
1308251881Speter  /* Remap a move into a DELETE + COPY.  */
1309251881Speter
1310251881Speter  change = insert_change(src_relpath, eb->changes);
1311251881Speter  change->action = RESTRUCTURE_DELETE;
1312251881Speter  /* change->kind = svn_node_unknown;  */
1313251881Speter  change->deleting = src_revision;
1314251881Speter
1315251881Speter  change = insert_change(dst_relpath, eb->changes);
1316251881Speter  change->action = RESTRUCTURE_ADD;
1317251881Speter  /* change->kind = svn_node_unknown;  */
1318251881Speter  change->deleting = replaces_rev;
1319251881Speter  change->copyfrom_path = apr_pstrdup(eb->edit_pool, src_relpath);
1320251881Speter  change->copyfrom_rev = src_revision;
1321251881Speter
1322251881Speter  /* We need the source's kind to know whether to call add_directory()
1323251881Speter     or add_file() later on.  */
1324251881Speter  SVN_ERR(eb->fetch_kind_func(&change->kind, eb->fetch_kind_baton,
1325251881Speter                              change->copyfrom_path,
1326251881Speter                              change->copyfrom_rev,
1327251881Speter                              scratch_pool));
1328251881Speter
1329251881Speter  /* Note: this node may later have alter_*() called on it.  */
1330251881Speter
1331251881Speter  return SVN_NO_ERROR;
1332251881Speter}
1333251881Speter
1334251881Speter/* This implements svn_editor_cb_rotate_t */
1335251881Speterstatic svn_error_t *
1336251881Speterrotate_cb(void *baton,
1337251881Speter          const apr_array_header_t *relpaths,
1338251881Speter          const apr_array_header_t *revisions,
1339251881Speter          apr_pool_t *scratch_pool)
1340251881Speter{
1341251881Speter  SVN__NOT_IMPLEMENTED();
1342251881Speter}
1343251881Speter
1344251881Speter
1345251881Speterstatic int
1346251881Spetercount_components(const char *relpath)
1347251881Speter{
1348251881Speter  int count = 1;
1349251881Speter  const char *slash = strchr(relpath, '/');
1350251881Speter
1351251881Speter  while (slash != NULL)
1352251881Speter    {
1353251881Speter      ++count;
1354251881Speter      slash = strchr(slash + 1, '/');
1355251881Speter    }
1356251881Speter  return count;
1357251881Speter}
1358251881Speter
1359251881Speter
1360251881Speterstatic int
1361251881Spetersort_deletes_first(const svn_sort__item_t *item1,
1362251881Speter                   const svn_sort__item_t *item2)
1363251881Speter{
1364251881Speter  const char *relpath1 = item1->key;
1365251881Speter  const char *relpath2 = item2->key;
1366251881Speter  const struct change_node *change1 = item1->value;
1367251881Speter  const struct change_node *change2 = item2->value;
1368251881Speter  const char *slash1;
1369251881Speter  const char *slash2;
1370251881Speter  ptrdiff_t len1;
1371251881Speter  ptrdiff_t len2;
1372251881Speter
1373251881Speter  /* Force the root to always sort first. Otherwise, it may look like a
1374251881Speter     sibling of its children (no slashes), and could get sorted *after*
1375251881Speter     any children that get deleted.  */
1376251881Speter  if (*relpath1 == '\0')
1377251881Speter    return -1;
1378251881Speter  if (*relpath2 == '\0')
1379251881Speter    return 1;
1380251881Speter
1381251881Speter  /* Are these two items siblings? The 'if' statement tests if they are
1382251881Speter     siblings in the root directory, or that slashes were found in both
1383251881Speter     paths, that the length of the paths to those slashes match, and that
1384251881Speter     the path contents up to those slashes also match.  */
1385251881Speter  slash1 = strrchr(relpath1, '/');
1386251881Speter  slash2 = strrchr(relpath2, '/');
1387251881Speter  if ((slash1 == NULL && slash2 == NULL)
1388251881Speter      || (slash1 != NULL
1389251881Speter          && slash2 != NULL
1390251881Speter          && (len1 = slash1 - relpath1) == (len2 = slash2 - relpath2)
1391251881Speter          && memcmp(relpath1, relpath2, len1) == 0))
1392251881Speter    {
1393251881Speter      if (change1->action == RESTRUCTURE_DELETE)
1394251881Speter        {
1395251881Speter          if (change2->action == RESTRUCTURE_DELETE)
1396251881Speter            {
1397251881Speter              /* If both items are being deleted, then we don't care about
1398251881Speter                 the order. State they are equal.  */
1399251881Speter              return 0;
1400251881Speter            }
1401251881Speter
1402251881Speter          /* ITEM1 is being deleted. Sort it before the surviving item.  */
1403251881Speter          return -1;
1404251881Speter        }
1405251881Speter      if (change2->action == RESTRUCTURE_DELETE)
1406251881Speter        /* ITEM2 is being deleted. Sort it before the surviving item.  */
1407251881Speter        return 1;
1408251881Speter
1409251881Speter      /* Normally, we don't care about the ordering of two siblings. However,
1410251881Speter         if these siblings are directories, then we need to provide an
1411251881Speter         ordering so that the quicksort algorithm will further sort them
1412251881Speter         relative to the maybe-directory's children.
1413251881Speter
1414251881Speter         Without this additional ordering, we could see that A/B/E and A/B/F
1415251881Speter         are equal. And then A/B/E/child is sorted before A/B/F. But since
1416251881Speter         E and F are "equal", A/B/E could arrive *after* A/B/F and after the
1417251881Speter         A/B/E/child node.  */
1418251881Speter
1419251881Speter      /* FALLTHROUGH */
1420251881Speter    }
1421251881Speter
1422251881Speter  /* Paths-to-be-deleted with fewer components always sort earlier.
1423251881Speter
1424251881Speter     For example, gamma will sort before E/alpha.
1425251881Speter
1426251881Speter     Without this test, E/alpha lexicographically sorts before gamma,
1427251881Speter     but gamma sorts before E when gamma is to be deleted. This kind of
1428251881Speter     ordering would place E/alpha before E. Not good.
1429251881Speter
1430251881Speter     With this test, gamma sorts before E/alpha. E and E/alpha are then
1431251881Speter     sorted by svn_path_compare_paths() (which places E before E/alpha).  */
1432251881Speter  if (change1->action == RESTRUCTURE_DELETE
1433251881Speter      || change2->action == RESTRUCTURE_DELETE)
1434251881Speter    {
1435251881Speter      int count1 = count_components(relpath1);
1436251881Speter      int count2 = count_components(relpath2);
1437251881Speter
1438251881Speter      if (count1 < count2 && change1->action == RESTRUCTURE_DELETE)
1439251881Speter        return -1;
1440251881Speter      if (count1 > count2 && change2->action == RESTRUCTURE_DELETE)
1441251881Speter        return 1;
1442251881Speter    }
1443251881Speter
1444251881Speter  /* Use svn_path_compare_paths() to get correct depth-based ordering.  */
1445251881Speter  return svn_path_compare_paths(relpath1, relpath2);
1446251881Speter}
1447251881Speter
1448251881Speter
1449251881Speterstatic const apr_array_header_t *
1450251881Speterget_sorted_paths(apr_hash_t *changes,
1451251881Speter                 const char *base_relpath,
1452251881Speter                 apr_pool_t *scratch_pool)
1453251881Speter{
1454251881Speter  const apr_array_header_t *items;
1455251881Speter  apr_array_header_t *paths;
1456251881Speter  int i;
1457251881Speter
1458251881Speter  /* Construct a sorted array of svn_sort__item_t structs. Within a given
1459251881Speter     directory, nodes that are to be deleted will appear first.  */
1460251881Speter  items = svn_sort__hash(changes, sort_deletes_first, scratch_pool);
1461251881Speter
1462251881Speter  /* Build a new array with just the paths, trimmed to relative paths for
1463251881Speter     the Ev1 drive.  */
1464251881Speter  paths = apr_array_make(scratch_pool, items->nelts, sizeof(const char *));
1465251881Speter  for (i = items->nelts; i--; )
1466251881Speter    {
1467251881Speter      const svn_sort__item_t *item;
1468251881Speter
1469251881Speter      item = &APR_ARRAY_IDX(items, i, const svn_sort__item_t);
1470251881Speter      APR_ARRAY_IDX(paths, i, const char *)
1471251881Speter        = svn_relpath_skip_ancestor(base_relpath, item->key);
1472251881Speter    }
1473251881Speter
1474251881Speter  /* We didn't use PUSH, so set the proper number of elements.  */
1475251881Speter  paths->nelts = items->nelts;
1476251881Speter
1477251881Speter  return paths;
1478251881Speter}
1479251881Speter
1480251881Speter
1481251881Speterstatic svn_error_t *
1482251881Speterdrive_ev1_props(const struct editor_baton *eb,
1483251881Speter                const char *repos_relpath,
1484251881Speter                const struct change_node *change,
1485251881Speter                void *node_baton,
1486251881Speter                apr_pool_t *scratch_pool)
1487251881Speter{
1488251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1489251881Speter  apr_hash_t *old_props;
1490251881Speter  apr_array_header_t *propdiffs;
1491251881Speter  int i;
1492251881Speter
1493251881Speter  /* If there are no properties to install, then just exit.  */
1494251881Speter  if (change->props == NULL)
1495251881Speter    return SVN_NO_ERROR;
1496251881Speter
1497251881Speter  if (change->copyfrom_path)
1498251881Speter    {
1499251881Speter      /* The pristine properties are from the copy/move source.  */
1500251881Speter      SVN_ERR(eb->fetch_props_func(&old_props, eb->fetch_props_baton,
1501251881Speter                                   change->copyfrom_path,
1502251881Speter                                   change->copyfrom_rev,
1503251881Speter                                   scratch_pool, iterpool));
1504251881Speter    }
1505251881Speter  else if (change->action == RESTRUCTURE_ADD)
1506251881Speter    {
1507251881Speter      /* Locally-added nodes have no pristine properties.
1508251881Speter
1509251881Speter         Note: we can use iterpool; this hash only needs to survive to
1510251881Speter         the propdiffs call, and there are no contents to preserve.  */
1511251881Speter      old_props = apr_hash_make(iterpool);
1512251881Speter    }
1513251881Speter  else
1514251881Speter    {
1515251881Speter      /* Fetch the pristine properties for whatever we're editing.  */
1516251881Speter      SVN_ERR(eb->fetch_props_func(&old_props, eb->fetch_props_baton,
1517251881Speter                                   repos_relpath, change->changing,
1518251881Speter                                   scratch_pool, iterpool));
1519251881Speter    }
1520251881Speter
1521251881Speter  SVN_ERR(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool));
1522251881Speter
1523251881Speter  for (i = 0; i < propdiffs->nelts; i++)
1524251881Speter    {
1525251881Speter      /* Note: the array returned by svn_prop_diffs() is an array of
1526251881Speter         actual structures, not pointers to them. */
1527251881Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
1528251881Speter
1529251881Speter      svn_pool_clear(iterpool);
1530251881Speter
1531251881Speter      if (change->kind == svn_node_dir)
1532251881Speter        SVN_ERR(eb->deditor->change_dir_prop(node_baton,
1533251881Speter                                             prop->name, prop->value,
1534251881Speter                                             iterpool));
1535251881Speter      else
1536251881Speter        SVN_ERR(eb->deditor->change_file_prop(node_baton,
1537251881Speter                                              prop->name, prop->value,
1538251881Speter                                              iterpool));
1539251881Speter    }
1540251881Speter
1541251881Speter  /* Handle the funky unlock protocol. Note: only possibly on files.  */
1542251881Speter  if (change->unlock)
1543251881Speter    {
1544251881Speter      SVN_ERR_ASSERT(change->kind == svn_node_file);
1545251881Speter      SVN_ERR(eb->deditor->change_file_prop(node_baton,
1546251881Speter                                            SVN_PROP_ENTRY_LOCK_TOKEN, NULL,
1547251881Speter                                            iterpool));
1548251881Speter    }
1549251881Speter
1550251881Speter  svn_pool_destroy(iterpool);
1551251881Speter  return SVN_NO_ERROR;
1552251881Speter}
1553251881Speter
1554251881Speter
1555251881Speter/* Conforms to svn_delta_path_driver_cb_func_t  */
1556251881Speterstatic svn_error_t *
1557251881Speterapply_change(void **dir_baton,
1558251881Speter             void *parent_baton,
1559251881Speter             void *callback_baton,
1560251881Speter             const char *ev1_relpath,
1561251881Speter             apr_pool_t *result_pool)
1562251881Speter{
1563251881Speter  /* ### fix this?  */
1564251881Speter  apr_pool_t *scratch_pool = result_pool;
1565251881Speter  const struct editor_baton *eb = callback_baton;
1566251881Speter  const struct change_node *change;
1567251881Speter  const char *relpath;
1568251881Speter  void *file_baton = NULL;
1569251881Speter
1570251881Speter  /* Typically, we are not creating new directory batons.  */
1571251881Speter  *dir_baton = NULL;
1572251881Speter
1573251881Speter  relpath = svn_relpath_join(eb->base_relpath, ev1_relpath, scratch_pool);
1574251881Speter  change = svn_hash_gets(eb->changes, relpath);
1575251881Speter
1576251881Speter  /* The callback should only be called for paths in CHANGES.  */
1577251881Speter  SVN_ERR_ASSERT(change != NULL);
1578251881Speter
1579251881Speter  /* Are we editing the root of the tree?  */
1580251881Speter  if (parent_baton == NULL)
1581251881Speter    {
1582251881Speter      /* The root was opened in start_edit_func()  */
1583251881Speter      *dir_baton = eb->root.baton;
1584251881Speter
1585251881Speter      /* Only property edits are allowed on the root.  */
1586251881Speter      SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
1587251881Speter      SVN_ERR(drive_ev1_props(eb, relpath, change, *dir_baton, scratch_pool));
1588251881Speter
1589251881Speter      /* No further action possible for the root.  */
1590251881Speter      return SVN_NO_ERROR;
1591251881Speter    }
1592251881Speter
1593251881Speter  if (change->action == RESTRUCTURE_DELETE)
1594251881Speter    {
1595251881Speter      SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting,
1596251881Speter                                        parent_baton, scratch_pool));
1597251881Speter
1598251881Speter      /* No futher action possible for this node.  */
1599251881Speter      return SVN_NO_ERROR;
1600251881Speter    }
1601251881Speter
1602251881Speter  /* If we're not deleting this node, then we should know its kind.  */
1603251881Speter  SVN_ERR_ASSERT(change->kind != svn_node_unknown);
1604251881Speter
1605251881Speter  if (change->action == RESTRUCTURE_ADD_ABSENT)
1606251881Speter    {
1607251881Speter      if (change->kind == svn_node_dir)
1608251881Speter        SVN_ERR(eb->deditor->absent_directory(ev1_relpath, parent_baton,
1609251881Speter                                              scratch_pool));
1610251881Speter      else
1611251881Speter        SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton,
1612251881Speter                                         scratch_pool));
1613251881Speter
1614251881Speter      /* No further action possible for this node.  */
1615251881Speter      return SVN_NO_ERROR;
1616251881Speter    }
1617251881Speter  /* RESTRUCTURE_NONE or RESTRUCTURE_ADD  */
1618251881Speter
1619251881Speter  if (change->action == RESTRUCTURE_ADD)
1620251881Speter    {
1621251881Speter      const char *copyfrom_url = NULL;
1622251881Speter      svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
1623251881Speter
1624251881Speter      /* Do we have an old node to delete first?  */
1625251881Speter      if (SVN_IS_VALID_REVNUM(change->deleting))
1626251881Speter        SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting,
1627251881Speter                                          parent_baton, scratch_pool));
1628251881Speter
1629251881Speter      /* Are we copying the node from somewhere?  */
1630251881Speter      if (change->copyfrom_path)
1631251881Speter        {
1632251881Speter          if (eb->repos_root)
1633251881Speter            copyfrom_url = svn_path_url_add_component2(eb->repos_root,
1634251881Speter                                                       change->copyfrom_path,
1635251881Speter                                                       scratch_pool);
1636251881Speter          else
1637253734Speter            {
1638253734Speter              copyfrom_url = change->copyfrom_path;
1639251881Speter
1640253734Speter              /* Make this an FS path by prepending "/" */
1641253734Speter              if (copyfrom_url[0] != '/')
1642253734Speter                copyfrom_url = apr_pstrcat(scratch_pool, "/",
1643253734Speter                                           copyfrom_url, NULL);
1644253734Speter            }
1645251881Speter
1646251881Speter          copyfrom_rev = change->copyfrom_rev;
1647251881Speter        }
1648251881Speter
1649251881Speter      if (change->kind == svn_node_dir)
1650251881Speter        SVN_ERR(eb->deditor->add_directory(ev1_relpath, parent_baton,
1651251881Speter                                           copyfrom_url, copyfrom_rev,
1652251881Speter                                           result_pool, dir_baton));
1653251881Speter      else
1654251881Speter        SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton,
1655251881Speter                                      copyfrom_url, copyfrom_rev,
1656251881Speter                                      result_pool, &file_baton));
1657251881Speter    }
1658251881Speter  else
1659251881Speter    {
1660251881Speter      if (change->kind == svn_node_dir)
1661251881Speter        SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton,
1662251881Speter                                            change->changing,
1663251881Speter                                            result_pool, dir_baton));
1664251881Speter      else
1665251881Speter        SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton,
1666251881Speter                                       change->changing,
1667251881Speter                                       result_pool, &file_baton));
1668251881Speter    }
1669251881Speter
1670251881Speter  /* Apply any properties in CHANGE to the node.  */
1671251881Speter  if (change->kind == svn_node_dir)
1672251881Speter    SVN_ERR(drive_ev1_props(eb, relpath, change, *dir_baton, scratch_pool));
1673251881Speter  else
1674251881Speter    SVN_ERR(drive_ev1_props(eb, relpath, change, file_baton, scratch_pool));
1675251881Speter
1676251881Speter  if (change->contents_abspath)
1677251881Speter    {
1678251881Speter      svn_txdelta_window_handler_t handler;
1679251881Speter      void *handler_baton;
1680251881Speter      svn_stream_t *contents;
1681251881Speter
1682251881Speter      /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the
1683251881Speter         ### shim code...  */
1684251881Speter      SVN_ERR(eb->deditor->apply_textdelta(file_baton, NULL, scratch_pool,
1685251881Speter                                           &handler, &handler_baton));
1686251881Speter      SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath,
1687251881Speter                                       scratch_pool, scratch_pool));
1688251881Speter      /* ### it would be nice to send a true txdelta here, but whatever.  */
1689251881Speter      SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton,
1690251881Speter                                      NULL, scratch_pool));
1691251881Speter      SVN_ERR(svn_stream_close(contents));
1692251881Speter    }
1693251881Speter
1694251881Speter  if (file_baton)
1695251881Speter    {
1696251881Speter      const char *digest = svn_checksum_to_cstring(change->checksum,
1697251881Speter                                                   scratch_pool);
1698251881Speter
1699251881Speter      SVN_ERR(eb->deditor->close_file(file_baton, digest, scratch_pool));
1700251881Speter    }
1701251881Speter
1702251881Speter  return SVN_NO_ERROR;
1703251881Speter}
1704251881Speter
1705251881Speter
1706251881Speterstatic svn_error_t *
1707251881Speterdrive_changes(const struct editor_baton *eb,
1708251881Speter              apr_pool_t *scratch_pool)
1709251881Speter{
1710251881Speter  struct change_node *change;
1711251881Speter  const apr_array_header_t *paths;
1712251881Speter
1713251881Speter  /* If we never opened a root baton, then the caller aborted the editor
1714251881Speter     before it even began. There is nothing to do. Bail.  */
1715251881Speter  if (eb->root.baton == NULL)
1716251881Speter    return SVN_NO_ERROR;
1717251881Speter
1718251881Speter  /* We need to make the path driver believe we want to make changes to
1719251881Speter     the root. Otherwise, it will attempt an open_root(), which we already
1720251881Speter     did in start_edit_func(). We can forge up a change record, if one
1721251881Speter     does not already exist.  */
1722251881Speter  change = insert_change(eb->base_relpath, eb->changes);
1723251881Speter  change->kind = svn_node_dir;
1724251881Speter  /* No property changes (tho they might exist from a real change).  */
1725251881Speter
1726251881Speter  /* Get a sorted list of Ev1-relative paths.  */
1727251881Speter  paths = get_sorted_paths(eb->changes, eb->base_relpath, scratch_pool);
1728251881Speter  SVN_ERR(svn_delta_path_driver2(eb->deditor, eb->dedit_baton, paths,
1729251881Speter                                 FALSE, apply_change, (void *)eb,
1730251881Speter                                 scratch_pool));
1731251881Speter
1732251881Speter  return SVN_NO_ERROR;
1733251881Speter}
1734251881Speter
1735251881Speter
1736251881Speter/* This implements svn_editor_cb_complete_t */
1737251881Speterstatic svn_error_t *
1738251881Spetercomplete_cb(void *baton,
1739251881Speter            apr_pool_t *scratch_pool)
1740251881Speter{
1741251881Speter  struct editor_baton *eb = baton;
1742251881Speter  svn_error_t *err;
1743251881Speter
1744251881Speter  /* Drive the tree we've created. */
1745251881Speter  err = drive_changes(eb, scratch_pool);
1746251881Speter  if (!err)
1747251881Speter     {
1748251881Speter       err = svn_error_compose_create(err, eb->deditor->close_edit(
1749251881Speter                                                            eb->dedit_baton,
1750251881Speter                                                            scratch_pool));
1751251881Speter     }
1752251881Speter
1753251881Speter  if (err)
1754251881Speter    svn_error_clear(eb->deditor->abort_edit(eb->dedit_baton, scratch_pool));
1755251881Speter
1756251881Speter  return svn_error_trace(err);
1757251881Speter}
1758251881Speter
1759251881Speter/* This implements svn_editor_cb_abort_t */
1760251881Speterstatic svn_error_t *
1761251881Speterabort_cb(void *baton,
1762251881Speter         apr_pool_t *scratch_pool)
1763251881Speter{
1764251881Speter  struct editor_baton *eb = baton;
1765251881Speter  svn_error_t *err;
1766251881Speter  svn_error_t *err2;
1767251881Speter
1768251881Speter  /* We still need to drive anything we collected in the editor to this
1769251881Speter     point. */
1770251881Speter
1771251881Speter  /* Drive the tree we've created. */
1772251881Speter  err = drive_changes(eb, scratch_pool);
1773251881Speter
1774251881Speter  err2 = eb->deditor->abort_edit(eb->dedit_baton, scratch_pool);
1775251881Speter
1776251881Speter  if (err2)
1777251881Speter    {
1778251881Speter      if (err)
1779251881Speter        svn_error_clear(err2);
1780251881Speter      else
1781251881Speter        err = err2;
1782251881Speter    }
1783251881Speter
1784251881Speter  return svn_error_trace(err);
1785251881Speter}
1786251881Speter
1787251881Speterstatic svn_error_t *
1788251881Speterstart_edit_func(void *baton,
1789251881Speter                svn_revnum_t base_revision)
1790251881Speter{
1791251881Speter  struct editor_baton *eb = baton;
1792251881Speter
1793251881Speter  eb->root.base_revision = base_revision;
1794251881Speter
1795251881Speter  /* For some Ev1 editors (such as the repos commit editor), the root must
1796251881Speter     be open before can invoke any callbacks. The open_root() call sets up
1797251881Speter     stuff (eg. open an FS txn) which will be needed.  */
1798251881Speter  SVN_ERR(eb->deditor->open_root(eb->dedit_baton, eb->root.base_revision,
1799251881Speter                                 eb->edit_pool, &eb->root.baton));
1800251881Speter
1801251881Speter  return SVN_NO_ERROR;
1802251881Speter}
1803251881Speter
1804251881Speterstatic svn_error_t *
1805251881Spetertarget_revision_func(void *baton,
1806251881Speter                     svn_revnum_t target_revision,
1807251881Speter                     apr_pool_t *scratch_pool)
1808251881Speter{
1809251881Speter  struct editor_baton *eb = baton;
1810251881Speter
1811251881Speter  SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision,
1812251881Speter                                           scratch_pool));
1813251881Speter
1814251881Speter  return SVN_NO_ERROR;
1815251881Speter}
1816251881Speter
1817251881Speterstatic svn_error_t *
1818251881Speterdo_unlock(void *baton,
1819251881Speter          const char *path,
1820251881Speter          apr_pool_t *scratch_pool)
1821251881Speter{
1822251881Speter  struct editor_baton *eb = baton;
1823251881Speter
1824251881Speter  {
1825251881Speter    /* PATH is REPOS_RELPATH  */
1826251881Speter    struct change_node *change = insert_change(path, eb->changes);
1827251881Speter
1828251881Speter    /* We will need to propagate a deletion of SVN_PROP_ENTRY_LOCK_TOKEN  */
1829251881Speter    change->unlock = TRUE;
1830251881Speter  }
1831251881Speter
1832251881Speter  return SVN_NO_ERROR;
1833251881Speter}
1834251881Speter
1835251881Speter/* Return an svn_editor_t * in EDITOR_P which will drive
1836251881Speter * DEDITOR/DEDIT_BATON.  EDITOR_P is allocated in RESULT_POOL, which may
1837251881Speter * become large and long-lived; SCRATCH_POOL is used for temporary
1838251881Speter * allocations.
1839251881Speter *
1840251881Speter * The other parameters are as follows:
1841251881Speter *  - EXB: An 'extra_baton' used for passing information between the coupled
1842251881Speter *         shims.  This includes actions like 'start edit' and 'set target'.
1843251881Speter *         As this shim receives these actions, it provides the extra baton
1844251881Speter *         to its caller.
1845251881Speter *  - UNLOCK_FUNC / UNLOCK_BATON: A callback / baton pair which a caller
1846251881Speter *         can use to notify this shim that a path should be unlocked (in the
1847251881Speter *         'svn lock' sense).  As this shim receives this action, it provides
1848251881Speter *         this callback / baton to its caller.
1849251881Speter *  - SEND_ABS_PATHS: A pointer which will be set prior to this edit (but
1850251881Speter *         not necessarily at the invocation of editor_from_delta()),and
1851251881Speter *         which indicates whether incoming paths should be expected to
1852251881Speter *         be absolute or relative.
1853251881Speter *  - CANCEL_FUNC / CANCEL_BATON: The usual; folded into the produced editor.
1854251881Speter *  - FETCH_KIND_FUNC / FETCH_KIND_BATON: A callback / baton pair which will
1855251881Speter *         be used by the shim handlers if they need to determine the kind of
1856251881Speter *         a path.
1857251881Speter *  - FETCH_PROPS_FUNC / FETCH_PROPS_BATON: A callback / baton pair which
1858251881Speter *         will be used by the shim handlers if they need to determine the
1859251881Speter *         existing properties on a path.
1860251881Speter */
1861251881Spetersvn_error_t *
1862251881Spetersvn_delta__editor_from_delta(svn_editor_t **editor_p,
1863251881Speter                  struct svn_delta__extra_baton **exb,
1864251881Speter                  svn_delta__unlock_func_t *unlock_func,
1865251881Speter                  void **unlock_baton,
1866251881Speter                  const svn_delta_editor_t *deditor,
1867251881Speter                  void *dedit_baton,
1868251881Speter                  svn_boolean_t *send_abs_paths,
1869251881Speter                  const char *repos_root,
1870251881Speter                  const char *base_relpath,
1871251881Speter                  svn_cancel_func_t cancel_func,
1872251881Speter                  void *cancel_baton,
1873251881Speter                  svn_delta_fetch_kind_func_t fetch_kind_func,
1874251881Speter                  void *fetch_kind_baton,
1875251881Speter                  svn_delta_fetch_props_func_t fetch_props_func,
1876251881Speter                  void *fetch_props_baton,
1877251881Speter                  apr_pool_t *result_pool,
1878251881Speter                  apr_pool_t *scratch_pool)
1879251881Speter{
1880251881Speter  svn_editor_t *editor;
1881251881Speter  static const svn_editor_cb_many_t editor_cbs = {
1882251881Speter      add_directory_cb,
1883251881Speter      add_file_cb,
1884251881Speter      add_symlink_cb,
1885251881Speter      add_absent_cb,
1886251881Speter      alter_directory_cb,
1887251881Speter      alter_file_cb,
1888251881Speter      alter_symlink_cb,
1889251881Speter      delete_cb,
1890251881Speter      copy_cb,
1891251881Speter      move_cb,
1892251881Speter      rotate_cb,
1893251881Speter      complete_cb,
1894251881Speter      abort_cb
1895251881Speter    };
1896251881Speter  struct editor_baton *eb = apr_pcalloc(result_pool, sizeof(*eb));
1897251881Speter  struct svn_delta__extra_baton *extra_baton = apr_pcalloc(result_pool,
1898251881Speter                                                sizeof(*extra_baton));
1899251881Speter
1900251881Speter  if (!base_relpath)
1901251881Speter    base_relpath = "";
1902251881Speter  else if (base_relpath[0] == '/')
1903251881Speter    base_relpath += 1;
1904251881Speter
1905251881Speter  eb->deditor = deditor;
1906251881Speter  eb->dedit_baton = dedit_baton;
1907251881Speter  eb->edit_pool = result_pool;
1908251881Speter  eb->repos_root = apr_pstrdup(result_pool, repos_root);
1909251881Speter  eb->base_relpath = apr_pstrdup(result_pool, base_relpath);
1910251881Speter
1911251881Speter  eb->changes = apr_hash_make(result_pool);
1912251881Speter
1913251881Speter  eb->fetch_kind_func = fetch_kind_func;
1914251881Speter  eb->fetch_kind_baton = fetch_kind_baton;
1915251881Speter  eb->fetch_props_func = fetch_props_func;
1916251881Speter  eb->fetch_props_baton = fetch_props_baton;
1917251881Speter
1918251881Speter  eb->root.base_revision = SVN_INVALID_REVNUM;
1919251881Speter
1920251881Speter  eb->make_abs_paths = send_abs_paths;
1921251881Speter
1922251881Speter  SVN_ERR(svn_editor_create(&editor, eb, cancel_func, cancel_baton,
1923251881Speter                            result_pool, scratch_pool));
1924251881Speter  SVN_ERR(svn_editor_setcb_many(editor, &editor_cbs, scratch_pool));
1925251881Speter
1926251881Speter  *editor_p = editor;
1927251881Speter
1928251881Speter  *unlock_func = do_unlock;
1929251881Speter  *unlock_baton = eb;
1930251881Speter
1931251881Speter  extra_baton->start_edit = start_edit_func;
1932251881Speter  extra_baton->target_revision = target_revision_func;
1933251881Speter  extra_baton->baton = eb;
1934251881Speter
1935251881Speter  *exb = extra_baton;
1936251881Speter
1937251881Speter  return SVN_NO_ERROR;
1938251881Speter}
1939251881Speter
1940251881Spetersvn_delta_shim_callbacks_t *
1941251881Spetersvn_delta_shim_callbacks_default(apr_pool_t *result_pool)
1942251881Speter{
1943251881Speter  svn_delta_shim_callbacks_t *shim_callbacks = apr_pcalloc(result_pool,
1944251881Speter                                                     sizeof(*shim_callbacks));
1945251881Speter  return shim_callbacks;
1946251881Speter}
1947251881Speter
1948251881Speter/* To enable editor shims throughout Subversion, ENABLE_EV2_SHIMS should be
1949251881Speter * defined.  This can be done manually, or by providing `--enable-ev2-shims'
1950251881Speter * to `configure'.  */
1951251881Speter
1952251881Spetersvn_error_t *
1953251881Spetersvn_editor__insert_shims(const svn_delta_editor_t **deditor_out,
1954251881Speter                         void **dedit_baton_out,
1955251881Speter                         const svn_delta_editor_t *deditor_in,
1956251881Speter                         void *dedit_baton_in,
1957251881Speter                         const char *repos_root,
1958251881Speter                         const char *base_relpath,
1959251881Speter                         svn_delta_shim_callbacks_t *shim_callbacks,
1960251881Speter                         apr_pool_t *result_pool,
1961251881Speter                         apr_pool_t *scratch_pool)
1962251881Speter{
1963251881Speter#ifndef ENABLE_EV2_SHIMS
1964251881Speter  /* Shims disabled, just copy the editor and baton directly. */
1965251881Speter  *deditor_out = deditor_in;
1966251881Speter  *dedit_baton_out = dedit_baton_in;
1967251881Speter#else
1968251881Speter  /* Use our shim APIs to create an intermediate svn_editor_t, and then
1969251881Speter     wrap that again back into a svn_delta_editor_t.  This introduces
1970251881Speter     a lot of overhead. */
1971251881Speter  svn_editor_t *editor;
1972251881Speter
1973251881Speter  /* The "extra baton" is a set of functions and a baton which allows the
1974251881Speter     shims to communicate additional events to each other.
1975251881Speter     svn_delta__editor_from_delta() returns a pointer to this baton, which
1976251881Speter     svn_delta__delta_from_editor() should then store. */
1977251881Speter  struct svn_delta__extra_baton *exb;
1978251881Speter
1979251881Speter  /* The reason this is a pointer is that we don't know the appropriate
1980251881Speter     value until we start receiving paths.  So process_actions() sets the
1981251881Speter     flag, which drive_tree() later consumes. */
1982251881Speter  svn_boolean_t *found_abs_paths = apr_palloc(result_pool,
1983251881Speter                                              sizeof(*found_abs_paths));
1984251881Speter
1985251881Speter  svn_delta__unlock_func_t unlock_func;
1986251881Speter  void *unlock_baton;
1987251881Speter
1988251881Speter  SVN_ERR_ASSERT(shim_callbacks->fetch_kind_func != NULL);
1989251881Speter  SVN_ERR_ASSERT(shim_callbacks->fetch_props_func != NULL);
1990251881Speter  SVN_ERR_ASSERT(shim_callbacks->fetch_base_func != NULL);
1991251881Speter
1992251881Speter  SVN_ERR(svn_delta__editor_from_delta(&editor, &exb,
1993251881Speter                            &unlock_func, &unlock_baton,
1994251881Speter                            deditor_in, dedit_baton_in,
1995251881Speter                            found_abs_paths, repos_root, base_relpath,
1996251881Speter                            NULL, NULL,
1997251881Speter                            shim_callbacks->fetch_kind_func,
1998251881Speter                            shim_callbacks->fetch_baton,
1999251881Speter                            shim_callbacks->fetch_props_func,
2000251881Speter                            shim_callbacks->fetch_baton,
2001251881Speter                            result_pool, scratch_pool));
2002251881Speter  SVN_ERR(svn_delta__delta_from_editor(deditor_out, dedit_baton_out, editor,
2003251881Speter                            unlock_func, unlock_baton,
2004251881Speter                            found_abs_paths,
2005251881Speter                            repos_root, base_relpath,
2006251881Speter                            shim_callbacks->fetch_props_func,
2007251881Speter                            shim_callbacks->fetch_baton,
2008251881Speter                            shim_callbacks->fetch_base_func,
2009251881Speter                            shim_callbacks->fetch_baton,
2010251881Speter                            exb, result_pool));
2011251881Speter
2012251881Speter#endif
2013251881Speter  return SVN_NO_ERROR;
2014251881Speter}
2015