Whoo hoo!
(Haven't tried it yet, but looks good so far...).
-K
kevin@tigris.org writes:
> User: kevin
> Date: 01/02/12 10:00:28
>
> Modified: subversion/include svn_path.h
> subversion/libsvn_subr Makefile.am
> subversion/tests/libsvn_subr Makefile.am
> Added: subversion/libsvn_subr target.c
> subversion/tests/libsvn_subr target-test.c target-test.sh
> Log:
> The implementation of svn_path_get_absolute, svn_path_split_if_file
> and svn_path_condense_targets.
> * new file libsvn_subr/target.c implementations
>
> * new file test/libsvn_subr/target-test.c a test program for above
>
> * new file test/libsvn_subr/target-test.sh invokation script for test
>
> * libsvn_subr/Makefile.am test/libsvn_subr/Makefile.am, addition of
> those files and test suites.
>
> * include/svn_path.h prototypes for the above functions.
>
> Revision Changes Path
> 1.29 +34 -0 subversion/subversion/include/svn_path.h
>
> Index: svn_path.h
> ===================================================================
> RCS file: /cvs/subversion/subversion/include/svn_path.h,v
> retrieving revision 1.28
> retrieving revision 1.29
> diff -u -r1.28 -r1.29
> --- svn_path.h 2001/02/09 03:36:22 1.28
> +++ svn_path.h 2001/02/12 18:00:27 1.29
> @@ -22,7 +22,9 @@
>
>
> #include <apr_pools.h>
> +#include <apr_tables.h>
> #include "svn_string.h"
> +#include "svn_error.h"
>
>
> /*** Notes:
> @@ -120,6 +122,38 @@
> svn_string_t *svn_path_get_longest_ancestor (const svn_string_t *path1,
> const svn_string_t *path2,
> apr_pool_t *pool);
> +
> +/* Convert RELATIVE path to an absolute path and return the results in
> + *PABSOLUTE. */
> +svn_error_t *
> +svn_path_get_absolute(svn_string_t **pabsolute,
> + const svn_string_t *relative,
> + apr_pool_t *pool);
> +
> +/* Return the path part of PATH in *PDIRECTORY, and the file part in *PFILE.
> + If PATH is a directory, it will be returned through PDIRECTORY, and *PFILE
> + will be the empty string (not NULL). */
> +svn_error_t *
> +svn_path_split_if_file(svn_string_t *path,
> + svn_string_t **pdirectory,
> + svn_string_t **pfile,
> + apr_pool_t *pool);
> +
> +/* Find the common part of all the paths in TARGETS. The elements in
> + TARGETS must be existing files or directories, in local path style.
> + PBASEDIR will be set to the absolute path that is common to all of the
> + items. Additionally, if PCONDENSED_TARGETS is non-null, it will be
> + set to a list of targets relative to *PBASEDIR, with no overlapping
> + targets. If there are no items in TARGETS, *PBASENAME and (if applicable)
> + *PCONDENSED_TARGETS will be NULL. If an item in TARGETS is equal to
> + *PBASENAME, it will be returned as an empty string.
> +
> + NOTE: There is no guarantee that *PBASENAME is within a working copy. */
> +svn_error_t *
> +svn_path_condense_targets(svn_string_t **pbasedir,
> + apr_array_header_t **pcondensed_targets,
> + const apr_array_header_t *targets,
> + apr_pool_t *pool);
>
> #endif /* SVN_PATHS_H */
>
>
>
>
> 1.16 +1 -1 subversion/subversion/libsvn_subr/Makefile.am
>
> Index: Makefile.am
> ===================================================================
> RCS file: /cvs/subversion/subversion/libsvn_subr/Makefile.am,v
> retrieving revision 1.15
> retrieving revision 1.16
> diff -u -r1.15 -r1.16
> --- Makefile.am 2001/01/30 23:06:22 1.15
> +++ Makefile.am 2001/02/12 18:00:27 1.16
> @@ -10,7 +10,7 @@
> ## Sources needed to build each library (names canonicalized)
> libsvn_subr_la_SOURCES = svn_string.c svn_error.c path.c \
> hashdump.c xml.c base64.c quoprint.c io.c \
> - keysort.c
> + keysort.c target.c
>
> ## Build flags ---------
>
>
>
>
> 1.1 subversion/subversion/libsvn_subr/target.c
>
> Index: target.c
> ===================================================================
> /*
> * target.c: functions which operate on a list of targets supplied to
> * a subversion subcommand.
> *
> * ====================================================================
> * Copyright (c) 2000 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.
> * ====================================================================
> */
>
> /* ==================================================================== */
>
>
>
> /*** Includes. ***/
>
> #include "svn_string.h"
> #include "svn_error.h"
> #include "svn_path.h"
> #include "apr_file_info.h"
>
>
> /*** Code. ***/
>
> svn_error_t *
> svn_path_get_absolute(svn_string_t **pabsolute,
> const svn_string_t *relative,
> apr_pool_t *pool)
> {
> #ifdef WIN32
> char buffer[_MAX_PATH];
> if (_fullpath(buffer, relative->data, _MAX_PATH) != NULL)
> {
> *pabsolute = svn_string_create(buffer, pool);
> }
> else
> {
> /* TODO: (kevin) Create better error messages, once I learn about
> the errors returned from _fullpath() */
> return svn_error_createf(APR_SUCCESS, SVN_ERR_BAD_FILENAME,
> NULL, pool, "Could not determine absolute "
> "path of %s", relative->data);
> }
> #else
> char buffer[PATH_MAX];
> if (realpath(relative->data, buffer) != NULL)
> {
> *pabsolute = svn_string_create(buffer, pool);
> }
> else
> {
> switch (errno)
> {
> case EACCES:
> return svn_error_createf(APR_SUCCESS, SVN_ERR_NOT_AUTHORIZED,
> NULL, pool, "Could not get absolute path "
> "for %s, because you lack permissions",
> relative->data);
> break;
> case EINVAL: /* FALLTHRU */
> case EIO: /* FALLTHRU */
> case ELOOP: /* FALLTHRU */
> case ENAMETOOLONG: /* FALLTHRU */
> case ENOENT: /* FALLTHRU */
> case ENOTDIR:
> return svn_error_createf(APR_SUCCESS, SVN_ERR_BAD_FILENAME,
> NULL, pool, "Could not get absolute path "
> "for %s, because it is not a valid file "
> "name.", relative->data);
> default:
> return svn_error_createf(APR_SUCCESS, SVN_ERR_BAD_FILENAME,
> NULL, pool, "Could not determine if %s "
> "is a file or directory.", relative->data);
> break;
> }
> }
> #endif
> return SVN_NO_ERROR;
> }
>
> svn_error_t *
> svn_path_split_if_file(svn_string_t *path,
> svn_string_t **pdirectory,
> svn_string_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(apr_err, SVN_ERR_BAD_FILENAME, NULL,
> pool, "Couldn't determine if %s was a file or "
> "directory.", path->data);
> }
> else
> {
> if (finfo.filetype == APR_DIR)
> {
> *pdirectory = path;
> *pfile = svn_string_create("", pool);
> }
> else if (finfo.filetype == APR_REG)
> {
> svn_path_split(path, pdirectory, pfile, svn_path_local_style, pool);
> }
> else
> {
> return svn_error_createf(APR_SUCCESS, SVN_ERR_BAD_FILENAME, 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_string_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_string_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_string_t*));
> SVN_ERR(svn_path_get_absolute(pbasedir, ((svn_string_t **)targets->elts)[0], pool));
> (*((svn_string_t**)apr_array_push(abs_targets))) = *pbasedir;
> for(i = 1; i < targets->nelts; ++i)
> {
> svn_string_t *rel = ((svn_string_t **)targets->elts)[i];
> svn_string_t *absolute;
> SVN_ERR(svn_path_get_absolute(&absolute, rel, pool));
> (*((svn_string_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 (pcondensed_targets != NULL)
> {
> for (i = 0; i < abs_targets->nelts - 1; ++i)
> {
> if (!removed[i])
> {
> for (j = i + 1; j < abs_targets->nelts; ++j)
> {
> if (!removed[i] && !removed[j])
> {
> svn_string_t *abs_targets_i = ((svn_string_t **)
> abs_targets->elts)[i];
> svn_string_t *abs_targets_j = ((svn_string_t **)
> abs_targets->elts)[j];
> svn_string_t *ancestor
> = svn_path_get_longest_ancestor(abs_targets_i,
> abs_targets_j,
> pool);
> if (ancestor != NULL)
> {
> if (svn_string_compare(ancestor, abs_targets_i))
> {
> removed[j] = TRUE;
> num_condensed--;
> }
> else if (svn_string_compare(ancestor,abs_targets_j))
> {
> 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_string_t*));
> for (i = 0; i < abs_targets->nelts; ++i)
> {
> if (!removed[i])
> {
> char * rel_item = ((svn_string_t**)abs_targets->elts)[i]->data;
> rel_item += (*pbasedir)->len + 1;
> (*((svn_string_t**)apr_array_push(*pcondensed_targets)))
> = svn_string_create(rel_item, pool);
> }
> }
> }
>
> /* Finally check if pbasedir is a dir or a file. */
> SVN_ERR(svn_path_split_if_file(*pbasedir, pbasedir, &file, pool));
> if (pcondensed_targets != NULL)
> {
> /* If we have only one element, then it is currently the empty string.
> Set it to the file if we found one, or the empty string, if not. */
> if (num_condensed == 1)
> ((svn_string_t **)(*pcondensed_targets)->elts)[0] = file;
> }
> }
> return SVN_NO_ERROR;
> }
>
>
> /*
> * local variables:
> * eval: (load-file "../svn-dev.el")
> * end: */
>
>
>
> 1.17 +8 -2 subversion/subversion/tests/libsvn_subr/Makefile.am
>
> Index: Makefile.am
> ===================================================================
> RCS file: /cvs/subversion/subversion/tests/libsvn_subr/Makefile.am,v
> retrieving revision 1.16
> retrieving revision 1.17
> diff -u -r1.16 -r1.17
> --- Makefile.am 2001/02/05 00:25:16 1.16
> +++ Makefile.am 2001/02/12 18:00:28 1.17
> @@ -1,8 +1,9 @@
> ## Makefile.in is generated from this by automake.
>
> -noinst_PROGRAMS = hashdump-test stringtest
> +noinst_PROGRAMS = hashdump-test stringtest target-test
> hashdump_test_SOURCES = hashdump-test.c
> stringtest_SOURCES = stringtest.c
> +target_test_SOURCES = target-test.c
>
> ## Flags needed when compiling:
> INCLUDES = @SVN_INCLUDES@ @SVN_APR_INCLUDES@
> @@ -20,13 +21,18 @@
> @SVN_LIBSVN_SUBR_LIBS@ \
> @SVN_APR_LIBS@ @SVN_EXPAT_LIBS@
>
> +target_test_LDADD = @SVN_LIBSVN_SUBR_LIBS@ @SVN_APR_LIBS@ @SVN_EXPAT_LIBS@
> +
> ## Make libtool be quiet
> LIBTOOL = @LIBTOOL@ --silent
>
> ## Automatic tests run by `make check` -----------------------------
>
> ## A list of test-programs to run. (Each program contains sub-tests.)
> -SVN_TESTS = stringtest hashdump-test
> +SVN_TESTS = stringtest hashdump-test $(srcdir)/target-test.sh
> +
> +## Give the shell script along with the distribution
> +EXTRA_DIST = target-test.sh
>
> ## We're overriding automake's own `check' rule, because it's extremely
> ## inflexible; we want better control over automated-test output.
>
>
>
> 1.1 subversion/subversion/tests/libsvn_subr/target-test.c
>
> Index: target-test.c
> ===================================================================
> #include <svn_path.h>
> #include <stdio.h>
> #include <apr_general.h>
>
> int main(int argc, char **argv)
> {
> apr_pool_t *pool;
> svn_error_t *err;
> apr_array_header_t *targets;
> apr_array_header_t *condensed_targets;
> svn_string_t *common_path = 0;
> int i;
>
> if (argc < 2) {
> fprintf(stderr, "USAGE: %s <list of entries to be compared>\n", argv[0]);
> return EXIT_FAILURE;
> }
>
> apr_initialize();
> pool = svn_pool_create(NULL);
>
> /* Create the target array */
> targets = apr_array_make(pool, argc - 1, sizeof(svn_string_t*));
> for (i = 1; i < argc; i++)
> {
> svn_string_t * target = svn_string_create(argv[i], pool);
> (*((svn_string_t **)apr_array_push(targets))) = target;
> }
>
> /* Call the function */
> err = svn_path_condense_targets(&common_path, &condensed_targets, targets, pool);
> if (err != SVN_NO_ERROR)
> svn_handle_error(err, stderr, 1);
>
> /* Display the results */
> printf("%s: ", common_path->data);
> for (i = 0; i < condensed_targets->nelts; i++)
> {
> svn_string_t * target = ((svn_string_t**)condensed_targets->elts)[i];
> if (target)
> printf("%s, ", ((svn_string_t **)condensed_targets->elts)[i]->data);
> else
> printf("NULL, ");
> }
> printf("\n");
>
> /* Now ensure it works without the pbasename */
> err = svn_path_condense_targets(&common_path, NULL, targets, pool);
> if (err != SVN_NO_ERROR)
> svn_handle_error(err, stderr, 1);
>
> printf("%s\n", common_path->data);
>
> return EXIT_SUCCESS;
> }
>
>
>
> 1.1 subversion/subversion/tests/libsvn_subr/target-test.sh
>
> Index: target-test.sh
> ===================================================================
> #!/bin/sh
>
> if ! test -d "z"; then
> mkdir z
> mkdir z/A
> mkdir z/A/B
> mkdir z/A/C
> mkdir z/D
> mkdir z/D/E
> mkdir z/D/F
> mkdir z/G
> mkdir z/G/H
> mkdir z/G/I
> touch z/A/file
> fi
>
> echo "Testing normal usage"
> ./target-test z/A/B z/A z/A/C z/D/E z/D/F z/D z/G z/G/H z/G/I
> echo
>
> echo "Testing with identical arguments (that are dirs)"
> ./target-test z/A z/A z/A z/A
> echo
>
> echo "Testing with identical arguments (that are files)"
> ./target-test z/A/file z/A/file z/A/file z/A/file
> echo
>
> echo "Testing with a single dir"
> ./target-test z/A
> echo
>
> echo "Testing with a single file"
> ./target-test z/A/file
> echo
>
>
>
Received on Sat Oct 21 14:36:22 2006