Blame


1 4cd67caa 2022-02-09 op /* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
2 4cd67caa 2022-02-09 op *
3 4cd67caa 2022-02-09 op * Permission is hereby granted, free of charge, to any person
4 4cd67caa 2022-02-09 op * obtaining a copy of this software and associated documentation
5 4cd67caa 2022-02-09 op * files (the "Software"), to deal in the Software without
6 4cd67caa 2022-02-09 op * restriction, including without limitation the rights to use, copy,
7 4cd67caa 2022-02-09 op * modify, merge, publish, distribute, sublicense, and/or sell copies
8 4cd67caa 2022-02-09 op * of the Software, and to permit persons to whom the Software is
9 4cd67caa 2022-02-09 op * furnished to do so, subject to the following conditions:
10 4cd67caa 2022-02-09 op *
11 4cd67caa 2022-02-09 op * The above copyright notice and this permission notice shall be
12 4cd67caa 2022-02-09 op * included in all copies or substantial portions of the Software.
13 4cd67caa 2022-02-09 op *
14 4cd67caa 2022-02-09 op * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 4cd67caa 2022-02-09 op * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 4cd67caa 2022-02-09 op * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 4cd67caa 2022-02-09 op * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18 4cd67caa 2022-02-09 op * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19 4cd67caa 2022-02-09 op * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 4cd67caa 2022-02-09 op * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 4cd67caa 2022-02-09 op * SOFTWARE.
22 4cd67caa 2022-02-09 op */
23 4cd67caa 2022-02-09 op
24 4cd67caa 2022-02-09 op #include "compat.h"
25 4cd67caa 2022-02-09 op
26 4cd67caa 2022-02-09 op #include <assert.h>
27 4cd67caa 2022-02-09 op #include <stddef.h>
28 4cd67caa 2022-02-09 op #include <stdint.h>
29 4cd67caa 2022-02-09 op #include <wchar.h>
30 4cd67caa 2022-02-09 op
31 4cd67caa 2022-02-09 op #include "telescope.h"
32 4cd67caa 2022-02-09 op #include "utf8.h"
33 4cd67caa 2022-02-09 op
34 4cd67caa 2022-02-09 op #define UTF8_ACCEPT 0
35 4cd67caa 2022-02-09 op #define UTF8_REJECT 1
36 4cd67caa 2022-02-09 op
37 4cd67caa 2022-02-09 op static const uint8_t utf8d[] = {
38 4cd67caa 2022-02-09 op 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
39 4cd67caa 2022-02-09 op 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
40 4cd67caa 2022-02-09 op 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
41 4cd67caa 2022-02-09 op 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
42 4cd67caa 2022-02-09 op 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
43 4cd67caa 2022-02-09 op 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
44 4cd67caa 2022-02-09 op 8,8,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, // c0..df
45 4cd67caa 2022-02-09 op 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
46 4cd67caa 2022-02-09 op 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
47 4cd67caa 2022-02-09 op 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
48 4cd67caa 2022-02-09 op 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
49 4cd67caa 2022-02-09 op 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
50 4cd67caa 2022-02-09 op 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
51 4cd67caa 2022-02-09 op 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
52 4cd67caa 2022-02-09 op };
53 4cd67caa 2022-02-09 op
54 4cd67caa 2022-02-09 op static inline uint32_t
55 4cd67caa 2022-02-09 op decode(uint32_t* restrict state, uint32_t* restrict codep, uint8_t byte)
56 4cd67caa 2022-02-09 op {
57 4cd67caa 2022-02-09 op uint32_t type = utf8d[byte];
58 4cd67caa 2022-02-09 op
59 4cd67caa 2022-02-09 op *codep = (*state != UTF8_ACCEPT) ?
60 4cd67caa 2022-02-09 op (byte & 0x3fu) | (*codep << 6) :
61 4cd67caa 2022-02-09 op (0xff >> type) & (byte);
62 4cd67caa 2022-02-09 op
63 4cd67caa 2022-02-09 op *state = utf8d[256 + *state*16 + type];
64 4cd67caa 2022-02-09 op return *state;
65 4cd67caa 2022-02-09 op }
66 4cd67caa 2022-02-09 op
67 4cd67caa 2022-02-09 op
68 4cd67caa 2022-02-09 op /* end of the converter, utility functions ahead */
69 4cd67caa 2022-02-09 op
70 4cd67caa 2022-02-09 op #define ZERO_WIDTH_SPACE 0x200B
71 4cd67caa 2022-02-09 op
72 4cd67caa 2022-02-09 op /* public version of decode */
73 4cd67caa 2022-02-09 op uint32_t
74 4cd67caa 2022-02-09 op utf8_decode(uint32_t* restrict state, uint32_t* restrict codep, uint8_t byte)
75 4cd67caa 2022-02-09 op {
76 4cd67caa 2022-02-09 op return decode(state, codep, byte);
77 4cd67caa 2022-02-09 op }
78 4cd67caa 2022-02-09 op
79 4cd67caa 2022-02-09 op /* encode cp in s. s must be at least 4 bytes wide */
80 4cd67caa 2022-02-09 op size_t
81 4cd67caa 2022-02-09 op utf8_encode(uint32_t cp, char *s)
82 4cd67caa 2022-02-09 op {
83 4cd67caa 2022-02-09 op if (cp <= 0x7F) {
84 4cd67caa 2022-02-09 op *s = (uint8_t)cp;
85 4cd67caa 2022-02-09 op return 1;
86 4cd67caa 2022-02-09 op } else if (cp <= 0x7FF) {
87 4cd67caa 2022-02-09 op s[1] = (uint8_t)(( cp & 0x3F ) + 0x80);
88 4cd67caa 2022-02-09 op s[0] = (uint8_t)(((cp >> 6) & 0x1F) + 0xC0);
89 4cd67caa 2022-02-09 op return 2;
90 4cd67caa 2022-02-09 op } else if (cp <= 0xFFFF) {
91 4cd67caa 2022-02-09 op s[2] = (uint8_t)(( cp & 0x3F) + 0x80);
92 4cd67caa 2022-02-09 op s[1] = (uint8_t)(((cp >> 6) & 0x3F) + 0x80);
93 4cd67caa 2022-02-09 op s[0] = (uint8_t)(((cp >> 12) & 0x0F) + 0xE0);
94 4cd67caa 2022-02-09 op return 3;
95 4cd67caa 2022-02-09 op } else if (cp <= 0x10FFFF) {
96 4cd67caa 2022-02-09 op s[3] = (uint8_t)(( cp & 0x3F) + 0x80);
97 4cd67caa 2022-02-09 op s[2] = (uint8_t)(((cp >> 6) & 0x3F) + 0x80);
98 4cd67caa 2022-02-09 op s[1] = (uint8_t)(((cp >> 12) & 0x3F) + 0x80);
99 4cd67caa 2022-02-09 op s[0] = (uint8_t)(((cp >> 18) & 0x07) + 0xF0);
100 4cd67caa 2022-02-09 op return 4;
101 4cd67caa 2022-02-09 op } else {
102 4cd67caa 2022-02-09 op s[0] = '\0';
103 4cd67caa 2022-02-09 op return 0;
104 4cd67caa 2022-02-09 op }
105 4cd67caa 2022-02-09 op }
106 4cd67caa 2022-02-09 op
107 4cd67caa 2022-02-09 op char *
108 4cd67caa 2022-02-09 op utf8_nth(char *s, size_t n)
109 4cd67caa 2022-02-09 op {
110 4cd67caa 2022-02-09 op size_t i;
111 4cd67caa 2022-02-09 op uint32_t cp = 0, state = 0;
112 4cd67caa 2022-02-09 op
113 4cd67caa 2022-02-09 op for (i = 0; *s && i < n; ++s)
114 4cd67caa 2022-02-09 op if (!decode(&state, &cp, *s))
115 4cd67caa 2022-02-09 op ++i;
116 4cd67caa 2022-02-09 op
117 4cd67caa 2022-02-09 op if (state != UTF8_ACCEPT)
118 4cd67caa 2022-02-09 op return NULL;
119 4cd67caa 2022-02-09 op if (i == n)
120 4cd67caa 2022-02-09 op return s;
121 4cd67caa 2022-02-09 op return NULL;
122 4cd67caa 2022-02-09 op }
123 4cd67caa 2022-02-09 op
124 4cd67caa 2022-02-09 op size_t
125 4cd67caa 2022-02-09 op utf8_cplen(char *s)
126 4cd67caa 2022-02-09 op {
127 4cd67caa 2022-02-09 op uint32_t cp = 0, state = 0;
128 4cd67caa 2022-02-09 op size_t len;
129 4cd67caa 2022-02-09 op
130 4cd67caa 2022-02-09 op len = 0;
131 4cd67caa 2022-02-09 op for (; *s; ++s)
132 4cd67caa 2022-02-09 op if (!decode(&state, &cp, *s))
133 4cd67caa 2022-02-09 op len++;
134 4cd67caa 2022-02-09 op return len;
135 4cd67caa 2022-02-09 op }
136 4cd67caa 2022-02-09 op
137 4cd67caa 2022-02-09 op /* returns only 0, 1, 2 or 8. assumes sizeof(wchar_t) is 4 */
138 4cd67caa 2022-02-09 op size_t
139 4cd67caa 2022-02-09 op utf8_chwidth(uint32_t cp)
140 4cd67caa 2022-02-09 op {
141 4cd67caa 2022-02-09 op /* XXX: if we're running on a platform where sizeof(wchar_t)
142 4cd67caa 2022-02-09 op * == 2 what to do? The manpage for wcwidth and wcs isn't
143 4cd67caa 2022-02-09 op * clear about the encoding, but if it's 16 bit wide I assume
144 4cd67caa 2022-02-09 op * it must use UTF-16... right? */
145 4cd67caa 2022-02-09 op assert(sizeof(wchar_t) == 4);
146 4cd67caa 2022-02-09 op
147 4cd67caa 2022-02-09 op /*
148 4cd67caa 2022-02-09 op * quick and dirty fix for the tabs. In the future we may
149 4cd67caa 2022-02-09 op * want to expand tabs into N spaces, but for the time being
150 4cd67caa 2022-02-09 op * this seems to be good enough (tm).
151 4cd67caa 2022-02-09 op */
152 4cd67caa 2022-02-09 op if (cp == '\t')
153 4cd67caa 2022-02-09 op return 8;
154 4cd67caa 2022-02-09 op
155 4cd67caa 2022-02-09 op return wcwidth((wchar_t)cp);
156 4cd67caa 2022-02-09 op }
157 4cd67caa 2022-02-09 op
158 4cd67caa 2022-02-09 op /* NOTE: n is the number of codepoints, NOT the byte length. In
159 4cd67caa 2022-02-09 op * other words, s MUST be NUL-terminated. */
160 4cd67caa 2022-02-09 op size_t
161 4cd67caa 2022-02-09 op utf8_snwidth(const char *s, size_t n)
162 4cd67caa 2022-02-09 op {
163 4cd67caa 2022-02-09 op size_t i, tot;
164 4cd67caa 2022-02-09 op uint32_t cp = 0, state = 0;
165 4cd67caa 2022-02-09 op
166 4cd67caa 2022-02-09 op tot = 0;
167 4cd67caa 2022-02-09 op for (i = 0; *s && i < n; ++s)
168 4cd67caa 2022-02-09 op if (!decode(&state, &cp, *s)) {
169 4cd67caa 2022-02-09 op i++;
170 4cd67caa 2022-02-09 op tot += utf8_chwidth(cp);
171 4cd67caa 2022-02-09 op }
172 4cd67caa 2022-02-09 op
173 4cd67caa 2022-02-09 op return tot;
174 4cd67caa 2022-02-09 op }
175 4cd67caa 2022-02-09 op
176 4cd67caa 2022-02-09 op size_t
177 4cd67caa 2022-02-09 op utf8_swidth(const char *s)
178 4cd67caa 2022-02-09 op {
179 4cd67caa 2022-02-09 op size_t tot;
180 4cd67caa 2022-02-09 op uint32_t cp = 0, state = 0;
181 4cd67caa 2022-02-09 op
182 4cd67caa 2022-02-09 op tot = 0;
183 4cd67caa 2022-02-09 op for (; *s; ++s)
184 4cd67caa 2022-02-09 op if (!decode(&state, &cp, *s))
185 4cd67caa 2022-02-09 op tot += utf8_chwidth(cp);
186 4cd67caa 2022-02-09 op
187 4cd67caa 2022-02-09 op return tot;
188 4cd67caa 2022-02-09 op }
189 4cd67caa 2022-02-09 op
190 4cd67caa 2022-02-09 op size_t
191 4cd67caa 2022-02-09 op utf8_swidth_between(const char *str, const char *end)
192 4cd67caa 2022-02-09 op {
193 4cd67caa 2022-02-09 op size_t tot;
194 4cd67caa 2022-02-09 op uint32_t cp = 0, state = 0;
195 4cd67caa 2022-02-09 op
196 4cd67caa 2022-02-09 op tot = 0;
197 4cd67caa 2022-02-09 op for (; *str && str < end; ++str)
198 4cd67caa 2022-02-09 op if (!decode(&state, &cp, *str))
199 4cd67caa 2022-02-09 op tot += utf8_chwidth(cp);
200 4cd67caa 2022-02-09 op return tot;
201 4cd67caa 2022-02-09 op }
202 4cd67caa 2022-02-09 op
203 4cd67caa 2022-02-09 op char *
204 4cd67caa 2022-02-09 op utf8_next_cp(const char *s)
205 4cd67caa 2022-02-09 op {
206 4cd67caa 2022-02-09 op uint32_t cp = 0, state = 0;
207 4cd67caa 2022-02-09 op
208 4cd67caa 2022-02-09 op for (; *s; ++s)
209 4cd67caa 2022-02-09 op if (!decode(&state, &cp, *s))
210 4cd67caa 2022-02-09 op break;
211 4cd67caa 2022-02-09 op return (char*)s+1;
212 4cd67caa 2022-02-09 op }
213 4cd67caa 2022-02-09 op
214 4cd67caa 2022-02-09 op char *
215 4cd67caa 2022-02-09 op utf8_prev_cp(const char *start, const char *base)
216 4cd67caa 2022-02-09 op {
217 4cd67caa 2022-02-09 op uint8_t c;
218 4cd67caa 2022-02-09 op
219 4cd67caa 2022-02-09 op for (; start > base; start--) {
220 4cd67caa 2022-02-09 op c = *start;
221 4cd67caa 2022-02-09 op if ((c & 0xC0) != 0x80)
222 4cd67caa 2022-02-09 op return (char*)start;
223 4cd67caa 2022-02-09 op }
224 4cd67caa 2022-02-09 op
225 4cd67caa 2022-02-09 op return (char*)base;
226 4cd67caa 2022-02-09 op }
227 4cd67caa 2022-02-09 op
228 4cd67caa 2022-02-09 op /*
229 4cd67caa 2022-02-09 op * XXX: This is not correct. There are codepoints classified as
230 4cd67caa 2022-02-09 op * "emoji", but these can be joined toghether to form more complex
231 a24289b5 2022-05-19 op * emoji. There is an official list of what these valid combinations
232 4cd67caa 2022-02-09 op * are, but it would require a costly lookup (a trie can be used to
233 4cd67caa 2022-02-09 op * reduce the times, but...). The following approach is conceptually
234 4cd67caa 2022-02-09 op * simpler: if there is a sequence of "emoji codepoints" (or ZWS) and
235 4cd67caa 2022-02-09 op * then a space, consider everything before the space a single emoji.
236 4cd67caa 2022-02-09 op * It needs a special check for numbers (yes, 0..9 and # are
237 4cd67caa 2022-02-09 op * technically speaking emojis) but otherwise seems to work well in
238 4cd67caa 2022-02-09 op * practice.
239 4cd67caa 2022-02-09 op */
240 4cd67caa 2022-02-09 op int
241 4cd67caa 2022-02-09 op emojied_line(const char *s, const char **space_ret)
242 4cd67caa 2022-02-09 op {
243 4cd67caa 2022-02-09 op uint32_t cp = 0, state = 0;
244 4cd67caa 2022-02-09 op int only_numbers = 1;
245 4cd67caa 2022-02-09 op
246 4cd67caa 2022-02-09 op for (; *s; ++s) {
247 4cd67caa 2022-02-09 op if (!decode(&state, &cp, *s)) {
248 4cd67caa 2022-02-09 op if (cp == ZERO_WIDTH_SPACE)
249 4cd67caa 2022-02-09 op continue;
250 4cd67caa 2022-02-09 op if (cp == ' ') {
251 4cd67caa 2022-02-09 op *space_ret = s;
252 4cd67caa 2022-02-09 op return !only_numbers;
253 4cd67caa 2022-02-09 op }
254 4cd67caa 2022-02-09 op if (!is_emoji(cp))
255 4cd67caa 2022-02-09 op return 0;
256 4cd67caa 2022-02-09 op if (cp < '0' || cp > '9')
257 4cd67caa 2022-02-09 op only_numbers = 0;
258 4cd67caa 2022-02-09 op }
259 4cd67caa 2022-02-09 op }
260 4cd67caa 2022-02-09 op
261 4cd67caa 2022-02-09 op return 0;
262 4cd67caa 2022-02-09 op }