Fix BLI_str_escape with control characters, add unit tests
This commit is contained in:
@@ -85,7 +85,7 @@ size_t BLI_vsnprintf_rlen(char *__restrict buffer,
|
|||||||
char *BLI_sprintfN(const char *__restrict format, ...) ATTR_WARN_UNUSED_RESULT
|
char *BLI_sprintfN(const char *__restrict format, ...) ATTR_WARN_UNUSED_RESULT
|
||||||
ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1, 2);
|
ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1, 2);
|
||||||
|
|
||||||
size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
|
size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const size_t dst_maxncpy)
|
||||||
ATTR_NONNULL();
|
ATTR_NONNULL();
|
||||||
|
|
||||||
size_t BLI_str_format_int_grouped(char dst[16], int num) ATTR_NONNULL();
|
size_t BLI_str_format_int_grouped(char dst[16], int num) ATTR_NONNULL();
|
||||||
|
@@ -317,49 +317,40 @@ char *BLI_sprintfN(const char *__restrict format, ...)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* match pythons string escaping, assume double quotes - (")
|
/**
|
||||||
* TODO: should be used to create RNA animation paths.
|
* This roughly matches C and Python's string escaping with double quotes - `"`.
|
||||||
* TODO: support more fancy string escaping. current code is primitive
|
*
|
||||||
* this basically is an ascii version of PyUnicode_EncodeUnicodeEscape()
|
* Since every character may need escaping,
|
||||||
* which is a useful reference. */
|
* it's common to create a buffer twice as large as the input.
|
||||||
size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
|
*
|
||||||
|
* \param dst: The destination string, at least \a dst_maxncpy, typically `(strlen(src) * 2) + 1`.
|
||||||
|
* \param src: The un-escaped source string.
|
||||||
|
* \param dst_maxncpy: The maximum number of bytes allowable to copy.
|
||||||
|
*
|
||||||
|
* \note This is used for creating animation paths in blend files.
|
||||||
|
*/
|
||||||
|
size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const size_t dst_maxncpy)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
BLI_assert(dst_maxncpy != 0);
|
||||||
|
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
for (; (len < dst_maxncpy) && (*src != '\0'); dst++, src++, len++) {
|
||||||
BLI_assert(maxncpy != 0);
|
char c = *src;
|
||||||
|
if (ELEM(c, '\\', '"') || /* Use as-is. */
|
||||||
while (len < maxncpy) {
|
((c == '\t') && ((void)(c = 't'), true)) || /* Tab. */
|
||||||
switch (*src) {
|
((c == '\n') && ((void)(c = 'n'), true)) || /* Newline. */
|
||||||
case '\0':
|
((c == '\r') && ((void)(c = 'r'), true))) /* Carriage return. */
|
||||||
goto escape_finish;
|
{
|
||||||
case '\\':
|
if (UNLIKELY(len + 1 >= dst_maxncpy)) {
|
||||||
case '"':
|
/* Not enough space to escape. */
|
||||||
ATTR_FALLTHROUGH;
|
|
||||||
|
|
||||||
/* less common but should also be support */
|
|
||||||
case '\t':
|
|
||||||
case '\n':
|
|
||||||
case '\r':
|
|
||||||
if (len + 1 < maxncpy) {
|
|
||||||
*dst++ = '\\';
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* not enough space to escape */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ATTR_FALLTHROUGH;
|
|
||||||
default:
|
|
||||||
*dst = *src;
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
*dst++ = '\\';
|
||||||
|
len++;
|
||||||
}
|
}
|
||||||
dst++;
|
*dst = c;
|
||||||
src++;
|
|
||||||
len++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
escape_finish:
|
|
||||||
|
|
||||||
*dst = '\0';
|
*dst = '\0';
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
@@ -802,3 +802,69 @@ TEST_F(StringCasecmpNatural, TextAndNumbers)
|
|||||||
testReturnsLessThanZeroForAll(negative);
|
testReturnsLessThanZeroForAll(negative);
|
||||||
testReturnsMoreThanZeroForAll(positive);
|
testReturnsMoreThanZeroForAll(positive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* BLI_str_escape */
|
||||||
|
|
||||||
|
class StringEscape : public testing::Test {
|
||||||
|
protected:
|
||||||
|
StringEscape()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using CompareWordsArray = vector<std::array<const char *, 2>>;
|
||||||
|
|
||||||
|
void testEscapeWords(const CompareWordsArray &items)
|
||||||
|
{
|
||||||
|
size_t dst_test_len;
|
||||||
|
char dst_test[64];
|
||||||
|
for (const auto &item : items) {
|
||||||
|
/* Escape the string. */
|
||||||
|
dst_test_len = BLI_str_escape(dst_test, item[0], SIZE_MAX);
|
||||||
|
EXPECT_STREQ(dst_test, item[1]);
|
||||||
|
EXPECT_EQ(dst_test_len, strlen(dst_test));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(StringEscape, Simple)
|
||||||
|
{
|
||||||
|
const CompareWordsArray equal{
|
||||||
|
{"", ""},
|
||||||
|
{"/", "/"},
|
||||||
|
{"'", "'"},
|
||||||
|
{"?", "?"},
|
||||||
|
};
|
||||||
|
|
||||||
|
const CompareWordsArray escaped{
|
||||||
|
{"\\", "\\\\"},
|
||||||
|
{"A\\", "A\\\\"},
|
||||||
|
{"\\A", "\\\\A"},
|
||||||
|
{"A\\B", "A\\\\B"},
|
||||||
|
{"?", "?"},
|
||||||
|
{"\"\\", "\\\"\\\\"},
|
||||||
|
{"\\\"", "\\\\\\\""},
|
||||||
|
{"\"\\\"", "\\\"\\\\\\\""},
|
||||||
|
|
||||||
|
{"\"\"\"", "\\\"\\\"\\\""},
|
||||||
|
{"\\\\\\", "\\\\\\\\\\\\"},
|
||||||
|
};
|
||||||
|
|
||||||
|
testEscapeWords(equal);
|
||||||
|
testEscapeWords(escaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StringEscape, Control)
|
||||||
|
{
|
||||||
|
const CompareWordsArray escaped{
|
||||||
|
{"\n", "\\n"},
|
||||||
|
{"\r", "\\r"},
|
||||||
|
{"\t", "\\t"},
|
||||||
|
{"A\n", "A\\n"},
|
||||||
|
{"\nA", "\\nA"},
|
||||||
|
{"\n\r\t", "\\n\\r\\t"},
|
||||||
|
{"\n_\r_\t", "\\n_\\r_\\t"},
|
||||||
|
{"\n\\\r\\\t", "\\n\\\\\\r\\\\\\t"},
|
||||||
|
};
|
||||||
|
|
||||||
|
testEscapeWords(escaped);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user