allow UTF-8 inside strings
This commit is contained in:
@@ -434,6 +434,122 @@ int xutf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t ut
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SYMBOL
|
||||||
|
** xutf8_sequence_to_utf32_codepoint
|
||||||
|
**
|
||||||
|
** DESCRIPTION
|
||||||
|
** Transform a UTF-8 encoded byte sequence pointed by `utf8_data`
|
||||||
|
** into a UTF-32 encoded codepoint.
|
||||||
|
**
|
||||||
|
** ARGUMENTS
|
||||||
|
** The [utf8_data] pointer refers to the location of the UTF-8 sequence.
|
||||||
|
**
|
||||||
|
** The [nbytes] argument specifies the maximum number of bytes that can
|
||||||
|
** be read after [utf8_data]. It can't be negative.
|
||||||
|
**
|
||||||
|
** NOTE: The [nbytes] argument has no relation to the UTF-8 byte count sequence.
|
||||||
|
** You may think about this argument as the "raw" string length (the one
|
||||||
|
** [strlen] whould return if [utf8_data] were zero-terminated).
|
||||||
|
**
|
||||||
|
** The [utf32_code] argument is the location where the encoded UTF-32 code
|
||||||
|
** will be stored. It may be NULL, in which case the value is evaluated and then
|
||||||
|
** thrown away.
|
||||||
|
**
|
||||||
|
** RETURN
|
||||||
|
** The codepoint is returned through the output parameter `utf32_code`.
|
||||||
|
** The returned value is the number of bytes of the UTF-8 sequence that
|
||||||
|
** were scanned to encode the UTF-32 code, or -1 if the UTF-8 sequence
|
||||||
|
** is invalid.
|
||||||
|
**
|
||||||
|
** NOTE: By calling this function with a NULL [utf32_code], you can check the
|
||||||
|
** validity of a UTF-8 sequence.
|
||||||
|
*/
|
||||||
|
int xutf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code)
|
||||||
|
{
|
||||||
|
assert(utf8_data != NULL);
|
||||||
|
assert(nbytes >= 0);
|
||||||
|
|
||||||
|
uint32_t dummy;
|
||||||
|
if(utf32_code == NULL)
|
||||||
|
utf32_code = &dummy;
|
||||||
|
|
||||||
|
if(nbytes == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(utf8_data[0] & 0x80)
|
||||||
|
{
|
||||||
|
// May be UTF-8.
|
||||||
|
|
||||||
|
if((unsigned char) utf8_data[0] >= 0xF0)
|
||||||
|
{
|
||||||
|
// 4 bytes.
|
||||||
|
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||||
|
|
||||||
|
if(nbytes < 4)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint32_t temp
|
||||||
|
= (((uint32_t) utf8_data[0] & 0x07) << 18)
|
||||||
|
| (((uint32_t) utf8_data[1] & 0x3f) << 12)
|
||||||
|
| (((uint32_t) utf8_data[2] & 0x3f) << 6)
|
||||||
|
| (((uint32_t) utf8_data[3] & 0x3f));
|
||||||
|
|
||||||
|
if(temp > 0x10ffff)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*utf32_code = temp;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((unsigned char) utf8_data[0] >= 0xE0)
|
||||||
|
{
|
||||||
|
// 3 bytes.
|
||||||
|
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||||
|
|
||||||
|
if(nbytes < 3)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint32_t temp
|
||||||
|
= (((uint32_t) utf8_data[0] & 0x0f) << 12)
|
||||||
|
| (((uint32_t) utf8_data[1] & 0x3f) << 6)
|
||||||
|
| (((uint32_t) utf8_data[2] & 0x3f));
|
||||||
|
|
||||||
|
if(temp > 0x10ffff)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*utf32_code = temp;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((unsigned char) utf8_data[0] >= 0xC0)
|
||||||
|
{
|
||||||
|
// 2 bytes.
|
||||||
|
// 110xxxxx 10xxxxxx
|
||||||
|
|
||||||
|
if(nbytes < 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*utf32_code
|
||||||
|
= (((uint32_t) utf8_data[0] & 0x1f) << 6)
|
||||||
|
| (((uint32_t) utf8_data[1] & 0x3f));
|
||||||
|
|
||||||
|
assert(*utf32_code <= 0x10ffff);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 byte
|
||||||
|
// 10xxxxxx
|
||||||
|
*utf32_code = (uint32_t) utf8_data[0] & 0x3f;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's ASCII
|
||||||
|
// 0xxxxxxx
|
||||||
|
|
||||||
|
*utf32_code = (uint32_t) utf8_data[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static _Bool parse_XXXX_after_u(context_t *ctx, uint16_t *res)
|
static _Bool parse_XXXX_after_u(context_t *ctx, uint16_t *res)
|
||||||
{
|
{
|
||||||
const char *bytes = ctx->str + ctx->i;
|
const char *bytes = ctx->str + ctx->i;
|
||||||
@@ -557,13 +673,6 @@ static void *parse_string(context_t *ctx, _Bool raw)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((unsigned char) ctx->str[ctx->i] > 127)
|
|
||||||
{
|
|
||||||
xj_preport(ctx->error, ctx->str, ctx->i, "String contains non-ASCII data");
|
|
||||||
spc_free(&spc);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((unsigned char) ctx->str[ctx->i] < 32)
|
if((unsigned char) ctx->str[ctx->i] < 32)
|
||||||
{
|
{
|
||||||
xj_preport(ctx->error, ctx->str, ctx->i, "String contains control characters");
|
xj_preport(ctx->error, ctx->str, ctx->i, "String contains control characters");
|
||||||
@@ -583,8 +692,8 @@ static void *parse_string(context_t *ctx, _Bool raw)
|
|||||||
if(ctx->str[ctx->i] == '"')
|
if(ctx->str[ctx->i] == '"')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
assert(ctx->str[ctx->i] == '\\');
|
if(ctx->str[ctx->i] == '\\')
|
||||||
|
{
|
||||||
ctx->i += 1; // Skip '\'.
|
ctx->i += 1; // Skip '\'.
|
||||||
|
|
||||||
if(ctx->i == ctx->len)
|
if(ctx->i == ctx->len)
|
||||||
@@ -719,6 +828,30 @@ static void *parse_string(context_t *ctx, _Bool raw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(!isascii(ctx->str[ctx->i]));
|
||||||
|
|
||||||
|
int n = xutf8_sequence_to_utf32_codepoint(ctx->str + ctx->i, ctx->len - ctx->i, NULL);
|
||||||
|
if(n < 0)
|
||||||
|
{
|
||||||
|
xj_preport(ctx->error, ctx->str, ctx->i, "Invalid UTF-8");
|
||||||
|
spc_free(&spc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(n > 0);
|
||||||
|
|
||||||
|
if(!spc_append(&spc, ctx->str + ctx->i, n))
|
||||||
|
{
|
||||||
|
xj_report(ctx->error, "Out of memory");
|
||||||
|
spc_free(&spc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->i += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx->i += 1; // Skip '"'.
|
ctx->i += 1; // Skip '"'.
|
||||||
|
|
||||||
@@ -1198,122 +1331,6 @@ static xj_bool append_string(buffer_t *buff, const char *str, int len)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SYMBOL
|
|
||||||
** xutf8_sequence_to_utf32_codepoint
|
|
||||||
**
|
|
||||||
** DESCRIPTION
|
|
||||||
** Transform a UTF-8 encoded byte sequence pointed by `utf8_data`
|
|
||||||
** into a UTF-32 encoded codepoint.
|
|
||||||
**
|
|
||||||
** ARGUMENTS
|
|
||||||
** The [utf8_data] pointer refers to the location of the UTF-8 sequence.
|
|
||||||
**
|
|
||||||
** The [nbytes] argument specifies the maximum number of bytes that can
|
|
||||||
** be read after [utf8_data]. It can't be negative.
|
|
||||||
**
|
|
||||||
** NOTE: The [nbytes] argument has no relation to the UTF-8 byte count sequence.
|
|
||||||
** You may think about this argument as the "raw" string length (the one
|
|
||||||
** [strlen] whould return if [utf8_data] were zero-terminated).
|
|
||||||
**
|
|
||||||
** The [utf32_code] argument is the location where the encoded UTF-32 code
|
|
||||||
** will be stored. It may be NULL, in which case the value is evaluated and then
|
|
||||||
** thrown away.
|
|
||||||
**
|
|
||||||
** RETURN
|
|
||||||
** The codepoint is returned through the output parameter `utf32_code`.
|
|
||||||
** The returned value is the number of bytes of the UTF-8 sequence that
|
|
||||||
** were scanned to encode the UTF-32 code, or -1 if the UTF-8 sequence
|
|
||||||
** is invalid.
|
|
||||||
**
|
|
||||||
** NOTE: By calling this function with a NULL [utf32_code], you can check the
|
|
||||||
** validity of a UTF-8 sequence.
|
|
||||||
*/
|
|
||||||
int xutf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code)
|
|
||||||
{
|
|
||||||
assert(utf8_data != NULL);
|
|
||||||
assert(nbytes >= 0);
|
|
||||||
|
|
||||||
uint32_t dummy;
|
|
||||||
if(utf32_code == NULL)
|
|
||||||
utf32_code = &dummy;
|
|
||||||
|
|
||||||
if(nbytes == 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if(utf8_data[0] & 0x80)
|
|
||||||
{
|
|
||||||
// May be UTF-8.
|
|
||||||
|
|
||||||
if((unsigned char) utf8_data[0] >= 0xF0)
|
|
||||||
{
|
|
||||||
// 4 bytes.
|
|
||||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
|
||||||
|
|
||||||
if(nbytes < 4)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
uint32_t temp
|
|
||||||
= (((uint32_t) utf8_data[0] & 0x07) << 18)
|
|
||||||
| (((uint32_t) utf8_data[1] & 0x3f) << 12)
|
|
||||||
| (((uint32_t) utf8_data[2] & 0x3f) << 6)
|
|
||||||
| (((uint32_t) utf8_data[3] & 0x3f));
|
|
||||||
|
|
||||||
if(temp > 0x10ffff)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
*utf32_code = temp;
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((unsigned char) utf8_data[0] >= 0xE0)
|
|
||||||
{
|
|
||||||
// 3 bytes.
|
|
||||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
|
||||||
|
|
||||||
if(nbytes < 3)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
uint32_t temp
|
|
||||||
= (((uint32_t) utf8_data[0] & 0x0f) << 12)
|
|
||||||
| (((uint32_t) utf8_data[1] & 0x3f) << 6)
|
|
||||||
| (((uint32_t) utf8_data[2] & 0x3f));
|
|
||||||
|
|
||||||
if(temp > 0x10ffff)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
*utf32_code = temp;
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((unsigned char) utf8_data[0] >= 0xC0)
|
|
||||||
{
|
|
||||||
// 2 bytes.
|
|
||||||
// 110xxxxx 10xxxxxx
|
|
||||||
|
|
||||||
if(nbytes < 2)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
*utf32_code
|
|
||||||
= (((uint32_t) utf8_data[0] & 0x1f) << 6)
|
|
||||||
| (((uint32_t) utf8_data[1] & 0x3f));
|
|
||||||
|
|
||||||
assert(*utf32_code <= 0x10ffff);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 byte
|
|
||||||
// 10xxxxxx
|
|
||||||
*utf32_code = (uint32_t) utf8_data[0] & 0x3f;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's ASCII
|
|
||||||
// 0xxxxxxx
|
|
||||||
|
|
||||||
*utf32_code = (uint32_t) utf8_data[0];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static _Bool encode_string(const char *str, int len, buffer_t *buff)
|
static _Bool encode_string(const char *str, int len, buffer_t *buff)
|
||||||
{
|
{
|
||||||
if(!append_string(buff, "\"", 1))
|
if(!append_string(buff, "\"", 1))
|
||||||
|
|||||||
Reference in New Issue
Block a user