15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29 #include "hb-private.hh"
30
31 #include "hb-mutex-private.hh"
32 #include "hb-object-private.hh"
33
34 #include <locale.h>
35
36
37 /* hb_options_t */
38
39 hb_options_union_t _hb_options;
40
41 void
42 _hb_options_init (void)
43 {
44 hb_options_union_t u;
45 u.i = 0;
46 u.opts.initialized = 1;
47
48 char *c = getenv ("HB_OPTIONS");
49 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
50
51 /* This is idempotent and threadsafe. */
52 _hb_options = u;
53 }
54
65 * Return value:
66 *
67 * Since: 0.9.2
68 **/
69 hb_tag_t
70 hb_tag_from_string (const char *str, int len)
71 {
72 char tag[4];
73 unsigned int i;
74
75 if (!str || !len || !*str)
76 return HB_TAG_NONE;
77
78 if (len < 0 || len > 4)
79 len = 4;
80 for (i = 0; i < (unsigned) len && str[i]; i++)
81 tag[i] = str[i];
82 for (; i < 4; i++)
83 tag[i] = ' ';
84
85 return HB_TAG_CHAR4 (tag);
86 }
87
88 /**
89 * hb_tag_to_string:
90 * @tag:
91 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
92 *
93 *
94 *
95 * Since: 0.9.5
96 **/
97 void
98 hb_tag_to_string (hb_tag_t tag, char *buf)
99 {
100 buf[0] = (char) (uint8_t) (tag >> 24);
101 buf[1] = (char) (uint8_t) (tag >> 16);
102 buf[2] = (char) (uint8_t) (tag >> 8);
103 buf[3] = (char) (uint8_t) (tag >> 0);
104 }
105
169 };
170
171 static const char canon_map[256] = {
172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
175 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
176 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
177 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
178 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
179 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
180 };
181
182 static bool
183 lang_equal (hb_language_t v1,
184 const void *v2)
185 {
186 const unsigned char *p1 = (const unsigned char *) v1;
187 const unsigned char *p2 = (const unsigned char *) v2;
188
189 while (*p1 && *p1 == canon_map[*p2])
190 p1++, p2++;
191
192 return *p1 == canon_map[*p2];
193 }
194
195 #if 0
196 static unsigned int
197 lang_hash (const void *key)
198 {
199 const unsigned char *p = key;
200 unsigned int h = 0;
201 while (canon_map[*p])
202 {
203 h = (h << 5) - h + canon_map[*p];
204 p++;
205 }
206
207 return h;
208 }
209 #endif
210
211
212 struct hb_language_item_t {
213
214 struct hb_language_item_t *next;
215 hb_language_t lang;
216
217 inline bool operator == (const char *s) const {
218 return lang_equal (lang, s);
219 }
220
221 inline hb_language_item_t & operator = (const char *s) {
222 lang = (hb_language_t) strdup (s);
223 for (unsigned char *p = (unsigned char *) lang; *p; p++)
224 *p = canon_map[*p];
225
226 return *this;
227 }
228
229 void finish (void) { free ((void *) lang); }
230 };
231
232
233 /* Thread-safe lock-free language list */
234
235 static hb_language_item_t *langs;
236
237 #ifdef HB_USE_ATEXIT
238 static
239 void free_langs (void)
240 {
241 while (langs) {
242 hb_language_item_t *next = langs->next;
243 langs->finish ();
244 free (langs);
245 langs = next;
246 }
247 }
248 #endif
249
250 static hb_language_item_t *
251 lang_find_or_insert (const char *key)
252 {
253 retry:
254 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
255
256 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
257 if (*lang == key)
258 return lang;
259
260 /* Not found; allocate one. */
261 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
262 if (unlikely (!lang))
263 return NULL;
264 lang->next = first_lang;
265 *lang = key;
266
267 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
268 lang->finish ();
269 free (lang);
270 goto retry;
271 }
272
273 #ifdef HB_USE_ATEXIT
274 if (!first_lang)
275 atexit (free_langs); /* First person registers atexit() callback. */
276 #endif
277
278 return lang;
279 }
280
281
282 /**
283 * hb_language_from_string:
284 * @str: (array length=len) (element-type uint8_t): a string representing
285 * ISO 639 language code
286 * @len: length of the @str, or -1 if it is %NULL-terminated.
287 *
288 * Converts @str representing an ISO 639 language code to the corresponding
289 * #hb_language_t.
290 *
291 * Return value: (transfer none):
292 * The #hb_language_t corresponding to the ISO 639 language code.
293 *
294 * Since: 0.9.2
295 **/
296 hb_language_t
297 hb_language_from_string (const char *str, int len)
298 {
299 if (!str || !len || !*str)
300 return HB_LANGUAGE_INVALID;
301
302 hb_language_item_t *item = NULL;
303 if (len >= 0)
304 {
305 /* NUL-terminate it. */
306 char strbuf[64];
307 len = MIN (len, (int) sizeof (strbuf) - 1);
308 memcpy (strbuf, str, len);
309 strbuf[len] = '\0';
310 item = lang_find_or_insert (strbuf);
311 }
312 else
313 item = lang_find_or_insert (str);
314
315 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
316 }
317
318 /**
319 * hb_language_to_string:
320 * @language: an #hb_language_t to convert.
321 *
322 * See hb_language_from_string().
323 *
324 * Return value: (transfer none):
325 * A %NULL-terminated string representing the @language. Must not be freed by
326 * the caller.
327 *
328 * Since: 0.9.2
329 **/
330 const char *
331 hb_language_to_string (hb_language_t language)
332 {
333 /* This is actually NULL-safe! */
334 return language->s;
335 }
336
337 /**
338 * hb_language_get_default:
339 *
340 *
341 *
342 * Return value: (transfer none):
343 *
344 * Since: 0.9.2
345 **/
346 hb_language_t
347 hb_language_get_default (void)
348 {
349 static hb_language_t default_language = HB_LANGUAGE_INVALID;
350
351 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
352 if (unlikely (language == HB_LANGUAGE_INVALID)) {
353 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
354 (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
355 }
356
357 return default_language;
358 }
359
360
361 /* hb_script_t */
362
363 /**
364 * hb_script_from_iso15924_tag:
365 * @tag: an #hb_tag_t representing an ISO 15924 tag.
366 *
367 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
368 *
369 * Return value:
370 * An #hb_script_t corresponding to the ISO 15924 tag.
371 *
372 * Since: 0.9.2
373 **/
526 hb_bool_t replace)
527 {
528 if (!key)
529 return false;
530
531 if (replace) {
532 if (!data && !destroy) {
533 items.remove (key, lock);
534 return true;
535 }
536 }
537 hb_user_data_item_t item = {key, data, destroy};
538 bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
539
540 return ret;
541 }
542
543 void *
544 hb_user_data_array_t::get (hb_user_data_key_t *key)
545 {
546 hb_user_data_item_t item = {NULL, NULL, NULL};
547
548 return items.find (key, &item, lock) ? item.data : NULL;
549 }
550
551
552 /* hb_version */
553
554 /**
555 * hb_version:
556 * @major: (out): Library major version component.
557 * @minor: (out): Library minor version component.
558 * @micro: (out): Library micro version component.
559 *
560 * Returns library version as three integer components.
561 *
562 * Since: 0.9.2
563 **/
564 void
565 hb_version (unsigned int *major,
566 unsigned int *minor,
567 unsigned int *micro)
568 {
587 }
588
589 /**
590 * hb_version_atleast:
591 * @major:
592 * @minor:
593 * @micro:
594 *
595 *
596 *
597 * Return value:
598 *
599 * Since: 0.9.30
600 **/
601 hb_bool_t
602 hb_version_atleast (unsigned int major,
603 unsigned int minor,
604 unsigned int micro)
605 {
606 return HB_VERSION_ATLEAST (major, minor, micro);
607 }
|
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29 #include "hb-private.hh"
30
31 #include "hb-mutex-private.hh"
32 #include "hb-object-private.hh"
33
34 #include <locale.h>
35 #ifdef HAVE_XLOCALE_H
36 #include <xlocale.h>
37 #endif
38
39
40 /* hb_options_t */
41
42 hb_options_union_t _hb_options;
43
44 void
45 _hb_options_init (void)
46 {
47 hb_options_union_t u;
48 u.i = 0;
49 u.opts.initialized = 1;
50
51 char *c = getenv ("HB_OPTIONS");
52 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
53
54 /* This is idempotent and threadsafe. */
55 _hb_options = u;
56 }
57
68 * Return value:
69 *
70 * Since: 0.9.2
71 **/
72 hb_tag_t
73 hb_tag_from_string (const char *str, int len)
74 {
75 char tag[4];
76 unsigned int i;
77
78 if (!str || !len || !*str)
79 return HB_TAG_NONE;
80
81 if (len < 0 || len > 4)
82 len = 4;
83 for (i = 0; i < (unsigned) len && str[i]; i++)
84 tag[i] = str[i];
85 for (; i < 4; i++)
86 tag[i] = ' ';
87
88 return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
89 }
90
91 /**
92 * hb_tag_to_string:
93 * @tag:
94 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
95 *
96 *
97 *
98 * Since: 0.9.5
99 **/
100 void
101 hb_tag_to_string (hb_tag_t tag, char *buf)
102 {
103 buf[0] = (char) (uint8_t) (tag >> 24);
104 buf[1] = (char) (uint8_t) (tag >> 16);
105 buf[2] = (char) (uint8_t) (tag >> 8);
106 buf[3] = (char) (uint8_t) (tag >> 0);
107 }
108
172 };
173
174 static const char canon_map[256] = {
175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
178 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
179 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
180 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
181 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
182 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
183 };
184
185 static bool
186 lang_equal (hb_language_t v1,
187 const void *v2)
188 {
189 const unsigned char *p1 = (const unsigned char *) v1;
190 const unsigned char *p2 = (const unsigned char *) v2;
191
192 while (*p1 && *p1 == canon_map[*p2]) {
193 p1++;
194 p2++;
195 }
196
197 return *p1 == canon_map[*p2];
198 }
199
200 #if 0
201 static unsigned int
202 lang_hash (const void *key)
203 {
204 const unsigned char *p = key;
205 unsigned int h = 0;
206 while (canon_map[*p])
207 {
208 h = (h << 5) - h + canon_map[*p];
209 p++;
210 }
211
212 return h;
213 }
214 #endif
215
216
217 struct hb_language_item_t {
218
219 struct hb_language_item_t *next;
220 hb_language_t lang;
221
222 inline bool operator == (const char *s) const {
223 return lang_equal (lang, s);
224 }
225
226 inline hb_language_item_t & operator = (const char *s) {
227 /* If a custom allocated is used calling strdup() pairs
228 badly with a call to the custom free() in finish() below.
229 Therefore don't call strdup(), implement its behavior.
230 */
231 size_t len = strlen(s) + 1;
232 lang = (hb_language_t) malloc(len);
233 if (likely (lang))
234 {
235 memcpy((unsigned char *) lang, s, len);
236 for (unsigned char *p = (unsigned char *) lang; *p; p++)
237 *p = canon_map[*p];
238 }
239
240 return *this;
241 }
242
243 void finish (void) { free ((void *) lang); }
244 };
245
246
247 /* Thread-safe lock-free language list */
248
249 static hb_language_item_t *langs;
250
251 #ifdef HB_USE_ATEXIT
252 static void
253 free_langs (void)
254 {
255 while (langs) {
256 hb_language_item_t *next = langs->next;
257 langs->finish ();
258 free (langs);
259 langs = next;
260 }
261 }
262 #endif
263
264 static hb_language_item_t *
265 lang_find_or_insert (const char *key)
266 {
267 retry:
268 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
269
270 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
271 if (*lang == key)
272 return lang;
273
274 /* Not found; allocate one. */
275 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
276 if (unlikely (!lang))
277 return nullptr;
278 lang->next = first_lang;
279 *lang = key;
280 if (unlikely (!lang->lang))
281 {
282 free (lang);
283 return nullptr;
284 }
285
286 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
287 lang->finish ();
288 free (lang);
289 goto retry;
290 }
291
292 #ifdef HB_USE_ATEXIT
293 if (!first_lang)
294 atexit (free_langs); /* First person registers atexit() callback. */
295 #endif
296
297 return lang;
298 }
299
300
301 /**
302 * hb_language_from_string:
303 * @str: (array length=len) (element-type uint8_t): a string representing
304 * ISO 639 language code
305 * @len: length of the @str, or -1 if it is %NULL-terminated.
306 *
307 * Converts @str representing an ISO 639 language code to the corresponding
308 * #hb_language_t.
309 *
310 * Return value: (transfer none):
311 * The #hb_language_t corresponding to the ISO 639 language code.
312 *
313 * Since: 0.9.2
314 **/
315 hb_language_t
316 hb_language_from_string (const char *str, int len)
317 {
318 if (!str || !len || !*str)
319 return HB_LANGUAGE_INVALID;
320
321 hb_language_item_t *item = nullptr;
322 if (len >= 0)
323 {
324 /* NUL-terminate it. */
325 char strbuf[64];
326 len = MIN (len, (int) sizeof (strbuf) - 1);
327 memcpy (strbuf, str, len);
328 strbuf[len] = '\0';
329 item = lang_find_or_insert (strbuf);
330 }
331 else
332 item = lang_find_or_insert (str);
333
334 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
335 }
336
337 /**
338 * hb_language_to_string:
339 * @language: an #hb_language_t to convert.
340 *
341 * See hb_language_from_string().
342 *
343 * Return value: (transfer none):
344 * A %NULL-terminated string representing the @language. Must not be freed by
345 * the caller.
346 *
347 * Since: 0.9.2
348 **/
349 const char *
350 hb_language_to_string (hb_language_t language)
351 {
352 /* This is actually nullptr-safe! */
353 return language->s;
354 }
355
356 /**
357 * hb_language_get_default:
358 *
359 *
360 *
361 * Return value: (transfer none):
362 *
363 * Since: 0.9.2
364 **/
365 hb_language_t
366 hb_language_get_default (void)
367 {
368 static hb_language_t default_language = HB_LANGUAGE_INVALID;
369
370 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
371 if (unlikely (language == HB_LANGUAGE_INVALID)) {
372 language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
373 (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
374 }
375
376 return default_language;
377 }
378
379
380 /* hb_script_t */
381
382 /**
383 * hb_script_from_iso15924_tag:
384 * @tag: an #hb_tag_t representing an ISO 15924 tag.
385 *
386 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
387 *
388 * Return value:
389 * An #hb_script_t corresponding to the ISO 15924 tag.
390 *
391 * Since: 0.9.2
392 **/
545 hb_bool_t replace)
546 {
547 if (!key)
548 return false;
549
550 if (replace) {
551 if (!data && !destroy) {
552 items.remove (key, lock);
553 return true;
554 }
555 }
556 hb_user_data_item_t item = {key, data, destroy};
557 bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
558
559 return ret;
560 }
561
562 void *
563 hb_user_data_array_t::get (hb_user_data_key_t *key)
564 {
565 hb_user_data_item_t item = {nullptr, nullptr, nullptr};
566
567 return items.find (key, &item, lock) ? item.data : nullptr;
568 }
569
570
571 /* hb_version */
572
573 /**
574 * hb_version:
575 * @major: (out): Library major version component.
576 * @minor: (out): Library minor version component.
577 * @micro: (out): Library micro version component.
578 *
579 * Returns library version as three integer components.
580 *
581 * Since: 0.9.2
582 **/
583 void
584 hb_version (unsigned int *major,
585 unsigned int *minor,
586 unsigned int *micro)
587 {
606 }
607
608 /**
609 * hb_version_atleast:
610 * @major:
611 * @minor:
612 * @micro:
613 *
614 *
615 *
616 * Return value:
617 *
618 * Since: 0.9.30
619 **/
620 hb_bool_t
621 hb_version_atleast (unsigned int major,
622 unsigned int minor,
623 unsigned int micro)
624 {
625 return HB_VERSION_ATLEAST (major, minor, micro);
626 }
627
628
629
630 /* hb_feature_t and hb_variation_t */
631
632 static bool
633 parse_space (const char **pp, const char *end)
634 {
635 while (*pp < end && ISSPACE (**pp))
636 (*pp)++;
637 return true;
638 }
639
640 static bool
641 parse_char (const char **pp, const char *end, char c)
642 {
643 parse_space (pp, end);
644
645 if (*pp == end || **pp != c)
646 return false;
647
648 (*pp)++;
649 return true;
650 }
651
652 static bool
653 parse_uint (const char **pp, const char *end, unsigned int *pv)
654 {
655 char buf[32];
656 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
657 strncpy (buf, *pp, len);
658 buf[len] = '\0';
659
660 char *p = buf;
661 char *pend = p;
662 unsigned int v;
663
664 /* Intentionally use strtol instead of strtoul, such that
665 * -1 turns into "big number"... */
666 errno = 0;
667 v = strtol (p, &pend, 0);
668 if (errno || p == pend)
669 return false;
670
671 *pv = v;
672 *pp += pend - p;
673 return true;
674 }
675
676 static bool
677 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
678 {
679 char buf[32];
680 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
681 strncpy (buf, *pp, len);
682 buf[len] = '\0';
683
684 char *p = buf;
685 char *pend = p;
686 unsigned int v;
687
688 /* Intentionally use strtol instead of strtoul, such that
689 * -1 turns into "big number"... */
690 errno = 0;
691 v = strtol (p, &pend, 0);
692 if (errno || p == pend)
693 return false;
694
695 *pv = v;
696 *pp += pend - p;
697 return true;
698 }
699
700 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
701 #define USE_XLOCALE 1
702 #define HB_LOCALE_T locale_t
703 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
704 #define HB_FREE_LOCALE(loc) freelocale (loc)
705 #elif defined(_MSC_VER)
706 #define USE_XLOCALE 1
707 #define HB_LOCALE_T _locale_t
708 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
709 #define HB_FREE_LOCALE(loc) _free_locale (loc)
710 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
711 #endif
712
713 #ifdef USE_XLOCALE
714
715 static HB_LOCALE_T C_locale;
716
717 #ifdef HB_USE_ATEXIT
718 static void
719 free_C_locale (void)
720 {
721 if (C_locale)
722 HB_FREE_LOCALE (C_locale);
723 }
724 #endif
725
726 static HB_LOCALE_T
727 get_C_locale (void)
728 {
729 retry:
730 HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
731
732 if (unlikely (!C))
733 {
734 C = HB_CREATE_LOCALE ("C");
735
736 if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
737 {
738 HB_FREE_LOCALE (C_locale);
739 goto retry;
740 }
741
742 #ifdef HB_USE_ATEXIT
743 atexit (free_C_locale); /* First person registers atexit() callback. */
744 #endif
745 }
746
747 return C;
748 }
749 #endif
750
751 static bool
752 parse_float (const char **pp, const char *end, float *pv)
753 {
754 char buf[32];
755 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
756 strncpy (buf, *pp, len);
757 buf[len] = '\0';
758
759 char *p = buf;
760 char *pend = p;
761 float v;
762
763 errno = 0;
764 #ifdef USE_XLOCALE
765 v = strtod_l (p, &pend, get_C_locale ());
766 #else
767 v = strtod (p, &pend);
768 #endif
769 if (errno || p == pend)
770 return false;
771
772 *pv = v;
773 *pp += pend - p;
774 return true;
775 }
776
777 static bool
778 parse_bool (const char **pp, const char *end, uint32_t *pv)
779 {
780 parse_space (pp, end);
781
782 const char *p = *pp;
783 while (*pp < end && ISALPHA(**pp))
784 (*pp)++;
785
786 /* CSS allows on/off as aliases 1/0. */
787 if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
788 *pv = 1;
789 else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
790 *pv = 0;
791 else
792 return false;
793
794 return true;
795 }
796
797 /* hb_feature_t */
798
799 static bool
800 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
801 {
802 if (parse_char (pp, end, '-'))
803 feature->value = 0;
804 else {
805 parse_char (pp, end, '+');
806 feature->value = 1;
807 }
808
809 return true;
810 }
811
812 static bool
813 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
814 {
815 parse_space (pp, end);
816
817 char quote = 0;
818
819 if (*pp < end && (**pp == '\'' || **pp == '"'))
820 {
821 quote = **pp;
822 (*pp)++;
823 }
824
825 const char *p = *pp;
826 while (*pp < end && ISALNUM(**pp))
827 (*pp)++;
828
829 if (p == *pp || *pp - p > 4)
830 return false;
831
832 *tag = hb_tag_from_string (p, *pp - p);
833
834 if (quote)
835 {
836 /* CSS expects exactly four bytes. And we only allow quotations for
837 * CSS compatibility. So, enforce the length. */
838 if (*pp - p != 4)
839 return false;
840 if (*pp == end || **pp != quote)
841 return false;
842 (*pp)++;
843 }
844
845 return true;
846 }
847
848 static bool
849 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
850 {
851 parse_space (pp, end);
852
853 bool has_start;
854
855 feature->start = 0;
856 feature->end = (unsigned int) -1;
857
858 if (!parse_char (pp, end, '['))
859 return true;
860
861 has_start = parse_uint (pp, end, &feature->start);
862
863 if (parse_char (pp, end, ':')) {
864 parse_uint (pp, end, &feature->end);
865 } else {
866 if (has_start)
867 feature->end = feature->start + 1;
868 }
869
870 return parse_char (pp, end, ']');
871 }
872
873 static bool
874 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
875 {
876 bool had_equal = parse_char (pp, end, '=');
877 bool had_value = parse_uint32 (pp, end, &feature->value) ||
878 parse_bool (pp, end, &feature->value);
879 /* CSS doesn't use equal-sign between tag and value.
880 * If there was an equal-sign, then there *must* be a value.
881 * A value without an eqaul-sign is ok, but not required. */
882 return !had_equal || had_value;
883 }
884
885 static bool
886 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
887 {
888 return parse_feature_value_prefix (pp, end, feature) &&
889 parse_tag (pp, end, &feature->tag) &&
890 parse_feature_indices (pp, end, feature) &&
891 parse_feature_value_postfix (pp, end, feature) &&
892 parse_space (pp, end) &&
893 *pp == end;
894 }
895
896 /**
897 * hb_feature_from_string:
898 * @str: (array length=len) (element-type uint8_t): a string to parse
899 * @len: length of @str, or -1 if string is %NULL terminated
900 * @feature: (out): the #hb_feature_t to initialize with the parsed values
901 *
902 * Parses a string into a #hb_feature_t.
903 *
904 * TODO: document the syntax here.
905 *
906 * Return value:
907 * %true if @str is successfully parsed, %false otherwise.
908 *
909 * Since: 0.9.5
910 **/
911 hb_bool_t
912 hb_feature_from_string (const char *str, int len,
913 hb_feature_t *feature)
914 {
915 hb_feature_t feat;
916
917 if (len < 0)
918 len = strlen (str);
919
920 if (likely (parse_one_feature (&str, str + len, &feat)))
921 {
922 if (feature)
923 *feature = feat;
924 return true;
925 }
926
927 if (feature)
928 memset (feature, 0, sizeof (*feature));
929 return false;
930 }
931
932 /**
933 * hb_feature_to_string:
934 * @feature: an #hb_feature_t to convert
935 * @buf: (array length=size) (out): output string
936 * @size: the allocated size of @buf
937 *
938 * Converts a #hb_feature_t into a %NULL-terminated string in the format
939 * understood by hb_feature_from_string(). The client in responsible for
940 * allocating big enough size for @buf, 128 bytes is more than enough.
941 *
942 * Since: 0.9.5
943 **/
944 void
945 hb_feature_to_string (hb_feature_t *feature,
946 char *buf, unsigned int size)
947 {
948 if (unlikely (!size)) return;
949
950 char s[128];
951 unsigned int len = 0;
952 if (feature->value == 0)
953 s[len++] = '-';
954 hb_tag_to_string (feature->tag, s + len);
955 len += 4;
956 while (len && s[len - 1] == ' ')
957 len--;
958 if (feature->start != 0 || feature->end != (unsigned int) -1)
959 {
960 s[len++] = '[';
961 if (feature->start)
962 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
963 if (feature->end != feature->start + 1) {
964 s[len++] = ':';
965 if (feature->end != (unsigned int) -1)
966 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
967 }
968 s[len++] = ']';
969 }
970 if (feature->value > 1)
971 {
972 s[len++] = '=';
973 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
974 }
975 assert (len < ARRAY_LENGTH (s));
976 len = MIN (len, size - 1);
977 memcpy (buf, s, len);
978 buf[len] = '\0';
979 }
980
981 /* hb_variation_t */
982
983 static bool
984 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
985 {
986 parse_char (pp, end, '='); /* Optional. */
987 return parse_float (pp, end, &variation->value);
988 }
989
990 static bool
991 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
992 {
993 return parse_tag (pp, end, &variation->tag) &&
994 parse_variation_value (pp, end, variation) &&
995 parse_space (pp, end) &&
996 *pp == end;
997 }
998
999 /**
1000 * hb_variation_from_string:
1001 *
1002 * Since: 1.4.2
1003 */
1004 hb_bool_t
1005 hb_variation_from_string (const char *str, int len,
1006 hb_variation_t *variation)
1007 {
1008 hb_variation_t var;
1009
1010 if (len < 0)
1011 len = strlen (str);
1012
1013 if (likely (parse_one_variation (&str, str + len, &var)))
1014 {
1015 if (variation)
1016 *variation = var;
1017 return true;
1018 }
1019
1020 if (variation)
1021 memset (variation, 0, sizeof (*variation));
1022 return false;
1023 }
1024
1025 /**
1026 * hb_variation_to_string:
1027 *
1028 * Since: 1.4.2
1029 */
1030 void
1031 hb_variation_to_string (hb_variation_t *variation,
1032 char *buf, unsigned int size)
1033 {
1034 if (unlikely (!size)) return;
1035
1036 char s[128];
1037 unsigned int len = 0;
1038 hb_tag_to_string (variation->tag, s + len);
1039 len += 4;
1040 while (len && s[len - 1] == ' ')
1041 len--;
1042 s[len++] = '=';
1043 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
1044
1045 assert (len < ARRAY_LENGTH (s));
1046 len = MIN (len, size - 1);
1047 memcpy (buf, s, len);
1048 buf[len] = '\0';
1049 }
|