As the standard requires, take 4 hex digits after the \u opener of a Universal Character Name, or take 8 hex digits after \U, but reject smaller counts and don't consume more (https://port70.net/~nsz/c/c11/n1570.html#6.4.3, https://port70.net/~nsz/c/c99/n1256.html#6.4.3). The unicode codepoint used to get truncated to 1 byte. Now it gets expanded into UTF-8, matching gcc & clang behavior on Linux.
Unified · Split
| @@ -345,6 +345,31 @@ ST_INLN void cstr_ccat(CString *cstr, int ch) · 344 unmodified lines | ||
| 345 | 345 | cstr->size = size; |
| 346 | 346 | } |
| 347 | 347 | |
| 348 | +char *unicode_to_utf8 (char *b, uint32_t Uc) | |
| 349 | +{ | |
| 350 | + if (Uc<0x80) *b++=Uc; | |
| 351 | + else if (Uc<0x800) *b++=192+Uc/64, *b++=128+Uc%64; | |
| 352 | + else if (Uc-0xd800u<0x800) return b; | |
| 353 | + else if (Uc<0x10000) *b++=224+Uc/4096, *b++=128+Uc/64%64, *b++=128+Uc%64; | |
| 354 | + else if (Uc<0x110000) *b++=240+Uc/262144, *b++=128+Uc/4096%64, *b++=128+Uc/64%64, *b++=128+Uc%64; | |
| 355 | + return b; | |
| 356 | +} | |
| 357 | + | |
| 358 | +/* add a unicode character expanded into utf8 */ | |
| 359 | +void cstr_u8cat(CString *cstr, int ch) | |
| 360 | +{ | |
| 361 | + unsigned char buf[4]; | |
| 362 | + int size; | |
| 363 | + int add = (int)((unsigned char*)unicode_to_utf8(&buf[0],(uint32_t)ch) - &buf[0]); | |
| 364 | + unsigned char *p,*b=buf; | |
| 365 | + size = cstr->size + add; | |
| 366 | + if (size > cstr->size_allocated) | |
| 367 | + cstr_realloc(cstr, size); | |
| 368 | + for(p = (unsigned char*)cstr->data + (size - add); add; add--) *p++=*b++; | |
| 369 | + cstr->size = size; | |
| 370 | +} | |
| 371 | + | |
| 372 | + | |
| 348 | 373 | ST_FUNC void cstr_cat(CString *cstr, const char *str, int len) |
| 349 | 374 | { |
| 350 | 375 | int size; |
| @@ -2100,12 +2125,13 @@ static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long · 1749 unmodified lines | ||
| 2100 | 2125 | } |
| 2101 | 2126 | c = n; |
| 2102 | 2127 | goto add_char_nonext; |
| 2103 | − case 'x': | |
| 2104 | − case 'u': | |
| 2105 | − case 'U': | |
| 2128 | + case 'x': { unsigned ucn_chars_nr = -1u; goto parse_hex_or_ucn; | |
| 2129 | + case 'u': ucn_chars_nr = 4; goto parse_hex_or_ucn; | |
| 2130 | + case 'U': ucn_chars_nr = 8; goto parse_hex_or_ucn; | |
| 2131 | + parse_hex_or_ucn:; | |
| 2106 | 2132 | p++; |
| 2107 | 2133 | n = 0; |
| 2108 | − for(;;) { | |
| 2134 | + for(unsigned i=1;i<=ucn_chars_nr;i++) { | |
| 2109 | 2135 | c = *p; |
| 2110 | 2136 | if (c >= 'a' && c <= 'f') |
| 2111 | 2137 | c = c - 'a' + 10; |
| @@ -2113,13 +2139,19 @@ static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long · 1 unmodified line | ||
| 2113 | 2139 | c = c - 'A' + 10; |
| 2114 | 2140 | else if (isnum(c)) |
| 2115 | 2141 | c = c - '0'; |
| 2116 | − else | |
| 2142 | + else{ | |
| 2143 | + if (ucn_chars_nr!=-1) | |
| 2144 | + tcc_error("%u hex digits expected in universal-character-name\n", ucn_chars_nr); | |
| 2117 | 2145 | break; |
| 2146 | + } | |
| 2118 | 2147 | n = n * 16 + c; |
| 2119 | 2148 | p++; |
| 2120 | 2149 | } |
| 2121 | 2150 | c = n; |
| 2122 | − goto add_char_nonext; | |
| 2151 | + if(ucn_chars_nr==-1) goto add_char_nonext; | |
| 2152 | + cstr_u8cat(outstr, c); | |
| 2153 | + continue; | |
| 2154 | + } | |
| 2123 | 2155 | case 'a': |
| 2124 | 2156 | c = '\a'; |
| 2125 | 2157 | break; |
| @@ -1,12 +1,19 @@ | ||
| 1 | 1 | // this file contains BMP chars encoded in UTF-8 |
| 2 | 2 | #include <stdio.h> |
| 3 | 3 | #include <wchar.h> |
| 4 | +#include <stdlib.h> | |
| 5 | +#include <string.h> | |
| 4 | 6 | |
| 5 | 7 | int main() |
| 6 | 8 | { |
| 9 | + char hello_world_in_czech[] = "čau, světe"; | |
| 10 | + char hello_world_in_czech_ucn[] = "\u010dau, sv\u011bte"; | |
| 11 | + if (sizeof(hello_world_in_czech) != sizeof(hello_world_in_czech_ucn) | |
| 12 | + || strcmp(hello_world_in_czech, hello_world_in_czech_ucn)) | |
| 13 | + abort(); | |
| 14 | + | |
| 7 | 15 | wchar_t s[] = L"hello$$你好¢¢世界€€world"; |
| 8 | 16 | wchar_t *p; |
| 9 | 17 | for (p = s; *p; p++) printf("%04X ", (unsigned) *p); |
| 10 | − printf("\n"); | |
| 11 | 18 | return 0; |
| 12 | 19 | } |