~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_delta/path_driver.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * path_driver.c -- drive an editor across a set of paths
 
3
 * 
 
4
 * ====================================================================
 
5
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
6
 *
 
7
 * This software is licensed as described in the file COPYING, which
 
8
 * you should have received as part of this distribution.  The terms
 
9
 * are also available at http://subversion.tigris.org/license-1.html.
 
10
 * If newer versions of this license are posted there, you may use a
 
11
 * newer version instead, at your option.
 
12
 *
 
13
 * This software consists of voluntary contributions made by many
 
14
 * individuals.  For exact contribution history, see the revision
 
15
 * history and logs, available at http://subversion.tigris.org/.
 
16
 * ====================================================================
 
17
 */
 
18
 
 
19
 
 
20
#include <assert.h>
 
21
#include <apr_pools.h>
 
22
#include <apr_strings.h>
 
23
 
 
24
#include "svn_types.h"
 
25
#include "svn_delta.h"
 
26
#include "svn_pools.h"
 
27
#include "svn_path.h"
 
28
#include "svn_sorts.h"
 
29
 
 
30
 
 
31
/*** Helper functions. ***/
 
32
 
 
33
typedef struct dir_stack_t
 
34
{
 
35
  void *dir_baton;   /* the dir baton. */
 
36
  apr_pool_t *pool;  /* the pool associated with the dir baton. */
 
37
 
 
38
} dir_stack_t;
 
39
 
 
40
 
 
41
/* Call EDITOR's open_directory() function with the PATH and REVISION
 
42
 * arguments, and then add the resulting dir baton to the dir baton
 
43
 * stack. 
 
44
 */
 
45
static svn_error_t *
 
46
open_dir (apr_array_header_t *db_stack,
 
47
          const svn_delta_editor_t *editor,
 
48
          const char *path,
 
49
          svn_revnum_t revision,
 
50
          apr_pool_t *pool)
 
51
{
 
52
  void *parent_db, *db;
 
53
  dir_stack_t *item;
 
54
  apr_pool_t *subpool;
 
55
 
 
56
  /* Assert that we are in a stable state. */
 
57
  assert (db_stack && db_stack->nelts);
 
58
 
 
59
  /* Get the parent dir baton. */
 
60
  item = APR_ARRAY_IDX (db_stack, db_stack->nelts - 1, void *);
 
61
  parent_db = item->dir_baton;
 
62
 
 
63
  /* Call the EDITOR's open_directory function to get a new directory
 
64
     baton. */
 
65
  subpool = svn_pool_create (pool);
 
66
  SVN_ERR (editor->open_directory (path, parent_db, revision, subpool, &db));
 
67
 
 
68
  /* Now add the dir baton to the stack. */
 
69
  item = apr_pcalloc (subpool, sizeof (*item));
 
70
  item->dir_baton = db;
 
71
  item->pool = subpool;
 
72
  APR_ARRAY_PUSH (db_stack, dir_stack_t *) = item;
 
73
 
 
74
  return SVN_NO_ERROR;
 
75
}
 
76
 
 
77
 
 
78
/* Pop a directory from the dir baton stack and update the stack
 
79
 * pointer.
 
80
 *
 
81
 * This function calls the EDITOR's close_directory() function.
 
82
 */
 
83
static svn_error_t *
 
84
pop_stack (apr_array_header_t *db_stack,
 
85
           const svn_delta_editor_t *editor)
 
86
{
 
87
  dir_stack_t *item;
 
88
 
 
89
  /* Assert that we are in a stable state. */
 
90
  assert (db_stack && db_stack->nelts);
 
91
 
 
92
  /* Close the most recent directory pushed to the stack. */
 
93
  item = APR_ARRAY_IDX (db_stack, db_stack->nelts - 1, dir_stack_t *);
 
94
  (void) apr_array_pop (db_stack);
 
95
  SVN_ERR (editor->close_directory (item->dir_baton, item->pool));
 
96
  svn_pool_destroy (item->pool);
 
97
 
 
98
  return SVN_NO_ERROR;
 
99
}
 
100
 
 
101
 
 
102
/* Count the number of path components in PATH. */
 
103
static int
 
104
count_components (const char *path)
 
105
{
 
106
  int count = 1;
 
107
  const char *instance = path;
 
108
 
 
109
  if ((strlen (path) == 1) && (path[0] == '/'))
 
110
    return 0;
 
111
 
 
112
  do
 
113
    {
 
114
      instance++;
 
115
      instance = strchr (instance, '/');
 
116
      if (instance)
 
117
        count++;
 
118
    }
 
119
  while (instance);
 
120
 
 
121
  return count;
 
122
}
 
123
 
 
124
 
 
125
 
 
126
/*** Public interfaces ***/
 
127
svn_error_t *
 
128
svn_delta_path_driver (const svn_delta_editor_t *editor,
 
129
                       void *edit_baton,
 
130
                       svn_revnum_t revision,
 
131
                       apr_array_header_t *paths,
 
132
                       svn_delta_path_driver_cb_func_t callback_func,
 
133
                       void *callback_baton,
 
134
                       apr_pool_t *pool)
 
135
{
 
136
  apr_array_header_t *db_stack = apr_array_make (pool, 4, sizeof (void *));
 
137
  const char *last_path = NULL;
 
138
  int i = 0;
 
139
  void *parent_db = NULL, *db = NULL;
 
140
  const char *path;
 
141
  apr_pool_t *subpool = svn_pool_create (pool);
 
142
  apr_pool_t *iterpool = svn_pool_create (pool);
 
143
  dir_stack_t *item = apr_pcalloc (subpool, sizeof (*item));
 
144
 
 
145
  /* Do nothing if there are no paths. */
 
146
  if (! paths->nelts)
 
147
    return SVN_NO_ERROR;
 
148
 
 
149
  /* Sort the paths in a depth-first directory-ish order. */
 
150
  qsort (paths->elts, paths->nelts, paths->elt_size, svn_sort_compare_paths);
 
151
 
 
152
  /* If the root of the edit is also a target path, we want to call
 
153
     the callback function to let the user open the root directory and
 
154
     do what needs to be done.  Otherwise, we'll do the open_root()
 
155
     ourselves. */
 
156
  path = APR_ARRAY_IDX (paths, 0, const char *);
 
157
  if (svn_path_is_empty (path))
 
158
    {
 
159
      SVN_ERR (callback_func (&db, NULL, callback_baton, path, subpool));
 
160
      last_path = path;
 
161
      i++;
 
162
    }
 
163
  else
 
164
    {
 
165
      SVN_ERR (editor->open_root (edit_baton, revision, subpool, &db));
 
166
    }
 
167
  item->pool = subpool;
 
168
  item->dir_baton = db;
 
169
  APR_ARRAY_PUSH (db_stack, void *) = item;
 
170
 
 
171
  /* Now, loop over the commit items, traversing the URL tree and
 
172
     driving the editor. */
 
173
  for (; i < paths->nelts; i++)
 
174
    {
 
175
      const char *pdir, *bname;
 
176
      const char *common = "";
 
177
      size_t common_len;
 
178
 
 
179
      /* Clear the iteration pool. */
 
180
      svn_pool_clear (iterpool);
 
181
 
 
182
      /* Get the next path. */
 
183
      path = APR_ARRAY_IDX (paths, i, const char *);
 
184
 
 
185
      /*** Step A - Find the common ancestor of the last path and the
 
186
           current one.  For the first iteration, this is just the
 
187
           empty string. ***/
 
188
      if (i > 0)
 
189
        common = svn_path_get_longest_ancestor (last_path, path, iterpool);
 
190
      common_len = strlen (common);
 
191
 
 
192
      /*** Step B - Close any directories between the last path and
 
193
           the new common ancestor, if any need to be closed.
 
194
           Sometimes there is nothing to do here (like, for the first
 
195
           iteration, or when the last path was an ancestor of the
 
196
           current one). ***/
 
197
      if ((i > 0) && (strlen (last_path) > common_len))
 
198
        {
 
199
          const char *rel = last_path + (common_len ? (common_len + 1) : 0);
 
200
          int count = count_components (rel);
 
201
          while (count--)
 
202
            {
 
203
              SVN_ERR (pop_stack (db_stack, editor));
 
204
            }
 
205
        }
 
206
 
 
207
      /*** Step C - Open any directories between the common ancestor
 
208
           and the parent of the current path. ***/
 
209
      svn_path_split (path, &pdir, &bname, iterpool);
 
210
      if (strlen (pdir) > common_len)
 
211
        {
 
212
          const char *piece = pdir + common_len + 1;
 
213
 
 
214
          while (1)
 
215
            {
 
216
              const char *rel = pdir;
 
217
 
 
218
              /* Find the first separator. */
 
219
              piece = strchr (piece, '/');
 
220
 
 
221
              /* Calculate REL as the portion of PDIR up to (but not
 
222
                 including) the location to which PIECE is pointing. */
 
223
              if (piece)
 
224
                rel = apr_pstrmemdup (iterpool, pdir, piece - pdir);
 
225
 
 
226
              /* Open the subdirectory. */
 
227
              SVN_ERR (open_dir (db_stack, editor, rel, revision, pool));
 
228
              
 
229
              /* If we found a '/', advance our PIECE pointer to
 
230
                 character just after that '/'.  Otherwise, we're
 
231
                 done.  */
 
232
              if (piece)
 
233
                piece++;    
 
234
              else
 
235
                break;
 
236
            }
 
237
        }
 
238
 
 
239
      /*** Step D - Tell our caller to handle the current path. ***/
 
240
      item = APR_ARRAY_IDX (db_stack, db_stack->nelts - 1, void *);
 
241
      parent_db = item->dir_baton;
 
242
      subpool = svn_pool_create (pool);
 
243
      SVN_ERR (callback_func (&db, parent_db, callback_baton, path, subpool));
 
244
      if (db)
 
245
        {
 
246
          item = apr_pcalloc (subpool, sizeof (*item));
 
247
          item->dir_baton = db;
 
248
          item->pool = subpool;
 
249
          APR_ARRAY_PUSH (db_stack, void *) = item;
 
250
        }
 
251
      else
 
252
        {
 
253
          svn_pool_destroy (subpool);
 
254
        }
 
255
 
 
256
      /*** Step E - Save our state for the next iteration.  If our
 
257
           caller opened or added PATH as a directory, that becomes
 
258
           our LAST_PATH.  Otherwise, we use PATH's parent
 
259
           directory. ***/
 
260
 
 
261
      /* NOTE:  The variable LAST_PATH needs to outlive the loop. */
 
262
      if (db)
 
263
        last_path = path; /* lives in a pool outside our control. */
 
264
      else
 
265
        last_path = apr_pstrdup (pool, pdir); /* duping into POOL. */
 
266
    }
 
267
 
 
268
  /* Destroy the iteration subpool. */
 
269
  svn_pool_destroy (iterpool);
 
270
 
 
271
  /* Close down any remaining open directory batons. */
 
272
  while (db_stack->nelts)
 
273
    {
 
274
      SVN_ERR (pop_stack (db_stack, editor));
 
275
    }
 
276
 
 
277
  return SVN_NO_ERROR;
 
278
}