sln gave a good solution to your problem, so I'll try to explain what the problem is.
Welcome to the joys of catastrophic backtracking. The core of your problem is in (((?![<>:"/\\|?*]).)+((?<![ .])(\\|/))?)*
. (Now that I've said that, all your problems are solved, right? Easy peasy.)
Assuming you're a bit like me and blinked blankly a couple of times the first time someone said "regex backtracking", we can work through your regex with the shorter input /path./
. This is an invalid path according to your regex, but lets us (somewhat) easily walk through the problem.
^([\.]*)([/]+)
matches the leading /
. This works fine.
For the sake of readability here, I'm going to call the first half of the problematic capture group, ((?![<>:"/\\|?*]).)+
, x+
, and the second half, ((?<![ .])(\\|/))?
, y?
. The whole group is (x+y?)
.
How is (x+y?)*$
backtracking when matching path./
?
x+
matches path.
y?
matches
(it matches 0 times, which is fine because of the ?
)
(x+y?)
has now matched once
(x+y?)
repeats, and fails, since it does not match /
. Thus, (x+y?)*
has matched path.
$
fails, since it does not match /
.
- The regex engine backtracks:
(x+y?)*
can only backtrack into its first iteration, since it only had one iteration.
- Within that iteration,
y?
can't backtrack, since it matched 0 times.
x+
backtracks, to match only path
instead of path.
x+
matches path
y?
matches
(x+y?)
has now matched once (path
)
(x+y?)
repeats and matches again:
(x+y?)
repeats, and fails since it does not match /
. Thus, (x+y?)*
has matched path.
$
fails, since it does not match /
.
- The regex engine backtracks:
(x+y?)*
can only backtrack into its first iteration, since in its second iteration x+
matched only once and y?
matched 0 times.
y?
in the first iteration matched 0 times, so it can't backtrack
x+
can backtrack to only matching pat
- Hopefully you get the idea, but
(x+y?)
matches twice: pat
, h.
; then on the next backtrack we have pat
h
.
, and then pa
th.
, and so on.
It takes 478 steps for the engine to determine that /path./
is not matched by your regex. Every additional character in that problematic capture group increases the number of backtracks by a lot, and after a certain point your regex implementation is just going to throw up its hands and give up. sln's solution takes only 49 steps.
The behaviour of a regex engine when backtracking is difficult both to explain and to grasp, especially when limited to Markdown, so I would recommend running your regex through a debugger to visualize what's going on.