#include <sys/types.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#else
#include <sys/u8_textprep.h>
#include <strings.h>
#endif
#include <sys/byteorder.h>
#include <sys/errno.h>
#include <sys/u8_textprep_data.h>
#include <sys/sysmacros.h>
#define U8_MB_CUR_MAX (4)
#define U8_MAX_BYTES_UCS2 (3)
#define U8_STREAM_SAFE_TEXT_MAX (128)
#define U8_MAX_CHARS_A_SEQ (32)
#define U8_UPPER_LIMIT_IN_A_SEQ (31)
#define U8_COMBINING_CLASS_STARTER (0)
#define U8_HANGUL_SYL_FIRST (0xAC00U)
#define U8_HANGUL_SYL_LAST (0xD7A3U)
#define U8_HANGUL_JAMO_L_FIRST (0x1100U)
#define U8_HANGUL_JAMO_L_LAST (0x1112U)
#define U8_HANGUL_JAMO_V_FIRST (0x1161U)
#define U8_HANGUL_JAMO_V_LAST (0x1175U)
#define U8_HANGUL_JAMO_T_FIRST (0x11A7U)
#define U8_HANGUL_JAMO_T_LAST (0x11C2U)
#define U8_HANGUL_V_COUNT (21)
#define U8_HANGUL_VT_COUNT (588)
#define U8_HANGUL_T_COUNT (28)
#define U8_HANGUL_JAMO_1ST_BYTE (0xE1U)
#define U8_SAVE_HANGUL_AS_UTF8(s, i, j, k, b) \
(s)[(i)] = (uchar_t)(0xE0U | ((uint32_t)(b) & 0xF000U) >> 12); \
(s)[(j)] = (uchar_t)(0x80U | ((uint32_t)(b) & 0x0FC0U) >> 6); \
(s)[(k)] = (uchar_t)(0x80U | ((uint32_t)(b) & 0x003FU));
#define U8_HANGUL_JAMO_L(u) \
((u) >= U8_HANGUL_JAMO_L_FIRST && (u) <= U8_HANGUL_JAMO_L_LAST)
#define U8_HANGUL_JAMO_V(u) \
((u) >= U8_HANGUL_JAMO_V_FIRST && (u) <= U8_HANGUL_JAMO_V_LAST)
#define U8_HANGUL_JAMO_T(u) \
((u) > U8_HANGUL_JAMO_T_FIRST && (u) <= U8_HANGUL_JAMO_T_LAST)
#define U8_HANGUL_JAMO(u) \
((u) >= U8_HANGUL_JAMO_L_FIRST && (u) <= U8_HANGUL_JAMO_T_LAST)
#define U8_HANGUL_SYLLABLE(u) \
((u) >= U8_HANGUL_SYL_FIRST && (u) <= U8_HANGUL_SYL_LAST)
#define U8_HANGUL_COMPOSABLE_L_V(s, u) \
((s) == U8_STATE_HANGUL_L && U8_HANGUL_JAMO_V((u)))
#define U8_HANGUL_COMPOSABLE_LV_T(s, u) \
((s) == U8_STATE_HANGUL_LV && U8_HANGUL_JAMO_T((u)))
#define U8_DECOMP_BOTH (0xF5U)
#define U8_DECOMP_CANONICAL (0xF6U)
#define U8_16BIT_TABLE_INDICATOR (0x8000U)
#define U8_PUT_3BYTES_INTO_UTF32(u, b1, b2, b3) \
(u) = ((uint32_t)(b1) & 0x0F) << 12 | ((uint32_t)(b2) & 0x3F) << 6 | \
(uint32_t)(b3) & 0x3F;
#define U8_SIMPLE_SWAP(a, b, t) \
(t) = (a); \
(a) = (b); \
(b) = (t);
#define U8_ASCII_TOUPPER(c) \
(((c) >= 'a' && (c) <= 'z') ? (c) - 'a' + 'A' : (c))
#define U8_ASCII_TOLOWER(c) \
(((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
#define U8_ISASCII(c) (((uchar_t)(c)) < 0x80U)
#define U8_SWAP_COMB_MARKS(a, b) \
for (k = 0; k < disp[(a)]; k++) \
u8t[k] = u8s[start[(a)] + k]; \
for (k = 0; k < disp[(b)]; k++) \
u8s[start[(a)] + k] = u8s[start[(b)] + k]; \
start[(b)] = start[(a)] + disp[(b)]; \
for (k = 0; k < disp[(a)]; k++) \
u8s[start[(b)] + k] = u8t[k]; \
U8_SIMPLE_SWAP(comb_class[(a)], comb_class[(b)], tc); \
U8_SIMPLE_SWAP(disp[(a)], disp[(b)], tc);
typedef enum {
U8_STATE_START = 0,
U8_STATE_HANGUL_L = 1,
U8_STATE_HANGUL_LV = 2,
U8_STATE_HANGUL_LVT = 3,
U8_STATE_HANGUL_V = 4,
U8_STATE_HANGUL_T = 5,
U8_STATE_COMBINING_MARK = 6
} u8_normalization_states_t;
#define U8_ILLEGAL_NEXT_BYTE_COMMON(c) ((c) < 0x80 || (c) > 0xBF)
#define I_ U8_ILLEGAL_CHAR
#define O_ U8_OUT_OF_RANGE_CHAR
const int8_t u8_number_of_bytes[0x100] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
I_, I_, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, O_, O_, O_, O_, O_, O_, O_, O_, O_, O_, O_,
};
#undef I_
#undef O_
const uint8_t u8_valid_min_2nd_byte[0x100] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0xa0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x90, 0x80, 0x80, 0x80, 0x80, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
const uint8_t u8_valid_max_2nd_byte[0x100] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x9f, 0xbf, 0xbf,
0xbf, 0xbf, 0xbf, 0xbf, 0x8f, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
int
u8_validate(char *u8str, size_t n, char **list, int flag, int *errnum)
{
uchar_t *ib;
uchar_t *ibtail;
uchar_t **p;
uchar_t *s1;
uchar_t *s2;
uchar_t f;
int sz;
size_t i;
int ret_val;
boolean_t second;
boolean_t no_need_to_validate_entire;
boolean_t check_additional;
boolean_t validate_ucs2_range_only;
if (! u8str)
return (0);
ib = (uchar_t *)u8str;
ibtail = ib + n;
ret_val = 0;
no_need_to_validate_entire = ! (flag & U8_VALIDATE_ENTIRE);
check_additional = flag & U8_VALIDATE_CHECK_ADDITIONAL;
validate_ucs2_range_only = flag & U8_VALIDATE_UCS2_RANGE;
while (ib < ibtail) {
sz = u8_number_of_bytes[*ib];
if (sz == U8_ILLEGAL_CHAR) {
*errnum = EILSEQ;
return (-1);
}
if (sz == U8_OUT_OF_RANGE_CHAR ||
(validate_ucs2_range_only && sz > U8_MAX_BYTES_UCS2)) {
*errnum = ERANGE;
return (-1);
}
if ((ibtail - ib) < sz) {
*errnum = EINVAL;
return (-1);
}
if (sz == 1) {
ib++;
ret_val++;
} else {
f = *ib++;
ret_val++;
second = B_TRUE;
for (i = 1; i < sz; i++) {
if (second) {
if (*ib < u8_valid_min_2nd_byte[f] ||
*ib > u8_valid_max_2nd_byte[f]) {
*errnum = EILSEQ;
return (-1);
}
second = B_FALSE;
} else if (U8_ILLEGAL_NEXT_BYTE_COMMON(*ib)) {
*errnum = EILSEQ;
return (-1);
}
ib++;
ret_val++;
}
}
if (check_additional) {
for (p = (uchar_t **)list, i = 0; p[i]; i++) {
s1 = ib - sz;
s2 = p[i];
while (s1 < ib) {
if (*s1 != *s2 || *s2 == '\0')
break;
s1++;
s2++;
}
if (s1 >= ib && *s2 == '\0') {
*errnum = EBADF;
return (-1);
}
}
}
if (no_need_to_validate_entire)
break;
}
return (ret_val);
}
static size_t
do_case_conv(int uv, uchar_t *u8s, uchar_t *s, int sz, boolean_t is_it_toupper)
{
size_t i;
uint16_t b1 = 0;
uint16_t b2 = 0;
uint16_t b3 = 0;
uint16_t b3_tbl;
uint16_t b3_base;
uint16_t b4 = 0;
size_t start_id;
size_t end_id;
if (sz == 2) {
b3 = u8s[0] = s[0];
b4 = u8s[1] = s[1];
} else if (sz == 3) {
b2 = u8s[0] = s[0];
b3 = u8s[1] = s[1];
b4 = u8s[2] = s[2];
} else if (sz == 4) {
b1 = u8s[0] = s[0];
b2 = u8s[1] = s[1];
b3 = u8s[2] = s[2];
b4 = u8s[3] = s[3];
} else {
if (is_it_toupper)
*u8s = U8_ASCII_TOUPPER(*s);
else
*u8s = U8_ASCII_TOLOWER(*s);
u8s[1] = '\0';
return (1);
}
u8s[sz] = '\0';
b1 = u8_common_b1_tbl[uv][b1];
if (b1 == U8_TBL_ELEMENT_NOT_DEF)
return ((size_t)sz);
b2 = u8_case_common_b2_tbl[uv][b1][b2];
if (b2 == U8_TBL_ELEMENT_NOT_DEF)
return ((size_t)sz);
if (is_it_toupper) {
b3_tbl = u8_toupper_b3_tbl[uv][b2][b3].tbl_id;
if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
return ((size_t)sz);
start_id = u8_toupper_b4_tbl[uv][b3_tbl][b4];
end_id = u8_toupper_b4_tbl[uv][b3_tbl][b4 + 1];
if (start_id >= end_id || (end_id - start_id) > U8_MB_CUR_MAX)
return ((size_t)sz);
b3_base = u8_toupper_b3_tbl[uv][b2][b3].base;
for (i = 0; start_id < end_id; start_id++)
u8s[i++] = u8_toupper_final_tbl[uv][b3_base + start_id];
} else {
b3_tbl = u8_tolower_b3_tbl[uv][b2][b3].tbl_id;
if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
return ((size_t)sz);
start_id = u8_tolower_b4_tbl[uv][b3_tbl][b4];
end_id = u8_tolower_b4_tbl[uv][b3_tbl][b4 + 1];
if (start_id >= end_id || (end_id - start_id) > U8_MB_CUR_MAX)
return ((size_t)sz);
b3_base = u8_tolower_b3_tbl[uv][b2][b3].base;
for (i = 0; start_id < end_id; start_id++)
u8s[i++] = u8_tolower_final_tbl[uv][b3_base + start_id];
}
if (i == 0)
return ((size_t)sz);
u8s[i] = '\0';
return (i);
}
static int
do_case_compare(size_t uv, uchar_t *s1, uchar_t *s2, size_t n1,
size_t n2, boolean_t is_it_toupper, int *errnum)
{
int f;
int sz1;
int sz2;
size_t j;
size_t i1;
size_t i2;
uchar_t u8s1[U8_MB_CUR_MAX + 1];
uchar_t u8s2[U8_MB_CUR_MAX + 1];
i1 = i2 = 0;
while (i1 < n1 && i2 < n2) {
sz1 = u8_number_of_bytes[*s1];
if (sz1 < 0) {
*errnum = EILSEQ;
sz1 = 1;
}
if (sz1 == 1) {
if (is_it_toupper)
u8s1[0] = U8_ASCII_TOUPPER(*s1);
else
u8s1[0] = U8_ASCII_TOLOWER(*s1);
s1++;
u8s1[1] = '\0';
} else if ((i1 + sz1) > n1) {
*errnum = EINVAL;
for (j = 0; (i1 + j) < n1; )
u8s1[j++] = *s1++;
u8s1[j] = '\0';
} else {
(void) do_case_conv(uv, u8s1, s1, sz1, is_it_toupper);
s1 += sz1;
}
sz2 = u8_number_of_bytes[*s2];
if (sz2 < 0) {
*errnum = EILSEQ;
sz2 = 1;
}
if (sz2 == 1) {
if (is_it_toupper)
u8s2[0] = U8_ASCII_TOUPPER(*s2);
else
u8s2[0] = U8_ASCII_TOLOWER(*s2);
s2++;
u8s2[1] = '\0';
} else if ((i2 + sz2) > n2) {
*errnum = EINVAL;
for (j = 0; (i2 + j) < n2; )
u8s2[j++] = *s2++;
u8s2[j] = '\0';
} else {
(void) do_case_conv(uv, u8s2, s2, sz2, is_it_toupper);
s2 += sz2;
}
if (sz1 == 1 && sz2 == 1) {
if (*u8s1 > *u8s2)
return (1);
if (*u8s1 < *u8s2)
return (-1);
} else {
f = strcmp((const char *)u8s1, (const char *)u8s2);
if (f != 0)
return (f);
}
i1 += sz1;
i2 += sz2;
}
if (i1 >= n1) {
if (i2 >= n2)
return (0);
return (-1);
}
return (1);
}
static uchar_t
combining_class(size_t uv, uchar_t *s, size_t sz)
{
uint16_t b1 = 0;
uint16_t b2 = 0;
uint16_t b3 = 0;
uint16_t b4 = 0;
if (sz == 1 || sz > 4)
return (0);
if (sz == 2) {
b3 = s[0];
b4 = s[1];
} else if (sz == 3) {
b2 = s[0];
b3 = s[1];
b4 = s[2];
} else if (sz == 4) {
b1 = s[0];
b2 = s[1];
b3 = s[2];
b4 = s[3];
}
b1 = u8_common_b1_tbl[uv][b1];
if (b1 == U8_TBL_ELEMENT_NOT_DEF)
return (0);
b2 = u8_combining_class_b2_tbl[uv][b1][b2];
if (b2 == U8_TBL_ELEMENT_NOT_DEF)
return (0);
b3 = u8_combining_class_b3_tbl[uv][b2][b3];
if (b3 == U8_TBL_ELEMENT_NOT_DEF)
return (0);
return (u8_combining_class_b4_tbl[uv][b3][b4]);
}
static size_t
do_decomp(size_t uv, uchar_t *u8s, uchar_t *s, int sz,
boolean_t canonical_decomposition, u8_normalization_states_t *state)
{
uint16_t b1 = 0;
uint16_t b2 = 0;
uint16_t b3 = 0;
uint16_t b3_tbl;
uint16_t b3_base;
uint16_t b4 = 0;
size_t start_id;
size_t end_id;
size_t i;
uint32_t u1;
if (sz == 2) {
b3 = u8s[0] = s[0];
b4 = u8s[1] = s[1];
u8s[2] = '\0';
} else if (sz == 3) {
U8_PUT_3BYTES_INTO_UTF32(u1, s[0], s[1], s[2]);
if (U8_HANGUL_SYLLABLE(u1)) {
u1 -= U8_HANGUL_SYL_FIRST;
b1 = U8_HANGUL_JAMO_L_FIRST + u1 / U8_HANGUL_VT_COUNT;
b2 = U8_HANGUL_JAMO_V_FIRST + (u1 % U8_HANGUL_VT_COUNT)
/ U8_HANGUL_T_COUNT;
b3 = u1 % U8_HANGUL_T_COUNT;
U8_SAVE_HANGUL_AS_UTF8(u8s, 0, 1, 2, b1);
U8_SAVE_HANGUL_AS_UTF8(u8s, 3, 4, 5, b2);
if (b3) {
b3 += U8_HANGUL_JAMO_T_FIRST;
U8_SAVE_HANGUL_AS_UTF8(u8s, 6, 7, 8, b3);
u8s[9] = '\0';
*state = U8_STATE_HANGUL_LVT;
return (9);
}
u8s[6] = '\0';
*state = U8_STATE_HANGUL_LV;
return (6);
}
b2 = u8s[0] = s[0];
b3 = u8s[1] = s[1];
b4 = u8s[2] = s[2];
u8s[3] = '\0';
if (U8_HANGUL_JAMO_L(u1)) {
*state = U8_STATE_HANGUL_L;
return (3);
}
if (U8_HANGUL_JAMO_V(u1)) {
if (*state == U8_STATE_HANGUL_L)
*state = U8_STATE_HANGUL_LV;
else
*state = U8_STATE_HANGUL_V;
return (3);
}
if (U8_HANGUL_JAMO_T(u1)) {
if (*state == U8_STATE_HANGUL_LV)
*state = U8_STATE_HANGUL_LVT;
else
*state = U8_STATE_HANGUL_T;
return (3);
}
} else if (sz == 4) {
b1 = u8s[0] = s[0];
b2 = u8s[1] = s[1];
b3 = u8s[2] = s[2];
b4 = u8s[3] = s[3];
u8s[4] = '\0';
} else {
u8s[0] = s[0];
u8s[1] = '\0';
*state = U8_STATE_START;
return (1);
}
*state = U8_STATE_START;
b1 = u8_common_b1_tbl[uv][b1];
if (b1 == U8_TBL_ELEMENT_NOT_DEF)
return ((size_t)sz);
b2 = u8_decomp_b2_tbl[uv][b1][b2];
if (b2 == U8_TBL_ELEMENT_NOT_DEF)
return ((size_t)sz);
b3_tbl = u8_decomp_b3_tbl[uv][b2][b3].tbl_id;
if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
return ((size_t)sz);
if (b3_tbl >= U8_16BIT_TABLE_INDICATOR) {
b3_tbl -= U8_16BIT_TABLE_INDICATOR;
start_id = u8_decomp_b4_16bit_tbl[uv][b3_tbl][b4];
end_id = u8_decomp_b4_16bit_tbl[uv][b3_tbl][b4 + 1];
} else {
if (b3_tbl >= ARRAY_SIZE(u8_decomp_b4_tbl[uv]))
return ((size_t)sz);
start_id = u8_decomp_b4_tbl[uv][b3_tbl][b4];
end_id = u8_decomp_b4_tbl[uv][b3_tbl][b4 + 1];
}
if (start_id >= end_id)
return ((size_t)sz);
b3_base = u8_decomp_b3_tbl[uv][b2][b3].base;
b1 = u8_decomp_final_tbl[uv][b3_base + start_id];
if (canonical_decomposition) {
if (b1 < U8_DECOMP_BOTH)
return ((size_t)sz);
start_id++;
if (b1 == U8_DECOMP_BOTH) {
end_id = start_id +
u8_decomp_final_tbl[uv][b3_base + start_id];
start_id++;
}
} else {
if (b1 == U8_DECOMP_BOTH) {
start_id++;
start_id += u8_decomp_final_tbl[uv][b3_base + start_id];
} else if (b1 == U8_DECOMP_CANONICAL) {
start_id++;
}
}
for (i = 0; start_id < end_id; start_id++)
u8s[i++] = u8_decomp_final_tbl[uv][b3_base + start_id];
u8s[i] = '\0';
return (i);
}
static uchar_t *
find_composition_start(size_t uv, uchar_t *s, size_t sz)
{
uint16_t b1 = 0;
uint16_t b2 = 0;
uint16_t b3 = 0;
uint16_t b3_tbl;
uint16_t b3_base;
uint16_t b4 = 0;
size_t start_id;
size_t end_id;
if (sz == 1) {
b4 = s[0];
} else if (sz == 2) {
b3 = s[0];
b4 = s[1];
} else if (sz == 3) {
b2 = s[0];
b3 = s[1];
b4 = s[2];
} else if (sz == 4) {
b1 = s[0];
b2 = s[1];
b3 = s[2];
b4 = s[3];
} else {
return (NULL);
}
b1 = u8_composition_b1_tbl[uv][b1];
if (b1 == U8_TBL_ELEMENT_NOT_DEF)
return (NULL);
b2 = u8_composition_b2_tbl[uv][b1][b2];
if (b2 == U8_TBL_ELEMENT_NOT_DEF)
return (NULL);
b3_tbl = u8_composition_b3_tbl[uv][b2][b3].tbl_id;
if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
return (NULL);
if (b3_tbl >= U8_16BIT_TABLE_INDICATOR) {
b3_tbl -= U8_16BIT_TABLE_INDICATOR;
start_id = u8_composition_b4_16bit_tbl[uv][b3_tbl][b4];
end_id = u8_composition_b4_16bit_tbl[uv][b3_tbl][b4 + 1];
} else {
if (b3_tbl >= ARRAY_SIZE(u8_composition_b4_tbl[uv]))
return (NULL);
start_id = u8_composition_b4_tbl[uv][b3_tbl][b4];
end_id = u8_composition_b4_tbl[uv][b3_tbl][b4 + 1];
}
if (start_id >= end_id)
return (NULL);
b3_base = u8_composition_b3_tbl[uv][b2][b3].base;
return ((uchar_t *)&(u8_composition_final_tbl[uv][b3_base + start_id]));
}
static boolean_t
blocked(uchar_t *comb_class, size_t last)
{
uchar_t my_comb_class;
size_t i;
my_comb_class = comb_class[last];
for (i = 1; i < last; i++)
if (comb_class[i] >= my_comb_class ||
comb_class[i] == U8_COMBINING_CLASS_STARTER)
return (B_TRUE);
return (B_FALSE);
}
static size_t
do_composition(size_t uv, uchar_t *s, uchar_t *comb_class, uchar_t *start,
uchar_t *disp, size_t last, uchar_t **os, uchar_t *oslast)
{
uchar_t t[U8_STREAM_SAFE_TEXT_MAX + 1];
uchar_t tc[U8_MB_CUR_MAX];
uint8_t saved_marks[U8_MAX_CHARS_A_SEQ];
size_t saved_marks_count;
uchar_t *p;
uchar_t *saved_p;
uchar_t *q;
size_t i;
size_t saved_i;
size_t j;
size_t k;
size_t l;
size_t C;
size_t saved_l;
size_t size;
uint32_t u1;
uint32_t u2;
boolean_t match_not_found = B_TRUE;
if (last >= U8_MAX_CHARS_A_SEQ)
last = U8_UPPER_LIMIT_IN_A_SEQ;
for (i = l = 0; i <= last; i++) {
if (i >= last || comb_class[i] != U8_COMBINING_CLASS_STARTER) {
SAVE_THE_CHAR:
p = s + start[i];
size = disp[i];
for (k = 0; k < size; k++)
t[l++] = *p++;
continue;
}
if (s[start[i]] == U8_HANGUL_JAMO_1ST_BYTE) {
U8_PUT_3BYTES_INTO_UTF32(u1, s[start[i]],
s[start[i] + 1], s[start[i] + 2]);
U8_PUT_3BYTES_INTO_UTF32(u2, s[start[i] + 3],
s[start[i] + 4], s[start[i] + 5]);
if (U8_HANGUL_JAMO_L(u1) && U8_HANGUL_JAMO_V(u2)) {
u1 -= U8_HANGUL_JAMO_L_FIRST;
u2 -= U8_HANGUL_JAMO_V_FIRST;
u1 = U8_HANGUL_SYL_FIRST +
(u1 * U8_HANGUL_V_COUNT + u2) *
U8_HANGUL_T_COUNT;
i += 2;
if (i <= last) {
U8_PUT_3BYTES_INTO_UTF32(u2,
s[start[i]], s[start[i] + 1],
s[start[i] + 2]);
if (U8_HANGUL_JAMO_T(u2)) {
u1 += u2 -
U8_HANGUL_JAMO_T_FIRST;
i++;
}
}
U8_SAVE_HANGUL_AS_UTF8(t + l, 0, 1, 2, u1);
i--;
l += 3;
continue;
}
}
p = find_composition_start(uv, s + start[i], disp[i]);
if (p == NULL)
goto SAVE_THE_CHAR;
saved_p = p;
saved_i = i;
saved_l = l;
saved_marks_count = 0;
TRY_THE_NEXT_MARK:
q = s + start[++i];
size = disp[i];
match_not_found = B_TRUE;
for (C = *p++; C > 0; C--) {
for (k = 0; k < size; p++, k++)
if (*p != q[k])
break;
if (k >= size && *p == U8_TBL_ELEMENT_FILLER) {
match_not_found = B_FALSE;
l = saved_l;
while (*++p != U8_TBL_ELEMENT_FILLER)
t[l++] = *p;
break;
}
if (*p != U8_TBL_ELEMENT_FILLER)
while (*++p != U8_TBL_ELEMENT_FILLER)
;
while (*++p != U8_TBL_ELEMENT_FILLER)
;
p++;
}
if (match_not_found) {
if (comb_class[i] == U8_COMBINING_CLASS_STARTER) {
i--;
goto SAVE_THE_CHAR;
}
saved_marks[saved_marks_count++] = i;
}
if (saved_l == l) {
while (i < last) {
if (blocked(comb_class, i + 1))
saved_marks[saved_marks_count++] = ++i;
else
break;
}
if (i < last) {
p = saved_p;
goto TRY_THE_NEXT_MARK;
}
} else if (i < last) {
p = find_composition_start(uv, t + saved_l,
l - saved_l);
if (p != NULL) {
saved_p = p;
goto TRY_THE_NEXT_MARK;
}
}
if (saved_l == l) {
p = s + start[saved_i];
size = disp[saved_i];
for (j = 0; j < size; j++)
t[l++] = *p++;
}
for (k = 0; k < saved_marks_count; k++) {
p = s + start[saved_marks[k]];
size = disp[saved_marks[k]];
for (j = 0; j < size; j++)
t[l++] = *p++;
}
}
if (comb_class[last] == U8_COMBINING_CLASS_STARTER) {
p = *os;
saved_l = l - disp[last];
while (p < oslast) {
int8_t number_of_bytes = u8_number_of_bytes[*p];
if (number_of_bytes <= 1)
break;
size = number_of_bytes;
if ((p + size) > oslast)
break;
saved_p = p;
for (i = 0; i < size; i++)
tc[i] = *p++;
q = find_composition_start(uv, t + saved_l,
l - saved_l);
if (q == NULL) {
p = saved_p;
break;
}
match_not_found = B_TRUE;
for (C = *q++; C > 0; C--) {
for (k = 0; k < size; q++, k++)
if (*q != tc[k])
break;
if (k >= size && *q == U8_TBL_ELEMENT_FILLER) {
match_not_found = B_FALSE;
l = saved_l;
while (*++q != U8_TBL_ELEMENT_FILLER) {
if (l >=
U8_STREAM_SAFE_TEXT_MAX) {
p = saved_p;
goto SAFE_RETURN;
}
t[l++] = *q;
}
break;
}
if (*q != U8_TBL_ELEMENT_FILLER)
while (*++q != U8_TBL_ELEMENT_FILLER)
;
while (*++q != U8_TBL_ELEMENT_FILLER)
;
q++;
}
if (match_not_found) {
p = saved_p;
break;
}
}
SAFE_RETURN:
*os = p;
}
for (i = 0; i < l; i++)
s[i] = t[i];
s[l] = '\0';
return (l);
}
static size_t
collect_a_seq(size_t uv, uchar_t *u8s, uchar_t **source, uchar_t *slast,
boolean_t is_it_toupper,
boolean_t is_it_tolower,
boolean_t canonical_decomposition,
boolean_t compatibility_decomposition,
boolean_t canonical_composition,
int *errnum, u8_normalization_states_t *state)
{
uchar_t *s;
int sz;
int saved_sz;
size_t i;
size_t j;
size_t k;
size_t l;
uchar_t comb_class[U8_MAX_CHARS_A_SEQ];
uchar_t disp[U8_MAX_CHARS_A_SEQ];
uchar_t start[U8_MAX_CHARS_A_SEQ];
uchar_t u8t[U8_MB_CUR_MAX];
uchar_t uts[U8_STREAM_SAFE_TEXT_MAX + 1];
uchar_t tc;
size_t last;
size_t saved_last;
uint32_t u1;
s = *source;
if (s >= slast) {
u8s[0] = '\0';
return (0);
}
sz = u8_number_of_bytes[*s];
if (sz < 0) {
*errnum = EILSEQ;
u8s[0] = *s++;
u8s[1] = '\0';
*source = s;
return (1);
}
if (sz == 1) {
if (is_it_toupper)
u8s[0] = U8_ASCII_TOUPPER(*s);
else if (is_it_tolower)
u8s[0] = U8_ASCII_TOLOWER(*s);
else
u8s[0] = *s;
s++;
u8s[1] = '\0';
} else if ((s + sz) > slast) {
*errnum = EINVAL;
for (i = 0; s < slast; )
u8s[i++] = *s++;
u8s[i] = '\0';
*source = s;
return (i);
} else {
if (is_it_toupper || is_it_tolower) {
i = do_case_conv(uv, u8s, s, sz, is_it_toupper);
s += sz;
sz = i;
} else {
for (i = 0; i < sz; )
u8s[i++] = *s++;
u8s[i] = '\0';
}
}
if (canonical_decomposition || compatibility_decomposition) {
if (sz == 1) {
*state = U8_STATE_START;
saved_sz = 1;
comb_class[0] = 0;
start[0] = 0;
disp[0] = 1;
last = 1;
} else {
saved_sz = do_decomp(uv, u8s, u8s, sz,
canonical_decomposition, state);
last = 0;
for (i = 0; i < saved_sz; ) {
sz = u8_number_of_bytes[u8s[i]];
comb_class[last] = combining_class(uv,
u8s + i, sz);
start[last] = i;
disp[last] = sz;
last++;
i += sz;
}
if (*state == U8_STATE_START) {
if (comb_class[last - 1])
*state = U8_STATE_COMBINING_MARK;
}
}
saved_last = last;
while (s < slast) {
sz = u8_number_of_bytes[*s];
if (sz <= 1) {
break;
} else if ((s + sz) > slast) {
break;
} else {
if (*s == U8_HANGUL_JAMO_1ST_BYTE) {
U8_PUT_3BYTES_INTO_UTF32(u1,
*s, *(s + 1), *(s + 2));
if (U8_HANGUL_COMPOSABLE_L_V(*state,
u1)) {
i = 0;
*state = U8_STATE_HANGUL_LV;
goto COLLECT_A_HANGUL;
}
if (U8_HANGUL_COMPOSABLE_LV_T(*state,
u1)) {
i = 0;
*state = U8_STATE_HANGUL_LVT;
goto COLLECT_A_HANGUL;
}
}
i = combining_class(uv, s, sz);
if (i == U8_COMBINING_CLASS_STARTER)
break;
if (*state != U8_STATE_START &&
*state != U8_STATE_COMBINING_MARK)
break;
*state = U8_STATE_COMBINING_MARK;
COLLECT_A_HANGUL:
if (last >= U8_UPPER_LIMIT_IN_A_SEQ) {
TURN_STREAM_SAFE:
*state = U8_STATE_START;
comb_class[last] = 0;
start[last] = saved_sz;
disp[last] = 2;
last++;
u8s[saved_sz++] = 0xCD;
u8s[saved_sz++] = 0x8F;
break;
}
if (*state == U8_STATE_COMBINING_MARK) {
k = last;
l = sz;
i = do_decomp(uv, uts, s, sz,
canonical_decomposition, state);
for (j = 0; j < i; ) {
sz = u8_number_of_bytes[uts[j]];
comb_class[last] =
combining_class(uv,
uts + j, sz);
start[last] = saved_sz + j;
disp[last] = sz;
last++;
if (last >=
U8_UPPER_LIMIT_IN_A_SEQ) {
last = k;
goto TURN_STREAM_SAFE;
}
j += sz;
}
*state = U8_STATE_COMBINING_MARK;
sz = i;
s += l;
for (i = 0; i < sz; i++)
u8s[saved_sz++] = uts[i];
} else {
comb_class[last] = i;
start[last] = saved_sz;
disp[last] = sz;
last++;
for (i = 0; i < sz; i++)
u8s[saved_sz++] = *s++;
}
if (is_it_toupper && sz >= 2 &&
u8s[saved_sz - 2] == 0xCD &&
u8s[saved_sz - 1] == 0x85) {
u8s[saved_sz - 2] = 0xCE;
u8s[saved_sz - 1] = 0x99;
}
}
}
last--;
if (last >= saved_last) {
for (i = 0; i < last; i++)
for (j = last; j > i; j--)
if (comb_class[j] &&
comb_class[j - 1] > comb_class[j]) {
U8_SWAP_COMB_MARKS(j - 1, j);
}
}
*source = s;
if (! canonical_composition) {
u8s[saved_sz] = '\0';
return (saved_sz);
}
sz = do_composition(uv, u8s, comb_class, start, disp, last,
&s, slast);
}
*source = s;
return ((size_t)sz);
}
static int
do_norm_compare(size_t uv, uchar_t *s1, uchar_t *s2, size_t n1, size_t n2,
int flag, int *errnum)
{
int result;
size_t sz1;
size_t sz2;
uchar_t u8s1[U8_STREAM_SAFE_TEXT_MAX + 1];
uchar_t u8s2[U8_STREAM_SAFE_TEXT_MAX + 1];
uchar_t *s1last;
uchar_t *s2last;
boolean_t is_it_toupper;
boolean_t is_it_tolower;
boolean_t canonical_decomposition;
boolean_t compatibility_decomposition;
boolean_t canonical_composition;
u8_normalization_states_t state;
s1last = s1 + n1;
s2last = s2 + n2;
is_it_toupper = flag & U8_TEXTPREP_TOUPPER;
is_it_tolower = flag & U8_TEXTPREP_TOLOWER;
canonical_decomposition = flag & U8_CANON_DECOMP;
compatibility_decomposition = flag & U8_COMPAT_DECOMP;
canonical_composition = flag & U8_CANON_COMP;
while (s1 < s1last && s2 < s2last) {
if (U8_ISASCII(*s1) && ((s1 + 1) >= s1last ||
((s1 + 1) < s1last && U8_ISASCII(*(s1 + 1))))) {
if (is_it_toupper)
u8s1[0] = U8_ASCII_TOUPPER(*s1);
else if (is_it_tolower)
u8s1[0] = U8_ASCII_TOLOWER(*s1);
else
u8s1[0] = *s1;
u8s1[1] = '\0';
sz1 = 1;
s1++;
} else {
state = U8_STATE_START;
sz1 = collect_a_seq(uv, u8s1, &s1, s1last,
is_it_toupper, is_it_tolower,
canonical_decomposition,
compatibility_decomposition,
canonical_composition, errnum, &state);
}
if (U8_ISASCII(*s2) && ((s2 + 1) >= s2last ||
((s2 + 1) < s2last && U8_ISASCII(*(s2 + 1))))) {
if (is_it_toupper)
u8s2[0] = U8_ASCII_TOUPPER(*s2);
else if (is_it_tolower)
u8s2[0] = U8_ASCII_TOLOWER(*s2);
else
u8s2[0] = *s2;
u8s2[1] = '\0';
sz2 = 1;
s2++;
} else {
state = U8_STATE_START;
sz2 = collect_a_seq(uv, u8s2, &s2, s2last,
is_it_toupper, is_it_tolower,
canonical_decomposition,
compatibility_decomposition,
canonical_composition, errnum, &state);
}
if (sz1 == 1 && sz2 == 1) {
if (*u8s1 > *u8s2)
return (1);
if (*u8s1 < *u8s2)
return (-1);
} else {
result = strcmp((const char *)u8s1, (const char *)u8s2);
if (result != 0)
return (result);
}
}
if (s1 >= s1last) {
if (s2 >= s2last)
return (0);
return (-1);
}
return (1);
}
int
u8_strcmp(const char *s1, const char *s2, size_t n, int flag, size_t uv,
int *errnum)
{
int f;
size_t n1;
size_t n2;
*errnum = 0;
if (uv > U8_UNICODE_LATEST) {
*errnum = ERANGE;
uv = U8_UNICODE_LATEST;
}
if (flag == 0) {
flag = U8_STRCMP_CS;
} else {
f = flag & (U8_STRCMP_CS | U8_STRCMP_CI_UPPER |
U8_STRCMP_CI_LOWER);
if (f == 0) {
flag |= U8_STRCMP_CS;
} else if (f != U8_STRCMP_CS && f != U8_STRCMP_CI_UPPER &&
f != U8_STRCMP_CI_LOWER) {
*errnum = EBADF;
flag = U8_STRCMP_CS;
}
f = flag & (U8_CANON_DECOMP | U8_COMPAT_DECOMP | U8_CANON_COMP);
if (f && f != U8_STRCMP_NFD && f != U8_STRCMP_NFC &&
f != U8_STRCMP_NFKD && f != U8_STRCMP_NFKC) {
*errnum = EBADF;
flag = U8_STRCMP_CS;
}
}
if (flag == U8_STRCMP_CS) {
return (n == 0 ? strcmp(s1, s2) : strncmp(s1, s2, n));
}
n1 = strlen(s1);
n2 = strlen(s2);
if (n != 0) {
if (n < n1)
n1 = n;
if (n < n2)
n2 = n;
}
if (flag == U8_STRCMP_CI_UPPER) {
return (do_case_compare(uv, (uchar_t *)s1, (uchar_t *)s2,
n1, n2, B_TRUE, errnum));
} else if (flag == U8_STRCMP_CI_LOWER) {
return (do_case_compare(uv, (uchar_t *)s1, (uchar_t *)s2,
n1, n2, B_FALSE, errnum));
}
return (do_norm_compare(uv, (uchar_t *)s1, (uchar_t *)s2, n1, n2,
flag, errnum));
}
size_t
u8_textprep_str(char *inarray, size_t *inlen, char *outarray, size_t *outlen,
int flag, size_t unicode_version, int *errnum)
{
int f;
int sz;
uchar_t *ib;
uchar_t *ibtail;
uchar_t *ob;
uchar_t *obtail;
boolean_t do_not_ignore_null;
boolean_t do_not_ignore_invalid;
boolean_t is_it_toupper;
boolean_t is_it_tolower;
boolean_t canonical_decomposition;
boolean_t compatibility_decomposition;
boolean_t canonical_composition;
size_t ret_val;
size_t i;
size_t j;
uchar_t u8s[U8_STREAM_SAFE_TEXT_MAX + 1];
u8_normalization_states_t state;
if (unicode_version > U8_UNICODE_LATEST) {
*errnum = ERANGE;
return ((size_t)-1);
}
f = flag & (U8_TEXTPREP_TOUPPER | U8_TEXTPREP_TOLOWER);
if (f == (U8_TEXTPREP_TOUPPER | U8_TEXTPREP_TOLOWER)) {
*errnum = EBADF;
return ((size_t)-1);
}
f = flag & (U8_CANON_DECOMP | U8_COMPAT_DECOMP | U8_CANON_COMP);
if (f && f != U8_TEXTPREP_NFD && f != U8_TEXTPREP_NFC &&
f != U8_TEXTPREP_NFKD && f != U8_TEXTPREP_NFKC) {
*errnum = EBADF;
return ((size_t)-1);
}
if (inarray == NULL || *inlen == 0)
return (0);
if (outarray == NULL) {
*errnum = E2BIG;
return ((size_t)-1);
}
ib = (uchar_t *)inarray;
ob = (uchar_t *)outarray;
ibtail = ib + *inlen;
obtail = ob + *outlen;
do_not_ignore_null = !(flag & U8_TEXTPREP_IGNORE_NULL);
do_not_ignore_invalid = !(flag & U8_TEXTPREP_IGNORE_INVALID);
is_it_toupper = flag & U8_TEXTPREP_TOUPPER;
is_it_tolower = flag & U8_TEXTPREP_TOLOWER;
ret_val = 0;
if (f == 0) {
while (ib < ibtail) {
if (*ib == '\0' && do_not_ignore_null)
break;
sz = u8_number_of_bytes[*ib];
if (sz < 0) {
if (do_not_ignore_invalid) {
*errnum = EILSEQ;
ret_val = (size_t)-1;
break;
}
sz = 1;
ret_val++;
}
if (sz == 1) {
if (ob >= obtail) {
*errnum = E2BIG;
ret_val = (size_t)-1;
break;
}
if (is_it_toupper)
*ob = U8_ASCII_TOUPPER(*ib);
else if (is_it_tolower)
*ob = U8_ASCII_TOLOWER(*ib);
else
*ob = *ib;
ib++;
ob++;
} else if ((ib + sz) > ibtail) {
if (do_not_ignore_invalid) {
*errnum = EINVAL;
ret_val = (size_t)-1;
break;
}
if ((obtail - ob) < (ibtail - ib)) {
*errnum = E2BIG;
ret_val = (size_t)-1;
break;
}
ret_val++;
while (ib < ibtail)
*ob++ = *ib++;
} else {
if (is_it_toupper || is_it_tolower) {
i = do_case_conv(unicode_version, u8s,
ib, sz, is_it_toupper);
if ((obtail - ob) < i) {
*errnum = E2BIG;
ret_val = (size_t)-1;
break;
}
ib += sz;
for (sz = 0; sz < i; sz++)
*ob++ = u8s[sz];
} else {
if ((obtail - ob) < sz) {
*errnum = E2BIG;
ret_val = (size_t)-1;
break;
}
for (i = 0; i < sz; i++)
*ob++ = *ib++;
}
}
}
} else {
canonical_decomposition = flag & U8_CANON_DECOMP;
compatibility_decomposition = flag & U8_COMPAT_DECOMP;
canonical_composition = flag & U8_CANON_COMP;
while (ib < ibtail) {
if (*ib == '\0' && do_not_ignore_null)
break;
if (U8_ISASCII(*ib) && ((ib + 1) >= ibtail ||
((ib + 1) < ibtail && U8_ISASCII(*(ib + 1))))) {
if (ob >= obtail) {
*errnum = E2BIG;
ret_val = (size_t)-1;
break;
}
if (is_it_toupper)
*ob = U8_ASCII_TOUPPER(*ib);
else if (is_it_tolower)
*ob = U8_ASCII_TOLOWER(*ib);
else
*ob = *ib;
ib++;
ob++;
} else {
*errnum = 0;
state = U8_STATE_START;
j = collect_a_seq(unicode_version, u8s,
&ib, ibtail,
is_it_toupper,
is_it_tolower,
canonical_decomposition,
compatibility_decomposition,
canonical_composition,
errnum, &state);
if (*errnum && do_not_ignore_invalid) {
ret_val = (size_t)-1;
break;
}
if ((obtail - ob) < j) {
*errnum = E2BIG;
ret_val = (size_t)-1;
break;
}
for (i = 0; i < j; i++)
*ob++ = u8s[i];
}
}
}
*inlen = ibtail - ib;
*outlen = obtail - ob;
return (ret_val);
}