2 * UTF-8 utility functions
4 * (c) 2010-2016 Steve Bennett <steveb@workware.net.au>
6 * See LICENCE for licence details.
16 /* This one is always implemented */
17 int utf8_fromunicode(char *p
, unsigned uc
)
23 else if (uc
<= 0x7ff) {
24 *p
++ = 0xc0 | ((uc
& 0x7c0) >> 6);
25 *p
= 0x80 | (uc
& 0x3f);
28 else if (uc
<= 0xffff) {
29 *p
++ = 0xe0 | ((uc
& 0xf000) >> 12);
30 *p
++ = 0x80 | ((uc
& 0xfc0) >> 6);
31 *p
= 0x80 | (uc
& 0x3f);
34 /* Note: We silently truncate to 21 bits here: 0x1fffff */
36 *p
++ = 0xf0 | ((uc
& 0x1c0000) >> 18);
37 *p
++ = 0x80 | ((uc
& 0x3f000) >> 12);
38 *p
++ = 0x80 | ((uc
& 0xfc0) >> 6);
39 *p
= 0x80 | (uc
& 0x3f);
44 #if defined(USE_UTF8) && !defined(JIM_BOOTSTRAP)
45 int utf8_charlen(int c
)
47 if ((c
& 0x80) == 0) {
50 if ((c
& 0xe0) == 0xc0) {
53 if ((c
& 0xf0) == 0xe0) {
56 if ((c
& 0xf8) == 0xf0) {
59 /* Invalid sequence, so treat it as a single byte */
63 int utf8_strlen(const char *str
, int bytelen
)
67 bytelen
= strlen(str
);
71 int l
= utf8_tounicode(str
, &c
);
79 int utf8_strwidth(const char *str
, int charlen
)
84 int l
= utf8_tounicode(str
, &c
);
85 width
+= utf8_width(c
);
92 int utf8_index(const char *str
, int index
)
96 s
+= utf8_charlen(*s
);
101 int utf8_prev_len(const char *str
, int len
)
107 /* Look up to len chars backward for a start-of-char byte */
109 if ((str
[-n
] & 0x80) == 0) {
110 /* Start of a 1-byte char */
113 if ((str
[-n
] & 0xc0) == 0xc0) {
114 /* Start of a multi-byte char */
122 int utf8_tounicode(const char *str
, int *uc
)
124 unsigned const char *s
= (unsigned const char *)str
;
131 if ((s
[1] & 0xc0) == 0x80) {
132 *uc
= ((s
[0] & ~0xc0) << 6) | (s
[1] & ~0x80);
136 /* Otherwise this is an invalid sequence */
139 else if (s
[0] < 0xf0) {
140 if (((str
[1] & 0xc0) == 0x80) && ((str
[2] & 0xc0) == 0x80)) {
141 *uc
= ((s
[0] & ~0xe0) << 12) | ((s
[1] & ~0x80) << 6) | (s
[2] & ~0x80);
145 /* Otherwise this is an invalid sequence */
148 else if (s
[0] < 0xf8) {
149 if (((str
[1] & 0xc0) == 0x80) && ((str
[2] & 0xc0) == 0x80) && ((str
[3] & 0xc0) == 0x80)) {
150 *uc
= ((s
[0] & ~0xf0) << 18) | ((s
[1] & ~0x80) << 12) | ((s
[2] & ~0x80) << 6) | (s
[3] & ~0x80);
151 if (*uc
>= 0x10000) {
154 /* Otherwise this is an invalid sequence */
158 /* Invalid sequence, so just return the byte */
164 unsigned short code
; /* code point */
165 unsigned short altcode
; /* alternate case code point */
169 unsigned lower
; /* lower inclusive */
170 unsigned upper
; /* upper exclusive */
174 /* Generated mapping tables */
175 #include "_unicode_mapping.c"
177 #define ARRAYSIZE(A) sizeof(A) / sizeof(*(A))
179 static int cmp_casemap(const void *key
, const void *cm
)
181 return *(int *)key
- (int)((const struct casemap
*)cm
)->code
;
184 static int utf8_map_case(const struct casemap
*mapping
, int num
, int ch
)
186 /* We only support 16 bit case mapping */
188 const struct casemap
*cm
=
189 bsearch(&ch
, mapping
, num
, sizeof(*mapping
), cmp_casemap
);
198 static int cmp_range(const void *key
, const void *cm
)
200 const struct utf8range
*range
= (const struct utf8range
*)cm
;
201 unsigned ch
= *(unsigned *)key
;
202 if (ch
< range
->lower
) {
205 if (ch
>= range
->upper
) {
211 static int utf8_in_range(const struct utf8range
*range
, int num
, int ch
)
213 const struct utf8range
*r
=
214 bsearch(&ch
, range
, num
, sizeof(*range
), cmp_range
);
222 int utf8_upper(int ch
)
227 return utf8_map_case(unicode_case_mapping_upper
, ARRAYSIZE(unicode_case_mapping_upper
), ch
);
230 int utf8_lower(int ch
)
235 return utf8_map_case(unicode_case_mapping_lower
, ARRAYSIZE(unicode_case_mapping_lower
), ch
);
238 int utf8_title(int ch
)
241 int newch
= utf8_map_case(unicode_case_mapping_title
, ARRAYSIZE(unicode_case_mapping_title
), ch
);
243 return newch
? newch
: ch
;
246 return utf8_upper(ch
);
249 int utf8_width(int ch
)
252 if (utf8_in_range(unicode_range_combining
, ARRAYSIZE(unicode_range_combining
), ch
)) {
255 if (utf8_in_range(unicode_range_wide
, ARRAYSIZE(unicode_range_wide
), ch
)) {
262 #endif /* JIM_BOOTSTRAP */