diff options
author | Ben Boeckel <ben.boeckel@kitware.com> | 2019-03-14 18:33:28 (GMT) |
---|---|---|
committer | Ben Boeckel <ben.boeckel@kitware.com> | 2019-03-18 18:18:13 (GMT) |
commit | 53184a727d0f3b233988212628fec6ef7803da69 (patch) | |
tree | 6d533af81c3b81368b4fd0aab0c9f91ba1cb51ca | |
parent | 875f49cc588b898f5da24bff34638d16de5c6413 (diff) | |
download | CMake-53184a727d0f3b233988212628fec6ef7803da69.zip CMake-53184a727d0f3b233988212628fec6ef7803da69.tar.gz CMake-53184a727d0f3b233988212628fec6ef7803da69.tar.bz2 |
cm_utf8: add an is_valid function
-rw-r--r-- | Source/cm_utf8.c | 19 | ||||
-rw-r--r-- | Source/cm_utf8.h | 4 | ||||
-rw-r--r-- | Tests/CMakeLib/testUTF8.cxx | 78 |
3 files changed, 101 insertions, 0 deletions
diff --git a/Source/cm_utf8.c b/Source/cm_utf8.c index d41d097..62e7e8c 100644 --- a/Source/cm_utf8.c +++ b/Source/cm_utf8.c @@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cm_utf8.h" +#include <string.h> + /* RFC 3629 07-bit: 0xxxxxxx @@ -85,3 +87,20 @@ const char* cm_utf8_decode_character(const char* first, const char* last, return first; } } + +int cm_utf8_is_valid(const char* s) +{ + if (!s) { + return 0; + } + + const char* last = s + strlen(s); + const char* pos = s; + unsigned int pc; + + while (pos != last && (pos = cm_utf8_decode_character(pos, last, &pc))) { + /* Nothing to do. */ + } + + return pos == last; +} diff --git a/Source/cm_utf8.h b/Source/cm_utf8.h index fcb43e0..27dc559 100644 --- a/Source/cm_utf8.h +++ b/Source/cm_utf8.h @@ -13,6 +13,10 @@ extern "C" { const char* cm_utf8_decode_character(const char* first, const char* last, unsigned int* pc); +/** Returns whether a C string is a sequence of valid UTF-8 encoded Unicode + codepoints. Returns non-zero on success. */ +int cm_utf8_is_valid(const char* s); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/Tests/CMakeLib/testUTF8.cxx b/Tests/CMakeLib/testUTF8.cxx index 7f52c82..a0bb5cd 100644 --- a/Tests/CMakeLib/testUTF8.cxx +++ b/Tests/CMakeLib/testUTF8.cxx @@ -13,6 +13,21 @@ static void test_utf8_char_print(test_utf8_char const c) static_cast<int>(d[3])); } +static void byte_array_print(char const* s) +{ + unsigned char const* d = reinterpret_cast<unsigned char const*>(s); + bool started = false; + printf("["); + for (; *d; ++d) { + if (started) { + printf(","); + } + started = true; + printf("0x%02X", static_cast<int>(*d)); + } + printf("]"); +} + struct test_utf8_entry { int n; @@ -46,6 +61,13 @@ static test_utf8_char const bad_chars[] = { { 0, 0, 0, 0, 0 } }; +static char const* good_strings[] = { "", "ASCII", "\xC2\xA9 Kitware", 0 }; + +static char const* bad_strings[] = { + "\xC0\x80", /* Modified UTF-8 for embedded 0-byte. */ + 0 +}; + static void report_good(bool passed, test_utf8_char const c) { printf("%s: decoding good ", passed ? "pass" : "FAIL"); @@ -99,6 +121,46 @@ static bool decode_bad(test_utf8_char const s) return true; } +static void report_valid(bool passed, char const* s) +{ + printf("%s: validity good ", passed ? "pass" : "FAIL"); + byte_array_print(s); + printf(" (%s) ", s); +} + +static void report_invalid(bool passed, char const* s) +{ + printf("%s: validity bad ", passed ? "pass" : "FAIL"); + byte_array_print(s); + printf(" "); +} + +static bool is_valid(const char* s) +{ + bool valid = cm_utf8_is_valid(s) != 0; + if (!valid) { + report_valid(false, s); + printf("expected valid, reported as invalid\n"); + return false; + } + report_valid(true, s); + printf("valid as expected\n"); + return true; +} + +static bool is_invalid(const char* s) +{ + bool valid = cm_utf8_is_valid(s) != 0; + if (valid) { + report_invalid(false, s); + printf("expected invalid, reported as valid\n"); + return false; + } + report_invalid(true, s); + printf("invalid as expected\n"); + return true; +} + int testUTF8(int /*unused*/, char* /*unused*/ []) { int result = 0; @@ -106,11 +168,27 @@ int testUTF8(int /*unused*/, char* /*unused*/ []) if (!decode_good(*e)) { result = 1; } + if (!is_valid(e->str)) { + result = 1; + } } for (test_utf8_char const* c = bad_chars; (*c)[0]; ++c) { if (!decode_bad(*c)) { result = 1; } + if (!is_invalid(*c)) { + result = 1; + } + } + for (char const** s = good_strings; *s; ++s) { + if (!is_valid(*s)) { + result = 1; + } + } + for (char const** s = bad_strings; *s; ++s) { + if (!is_invalid(*s)) { + result = 1; + } } return result; } |