/* Copyright 2016 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #ifdef CRAS_DBUS #include #endif #include "cras_utf8.h" #include "cras_util.h" static const uint8_t kUTF8ByteOrderMask[3] = { 0xef, 0xbb, 0xbf }; typedef struct u8range { uint8_t min; uint8_t max; } u8range_t; static const u8range_t kUTF8TwoByteSeq[] = { { 0xc2, 0xdf }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8ByteSeqE0[] = { { 0xe0, 0xe0 }, { 0xa0, 0xbf }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8ByteSeqE1EC[] = { { 0xe1, 0xec }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8ByteSeqED[] = { { 0xed, 0xed }, { 0x80, 0x9f }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8ByteSeqEEEF[] = { { 0xee, 0xef }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8ByteSeqF0[] = { { 0xf0, 0xf0 }, { 0x90, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8ByteSeqF1F3[] = { { 0xf1, 0xf3 }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8ByteSeqF4[] = { { 0xf4, 0xf4 }, { 0x80, 0x8f }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } }; static const u8range_t kUTF8NullRange[] = { { 0, 0 } }; typedef struct utf8seq { const u8range_t *ranges; } utf8seq_t; static const utf8seq_t kUTF8Sequences[] = { { kUTF8TwoByteSeq }, { kUTF8ByteSeqE0 }, { kUTF8ByteSeqE1EC }, { kUTF8ByteSeqED }, { kUTF8ByteSeqEEEF }, { kUTF8ByteSeqF0 }, { kUTF8ByteSeqF1F3 }, { kUTF8ByteSeqF4 }, { kUTF8NullRange } }; int valid_utf8_string(const char *string, size_t *bad_pos) { int bom_chars = 0; uint8_t byte; const char *pos = string; int ret = 1; const utf8seq_t *seq = NULL; const u8range_t *range = NULL; if (!pos) { ret = 0; goto error; } while ((byte = (uint8_t)*(pos++))) { if (!range || range->min == 0) { if (byte < 128) { /* Ascii character. */ continue; } if (bom_chars < ARRAY_SIZE(kUTF8ByteOrderMask)) { if (byte == kUTF8ByteOrderMask[bom_chars]) { bom_chars++; continue; } else { /* Characters not matching BOM. * Rewind and assume that there is * no BOM. */ bom_chars = ARRAY_SIZE(kUTF8ByteOrderMask); pos = string; continue; } } /* Find the matching sequence of characters by * matching the first character in the sequence. */ seq = kUTF8Sequences; while (seq->ranges->min != 0) { if (byte >= seq->ranges->min && byte <= seq->ranges->max) { /* Matching sequence. */ break; } seq++; } if (seq->ranges->min == 0) { /* Could not find a matching sequence. */ ret = 0; goto error; } /* Found the appropriate sequence. */ range = seq->ranges + 1; continue; } if (byte >= range->min && byte <= range->max) { range++; continue; } /* This character doesn't belong in UTF8. */ ret = 0; goto error; } if (range && range->min != 0) { /* Stopped in the middle of a sequence. */ ret = 0; } error: if (bad_pos) *bad_pos = pos - string - 1; return ret; } #ifdef CRAS_DBUS /* Use the DBus implementation if available to ensure that the UTF-8 * sequences match those expected by the DBus implementation. */ int is_utf8_string(const char *string) { return !!dbus_validate_utf8(string, NULL); } #else int is_utf8_string (const char *string) { return valid_utf8_string(string, NULL); } #endif