mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-24 05:21:25 +00:00
169 lines
3.7 KiB
C
169 lines
3.7 KiB
C
/*
|
|
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
*
|
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
* a Linking Exception. For full terms see the included COPYING file.
|
|
*/
|
|
|
|
#include "pathspec.h"
|
|
#include "buf_text.h"
|
|
#include "attr_file.h"
|
|
|
|
/* what is the common non-wildcard prefix for all items in the pathspec */
|
|
char *git_pathspec_prefix(const git_strarray *pathspec)
|
|
{
|
|
git_buf prefix = GIT_BUF_INIT;
|
|
const char *scan;
|
|
|
|
if (!pathspec || !pathspec->count ||
|
|
git_buf_text_common_prefix(&prefix, pathspec) < 0)
|
|
return NULL;
|
|
|
|
/* diff prefix will only be leading non-wildcards */
|
|
for (scan = prefix.ptr; *scan; ++scan) {
|
|
if (git__iswildcard(*scan) &&
|
|
(scan == prefix.ptr || (*(scan - 1) != '\\')))
|
|
break;
|
|
}
|
|
git_buf_truncate(&prefix, scan - prefix.ptr);
|
|
|
|
if (prefix.size <= 0) {
|
|
git_buf_free(&prefix);
|
|
return NULL;
|
|
}
|
|
|
|
git_buf_text_unescape(&prefix);
|
|
|
|
return git_buf_detach(&prefix);
|
|
}
|
|
|
|
/* is there anything in the spec that needs to be filtered on */
|
|
bool git_pathspec_is_empty(const git_strarray *pathspec)
|
|
{
|
|
size_t i;
|
|
|
|
if (pathspec == NULL)
|
|
return true;
|
|
|
|
for (i = 0; i < pathspec->count; ++i) {
|
|
const char *str = pathspec->strings[i];
|
|
|
|
if (str && str[0])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* build a vector of fnmatch patterns to evaluate efficiently */
|
|
int git_pathspec_init(
|
|
git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
|
|
{
|
|
size_t i;
|
|
|
|
memset(vspec, 0, sizeof(*vspec));
|
|
|
|
if (git_pathspec_is_empty(strspec))
|
|
return 0;
|
|
|
|
if (git_vector_init(vspec, strspec->count, NULL) < 0)
|
|
return -1;
|
|
|
|
for (i = 0; i < strspec->count; ++i) {
|
|
int ret;
|
|
const char *pattern = strspec->strings[i];
|
|
git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
|
|
if (!match)
|
|
return -1;
|
|
|
|
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
|
|
|
|
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
|
|
if (ret == GIT_ENOTFOUND) {
|
|
git__free(match);
|
|
continue;
|
|
} else if (ret < 0)
|
|
return ret;
|
|
|
|
if (git_vector_insert(vspec, match) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* free data from the pathspec vector */
|
|
void git_pathspec_free(git_vector *vspec)
|
|
{
|
|
git_attr_fnmatch *match;
|
|
unsigned int i;
|
|
|
|
git_vector_foreach(vspec, i, match) {
|
|
git__free(match);
|
|
vspec->contents[i] = NULL;
|
|
}
|
|
|
|
git_vector_free(vspec);
|
|
}
|
|
|
|
/* match a path against the vectorized pathspec */
|
|
bool git_pathspec_match_path(
|
|
git_vector *vspec,
|
|
const char *path,
|
|
bool disable_fnmatch,
|
|
bool casefold,
|
|
const char **matched_pathspec)
|
|
{
|
|
size_t i;
|
|
git_attr_fnmatch *match;
|
|
int fnmatch_flags = 0;
|
|
int (*use_strcmp)(const char *, const char *);
|
|
int (*use_strncmp)(const char *, const char *, size_t);
|
|
|
|
if (matched_pathspec)
|
|
*matched_pathspec = NULL;
|
|
|
|
if (!vspec || !vspec->length)
|
|
return true;
|
|
|
|
if (disable_fnmatch)
|
|
fnmatch_flags = -1;
|
|
else if (casefold)
|
|
fnmatch_flags = FNM_CASEFOLD;
|
|
|
|
if (casefold) {
|
|
use_strcmp = git__strcasecmp;
|
|
use_strncmp = git__strncasecmp;
|
|
} else {
|
|
use_strcmp = git__strcmp;
|
|
use_strncmp = git__strncmp;
|
|
}
|
|
|
|
git_vector_foreach(vspec, i, match) {
|
|
int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
|
|
|
|
if (result == FNM_NOMATCH)
|
|
result = use_strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
|
|
|
|
if (fnmatch_flags >= 0 && result == FNM_NOMATCH)
|
|
result = p_fnmatch(match->pattern, path, fnmatch_flags);
|
|
|
|
/* if we didn't match, look for exact dirname prefix match */
|
|
if (result == FNM_NOMATCH &&
|
|
(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
|
|
use_strncmp(path, match->pattern, match->length) == 0 &&
|
|
path[match->length] == '/')
|
|
result = 0;
|
|
|
|
if (result == 0) {
|
|
if (matched_pathspec)
|
|
*matched_pathspec = match->pattern;
|
|
|
|
return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|