In the command-line client, forbid
[svn.git] / subversion / libsvn_subr / target.c
bloba91594d21d27a1ce6b949103a26cf8d2a5cc6aba
1 /*
2 * target.c: functions which operate on a list of targets supplied to
3 * a subversion subcommand.
5 * ====================================================================
6 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
8 * This software is licensed as described in the file COPYING, which
9 * you should have received as part of this distribution. The terms
10 * are also available at http://subversion.tigris.org/license-1.html.
11 * If newer versions of this license are posted there, you may use a
12 * newer version instead, at your option.
14 * This software consists of voluntary contributions made by many
15 * individuals. For exact contribution history, see the revision
16 * history and logs, available at http://subversion.tigris.org/.
17 * ====================================================================
20 /* ==================================================================== */
24 /*** Includes. ***/
26 #include "svn_pools.h"
27 #include "svn_error.h"
28 #include "svn_path.h"
31 /*** Code. ***/
33 svn_error_t *
34 svn_path_condense_targets(const char **pcommon,
35 apr_array_header_t **pcondensed_targets,
36 const apr_array_header_t *targets,
37 svn_boolean_t remove_redundancies,
38 apr_pool_t *pool)
40 int i, j, num_condensed = targets->nelts;
41 svn_boolean_t *removed;
42 apr_array_header_t *abs_targets;
43 int basedir_len;
45 /* Early exit when there's no data to work on. */
46 if (targets->nelts <= 0)
48 *pcommon = NULL;
49 if (pcondensed_targets)
50 *pcondensed_targets = NULL;
51 return SVN_NO_ERROR;
54 /* Get the absolute path of the first target. */
55 SVN_ERR(svn_path_get_absolute(pcommon,
56 APR_ARRAY_IDX(targets, 0, const char *),
57 pool));
59 /* Early exit when there's only one path to work on. */
60 if (targets->nelts == 1)
62 if (pcondensed_targets)
63 *pcondensed_targets = apr_array_make(pool, 0, sizeof(const char *));
64 return SVN_NO_ERROR;
67 /* Copy the targets array, but with absolute paths instead of
68 relative. Also, find the pcommon argument by finding what is
69 common in all of the absolute paths. NOTE: This is not as
70 efficient as it could be. The calculation of the basedir could
71 be done in the loop below, which would save some calls to
72 svn_path_get_longest_ancestor. I decided to do it this way
73 because I thought it would be simpler, since this way, we don't
74 even do the loop if we don't need to condense the targets. */
76 removed = apr_pcalloc(pool, (targets->nelts * sizeof(svn_boolean_t)));
77 abs_targets = apr_array_make(pool, targets->nelts, sizeof(const char *));
79 APR_ARRAY_PUSH(abs_targets, const char *) = *pcommon;
81 for (i = 1; i < targets->nelts; ++i)
83 const char *rel = APR_ARRAY_IDX(targets, i, const char *);
84 const char *absolute;
85 SVN_ERR(svn_path_get_absolute(&absolute, rel, pool));
86 APR_ARRAY_PUSH(abs_targets, const char *) = absolute;
87 *pcommon = svn_path_get_longest_ancestor(*pcommon, absolute, pool);
90 if (pcondensed_targets != NULL)
92 if (remove_redundancies)
94 /* Find the common part of each pair of targets. If
95 common part is equal to one of the paths, the other
96 is a child of it, and can be removed. If a target is
97 equal to *pcommon, it can also be removed. */
99 /* First pass: when one non-removed target is a child of
100 another non-removed target, remove the child. */
101 for (i = 0; i < abs_targets->nelts; ++i)
103 if (removed[i])
104 continue;
106 for (j = i + 1; j < abs_targets->nelts; ++j)
108 const char *abs_targets_i;
109 const char *abs_targets_j;
110 const char *ancestor;
112 if (removed[j])
113 continue;
115 abs_targets_i = APR_ARRAY_IDX(abs_targets, i, const char *);
116 abs_targets_j = APR_ARRAY_IDX(abs_targets, j, const char *);
118 ancestor = svn_path_get_longest_ancestor
119 (abs_targets_i, abs_targets_j, pool);
121 if (*ancestor == '\0')
122 continue;
124 if (strcmp(ancestor, abs_targets_i) == 0)
126 removed[j] = TRUE;
127 num_condensed--;
129 else if (strcmp(ancestor, abs_targets_j) == 0)
131 removed[i] = TRUE;
132 num_condensed--;
137 /* Second pass: when a target is the same as *pcommon,
138 remove the target. */
139 for (i = 0; i < abs_targets->nelts; ++i)
141 const char *abs_targets_i = APR_ARRAY_IDX(abs_targets, i,
142 const char *);
144 if ((strcmp(abs_targets_i, *pcommon) == 0) && (! removed[i]))
146 removed[i] = TRUE;
147 num_condensed--;
152 /* Now create the return array, and copy the non-removed items */
153 basedir_len = strlen(*pcommon);
154 *pcondensed_targets = apr_array_make(pool, num_condensed,
155 sizeof(const char *));
157 for (i = 0; i < abs_targets->nelts; ++i)
159 const char *rel_item = APR_ARRAY_IDX(abs_targets, i, const char *);
161 /* Skip this if it's been removed. */
162 if (removed[i])
163 continue;
165 /* If a common prefix was found, condensed_targets are given
166 relative to that prefix. */
167 if (basedir_len > 0)
169 /* Only advance our pointer past a path separator if
170 REL_ITEM isn't the same as *PCOMMON.
172 If *PCOMMON is a root path, basedir_len will already
173 include the closing '/', so never advance the pointer
174 here.
176 rel_item += basedir_len;
177 if (rel_item[0] &&
178 ! svn_dirent_is_root(*pcommon, basedir_len))
179 rel_item++;
182 APR_ARRAY_PUSH(*pcondensed_targets, const char *)
183 = apr_pstrdup(pool, rel_item);
187 return SVN_NO_ERROR;
191 svn_error_t *
192 svn_path_remove_redundancies(apr_array_header_t **pcondensed_targets,
193 const apr_array_header_t *targets,
194 apr_pool_t *pool)
196 apr_pool_t *temp_pool;
197 apr_array_header_t *abs_targets;
198 apr_array_header_t *rel_targets;
199 int i;
201 if ((targets->nelts <= 0) || (! pcondensed_targets))
203 /* No targets or no place to store our work means this function
204 really has nothing to do. */
205 if (pcondensed_targets)
206 *pcondensed_targets = NULL;
207 return SVN_NO_ERROR;
210 /* Initialize our temporary pool. */
211 temp_pool = svn_pool_create(pool);
213 /* Create our list of absolute paths for our "keepers" */
214 abs_targets = apr_array_make(temp_pool, targets->nelts,
215 sizeof(const char *));
217 /* Create our list of untainted paths for our "keepers" */
218 rel_targets = apr_array_make(pool, targets->nelts,
219 sizeof(const char *));
221 /* For each target in our list we do the following:
223 1. Calculate its absolute path (ABS_PATH).
224 2. See if any of the keepers in ABS_TARGETS is a parent of, or
225 is the same path as, ABS_PATH. If so, we ignore this
226 target. If not, however, add this target's absolute path to
227 ABS_TARGETS and its original path to REL_TARGETS.
229 for (i = 0; i < targets->nelts; i++)
231 const char *rel_path = APR_ARRAY_IDX(targets, i, const char *);
232 const char *abs_path;
233 int j;
234 svn_boolean_t keep_me;
236 /* Get the absolute path for this target. */
237 SVN_ERR(svn_path_get_absolute(&abs_path, rel_path, temp_pool));
239 /* For each keeper in ABS_TARGETS, see if this target is the
240 same as or a child of that keeper. */
241 keep_me = TRUE;
242 for (j = 0; j < abs_targets->nelts; j++)
244 const char *keeper = APR_ARRAY_IDX(abs_targets, j, const char *);
246 /* Quit here if we find this path already in the keepers. */
247 if (strcmp(keeper, abs_path) == 0)
249 keep_me = FALSE;
250 break;
253 /* Quit here if this path is a child of one of the keepers. */
254 if (svn_path_is_child(keeper, abs_path, temp_pool))
256 keep_me = FALSE;
257 break;
261 /* If this is a new keeper, add its absolute path to ABS_TARGETS
262 and its original path to REL_TARGETS. */
263 if (keep_me)
265 APR_ARRAY_PUSH(abs_targets, const char *) = abs_path;
266 APR_ARRAY_PUSH(rel_targets, const char *) = rel_path;
270 /* Destroy our temporary pool. */
271 svn_pool_destroy(temp_pool);
273 /* Make sure we return the list of untainted keeper paths. */
274 *pcondensed_targets = rel_targets;
276 return SVN_NO_ERROR;