match_pathspec() -- return how well the spec matched

This updates the return value from match_pathspec() so that the
caller can tell cases between exact match, leading pathname
match (i.e. file "foo/bar" matches a pathspec "foo"), or
filename glob match.  This can be used to prevent "rm dir" from
removing "dir/file" without explicitly asking for recursive
behaviour with -r flag, for example.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2006-12-25 03:09:52 -08:00
parent d4ada4876d
commit e813d50e35
2 changed files with 39 additions and 16 deletions

51
dir.c
View File

@ -40,6 +40,18 @@ int common_prefix(const char **pathspec)
return prefix;
}
/*
* Does 'match' matches the given name?
* A match is found if
*
* (1) the 'match' string is leading directory of 'name', or
* (2) the 'match' string is a wildcard and matches 'name', or
* (3) the 'match' string is exactly the same as 'name'.
*
* and the return value tells which case it was.
*
* It returns 0 when there is no match.
*/
static int match_one(const char *match, const char *name, int namelen)
{
int matchlen;
@ -47,27 +59,30 @@ static int match_one(const char *match, const char *name, int namelen)
/* If the match was just the prefix, we matched */
matchlen = strlen(match);
if (!matchlen)
return 1;
return MATCHED_RECURSIVELY;
/*
* If we don't match the matchstring exactly,
* we need to match by fnmatch
*/
if (strncmp(match, name, matchlen))
return !fnmatch(match, name, 0);
return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
/*
* If we did match the string exactly, we still
* need to make sure that it happened on a path
* component boundary (ie either the last character
* of the match was '/', or the next character of
* the name was '/' or the terminating NUL.
*/
return match[matchlen-1] == '/' ||
name[matchlen] == '/' ||
!name[matchlen];
if (!name[matchlen])
return MATCHED_EXACTLY;
if (match[matchlen-1] == '/' || name[matchlen] == '/')
return MATCHED_RECURSIVELY;
return 0;
}
/*
* Given a name and a list of pathspecs, see if the name matches
* any of the pathspecs. The caller is also interested in seeing
* all pathspec matches some names it calls this function with
* (otherwise the user could have mistyped the unmatched pathspec),
* and a mark is left in seen[] array for pathspec element that
* actually matched anything.
*/
int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
{
int retval;
@ -77,12 +92,16 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
namelen -= prefix;
for (retval = 0; (match = *pathspec++) != NULL; seen++) {
if (retval & *seen)
int how;
if (retval && *seen == MATCHED_EXACTLY)
continue;
match += prefix;
if (match_one(match, name, namelen)) {
retval = 1;
*seen = 1;
how = match_one(match, name, namelen);
if (how) {
if (retval < how)
retval = how;
if (*seen < how)
*seen = how;
}
}
return retval;

4
dir.h
View File

@ -40,6 +40,10 @@ struct dir_struct {
};
extern int common_prefix(const char **pathspec);
#define MATCHED_RECURSIVELY 1
#define MATCHED_FNMATCH 2
#define MATCHED_EXACTLY 3
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);