diff options
author | Junio C Hamano <junkio@cox.net> | 2006-12-25 03:09:52 -0800 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2006-12-25 03:29:08 -0800 |
commit | e813d50e35653bdb0ce3329f99d1be7fc1c36de5 (patch) | |
tree | bc0ef733fe3e1019577a5c0b24b9b165b13bbfa5 | |
parent | d4ada4876d5ab7b271857917231f6006f2160bca (diff) | |
download | git-e813d50e35653bdb0ce3329f99d1be7fc1c36de5.tar.gz |
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>
-rw-r--r-- | dir.c | 51 | ||||
-rw-r--r-- | dir.h | 4 |
2 files changed, 39 insertions, 16 deletions
@@ -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; @@ -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); |