#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <apr_uri.h>
#include <apr_lib.h>
#include "svn_private_config.h"
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_ctype.h"
#include "dirent_uri.h"
#include "private/svn_fspath.h"
#include "private/svn_cert.h"
#define SVN_EMPTY_PATH ""
#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0')
#define SVN_PATH_IS_PLATFORM_EMPTY(s,n) ((n) == 1 && (s)[0] == '.')
#if defined(WIN32) || defined(__CYGWIN__) || defined(__OS2__)
#define SVN_USE_DOS_PATHS
#endif
typedef enum path_type_t {
type_uri,
type_dirent,
type_relpath
} path_type_t;
static svn_boolean_t
relpath_is_canonical(const char *relpath);
static const char *
internal_style(const char *path, apr_pool_t *pool)
{
#if '/' != SVN_PATH_LOCAL_SEPARATOR
{
char *p = apr_pstrdup(pool, path);
path = p;
for (; *p != '\0'; ++p)
if (*p == SVN_PATH_LOCAL_SEPARATOR)
*p = '/';
}
#endif
return path;
}
static char
canonicalize_to_lower(char c)
{
if (c < 'A' || c > 'Z')
return c;
else
return (char)(c - 'A' + 'a');
}
static char
canonicalize_to_upper(char c)
{
if (c < 'a' || c > 'z')
return c;
else
return (char)(c - 'a' + 'A');
}
static apr_size_t
dirent_root_length(const char *dirent, apr_size_t len)
{
#ifdef SVN_USE_DOS_PATHS
if (len >= 2 && dirent[1] == ':' &&
((dirent[0] >= 'A' && dirent[0] <= 'Z') ||
(dirent[0] >= 'a' && dirent[0] <= 'z')))
{
return (len > 2 && dirent[2] == '/') ? 3 : 2;
}
if (len > 2 && dirent[0] == '/' && dirent[1] == '/')
{
apr_size_t i = 2;
while (i < len && dirent[i] != '/')
i++;
if (i == len)
return len;
i++;
while (i < len && dirent[i] != '/')
i++;
return i;
}
#endif
if (len >= 1 && dirent[0] == '/')
return 1;
return 0;
}
static apr_size_t
dirent_previous_segment(const char *dirent,
apr_size_t len)
{
if (len == 0)
return 0;
--len;
while (len > 0 && dirent[len] != '/'
#ifdef SVN_USE_DOS_PATHS
&& (dirent[len] != ':' || len != 1)
#endif
)
--len;
if (dirent_root_length(dirent, len+1) == len + 1)
return len + 1;
else
return len;
}
static apr_size_t
uri_schema_root_length(const char *uri, apr_size_t len)
{
apr_size_t i;
for (i = 0; i < len; i++)
{
if (uri[i] == '/')
{
if (i > 0 && uri[i-1] == ':' && i < len-1 && uri[i+1] == '/')
{
if (i == 5 && strncmp("file", uri, 4) == 0)
return 7;
else
{
for (i += 2; i < len; i++)
if (uri[i] == '/')
return i;
return len;
}
}
else
return 0;
}
}
return 0;
}
static svn_boolean_t
dirent_is_rooted(const char *dirent)
{
if (! dirent)
return FALSE;
if (dirent[0] == '/')
return TRUE;
#ifdef SVN_USE_DOS_PATHS
if (((dirent[0] >= 'A' && dirent[0] <= 'Z') ||
(dirent[0] >= 'a' && dirent[0] <= 'z')) &&
(dirent[1] == ':'))
return TRUE;
#endif
return FALSE;
}
static apr_size_t
relpath_previous_segment(const char *relpath,
apr_size_t len)
{
if (len == 0)
return 0;
--len;
while (len > 0 && relpath[len] != '/')
--len;
return len;
}
static apr_size_t
uri_previous_segment(const char *uri,
apr_size_t len)
{
apr_size_t root_length;
apr_size_t i = len;
if (len == 0)
return 0;
root_length = uri_schema_root_length(uri, len);
--i;
while (len > root_length && uri[i] != '/')
--i;
if (i == 0 && len > 1 && *uri == '/')
return 1;
return i;
}
static const char *
canonicalize(path_type_t type, const char *path, apr_pool_t *pool)
{
char *canon, *dst;
const char *src;
apr_size_t seglen;
apr_size_t schemelen = 0;
apr_size_t canon_segments = 0;
svn_boolean_t url = FALSE;
char *schema_data = NULL;
if (SVN_PATH_IS_EMPTY(path))
{
assert(type != type_uri);
return "";
}
dst = canon = apr_pcalloc(pool, strlen(path) + 1);
src = path;
if (type == type_uri)
{
assert(*src != '/');
while (*src && (*src != '/') && (*src != ':'))
src++;
if (*src == ':' && *(src+1) == '/' && *(src+2) == '/')
{
const char *seg;
url = TRUE;
src = path;
while (*src != ':')
{
*(dst++) = canonicalize_to_lower((*src++));
schemelen++;
}
*(dst++) = ':';
*(dst++) = '/';
*(dst++) = '/';
src += 3;
schemelen += 3;
seg = src;
while (*src && (*src != '/') && (*src != '@'))
src++;
if (*src == '@')
{
seglen = src - seg + 1;
memcpy(dst, seg, seglen);
dst += seglen;
src++;
}
else
src = seg;
if (*src == '[')
{
*(dst++) = *(src++);
while (*src == ':'
|| (*src >= '0' && (*src <= '9'))
|| (*src >= 'a' && (*src <= 'f'))
|| (*src >= 'A' && (*src <= 'F')))
{
*(dst++) = canonicalize_to_lower((*src++));
}
if (*src == ']')
*(dst++) = *(src++);
}
else
while (*src && (*src != '/') && (*src != ':'))
*(dst++) = canonicalize_to_lower((*src++));
if (*src == ':')
{
if (src[1] == '8' && src[2] == '0'
&& (src[3]== '/'|| !src[3])
&& !strncmp(canon, "http:", 5))
{
src += 3;
}
else if (src[1] == '4' && src[2] == '4' && src[3] == '3'
&& (src[4]== '/'|| !src[4])
&& !strncmp(canon, "https:", 6))
{
src += 4;
}
else if (src[1] == '3' && src[2] == '6'
&& src[3] == '9' && src[4] == '0'
&& (src[5]== '/'|| !src[5])
&& !strncmp(canon, "svn:", 4))
{
src += 5;
}
else if (src[1] == '/' || !src[1])
{
src += 1;
}
while (*src && (*src != '/'))
*(dst++) = canonicalize_to_lower((*src++));
}
*(dst) = *(src);
if (*src)
{
src++;
dst++;
schema_data = dst;
}
canon_segments = 1;
}
}
if (! url && type != type_relpath)
{
src = path;
if (*src == '/')
{
*(dst++) = *(src++);
#ifdef SVN_USE_DOS_PATHS
if ((type == type_dirent) && *src == '/')
*(dst++) = *(src++);
#endif
}
#ifdef SVN_USE_DOS_PATHS
else if (type == type_dirent &&
((*src >= 'a' && *src <= 'z') ||
(*src >= 'A' && *src <= 'Z')) &&
(src[1] == ':'))
{
*(dst++) = canonicalize_to_upper(*(src++));
}
#endif
}
while (*src)
{
const char *next = src;
apr_size_t slash_len = 0;
while (*next
&& (next[0] != '/')
&& (! (type == type_uri && next[0] == '%' && next[1] == '2' &&
canonicalize_to_upper(next[2]) == 'F')))
{
++next;
}
if (next[0] == '/')
slash_len = 1;
else if (type == type_uri && next[0] == '%')
slash_len = 3;
seglen = next - src;
if (seglen == 0
|| (seglen == 1 && src[0] == '.')
|| (type == type_uri && seglen == 3 && src[0] == '%' && src[1] == '2'
&& canonicalize_to_upper(src[2]) == 'E'))
{
}
#ifdef SVN_USE_DOS_PATHS
else if (url && canon_segments == 1 && seglen >= 2 &&
(strncmp(canon, "file:", 5) == 0) &&
src[0] >= 'a' && src[0] <= 'z' && src[1] == ':')
{
*(dst++) = canonicalize_to_upper(src[0]);
*(dst++) = ':';
if (seglen > 2)
{
memcpy(dst, src + 2, seglen - 2);
dst += seglen - 2;
}
if (slash_len)
*(dst++) = '/';
canon_segments++;
}
#endif
else
{
memcpy(dst, src, seglen);
dst += seglen;
if (slash_len)
*(dst++) = '/';
canon_segments++;
}
src = next + slash_len;
}
if ((canon_segments > 0 && *(dst - 1) == '/')
&& ! (url && path[schemelen] == '\0'))
{
dst --;
}
*dst = '\0';
#ifdef SVN_USE_DOS_PATHS
if ((type == type_dirent) && canon[0] == '/' && canon[1] == '/')
{
if (canon_segments < 2)
return canon + 1;
else
{
for (dst = canon + 2; *dst && *dst != '/'; dst++)
*dst = canonicalize_to_lower(*dst);
}
}
#endif
if (schema_data)
{
int need_extra = 0;
src = schema_data;
while (*src)
{
switch (*src)
{
case '/':
break;
case '%':
if (!svn_ctype_isxdigit(*(src+1)) ||
!svn_ctype_isxdigit(*(src+2)))
need_extra += 2;
else
src += 2;
break;
default:
if (!svn_uri__char_validity[(unsigned char)*src])
need_extra += 2;
break;
}
src++;
}
if (need_extra > 0)
{
apr_size_t pre_schema_size = (apr_size_t)(schema_data - canon);
dst = apr_palloc(pool, (apr_size_t)(src - canon) + need_extra + 1);
memcpy(dst, canon, pre_schema_size);
canon = dst;
dst += pre_schema_size;
}
else
dst = schema_data;
src = schema_data;
while (*src)
{
switch (*src)
{
case '/':
*(dst++) = '/';
break;
case '%':
if (!svn_ctype_isxdigit(*(src+1)) ||
!svn_ctype_isxdigit(*(src+2)))
{
*(dst++) = '%';
*(dst++) = '2';
*(dst++) = '5';
}
else
{
char digitz[3];
int val;
digitz[0] = *(++src);
digitz[1] = *(++src);
digitz[2] = 0;
val = (int)strtol(digitz, NULL, 16);
if (svn_uri__char_validity[(unsigned char)val])
*(dst++) = (char)val;
else
{
*(dst++) = '%';
*(dst++) = canonicalize_to_upper(digitz[0]);
*(dst++) = canonicalize_to_upper(digitz[1]);
}
}
break;
default:
if (!svn_uri__char_validity[(unsigned char)*src])
{
apr_snprintf(dst, 4, "%%%02X", (unsigned char)*src);
dst += 3;
}
else
*(dst++) = *src;
break;
}
src++;
}
*dst = '\0';
}
return canon;
}
static apr_size_t
get_longest_ancestor_length(path_type_t types,
const char *path1,
const char *path2,
apr_pool_t *pool)
{
apr_size_t path1_len, path2_len;
apr_size_t i = 0;
apr_size_t last_dirsep = 0;
#ifdef SVN_USE_DOS_PATHS
svn_boolean_t unc = FALSE;
#endif
path1_len = strlen(path1);
path2_len = strlen(path2);
if (SVN_PATH_IS_EMPTY(path1) || SVN_PATH_IS_EMPTY(path2))
return 0;
while (path1[i] == path2[i])
{
if (path1[i] == '/')
last_dirsep = i;
i++;
if ((i == path1_len) || (i == path2_len))
break;
}
if (i == 1 && path1[0] == '/' && path2[0] == '/')
return 1;
if (types == type_dirent && i == 0)
return 0;
#ifdef SVN_USE_DOS_PATHS
if (types == type_dirent)
{
if (last_dirsep == 1 && path1[0] == '/' && path1[1] == '/')
{
last_dirsep = 0;
unc = TRUE;
}
if (i == 3 && path1[2] == '/' && path1[1] == ':')
return i;
assert(i > 0);
if ((path1[i - 1] == ':' && path2[i] == '/') ||
(path2[i - 1] == ':' && path1[i] == '/'))
return 0;
if (path1[i - 1] == ':' || path2[i - 1] == ':')
return i;
}
#endif
if (((i == path1_len) && (path2[i] == '/'))
|| ((i == path2_len) && (path1[i] == '/'))
|| ((i == path1_len) && (i == path2_len)))
return i;
else
{
#ifdef SVN_USE_DOS_PATHS
if (! unc)
{
if ((types == type_dirent) &&
last_dirsep == 2 && path1[1] == ':' && path1[2] == '/'
&& path2[1] == ':' && path2[2] == '/')
return 3;
#endif
if (last_dirsep == 0 && path1[0] == '/' && path2[0] == '/')
return 1;
#ifdef SVN_USE_DOS_PATHS
}
#endif
}
return last_dirsep;
}
static const char *
is_child(path_type_t type, const char *path1, const char *path2,
apr_pool_t *pool)
{
apr_size_t i;
if (SVN_PATH_IS_EMPTY(path1))
{
if (SVN_PATH_IS_EMPTY(path2))
return NULL;
if ((type == type_uri) ||
(type == type_dirent && dirent_is_rooted(path2)))
return NULL;
else
return pool ? apr_pstrdup(pool, path2) : path2;
}
for (i = 0; path1[i] && path2[i]; i++)
if (path1[i] != path2[i])
return NULL;
if (path1[i] == '\0' && path2[i])
{
if (path1[i - 1] == '/'
#ifdef SVN_USE_DOS_PATHS
|| ((type == type_dirent) && path1[i - 1] == ':')
#endif
)
{
if (path2[i] == '/')
return NULL;
else
return pool ? apr_pstrdup(pool, path2 + i) : path2 + i;
}
else if (path2[i] == '/')
{
if (path2[i + 1])
return pool ? apr_pstrdup(pool, path2 + i + 1) : path2 + i + 1;
else
return NULL;
}
}
return NULL;
}
const char *
svn_dirent_internal_style(const char *dirent, apr_pool_t *pool)
{
return svn_dirent_canonicalize(internal_style(dirent, pool), pool);
}
const char *
svn_dirent_local_style(const char *dirent, apr_pool_t *pool)
{
if (SVN_PATH_IS_EMPTY(dirent))
return ".";
#if '/' != SVN_PATH_LOCAL_SEPARATOR
{
char *p = apr_pstrdup(pool, dirent);
dirent = p;
for (; *p != '\0'; ++p)
if (*p == '/')
*p = SVN_PATH_LOCAL_SEPARATOR;
}
#endif
return dirent;
}
const char *
svn_relpath__internal_style(const char *relpath,
apr_pool_t *pool)
{
return svn_relpath_canonicalize(internal_style(relpath, pool), pool);
}
svn_boolean_t
svn_dirent_is_root(const char *dirent, apr_size_t len)
{
#ifdef SVN_USE_DOS_PATHS
if ((len == 2 || ((len == 3) && (dirent[2] == '/'))) &&
(dirent[1] == ':') &&
((dirent[0] >= 'A' && dirent[0] <= 'Z') ||
(dirent[0] >= 'a' && dirent[0] <= 'z')))
return TRUE;
if (len >= 2 && dirent[0] == '/' && dirent[1] == '/'
&& dirent[len - 1] != '/')
{
int segments = 0;
apr_size_t i;
for (i = len; i >= 2; i--)
{
if (dirent[i] == '/')
{
segments ++;
if (segments > 1)
return FALSE;
}
}
#ifdef __CYGWIN__
return (segments <= 1);
#else
return (segments == 1);
#endif
}
#endif
if (len == 1 && dirent[0] == '/')
return TRUE;
return FALSE;
}
svn_boolean_t
svn_uri_is_root(const char *uri, apr_size_t len)
{
assert(svn_uri_is_canonical(uri, NULL));
return (len == uri_schema_root_length(uri, len));
}
char *svn_dirent_join(const char *base,
const char *component,
apr_pool_t *pool)
{
apr_size_t blen = strlen(base);
apr_size_t clen = strlen(component);
char *dirent;
int add_separator;
assert(svn_dirent_is_canonical(base, pool));
assert(svn_dirent_is_canonical(component, pool));
if (svn_dirent_is_absolute(component))
return apr_pmemdup(pool, component, clen + 1);
if (SVN_PATH_IS_EMPTY(base))
return apr_pmemdup(pool, component, clen + 1);
if (SVN_PATH_IS_EMPTY(component))
return apr_pmemdup(pool, base, blen + 1);
#ifdef SVN_USE_DOS_PATHS
if (component[0] == '/')
{
if (dirent_is_rooted(base))
{
blen = dirent_root_length(base, blen);
component++;
clen--;
if (blen == 2 && base[1] == ':')
{
char *root = apr_pmemdup(pool, base, 3);
root[2] = '/';
base = root;
blen = 3;
}
if (clen == 0)
return apr_pstrndup(pool, base, blen);
}
else
return apr_pmemdup(pool, component, clen + 1);
}
else if (dirent_is_rooted(component))
return apr_pmemdup(pool, component, clen + 1);
#endif
add_separator = 1;
if (base[blen - 1] == '/'
#ifdef SVN_USE_DOS_PATHS
|| base[blen - 1] == ':'
#endif
)
add_separator = 0;
dirent = apr_palloc(pool, blen + add_separator + clen + 1);
memcpy(dirent, base, blen);
if (add_separator)
dirent[blen] = '/';
memcpy(dirent + blen + add_separator, component, clen + 1);
return dirent;
}
char *svn_dirent_join_many(apr_pool_t *pool, const char *base, ...)
{
#define MAX_SAVED_LENGTHS 10
apr_size_t saved_lengths[MAX_SAVED_LENGTHS];
apr_size_t total_len;
int nargs;
va_list va;
const char *s;
apr_size_t len;
char *dirent;
char *p;
int add_separator;
int base_arg = 0;
total_len = strlen(base);
assert(svn_dirent_is_canonical(base, pool));
add_separator = 1;
if (total_len == 0
|| base[total_len - 1] == '/'
#ifdef SVN_USE_DOS_PATHS
|| base[total_len - 1] == ':'
#endif
)
add_separator = 0;
saved_lengths[0] = total_len;
nargs = 0;
va_start(va, base);
while ((s = va_arg(va, const char *)) != NULL)
{
len = strlen(s);
assert(svn_dirent_is_canonical(s, pool));
if (SVN_PATH_IS_EMPTY(s))
continue;
if (nargs++ < MAX_SAVED_LENGTHS)
saved_lengths[nargs] = len;
if (dirent_is_rooted(s))
{
total_len = len;
base_arg = nargs;
#ifdef SVN_USE_DOS_PATHS
if (!svn_dirent_is_absolute(s))
{
base = s = svn_dirent_join(base, s, pool);
base_arg++;
saved_lengths[0] = total_len = len = strlen(s);
}
else
#endif
{
base = "";
saved_lengths[0] = 0;
}
add_separator = 1;
if (s[len - 1] == '/'
#ifdef SVN_USE_DOS_PATHS
|| s[len - 1] == ':'
#endif
)
add_separator = 0;
}
else if (nargs <= base_arg + 1)
{
total_len += add_separator + len;
}
else
{
total_len += 1 + len;
}
}
va_end(va);
if (add_separator == 0 && total_len == 1)
return apr_pmemdup(pool, "/", 2);
dirent = p = apr_palloc(pool, total_len + 1);
if (! SVN_PATH_IS_EMPTY(base))
{
memcpy(p, base, len = saved_lengths[0]);
p += len;
}
nargs = 0;
va_start(va, base);
while ((s = va_arg(va, const char *)) != NULL)
{
if (SVN_PATH_IS_EMPTY(s))
continue;
if (++nargs < base_arg)
continue;
if (nargs < MAX_SAVED_LENGTHS)
len = saved_lengths[nargs];
else
len = strlen(s);
if (p != dirent &&
( ! (nargs - 1 <= base_arg) || add_separator))
*p++ = '/';
memcpy(p, s, len);
p += len;
}
va_end(va);
*p = '\0';
assert((apr_size_t)(p - dirent) == total_len);
return dirent;
}
char *
svn_relpath_join(const char *base,
const char *component,
apr_pool_t *pool)
{
apr_size_t blen = strlen(base);
apr_size_t clen = strlen(component);
char *path;
assert(relpath_is_canonical(base));
assert(relpath_is_canonical(component));
if (blen == 0)
return apr_pmemdup(pool, component, clen + 1);
if (clen == 0)
return apr_pmemdup(pool, base, blen + 1);
path = apr_palloc(pool, blen + 1 + clen + 1);
memcpy(path, base, blen);
path[blen] = '/';
memcpy(path + blen + 1, component, clen + 1);
return path;
}
char *
svn_dirent_dirname(const char *dirent, apr_pool_t *pool)
{
apr_size_t len = strlen(dirent);
assert(svn_dirent_is_canonical(dirent, pool));
if (len == dirent_root_length(dirent, len))
return apr_pstrmemdup(pool, dirent, len);
else
return apr_pstrmemdup(pool, dirent, dirent_previous_segment(dirent, len));
}
const char *
svn_dirent_basename(const char *dirent, apr_pool_t *pool)
{
apr_size_t len = strlen(dirent);
apr_size_t start;
assert(!pool || svn_dirent_is_canonical(dirent, pool));
if (svn_dirent_is_root(dirent, len))
return "";
else
{
start = len;
while (start > 0 && dirent[start - 1] != '/'
#ifdef SVN_USE_DOS_PATHS
&& dirent[start - 1] != ':'
#endif
)
--start;
}
if (pool)
return apr_pstrmemdup(pool, dirent + start, len - start);
else
return dirent + start;
}
void
svn_dirent_split(const char **dirpath,
const char **base_name,
const char *dirent,
apr_pool_t *pool)
{
assert(dirpath != base_name);
if (dirpath)
*dirpath = svn_dirent_dirname(dirent, pool);
if (base_name)
*base_name = svn_dirent_basename(dirent, pool);
}
char *
svn_relpath_dirname(const char *relpath,
apr_pool_t *pool)
{
apr_size_t len = strlen(relpath);
assert(relpath_is_canonical(relpath));
return apr_pstrmemdup(pool, relpath,
relpath_previous_segment(relpath, len));
}
const char *
svn_relpath_basename(const char *relpath,
apr_pool_t *pool)
{
apr_size_t len = strlen(relpath);
apr_size_t start;
assert(relpath_is_canonical(relpath));
start = len;
while (start > 0 && relpath[start - 1] != '/')
--start;
if (pool)
return apr_pstrmemdup(pool, relpath + start, len - start);
else
return relpath + start;
}
void
svn_relpath_split(const char **dirpath,
const char **base_name,
const char *relpath,
apr_pool_t *pool)
{
assert(dirpath != base_name);
if (dirpath)
*dirpath = svn_relpath_dirname(relpath, pool);
if (base_name)
*base_name = svn_relpath_basename(relpath, pool);
}
const char *
svn_relpath_prefix(const char *relpath,
int max_components,
apr_pool_t *result_pool)
{
const char *end;
assert(relpath_is_canonical(relpath));
if (max_components <= 0)
return "";
for (end = relpath; *end; end++)
{
if (*end == '/')
{
if (!--max_components)
break;
}
}
return apr_pstrmemdup(result_pool, relpath, end-relpath);
}
char *
svn_uri_dirname(const char *uri, apr_pool_t *pool)
{
apr_size_t len = strlen(uri);
assert(svn_uri_is_canonical(uri, pool));
if (svn_uri_is_root(uri, len))
return apr_pstrmemdup(pool, uri, len);
else
return apr_pstrmemdup(pool, uri, uri_previous_segment(uri, len));
}
const char *
svn_uri_basename(const char *uri, apr_pool_t *pool)
{
apr_size_t len = strlen(uri);
apr_size_t start;
assert(svn_uri_is_canonical(uri, NULL));
if (svn_uri_is_root(uri, len))
return "";
start = len;
while (start > 0 && uri[start - 1] != '/')
--start;
return svn_path_uri_decode(uri + start, pool);
}
void
svn_uri_split(const char **dirpath,
const char **base_name,
const char *uri,
apr_pool_t *pool)
{
assert(dirpath != base_name);
if (dirpath)
*dirpath = svn_uri_dirname(uri, pool);
if (base_name)
*base_name = svn_uri_basename(uri, pool);
}
char *
svn_dirent_get_longest_ancestor(const char *dirent1,
const char *dirent2,
apr_pool_t *pool)
{
return apr_pstrndup(pool, dirent1,
get_longest_ancestor_length(type_dirent, dirent1,
dirent2, pool));
}
char *
svn_relpath_get_longest_ancestor(const char *relpath1,
const char *relpath2,
apr_pool_t *pool)
{
assert(relpath_is_canonical(relpath1));
assert(relpath_is_canonical(relpath2));
return apr_pstrndup(pool, relpath1,
get_longest_ancestor_length(type_relpath, relpath1,
relpath2, pool));
}
char *
svn_uri_get_longest_ancestor(const char *uri1,
const char *uri2,
apr_pool_t *pool)
{
apr_size_t uri_ancestor_len;
apr_size_t i = 0;
assert(svn_uri_is_canonical(uri1, NULL));
assert(svn_uri_is_canonical(uri2, NULL));
while (1)
{
if (uri1[i] != uri2[i])
return apr_pmemdup(pool, SVN_EMPTY_PATH,
sizeof(SVN_EMPTY_PATH));
if (uri1[i] == ':')
break;
assert((uri1[i] != '\0') && (uri2[i] != '\0'));
i++;
}
i += 3;
uri_ancestor_len = get_longest_ancestor_length(type_uri, uri1 + i,
uri2 + i, pool);
if (uri_ancestor_len == 0 ||
(uri_ancestor_len == 1 && (uri1 + i)[0] == '/'))
return apr_pmemdup(pool, SVN_EMPTY_PATH, sizeof(SVN_EMPTY_PATH));
else
return apr_pstrndup(pool, uri1, uri_ancestor_len + i);
}
const char *
svn_dirent_is_child(const char *parent_dirent,
const char *child_dirent,
apr_pool_t *pool)
{
return is_child(type_dirent, parent_dirent, child_dirent, pool);
}
const char *
svn_dirent_skip_ancestor(const char *parent_dirent,
const char *child_dirent)
{
apr_size_t len = strlen(parent_dirent);
apr_size_t root_len;
if (0 != strncmp(parent_dirent, child_dirent, len))
return NULL;
if (child_dirent[len] == 0)
return "";
root_len = dirent_root_length(child_dirent, strlen(child_dirent));
if (root_len > len)
return NULL;
if (child_dirent[len] == '/')
return child_dirent + len + 1;
if (root_len == len)
return child_dirent + len;
return NULL;
}
const char *
svn_relpath_skip_ancestor(const char *parent_relpath,
const char *child_relpath)
{
apr_size_t len = strlen(parent_relpath);
assert(relpath_is_canonical(parent_relpath));
assert(relpath_is_canonical(child_relpath));
if (len == 0)
return child_relpath;
if (0 != strncmp(parent_relpath, child_relpath, len))
return NULL;
if (child_relpath[len] == 0)
return "";
if (child_relpath[len] == '/')
return child_relpath + len + 1;
return NULL;
}
static const char *
uri_skip_ancestor(const char *parent_uri,
const char *child_uri)
{
apr_size_t len = strlen(parent_uri);
assert(svn_uri_is_canonical(parent_uri, NULL));
assert(svn_uri_is_canonical(child_uri, NULL));
if (0 != strncmp(parent_uri, child_uri, len))
return NULL;
if (child_uri[len] == 0)
return "";
if (child_uri[len] == '/')
return child_uri + len + 1;
return NULL;
}
const char *
svn_uri_skip_ancestor(const char *parent_uri,
const char *child_uri,
apr_pool_t *result_pool)
{
const char *result = uri_skip_ancestor(parent_uri, child_uri);
return result ? svn_path_uri_decode(result, result_pool) : NULL;
}
svn_boolean_t
svn_dirent_is_ancestor(const char *parent_dirent, const char *child_dirent)
{
return svn_dirent_skip_ancestor(parent_dirent, child_dirent) != NULL;
}
svn_boolean_t
svn_uri__is_ancestor(const char *parent_uri, const char *child_uri)
{
return uri_skip_ancestor(parent_uri, child_uri) != NULL;
}
svn_boolean_t
svn_dirent_is_absolute(const char *dirent)
{
if (! dirent)
return FALSE;
if (dirent[0] == '/'
#ifdef SVN_USE_DOS_PATHS
&& dirent[1] == '/'
#endif
)
return TRUE;
#ifdef SVN_USE_DOS_PATHS
if (((dirent[0] >= 'A' && dirent[0] <= 'Z')) &&
(dirent[1] == ':') && (dirent[2] == '/'))
return TRUE;
#endif
return FALSE;
}
svn_error_t *
svn_dirent_get_absolute(const char **pabsolute,
const char *relative,
apr_pool_t *pool)
{
char *buffer;
apr_status_t apr_err;
const char *path_apr;
SVN_ERR_ASSERT(! svn_path_is_url(relative));
SVN_ERR(svn_path_cstring_from_utf8(&path_apr, relative, pool));
apr_err = apr_filepath_merge(&buffer, NULL,
path_apr,
APR_FILEPATH_NOTRELATIVE,
pool);
if (apr_err)
{
if (svn_dirent_is_absolute(relative)
&& svn_dirent_is_canonical(relative, pool)
&& !svn_path_is_backpath_present(relative))
{
*pabsolute = apr_pstrdup(pool, relative);
return SVN_NO_ERROR;
}
return svn_error_createf(SVN_ERR_BAD_FILENAME,
svn_error_create(apr_err, NULL, NULL),
_("Couldn't determine absolute path of '%s'"),
svn_dirent_local_style(relative, pool));
}
SVN_ERR(svn_path_cstring_to_utf8(pabsolute, buffer, pool));
*pabsolute = svn_dirent_canonicalize(*pabsolute, pool);
return SVN_NO_ERROR;
}
const char *
svn_uri_canonicalize(const char *uri, apr_pool_t *pool)
{
return canonicalize(type_uri, uri, pool);
}
const char *
svn_relpath_canonicalize(const char *relpath, apr_pool_t *pool)
{
return canonicalize(type_relpath, relpath, pool);
}
const char *
svn_dirent_canonicalize(const char *dirent, apr_pool_t *pool)
{
const char *dst = canonicalize(type_dirent, dirent, pool);
#ifdef SVN_USE_DOS_PATHS
if (((dirent[0] >= 'A' && dirent[0] <= 'Z') ||
(dirent[0] >= 'a' && dirent[0] <= 'z')) &&
dirent[1] == ':' && dirent[2] == '/' &&
dst[3] == '\0')
{
char *dst_slash = apr_pcalloc(pool, 4);
dst_slash[0] = canonicalize_to_upper(dirent[0]);
dst_slash[1] = ':';
dst_slash[2] = '/';
dst_slash[3] = '\0';
return dst_slash;
}
#endif
return dst;
}
svn_boolean_t
svn_dirent_is_canonical(const char *dirent, apr_pool_t *scratch_pool)
{
const char *ptr = dirent;
if (*ptr == '/')
{
ptr++;
#ifdef SVN_USE_DOS_PATHS
if (*ptr == '/')
{
return (strcmp(dirent, svn_dirent_canonicalize(dirent, scratch_pool))
== 0);
}
#endif
}
#ifdef SVN_USE_DOS_PATHS
else if (((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z')) &&
(ptr[1] == ':'))
{
if (*ptr < 'A' || *ptr > 'Z')
return FALSE;
ptr += 2;
if (*ptr == '/')
ptr++;
}
#endif
return relpath_is_canonical(ptr);
}
static svn_boolean_t
relpath_is_canonical(const char *relpath)
{
const char *dot_pos, *ptr = relpath;
apr_size_t i, len;
unsigned pattern = 0;
if (*ptr == '/')
return FALSE;
if (ptr[0] == '.' && (ptr[1] == '/' || ptr[1] == '\0'))
return FALSE;
len = strlen(ptr);
if (len < 2)
return TRUE;
if (ptr[len-1] == '/' || (ptr[len-1] == '.' && ptr[len-2] == '/'))
return FALSE;
for (dot_pos = memchr(ptr, '.', len);
dot_pos;
dot_pos = strchr(dot_pos+1, '.'))
if (dot_pos > ptr && dot_pos[-1] == '/' && dot_pos[1] == '/')
return FALSE;
for (i = 0; i < len - 1; ++i)
{
pattern = ((pattern & 0xff) << 8) + (unsigned char)ptr[i];
if (pattern == 0x101 * (unsigned char)('/'))
return FALSE;
}
return TRUE;
}
svn_boolean_t
svn_relpath_is_canonical(const char *relpath)
{
return relpath_is_canonical(relpath);
}
svn_boolean_t
svn_uri_is_canonical(const char *uri, apr_pool_t *scratch_pool)
{
const char *ptr = uri, *seg = uri;
const char *schema_data = NULL;
if (*uri == '\0')
return FALSE;
if (! svn_path_is_url(uri))
return FALSE;
while (*ptr && (*ptr != '/') && (*ptr != ':'))
ptr++;
if (! (*ptr == ':' && *(ptr+1) == '/' && *(ptr+2) == '/'))
return FALSE;
ptr = uri;
while (*ptr != ':')
{
if (*ptr >= 'A' && *ptr <= 'Z')
return FALSE;
ptr++;
}
ptr += 3;
if (! *ptr)
return TRUE;
seg = ptr;
while (*ptr && (*ptr != '/') && (*ptr != '@'))
ptr++;
if (*ptr == '@')
seg = ptr + 1;
ptr = seg;
if (*ptr == '[')
{
ptr++;
while (*ptr == ':'
|| (*ptr >= '0' && *ptr <= '9')
|| (*ptr >= 'a' && *ptr <= 'f'))
{
ptr++;
}
if (*ptr != ']')
return FALSE;
ptr++;
}
else
while (*ptr && *ptr != '/' && *ptr != ':')
{
if (*ptr >= 'A' && *ptr <= 'Z')
return FALSE;
ptr++;
}
if (*ptr == ':')
{
apr_int64_t port = 0;
ptr++;
schema_data = ptr;
while (*ptr >= '0' && *ptr <= '9')
{
port = 10 * port + (*ptr - '0');
ptr++;
}
if (ptr == schema_data && (*ptr == '/' || *ptr == '\0'))
return FALSE;
if (port == 80 && strncmp(uri, "http:", 5) == 0)
return FALSE;
else if (port == 443 && strncmp(uri, "https:", 6) == 0)
return FALSE;
else if (port == 3690 && strncmp(uri, "svn:", 4) == 0)
return FALSE;
while (*ptr && *ptr != '/')
++ptr;
}
schema_data = ptr;
#ifdef SVN_USE_DOS_PATHS
if (schema_data && *ptr == '/')
{
if (strncmp(uri, "file:", 5) == 0 &&
! (*(ptr+1) >= 'A' && *(ptr+1) <= 'Z') &&
*(ptr+2) == ':')
return FALSE;
}
#endif
seg = ptr;
while (*ptr && (*ptr != '/'))
ptr++;
while(1)
{
apr_size_t seglen = ptr - seg;
if (seglen == 1 && *seg == '.')
return FALSE;
if (*ptr == '/' && *(ptr+1) == '/')
return FALSE;
if (! *ptr && *(ptr - 1) == '/' && ptr - 1 != uri)
return FALSE;
if (! *ptr)
break;
if (*ptr == '/')
ptr++;
seg = ptr;
while (*ptr && (*ptr != '/'))
ptr++;
}
ptr = schema_data;
while (*ptr)
{
if (*ptr == '%')
{
char digitz[3];
int val;
if (((*(ptr+1) < '0' || *(ptr+1) > '9'))
&& (*(ptr+1) < 'A' || *(ptr+1) > 'F'))
return FALSE;
else if (((*(ptr+2) < '0' || *(ptr+2) > '9'))
&& (*(ptr+2) < 'A' || *(ptr+2) > 'F'))
return FALSE;
digitz[0] = *(++ptr);
digitz[1] = *(++ptr);
digitz[2] = '\0';
val = (int)strtol(digitz, NULL, 16);
if (svn_uri__char_validity[val])
return FALSE;
}
else if (*ptr != '/' && !svn_uri__char_validity[(unsigned char)*ptr])
return FALSE;
ptr++;
}
return TRUE;
}
svn_error_t *
svn_dirent_condense_targets(const char **pcommon,
apr_array_header_t **pcondensed_targets,
const apr_array_header_t *targets,
svn_boolean_t remove_redundancies,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i, num_condensed = targets->nelts;
svn_boolean_t *removed;
apr_array_header_t *abs_targets;
if (targets->nelts <= 0)
{
*pcommon = NULL;
if (pcondensed_targets)
*pcondensed_targets = NULL;
return SVN_NO_ERROR;
}
SVN_ERR(svn_dirent_get_absolute(pcommon,
APR_ARRAY_IDX(targets, 0, const char *),
scratch_pool));
if (targets->nelts == 1)
{
*pcommon = apr_pstrdup(result_pool, *pcommon);
if (pcondensed_targets)
*pcondensed_targets = apr_array_make(result_pool, 0,
sizeof(const char *));
return SVN_NO_ERROR;
}
removed = apr_pcalloc(scratch_pool, (targets->nelts *
sizeof(svn_boolean_t)));
abs_targets = apr_array_make(scratch_pool, targets->nelts,
sizeof(const char *));
APR_ARRAY_PUSH(abs_targets, const char *) = *pcommon;
for (i = 1; i < targets->nelts; ++i)
{
const char *rel = APR_ARRAY_IDX(targets, i, const char *);
const char *absolute;
SVN_ERR(svn_dirent_get_absolute(&absolute, rel, scratch_pool));
APR_ARRAY_PUSH(abs_targets, const char *) = absolute;
*pcommon = svn_dirent_get_longest_ancestor(*pcommon, absolute,
scratch_pool);
}
*pcommon = apr_pstrdup(result_pool, *pcommon);
if (pcondensed_targets != NULL)
{
size_t basedir_len;
if (remove_redundancies)
{
for (i = 0; i < abs_targets->nelts; ++i)
{
int j;
if (removed[i])
continue;
for (j = i + 1; j < abs_targets->nelts; ++j)
{
const char *abs_targets_i;
const char *abs_targets_j;
const char *ancestor;
if (removed[j])
continue;
abs_targets_i = APR_ARRAY_IDX(abs_targets, i, const char *);
abs_targets_j = APR_ARRAY_IDX(abs_targets, j, const char *);
ancestor = svn_dirent_get_longest_ancestor
(abs_targets_i, abs_targets_j, scratch_pool);
if (*ancestor == '\0')
continue;
if (strcmp(ancestor, abs_targets_i) == 0)
{
removed[j] = TRUE;
num_condensed--;
}
else if (strcmp(ancestor, abs_targets_j) == 0)
{
removed[i] = TRUE;
num_condensed--;
}
}
}
for (i = 0; i < abs_targets->nelts; ++i)
{
const char *abs_targets_i = APR_ARRAY_IDX(abs_targets, i,
const char *);
if ((strcmp(abs_targets_i, *pcommon) == 0) && (! removed[i]))
{
removed[i] = TRUE;
num_condensed--;
}
}
}
basedir_len = strlen(*pcommon);
*pcondensed_targets = apr_array_make(result_pool, num_condensed,
sizeof(const char *));
for (i = 0; i < abs_targets->nelts; ++i)
{
const char *rel_item = APR_ARRAY_IDX(abs_targets, i, const char *);
if (removed[i])
continue;
if (basedir_len > 0)
{
rel_item += basedir_len;
if (rel_item[0] &&
! svn_dirent_is_root(*pcommon, basedir_len))
rel_item++;
}
APR_ARRAY_PUSH(*pcondensed_targets, const char *)
= apr_pstrdup(result_pool, rel_item);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_uri_condense_targets(const char **pcommon,
apr_array_header_t **pcondensed_targets,
const apr_array_header_t *targets,
svn_boolean_t remove_redundancies,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i, num_condensed = targets->nelts;
apr_array_header_t *uri_targets;
svn_boolean_t *removed;
if (targets->nelts <= 0)
{
*pcommon = NULL;
if (pcondensed_targets)
*pcondensed_targets = NULL;
return SVN_NO_ERROR;
}
*pcommon = svn_uri_canonicalize(APR_ARRAY_IDX(targets, 0, const char *),
scratch_pool);
if (targets->nelts == 1)
{
*pcommon = apr_pstrdup(result_pool, *pcommon);
if (pcondensed_targets)
*pcondensed_targets = apr_array_make(result_pool, 0,
sizeof(const char *));
return SVN_NO_ERROR;
}
removed = apr_pcalloc(scratch_pool, (targets->nelts *
sizeof(svn_boolean_t)));
uri_targets = apr_array_make(scratch_pool, targets->nelts,
sizeof(const char *));
APR_ARRAY_PUSH(uri_targets, const char *) = *pcommon;
for (i = 1; i < targets->nelts; ++i)
{
const char *uri = svn_uri_canonicalize(
APR_ARRAY_IDX(targets, i, const char *),
scratch_pool);
APR_ARRAY_PUSH(uri_targets, const char *) = uri;
if (**pcommon != '\0')
*pcommon = svn_uri_get_longest_ancestor(*pcommon, uri,
scratch_pool);
}
*pcommon = apr_pstrdup(result_pool, *pcommon);
if (pcondensed_targets != NULL)
{
size_t basedir_len;
if (remove_redundancies)
{
for (i = 0; i < uri_targets->nelts; ++i)
{
int j;
if (removed[i])
continue;
for (j = i + 1; j < uri_targets->nelts; ++j)
{
const char *uri_i;
const char *uri_j;
const char *ancestor;
if (removed[j])
continue;
uri_i = APR_ARRAY_IDX(uri_targets, i, const char *);
uri_j = APR_ARRAY_IDX(uri_targets, j, const char *);
ancestor = svn_uri_get_longest_ancestor(uri_i,
uri_j,
scratch_pool);
if (*ancestor == '\0')
continue;
if (strcmp(ancestor, uri_i) == 0)
{
removed[j] = TRUE;
num_condensed--;
}
else if (strcmp(ancestor, uri_j) == 0)
{
removed[i] = TRUE;
num_condensed--;
}
}
}
for (i = 0; i < uri_targets->nelts; ++i)
{
const char *uri_targets_i = APR_ARRAY_IDX(uri_targets, i,
const char *);
if ((strcmp(uri_targets_i, *pcommon) == 0) && (! removed[i]))
{
removed[i] = TRUE;
num_condensed--;
}
}
}
basedir_len = strlen(*pcommon);
*pcondensed_targets = apr_array_make(result_pool, num_condensed,
sizeof(const char *));
for (i = 0; i < uri_targets->nelts; ++i)
{
const char *rel_item = APR_ARRAY_IDX(uri_targets, i, const char *);
if (removed[i])
continue;
if (basedir_len > 0)
{
rel_item += basedir_len;
if ((rel_item[0] == '/') ||
(rel_item[0] && !svn_uri_is_root(*pcommon, basedir_len)))
{
rel_item++;
}
}
APR_ARRAY_PUSH(*pcondensed_targets, const char *)
= svn_path_uri_decode(rel_item, result_pool);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_dirent_is_under_root(svn_boolean_t *under_root,
const char **result_path,
const char *base_path,
const char *path,
apr_pool_t *result_pool)
{
apr_status_t status;
char *full_path;
*under_root = FALSE;
if (result_path)
*result_path = NULL;
status = apr_filepath_merge(&full_path,
base_path,
path,
APR_FILEPATH_NOTABOVEROOT
| APR_FILEPATH_SECUREROOTTEST,
result_pool);
if (status == APR_SUCCESS)
{
if (result_path)
*result_path = svn_dirent_canonicalize(full_path, result_pool);
*under_root = TRUE;
return SVN_NO_ERROR;
}
else if (status == APR_EABOVEROOT)
{
*under_root = FALSE;
return SVN_NO_ERROR;
}
return svn_error_wrap_apr(status, NULL);
}
svn_error_t *
svn_uri_get_dirent_from_file_url(const char **dirent,
const char *url,
apr_pool_t *pool)
{
const char *hostname, *path;
SVN_ERR_ASSERT(svn_uri_is_canonical(url, pool));
if (strncmp(url, "file://", 7) != 0)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("Local URL '%s' does not contain 'file://' "
"prefix"), url);
hostname = url + 7;
path = strchr(hostname, '/');
if (path)
hostname = apr_pstrmemdup(pool, hostname, path - hostname);
else
path = "/";
if (*hostname == '\0')
hostname = NULL;
else
{
hostname = svn_path_uri_decode(hostname, pool);
if (strcmp(hostname, "localhost") == 0)
hostname = NULL;
}
#ifdef SVN_USE_DOS_PATHS
{
static const char valid_drive_letters[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
char *dup_path = (char *)svn_path_uri_decode(path, pool);
if (!hostname && dup_path[1] && strchr(valid_drive_letters, dup_path[1])
&& (dup_path[2] == ':' || dup_path[2] == '|'))
{
++dup_path;
if (dup_path[1] == '|')
dup_path[1] = ':';
if (dup_path[2] == '/' || dup_path[2] == '\\' || dup_path[2] == '\0')
{
dup_path[0] = canonicalize_to_upper(dup_path[0]);
if (dup_path[2] == '\0')
{
char *new_path = apr_pcalloc(pool, 4);
new_path[0] = dup_path[0];
new_path[1] = ':';
new_path[2] = '/';
new_path[3] = '\0';
dup_path = new_path;
}
else
dup_path[2] = '/';
}
}
if (hostname)
{
if (dup_path[0] == '/' && dup_path[1] == '\0')
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("Local URL '%s' contains only a hostname, "
"no path"), url);
*dirent = apr_pstrcat(pool, "//", hostname, dup_path, SVN_VA_NULL);
}
else
*dirent = dup_path;
}
#else
if (hostname)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
_("Local URL '%s' contains unsupported hostname"),
url);
*dirent = svn_path_uri_decode(path, pool);
#endif
return SVN_NO_ERROR;
}
svn_error_t *
svn_uri_get_file_url_from_dirent(const char **url,
const char *dirent,
apr_pool_t *pool)
{
assert(svn_dirent_is_canonical(dirent, pool));
SVN_ERR(svn_dirent_get_absolute(&dirent, dirent, pool));
dirent = svn_path_uri_encode(dirent, pool);
#ifndef SVN_USE_DOS_PATHS
if (dirent[0] == '/' && dirent[1] == '\0')
dirent = NULL;
*url = apr_pstrcat(pool, "file://", dirent, SVN_VA_NULL);
#else
if (dirent[0] == '/')
{
assert(dirent[1] == '/');
*url = apr_pstrcat(pool, "file:", dirent, SVN_VA_NULL);
}
else
{
char *uri = apr_pstrcat(pool, "file:///", dirent, SVN_VA_NULL);
apr_size_t len = 8 + strlen(dirent);
if (uri[len-1] == '/')
uri[len-1] = '\0';
*url = uri;
}
#endif
return SVN_NO_ERROR;
}
svn_boolean_t
svn_fspath__is_canonical(const char *fspath)
{
return fspath[0] == '/' && relpath_is_canonical(fspath + 1);
}
const char *
svn_fspath__canonicalize(const char *fspath,
apr_pool_t *pool)
{
if ((fspath[0] == '/') && (fspath[1] == '\0'))
return "/";
return apr_pstrcat(pool, "/", svn_relpath_canonicalize(fspath, pool),
SVN_VA_NULL);
}
svn_boolean_t
svn_fspath__is_root(const char *fspath, apr_size_t len)
{
return (len == 1 && fspath[0] == '/');
}
const char *
svn_fspath__skip_ancestor(const char *parent_fspath,
const char *child_fspath)
{
assert(svn_fspath__is_canonical(parent_fspath));
assert(svn_fspath__is_canonical(child_fspath));
return svn_relpath_skip_ancestor(parent_fspath + 1, child_fspath + 1);
}
const char *
svn_fspath__dirname(const char *fspath,
apr_pool_t *pool)
{
assert(svn_fspath__is_canonical(fspath));
if (fspath[0] == '/' && fspath[1] == '\0')
return apr_pstrdup(pool, fspath);
else
return apr_pstrcat(pool, "/", svn_relpath_dirname(fspath + 1, pool),
SVN_VA_NULL);
}
const char *
svn_fspath__basename(const char *fspath,
apr_pool_t *pool)
{
const char *result;
assert(svn_fspath__is_canonical(fspath));
result = svn_relpath_basename(fspath + 1, pool);
assert(strchr(result, '/') == NULL);
return result;
}
void
svn_fspath__split(const char **dirpath,
const char **base_name,
const char *fspath,
apr_pool_t *result_pool)
{
assert(dirpath != base_name);
if (dirpath)
*dirpath = svn_fspath__dirname(fspath, result_pool);
if (base_name)
*base_name = svn_fspath__basename(fspath, result_pool);
}
char *
svn_fspath__join(const char *fspath,
const char *relpath,
apr_pool_t *result_pool)
{
char *result;
assert(svn_fspath__is_canonical(fspath));
assert(svn_relpath_is_canonical(relpath));
if (relpath[0] == '\0')
result = apr_pstrdup(result_pool, fspath);
else if (fspath[1] == '\0')
result = apr_pstrcat(result_pool, "/", relpath, SVN_VA_NULL);
else
result = apr_pstrcat(result_pool, fspath, "/", relpath, SVN_VA_NULL);
assert(svn_fspath__is_canonical(result));
return result;
}
char *
svn_fspath__get_longest_ancestor(const char *fspath1,
const char *fspath2,
apr_pool_t *result_pool)
{
char *result;
assert(svn_fspath__is_canonical(fspath1));
assert(svn_fspath__is_canonical(fspath2));
result = apr_pstrcat(result_pool, "/",
svn_relpath_get_longest_ancestor(fspath1 + 1,
fspath2 + 1,
result_pool),
SVN_VA_NULL);
assert(svn_fspath__is_canonical(result));
return result;
}
const char *
svn_urlpath__canonicalize(const char *uri,
apr_pool_t *pool)
{
if (svn_path_is_url(uri))
{
uri = svn_uri_canonicalize(uri, pool);
}
else
{
uri = svn_fspath__canonicalize(uri, pool);
uri = svn_path_uri_decode(uri, pool);
uri = svn_path_uri_encode(uri, pool);
}
return uri;
}
svn_boolean_t
svn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname)
{
apr_size_t pattern_pos = 0, hostname_pos = 0;
if (pattern->len >= 2 &&
pattern->data[pattern_pos] == '*' &&
pattern->data[pattern_pos + 1] == '.')
{
while (hostname_pos < hostname->len &&
hostname->data[hostname_pos] != '.')
{
hostname_pos++;
}
if (hostname_pos == 0)
return FALSE;
pattern_pos++;
}
while (pattern_pos < pattern->len && hostname_pos < hostname->len)
{
char pattern_c = pattern->data[pattern_pos];
char hostname_c = hostname->data[hostname_pos];
pattern_c = canonicalize_to_lower(pattern_c);
hostname_c = canonicalize_to_lower(hostname_c);
if (pattern_c != hostname_c)
{
return FALSE;
}
else
{
pattern_pos++;
hostname_pos++;
}
}
if (pattern_pos == pattern->len &&
hostname_pos == hostname->len - 1 &&
hostname->data[hostname_pos] == '.')
hostname_pos++;
if (pattern_pos != pattern->len || hostname_pos != hostname->len)
{
return FALSE;
}
return TRUE;
}