#ifndef INCLUDE_fs_path_h__
#define INCLUDE_fs_path_h__
#include "git2_util.h"
#include "posix.h"
#include "str.h"
#include "vector.h"
#include "utf8.h"
extern char *git_fs_path_dirname(const char *path);
extern int git_fs_path_dirname_r(git_str *buffer, const char *path);
extern char *git_fs_path_basename(const char *path);
extern int git_fs_path_basename_r(git_str *buffer, const char *path);
extern size_t git_fs_path_basename_offset(git_str *buffer);
extern int git_fs_path_root(const char *path);
extern int git_fs_path_to_dir(git_str *path);
extern void git_fs_path_string_to_dir(char *path, size_t size);
GIT_INLINE(int) git_fs_path_is_dot_or_dotdot(const char *name)
{
return (name[0] == '.' &&
(name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0')));
}
#ifdef GIT_WIN32
GIT_INLINE(int) git_fs_path_is_dot_or_dotdotW(const wchar_t *name)
{
return (name[0] == L'.' &&
(name[1] == L'\0' ||
(name[1] == L'.' && name[2] == L'\0')));
}
#define git_fs_path_is_absolute(p) \
(git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
#define git_fs_path_is_dirsep(p) \
((p) == '/' || (p) == '\\')
GIT_INLINE(void) git_fs_path_mkposix(char *path)
{
while (*path) {
if (*path == '\\')
*path = '/';
path++;
}
}
#else
# define git_fs_path_mkposix(p)
#define git_fs_path_is_absolute(p) \
((p)[0] == '/')
#define git_fs_path_is_dirsep(p) \
((p) == '/')
#endif
GIT_INLINE(int) git_fs_path_is_relative(const char *p)
{
return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/')));
}
GIT_INLINE(int) git_fs_path_at_end_of_segment(const char *p)
{
return !*p || *p == '/';
}
extern int git__percent_decode(git_str *decoded_out, const char *input);
extern int git_fs_path_fromurl(git_str *local_path_out, const char *file_url);
extern bool git_fs_path_exists(const char *path);
extern bool git_fs_path_isdir(const char *path);
extern bool git_fs_path_isfile(const char *path);
extern bool git_fs_path_islink(const char *path);
extern bool git_fs_path_is_empty_dir(const char *path);
extern int git_fs_path_lstat(const char *path, struct stat *st);
extern bool git_fs_path_contains(git_str *dir, const char *item);
extern bool git_fs_path_contains_dir(git_str *parent, const char *subdir);
extern size_t git_fs_path_common_dirlen(const char *one, const char *two);
extern int git_fs_path_make_relative(git_str *path, const char *parent);
extern bool git_fs_path_contains_file(git_str *dir, const char *file);
extern int git_fs_path_join_unrooted(
git_str *path_out, const char *path, const char *base, ssize_t *root_at);
extern void git_fs_path_squash_slashes(git_str *path);
extern int git_fs_path_prettify(git_str *path_out, const char *path, const char *base);
extern int git_fs_path_prettify_dir(git_str *path_out, const char *path, const char *base);
extern int git_fs_path_find_dir(git_str *dir);
extern int git_fs_path_resolve_relative(git_str *path, size_t ceiling);
extern int git_fs_path_apply_relative(git_str *target, const char *relpath);
enum {
GIT_FS_PATH_DIR_IGNORE_CASE = (1u << 0),
GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2),
};
extern int git_fs_path_direach(
git_str *pathbuf,
uint32_t flags,
int (*callback)(void *payload, git_str *path),
void *payload);
extern int git_fs_path_cmp(
const char *name1, size_t len1, int isdir1,
const char *name2, size_t len2, int isdir2,
int (*compare)(const char *, const char *, size_t));
extern int git_fs_path_walk_up(
git_str *pathbuf,
const char *ceiling,
int (*callback)(void *payload, const char *path),
void *payload);
enum {
GIT_FS_PATH_NOTEQUAL = 0,
GIT_FS_PATH_EQUAL = 1,
GIT_FS_PATH_PREFIX = 2
};
GIT_INLINE(int) git_fs_path_equal_or_prefixed(
const char *parent,
const char *child,
ssize_t *prefixlen)
{
const char *p = parent, *c = child;
int lastslash = 0;
while (*p && *c) {
lastslash = (*p == '/');
if (*p++ != *c++)
return GIT_FS_PATH_NOTEQUAL;
}
if (*p != '\0')
return GIT_FS_PATH_NOTEQUAL;
if (*c == '\0') {
if (prefixlen)
*prefixlen = p - parent;
return GIT_FS_PATH_EQUAL;
}
if (*c == '/' || lastslash) {
if (prefixlen)
*prefixlen = (p - parent) - lastslash;
return GIT_FS_PATH_PREFIX;
}
return GIT_FS_PATH_NOTEQUAL;
}
extern int git_fs_path_set_error(
int errno_value, const char *path, const char *action);
extern bool git_fs_path_has_non_ascii(const char *path, size_t pathlen);
#define GIT_PATH_REPO_ENCODING "UTF-8"
#ifdef __APPLE__
#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
#else
#define GIT_PATH_NATIVE_ENCODING "UTF-8"
#endif
#ifdef GIT_USE_ICONV
#include <iconv.h>
typedef struct {
iconv_t map;
git_str buf;
} git_fs_path_iconv_t;
#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_STR_INIT }
extern int git_fs_path_iconv_init_precompose(git_fs_path_iconv_t *ic);
extern void git_fs_path_iconv_clear(git_fs_path_iconv_t *ic);
extern int git_fs_path_iconv(git_fs_path_iconv_t *ic, const char **in, size_t *inlen);
#endif
extern bool git_fs_path_does_decompose_unicode(const char *root);
typedef struct git_fs_path_diriter git_fs_path_diriter;
#if defined(GIT_WIN32) && !defined(__MINGW32__)
struct git_fs_path_diriter
{
git_win32_path path;
size_t parent_len;
git_str path_utf8;
size_t parent_utf8_len;
HANDLE handle;
unsigned int flags;
WIN32_FIND_DATAW current;
unsigned int needs_next;
};
#define GIT_FS_PATH_DIRITER_INIT { {0}, 0, GIT_STR_INIT, 0, INVALID_HANDLE_VALUE }
#else
struct git_fs_path_diriter
{
git_str path;
size_t parent_len;
unsigned int flags;
DIR *dir;
#ifdef GIT_USE_ICONV
git_fs_path_iconv_t ic;
#endif
};
#define GIT_FS_PATH_DIRITER_INIT { GIT_STR_INIT }
#endif
extern int git_fs_path_diriter_init(
git_fs_path_diriter *diriter,
const char *path,
unsigned int flags);
extern int git_fs_path_diriter_next(git_fs_path_diriter *diriter);
extern int git_fs_path_diriter_filename(
const char **out,
size_t *out_len,
git_fs_path_diriter *diriter);
extern int git_fs_path_diriter_fullpath(
const char **out,
size_t *out_len,
git_fs_path_diriter *diriter);
extern int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter);
extern void git_fs_path_diriter_free(git_fs_path_diriter *diriter);
extern int git_fs_path_dirload(
git_vector *contents,
const char *path,
size_t prefix_len,
uint32_t flags);
extern bool git_fs_path_is_local_file_url(const char *file_url);
extern int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url_or_path);
#define GIT_FS_PATH_REJECT_EMPTY_COMPONENT (1 << 0)
#define GIT_FS_PATH_REJECT_TRAVERSAL (1 << 1)
#define GIT_FS_PATH_REJECT_SLASH (1 << 2)
#define GIT_FS_PATH_REJECT_BACKSLASH (1 << 3)
#define GIT_FS_PATH_REJECT_TRAILING_DOT (1 << 4)
#define GIT_FS_PATH_REJECT_TRAILING_SPACE (1 << 5)
#define GIT_FS_PATH_REJECT_TRAILING_COLON (1 << 6)
#define GIT_FS_PATH_REJECT_DOS_PATHS (1 << 7)
#define GIT_FS_PATH_REJECT_NT_CHARS (1 << 8)
#define GIT_FS_PATH_REJECT_LONG_PATHS (1 << 9)
#define GIT_FS_PATH_REJECT_MAX (1 << 9)
#ifdef GIT_WIN32
# define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \
GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \
GIT_FS_PATH_REJECT_TRAVERSAL | \
GIT_FS_PATH_REJECT_BACKSLASH | \
GIT_FS_PATH_REJECT_TRAILING_DOT | \
GIT_FS_PATH_REJECT_TRAILING_SPACE | \
GIT_FS_PATH_REJECT_TRAILING_COLON | \
GIT_FS_PATH_REJECT_DOS_PATHS | \
GIT_FS_PATH_REJECT_NT_CHARS
#else
# define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \
GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \
GIT_FS_PATH_REJECT_TRAVERSAL
#endif
extern bool git_fs_path_str_is_valid_ext(
const git_str *path,
unsigned int flags,
bool (*validate_char_cb)(char ch, void *payload),
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len),
void *payload);
GIT_INLINE(bool) git_fs_path_is_valid_ext(
const char *path,
unsigned int flags,
bool (*validate_char_cb)(char ch, void *payload),
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len),
void *payload)
{
const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
return git_fs_path_str_is_valid_ext(
&str,
flags,
validate_char_cb,
validate_component_cb,
validate_length_cb,
payload);
}
GIT_INLINE(bool) git_fs_path_is_valid(
const char *path,
unsigned int flags)
{
const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
return git_fs_path_str_is_valid_ext(&str, flags, NULL, NULL, NULL, NULL);
}
GIT_INLINE(bool) git_fs_path_str_is_valid(
const git_str *path,
unsigned int flags)
{
return git_fs_path_str_is_valid_ext(path, flags, NULL, NULL, NULL, NULL);
}
extern int git_fs_path_validate_str_length_with_suffix(
git_str *path,
size_t suffix_len);
GIT_INLINE(int) git_fs_path_validate_filesystem_with_suffix(
const char *path,
size_t path_len,
size_t suffix_len)
{
#ifdef GIT_WIN32
size_t path_chars, total_chars;
path_chars = git_utf8_char_length(path, path_len);
if (GIT_ADD_SIZET_OVERFLOW(&total_chars, path_chars, suffix_len) ||
total_chars > MAX_PATH) {
git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path);
return -1;
}
return 0;
#else
GIT_UNUSED(path);
GIT_UNUSED(path_len);
GIT_UNUSED(suffix_len);
return 0;
#endif
}
GIT_INLINE(int) git_fs_path_validate_filesystem(
const char *path,
size_t path_len)
{
return git_fs_path_validate_filesystem_with_suffix(path, path_len, 0);
}
int git_fs_path_normalize_slashes(git_str *out, const char *path);
bool git_fs_path_supports_symlinks(const char *dir);
int git_fs_path_validate_system_file_ownership(const char *path);
int git_fs_path_find_executable(git_str *fullpath, const char *executable);
#endif