1#include <stddef.h>
2
3#include "uleutf.h"
4
5int uleutf_casecmp(const char* src1, const char* src2)
6{
7 ule_i32 src1_lwr_cp = 0;
8 ule_i32 src2_lwr_cp = 0;
9 ule_i32 src1_upr_cp = 0;
10 ule_i32 src2_upr_cp = 0;
11 ule_i32 src1_orig_cp = 0;
12 ule_i32 src2_orig_cp = 0;
13
14 for (;;) {
15 src1 = uleutf_codepoint(src1, &src1_orig_cp);
16 src2 = uleutf_codepoint(src2, &src2_orig_cp);
17
18 /* lower the srcs if required */
19 src1_lwr_cp = utf8lwrcodepoint(src1_orig_cp);
20 src2_lwr_cp = utf8lwrcodepoint(src2_orig_cp);
21
22 /* lower the srcs if required */
23 src1_upr_cp = utf8uprcodepoint(src1_orig_cp);
24 src2_upr_cp = utf8uprcodepoint(src2_orig_cp);
25
26 /* check if the lowered codepoints match */
27 if ((0 == src1_orig_cp) && (0 == src2_orig_cp))
28 return 0;
29 else if ((src1_lwr_cp == src2_lwr_cp) || (src1_upr_cp == src2_upr_cp))
30 continue;
31
32 /* if they don't match, then we return the difference between the characters */
33 return src1_lwr_cp - src2_lwr_cp;
34 }
35}
36
37char* uleutf_cat(char* dst, const char* src)
38{
39 char* d = dst;
40
41 /* find the null terminating byte in dst */
42 while ('\0' != *d)
43 d++;
44
45 /* overwriting the null terminating byte in dst, append src byte-by-byte */
46 while ('\0' != *src)
47 *d++ = *src++;
48
49 /* write out a new null terminating byte into dst */
50 *d = '\0';
51
52 return dst;
53}
54
55char* uleutf_chr(const char* src, ule_i32 chr)
56{
57 char c[5] = {'\0', '\0', '\0', '\0', '\0'};
58
59 if (0 == chr) {
60 /* being asked to return position of null terminating byte, so
61 just run s to the end, and return! */
62 while ('\0' != *src)
63 src++;
64
65 return (char*)src;
66 }
67 else if (0 == ((ule_i32)0xffffff80 & chr)) {
68 /* 1-byte/7-bit ascii
69 (0b0xxxxxxx) */
70 c[0] = (ule_i8)chr;
71 }
72 else if (0 == ((ule_i32)0xfffff800 & chr)) {
73 /* 2-byte/11-bit utf8 code point
74 (0b110xxxxx 0b10xxxxxx) */
75 c[0] = (ule_i8)(0xc0 | (ule_i8)(chr >> 6));
76 c[1] = (ule_i8)(0x80 | (ule_i8)(chr & 0x3f));
77 }
78 else if (0 == ((ule_i32)0xffff0000 & chr)) {
79 /* 3-byte/16-bit utf8 code point
80 (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) */
81 c[0] = (ule_i8)(0xe0 | (ule_i8)(chr >> 12));
82 c[1] = (ule_i8)(0x80 | (ule_i8)((chr >> 6) & 0x3f));
83 c[2] = (ule_i8)(0x80 | (ule_i8)(chr & 0x3f));
84 }
85 else { /* if (0 == ((int)0xffe00000 & chr)) { */
86 /* 4-byte/21-bit utf8 code point
87 (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) */
88 c[0] = (ule_i8)(0xf0 | (ule_i8)(chr >> 18));
89 c[1] = (ule_i8)(0x80 | (ule_i8)((chr >> 12) & 0x3f));
90 c[2] = (ule_i8)(0x80 | (ule_i8)((chr >> 6) & 0x3f));
91 c[3] = (ule_i8)(0x80 | (ule_i8)(chr & 0x3f));
92 }
93
94 /* we've made c into a 2 utf8 codepoint string, one for the chr we are
95 seeking, another for the null terminating byte. now use utf8str to search */
96 return uleutf_str(src, c);
97}
98
99int uleutf_cmp(const char* src1, const char* src2, size_t n)
100{
101 while (('\0' != *src1) || ('\0' != *src2)) {
102 if ((unsigned char)*src1 < (unsigned char)*src2)
103 return -1;
104 else if ((unsigned char)*src1 > (unsigned char)*src2)
105 return 1;
106
107 src1++;
108 src2++;
109 }
110
111 /* both utf8 strings matched */
112 return 0;
113}
114
115int uleutf_ncmp(const char* src1, const char* src2, size_t n)
116{
117 while ((0 != n--) && (('\0' != *src1) || ('\0' != *src2))) {
118 if ((unsigned char)*src1 < (unsigned char)*src2)
119 return -1;
120 else if ((unsigned char)*src1 > (unsigned char)*src2)
121 return 1;
122
123 src1++;
124 src2++;
125 }
126
127 /* both utf8 strings matched */
128 return 0;
129}
130
131char* uleutf_str(const char* haystack, const char* needle)
132{
133 ule_i32 throwaway_codepoint = 0;
134
135 /* if needle has no utf8 codepoints before the null terminating
136 byte then return haystack */
137 if ('\0' == *needle)
138 return (char*)haystack;
139
140 while ('\0' != *haystack) {
141 const char* maybe_match = haystack;
142 const char* n = needle;
143
144 while (*haystack == *n && (*haystack != '\0' && *n != '\0')) {
145 n++;
146 haystack++;
147 }
148
149 if ('\0' == *n)
150 /* we found the whole utf8 string for needle in haystack at
151 maybeMatch, so return it */
152 return (char*)maybe_match;
153 else
154 /* h could be in the middle of an unmatching utf8 codepoint,
155 so we need to march it on to the next character beginning
156 starting from the current character */
157 haystack = uleutf_codepoint(maybe_match, &throwaway_codepoint);
158 }
159
160 /* no match */
161 return NULL;
162}
163
164char* uleutf_codepoint(const char* str, ule_i32* out_codepoint)
165{
166 size_t n = 1;
167 size_t i = 1;
168 ule_i32 cp = (ule_i32)str[0];
169
170 if (0xf0 == (0xf8 & str[0])) {
171 /* 4 byte utf8 codepoint */
172 n = 4;
173 cp = 0x07 & str[0];
174 }
175 else if (0xe0 == (0xf0 & str[0])) {
176 /* 3 byte utf8 codepoint */
177 n = 3;
178 cp = 0x0f & str[0];
179 }
180 else if (0xc0 == (0xe0 & str[0])) {
181 /* 2 byte utf8 codepoint */
182 n = 2;
183 cp = 0x1f & str[0];
184 }
185
186 /* stop at the NUL if the sequence is truncated */
187 for (i = 1; i < n && '\0' != str[i]; i++)
188 cp = (cp << 6) | (0x3f & str[i]);
189
190 *out_codepoint = cp;
191 return (char*)(str + i);
192}
193
194size_t uleutf_codepointcalcsize(const char* str)
195{
196 if (0xf0 == (0xf8 & str[0]))
197 return 4; /* 4 byte utf8 codepoint */
198 else if (0xe0 == (0xf0 & str[0]))
199 return 3; /* 3 byte utf8 codepoint */
200 else if (0xc0 == (0xe0 & str[0]))
201 return 2; /* 2 byte utf8 codepoint */
202
203 return 1; /* 1 byte utf8 codepoint otherwise */
204}
205
206size_t uleutf_codepointsize(ule_i32 chr)
207{
208 if (0 == ((ule_i32)0xffffff80 & chr))
209 return 1;
210 else if (0 == ((ule_i32)0xfffff800 & chr))
211 return 2;
212 else if (0 == ((ule_i32)0xffff0000 & chr))
213 return 3;
214 else /* if (0 == ((int)0xffe00000 & chr)) */
215 return 4;
216}
217
218int uleutf_codepointvisualwidth(ule_i32 chr)
219{
220 /* the null codepoint takes up no visual space */
221 if (0 == chr)
222 return 0;
223
224 /* C0 and C1 control codepoints have no printable representation. */
225 if ((chr < 0x20) || ((0x7f <= chr) && (chr < 0xa0)))
226 return -1;
227
228 /* zero-width combining marks and format codepoints. the ranges follow the
229 widely used wcwidth() tables and cover Unicode general categories Mn, Me
230 and Cf that do not advance the cursor */
231 if (((0x0300 <= chr) && (0x036f >= chr)) ||
232 ((0x0483 <= chr) && (0x0486 >= chr)) ||
233 ((0x0488 <= chr) && (0x0489 >= chr)) ||
234 ((0x0591 <= chr) && (0x05bd >= chr)) || (0x05bf == chr) ||
235 ((0x05c1 <= chr) && (0x05c2 >= chr)) ||
236 ((0x05c4 <= chr) && (0x05c5 >= chr)) || (0x05c7 == chr) ||
237 ((0x0600 <= chr) && (0x0603 >= chr)) ||
238 ((0x0610 <= chr) && (0x0615 >= chr)) ||
239 ((0x064b <= chr) && (0x065e >= chr)) || (0x0670 == chr) ||
240 ((0x06d6 <= chr) && (0x06e4 >= chr)) ||
241 ((0x06e7 <= chr) && (0x06e8 >= chr)) ||
242 ((0x06ea <= chr) && (0x06ed >= chr)) || (0x070f == chr) ||
243 (0x0711 == chr) || ((0x0730 <= chr) && (0x074a >= chr)) ||
244 ((0x07a6 <= chr) && (0x07b0 >= chr)) ||
245 ((0x07eb <= chr) && (0x07f3 >= chr)) ||
246 ((0x0901 <= chr) && (0x0902 >= chr)) || (0x093c == chr) ||
247 ((0x0941 <= chr) && (0x0948 >= chr)) || (0x094d == chr) ||
248 ((0x0951 <= chr) && (0x0954 >= chr)) ||
249 ((0x0962 <= chr) && (0x0963 >= chr)) || (0x0981 == chr) ||
250 (0x09bc == chr) || ((0x09c1 <= chr) && (0x09c4 >= chr)) ||
251 (0x09cd == chr) || ((0x09e2 <= chr) && (0x09e3 >= chr)) ||
252 ((0x0a01 <= chr) && (0x0a02 >= chr)) || (0x0a3c == chr) ||
253 ((0x0a41 <= chr) && (0x0a42 >= chr)) ||
254 ((0x0a47 <= chr) && (0x0a48 >= chr)) ||
255 ((0x0a4b <= chr) && (0x0a4d >= chr)) ||
256 ((0x0a70 <= chr) && (0x0a71 >= chr)) ||
257 ((0x0a81 <= chr) && (0x0a82 >= chr)) || (0x0abc == chr) ||
258 ((0x0ac1 <= chr) && (0x0ac5 >= chr)) ||
259 ((0x0ac7 <= chr) && (0x0ac8 >= chr)) || (0x0acd == chr) ||
260 ((0x0ae2 <= chr) && (0x0ae3 >= chr)) || (0x0b01 == chr) ||
261 (0x0b3c == chr) || (0x0b3f == chr) ||
262 ((0x0b41 <= chr) && (0x0b43 >= chr)) || (0x0b4d == chr) ||
263 (0x0b56 == chr) || (0x0b82 == chr) || (0x0bc0 == chr) ||
264 (0x0bcd == chr) || ((0x0c3e <= chr) && (0x0c40 >= chr)) ||
265 ((0x0c46 <= chr) && (0x0c48 >= chr)) ||
266 ((0x0c4a <= chr) && (0x0c4d >= chr)) ||
267 ((0x0c55 <= chr) && (0x0c56 >= chr)) || (0x0cbc == chr) ||
268 (0x0cbf == chr) || (0x0cc6 == chr) ||
269 ((0x0ccc <= chr) && (0x0ccd >= chr)) ||
270 ((0x0ce2 <= chr) && (0x0ce3 >= chr)) ||
271 ((0x0d41 <= chr) && (0x0d43 >= chr)) || (0x0d4d == chr) ||
272 (0x0dca == chr) || ((0x0dd2 <= chr) && (0x0dd4 >= chr)) ||
273 (0x0dd6 == chr) || (0x0e31 == chr) ||
274 ((0x0e34 <= chr) && (0x0e3a >= chr)) ||
275 ((0x0e47 <= chr) && (0x0e4e >= chr)) || (0x0eb1 == chr) ||
276 ((0x0eb4 <= chr) && (0x0eb9 >= chr)) ||
277 ((0x0ebb <= chr) && (0x0ebc >= chr)) ||
278 ((0x0ec8 <= chr) && (0x0ecd >= chr)) ||
279 ((0x0f18 <= chr) && (0x0f19 >= chr)) || (0x0f35 == chr) ||
280 (0x0f37 == chr) || (0x0f39 == chr) ||
281 ((0x0f71 <= chr) && (0x0f7e >= chr)) ||
282 ((0x0f80 <= chr) && (0x0f84 >= chr)) ||
283 ((0x0f86 <= chr) && (0x0f87 >= chr)) ||
284 ((0x0f90 <= chr) && (0x0f97 >= chr)) ||
285 ((0x0f99 <= chr) && (0x0fbc >= chr)) || (0x0fc6 == chr) ||
286 ((0x102d <= chr) && (0x1030 >= chr)) || (0x1032 == chr) ||
287 ((0x1036 <= chr) && (0x1037 >= chr)) || (0x1039 == chr) ||
288 ((0x1058 <= chr) && (0x1059 >= chr)) ||
289 ((0x1160 <= chr) && (0x11ff >= chr)) || (0x135f == chr) ||
290 ((0x1712 <= chr) && (0x1714 >= chr)) ||
291 ((0x1732 <= chr) && (0x1734 >= chr)) ||
292 ((0x1752 <= chr) && (0x1753 >= chr)) ||
293 ((0x1772 <= chr) && (0x1773 >= chr)) ||
294 ((0x17b4 <= chr) && (0x17b5 >= chr)) ||
295 ((0x17b7 <= chr) && (0x17bd >= chr)) || (0x17c6 == chr) ||
296 ((0x17c9 <= chr) && (0x17d3 >= chr)) || (0x17dd == chr) ||
297 ((0x180b <= chr) && (0x180d >= chr)) || (0x18a9 == chr) ||
298 ((0x1920 <= chr) && (0x1922 >= chr)) ||
299 ((0x1927 <= chr) && (0x1928 >= chr)) || (0x1932 == chr) ||
300 ((0x1939 <= chr) && (0x193b >= chr)) ||
301 ((0x1a17 <= chr) && (0x1a18 >= chr)) ||
302 ((0x1b00 <= chr) && (0x1b03 >= chr)) || (0x1b34 == chr) ||
303 ((0x1b36 <= chr) && (0x1b3a >= chr)) || (0x1b3c == chr) ||
304 (0x1b42 == chr) || ((0x1b6b <= chr) && (0x1b73 >= chr)) ||
305 ((0x1dc0 <= chr) && (0x1dca >= chr)) ||
306 ((0x1dfe <= chr) && (0x1dff >= chr)) ||
307 ((0x200b <= chr) && (0x200f >= chr)) ||
308 ((0x202a <= chr) && (0x202e >= chr)) ||
309 ((0x2060 <= chr) && (0x2063 >= chr)) ||
310 ((0x206a <= chr) && (0x206f >= chr)) ||
311 ((0x20d0 <= chr) && (0x20ef >= chr)) ||
312 ((0x302a <= chr) && (0x302f >= chr)) ||
313 ((0x3099 <= chr) && (0x309a >= chr)) || (0xa806 == chr) ||
314 (0xa80b == chr) || ((0xa825 <= chr) && (0xa826 >= chr)) ||
315 (0xfb1e == chr) || ((0xfe00 <= chr) && (0xfe0f >= chr)) ||
316 ((0xfe20 <= chr) && (0xfe23 >= chr)) || (0xfeff == chr) ||
317 ((0xfff9 <= chr) && (0xfffb >= chr)) ||
318 ((0x10a01 <= chr) && (0x10a03 >= chr)) ||
319 ((0x10a05 <= chr) && (0x10a06 >= chr)) ||
320 ((0x10a0c <= chr) && (0x10a0f >= chr)) ||
321 ((0x10a38 <= chr) && (0x10a3a >= chr)) || (0x10a3f == chr) ||
322 ((0x1d167 <= chr) && (0x1d169 >= chr)) ||
323 ((0x1d173 <= chr) && (0x1d182 >= chr)) ||
324 ((0x1d185 <= chr) && (0x1d18b >= chr)) ||
325 ((0x1d1aa <= chr) && (0x1d1ad >= chr)) ||
326 ((0x1d242 <= chr) && (0x1d244 >= chr)) || (0xe0001 == chr) ||
327 ((0xe0020 <= chr) && (0xe007f >= chr)) ||
328 ((0xe0100 <= chr) && (0xe01ef >= chr)))
329 return 0;
330
331 /* wide (double-width) codepoints, mostly East Asian scripts */
332 if ((0x1100 <= chr) &&
333 (((0x1100 <= chr) && (0x115f >= chr)) || (0x2329 == chr) ||
334 (0x232a == chr) ||
335 (((0x2e80 <= chr) && (0xa4cf >= chr)) && (0x303f != chr)) ||
336 ((0xac00 <= chr) && (0xd7a3 >= chr)) ||
337 ((0xf900 <= chr) && (0xfaff >= chr)) ||
338 ((0xfe10 <= chr) && (0xfe19 >= chr)) ||
339 ((0xfe30 <= chr) && (0xfe6f >= chr)) ||
340 ((0xff00 <= chr) && (0xff60 >= chr)) ||
341 ((0xffe0 <= chr) && (0xffe6 >= chr)) ||
342 ((0x20000 <= chr) && (0x2fffd >= chr)) ||
343 ((0x30000 <= chr) && (0x3fffd >= chr))))
344 return 2;
345
346 return 1;
347}
348