/*************************************************************** MPU-UTF8ING.C This file contains UTF-8 string operation functions and strict UTF-8 <-> UCS-2 conversion support. PART OF : MPUIO - library . Copyright (C) 2000 - 2026 by Andrey V.Kosteltsev. All Rights Reserved. ***************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include int __mpu_utf8_decode( const __mpu_char8_t *src, __mpu_char32_t *value, __mpu_size_t *length ) { __mpu_char32_t c; if( src[0] < 0x80 ) { *value = src[0]; *length = 1; return 0; } if( src[0] >= 0xc2 && src[0] <= 0xdf ) { if( src[1] == 0 || (src[1] & 0xc0) != 0x80 ) return -1; *value = ((__mpu_char32_t)(src[0] & 0x1f) << 6) | (__mpu_char32_t)(src[1] & 0x3f); *length = 2; return 0; } if( src[0] >= 0xe0 && src[0] <= 0xef ) { if( src[1] == 0 || (src[1] & 0xc0) != 0x80 ) return -1; if( src[2] == 0 || (src[2] & 0xc0) != 0x80 ) return -1; if( src[0] == 0xe0 && src[1] < 0xa0 ) return -1; if( src[0] == 0xed && src[1] >= 0xa0 ) return -1; c = ((__mpu_char32_t)(src[0] & 0x0f) << 12) | ((__mpu_char32_t)(src[1] & 0x3f) << 6) | (__mpu_char32_t)(src[2] & 0x3f); *value = c; *length = 3; return 0; } if( src[0] >= 0xf0 && src[0] <= 0xf4 ) { if( src[1] == 0 || (src[1] & 0xc0) != 0x80 ) return -1; if( src[2] == 0 || (src[2] & 0xc0) != 0x80 ) return -1; if( src[3] == 0 || (src[3] & 0xc0) != 0x80 ) return -1; if( src[0] == 0xf0 && src[1] < 0x90 ) return -1; if( src[0] == 0xf4 && src[1] >= 0x90 ) return -1; c = ((__mpu_char32_t)(src[0] & 0x07) << 18) | ((__mpu_char32_t)(src[1] & 0x3f) << 12) | ((__mpu_char32_t)(src[2] & 0x3f) << 6) | (__mpu_char32_t)(src[3] & 0x3f); *value = c; *length = 4; return 0; } return -1; } int __mpu_utf8_encode( __mpu_char32_t value, __mpu_char8_t *dest, __mpu_size_t *length ) { if( value <= 0x7f ) { dest[0] = (__mpu_char8_t)value; *length = 1; return 0; } if( value <= 0x7ff ) { dest[0] = (__mpu_char8_t)(0xc0 | (value >> 6)); dest[1] = (__mpu_char8_t)(0x80 | (value & 0x3f)); *length = 2; return 0; } if( value >= 0xd800 && value <= 0xdfff ) return -1; if( value <= 0xffff ) { dest[0] = (__mpu_char8_t)(0xe0 | (value >> 12)); dest[1] = (__mpu_char8_t)(0x80 | ((value >> 6) & 0x3f)); dest[2] = (__mpu_char8_t)(0x80 | (value & 0x3f)); *length = 3; return 0; } if( value <= 0x10ffff ) { dest[0] = (__mpu_char8_t)(0xf0 | (value >> 18)); dest[1] = (__mpu_char8_t)(0x80 | ((value >> 12) & 0x3f)); dest[2] = (__mpu_char8_t)(0x80 | ((value >> 6) & 0x3f)); dest[3] = (__mpu_char8_t)(0x80 | (value & 0x3f)); *length = 4; return 0; } return -1; } const __mpu_char8_t * mpu_utf8next( const __mpu_char8_t *p ) { __mpu_char32_t value; __mpu_size_t len; if( *p == '\0' ) return p; if( __mpu_utf8_decode( (const __mpu_char8_t *)p, &value, &len ) < 0 ) { errno = EILSEQ; return NULL; } return p + len; } const __mpu_char8_t * mpu_utf8prev( const __mpu_char8_t *start, const __mpu_char8_t *p ) { const __mpu_char8_t *q; __mpu_size_t back = 0; __mpu_char32_t value; __mpu_size_t len; if( p <= start ) return start; q = (const __mpu_char8_t *)p - 1; while( q > (const __mpu_char8_t *)start && (*q & 0xc0) == 0x80 && back < 3 ) { --q; ++back; } if( __mpu_utf8_decode( q, &value, &len ) < 0 || q + len != (const __mpu_char8_t *)p ) { errno = EILSEQ; return NULL; } return (const __mpu_char8_t *)q; } const __mpu_char8_t * mpu_utf8last( const __mpu_char8_t *s ) { const __mpu_char8_t *end = s + strlen( s ); if( end == s ) return s; return mpu_utf8prev( s, end ); } const __mpu_char8_t * mpu_utf8get( const __mpu_char8_t *p, __mpu_char32_t *value ) { __mpu_size_t len; if( *p == '\0' ) { *value = 0; return p; } if( __mpu_utf8_decode( (const __mpu_char8_t *)p, value, &len ) < 0 ) { errno = EILSEQ; return NULL; } return p + len; } __mpu_size_t mpu_utf8len( const __mpu_char8_t *s ) { __mpu_size_t count = 0; const __mpu_char8_t *p = s; while( *p != '\0' ) { p = mpu_utf8next( p ); if( p == NULL ) return (__mpu_size_t)-1; ++count; } return count; } __mpu_size_t mpu_utf8nlen( const __mpu_char8_t *s, __mpu_size_t maxlen ) { __mpu_size_t count = 0; const __mpu_char8_t *p = s; while( *p != '\0' && count < maxlen ) { p = mpu_utf8next( p ); if( p == NULL ) return (__mpu_size_t)-1; ++count; } return count; } __mpu_size_t mpu_utf8nbytes( const __mpu_char8_t *s, __mpu_size_t n ) { const __mpu_char8_t *p = s; __mpu_size_t count = 0; while( *p != '\0' && count < n ) { p = mpu_utf8next( p ); if( p == NULL ) return (__mpu_size_t)-1; ++count; } return (__mpu_size_t)(p - s); } __mpu_size_t mpu_utf8bytes( const __mpu_char8_t *s ) { const __mpu_char8_t *p = s; while( *p != '\0' ) { p = mpu_utf8next( p ); if( p == NULL ) return (__mpu_size_t)-1; } return (__mpu_size_t)(p - s); } int mpu_utf8valid( const __mpu_char8_t *s ) { return mpu_utf8bytes( s ) != (__mpu_size_t)-1; } __mpu_size_t mpu_utf8_offset2index( const __mpu_char8_t *s, __mpu_size_t offset ) { const __mpu_char8_t *p = s; while( offset != 0 && *p != '\0' ) { p = mpu_utf8next( p ); if( p == NULL ) return (__mpu_size_t)-1; --offset; } return (__mpu_size_t)(p - s); } __mpu_size_t mpu_utf8_index2offset( const __mpu_char8_t *s, __mpu_size_t index ) { const __mpu_char8_t *p = s; __mpu_size_t offset = 0; while( *p != '\0' && (__mpu_size_t)(p - s) < index ) { p = mpu_utf8next( p ); if( p == NULL ) return (__mpu_size_t)-1; ++offset; } return offset; } int mpu_utf8cmp( const __mpu_char8_t *s1, const __mpu_char8_t *s2 ) { __mpu_char32_t c1 = 0; __mpu_char32_t c2 = 0; do { s1 = mpu_utf8get( s1, &c1 ); s2 = mpu_utf8get( s2, &c2 ); if( s1 == NULL || s2 == NULL ) return 0; if( c1 != c2 ) return (c1 < c2) ? -1 : 1; } while( c1 != 0 ); return 0; } int mpu_utf8ncmp( const __mpu_char8_t *s1, const __mpu_char8_t *s2, __mpu_size_t n ) { __mpu_char32_t c1 = 0; __mpu_char32_t c2 = 0; while( n != 0 ) { s1 = mpu_utf8get( s1, &c1 ); s2 = mpu_utf8get( s2, &c2 ); if( s1 == NULL || s2 == NULL ) return 0; if( c1 != c2 ) return (c1 < c2) ? -1 : 1; if( c1 == 0 ) return 0; --n; } return 0; } __mpu_char8_t * mpu_utf8cpy( __mpu_char8_t *dest, const __mpu_char8_t *src ) { return strcpy( dest, src ); } __mpu_char8_t * mpu_utf8cat( __mpu_char8_t *dest, const __mpu_char8_t *src ) { return strcat( dest, src ); } __mpu_char8_t * mpu_utf8ncpy( __mpu_char8_t *dest, const __mpu_char8_t *src, __mpu_size_t n ) { const __mpu_char8_t *end = src; __mpu_char8_t *ret = dest; while( n != 0 && *end != '\0' ) { end = mpu_utf8next( end ); if( end == NULL ) return NULL; --n; } memcpy( dest, src, (__mpu_size_t)(end - src) ); dest += end - src; *dest = '\0'; return ret; } __mpu_char8_t * mpu_utf8ncat( __mpu_char8_t *dest, const __mpu_char8_t *src, __mpu_size_t n ) { __mpu_char8_t *end = dest + strlen( dest ); if( mpu_utf8ncpy( end, src, n ) == NULL ) return NULL; return dest; } __mpu_char8_t * mpu_utf8pcpy( __mpu_char8_t *dest, const __mpu_char8_t *src ) { strcpy( dest, src ); return dest + strlen( dest ); } __mpu_char8_t * mpu_utf8pncpy( __mpu_char8_t *dest, const __mpu_char8_t *src, __mpu_size_t n ) { const __mpu_char8_t *end = src; __mpu_size_t bytes; while( n != 0 && *end != '\0' ) { end = mpu_utf8next( end ); if( end == NULL ) return NULL; --n; } bytes = (__mpu_size_t)(end - src); memcpy( dest, src, bytes ); dest[bytes] = '\0'; return dest + bytes; } __mpu_char8_t * mpu_utf8chr( const __mpu_char8_t *s, __mpu_char32_t c ) { __mpu_char32_t value; const __mpu_char8_t *p = s; do { const __mpu_char8_t *next = mpu_utf8get( p, &value ); if( next == NULL ) return NULL; if( value == c ) return (__mpu_char8_t *)p; p = next; } while( value != 0 ); return NULL; } __mpu_char8_t * mpu_utf8rchr( const __mpu_char8_t *s, __mpu_char32_t c ) { __mpu_char8_t *last = NULL; const __mpu_char8_t *p = s; __mpu_char32_t value; do { const __mpu_char8_t *next = mpu_utf8get( p, &value ); if( next == NULL ) return NULL; if( value == c ) last = (__mpu_char8_t *)p; p = next; } while( value != 0 ); return last; } __mpu_char8_t * mpu_utf8chrnul( const __mpu_char8_t *s, __mpu_char32_t c ) { __mpu_char8_t *p; errno = 0; p = mpu_utf8chr( s, c ); if( p != NULL ) return p; if( errno == EILSEQ ) return NULL; return (__mpu_char8_t *)(s + strlen( s )); } __mpu_size_t mpu_utf8spn( const __mpu_char8_t *s, const __mpu_char8_t *accept ) { __mpu_size_t count = 0; const __mpu_char8_t *p = s; while( *p != '\0' ) { __mpu_char32_t value; const __mpu_char8_t *next = mpu_utf8get( p, &value ); if( next == NULL ) return (__mpu_size_t)-1; errno = 0; if( mpu_utf8chr( accept, value ) == NULL ) { if( errno == EILSEQ ) return (__mpu_size_t)-1; break; } ++count; p = next; } return count; } __mpu_size_t mpu_utf8cspn( const __mpu_char8_t *s, const __mpu_char8_t *reject ) { __mpu_size_t count = 0; const __mpu_char8_t *p = s; while( *p != '\0' ) { __mpu_char32_t value; const __mpu_char8_t *next = mpu_utf8get( p, &value ); __mpu_char8_t *found; if( next == NULL ) return (__mpu_size_t)-1; errno = 0; found = mpu_utf8chr( reject, value ); if( found != NULL ) break; if( errno == EILSEQ ) return (__mpu_size_t)-1; ++count; p = next; } return count; } __mpu_char8_t * mpu_utf8pbrk( const __mpu_char8_t *s, const __mpu_char8_t *accept ) { const __mpu_char8_t *p = s; while( *p != '\0' ) { __mpu_char32_t value; const __mpu_char8_t *next = mpu_utf8get( p, &value ); __mpu_char8_t *found; if( next == NULL ) return NULL; errno = 0; found = mpu_utf8chr( accept, value ); if( found != NULL ) return (__mpu_char8_t *)p; if( errno == EILSEQ ) return NULL; p = next; } return NULL; } __mpu_char8_t * mpu_utf8str( const __mpu_char8_t *haystack, const __mpu_char8_t *needle ) { return strstr( haystack, needle ); } __mpu_char8_t * mpu_utf8rstr( const __mpu_char8_t *haystack, const __mpu_char8_t *needle ) { return mpu_str8rstr( haystack, needle ); } __mpu_char8_t * mpu_utf8tok( __mpu_char8_t *s, const __mpu_char8_t *delim ) { static MPU_THREAD_LOCAL __mpu_char8_t *saveptr; return mpu_utf8tok_r( s, delim, &saveptr ); } __mpu_char8_t * mpu_utf8tok_r( __mpu_char8_t *s, const __mpu_char8_t *delim, __mpu_char8_t **saveptr ) { __mpu_char8_t *token; __mpu_char8_t *sep; __mpu_size_t skip; if( s == NULL ) s = *saveptr; if( s == NULL ) return NULL; skip = mpu_utf8spn( s, delim ); if( skip == (__mpu_size_t)-1 ) return NULL; while( skip-- != 0 ) { const __mpu_char8_t *next = mpu_utf8next( s ); if( next == NULL ) return NULL; s = (__mpu_char8_t *)next; } if( *s == '\0' ) { *saveptr = s; return NULL; } token = s; errno = 0; sep = mpu_utf8pbrk( token, delim ); if( sep == NULL ) { if( errno == EILSEQ ) return NULL; *saveptr = token + strlen( token ); return token; } { const __mpu_char8_t *next = mpu_utf8next( sep ); if( next == NULL ) return NULL; *sep = '\0'; *saveptr = (__mpu_char8_t *)next; } return token; } __mpu_char8_t * mpu_utf8ndup( const __mpu_char8_t *s, __mpu_size_t n ) { __mpu_size_t bytes; __mpu_char8_t *copy; if( s == NULL ) { errno = EINVAL; return NULL; } bytes = mpu_utf8nbytes( s, n ); if( bytes == (__mpu_size_t)-1 ) return NULL; copy = (__mpu_char8_t *)malloc( bytes + 1 ); if( copy == NULL ) return NULL; if( bytes != 0 ) memcpy( copy, s, bytes ); copy[bytes] = '\0'; return copy; } __mpu_char8_t * mpu_utf8dup( const __mpu_char8_t *s ) { return mpu_str8dup( s ); } __mpu_size_t mpu_utf8_to_ucs2( __mpu_char16_t *dest, const __mpu_char8_t *src, __mpu_size_t nb ) { __mpu_size_t count = 0; const __mpu_char8_t *p = src; while( *p != '\0' ) { __mpu_char32_t value; const __mpu_char8_t *next = mpu_utf8get( p, &value ); if( next == NULL || value > 0xffff || (value >= 0xd800 && value <= 0xdfff) ) { errno = EILSEQ; return (__mpu_size_t)-1; } if( dest != NULL ) { if( nb == 0 ) return count; dest[count] = (__mpu_char16_t)value; --nb; } ++count; p = next; } if( dest != NULL && nb != 0 ) dest[count] = 0; return count; } __mpu_hidden_decl(__mpu_utf8_decode); __mpu_hidden_decl(__mpu_utf8_encode);