/* * 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; }