This unifies the utf8 handling code which was previously duplicated in udev and systemd, and also removes some unused code and renames utf8_is_valid to utf8_validate. --- TODO | 1 - src/core/load-fragment.c | 4 +- src/core/service.c | 2 +- src/libsystemd-bus/bus-message.c | 2 +- src/libsystemd-bus/bus-socket.c | 2 +- src/locale/localectl.c | 2 +- src/shared/conf-parser.c | 8 +- src/shared/env-util.c | 2 +- src/shared/fileio.c | 8 +- src/shared/utf8.c | 279 ++++++++++----------------------------- src/shared/utf8.h | 3 +- src/test/test-utf8.c | 15 ++- 12 files changed, 99 insertions(+), 229 deletions(-)
diff --git a/TODO b/TODO index c0f51de..01bc993 100644 --- a/TODO +++ b/TODO @@ -602,7 +602,6 @@ Features: * udev: - remove src/udev/udev-builtin-firmware.c (CONFIG_FW_LOADER_USER_HELPER=n) - move to LGPL - - unify utf8 validator code with shared/ - kill scsi_id - add trigger --subsystem-match=usb/usb_device device diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 74454ab..bdb2543 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -514,7 +514,7 @@ int config_parse_exec(const char *unit, goto fail; } - if (!utf8_is_valid(path)) { + if (!utf8_validate(path)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); @@ -531,7 +531,7 @@ int config_parse_exec(const char *unit, goto fail; } - if (!utf8_is_valid(c)) { + if (!utf8_validate(c)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); diff --git a/src/core/service.c b/src/core/service.c index cc61b54..4c22319 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3409,7 +3409,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (e[7]) { - if (!utf8_is_valid(e+7)) { + if (!utf8_validate(e+7)) { log_warning_unit(u->id, "Status message in notification is not UTF-8 clean."); return; diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c index 760a148..19f0b4b 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -2516,7 +2516,7 @@ static bool validate_string(const char *s, size_t l) { return false; /* Check if valid UTF8 */ - if (!utf8_is_valid(s)) + if (!utf8_validate(s)) return false; return true; diff --git a/src/libsystemd-bus/bus-socket.c b/src/libsystemd-bus/bus-socket.c index b60facb..f3ad53f 100644 --- a/src/libsystemd-bus/bus-socket.c +++ b/src/libsystemd-bus/bus-socket.c @@ -282,7 +282,7 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { if (memchr(token, 0, l/2)) return 0; - return !!utf8_is_valid(token); + return !!utf8_validate(token); } static int verify_external_token(sd_bus *b, const char *p, size_t l) { diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 8259c0a..e21454d 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -361,7 +361,7 @@ static int add_locales_from_archive(Set *locales) { if (e[i].locrec_offset == 0) continue; - if (!utf8_is_valid((char*) p + e[i].name_offset)) + if (!utf8_validate((char*) p + e[i].name_offset)) continue; z = strdup((char*) p + e[i].name_offset); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 6085d33..1da8c78 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -569,7 +569,7 @@ int config_parse_string(const char *unit, if (!n) return log_oom(); - if (!utf8_is_valid(n)) { + if (!utf8_validate(n)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "String is not UTF-8 clean, ignoring assignment: %s", rvalue); free(n); @@ -606,7 +606,7 @@ int config_parse_path(const char *unit, assert(rvalue); assert(data); - if (!utf8_is_valid(rvalue)) { + if (!utf8_validate(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); return 0; @@ -674,7 +674,7 @@ int config_parse_strv(const char *unit, if (!n) return log_oom(); - if (!utf8_is_valid(n)) { + if (!utf8_validate(n)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "String is not UTF-8 clean, ignoring: %s", rvalue); continue; @@ -722,7 +722,7 @@ int config_parse_path_strv(const char *unit, if (!n) return log_oom(); - if (!utf8_is_valid(n)) { + if (!utf8_validate(n)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); continue; diff --git a/src/shared/env-util.c b/src/shared/env-util.c index 5e29629..35b8468 100644 --- a/src/shared/env-util.c +++ b/src/shared/env-util.c @@ -75,7 +75,7 @@ bool env_value_is_valid(const char *e) { if (!e) return false; - if (!utf8_is_valid(e)) + if (!utf8_validate(e)) return false; if (string_has_cc(e)) diff --git a/src/shared/fileio.c b/src/shared/fileio.c index 8aa4cdb..7b12c96 100644 --- a/src/shared/fileio.c +++ b/src/shared/fileio.c @@ -461,9 +461,9 @@ fail: static int parse_env_file_push(const char *filename, unsigned line, const char *key, char *value, void *userdata) { - assert(utf8_is_valid(key)); + assert(utf8_validate(key)); - if (value && !utf8_is_valid(value)) + if (value && !utf8_validate(value)) /* FIXME: filter UTF-8 */ log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.", filename, line, key, value); @@ -513,9 +513,9 @@ int parse_env_file( static int load_env_file_push(const char *filename, unsigned line, const char *key, char *value, void *userdata) { - assert(utf8_is_valid(key)); + assert(utf8_validate(key)); - if (value && !utf8_is_valid(value)) + if (value && !utf8_validate(value)) /* FIXME: filter UTF-8 */ log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.", filename, line, key, value); diff --git a/src/shared/utf8.c b/src/shared/utf8.c index 1a68394..37382d8 100644 --- a/src/shared/utf8.c +++ b/src/shared/utf8.c @@ -51,8 +51,6 @@ #include "utf8.h" #include "util.h" -#define FILTER_CHAR '_' - static inline bool is_unicode_valid(uint32_t ch) { if (ch >= 0x110000) /* End of unicode space */ @@ -67,17 +65,6 @@ static inline bool is_unicode_valid(uint32_t ch) { return true; } -static inline bool is_continuation_char(uint8_t ch) { - if ((ch & 0xc0) != 0x80) /* 10xxxxxx */ - return false; - return true; -} - -static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) { - *u_ch <<= 6; - *u_ch |= ch & 0x3f; -} - static bool is_unicode_control(uint32_t ch) { /* @@ -90,163 +77,97 @@ static bool is_unicode_control(uint32_t ch) { (0x7F <= ch && ch <= 0x9F); } -bool utf8_is_printable(const char* str, size_t length) { - uint32_t val = 0; - uint32_t min = 0; - const uint8_t *p; +/* count of characters used to encode one unicode char */ +static int utf8_encoded_expected_len(const char *str) { + unsigned char c = (unsigned char)str[0]; - assert(str); + if (c < 0x80) + return 1; + if ((c & 0xe0) == 0xc0) + return 2; + if ((c & 0xf0) == 0xe0) + return 3; + if ((c & 0xf8) == 0xf0) + return 4; + if ((c & 0xfc) == 0xf8) + return 5; + if ((c & 0xfe) == 0xfc) + return 6; + return 0; +} - for (p = (const uint8_t*) str; length; p++, length--) { - if (*p < 128) { - val = *p; - } else { - if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */ - min = 128; - val = (uint32_t) (*p & 0x1e); - goto ONE_REMAINING; - } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/ - min = (1 << 11); - val = (uint32_t) (*p & 0x0f); - goto TWO_REMAINING; - } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */ - min = (1 << 16); - val = (uint32_t) (*p & 0x07); - } else - return false; - - p++; - length--; - if (!length || !is_continuation_char(*p)) - return false; - merge_continuation_char(&val, *p); - - TWO_REMAINING: - p++; - length--; - if (!is_continuation_char(*p)) - return false; - merge_continuation_char(&val, *p); - - ONE_REMAINING: - p++; - length--; - if (!is_continuation_char(*p)) - return false; - merge_continuation_char(&val, *p); - - if (val < min) - return false; - } +/* decode one unicode char */ +static int utf8_encoded_to_unichar(const char *str) { + int unichar; + int len; + int i; - if (is_unicode_control(val)) - return false; + len = utf8_encoded_expected_len(str); + switch (len) { + case 1: + return (int)str[0]; + case 2: + unichar = str[0] & 0x1f; + break; + case 3: + unichar = (int)str[0] & 0x0f; + break; + case 4: + unichar = (int)str[0] & 0x07; + break; + case 5: + unichar = (int)str[0] & 0x03; + break; + case 6: + unichar = (int)str[0] & 0x01; + break; + default: + return -1; } - return true; + for (i = 1; i < len; i++) { + if (((int)str[i] & 0xc0) != 0x80) + return -1; + unichar <<= 6; + unichar |= (int)str[i] & 0x3f; + } + + return unichar; } -static char* utf8_validate(const char *str, char *output) { - uint32_t val = 0; - uint32_t min = 0; - const uint8_t *p, *last; - int size; - uint8_t *o; +bool utf8_is_printable(const char* str, size_t length) { + const uint8_t *p; assert(str); - o = (uint8_t*) output; - for (p = (const uint8_t*) str; *p; p++) { - if (*p < 128) { - if (o) - *o = *p; - } else { - last = p; - - if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */ - size = 2; - min = 128; - val = (uint32_t) (*p & 0x1e); - goto ONE_REMAINING; - } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/ - size = 3; - min = (1 << 11); - val = (uint32_t) (*p & 0x0f); - goto TWO_REMAINING; - } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */ - size = 4; - min = (1 << 16); - val = (uint32_t) (*p & 0x07); - } else - goto error; - - p++; - if (!is_continuation_char(*p)) - goto error; - merge_continuation_char(&val, *p); - - TWO_REMAINING: - p++; - if (!is_continuation_char(*p)) - goto error; - merge_continuation_char(&val, *p); - - ONE_REMAINING: - p++; - if (!is_continuation_char(*p)) - goto error; - merge_continuation_char(&val, *p); - - if (val < min) - goto error; - - if (!is_unicode_valid(val)) - goto error; - - if (o) { - memcpy(o, last, (size_t) size); - o += size; - } - - continue; - - error: - if (o) { - *o = FILTER_CHAR; - p = last; /* We retry at the next character */ - } else - goto failure; - } + for (p = (const uint8_t*) str; length; p++) { + int encoded_len = utf8_encoded_valid_unichar((const char *)p); + int32_t val = utf8_encoded_to_unichar((const char*)p); - if (o) - o++; - } + if (encoded_len < 0 || val < 0 || is_unicode_control(val)) + return false; - if (o) { - *o = '\0'; - return output; + length -= encoded_len; } - return (char*) str; - -failure: - return NULL; -} - -char* utf8_is_valid (const char *str) { - return utf8_validate(str, NULL); + return true; } -char* utf8_filter (const char *str) { - char *new_str; +const char *utf8_validate(const char *str) { + const uint8_t *p; assert(str); - new_str = malloc(strlen(str) + 1); - if (!new_str) - return NULL; + for (p = (const uint8_t*) str; *p; ) { + int len = utf8_encoded_valid_unichar((const char *)p); + + if (len < 0) + return NULL; + + p += len; + } - return utf8_validate(str, new_str); + return str; } char *ascii_is_valid(const char *str) { @@ -318,64 +239,6 @@ char *utf16_to_utf8(const void *s, size_t length) { return r; } -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) { - unsigned char c = (unsigned char)str[0]; - - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - return 0; -} - -/* decode one unicode char */ -static int utf8_encoded_to_unichar(const char *str) { - int unichar; - int len; - int i; - - len = utf8_encoded_expected_len(str); - switch (len) { - case 1: - return (int)str[0]; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (int)str[0] & 0x0f; - break; - case 4: - unichar = (int)str[0] & 0x07; - break; - case 5: - unichar = (int)str[0] & 0x03; - break; - case 6: - unichar = (int)str[0] & 0x01; - break; - default: - return -1; - } - - for (i = 1; i < len; i++) { - if (((int)str[i] & 0xc0) != 0x80) - return -1; - unichar <<= 6; - unichar |= (int)str[i] & 0x3f; - } - - return unichar; -} - /* expected size used to encode one unicode char */ static int utf8_unichar_to_encoded_len(int unichar) { if (unichar < 0x80) diff --git a/src/shared/utf8.h b/src/shared/utf8.h index 7a5608c..3ebe965 100644 --- a/src/shared/utf8.h +++ b/src/shared/utf8.h @@ -25,12 +25,11 @@ #include "macro.h" -char *utf8_is_valid(const char *s) _pure_; +const char *utf8_validate(const char *s) _pure_; char *ascii_is_valid(const char *s) _pure_; bool utf8_is_printable(const char* str, size_t length) _pure_; -char *utf8_filter(const char *s); char *ascii_filter(const char *s); char *utf16_to_utf8(const void *s, size_t length); diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c index d2b9771..d45d51e 100644 --- a/src/test/test-utf8.c +++ b/src/test/test-utf8.c @@ -47,13 +47,22 @@ static void test_udev_encode_string(void) { assert_se(expect_encoded_as("s/ash/ng", "s\\x2fash\\x2fng")); } +static void test_utf8_is_printable(void) { + assert_se(utf8_is_printable("ascii is valid\tunicode", 22)); + assert_se(utf8_is_printable("\342\204\242", 3)); + assert_se(!utf8_is_printable("\341\204", 2)); +} + static void test_utf8_is_valid(void) { - assert_se(utf8_is_valid("ascii is valid unicode")); - assert_se(utf8_is_valid("\341\204\242")); - assert_se(!utf8_is_valid("\341\204")); + assert_se(utf8_validate("ascii is valid unicode")); + assert_se(utf8_validate("\341\204\242")); + assert_se(!utf8_validate("\341\204")); } int main(int argc, char *argv[]) { test_utf8_is_valid(); + test_utf8_is_printable(); test_udev_encode_string(); + + return 0; } -- 1.8.4 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel