diff options
author | Patrick Steinhardt <ps@pks.im> | 2019-06-07 09:17:23 +0200 |
---|---|---|
committer | Patrick Steinhardt <ps@pks.im> | 2019-06-13 10:45:55 +0200 |
commit | eb146e58664428d9e23bc84e0e9f75db7924ae37 (patch) | |
tree | c5262cf87de2e6a0c7229cb8b2e89ef6fa400a86 | |
parent | f7c6795f48850eec94793d22b5fd621b61cd2466 (diff) | |
download | libgit2-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.c | 30 |
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; |