📄 path.c
字号:
/*
* paths.c: a path manipulation library using svn_stringbuf_t
*
* ====================================================================
* Copyright (c) 2000-2004 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/.
* ====================================================================
*/
#include <string.h>
#include <assert.h>
#include <apr_file_info.h>
#include <apr_lib.h>
#include "svn_string.h"
#include "svn_path.h"
#include "svn_private_config.h" /* for SVN_PATH_LOCAL_SEPARATOR */
#include "svn_utf.h"
#include "svn_io.h" /* for svn_io_stat() */
/* The canonical empty path. Can this be changed? Well, change the empty
test below and the path library will work, not so sure about the fs/wc
libraries. */
#define SVN_EMPTY_PATH ""
/* TRUE if s is the canonical empty path, FALSE otherwise */
#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0')
/* TRUE if s,n is the platform's empty path ("."), FALSE otherwise. Can
this be changed? Well, the path library will work, not so sure about
the OS! */
#define SVN_PATH_IS_PLATFORM_EMPTY(s,n) ((n) == 1 && (s)[0] == '.')
const char *
svn_path_internal_style (const char *path, apr_pool_t *pool)
{
if ('/' != SVN_PATH_LOCAL_SEPARATOR)
{
char *p = apr_pstrdup (pool, path);
path = p;
/* Convert all local-style separators to the canonical ones. */
for (; *p != '\0'; ++p)
if (*p == SVN_PATH_LOCAL_SEPARATOR)
*p = '/';
}
return svn_path_canonicalize (path, pool);
/* FIXME: Should also remove trailing /.'s, if the style says so. */
}
const char *
svn_path_local_style (const char *path, apr_pool_t *pool)
{
path = svn_path_canonicalize (path, pool);
/* FIXME: Should also remove trailing /.'s, if the style says so. */
/* Internally, Subversion represents the current directory with the
empty string. But users like to see "." . */
if (SVN_PATH_IS_EMPTY(path))
return ".";
if ('/' != SVN_PATH_LOCAL_SEPARATOR)
{
char *p = apr_pstrdup (pool, path);
path = p;
/* Convert all canonical separators to the local-style ones. */
for (; *p != '\0'; ++p)
if (*p == '/')
*p = SVN_PATH_LOCAL_SEPARATOR;
}
return path;
}
#ifndef NDEBUG
static svn_boolean_t
is_canonical (const char *path,
apr_size_t len)
{
return (! SVN_PATH_IS_PLATFORM_EMPTY (path, len)
&& (len <= 1 || path[len-1] != '/'));
}
#endif
char *svn_path_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 (is_canonical (base, blen));
assert (is_canonical (component, clen));
/* If the component is absolute, then return it. */
if (*component == '/')
return apr_pmemdup (pool, component, clen + 1);
/* If either is empty return the other */
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);
if (blen == 1 && base[0] == '/')
blen = 0; /* Ignore base, just return separator + component */
/* Construct the new, combined path. */
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_path_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 *path;
char *p;
svn_boolean_t base_is_empty = FALSE, base_is_root = FALSE;
int base_arg = 0;
total_len = strlen (base);
assert (is_canonical (base, total_len));
if (total_len == 1 && *base == '/')
base_is_root = TRUE;
else if (SVN_PATH_IS_EMPTY (base))
{
total_len = sizeof (SVN_EMPTY_PATH) - 1;
base_is_empty = TRUE;
}
saved_lengths[0] = total_len;
/* Compute the length of the resulting string. */
nargs = 0;
va_start (va, base);
while ((s = va_arg (va, const char *)) != NULL)
{
len = strlen (s);
assert (is_canonical (s, len));
if (SVN_PATH_IS_EMPTY (s))
continue;
if (nargs++ < MAX_SAVED_LENGTHS)
saved_lengths[nargs] = len;
if (*s == '/')
{
/* an absolute path. skip all components to this point and reset
the total length. */
total_len = len;
base_arg = nargs;
base_is_root = len == 1;
base_is_empty = FALSE;
}
else if (nargs == base_arg
|| (nargs == base_arg + 1 && base_is_root)
|| base_is_empty)
{
/* if we have skipped everything up to this arg, then the base
and all prior components are empty. just set the length to
this component; do not add a separator. If the base is empty
we can now ignore it. */
if (base_is_empty)
{
base_is_empty = FALSE;
total_len = 0;
}
total_len += len;
}
else
{
total_len += 1 + len;
}
}
va_end (va);
/* base == "/" and no further components. just return that. */
if (base_is_root && total_len == 1)
return apr_pmemdup (pool, "/", 2);
/* we got the total size. allocate it, with room for a NULL character. */
path = p = apr_palloc (pool, total_len + 1);
/* if we aren't supposed to skip forward to an absolute component, and if
this is not an empty base that we are skipping, then copy the base
into the output. */
if (base_arg == 0 && ! (SVN_PATH_IS_EMPTY (base) && ! base_is_empty))
{
if (SVN_PATH_IS_EMPTY (base))
memcpy(p, SVN_EMPTY_PATH, len = saved_lengths[0]);
else
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);
/* insert a separator if we aren't copying in the first component
(which can happen when base_arg is set). also, don't put in a slash
if the prior character is a slash (occurs when prior component
is "/"). */
if (p != path && p[-1] != '/')
*p++ = '/';
/* copy the new component and advance the pointer */
memcpy (p, s, len);
p += len;
}
va_end (va);
*p = '\0';
assert ((apr_size_t)(p - path) == total_len);
return path;
}
apr_size_t
svn_path_component_count (const char *path)
{
apr_size_t count = 0;
assert (is_canonical (path, strlen (path)));
while (*path)
{
const char *start;
while (*path == '/')
++path;
start = path;
while (*path && *path != '/')
++path;
if (path != start)
++count;
}
return count;
}
/* Return the length of substring necessary to encompass the entire
* previous path segment in PATH, which should be a LEN byte string.
*
* A trailing slash will not be included in the returned length except
* in the case in which PATH is absolute and there are no more
* previous segments.
*/
static apr_size_t
previous_segment (const char *path,
apr_size_t len)
{
if (len == 0)
return 0;
while (len > 0 && path[--len] != '/')
;
if (len == 0 && path[0] == '/')
return 1;
else
return len;
}
void
svn_path_add_component (svn_stringbuf_t *path,
const char *component)
{
apr_size_t len = strlen (component);
assert (is_canonical (path->data, path->len));
assert (is_canonical (component, len));
/* Append a dir separator, but only if this path is neither empty
nor consists of a single dir separator already. */
if ((! SVN_PATH_IS_EMPTY (path->data))
&& (! ((path->len == 1) && (*(path->data) == '/'))))
{
char dirsep = '/';
svn_stringbuf_appendbytes (path, &dirsep, sizeof (dirsep));
}
svn_stringbuf_appendbytes (path, component, len);
}
void
svn_path_remove_component (svn_stringbuf_t *path)
{
assert (is_canonical (path->data, path->len));
path->len = previous_segment(path->data, path->len);
path->data[path->len] = '\0';
}
void
svn_path_remove_components (svn_stringbuf_t *path, apr_size_t n)
{
while (n > 0)
{
svn_path_remove_component (path);
n--;
}
}
char *
svn_path_dirname (const char *path, apr_pool_t *pool)
{
apr_size_t len = strlen (path);
assert (is_canonical (path, len));
return apr_pstrmemdup (pool, path, previous_segment(path, len));
}
char *
svn_path_basename (const char *path, apr_pool_t *pool)
{
apr_size_t len = strlen (path);
apr_size_t start;
assert (is_canonical (path, len));
if (len == 1 && path[0] == '/')
start = 0;
else
{
start = len;
while (start > 0 && path[start - 1] != '/')
--start;
}
return apr_pstrmemdup (pool, path + start, len - start);
}
void
svn_path_split (const char *path,
const char **dirpath,
const char **base_name,
apr_pool_t *pool)
{
assert (dirpath != base_name);
if (dirpath)
*dirpath = svn_path_dirname (path, pool);
if (base_name)
*base_name = svn_path_basename (path, pool);
}
int
svn_path_is_empty (const char *path)
{
/* assert (is_canonical (path, strlen (path))); ### Expensive strlen */
if (SVN_PATH_IS_EMPTY (path))
return 1;
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -