2
2
* paths.c: a path manipulation library using svn_stringbuf_t
4
4
* ====================================================================
5
* Copyright (c) 2000-2007, 2009 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
5
* Licensed to the Apache Software Foundation (ASF) under one
6
* or more contributor license agreements. See the NOTICE file
7
* distributed with this work for additional information
8
* regarding copyright ownership. The ASF licenses this file
9
* to you under the Apache License, Version 2.0 (the
10
* "License"); you may not use this file except in compliance
11
* with the License. You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* Unless required by applicable law or agreed to in writing,
16
* software distributed under the License is distributed on an
17
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
* KIND, either express or implied. See the License for the
19
* specific language governing permissions and limitations
16
21
* ====================================================================
49
55
#define SVN_PATH_IS_PLATFORM_EMPTY(s,n) ((n) == 1 && (s)[0] == '.')
53
svn_path_internal_style(const char *path, apr_pool_t *pool)
55
#if defined(WIN32) || defined(__CYGWIN__)
56
if ((path[0] == '/' || path[0] == '\\') && path[1] == path[0])
57
return svn_dirent_internal_style(path, pool);
59
return svn_uri_internal_style(path, pool);
64
svn_path_local_style(const char *path, apr_pool_t *pool)
66
#if defined(WIN32) || defined(__CYGWIN__)
67
if (path[0] == '/' && path[1] == '/')
68
return svn_dirent_local_style(path, pool);
70
return svn_uri_local_style(path, pool);
95
81
return (! SVN_PATH_IS_PLATFORM_EMPTY(path, len)
96
82
&& strstr(path, "/./") == NULL
84
|| (len == 1 && path[0] == '/')
85
|| (path[len-1] != '/')
86
#if defined(WIN32) || defined(__CYGWIN__)
98
87
|| svn_dirent_is_root(path, len)
99
/* The len > 0 check is redundant, but here to make
100
* sure we never ever end up indexing with -1. */
101
|| (len > 0 && path[len-1] != '/')));
391
379
return apr_pstrmemdup(pool, path + start, len - start);
396
svn_path_split(const char *path,
397
const char **dirpath,
398
const char **base_name,
401
assert(dirpath != base_name);
404
*dirpath = svn_path_dirname(path, pool);
407
*base_name = svn_path_basename(path, pool);
412
383
svn_path_is_empty(const char *path)
457
428
return (unsigned char)(path1[i]) < (unsigned char)(path2[i]) ? -1 : 1;
431
/* Return the string length of the longest common ancestor of PATH1 and PATH2.
433
* This function handles everything except the URL-handling logic
434
* of svn_path_get_longest_ancestor, and assumes that PATH1 and
435
* PATH2 are *not* URLs.
437
* If the two paths do not share a common ancestor, return 0.
439
* New strings are allocated in POOL.
442
get_path_ancestor_length(const char *path1,
446
apr_size_t path1_len, path2_len;
448
apr_size_t last_dirsep = 0;
450
path1_len = strlen(path1);
451
path2_len = strlen(path2);
453
if (SVN_PATH_IS_EMPTY(path1) || SVN_PATH_IS_EMPTY(path2))
456
while (path1[i] == path2[i])
458
/* Keep track of the last directory separator we hit. */
464
/* If we get to the end of either path, break out. */
465
if ((i == path1_len) || (i == path2_len))
469
/* two special cases:
470
1. '/' is the longest common ancestor of '/' and '/foo'
471
2. '/' is the longest common ancestor of '/rif' and '/raf' */
472
if (i == 1 && path1[0] == '/' && path2[0] == '/')
475
/* last_dirsep is now the offset of the last directory separator we
476
crossed before reaching a non-matching byte. i is the offset of
477
that non-matching byte. */
478
if (((i == path1_len) && (path2[i] == '/'))
479
|| ((i == path2_len) && (path1[i] == '/'))
480
|| ((i == path1_len) && (i == path2_len)))
483
if (last_dirsep == 0 && path1[0] == '/' && path2[0] == '/')
461
490
svn_path_get_longest_ancestor(const char *path1,
462
491
const char *path2,
463
492
apr_pool_t *pool)
465
return svn_uri_get_longest_ancestor(path1, path2, pool);
494
svn_boolean_t path1_is_url = svn_path_is_url(path1);
495
svn_boolean_t path2_is_url = svn_path_is_url(path2);
497
/* Are we messing with URLs? If we have a mix of URLs and non-URLs,
498
there's nothing common between them. */
499
if (path1_is_url && path2_is_url)
501
return svn_uri_get_longest_ancestor(path1, path2, pool);
503
else if ((! path1_is_url) && (! path2_is_url))
505
return apr_pstrndup(pool, path1,
506
get_path_ancestor_length(path1, path2, pool));
510
/* A URL and a non-URL => no common prefix */
511
return apr_pmemdup(pool, SVN_EMPTY_PATH, sizeof(SVN_EMPTY_PATH));
470
517
const char *path2,
471
518
apr_pool_t *pool)
473
return svn_uri_is_child(path1, path2, pool);
522
/* assert (is_canonical (path1, strlen (path1))); ### Expensive strlen */
523
/* assert (is_canonical (path2, strlen (path2))); ### Expensive strlen */
525
/* Allow "" and "foo" to be parent/child */
526
if (SVN_PATH_IS_EMPTY(path1)) /* "" is the parent */
528
if (SVN_PATH_IS_EMPTY(path2) /* "" not a child */
529
|| path2[0] == '/') /* "/foo" not a child */
532
/* everything else is child */
533
return pool ? apr_pstrdup(pool, path2) : path2;
536
/* Reach the end of at least one of the paths. How should we handle
537
things like path1:"foo///bar" and path2:"foo/bar/baz"? It doesn't
538
appear to arise in the current Subversion code, it's not clear to me
539
if they should be parent/child or not. */
540
for (i = 0; path1[i] && path2[i]; i++)
541
if (path1[i] != path2[i])
544
/* There are two cases that are parent/child
546
.../foo path2[i] == '/'
551
if (path1[i] == '\0' && path2[i])
554
return pool ? apr_pstrdup(pool, path2 + i + 1) : path2 + i + 1;
555
else if (i == 1 && path1[0] == '/')
556
return pool ? apr_pstrdup(pool, path2 + 1) : path2 + 1;
559
/* Otherwise, path2 isn't a child. */
478
565
svn_path_is_ancestor(const char *path1, const char *path2)
480
return svn_uri_is_ancestor(path1, path2);
567
apr_size_t path1_len = strlen(path1);
569
/* If path1 is empty and path2 is not absoulte, then path1 is an ancestor. */
570
if (SVN_PATH_IS_EMPTY(path1))
571
return *path2 != '/';
573
/* If path1 is a prefix of path2, then:
574
- If path1 ends in a path separator,
575
- If the paths are of the same length
577
- path2 starts a new path component after the common prefix,
578
then path1 is an ancestor. */
579
if (strncmp(path1, path2, path1_len) == 0)
580
return path1[path1_len - 1] == '/'
581
|| (path2[path1_len] == '/' || path2[path1_len] == '\0');
705
808
alphanum | mark | ":" | "@" | "&" | "=" | "+" | "$" | ","
707
static const char uri_char_validity[256] = {
810
const char svn_uri__char_validity[256] = {
708
811
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
709
812
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
710
813
0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
756
859
/* Allow '%XX' (where each X is a hex digit) */
757
860
if (path[i] == '%')
759
if (apr_isxdigit(path[i + 1]) && apr_isxdigit(path[i + 2]))
862
if (svn_ctype_isxdigit(path[i + 1]) &&
863
svn_ctype_isxdigit(path[i + 2]))
766
else if (! uri_char_validity[((unsigned char)path[i])])
870
else if (! svn_uri__char_validity[((unsigned char)path[i])])
800
904
svn_stringbuf_appendbytes(retstr, path + copied,
803
/* Now, sprintf() in our escaped character, making sure our
804
buffer is big enough to hold the '%' and two digits. We cast
805
the C to unsigned char here because the 'X' format character
806
will be tempted to treat it as an unsigned int...which causes
807
problem when messing with 0x80-0xFF chars. We also need space
808
for a null as sprintf will write one. */
907
/* Now, write in our escaped character, consisting of the
908
'%' and two digits. We cast the C to unsigned char here because
909
the 'X' format character will be tempted to treat it as an unsigned
910
int...which causes problem when messing with 0x80-0xFF chars.
911
We also need space for a null as apr_snprintf will write one. */
809
912
svn_stringbuf_ensure(retstr, retstr->len + 4);
810
sprintf(retstr->data + retstr->len, "%%%02X", (unsigned char)c);
913
apr_snprintf(retstr->data + retstr->len, 4, "%%%02X", (unsigned char)c);
811
914
retstr->len += 3;
813
916
/* Finally, update our copy counter. */
952
1055
const char *component,
953
1056
apr_pool_t *pool)
955
assert(svn_path_is_canonical(url, pool));
1058
/* = svn_path_uri_encode() but without always copying */
1059
component = uri_escape(component, svn_uri__char_validity, pool);
957
return svn_path_join(url, svn_path_uri_encode(component, pool), pool);
1061
return svn_path_join(url, component, pool);
971
1075
return svn_dirent_get_absolute(pabsolute, relative, pool);
976
svn_path_split_if_file(const char *path,
977
const char **pdirectory,
984
SVN_ERR_ASSERT(svn_path_is_canonical(path, pool));
986
err = svn_io_stat(&finfo, path, APR_FINFO_TYPE, pool);
987
if (err && ! APR_STATUS_IS_ENOENT(err->apr_err))
990
if (err || finfo.filetype == APR_REG)
992
svn_error_clear(err);
993
svn_path_split(path, pdirectory, pfile, pool);
995
else if (finfo.filetype == APR_DIR)
998
*pfile = SVN_EMPTY_PATH;
1002
return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
1003
_("'%s' is neither a file nor a directory name"),
1004
svn_path_local_style(path, pool));
1007
return SVN_NO_ERROR;
1012
svn_path_canonicalize(const char *path, apr_pool_t *pool)
1014
#if defined(WIN32) || defined(__CYGWIN__)
1015
if (path[0] == '/' && path[1] == '/')
1016
return svn_dirent_canonicalize(path, pool);
1018
return svn_uri_canonicalize(path, pool);
1022
svn_path_is_canonical(const char *path, apr_pool_t *pool)
1024
return svn_uri_is_canonical(path, pool)
1025
#if defined(WIN32) || defined(__CYGWIN__)
1026
|| (path[0] == '/' && path[1] == '/' &&
1027
svn_dirent_is_canonical(path, pool))
1033
1080
/** Get APR's internal path encoding. */
1118
1165
/*### The backslash separator doesn't work too great with Windows,
1119
1166
but it's what we'll use for consistency with invalid utf8
1120
1167
formatting (until someone has a better idea) */
1121
sprintf(retstr->data + retstr->len, "\\%03o", (unsigned char)c);
1168
apr_snprintf(retstr->data + retstr->len, 5, "\\%03o", (unsigned char)c);
1122
1169
retstr->len += 4;
1124
1171
/* Finally, update our copy counter. */
1152
1199
(SVN_ERR_FS_PATH_SYNTAX, NULL,
1153
1200
_("Invalid control character '0x%02x' in path '%s'"),
1154
1201
(unsigned char)*c,
1155
illegal_path_escape(svn_path_local_style(path, pool), pool));
1202
illegal_path_escape(svn_dirent_local_style(path, pool), pool));