Problem: possible undefined behaviour in mb_decompose(), when using the
same pointer as argument several times
Solution: use separate assignments to avoid reading and writing the same
object at the same time (Áron Hárnási)
closes: #17953
Signed-off-by: Áron Hárnási <aron.harnasi@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: style: more wrong indentation
Solution: reformat a few more places
(Yegappan Lakshmanan)
closes: #17309
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: regexp: max \U and \%U value is limited by INT_MAX but gives a
confusing error message (related: v8.1.0985).
Solution: give a better error message when the value reaches INT_MAX
When searching Vim allows to get up to 8 hex characters using the /\V
and /\%V regex atoms. However, when using "/\UFFFFFFFF" the code point is
already above what an integer variable can hold, which is 2,147,483,647.
Since patch v8.1.0985, Vim already limited the max codepoint to INT_MAX
(otherwise it caused a crash in the nfa regex engine), but instead of
error'ing out it silently fell back to parse the number as a backslash
value and not as a codepoint value and as such this "/[\UFFFFFFFF]" will
happily find a "\" or an literal "F". And this "/[\d127-\UFFFFFFFF]"
will error out as "reverse range in character class).
Interestingly, the max Unicode codepoint value is U+10FFFF which still
fits into an ordinary integer value, which means, that we don't even
need to parse 8 hex characters, but 6 should have been enough.
However, let's not limit Vim to search for only max 6 hex characters
(which would be a backward incompatible change), but instead allow all 8
characters and only if the codepoint reaches INT_MAX, give a more
precise error message (about what the max unicode codepoint value is).
This allows to search for "[\U7FFFFFFE]" (will likely return "E486
Pattern not found") and "[/\U7FFFFFF]" now errors "E1517: Value too
large, max Unicode codepoint is U+10FFFF".
While this change is straight forward on architectures where long is 8
bytes, this is not so simple on Windows or 32bit architectures where long
is 4 bytes (and therefore the test fails there). To account for that,
let's make use of the vimlong_T number type and make a few corresponding
changes in the regex engine code and cast the value to the expected data
type. This however may not work correctly on systems that doesn't have
the long long datatype (e.g. OpenVMS) and probably the test will fail
there.
fixes: #16949closes: #16994
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: string_T struct could be used more often
Solution: Refactor code and make use of string_T struct
for key-value pairs, reformat overlong lines
(John Marriott)
closes: #15975
Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Coverity warning in cstrncmp()
(after v9.1.0645)
Solution: Change the type of n2 to int.
(zeertzjq)
________________________________________________________________________________________________________
*** CID 1615684: Integer handling issues (INTEGER_OVERFLOW)
/src/regexp.c: 1757 in cstrncmp()
1751 n1 -= mb_ptr2len(s1);
1752 MB_PTR_ADV(p);
1753 n2++;
1754 }
1755 // count the number of bytes to advance the same number of chars for s2
1756 p = s2;
>>> CID 1615684: Integer handling issues (INTEGER_OVERFLOW)
>>> Expression "n2--", which is equal to 18446744073709551615, where "n2" is known to be equal to 0, underflows the type that receives it, an unsigned integer 64 bits wide.
1757 while (n2-- > 0 && *p != NUL)
1758 MB_PTR_ADV(p);
1759
1760 n2 = p - s2;
1761
1762 result = MB_STRNICMP2(s1, s2, *n, n2);
closes: #15409
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: regex: wrong match when searching multi-byte char
case-insensitive (diffsetter)
Solution: Apply proper case-folding for characters and search-string
This patch does the following 4 things:
1) When the regexp engine compares two utf-8 codepoints case
insensitive it may match an adjacent character, because it assumes
it can step over as many bytes as the pattern contains.
This however is not necessarily true because of case-folding, a
multi-byte UTF-8 character can be considered equal to some
single-byte value.
Let's consider the pattern 'ſ' and the string 's'. When comparing and
ignoring case, the single character 's' matches, and since it matches
Vim will try to step over the match (by the amount of bytes of the
pattern), assuming that since it matches, the length of both strings is
the same.
However in that case, it should only step over the single byte value
's' by 1 byte and try to start matching after it again. So for the
backtracking engine we need to ensure:
* we try to match the correct length for the pattern and the text
* in case of a match, we step over it correctly
There is one tricky thing for the backtracing engine. We also need to
calculate correctly the number of bytes to compare the 2 different
utf-8 strings s1 and s2. So we will count the number of characters in
s1 that the byte len specified. Then we count the number of bytes to
step over the same number of characters in string s2 and then we can
correctly compare the 2 utf-8 strings.
2) A similar thing can happen for the NFA engine, when skipping to the
next character to test for a match. We are skipping over the regstart
pointer, however we do not consider the case that because of
case-folding we may need to adjust the number of bytes to skip over.
So this needs to be adjusted in find_match_text() as well.
3) A related issue turned out, when prog->match_text is actually empty.
In that case we should try to find the next match and skip this
condition.
4) When comparing characters using collections, we must also apply case
folding to each character in the collection and not just to the
current character from the search string. This doesn't apply to the
NFA engine, because internally it converts collections to branches
[abc] -> a\|b\|c
fixes: #14294closes: #14756
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Wrong Ex command executed when :g uses '?' as delimiter and
pattern contains escaped '?'.
Solution: Don't use "*newp" when it's not allocated (zeertzjq).
closes: #14837
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: too many strlen() calls in the regexp engine
Solution: refactor code to retrieve strlen differently, make use
of bsearch() for getting the character class
(John Marriott)
closes: #14648
Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Patch 9.1.0296 causes too many issues
(Tony Mechelynck, @chdiza, CI)
Solution: Back out the change for now
Revert "patch 9.1.0296: regexp: engines do not handle case-folding well"
This reverts commit 7a27c108e0509f3255ebdcb6558e896c223e4d23 it causes
issues with syntax highlighting and breaks the FreeBSD and MacOS CI. It
needs more work.
fixes: #14487
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Regex engines do not handle case-folding well
Solution: Correctly calculate byte length of characters to skip
When the regexp engine compares two utf-8 codepoints case insensitively
it may match an adjacent character, because it assumes it can step over
as many bytes as the pattern contains.
This however is not necessarily true because of case-folding, a
multi-byte UTF-8 character can be considered equal to some single-byte
value.
Let's consider the pattern 'ſ' and the string 's'. When comparing and
ignoring case, the single character 's' matches, and since it matches
Vim will try to step over the match (by the amount of bytes of the
pattern), assuming that since it matches, the length of both strings is
the same.
However in that case, it should only step over the single byte
value 's' so by 1 byte and try to start matching after it again. So for the
backtracking engine we need to ensure:
- we try to match the correct length for the pattern and the text
- in case of a match, we step over it correctly
The same thing can happen for the NFA engine, when skipping to the next
character to test for a match. We are skipping over the regstart
pointer, however we do not consider the case that because of
case-folding we may need to adjust the number of bytes to skip over. So
this needs to be adjusted in find_match_text() as well.
A related issue turned out, when prog->match_text is actually empty. In
that case we should try to find the next match and skip this condition.
fixes: #14294closes: #14433
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: ml_get: invalid lnum when :s replaces visual selection
(@ropery)
Solution: substitute may decrement the number of lines in a buffer,
so validate, that the bottom lines of the visual selection
stays within the max buffer line
fixes: #13890closes: #13892
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: regexp cannot match combining chars in collection
Solution: Check for combining characters in regex collections for the
NFA and BT Regex Engine
Also, while at it, make debug mode work again.
fixes#10286closes: #12871
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: undefined behaviour upper/lower function ptrs
Solution: Fix UBSAN error in regexp and simplify upper/lowercase
modifier code
The implementation of \u / \U / \l / \L modifiers in the substitute
command relies on remembering the state by setting function pointers on
func_all/func_one in the code. The code signature of `fptr_T` is
supposed to return void* (due to C function signatures not being able to
return itself due to type recursion), and the definition of the
functions (e.g. to_Upper) didn't follow this rule, and so the code tries
to cast functions of different signatures, resulting in undefined
behavior error under UBSAN in Clang 17. See #12745.
We could just fix `do_Upper`/etc to just return void*, which would fix
the problem. However, these functions actually do not need to return
anything at all. It used to be the case that there was only one pointer
"func" to store the pointer, which is why the function needs to either
return itself or NULL to indicate whether it's a one time or ongoing
modification. However, c2c355df6f094cdb9e599fd395a78c14486ec697
(7.3.873) already made that obsolete by introducing `func_one` and
`func_all` to store one-time and ongoing operations separately, so these
functions don't actually need to return anything anymore because it's
implicit whether it's a one-time or ongoing operation. Simplify the code
to reflect that.
closes: #13117
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
Problem: CI error on different signedness in regexp.c
(after patch 9.0.1848)
Solution: Cast strlen() call to int
Signed-off-by: Christian Brabandt <cb@256bit.org>
Problem: Evaluating "expr" options has more overhead than needed.
Solution: Use call_simple_func() for 'foldtext', 'includeexpr', 'printexpr',
"expr" of 'spellsuggest', 'diffexpr', 'patchexpr', 'balloonexpr',
'formatexpr', 'indentexpr' and 'charconvert'.
Problem: Timeout handling is not optimal.
Solution: Avoid setting timeout_flag twice. Adjust the pointer when
stopping the regexp timeout. Adjust variable name.
Problem: Search timeout is overrun with some patterns.
Solution: Check for timeout in more places. Make the flag volatile and
atomic. Use assert_inrange() to see what happened.
Problem: "vimgrep /\%v/ *" may cause a crash.
Solution: When compiling the pattern with the old engine fails, restore the
regprog of the new engine instead of leaving it NULL.
(closes#10079)
Problem: Accessing invalid memory when a regular expression checks the
Visual area while matching in a string.
Solution: Do not try matching the Visual area in a string.