summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2019-06-15 15:47:41 +0100
committerGitHub <noreply@github.com>2019-06-15 15:47:41 +0100
commitfef847ae57d74e93563bc04222d9da7007fffc4f (patch)
tree30e23c0b5b069f04c024e86fc9c1934ad2aaf04a /src
parent2b6594de4d9a70eadeee63ec9549071d6b16a676 (diff)
parent13ded47cbd898662f48490d28ca00bc50344afd8 (diff)
downloadlibgit2-fef847ae57d74e93563bc04222d9da7007fffc4f.tar.gz
Merge pull request #5110 from pks-t/pks/wildmatch
Replace fnmatch with wildmatch
Diffstat (limited to 'src')
-rw-r--r--src/attr_file.c26
-rw-r--r--src/attr_file.h5
-rw-r--r--src/config_file.c47
-rw-r--r--src/describe.c5
-rw-r--r--src/fnmatch.c248
-rw-r--r--src/fnmatch.h48
-rw-r--r--src/ignore.c16
-rw-r--r--src/pathspec.c26
-rw-r--r--src/posix.h1
-rw-r--r--src/refdb_fs.c7
-rw-r--r--src/refspec.c8
-rw-r--r--src/status.c9
-rw-r--r--src/tag.c3
-rw-r--r--src/wildmatch.c320
-rw-r--r--src/wildmatch.h23
15 files changed, 411 insertions, 381 deletions
diff --git a/src/attr_file.c b/src/attr_file.c
index f01a743d2..edcfbdad5 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -15,6 +15,7 @@
#include "git2/tree.h"
#include "blob.h"
#include "index.h"
+#include "wildmatch.h"
#include <ctype.h>
static void attr_file_free(git_attr_file *file)
@@ -401,18 +402,13 @@ bool git_attr_fnmatch__match(
}
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
- flags |= FNM_CASEFOLD;
- if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
- flags |= FNM_LEADING_DIR;
+ flags |= WM_CASEFOLD;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
filename = relpath;
- flags |= FNM_PATHNAME;
+ flags |= WM_PATHNAME;
} else {
filename = path->basename;
-
- if (path->is_dir)
- flags |= FNM_LEADING_DIR;
}
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
@@ -427,8 +423,6 @@ bool git_attr_fnmatch__match(
path->basename == relpath)
return false;
- flags |= FNM_LEADING_DIR;
-
/* fail match if this is a file with same name as ignored folder */
samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ?
!strcasecmp(match->pattern, relpath) :
@@ -437,10 +431,10 @@ bool git_attr_fnmatch__match(
if (samename)
return false;
- return (p_fnmatch(match->pattern, relpath, flags) != FNM_NOMATCH);
+ return (wildmatch(match->pattern, relpath, flags) == WM_MATCH);
}
- return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
+ return (wildmatch(match->pattern, filename, flags) == WM_MATCH);
}
bool git_attr_rule__match(
@@ -658,8 +652,6 @@ int git_attr_fnmatch__parse(
if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
- if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0)
- spec->flags |= GIT_ATTR_FNMATCH_LEADINGDIR;
pattern++;
}
@@ -715,14 +707,6 @@ int git_attr_fnmatch__parse(
if (--slash_count <= 0)
spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
}
- if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 &&
- spec->length >= 2 &&
- pattern[spec->length - 1] == '*' &&
- pattern[spec->length - 2] == '/') {
- spec->length -= 2;
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR;
- /* leave FULLPATH match on, however */
- }
if (context) {
char *slash = strrchr(context, '/');
diff --git a/src/attr_file.h b/src/attr_file.h
index fedf55af5..7a45516fb 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -32,12 +32,9 @@
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
-#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11)
-#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12)
#define GIT_ATTR_FNMATCH__INCOMING \
- (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \
- GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR)
+ (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
typedef enum {
GIT_ATTR_FILE__IN_MEMORY = 0,
diff --git a/src/config_file.c b/src/config_file.c
index 716205851..27b9cc5d7 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -18,6 +18,7 @@
#include "array.h"
#include "config_parse.h"
#include "config_entries.h"
+#include "wildmatch.h"
#include <ctype.h>
#include <sys/types.h>
@@ -697,41 +698,41 @@ static int do_match_gitdir(
int *matches,
const git_repository *repo,
const char *cfg_file,
- const char *value,
+ const char *condition,
bool case_insensitive)
{
- git_buf path = GIT_BUF_INIT;
- int error, fnmatch_flags;
-
- if (value[0] == '.' && git_path_is_dirsep(value[1])) {
- git_path_dirname_r(&path, cfg_file);
- git_buf_joinpath(&path, path.ptr, value + 2);
- } else if (value[0] == '~' && git_path_is_dirsep(value[1]))
- git_sysdir_expand_global_file(&path, value + 1);
- else if (!git_path_is_absolute(value))
- git_buf_joinpath(&path, "**", value);
+ git_buf pattern = GIT_BUF_INIT, gitdir = GIT_BUF_INIT;
+ int error;
+
+ if (condition[0] == '.' && git_path_is_dirsep(condition[1])) {
+ git_path_dirname_r(&pattern, cfg_file);
+ git_buf_joinpath(&pattern, pattern.ptr, condition + 2);
+ } else if (condition[0] == '~' && git_path_is_dirsep(condition[1]))
+ git_sysdir_expand_global_file(&pattern, condition + 1);
+ else if (!git_path_is_absolute(condition))
+ git_buf_joinpath(&pattern, "**", condition);
else
- git_buf_sets(&path, value);
+ git_buf_sets(&pattern, condition);
- if (git_buf_oom(&path)) {
+ if (git_path_is_dirsep(condition[strlen(condition) - 1]))
+ git_buf_puts(&pattern, "**");
+
+ if (git_buf_oom(&pattern)) {
error = -1;
goto out;
}
- if (git_path_is_dirsep(value[strlen(value) - 1]))
- git_buf_puts(&path, "**");
-
- fnmatch_flags = FNM_PATHNAME|FNM_LEADING_DIR;
- if (case_insensitive)
- fnmatch_flags |= FNM_IGNORECASE;
-
- if ((error = p_fnmatch(path.ptr, git_repository_path(repo), fnmatch_flags)) < 0)
+ if ((error = git_repository_item_path(&gitdir, repo, GIT_REPOSITORY_ITEM_GITDIR)) < 0)
goto out;
- *matches = (error == 0);
+ if (git_path_is_dirsep(gitdir.ptr[gitdir.size - 1]))
+ git_buf_truncate(&gitdir, gitdir.size - 1);
+ *matches = wildmatch(pattern.ptr, gitdir.ptr,
+ WM_PATHNAME | (case_insensitive ? WM_CASEFOLD : 0)) == WM_MATCH;
out:
- git_buf_dispose(&path);
+ git_buf_dispose(&pattern);
+ git_buf_dispose(&gitdir);
return error;
}
diff --git a/src/describe.c b/src/describe.c
index 7f715193d..42e5848c2 100644
--- a/src/describe.c
+++ b/src/describe.c
@@ -16,10 +16,11 @@
#include "commit_list.h"
#include "oidmap.h"
#include "refs.h"
+#include "repository.h"
#include "revwalk.h"
#include "tag.h"
#include "vector.h"
-#include "repository.h"
+#include "wildmatch.h"
/* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
@@ -214,7 +215,7 @@ static int get_name(const char *refname, void *payload)
return 0;
/* Accept only tags that match the pattern, if given */
- if (data->opts->pattern && (!is_tag || p_fnmatch(data->opts->pattern,
+ if (data->opts->pattern && (!is_tag || wildmatch(data->opts->pattern,
refname + strlen(GIT_REFS_TAGS_DIR), 0)))
return 0;
diff --git a/src/fnmatch.c b/src/fnmatch.c
deleted file mode 100644
index 3cc2a27ba..000000000
--- a/src/fnmatch.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * This file contains code originally derrived from OpenBSD fnmatch.c
- *
- * Copyright (c) 1989, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Guido van Rossum.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
- * Compares a filename or pathname to a pattern.
- */
-
-#include "fnmatch.h"
-
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-
-#define EOS '\0'
-
-#define RANGE_MATCH 1
-#define RANGE_NOMATCH 0
-#define RANGE_ERROR (-1)
-
-static int rangematch(const char *, char, int, char **);
-
-static int
-p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs)
-{
- const char *stringstart;
- char *newp;
- char c, test;
- int recurs_flags = flags & ~FNM_PERIOD;
-
- if (recurs-- == 0)
- return FNM_NORES;
-
- for (stringstart = string;;)
- switch (c = *pattern++) {
- case EOS:
- if ((flags & FNM_LEADING_DIR) && *string == '/')
- return (0);
- return (*string == EOS ? 0 : FNM_NOMATCH);
- case '?':
- if (*string == EOS)
- return (FNM_NOMATCH);
- if (*string == '/' && (flags & FNM_PATHNAME))
- return (FNM_NOMATCH);
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
- ++string;
- break;
- case '*':
- c = *pattern;
-
- /* Let '**' override PATHNAME match for this segment.
- * It will be restored if/when we recurse below.
- */
- if (c == '*') {
- c = *++pattern;
- /* star-star-slash is at the end, match by default */
- if (c == EOS)
- return 0;
- /* Double-star must be at end or between slashes */
- if (c != '/')
- return (FNM_NOMATCH);
-
- c = *++pattern;
- do {
- int e = p_fnmatchx(pattern, string, recurs_flags, recurs);
- if (e != FNM_NOMATCH)
- return e;
- string = strchr(string, '/');
- } while (string++);
-
- /* If we get here, we didn't find a match */
- return FNM_NOMATCH;
- }
-
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
-
- /* Optimize for pattern with * at end or before /. */
- if (c == EOS) {
- if (flags & FNM_PATHNAME)
- return ((flags & FNM_LEADING_DIR) ||
- strchr(string, '/') == NULL ?
- 0 : FNM_NOMATCH);
- else
- return (0);
- } else if (c == '/' && (flags & FNM_PATHNAME)) {
- if ((string = strchr(string, '/')) == NULL)
- return (FNM_NOMATCH);
- break;
- }
-
- /* General case, use recursion. */
- while ((test = *string) != EOS) {
- int e;
-
- e = p_fnmatchx(pattern, string, recurs_flags, recurs);
- if (e != FNM_NOMATCH)
- return e;
- if (test == '/' && (flags & FNM_PATHNAME))
- break;
- ++string;
- }
- return (FNM_NOMATCH);
- case '[':
- if (*string == EOS)
- return (FNM_NOMATCH);
- if (*string == '/' && (flags & FNM_PATHNAME))
- return (FNM_NOMATCH);
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
-
- switch (rangematch(pattern, *string, flags, &newp)) {
- case RANGE_ERROR:
- /* not a good range, treat as normal text */
- goto normal;
- case RANGE_MATCH:
- pattern = newp;
- break;
- case RANGE_NOMATCH:
- return (FNM_NOMATCH);
- }
- ++string;
- break;
- case '\\':
- if (!(flags & FNM_NOESCAPE)) {
- if ((c = *pattern++) == EOS) {
- c = '\\';
- --pattern;
- }
- }
- /* FALLTHROUGH */
- default:
- normal:
- if (c != *string && !((flags & FNM_CASEFOLD) &&
- (git__tolower((unsigned char)c) ==
- git__tolower((unsigned char)*string))))
- return (FNM_NOMATCH);
- ++string;
- break;
- }
- /* NOTREACHED */
-}
-
-static int
-rangematch(const char *pattern, char test, int flags, char **newp)
-{
- int negate, ok;
- char c, c2;
-
- /*
- * A bracket expression starting with an unquoted circumflex
- * character produces unspecified results (IEEE 1003.2-1992,
- * 3.13.2). This implementation treats it like '!', for
- * consistency with the regular expression syntax.
- * J.T. Conklin (conklin@ngai.kaleida.com)
- */
- if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
- ++pattern;
-
- if (flags & FNM_CASEFOLD)
- test = (char)git__tolower((unsigned char)test);
-
- /*
- * A right bracket shall lose its special meaning and represent
- * itself in a bracket expression if it occurs first in the list.
- * -- POSIX.2 2.8.3.2
- */
- ok = 0;
- c = *pattern++;
- do {
- if (c == '\\' && !(flags & FNM_NOESCAPE))
- c = *pattern++;
- if (c == EOS)
- return (RANGE_ERROR);
- if (c == '/' && (flags & FNM_PATHNAME))
- return (RANGE_NOMATCH);
- if ((flags & FNM_CASEFOLD))
- c = (char)git__tolower((unsigned char)c);
- if (*pattern == '-'
- && (c2 = *(pattern+1)) != EOS && c2 != ']') {
- pattern += 2;
- if (c2 == '\\' && !(flags & FNM_NOESCAPE))
- c2 = *pattern++;
- if (c2 == EOS)
- return (RANGE_ERROR);
- if (flags & FNM_CASEFOLD)
- c2 = (char)git__tolower((unsigned char)c2);
- if (c <= test && test <= c2)
- ok = 1;
- } else if (c == test)
- ok = 1;
- } while ((c = *pattern++) != ']');
-
- *newp = (char *)pattern;
- return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
-}
-
-int
-p_fnmatch(const char *pattern, const char *string, int flags)
-{
- return p_fnmatchx(pattern, string, flags, 64);
-}
-
diff --git a/src/fnmatch.h b/src/fnmatch.h
deleted file mode 100644
index ddaae15bb..000000000
--- a/src/fnmatch.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#ifndef INCLUDE_fnmatch_h__
-#define INCLUDE_fnmatch_h__
-
-#include "common.h"
-
-#define FNM_NOMATCH 1 /* Match failed. */
-#define FNM_NOSYS 2 /* Function not supported (unused). */
-#define FNM_NORES 3 /* Out of resources */
-
-#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
-#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
-#define FNM_PERIOD 0x04 /* Period must be matched by period. */
-#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
-#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
-
-#define FNM_IGNORECASE FNM_CASEFOLD
-#define FNM_FILE_NAME FNM_PATHNAME
-
-extern int p_fnmatch(const char *pattern, const char *string, int flags);
-
-#endif
diff --git a/src/ignore.c b/src/ignore.c
index 5427efa29..3f748b80f 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -12,7 +12,7 @@
#include "attrcache.h"
#include "path.h"
#include "config.h"
-#include "fnmatch.h"
+#include "wildmatch.h"
#define GIT_IGNORE_INTERNAL "[internal]exclude"
@@ -101,7 +101,7 @@ static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
*/
static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match)
{
- int error = 0, fnflags;
+ int error = 0, wildmatch_flags;
size_t i;
git_attr_fnmatch *rule;
char *path;
@@ -109,9 +109,9 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
*out = 0;
- fnflags = FNM_PATHNAME;
+ wildmatch_flags = WM_PATHNAME;
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
- fnflags |= FNM_IGNORECASE;
+ wildmatch_flags |= WM_CASEFOLD;
/* path of the file relative to the workdir, so we match the rules in subdirs */
if (match->containing_dir) {
@@ -141,13 +141,13 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
if (git_buf_oom(&buf))
goto out;
- if ((error = p_fnmatch(git_buf_cstr(&buf), path, fnflags)) < 0) {
+ if ((error = wildmatch(git_buf_cstr(&buf), path, wildmatch_flags)) < 0) {
git_error_set(GIT_ERROR_INVALID, "error matching pattern");
goto out;
}
/* if we found a match, we want to keep this rule */
- if (error != FNM_NOMATCH) {
+ if (error != WM_NOMATCH) {
*out = 1;
error = 0;
goto out;
@@ -193,9 +193,7 @@ static int parse_ignore_file(
}
match->flags =
- GIT_ATTR_FNMATCH_ALLOWSPACE |
- GIT_ATTR_FNMATCH_ALLOWNEG |
- GIT_ATTR_FNMATCH_NOLEADINGDIR;
+ GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
if (!(error = git_attr_fnmatch__parse(
match, &attrs->pool, context, &scan)))
diff --git a/src/pathspec.c b/src/pathspec.c
index 07795f256..9eb952308 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -16,6 +16,7 @@
#include "index.h"
#include "bitvec.h"
#include "diff.h"
+#include "wildmatch.h"
/* what is the common non-wildcard prefix for all items in the pathspec */
char *git_pathspec_prefix(const git_strarray *pathspec)
@@ -84,8 +85,7 @@ int git_pathspec__vinit(
if (!match)
return -1;
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE |
- GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_NOLEADINGDIR;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
if (ret == GIT_ENOTFOUND) {
@@ -110,7 +110,7 @@ void git_pathspec__vfree(git_vector *vspec)
}
struct pathspec_match_context {
- int fnmatch_flags;
+ int wildmatch_flags;
int (*strcomp)(const char *, const char *);
int (*strncomp)(const char *, const char *, size_t);
};
@@ -121,11 +121,11 @@ static void pathspec_match_context_init(
bool casefold)
{
if (disable_fnmatch)
- ctxt->fnmatch_flags = -1;
+ ctxt->wildmatch_flags = -1;
else if (casefold)
- ctxt->fnmatch_flags = FNM_CASEFOLD;
+ ctxt->wildmatch_flags = WM_CASEFOLD;
else
- ctxt->fnmatch_flags = 0;
+ ctxt->wildmatch_flags = 0;
if (casefold) {
ctxt->strcomp = git__strcasecmp;
@@ -141,16 +141,16 @@ static int pathspec_match_one(
struct pathspec_match_context *ctxt,
const char *path)
{
- int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
+ int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : WM_NOMATCH;
- if (result == FNM_NOMATCH)
- result = ctxt->strcomp(match->pattern, path) ? FNM_NOMATCH : 0;
+ if (result == WM_NOMATCH)
+ result = ctxt->strcomp(match->pattern, path) ? WM_NOMATCH : 0;
- if (ctxt->fnmatch_flags >= 0 && result == FNM_NOMATCH)
- result = p_fnmatch(match->pattern, path, ctxt->fnmatch_flags);
+ if (ctxt->wildmatch_flags >= 0 && result == WM_NOMATCH)
+ result = wildmatch(match->pattern, path, ctxt->wildmatch_flags);
/* if we didn't match, look for exact dirname prefix match */
- if (result == FNM_NOMATCH &&
+ if (result == WM_NOMATCH &&
(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
ctxt->strncomp(path, match->pattern, match->length) == 0 &&
path[match->length] == '/')
@@ -159,7 +159,7 @@ static int pathspec_match_one(
/* if we didn't match and this is a negative match, check for exact
* match of filename with leading '!'
*/
- if (result == FNM_NOMATCH &&
+ if (result == WM_NOMATCH &&
(match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
*path == '!' &&
ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
diff --git a/src/posix.h b/src/posix.h
index 2934f2479..170ff2606 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -11,7 +11,6 @@
#include <fcntl.h>
#include <time.h>
-#include "fnmatch.h"
/* stat: file mode type testing macros */
#ifndef S_IFGITLINK
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index c8533e69f..e5ee392fc 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -18,6 +18,7 @@
#include "iterator.h"
#include "sortedcache.h"
#include "signature.h"
+#include "wildmatch.h"
#include <git2/tag.h>
#include <git2/object.h>
@@ -571,7 +572,7 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
ref_name = git_buf_cstr(&path);
if (git__suffixcmp(ref_name, ".lock") == 0 ||
- (iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0))
+ (iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
continue;
ref_dup = git_pool_strdup(&iter->pool, ref_name);
@@ -617,7 +618,7 @@ static int refdb_fs_backend__iterator_next(
if (ref->flags & PACKREF_SHADOWED)
continue;
- if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
+ if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
continue;
*out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
@@ -660,7 +661,7 @@ static int refdb_fs_backend__iterator_next_name(
if (ref->flags & PACKREF_SHADOWED)
continue;
- if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
+ if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
continue;
*out = ref->name;
diff --git a/src/refspec.c b/src/refspec.c
index dab053634..854240a84 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -9,10 +9,10 @@
#include "git2/errors.h"
-#include "util.h"
-#include "posix.h"
#include "refs.h"
+#include "util.h"
#include "vector.h"
+#include "wildmatch.h"
int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
{
@@ -213,7 +213,7 @@ int git_refspec_src_matches(const git_refspec *refspec, const char *refname)
if (refspec == NULL || refspec->src == NULL)
return false;
- return (p_fnmatch(refspec->src, refname, 0) == 0);
+ return (wildmatch(refspec->src, refname, 0) == 0);
}
int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
@@ -221,7 +221,7 @@ int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
if (refspec == NULL || refspec->dst == NULL)
return false;
- return (p_fnmatch(refspec->dst, refname, 0) == 0);
+ return (wildmatch(refspec->dst, refname, 0) == 0);
}
static int refspec_transform(
diff --git a/src/status.c b/src/status.c
index 8d3185f0e..a01736dee 100644
--- a/src/status.c
+++ b/src/status.c
@@ -16,6 +16,7 @@
#include "repository.h"
#include "ignore.h"
#include "index.h"
+#include "wildmatch.h"
#include "git2/diff.h"
#include "diff.h"
@@ -456,7 +457,7 @@ struct status_file_info {
char *expected;
unsigned int count;
unsigned int status;
- int fnm_flags;
+ int wildmatch_flags;
int ambiguous;
};
@@ -468,11 +469,11 @@ static int get_one_status(const char *path, unsigned int status, void *data)
sfi->count++;
sfi->status = status;
- strcomp = (sfi->fnm_flags & FNM_CASEFOLD) ? git__strcasecmp : git__strcmp;
+ strcomp = (sfi->wildmatch_flags & WM_CASEFOLD) ? git__strcasecmp : git__strcmp;
if (sfi->count > 1 ||
(strcomp(sfi->expected, path) != 0 &&
- p_fnmatch(sfi->expected, path, sfi->fnm_flags) != 0))
+ wildmatch(sfi->expected, path, sfi->wildmatch_flags) != 0))
{
sfi->ambiguous = true;
return GIT_EAMBIGUOUS; /* git_error_set will be done by caller */
@@ -499,7 +500,7 @@ int git_status_file(
if ((sfi.expected = git__strdup(path)) == NULL)
return -1;
if (index->ignore_case)
- sfi.fnm_flags = FNM_CASEFOLD;
+ sfi.wildmatch_flags = WM_CASEFOLD;
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
diff --git a/src/tag.c b/src/tag.c
index b4a5015df..42c4e99ad 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -10,6 +10,7 @@
#include "commit.h"
#include "signature.h"
#include "message.h"
+#include "wildmatch.h"
#include "git2/object.h"
#include "git2/repository.h"
#include "git2/signature.h"
@@ -475,7 +476,7 @@ static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
GIT_UNUSED(oid);
if (!*filter->pattern ||
- p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
+ wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
{
char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
GIT_ERROR_CHECK_ALLOC(matched);
diff --git a/src/wildmatch.c b/src/wildmatch.c
new file mode 100644
index 000000000..865f3f035
--- /dev/null
+++ b/src/wildmatch.c
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ *
+ * Do shell-style pattern matching for ?, \, [], and * characters.
+ * It is 8bit clean.
+ *
+ * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+ * Rich $alz is now <rsalz@bbn.com>.
+ *
+ * Modified by Wayne Davison to special-case '/' matching, to make '**'
+ * work differently than '*', and to fix the character-class code.
+ *
+ * Imported from git.git.
+ */
+
+#include "wildmatch.h"
+
+#define GIT_SPACE 0x01
+#define GIT_DIGIT 0x02
+#define GIT_ALPHA 0x04
+#define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
+#define GIT_PATHSPEC_MAGIC 0x20
+#define GIT_CNTRL 0x40
+#define GIT_PUNCT 0x80
+
+enum {
+ S = GIT_SPACE,
+ A = GIT_ALPHA,
+ D = GIT_DIGIT,
+ G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
+ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */
+ P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */
+ X = GIT_CNTRL,
+ U = GIT_PUNCT,
+ Z = GIT_CNTRL | GIT_SPACE
+};
+
+static const unsigned char sane_ctype[256] = {
+ X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */
+ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */
+ S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
+ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
+ A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
+ A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */
+ /* Nothing in the 128.. range */
+};
+
+#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
+
+typedef unsigned char uchar;
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS '!'
+#define NEGATE_CLASS2 '^'
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+ && *(class) == *(litmatch) \
+ && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+/* Match pattern "p" against "text" */
+static int dowild(const uchar *p, const uchar *text, unsigned int flags)
+{
+ uchar p_ch;
+ const uchar *pattern = p;
+
+ for ( ; (p_ch = *p) != '\0'; text++, p++) {
+ int matched, match_slash, negated;
+ uchar t_ch, prev_ch;
+ if ((t_ch = *text) == '\0' && p_ch != '*')
+ return WM_ABORT_ALL;
+ if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
+ p_ch = tolower(p_ch);
+ switch (p_ch) {
+ case '\\':
+ /* Literal match with following character. Note that the test
+ * in "default" handles the p[1] == '\0' failure case. */
+ p_ch = *++p;
+ /* FALLTHROUGH */
+ default:
+ if (t_ch != p_ch)
+ return WM_NOMATCH;
+ continue;
+ case '?':
+ /* Match anything but '/'. */
+ if ((flags & WM_PATHNAME) && t_ch == '/')
+ return WM_NOMATCH;
+ continue;
+ case '*':
+ if (*++p == '*') {
+ const uchar *prev_p = p - 2;
+ while (*++p == '*') {}
+ if (!(flags & WM_PATHNAME))
+ /* without WM_PATHNAME, '*' == '**' */
+ match_slash = 1;
+ else if ((prev_p < pattern || *prev_p == '/') &&
+ (*p == '\0' || *p == '/' ||
+ (p[0] == '\\' && p[1] == '/'))) {
+ /*
+ * Assuming we already match 'foo/' and are at
+ * <star star slash>, just assume it matches
+ * nothing and go ahead match the rest of the
+ * pattern with the remaining string. This
+ * helps make foo/<*><*>/bar (<> because
+ * otherwise it breaks C comment syntax) match
+ * both foo/bar and foo/a/bar.
+ */
+ if (p[0] == '/' &&
+ dowild(p + 1, text, flags) == WM_MATCH)
+ return WM_MATCH;
+ match_slash = 1;
+ } else /* WM_PATHNAME is set */
+ match_slash = 0;
+ } else
+ /* without WM_PATHNAME, '*' == '**' */
+ match_slash = flags & WM_PATHNAME ? 0 : 1;
+ if (*p == '\0') {
+ /* Trailing "**" matches everything. Trailing "*" matches
+ * only if there are no more slash characters. */
+ if (!match_slash) {
+ if (strchr((char*)text, '/') != NULL)
+ return WM_NOMATCH;
+ }
+ return WM_MATCH;
+ } else if (!match_slash && *p == '/') {
+ /*
+ * _one_ asterisk followed by a slash
+ * with WM_PATHNAME matches the next
+ * directory
+ */
+ const char *slash = strchr((char*)text, '/');
+ if (!slash)
+ return WM_NOMATCH;
+ text = (const uchar*)slash;
+ /* the slash is consumed by the top-level for loop */
+ break;
+ }
+ while (1) {
+ if (t_ch == '\0')
+ break;
+ /*
+ * Try to advance faster when an asterisk is
+ * followed by a literal. We know in this case
+ * that the string before the literal
+ * must belong to "*".
+ * If match_slash is false, do not look past
+ * the first slash as it cannot belong to '*'.
+ */
+ if (!is_glob_special(*p)) {
+ p_ch = *p;
+ if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
+ p_ch = tolower(p_ch);
+ while ((t_ch = *text) != '\0' &&
+ (match_slash || t_ch != '/')) {
+ if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ if (t_ch == p_ch)
+ break;
+ text++;
+ }
+ if (t_ch != p_ch)
+ return WM_NOMATCH;
+ }
+ if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
+ if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
+ return matched;
+ } else if (!match_slash && t_ch == '/')
+ return WM_ABORT_TO_STARSTAR;
+ t_ch = *++text;
+ }
+ return WM_ABORT_ALL;
+ case '[':
+ p_ch = *++p;
+#ifdef NEGATE_CLASS2
+ if (p_ch == NEGATE_CLASS2)
+ p_ch = NEGATE_CLASS;
+#endif
+ /* Assign literal 1/0 because of "matched" comparison. */
+ negated = p_ch == NEGATE_CLASS ? 1 : 0;
+ if (negated) {
+ /* Inverted character class. */
+ p_ch = *++p;
+ }
+ prev_ch = 0;
+ matched = 0;
+ do {
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ if (t_ch == p_ch)
+ matched = 1;
+ } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+ p_ch = *++p;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ }
+ if (t_ch <= p_ch && t_ch >= prev_ch)
+ matched = 1;
+ else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
+ uchar t_ch_upper = toupper(t_ch);
+ if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
+ matched = 1;
+ }
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (p_ch == '[' && p[1] == ':') {
+ const uchar *s;
+ int i;
+ for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ i = p - s - 1;
+ if (i < 0 || p[-1] != ':') {
+ /* Didn't find ":]", so treat like a normal set. */
+ p = s - 2;
+ p_ch = '[';
+ if (t_ch == p_ch)
+ matched = 1;
+ continue;
+ }
+ if (CC_EQ(s,i, "alnum")) {
+ if (ISALNUM(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "alpha")) {
+ if (ISALPHA(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "blank")) {
+ if (ISBLANK(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "cntrl")) {
+ if (ISCNTRL(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "digit")) {
+ if (ISDIGIT(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "graph")) {
+ if (ISGRAPH(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "lower")) {
+ if (ISLOWER(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "print")) {
+ if (ISPRINT(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "punct")) {
+ if (ISPUNCT(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "space")) {
+ if (ISSPACE(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "upper")) {
+ if (ISUPPER(t_ch))
+ matched = 1;
+ else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "xdigit")) {
+ if (ISXDIGIT(t_ch))
+ matched = 1;
+ } else /* malformed [:class:] string */
+ return WM_ABORT_ALL;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (t_ch == p_ch)
+ matched = 1;
+ } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+ if (matched == negated ||
+ ((flags & WM_PATHNAME) && t_ch == '/'))
+ return WM_NOMATCH;
+ continue;
+ }
+ }
+
+ return *text ? WM_NOMATCH : WM_MATCH;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text, unsigned int flags)
+{
+ return dowild((const uchar*)pattern, (const uchar*)text, flags);
+}
diff --git a/src/wildmatch.h b/src/wildmatch.h
new file mode 100644
index 000000000..44bb575a6
--- /dev/null
+++ b/src/wildmatch.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#ifndef INCLUDE_wildmatch_h__
+#define INCLUDE_wildmatch_h__
+
+#include "common.h"
+
+#define WM_CASEFOLD 1
+#define WM_PATHNAME 2
+
+#define WM_NOMATCH 1
+#define WM_MATCH 0
+#define WM_ABORT_ALL -1
+#define WM_ABORT_TO_STARSTAR -2
+
+int wildmatch(const char *pattern, const char *text, unsigned int flags);
+
+#endif