libstdc++: Fix std::regex_replace for strings with embedded null [PR103664]

The overload of std::regex_replace that takes a std::basic_string as the
fmt argument (for the replacement string) is implemented in terms of the
one taking a const C*, which uses std::char_traits to find the length.
That means it stops at a null character, even though the basic_string
might have additional characters beyond that.

Rather than duplicate the implementation of the const C* one for the
std::basic_string case, this moves that implementation to a new
__regex_replace function which takes a const C* and a length. Then both
the std::basic_string and const C* overloads can call that (with the
latter using char_traits to find the length to pass to the new
function).

libstdc++-v3/ChangeLog:

	PR libstdc++/103664
	* include/bits/regex.h (__regex_replace): Declare.
	(regex_replace): Use it.
	* include/bits/regex.tcc (__regex_replace): Replace regex_replace
	definition with __regex_replace.
	* testsuite/28_regex/algorithms/regex_replace/char/103664.cc: New test.
This commit is contained in:
Jonathan Wakely 2021-12-12 21:15:17 +00:00
parent 3788c4ed2c
commit ef5d671cd8
3 changed files with 33 additions and 7 deletions

View File

@ -2488,6 +2488,15 @@ _GLIBCXX_END_NAMESPACE_CXX11
= regex_constants::match_default) = delete;
// std [28.11.4] Function template regex_replace
template<typename _Out_iter, typename _Bi_iter,
typename _Rx_traits, typename _Ch_type>
_Out_iter
__regex_replace(_Out_iter __out, _Bi_iter __first, _Bi_iter __last,
const basic_regex<_Ch_type, _Rx_traits>& __e,
const _Ch_type* __fmt, size_t __len,
regex_constants::match_flag_type __flags);
/**
* @brief Search for a regular expression within a range for multiple times,
and replace the matched parts through filling a format string.
@ -2511,7 +2520,8 @@ _GLIBCXX_END_NAMESPACE_CXX11
regex_constants::match_flag_type __flags
= regex_constants::match_default)
{
return regex_replace(__out, __first, __last, __e, __fmt.c_str(), __flags);
return std::__regex_replace(__out, __first, __last, __e, __fmt.c_str(),
__fmt.length(), __flags);
}
/**
@ -2534,7 +2544,13 @@ _GLIBCXX_END_NAMESPACE_CXX11
const basic_regex<_Ch_type, _Rx_traits>& __e,
const _Ch_type* __fmt,
regex_constants::match_flag_type __flags
= regex_constants::match_default);
= regex_constants::match_default)
{
return std::__regex_replace(__out, __first, __last, __e, __fmt,
char_traits<_Ch_type>::length(__fmt),
__flags);
}
/**
* @brief Search for a regular expression within a string for multiple times,

View File

@ -461,10 +461,10 @@ namespace __detail
template<typename _Out_iter, typename _Bi_iter,
typename _Rx_traits, typename _Ch_type>
_Out_iter
regex_replace(_Out_iter __out, _Bi_iter __first, _Bi_iter __last,
const basic_regex<_Ch_type, _Rx_traits>& __e,
const _Ch_type* __fmt,
regex_constants::match_flag_type __flags)
__regex_replace(_Out_iter __out, _Bi_iter __first, _Bi_iter __last,
const basic_regex<_Ch_type, _Rx_traits>& __e,
const _Ch_type* __fmt, size_t __len,
regex_constants::match_flag_type __flags)
{
typedef regex_iterator<_Bi_iter, _Ch_type, _Rx_traits> _IterT;
_IterT __i(__first, __last, __e, __flags);
@ -477,7 +477,6 @@ namespace __detail
else
{
sub_match<_Bi_iter> __last;
auto __len = char_traits<_Ch_type>::length(__fmt);
for (; __i != __end; ++__i)
{
if (!(__flags & regex_constants::format_no_copy))

View File

@ -0,0 +1,11 @@
// { dg-do run { target c++11 } }
#include <regex>
#include <testsuite_hooks.h>
int main()
{
// PR libstdc++/103664
std::string a = regex_replace("123", std::regex("2"), std::string("a\0b", 3));
VERIFY( a == std::string("1a\0b3", 5) );
}