Skip to content

Commit eb146e5

Browse files
committed
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.
1 parent f7c6795 commit eb146e5

File tree

1 file changed

+16
-14
lines changed

1 file changed

+16
-14
lines changed

src/attr_file.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ int git_attr_fnmatch__parse(
587587
{
588588
const char *pattern, *scan;
589589
int slash_count, allow_space;
590+
bool escaped;
590591

591592
assert(spec && base && *base);
592593

@@ -623,28 +624,29 @@ int git_attr_fnmatch__parse(
623624
}
624625

625626
slash_count = 0;
627+
escaped = false;
628+
/* Scan until a non-escaped whitespace. */
626629
for (scan = pattern; *scan != '\0'; ++scan) {
627-
/*
628-
* Scan until a non-escaped whitespace: find a whitespace, then look
629-
* one char backward to ensure that it's not prefixed by a `\`.
630-
* Only look backward if we're not at the first position (`pattern`).
631-
*/
632-
if (git__isspace(*scan) && scan > pattern && *(scan - 1) != '\\') {
633-
if (!allow_space || (*scan != ' ' && *scan != '\t' && *scan != '\r'))
634-
break;
635-
}
630+
char c = *scan;
636631

637-
if (*scan == '/') {
632+
if (c == '\\' && !escaped) {
633+
escaped = true;
634+
continue;
635+
} else if (git__isspace(c) && !escaped) {
636+
if (!allow_space || (c != ' ' && c != '\t' && c != '\r'))
637+
break;
638+
} else if (c == '/') {
638639
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
639640
slash_count++;
640641

641642
if (slash_count == 1 && pattern == scan)
642643
pattern++;
643-
}
644-
/* remember if we see an unescaped wildcard in pattern */
645-
else if (git__iswildcard(*scan) &&
646-
(scan == pattern || (*(scan - 1) != '\\')))
644+
} else if (git__iswildcard(c) && !escaped) {
645+
/* remember if we see an unescaped wildcard in pattern */
647646
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
647+
}
648+
649+
escaped = false;
648650
}
649651

650652
*base = scan;

0 commit comments

Comments
 (0)