#include static unsigned char str_byte_next(const char **ptr) { unsigned char byte_value = **ptr; (*ptr)++; return byte_value; } static void str_byte_rewind(const char **ptr) { (*ptr)--; } int str_length(const char *str) { return (int)strlen(str); } int str_utf8_decode(const char **ptr) { // As per https://encoding.spec.whatwg.org/#utf-8-decoder. unsigned char utf8_lower_boundary = 0x80; unsigned char utf8_upper_boundary = 0xBF; int utf8_code_point = 0; int utf8_bytes_seen = 0; int utf8_bytes_needed = 0; while(true) { unsigned char byte_value = str_byte_next(ptr); if(utf8_bytes_needed == 0) { if(byte_value <= 0x7F) { return byte_value; } else if(0xC2 <= byte_value && byte_value <= 0xDF) { utf8_bytes_needed = 1; utf8_code_point = byte_value - 0xC0; } else if(0xE0 <= byte_value && byte_value <= 0xEF) { if(byte_value == 0xE0) utf8_lower_boundary = 0xA0; if(byte_value == 0xED) utf8_upper_boundary = 0x9F; utf8_bytes_needed = 2; utf8_code_point = byte_value - 0xE0; } else if(0xF0 <= byte_value && byte_value <= 0xF4) { if(byte_value == 0xF0) utf8_lower_boundary = 0x90; if(byte_value == 0xF4) utf8_upper_boundary = 0x8F; utf8_bytes_needed = 3; utf8_code_point = byte_value - 0xF0; } else { return -1; // Error. } utf8_code_point = utf8_code_point << (6 * utf8_bytes_needed); continue; } if(!(utf8_lower_boundary <= byte_value && byte_value <= utf8_upper_boundary)) { // Resetting variables not necessary, will be done when // the function is called again. str_byte_rewind(ptr); return -1; } utf8_lower_boundary = 0x80; utf8_upper_boundary = 0xBF; utf8_bytes_seen += 1; utf8_code_point = utf8_code_point + ((byte_value - 0x80) << (6 * (utf8_bytes_needed - utf8_bytes_seen))); if(utf8_bytes_seen != utf8_bytes_needed) { continue; } // Resetting variables not necessary, see above. return utf8_code_point; } } int str_utf8_isstart(char c) { if((c & 0xC0) == 0x80) /* 10xxxxxx */ return 0; return 1; } int str_utf8_rewind(const char *str, int cursor) { while(cursor) { cursor--; if(str_utf8_isstart(*(str + cursor))) break; } return cursor; } int str_utf8_fix_truncation(char *str) { int len = str_length(str); if(len > 0) { int last_char_index = str_utf8_rewind(str, len); const char *last_char = str + last_char_index; // Fix truncated UTF-8. if(str_utf8_decode(&last_char) == -1) { str[last_char_index] = 0; return last_char_index; } } return len; } int str_copy(char *dst, const char *src, int dst_size) { dst[0] = '\0'; strncat(dst, src, dst_size - 1); return str_utf8_fix_truncation(dst); } template void str_copy(char (&dst)[N], const char *src) { str_copy(dst, src, N); } int main() { enum { MAX_LINES = 25, MAX_LINE_LENGTH = 256 }; char m_aaLastChatMessages[MAX_LINES][MAX_LINE_LENGTH]; for(int i = MAX_LINES; i > 0; i--) { str_copy(m_aaLastChatMessages[i], m_aaLastChatMessages[i - 1]); } }