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 | cstr->size = size; | 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 | ST_FUNC void cstr_cat(CString *cstr, const char *str, int len) | 373 | ST_FUNC void cstr_cat(CString *cstr, const char *str, int len) |
| 349 | { | 374 | { |
| 350 | int size; | 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 | c = n; | 2126 | c = n; |
| 2102 | goto add_char_nonext; | 2127 | goto add_char_nonext; |
| 2103 | case 'x': | 2128 | case 'x': { unsigned ucn_chars_nr = -1u; goto parse_hex_or_ucn; |
| 2104 | case 'u': | 2129 | case 'u': ucn_chars_nr = 4; goto parse_hex_or_ucn; |
| 2105 | case 'U': | 2130 | case 'U': ucn_chars_nr = 8; goto parse_hex_or_ucn; |
| 2131 | parse_hex_or_ucn:; | ||
| 2106 | p++; | 2132 | p++; |
| 2107 | n = 0; | 2133 | n = 0; |
| 2108 | for(;;) { | 2134 | for(unsigned i=1;i<=ucn_chars_nr;i++) { |
| 2109 | c = *p; | 2135 | c = *p; |
| 2110 | if (c >= 'a' && c <= 'f') | 2136 | if (c >= 'a' && c <= 'f') |
| 2111 | c = c - 'a' + 10; | 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 | c = c - 'A' + 10; | 2139 | c = c - 'A' + 10; |
| 2114 | else if (isnum(c)) | 2140 | else if (isnum(c)) |
| 2115 | c = c - '0'; | 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 | break; | 2145 | break; |
| 2146 | } | ||
| 2118 | n = n * 16 + c; | 2147 | n = n * 16 + c; |
| 2119 | p++; | 2148 | p++; |
| 2120 | } | 2149 | } |
| 2121 | c = n; | 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 | case 'a': | 2155 | case 'a': |
| 2124 | c = '\a'; | 2156 | c = '\a'; |
| 2125 | break; | 2157 | break; |
| @@ -1,12 +1,19 @@ | |||
| 1 | // this file contains BMP chars encoded in UTF-8 | 1 | // this file contains BMP chars encoded in UTF-8 |
| 2 | #include <stdio.h> | 2 | #include <stdio.h> |
| 3 | #include <wchar.h> | 3 | #include <wchar.h> |
| 4 | #include <stdlib.h> | ||
| 5 | #include <string.h> | ||
| 4 | 6 | ||
| 5 | int main() | 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 | wchar_t s[] = L"hello$$你好¢¢世界€€world"; | 15 | wchar_t s[] = L"hello$$你好¢¢世界€€world"; |
| 8 | wchar_t *p; | 16 | wchar_t *p; |
| 9 | for (p = s; *p; p++) printf("%04X ", (unsigned) *p); | 17 | for (p = s; *p; p++) printf("%04X ", (unsigned) *p); |
| 10 | printf("\n"); | ||
| 11 | return 0; | 18 | return 0; |
| 12 | } | 19 | } |