2 * target.c: functions which operate on a list of targets supplied to
3 * a subversion subcommand.
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
14 * http://www.apache.org/licenses/LICENSE-2.0
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
22 * ====================================================================
25 /* ==================================================================== */
31 #include "svn_pools.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
40 svn_path_condense_targets(const char **pcommon,
41 apr_array_header_t **pcondensed_targets,
42 const apr_array_header_t *targets,
43 svn_boolean_t remove_redundancies,
46 int i, j, num_condensed = targets->nelts;
47 svn_boolean_t *removed;
48 apr_array_header_t *abs_targets;
50 const char *first_target;
51 svn_boolean_t first_target_is_url;
53 /* Early exit when there's no data to work on. */
54 if (targets->nelts <= 0)
57 if (pcondensed_targets)
58 *pcondensed_targets = NULL;
62 /* Get the absolute path of the first target. */
63 first_target = APR_ARRAY_IDX(targets, 0, const char *);
64 first_target_is_url = svn_path_is_url(first_target);
65 if (first_target_is_url)
67 first_target = apr_pstrdup(pool, first_target);
68 *pcommon = first_target;
71 SVN_ERR(svn_dirent_get_absolute(pcommon, first_target, pool));
73 /* Early exit when there's only one path to work on. */
74 if (targets->nelts == 1)
76 if (pcondensed_targets)
77 *pcondensed_targets = apr_array_make(pool, 0, sizeof(const char *));
81 /* Copy the targets array, but with absolute paths instead of
82 relative. Also, find the pcommon argument by finding what is
83 common in all of the absolute paths. NOTE: This is not as
84 efficient as it could be. The calculation of the basedir could
85 be done in the loop below, which would save some calls to
86 svn_path_get_longest_ancestor. I decided to do it this way
87 because I thought it would be simpler, since this way, we don't
88 even do the loop if we don't need to condense the targets. */
90 removed = apr_pcalloc(pool, (targets->nelts * sizeof(svn_boolean_t)));
91 abs_targets = apr_array_make(pool, targets->nelts, sizeof(const char *));
93 APR_ARRAY_PUSH(abs_targets, const char *) = *pcommon;
95 for (i = 1; i < targets->nelts; ++i)
97 const char *rel = APR_ARRAY_IDX(targets, i, const char *);
99 svn_boolean_t is_url = svn_path_is_url(rel);
102 absolute = apr_pstrdup(pool, rel); /* ### TODO: avoid pool dup? */
104 SVN_ERR(svn_dirent_get_absolute(&absolute, rel, pool));
106 APR_ARRAY_PUSH(abs_targets, const char *) = absolute;
108 /* If we've not already determined that there's no common
109 parent, then continue trying to do so. */
110 if (*pcommon && **pcommon)
112 /* If the is-url-ness of this target doesn't match that of
113 the first target, our search for a common ancestor can
114 end right here. Otherwise, use the appropriate
115 get-longest-ancestor function per the path type. */
116 if (is_url != first_target_is_url)
118 else if (first_target_is_url)
119 *pcommon = svn_uri_get_longest_ancestor(*pcommon, absolute, pool);
121 *pcommon = svn_dirent_get_longest_ancestor(*pcommon, absolute,
126 if (pcondensed_targets != NULL)
128 if (remove_redundancies)
130 /* Find the common part of each pair of targets. If
131 common part is equal to one of the paths, the other
132 is a child of it, and can be removed. If a target is
133 equal to *pcommon, it can also be removed. */
135 /* First pass: when one non-removed target is a child of
136 another non-removed target, remove the child. */
137 for (i = 0; i < abs_targets->nelts; ++i)
142 for (j = i + 1; j < abs_targets->nelts; ++j)
144 const char *abs_targets_i;
145 const char *abs_targets_j;
146 svn_boolean_t i_is_url, j_is_url;
147 const char *ancestor;
152 abs_targets_i = APR_ARRAY_IDX(abs_targets, i, const char *);
153 abs_targets_j = APR_ARRAY_IDX(abs_targets, j, const char *);
154 i_is_url = svn_path_is_url(abs_targets_i);
155 j_is_url = svn_path_is_url(abs_targets_j);
157 if (i_is_url != j_is_url)
161 ancestor = svn_uri_get_longest_ancestor(abs_targets_i,
165 ancestor = svn_dirent_get_longest_ancestor(abs_targets_i,
169 if (*ancestor == '\0')
172 if (strcmp(ancestor, abs_targets_i) == 0)
177 else if (strcmp(ancestor, abs_targets_j) == 0)
185 /* Second pass: when a target is the same as *pcommon,
186 remove the target. */
187 for (i = 0; i < abs_targets->nelts; ++i)
189 const char *abs_targets_i = APR_ARRAY_IDX(abs_targets, i,
192 if ((strcmp(abs_targets_i, *pcommon) == 0) && (! removed[i]))
200 /* Now create the return array, and copy the non-removed items */
201 basedir_len = strlen(*pcommon);
202 *pcondensed_targets = apr_array_make(pool, num_condensed,
203 sizeof(const char *));
205 for (i = 0; i < abs_targets->nelts; ++i)
207 const char *rel_item = APR_ARRAY_IDX(abs_targets, i, const char *);
209 /* Skip this if it's been removed. */
213 /* If a common prefix was found, condensed_targets are given
214 relative to that prefix. */
217 /* Only advance our pointer past a path separator if
218 REL_ITEM isn't the same as *PCOMMON.
220 If *PCOMMON is a root path, basedir_len will already
221 include the closing '/', so never advance the pointer
224 rel_item += basedir_len;
226 ! svn_dirent_is_root(*pcommon, basedir_len))
230 APR_ARRAY_PUSH(*pcondensed_targets, const char *)
231 = apr_pstrdup(pool, rel_item);
240 svn_path_remove_redundancies(apr_array_header_t **pcondensed_targets,
241 const apr_array_header_t *targets,
244 apr_pool_t *temp_pool;
245 apr_array_header_t *abs_targets;
246 apr_array_header_t *rel_targets;
249 if ((targets->nelts <= 0) || (! pcondensed_targets))
251 /* No targets or no place to store our work means this function
252 really has nothing to do. */
253 if (pcondensed_targets)
254 *pcondensed_targets = NULL;
258 /* Initialize our temporary pool. */
259 temp_pool = svn_pool_create(pool);
261 /* Create our list of absolute paths for our "keepers" */
262 abs_targets = apr_array_make(temp_pool, targets->nelts,
263 sizeof(const char *));
265 /* Create our list of untainted paths for our "keepers" */
266 rel_targets = apr_array_make(pool, targets->nelts,
267 sizeof(const char *));
269 /* For each target in our list we do the following:
271 1. Calculate its absolute path (ABS_PATH).
272 2. See if any of the keepers in ABS_TARGETS is a parent of, or
273 is the same path as, ABS_PATH. If so, we ignore this
274 target. If not, however, add this target's absolute path to
275 ABS_TARGETS and its original path to REL_TARGETS.
277 for (i = 0; i < targets->nelts; i++)
279 const char *rel_path = APR_ARRAY_IDX(targets, i, const char *);
280 const char *abs_path;
282 svn_boolean_t is_url, keep_me;
284 /* Get the absolute path for this target. */
285 is_url = svn_path_is_url(rel_path);
289 SVN_ERR(svn_dirent_get_absolute(&abs_path, rel_path, temp_pool));
291 /* For each keeper in ABS_TARGETS, see if this target is the
292 same as or a child of that keeper. */
294 for (j = 0; j < abs_targets->nelts; j++)
296 const char *keeper = APR_ARRAY_IDX(abs_targets, j, const char *);
297 svn_boolean_t keeper_is_url = svn_path_is_url(keeper);
298 const char *child_relpath;
300 /* If KEEPER hasn't the same is-url-ness as ABS_PATH, we
301 know they aren't equal and that one isn't the child of
303 if (is_url != keeper_is_url)
306 /* Quit here if this path is the same as or a child of one of the
309 child_relpath = svn_uri_skip_ancestor(keeper, abs_path, temp_pool);
311 child_relpath = svn_dirent_skip_ancestor(keeper, abs_path);
319 /* If this is a new keeper, add its absolute path to ABS_TARGETS
320 and its original path to REL_TARGETS. */
323 APR_ARRAY_PUSH(abs_targets, const char *) = abs_path;
324 APR_ARRAY_PUSH(rel_targets, const char *) = rel_path;
328 /* Destroy our temporary pool. */
329 svn_pool_destroy(temp_pool);
331 /* Make sure we return the list of untainted keeper paths. */
332 *pcondensed_targets = rel_targets;