blob: 5a9d7ba26d424695c86fd0f7ff64769988dfd8e5 [file] [log] [blame]
/*
* target.c: functions which operate on a list of targets supplied to
* a subversion subcommand.
*
* ====================================================================
* Copyright (c) 2000-2002 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include "svn_string.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_path.h"
#include "apr_file_info.h"
/*** Code. ***/
svn_error_t *
svn_path_get_absolute(svn_stringbuf_t **pabsolute,
const svn_stringbuf_t *relative,
apr_pool_t *pool)
{
char * buffer;
int apr_err = apr_filepath_merge(&buffer, NULL, relative->data,
APR_FILEPATH_NOTRELATIVE
| APR_FILEPATH_TRUENAME,
pool);
if (apr_err == APR_SUCCESS)
{
*pabsolute = svn_stringbuf_create(buffer, pool);
svn_path_canonicalize(*pabsolute);
return SVN_NO_ERROR;
}
else
{
return svn_error_createf(SVN_ERR_BAD_FILENAME, apr_err, NULL, pool,
"Couldn't determine absolute path of %s.",
relative->data);
}
}
svn_error_t *
svn_path_split_if_file(svn_stringbuf_t *path,
svn_stringbuf_t **pdirectory,
svn_stringbuf_t **pfile,
apr_pool_t * pool)
{
apr_finfo_t finfo;
apr_status_t apr_err = apr_stat(&finfo, path->data, APR_FINFO_TYPE, pool);
if (apr_err != APR_SUCCESS)
{
return svn_error_createf(SVN_ERR_BAD_FILENAME, apr_err, NULL,
pool, "Couldn't determine if %s was a file or "
"directory.", path->data);
}
else
{
if (finfo.filetype == APR_DIR)
{
*pdirectory = path;
*pfile = svn_stringbuf_create("", pool);
}
else if (finfo.filetype == APR_REG)
{
svn_path_split(path, pdirectory, pfile, pool);
}
else
{
return svn_error_createf(SVN_ERR_BAD_FILENAME, 0, NULL, pool,
"%s is neither a file nor a directory name.",
path->data);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_path_condense_targets (svn_stringbuf_t **pbasedir,
apr_array_header_t **pcondensed_targets,
const apr_array_header_t *targets,
apr_pool_t *pool)
{
if (targets->nelts <=0)
{
*pbasedir = NULL;
if (pcondensed_targets)
*pcondensed_targets = NULL;
}
else
{
int i, j, num_condensed = targets->nelts;
svn_stringbuf_t *file;
svn_boolean_t *removed
= apr_pcalloc (pool, (targets->nelts * sizeof (svn_boolean_t)));
/* Copy the targets array, but with absolute paths instead of
relative. Also, find the pbasedir argument by finding what is
common in all of the absolute paths. NOTE: This is not as
efficient as it could be The calculation of the the basedir
could be done in the loop below, which would save some calls to
svn_path_get_longest_ancestor. I decided to do it this way
because I thought it would simpler, since this way, we don't
even do the loop if we don't need to condense the targets. */
apr_array_header_t *abs_targets
= apr_array_make (pool, targets->nelts, sizeof (svn_stringbuf_t*));
SVN_ERR (svn_path_get_absolute (pbasedir,
((svn_stringbuf_t **) targets->elts)[0],
pool));
(*((svn_stringbuf_t**)apr_array_push (abs_targets))) = *pbasedir;
for (i = 1; i < targets->nelts; ++i)
{
svn_stringbuf_t *rel = ((svn_stringbuf_t **)targets->elts)[i];
svn_stringbuf_t *absolute;
SVN_ERR (svn_path_get_absolute (&absolute, rel, pool));
(*((svn_stringbuf_t **)apr_array_push (abs_targets))) = absolute;
*pbasedir = svn_path_get_longest_ancestor (*pbasedir,
absolute,
pool);
}
/* If we need to find the targets, find the common part of each pair
of targets. If common part is equal to one of the paths, the other
is a child of it, and can be removed. If a target is equal to
*pbasedir, it can also be removed. */
if (pcondensed_targets != NULL)
{
/* First pass: when one non-removed target is a child of
another non-removed target, remove the child. */
for (i = 0; i < abs_targets->nelts; ++i)
{
if (removed[i])
continue;
for (j = i + 1; j < abs_targets->nelts; ++j)
{
svn_stringbuf_t *abs_targets_i;
svn_stringbuf_t *abs_targets_j;
svn_stringbuf_t *ancestor;
if (removed[j])
continue;
abs_targets_i =
((svn_stringbuf_t **)abs_targets->elts)[i];
abs_targets_j =
((svn_stringbuf_t **)abs_targets->elts)[j];
ancestor = svn_path_get_longest_ancestor
(abs_targets_i, abs_targets_j, pool);
if (! ancestor)
continue;
if (svn_stringbuf_compare (ancestor, abs_targets_i))
{
removed[j] = TRUE;
num_condensed--;
}
else if (svn_stringbuf_compare (ancestor, abs_targets_j))
{
removed[i] = TRUE;
num_condensed--;
}
}
}
/* Second pass: when a target is the same as *pbasedir,
remove the target. */
for (i = 0; i < abs_targets->nelts; ++i)
{
svn_stringbuf_t *abs_targets_i = ((svn_stringbuf_t **)
abs_targets->elts)[i];
if ((svn_stringbuf_compare (abs_targets_i, *pbasedir))
&& !removed[i])
{
removed[i] = TRUE;
num_condensed--;
}
}
/* Now create the return array, and copy the non-removed items */
*pcondensed_targets = apr_array_make (pool, num_condensed,
sizeof (svn_stringbuf_t*));
for (i = 0; i < abs_targets->nelts; ++i)
{
char *rel_item;
if (removed[i])
continue;
rel_item = ((svn_stringbuf_t**)abs_targets->elts)[i]->data;
rel_item += (*pbasedir)->len + 1;
(*((svn_stringbuf_t**)apr_array_push (*pcondensed_targets)))
= svn_stringbuf_create (rel_item, pool);
}
}
/* Finally check if pbasedir is a dir or a file. */
if (! svn_path_split_if_file (*pbasedir, pbasedir, &file, pool))
{
if ((pcondensed_targets != NULL)
&& (! svn_path_is_empty (file)))
{
/* If there was just one target, and it was a file, then
return it as the sole condensed target. */
(*((svn_stringbuf_t**)apr_array_push (*pcondensed_targets))) = file;
}
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_path_remove_redundancies (apr_array_header_t **pcondensed_targets,
const apr_array_header_t *targets,
apr_pool_t *pool)
{
apr_pool_t *temp_pool;
apr_array_header_t *abs_targets;
apr_array_header_t *rel_targets;
int i;
if ((targets->nelts <= 0) || (! pcondensed_targets))
{
/* No targets or no place to store our work means this function
really has nothing to do. */
if (pcondensed_targets)
*pcondensed_targets = NULL;
return SVN_NO_ERROR;
}
/* Initialize our temporary pool. */
temp_pool = svn_pool_create (pool);
/* Create our list of absolute paths for our "keepers" */
abs_targets = apr_array_make (temp_pool, targets->nelts,
sizeof (svn_stringbuf_t *));
/* Create our list of untainted paths for our "keepers" */
rel_targets = apr_array_make (pool, targets->nelts,
sizeof (svn_stringbuf_t *));
/* For each target in our list we do the following:
1. Calculate its absolute path (ABS_PATH).
2. See if any of the keepers in ABS_TARGETS is a parent of, or
is the same path as, ABS_PATH. If so, we ignore this
target. If not, however, add this target's absolute path to
ABS_TARGETS and its original path to REL_TARGETS.
*/
for (i = 0; i < targets->nelts; i++)
{
svn_stringbuf_t *rel_path = ((svn_stringbuf_t **)targets->elts)[i];
svn_stringbuf_t *abs_path;
int j;
svn_boolean_t keep_me;
/* Get the absolute path for this target. */
SVN_ERR (svn_path_get_absolute (&abs_path, rel_path, temp_pool));
/* For each keeper in ABS_TARGETS, see if this target is the
same as or a child of that keeper. */
keep_me = TRUE;
for (j = 0; j < abs_targets->nelts; j++)
{
svn_stringbuf_t *keeper = ((svn_stringbuf_t **)abs_targets->elts)[j];
/* Quit here if we find this path already in the keepers. */
if (svn_stringbuf_compare (keeper, abs_path))
{
keep_me = FALSE;
break;
}
/* Quit here if this path is a child of one of the keepers. */
if (svn_path_is_child (keeper, abs_path, temp_pool))
{
keep_me = FALSE;
break;
}
}
/* If this is a new keeper, add its absolute path to ABS_TARGETS
and its original path to REL_TARGETS. */
if (keep_me)
{
(* ((svn_stringbuf_t **) apr_array_push (abs_targets))) = abs_path;
(* ((svn_stringbuf_t **) apr_array_push (rel_targets))) = rel_path;
}
}
/* Destroy our temporary pool. */
svn_pool_destroy (temp_pool);
/* Make sure we return the list of untainted keeper paths. */
*pcondensed_targets = rel_targets;
return SVN_NO_ERROR;
}
/*
* local variables:
* eval: (load-file "../../tools/dev/svn-dev.el")
* end: */