]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_delta/depth_filter_editor.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_delta / depth_filter_editor.c
1 /*
2  * depth_filter_editor.c -- provide a svn_delta_editor_t which wraps
3  *                          another editor and provides depth-based filtering
4  *
5  * ====================================================================
6  *    Licensed to the Apache Software Foundation (ASF) under one
7  *    or more contributor license agreements.  See the NOTICE file
8  *    distributed with this work for additional information
9  *    regarding copyright ownership.  The ASF licenses this file
10  *    to you under the Apache License, Version 2.0 (the
11  *    "License"); you may not use this file except in compliance
12  *    with the License.  You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  *    Unless required by applicable law or agreed to in writing,
17  *    software distributed under the License is distributed on an
18  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19  *    KIND, either express or implied.  See the License for the
20  *    specific language governing permissions and limitations
21  *    under the License.
22  * ====================================================================
23  */
24
25 #include "svn_delta.h"
26
27 \f
28 /*** Batons, and the Toys That Create Them ***/
29
30 struct edit_baton
31 {
32   /* The editor/baton we're wrapping. */
33   const svn_delta_editor_t *wrapped_editor;
34   void *wrapped_edit_baton;
35
36   /* The depth to which we are limiting the drive of the wrapped
37      editor/baton. */
38   svn_depth_t requested_depth;
39
40   /* Does the wrapped editor/baton have an explicit target (in the
41      anchor/target sense of the word)? */
42   svn_boolean_t has_target;
43 };
44
45 struct node_baton
46 {
47   /* TRUE iff this node was filtered out -- that is, not allowed to
48      pass through to the wrapped editor -- by virtue of not appearing
49      at a depth in the tree that was "inside" the requested depth.  Of
50      course, any children of this node will be deeper still, and so
51      will also be filtered out for the same reason. */
52   svn_boolean_t filtered;
53
54   /* Pointer to the edit_baton. */
55   void *edit_baton;
56
57   /* The real node baton we're wrapping.  May be a directory or file
58      baton; we don't care. */
59   void *wrapped_baton;
60
61   /* The calculated depth (in terms of counted, stacked, integral
62      deepnesses) of this node.  If the node is a directory, this value
63      is 1 greater than the value of the same on its parent directory;
64      if a file, it is equal to its parent directory's depth value. */
65   int dir_depth;
66 };
67
68 /* Allocate and return a new node_baton structure, populated via the
69    the input to this helper function. */
70 static struct node_baton *
71 make_node_baton(void *edit_baton,
72                 svn_boolean_t filtered,
73                 int dir_depth,
74                 apr_pool_t *pool)
75 {
76   struct node_baton *b = apr_palloc(pool, sizeof(*b));
77   b->edit_baton = edit_baton;
78   b->wrapped_baton = NULL;
79   b->filtered = filtered;
80   b->dir_depth = dir_depth;
81   return b;
82 }
83
84 /* Return TRUE iff changes to immediate children of the directory
85    identified by PB, when those children are of node kind KIND, are
86    allowed by the requested depth which this editor is trying to
87    preserve.  EB is the edit baton.  */
88 static svn_boolean_t
89 okay_to_edit(struct edit_baton *eb,
90              struct node_baton *pb,
91              svn_node_kind_t kind)
92 {
93   int effective_depth;
94
95   /* If we've already filter out the parent directory, we necessarily
96      are filtering out its children, too.  */
97   if (pb->filtered)
98     return FALSE;
99
100   /* Calculate the effective depth of the parent directory.
101
102      NOTE:  "Depth" in this sense is not the same as the Subversion
103      magic depth keywords.  Here, we're talking about a literal,
104      integral, stacked depth of directories.
105
106      The root of the edit is generally depth=1, subdirectories thereof
107      depth=2, and so on.  But if we have an edit target -- which means
108      that the real target of the edit operation isn't the root
109      directory, but is instead some immediate child thereof -- we have
110      to adjust our calculated effected depth such that the target
111      itself is depth=1 (as are its siblings, which we trust aren't
112      present in the edit at all), immediate subdirectories thereof are
113      depth=2, and so on.
114   */
115   effective_depth = pb->dir_depth - (eb->has_target ? 1 : 0);
116   switch (eb->requested_depth)
117     {
118     case svn_depth_empty:
119       return (effective_depth <= 0);
120     case svn_depth_files:
121       return ((effective_depth <= 0)
122               || (kind == svn_node_file && effective_depth == 1));
123     case svn_depth_immediates:
124       return (effective_depth <= 1);
125     case svn_depth_unknown:
126     case svn_depth_exclude:
127     case svn_depth_infinity:
128       /* Shouldn't reach; see svn_delta_depth_filter_editor() */
129     default:
130       SVN_ERR_MALFUNCTION_NO_RETURN();
131     }
132 }
133
134 \f
135 /*** Editor Functions ***/
136
137 static svn_error_t *
138 set_target_revision(void *edit_baton,
139                     svn_revnum_t target_revision,
140                     apr_pool_t *pool)
141 {
142   struct edit_baton *eb = edit_baton;
143
144   /* Nothing depth-y to filter here. */
145  return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
146                                                 target_revision, pool);
147 }
148
149 static svn_error_t *
150 open_root(void *edit_baton,
151           svn_revnum_t base_revision,
152           apr_pool_t *pool,
153           void **root_baton)
154 {
155   struct edit_baton *eb = edit_baton;
156   struct node_baton *b;
157
158   /* The root node always gets through cleanly. */
159   b = make_node_baton(edit_baton, FALSE, 1, pool);
160   SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, base_revision,
161                                         pool, &b->wrapped_baton));
162
163   *root_baton = b;
164   return SVN_NO_ERROR;
165 }
166
167 static svn_error_t *
168 delete_entry(const char *path,
169              svn_revnum_t base_revision,
170              void *parent_baton,
171              apr_pool_t *pool)
172 {
173   struct node_baton *pb = parent_baton;
174   struct edit_baton *eb = pb->edit_baton;
175
176   /* ### FIXME: We don't know the type of the entry, which ordinarily
177      doesn't matter, but is a key (*the* key, in fact) distinction
178      between depth "files" and depths "immediates".  If the server is
179      telling us to delete a subdirectory and our requested depth was
180      "immediates", that's fine; if our requested depth was "files",
181      though, this deletion shouldn't survive filtering.  For now,
182      we'll claim to our helper function that the to-be-deleted thing
183      is a file because that's the conservative route to take. */
184   if (okay_to_edit(eb, pb, svn_node_file))
185     SVN_ERR(eb->wrapped_editor->delete_entry(path, base_revision,
186                                              pb->wrapped_baton, pool));
187
188   return SVN_NO_ERROR;
189 }
190
191 static svn_error_t *
192 add_directory(const char *path,
193               void *parent_baton,
194               const char *copyfrom_path,
195               svn_revnum_t copyfrom_revision,
196               apr_pool_t *pool,
197               void **child_baton)
198 {
199   struct node_baton *pb = parent_baton;
200   struct edit_baton *eb = pb->edit_baton;
201   struct node_baton *b = NULL;
202
203   /* Check for sufficient depth. */
204   if (okay_to_edit(eb, pb, svn_node_dir))
205     {
206       b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool);
207       SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_baton,
208                                                 copyfrom_path,
209                                                 copyfrom_revision,
210                                                 pool, &b->wrapped_baton));
211     }
212   else
213     {
214       b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool);
215     }
216
217   *child_baton = b;
218   return SVN_NO_ERROR;
219 }
220
221 static svn_error_t *
222 open_directory(const char *path,
223                void *parent_baton,
224                svn_revnum_t base_revision,
225                apr_pool_t *pool,
226                void **child_baton)
227 {
228   struct node_baton *pb = parent_baton;
229   struct edit_baton *eb = pb->edit_baton;
230   struct node_baton *b;
231
232   /* Check for sufficient depth. */
233   if (okay_to_edit(eb, pb, svn_node_dir))
234     {
235       b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool);
236       SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_baton,
237                                                  base_revision, pool,
238                                                  &b->wrapped_baton));
239     }
240   else
241     {
242       b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool);
243     }
244
245   *child_baton = b;
246   return SVN_NO_ERROR;
247 }
248
249 static svn_error_t *
250 add_file(const char *path,
251          void *parent_baton,
252          const char *copyfrom_path,
253          svn_revnum_t copyfrom_revision,
254          apr_pool_t *pool,
255          void **child_baton)
256 {
257   struct node_baton *pb = parent_baton;
258   struct edit_baton *eb = pb->edit_baton;
259   struct node_baton *b = NULL;
260
261   /* Check for sufficient depth. */
262   if (okay_to_edit(eb, pb, svn_node_file))
263     {
264       b = make_node_baton(eb, FALSE, pb->dir_depth, pool);
265       SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_baton,
266                                            copyfrom_path, copyfrom_revision,
267                                            pool, &b->wrapped_baton));
268     }
269   else
270     {
271       b = make_node_baton(eb, TRUE, pb->dir_depth, pool);
272     }
273
274   *child_baton = b;
275   return SVN_NO_ERROR;
276 }
277
278 static svn_error_t *
279 open_file(const char *path,
280           void *parent_baton,
281           svn_revnum_t base_revision,
282           apr_pool_t *pool,
283           void **child_baton)
284 {
285   struct node_baton *pb = parent_baton;
286   struct edit_baton *eb = pb->edit_baton;
287   struct node_baton *b;
288
289   /* Check for sufficient depth. */
290   if (okay_to_edit(eb, pb, svn_node_file))
291     {
292       b = make_node_baton(eb, FALSE, pb->dir_depth, pool);
293       SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_baton,
294                                             base_revision, pool,
295                                             &b->wrapped_baton));
296     }
297   else
298     {
299       b = make_node_baton(eb, TRUE, pb->dir_depth, pool);
300     }
301
302   *child_baton = b;
303   return SVN_NO_ERROR;
304 }
305
306 static svn_error_t *
307 apply_textdelta(void *file_baton,
308                 const char *base_checksum,
309                 apr_pool_t *pool,
310                 svn_txdelta_window_handler_t *handler,
311                 void **handler_baton)
312 {
313   struct node_baton *fb = file_baton;
314   struct edit_baton *eb = fb->edit_baton;
315
316   /* For filtered files, we just consume the textdelta. */
317   if (fb->filtered)
318     {
319       *handler = svn_delta_noop_window_handler;
320       *handler_baton = NULL;
321     }
322   else
323     {
324       SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_baton,
325                                                   base_checksum, pool,
326                                                   handler, handler_baton));
327     }
328   return SVN_NO_ERROR;
329 }
330
331 static svn_error_t *
332 close_file(void *file_baton,
333            const char *text_checksum,
334            apr_pool_t *pool)
335 {
336   struct node_baton *fb = file_baton;
337   struct edit_baton *eb = fb->edit_baton;
338
339   /* Don't close filtered files. */
340   if (! fb->filtered)
341     SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_baton,
342                                            text_checksum, pool));
343
344   return SVN_NO_ERROR;
345 }
346
347 static svn_error_t *
348 absent_file(const char *path,
349             void *parent_baton,
350             apr_pool_t *pool)
351 {
352   struct node_baton *pb = parent_baton;
353   struct edit_baton *eb = pb->edit_baton;
354
355   /* Don't report absent items in filtered directories. */
356   if (! pb->filtered)
357     SVN_ERR(eb->wrapped_editor->absent_file(path, pb->wrapped_baton, pool));
358
359   return SVN_NO_ERROR;
360 }
361
362 static svn_error_t *
363 close_directory(void *dir_baton,
364                 apr_pool_t *pool)
365 {
366   struct node_baton *db = dir_baton;
367   struct edit_baton *eb = db->edit_baton;
368
369   /* Don't close filtered directories. */
370   if (! db->filtered)
371     SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_baton, pool));
372
373   return SVN_NO_ERROR;
374 }
375
376 static svn_error_t *
377 absent_directory(const char *path,
378                  void *parent_baton,
379                  apr_pool_t *pool)
380 {
381   struct node_baton *pb = parent_baton;
382   struct edit_baton *eb = pb->edit_baton;
383
384   /* Don't report absent items in filtered directories. */
385   if (! pb->filtered)
386     SVN_ERR(eb->wrapped_editor->absent_directory(path, pb->wrapped_baton,
387                                                  pool));
388
389   return SVN_NO_ERROR;
390 }
391
392 static svn_error_t *
393 change_file_prop(void *file_baton,
394                  const char *name,
395                  const svn_string_t *value,
396                  apr_pool_t *pool)
397 {
398   struct node_baton *fb = file_baton;
399   struct edit_baton *eb = fb->edit_baton;
400
401   /* No propchanges on filtered files. */
402   if (! fb->filtered)
403     SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_baton,
404                                                  name, value, pool));
405
406   return SVN_NO_ERROR;
407 }
408
409 static svn_error_t *
410 change_dir_prop(void *dir_baton,
411                 const char *name,
412                 const svn_string_t *value,
413                 apr_pool_t *pool)
414 {
415   struct node_baton *db = dir_baton;
416   struct edit_baton *eb = db->edit_baton;
417
418   /* No propchanges on filtered nodes. */
419   if (! db->filtered)
420     SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_baton,
421                                                 name, value, pool));
422
423   return SVN_NO_ERROR;
424 }
425
426 static svn_error_t *
427 close_edit(void *edit_baton,
428            apr_pool_t *pool)
429 {
430   struct edit_baton *eb = edit_baton;
431   return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool);
432 }
433
434 svn_error_t *
435 svn_delta_depth_filter_editor(const svn_delta_editor_t **editor,
436                               void **edit_baton,
437                               const svn_delta_editor_t *wrapped_editor,
438                               void *wrapped_edit_baton,
439                               svn_depth_t requested_depth,
440                               svn_boolean_t has_target,
441                               apr_pool_t *pool)
442 {
443   svn_delta_editor_t *depth_filter_editor;
444   struct edit_baton *eb;
445
446   /* Easy out: if the caller wants infinite depth, there's nothing to
447      filter, so just return the editor we were supposed to wrap.  And
448      if they've asked for an unknown depth, we can't possibly know
449      what that means, so why bother?  */
450   if ((requested_depth == svn_depth_unknown)
451       || (requested_depth == svn_depth_infinity))
452     {
453       *editor = wrapped_editor;
454       *edit_baton = wrapped_edit_baton;
455       return SVN_NO_ERROR;
456     }
457
458   depth_filter_editor = svn_delta_default_editor(pool);
459   depth_filter_editor->set_target_revision = set_target_revision;
460   depth_filter_editor->open_root = open_root;
461   depth_filter_editor->delete_entry = delete_entry;
462   depth_filter_editor->add_directory = add_directory;
463   depth_filter_editor->open_directory = open_directory;
464   depth_filter_editor->change_dir_prop = change_dir_prop;
465   depth_filter_editor->close_directory = close_directory;
466   depth_filter_editor->absent_directory = absent_directory;
467   depth_filter_editor->add_file = add_file;
468   depth_filter_editor->open_file = open_file;
469   depth_filter_editor->apply_textdelta = apply_textdelta;
470   depth_filter_editor->change_file_prop = change_file_prop;
471   depth_filter_editor->close_file = close_file;
472   depth_filter_editor->absent_file = absent_file;
473   depth_filter_editor->close_edit = close_edit;
474
475   eb = apr_palloc(pool, sizeof(*eb));
476   eb->wrapped_editor = wrapped_editor;
477   eb->wrapped_edit_baton = wrapped_edit_baton;
478   eb->has_target = has_target;
479   eb->requested_depth = requested_depth;
480
481   *editor = depth_filter_editor;
482   *edit_baton = eb;
483
484   return SVN_NO_ERROR;
485 }