summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2019-06-07 09:17:23 +0200
committerPatrick Steinhardt <ps@pks.im>2019-06-13 10:45:55 +0200
commiteb146e58664428d9e23bc84e0e9f75db7924ae37 (patch)
treec5262cf87de2e6a0c7229cb8b2e89ef6fa400a86
parentf7c6795f48850eec94793d22b5fd621b61cd2466 (diff)
downloadlibgit2-eb146e58664428d9e23bc84e0e9f75db7924ae37.tar.gz
attr_file: properly handle escaped '\' when searching non-escaped spaces
When parsing attributes, we need to search for the first unescaped whitespace character to determine where the pattern is to be cut off. The scan fails to account for the case where the escaping '\' character is itself escaped, though, and thus we would not recognize the cut-off point in patterns like "\\ ". Refactor the scanning loop to remember whether the last character was an escape character. If it was and the next character is a '\', too, then we will reset to non-escaped mode again. Thus, we now handle escaped whitespaces as well as escaped wildcards correctly.
-rw-r--r--src/attr_file.c30
1 files changed, 16 insertions, 14 deletions
diff --git a/src/attr_file.c b/src/attr_file.c
index 673f9a406..5b1007ee5 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -587,6 +587,7 @@ int git_attr_fnmatch__parse(
{
const char *pattern, *scan;
int slash_count, allow_space;
+ bool escaped;
assert(spec && base && *base);
@@ -623,28 +624,29 @@ int git_attr_fnmatch__parse(
}
slash_count = 0;
+ escaped = false;
+ /* Scan until a non-escaped whitespace. */
for (scan = pattern; *scan != '\0'; ++scan) {
- /*
- * Scan until a non-escaped whitespace: find a whitespace, then look
- * one char backward to ensure that it's not prefixed by a `\`.
- * Only look backward if we're not at the first position (`pattern`).
- */
- if (git__isspace(*scan) && scan > pattern && *(scan - 1) != '\\') {
- if (!allow_space || (*scan != ' ' && *scan != '\t' && *scan != '\r'))
- break;
- }
+ char c = *scan;
- if (*scan == '/') {
+ if (c == '\\' && !escaped) {
+ escaped = true;
+ continue;
+ } else if (git__isspace(c) && !escaped) {
+ if (!allow_space || (c != ' ' && c != '\t' && c != '\r'))
+ break;
+ } else if (c == '/') {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
slash_count++;
if (slash_count == 1 && pattern == scan)
pattern++;
- }
- /* remember if we see an unescaped wildcard in pattern */
- else if (git__iswildcard(*scan) &&
- (scan == pattern || (*(scan - 1) != '\\')))
+ } else if (git__iswildcard(c) && !escaped) {
+ /* remember if we see an unescaped wildcard in pattern */
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
+ }
+
+ escaped = false;
}
*base = scan;