1251881Speter/* 2251881Speter * target.c: functions which operate on a list of targets supplied to 3251881Speter * a subversion subcommand. 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter */ 24251881Speter 25251881Speter/* ==================================================================== */ 26251881Speter 27251881Speter 28251881Speter 29251881Speter/*** Includes. ***/ 30251881Speter 31251881Speter#include "svn_pools.h" 32251881Speter#include "svn_error.h" 33251881Speter#include "svn_dirent_uri.h" 34251881Speter#include "svn_path.h" 35251881Speter 36251881Speter 37251881Speter/*** Code. ***/ 38251881Speter 39251881Spetersvn_error_t * 40251881Spetersvn_path_condense_targets(const char **pcommon, 41251881Speter apr_array_header_t **pcondensed_targets, 42251881Speter const apr_array_header_t *targets, 43251881Speter svn_boolean_t remove_redundancies, 44251881Speter apr_pool_t *pool) 45251881Speter{ 46251881Speter int i, j, num_condensed = targets->nelts; 47251881Speter svn_boolean_t *removed; 48251881Speter apr_array_header_t *abs_targets; 49251881Speter size_t basedir_len; 50251881Speter const char *first_target; 51251881Speter svn_boolean_t first_target_is_url; 52251881Speter 53251881Speter /* Early exit when there's no data to work on. */ 54251881Speter if (targets->nelts <= 0) 55251881Speter { 56251881Speter *pcommon = NULL; 57251881Speter if (pcondensed_targets) 58251881Speter *pcondensed_targets = NULL; 59251881Speter return SVN_NO_ERROR; 60251881Speter } 61251881Speter 62251881Speter /* Get the absolute path of the first target. */ 63251881Speter first_target = APR_ARRAY_IDX(targets, 0, const char *); 64251881Speter first_target_is_url = svn_path_is_url(first_target); 65251881Speter if (first_target_is_url) 66251881Speter { 67251881Speter first_target = apr_pstrdup(pool, first_target); 68251881Speter *pcommon = first_target; 69251881Speter } 70251881Speter else 71251881Speter SVN_ERR(svn_dirent_get_absolute(pcommon, first_target, pool)); 72251881Speter 73251881Speter /* Early exit when there's only one path to work on. */ 74251881Speter if (targets->nelts == 1) 75251881Speter { 76251881Speter if (pcondensed_targets) 77251881Speter *pcondensed_targets = apr_array_make(pool, 0, sizeof(const char *)); 78251881Speter return SVN_NO_ERROR; 79251881Speter } 80251881Speter 81251881Speter /* Copy the targets array, but with absolute paths instead of 82251881Speter relative. Also, find the pcommon argument by finding what is 83251881Speter common in all of the absolute paths. NOTE: This is not as 84251881Speter efficient as it could be. The calculation of the basedir could 85251881Speter be done in the loop below, which would save some calls to 86251881Speter svn_path_get_longest_ancestor. I decided to do it this way 87251881Speter because I thought it would be simpler, since this way, we don't 88251881Speter even do the loop if we don't need to condense the targets. */ 89251881Speter 90251881Speter removed = apr_pcalloc(pool, (targets->nelts * sizeof(svn_boolean_t))); 91251881Speter abs_targets = apr_array_make(pool, targets->nelts, sizeof(const char *)); 92251881Speter 93251881Speter APR_ARRAY_PUSH(abs_targets, const char *) = *pcommon; 94251881Speter 95251881Speter for (i = 1; i < targets->nelts; ++i) 96251881Speter { 97251881Speter const char *rel = APR_ARRAY_IDX(targets, i, const char *); 98251881Speter const char *absolute; 99251881Speter svn_boolean_t is_url = svn_path_is_url(rel); 100251881Speter 101251881Speter if (is_url) 102251881Speter absolute = apr_pstrdup(pool, rel); /* ### TODO: avoid pool dup? */ 103251881Speter else 104251881Speter SVN_ERR(svn_dirent_get_absolute(&absolute, rel, pool)); 105251881Speter 106251881Speter APR_ARRAY_PUSH(abs_targets, const char *) = absolute; 107251881Speter 108251881Speter /* If we've not already determined that there's no common 109251881Speter parent, then continue trying to do so. */ 110251881Speter if (*pcommon && **pcommon) 111251881Speter { 112251881Speter /* If the is-url-ness of this target doesn't match that of 113251881Speter the first target, our search for a common ancestor can 114251881Speter end right here. Otherwise, use the appropriate 115251881Speter get-longest-ancestor function per the path type. */ 116251881Speter if (is_url != first_target_is_url) 117251881Speter *pcommon = ""; 118251881Speter else if (first_target_is_url) 119251881Speter *pcommon = svn_uri_get_longest_ancestor(*pcommon, absolute, pool); 120251881Speter else 121251881Speter *pcommon = svn_dirent_get_longest_ancestor(*pcommon, absolute, 122251881Speter pool); 123251881Speter } 124251881Speter } 125251881Speter 126251881Speter if (pcondensed_targets != NULL) 127251881Speter { 128251881Speter if (remove_redundancies) 129251881Speter { 130251881Speter /* Find the common part of each pair of targets. If 131251881Speter common part is equal to one of the paths, the other 132251881Speter is a child of it, and can be removed. If a target is 133251881Speter equal to *pcommon, it can also be removed. */ 134251881Speter 135251881Speter /* First pass: when one non-removed target is a child of 136251881Speter another non-removed target, remove the child. */ 137251881Speter for (i = 0; i < abs_targets->nelts; ++i) 138251881Speter { 139251881Speter if (removed[i]) 140251881Speter continue; 141251881Speter 142251881Speter for (j = i + 1; j < abs_targets->nelts; ++j) 143251881Speter { 144251881Speter const char *abs_targets_i; 145251881Speter const char *abs_targets_j; 146251881Speter svn_boolean_t i_is_url, j_is_url; 147251881Speter const char *ancestor; 148251881Speter 149251881Speter if (removed[j]) 150251881Speter continue; 151251881Speter 152251881Speter abs_targets_i = APR_ARRAY_IDX(abs_targets, i, const char *); 153251881Speter abs_targets_j = APR_ARRAY_IDX(abs_targets, j, const char *); 154251881Speter i_is_url = svn_path_is_url(abs_targets_i); 155251881Speter j_is_url = svn_path_is_url(abs_targets_j); 156251881Speter 157251881Speter if (i_is_url != j_is_url) 158251881Speter continue; 159251881Speter 160251881Speter if (i_is_url) 161251881Speter ancestor = svn_uri_get_longest_ancestor(abs_targets_i, 162251881Speter abs_targets_j, 163251881Speter pool); 164251881Speter else 165251881Speter ancestor = svn_dirent_get_longest_ancestor(abs_targets_i, 166251881Speter abs_targets_j, 167251881Speter pool); 168251881Speter 169251881Speter if (*ancestor == '\0') 170251881Speter continue; 171251881Speter 172251881Speter if (strcmp(ancestor, abs_targets_i) == 0) 173251881Speter { 174251881Speter removed[j] = TRUE; 175251881Speter num_condensed--; 176251881Speter } 177251881Speter else if (strcmp(ancestor, abs_targets_j) == 0) 178251881Speter { 179251881Speter removed[i] = TRUE; 180251881Speter num_condensed--; 181251881Speter } 182251881Speter } 183251881Speter } 184251881Speter 185251881Speter /* Second pass: when a target is the same as *pcommon, 186251881Speter remove the target. */ 187251881Speter for (i = 0; i < abs_targets->nelts; ++i) 188251881Speter { 189251881Speter const char *abs_targets_i = APR_ARRAY_IDX(abs_targets, i, 190251881Speter const char *); 191251881Speter 192251881Speter if ((strcmp(abs_targets_i, *pcommon) == 0) && (! removed[i])) 193251881Speter { 194251881Speter removed[i] = TRUE; 195251881Speter num_condensed--; 196251881Speter } 197251881Speter } 198251881Speter } 199251881Speter 200251881Speter /* Now create the return array, and copy the non-removed items */ 201251881Speter basedir_len = strlen(*pcommon); 202251881Speter *pcondensed_targets = apr_array_make(pool, num_condensed, 203251881Speter sizeof(const char *)); 204251881Speter 205251881Speter for (i = 0; i < abs_targets->nelts; ++i) 206251881Speter { 207251881Speter const char *rel_item = APR_ARRAY_IDX(abs_targets, i, const char *); 208251881Speter 209251881Speter /* Skip this if it's been removed. */ 210251881Speter if (removed[i]) 211251881Speter continue; 212251881Speter 213251881Speter /* If a common prefix was found, condensed_targets are given 214251881Speter relative to that prefix. */ 215251881Speter if (basedir_len > 0) 216251881Speter { 217251881Speter /* Only advance our pointer past a path separator if 218251881Speter REL_ITEM isn't the same as *PCOMMON. 219251881Speter 220251881Speter If *PCOMMON is a root path, basedir_len will already 221251881Speter include the closing '/', so never advance the pointer 222251881Speter here. 223251881Speter */ 224251881Speter rel_item += basedir_len; 225251881Speter if (rel_item[0] && 226251881Speter ! svn_dirent_is_root(*pcommon, basedir_len)) 227251881Speter rel_item++; 228251881Speter } 229251881Speter 230251881Speter APR_ARRAY_PUSH(*pcondensed_targets, const char *) 231251881Speter = apr_pstrdup(pool, rel_item); 232251881Speter } 233251881Speter } 234251881Speter 235251881Speter return SVN_NO_ERROR; 236251881Speter} 237251881Speter 238251881Speter 239251881Spetersvn_error_t * 240251881Spetersvn_path_remove_redundancies(apr_array_header_t **pcondensed_targets, 241251881Speter const apr_array_header_t *targets, 242251881Speter apr_pool_t *pool) 243251881Speter{ 244251881Speter apr_pool_t *temp_pool; 245251881Speter apr_array_header_t *abs_targets; 246251881Speter apr_array_header_t *rel_targets; 247251881Speter int i; 248251881Speter 249251881Speter if ((targets->nelts <= 0) || (! pcondensed_targets)) 250251881Speter { 251251881Speter /* No targets or no place to store our work means this function 252251881Speter really has nothing to do. */ 253251881Speter if (pcondensed_targets) 254251881Speter *pcondensed_targets = NULL; 255251881Speter return SVN_NO_ERROR; 256251881Speter } 257251881Speter 258251881Speter /* Initialize our temporary pool. */ 259251881Speter temp_pool = svn_pool_create(pool); 260251881Speter 261251881Speter /* Create our list of absolute paths for our "keepers" */ 262251881Speter abs_targets = apr_array_make(temp_pool, targets->nelts, 263251881Speter sizeof(const char *)); 264251881Speter 265251881Speter /* Create our list of untainted paths for our "keepers" */ 266251881Speter rel_targets = apr_array_make(pool, targets->nelts, 267251881Speter sizeof(const char *)); 268251881Speter 269251881Speter /* For each target in our list we do the following: 270251881Speter 271251881Speter 1. Calculate its absolute path (ABS_PATH). 272251881Speter 2. See if any of the keepers in ABS_TARGETS is a parent of, or 273251881Speter is the same path as, ABS_PATH. If so, we ignore this 274251881Speter target. If not, however, add this target's absolute path to 275251881Speter ABS_TARGETS and its original path to REL_TARGETS. 276251881Speter */ 277251881Speter for (i = 0; i < targets->nelts; i++) 278251881Speter { 279251881Speter const char *rel_path = APR_ARRAY_IDX(targets, i, const char *); 280251881Speter const char *abs_path; 281251881Speter int j; 282251881Speter svn_boolean_t is_url, keep_me; 283251881Speter 284251881Speter /* Get the absolute path for this target. */ 285251881Speter is_url = svn_path_is_url(rel_path); 286251881Speter if (is_url) 287251881Speter abs_path = rel_path; 288251881Speter else 289251881Speter SVN_ERR(svn_dirent_get_absolute(&abs_path, rel_path, temp_pool)); 290251881Speter 291251881Speter /* For each keeper in ABS_TARGETS, see if this target is the 292251881Speter same as or a child of that keeper. */ 293251881Speter keep_me = TRUE; 294251881Speter for (j = 0; j < abs_targets->nelts; j++) 295251881Speter { 296251881Speter const char *keeper = APR_ARRAY_IDX(abs_targets, j, const char *); 297251881Speter svn_boolean_t keeper_is_url = svn_path_is_url(keeper); 298251881Speter const char *child_relpath; 299251881Speter 300251881Speter /* If KEEPER hasn't the same is-url-ness as ABS_PATH, we 301251881Speter know they aren't equal and that one isn't the child of 302251881Speter the other. */ 303251881Speter if (is_url != keeper_is_url) 304251881Speter continue; 305251881Speter 306251881Speter /* Quit here if this path is the same as or a child of one of the 307251881Speter keepers. */ 308251881Speter if (is_url) 309251881Speter child_relpath = svn_uri_skip_ancestor(keeper, abs_path, temp_pool); 310251881Speter else 311251881Speter child_relpath = svn_dirent_skip_ancestor(keeper, abs_path); 312251881Speter if (child_relpath) 313251881Speter { 314251881Speter keep_me = FALSE; 315251881Speter break; 316251881Speter } 317251881Speter } 318251881Speter 319251881Speter /* If this is a new keeper, add its absolute path to ABS_TARGETS 320251881Speter and its original path to REL_TARGETS. */ 321251881Speter if (keep_me) 322251881Speter { 323251881Speter APR_ARRAY_PUSH(abs_targets, const char *) = abs_path; 324251881Speter APR_ARRAY_PUSH(rel_targets, const char *) = rel_path; 325251881Speter } 326251881Speter } 327251881Speter 328251881Speter /* Destroy our temporary pool. */ 329251881Speter svn_pool_destroy(temp_pool); 330251881Speter 331251881Speter /* Make sure we return the list of untainted keeper paths. */ 332251881Speter *pcondensed_targets = rel_targets; 333251881Speter 334251881Speter return SVN_NO_ERROR; 335251881Speter} 336