]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_delta/path_driver.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_delta / path_driver.c
1 /*
2  * path_driver.c -- drive an editor across a set of paths
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 #include <apr_pools.h>
26 #include <apr_strings.h>
27
28 #include "svn_types.h"
29 #include "svn_delta.h"
30 #include "svn_pools.h"
31 #include "svn_dirent_uri.h"
32 #include "svn_path.h"
33 #include "svn_sorts.h"
34 #include "private/svn_fspath.h"
35 #include "private/svn_sorts_private.h"
36
37 \f
38 /*** Helper functions. ***/
39
40 typedef struct dir_stack_t
41 {
42   void *dir_baton;   /* the dir baton. */
43   apr_pool_t *pool;  /* the pool associated with the dir baton. */
44
45 } dir_stack_t;
46
47
48 /* Call EDITOR's open_directory() function with the PATH argument, then
49  * add the resulting dir baton to the dir baton stack.
50  */
51 static svn_error_t *
52 open_dir(apr_array_header_t *db_stack,
53          const svn_delta_editor_t *editor,
54          const char *path,
55          apr_pool_t *pool)
56 {
57   void *parent_db, *db;
58   dir_stack_t *item;
59   apr_pool_t *subpool;
60
61   /* Assert that we are in a stable state. */
62   SVN_ERR_ASSERT(db_stack && db_stack->nelts);
63
64   /* Get the parent dir baton. */
65   item = APR_ARRAY_IDX(db_stack, db_stack->nelts - 1, void *);
66   parent_db = item->dir_baton;
67
68   /* Call the EDITOR's open_directory function to get a new directory
69      baton. */
70   subpool = svn_pool_create(pool);
71   SVN_ERR(editor->open_directory(path, parent_db, SVN_INVALID_REVNUM, subpool,
72                                  &db));
73
74   /* Now add the dir baton to the stack. */
75   item = apr_pcalloc(subpool, sizeof(*item));
76   item->dir_baton = db;
77   item->pool = subpool;
78   APR_ARRAY_PUSH(db_stack, dir_stack_t *) = item;
79
80   return SVN_NO_ERROR;
81 }
82
83
84 /* Pop a directory from the dir baton stack and update the stack
85  * pointer.
86  *
87  * This function calls the EDITOR's close_directory() function.
88  */
89 static svn_error_t *
90 pop_stack(apr_array_header_t *db_stack,
91           const svn_delta_editor_t *editor)
92 {
93   dir_stack_t *item;
94
95   /* Assert that we are in a stable state. */
96   SVN_ERR_ASSERT(db_stack && db_stack->nelts);
97
98   /* Close the most recent directory pushed to the stack. */
99   item = APR_ARRAY_IDX(db_stack, db_stack->nelts - 1, dir_stack_t *);
100   (void) apr_array_pop(db_stack);
101   SVN_ERR(editor->close_directory(item->dir_baton, item->pool));
102   svn_pool_destroy(item->pool);
103
104   return SVN_NO_ERROR;
105 }
106
107
108 /* Count the number of path components in PATH. */
109 static int
110 count_components(const char *path)
111 {
112   int count = 1;
113   const char *instance = path;
114
115   if ((strlen(path) == 1) && (path[0] == '/'))
116     return 0;
117
118   do
119     {
120       instance++;
121       instance = strchr(instance, '/');
122       if (instance)
123         count++;
124     }
125   while (instance);
126
127   return count;
128 }
129
130
131 \f
132 /*** Public interfaces ***/
133 svn_error_t *
134 svn_delta_path_driver2(const svn_delta_editor_t *editor,
135                        void *edit_baton,
136                        const apr_array_header_t *paths,
137                        svn_boolean_t sort_paths,
138                        svn_delta_path_driver_cb_func_t callback_func,
139                        void *callback_baton,
140                        apr_pool_t *pool)
141 {
142   apr_array_header_t *db_stack = apr_array_make(pool, 4, sizeof(void *));
143   const char *last_path = NULL;
144   int i = 0;
145   void *parent_db = NULL, *db = NULL;
146   const char *path;
147   apr_pool_t *subpool, *iterpool;
148   dir_stack_t *item;
149
150   /* Do nothing if there are no paths. */
151   if (! paths->nelts)
152     return SVN_NO_ERROR;
153
154   subpool = svn_pool_create(pool);
155   iterpool = svn_pool_create(pool);
156
157   /* sort paths if necessary */
158   if (sort_paths && paths->nelts > 1)
159     {
160       apr_array_header_t *sorted = apr_array_copy(subpool, paths);
161       svn_sort__array(sorted, svn_sort_compare_paths);
162       paths = sorted;
163     }
164
165   item = apr_pcalloc(subpool, sizeof(*item));
166
167   /* If the root of the edit is also a target path, we want to call
168      the callback function to let the user open the root directory and
169      do what needs to be done.  Otherwise, we'll do the open_root()
170      ourselves. */
171   path = APR_ARRAY_IDX(paths, 0, const char *);
172   if (svn_path_is_empty(path))
173     {
174       SVN_ERR(callback_func(&db, NULL, callback_baton, path, subpool));
175       last_path = path;
176       i++;
177     }
178   else
179     {
180       SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, subpool, &db));
181     }
182   item->pool = subpool;
183   item->dir_baton = db;
184   APR_ARRAY_PUSH(db_stack, void *) = item;
185
186   /* Now, loop over the commit items, traversing the URL tree and
187      driving the editor. */
188   for (; i < paths->nelts; i++)
189     {
190       const char *pdir;
191       const char *common = "";
192       size_t common_len;
193
194       /* Clear the iteration pool. */
195       svn_pool_clear(iterpool);
196
197       /* Get the next path. */
198       path = APR_ARRAY_IDX(paths, i, const char *);
199
200       /*** Step A - Find the common ancestor of the last path and the
201            current one.  For the first iteration, this is just the
202            empty string. ***/
203       if (i > 0)
204         common = (last_path[0] == '/')
205           ? svn_fspath__get_longest_ancestor(last_path, path, iterpool)
206           : svn_relpath_get_longest_ancestor(last_path, path, iterpool);
207       common_len = strlen(common);
208
209       /*** Step B - Close any directories between the last path and
210            the new common ancestor, if any need to be closed.
211            Sometimes there is nothing to do here (like, for the first
212            iteration, or when the last path was an ancestor of the
213            current one). ***/
214       if ((i > 0) && (strlen(last_path) > common_len))
215         {
216           const char *rel = last_path + (common_len ? (common_len + 1) : 0);
217           int count = count_components(rel);
218           while (count--)
219             {
220               SVN_ERR(pop_stack(db_stack, editor));
221             }
222         }
223
224       /*** Step C - Open any directories between the common ancestor
225            and the parent of the current path. ***/
226       if (*path == '/')
227         pdir = svn_fspath__dirname(path, iterpool);
228       else
229         pdir = svn_relpath_dirname(path, iterpool);
230
231       if (strlen(pdir) > common_len)
232         {
233           const char *piece = pdir + common_len + 1;
234
235           while (1)
236             {
237               const char *rel = pdir;
238
239               /* Find the first separator. */
240               piece = strchr(piece, '/');
241
242               /* Calculate REL as the portion of PDIR up to (but not
243                  including) the location to which PIECE is pointing. */
244               if (piece)
245                 rel = apr_pstrmemdup(iterpool, pdir, piece - pdir);
246
247               /* Open the subdirectory. */
248               SVN_ERR(open_dir(db_stack, editor, rel, pool));
249
250               /* If we found a '/', advance our PIECE pointer to
251                  character just after that '/'.  Otherwise, we're
252                  done.  */
253               if (piece)
254                 piece++;
255               else
256                 break;
257             }
258         }
259
260       /*** Step D - Tell our caller to handle the current path. ***/
261       item = APR_ARRAY_IDX(db_stack, db_stack->nelts - 1, void *);
262       parent_db = item->dir_baton;
263       subpool = svn_pool_create(pool);
264       SVN_ERR(callback_func(&db, parent_db, callback_baton, path, subpool));
265       if (db)
266         {
267           item = apr_pcalloc(subpool, sizeof(*item));
268           item->dir_baton = db;
269           item->pool = subpool;
270           APR_ARRAY_PUSH(db_stack, void *) = item;
271         }
272       else
273         {
274           svn_pool_destroy(subpool);
275         }
276
277       /*** Step E - Save our state for the next iteration.  If our
278            caller opened or added PATH as a directory, that becomes
279            our LAST_PATH.  Otherwise, we use PATH's parent
280            directory. ***/
281
282       /* NOTE:  The variable LAST_PATH needs to outlive the loop. */
283       if (db)
284         last_path = path; /* lives in a pool outside our control. */
285       else
286         last_path = apr_pstrdup(pool, pdir); /* duping into POOL. */
287     }
288
289   /* Destroy the iteration subpool. */
290   svn_pool_destroy(iterpool);
291
292   /* Close down any remaining open directory batons. */
293   while (db_stack->nelts)
294     {
295       SVN_ERR(pop_stack(db_stack, editor));
296     }
297
298   return SVN_NO_ERROR;
299 }