Skip to content

Commit a5ddae6

Browse files
authored
Merge pull request libgit2#5097 from pks-t/pks/ignore-escapes
gitignore with escapes
2 parents e277ff4 + 3b51735 commit a5ddae6

File tree

6 files changed

+222
-1357
lines changed

6 files changed

+222
-1357
lines changed

src/attr_file.c

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -565,15 +565,55 @@ void git_attr_path__free(git_attr_path *info)
565565
*/
566566
static size_t trailing_space_length(const char *p, size_t len)
567567
{
568-
size_t n;
568+
size_t n, i;
569569
for (n = len; n; n--) {
570-
if ((p[n-1] != ' ' && p[n-1] != '\t') ||
571-
(n > 1 && p[n-2] == '\\'))
570+
if (p[n-1] != ' ' && p[n-1] != '\t')
571+
break;
572+
573+
/*
574+
* Count escape-characters before space. In case where it's an
575+
* even number of escape characters, then the escape char itself
576+
* is escaped and the whitespace is an unescaped whitespace.
577+
* Otherwise, the last escape char is not escaped and the
578+
* whitespace in an escaped whitespace.
579+
*/
580+
i = n;
581+
while (i > 1 && p[i-2] == '\\')
582+
i--;
583+
if ((n - i) % 2)
572584
break;
573585
}
574586
return len - n;
575587
}
576588

589+
static size_t unescape_spaces(char *str)
590+
{
591+
char *scan, *pos = str;
592+
bool escaped = false;
593+
594+
if (!str)
595+
return 0;
596+
597+
for (scan = str; *scan; scan++) {
598+
if (!escaped && *scan == '\\') {
599+
escaped = true;
600+
continue;
601+
}
602+
603+
/* Only insert the escape character for escaped non-spaces */
604+
if (escaped && !git__isspace(*scan))
605+
*pos++ = '\\';
606+
607+
*pos++ = *scan;
608+
escaped = false;
609+
}
610+
611+
if (pos != scan)
612+
*pos = '\0';
613+
614+
return (pos - str);
615+
}
616+
577617
/*
578618
* This will return 0 if the spec was filled out,
579619
* GIT_ENOTFOUND if the fnmatch does not require matching, or
@@ -587,6 +627,7 @@ int git_attr_fnmatch__parse(
587627
{
588628
const char *pattern, *scan;
589629
int slash_count, allow_space;
630+
bool escaped;
590631

591632
assert(spec && base && *base);
592633

@@ -623,28 +664,29 @@ int git_attr_fnmatch__parse(
623664
}
624665

625666
slash_count = 0;
667+
escaped = false;
668+
/* Scan until a non-escaped whitespace. */
626669
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-
}
670+
char c = *scan;
636671

637-
if (*scan == '/') {
672+
if (c == '\\' && !escaped) {
673+
escaped = true;
674+
continue;
675+
} else if (git__isspace(c) && !escaped) {
676+
if (!allow_space || (c != ' ' && c != '\t' && c != '\r'))
677+
break;
678+
} else if (c == '/') {
638679
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
639680
slash_count++;
640681

641682
if (slash_count == 1 && pattern == scan)
642683
pattern++;
643-
}
644-
/* remember if we see an unescaped wildcard in pattern */
645-
else if (git__iswildcard(*scan) &&
646-
(scan == pattern || (*(scan - 1) != '\\')))
684+
} else if (git__iswildcard(c) && !escaped) {
685+
/* remember if we see an unescaped wildcard in pattern */
647686
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
687+
}
688+
689+
escaped = false;
648690
}
649691

650692
*base = scan;
@@ -699,9 +741,8 @@ int git_attr_fnmatch__parse(
699741
*base = git__next_line(pattern);
700742
return -1;
701743
} else {
702-
/* strip '\' that might have be used for internal whitespace */
703-
spec->length = git__unescape(spec->pattern);
704-
/* TODO: convert remaining '\' into '/' for POSIX ??? */
744+
/* strip '\' that might have been used for internal whitespace */
745+
spec->length = unescape_spaces(spec->pattern);
705746
}
706747

707748
return 0;

src/path.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,12 @@ int git_path_root(const char *path)
276276
while (path[offset] && path[offset] != '/' && path[offset] != '\\')
277277
offset++;
278278
}
279+
280+
if (path[offset] == '\\')
281+
return offset;
279282
#endif
280283

281-
if (path[offset] == '/' || path[offset] == '\\')
284+
if (path[offset] == '/')
282285
return offset;
283286

284287
return -1; /* Not a real error - signals that path is not rooted */

0 commit comments

Comments
 (0)