Skip to content

Commit e813d50

Browse files
author
Junio C Hamano
committed
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>
1 parent d4ada48 commit e813d50

File tree

2 files changed

+39
-16
lines changed

2 files changed

+39
-16
lines changed

dir.c

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,34 +40,49 @@ int common_prefix(const char **pathspec)
4040
return prefix;
4141
}
4242

43+
/*
44+
* Does 'match' matches the given name?
45+
* A match is found if
46+
*
47+
* (1) the 'match' string is leading directory of 'name', or
48+
* (2) the 'match' string is a wildcard and matches 'name', or
49+
* (3) the 'match' string is exactly the same as 'name'.
50+
*
51+
* and the return value tells which case it was.
52+
*
53+
* It returns 0 when there is no match.
54+
*/
4355
static int match_one(const char *match, const char *name, int namelen)
4456
{
4557
int matchlen;
4658

4759
/* If the match was just the prefix, we matched */
4860
matchlen = strlen(match);
4961
if (!matchlen)
50-
return 1;
62+
return MATCHED_RECURSIVELY;
5163

5264
/*
5365
* If we don't match the matchstring exactly,
5466
* we need to match by fnmatch
5567
*/
5668
if (strncmp(match, name, matchlen))
57-
return !fnmatch(match, name, 0);
69+
return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
5870

59-
/*
60-
* If we did match the string exactly, we still
61-
* need to make sure that it happened on a path
62-
* component boundary (ie either the last character
63-
* of the match was '/', or the next character of
64-
* the name was '/' or the terminating NUL.
65-
*/
66-
return match[matchlen-1] == '/' ||
67-
name[matchlen] == '/' ||
68-
!name[matchlen];
71+
if (!name[matchlen])
72+
return MATCHED_EXACTLY;
73+
if (match[matchlen-1] == '/' || name[matchlen] == '/')
74+
return MATCHED_RECURSIVELY;
75+
return 0;
6976
}
7077

78+
/*
79+
* Given a name and a list of pathspecs, see if the name matches
80+
* any of the pathspecs. The caller is also interested in seeing
81+
* all pathspec matches some names it calls this function with
82+
* (otherwise the user could have mistyped the unmatched pathspec),
83+
* and a mark is left in seen[] array for pathspec element that
84+
* actually matched anything.
85+
*/
7186
int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
7287
{
7388
int retval;
@@ -77,12 +92,16 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
7792
namelen -= prefix;
7893

7994
for (retval = 0; (match = *pathspec++) != NULL; seen++) {
80-
if (retval & *seen)
95+
int how;
96+
if (retval && *seen == MATCHED_EXACTLY)
8197
continue;
8298
match += prefix;
83-
if (match_one(match, name, namelen)) {
84-
retval = 1;
85-
*seen = 1;
99+
how = match_one(match, name, namelen);
100+
if (how) {
101+
if (retval < how)
102+
retval = how;
103+
if (*seen < how)
104+
*seen = how;
86105
}
87106
}
88107
return retval;

dir.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ struct dir_struct {
4040
};
4141

4242
extern int common_prefix(const char **pathspec);
43+
44+
#define MATCHED_RECURSIVELY 1
45+
#define MATCHED_FNMATCH 2
46+
#define MATCHED_EXACTLY 3
4347
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
4448

4549
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);

0 commit comments

Comments
 (0)