• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2009,2010  Red Hat, Inc.
3  * Copyright © 2011,2012  Google, Inc.
4  *
5  *  This is part of HarfBuzz, a text shaping library.
6  *
7  * Permission is hereby granted, without written agreement and without
8  * license or royalty fees, to use, copy, modify, and distribute this
9  * software and its documentation for any purpose, provided that the
10  * above copyright notice and the following two paragraphs appear in
11  * all copies of this software.
12  *
13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
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
_hb_options_init(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 
58 
59 /* hb_tag_t */
60 
61 /**
62  * hb_tag_from_string:
63  * @str: (array length=len) (element-type uint8_t):
64  * @len:
65  *
66  *
67  *
68  * Return value:
69  *
70  * Since: 0.9.2
71  **/
72 hb_tag_t
hb_tag_from_string(const char * str,int len)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
hb_tag_to_string(hb_tag_t tag,char * buf)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 
109 
110 /* hb_direction_t */
111 
112 const char direction_strings[][4] = {
113   "ltr",
114   "rtl",
115   "ttb",
116   "btt"
117 };
118 
119 /**
120  * hb_direction_from_string:
121  * @str: (array length=len) (element-type uint8_t):
122  * @len:
123  *
124  *
125  *
126  * Return value:
127  *
128  * Since: 0.9.2
129  **/
130 hb_direction_t
hb_direction_from_string(const char * str,int len)131 hb_direction_from_string (const char *str, int len)
132 {
133   if (unlikely (!str || !len || !*str))
134     return HB_DIRECTION_INVALID;
135 
136   /* Lets match loosely: just match the first letter, such that
137    * all of "ltr", "left-to-right", etc work!
138    */
139   char c = TOLOWER (str[0]);
140   for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
141     if (c == direction_strings[i][0])
142       return (hb_direction_t) (HB_DIRECTION_LTR + i);
143 
144   return HB_DIRECTION_INVALID;
145 }
146 
147 /**
148  * hb_direction_to_string:
149  * @direction:
150  *
151  *
152  *
153  * Return value: (transfer none):
154  *
155  * Since: 0.9.2
156  **/
157 const char *
hb_direction_to_string(hb_direction_t direction)158 hb_direction_to_string (hb_direction_t direction)
159 {
160   if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
161 	      < ARRAY_LENGTH (direction_strings)))
162     return direction_strings[direction - HB_DIRECTION_LTR];
163 
164   return "invalid";
165 }
166 
167 
168 /* hb_language_t */
169 
170 struct hb_language_impl_t {
171   const char s[1];
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
lang_equal(hb_language_t v1,const void * v2)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 
operator ==hb_language_item_t222   inline bool operator == (const char *s) const {
223     return lang_equal (lang, s);
224   }
225 
operator =hb_language_item_t226   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 
finishhb_language_item_t243   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
free_langs(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 *
lang_find_or_insert(const char * key)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
hb_language_from_string(const char * str,int len)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 *
hb_language_to_string(hb_language_t language)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
hb_language_get_default(void)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  **/
393 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)394 hb_script_from_iso15924_tag (hb_tag_t tag)
395 {
396   if (unlikely (tag == HB_TAG_NONE))
397     return HB_SCRIPT_INVALID;
398 
399   /* Be lenient, adjust case (one capital letter followed by three small letters) */
400   tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
401 
402   switch (tag) {
403 
404     /* These graduated from the 'Q' private-area codes, but
405      * the old code is still aliased by Unicode, and the Qaai
406      * one in use by ICU. */
407     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
408     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
409 
410     /* Script variants from http://unicode.org/iso15924/ */
411     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
412     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
413     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
414     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
415     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
416     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
417   }
418 
419   /* If it looks right, just use the tag as a script */
420   if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
421     return (hb_script_t) tag;
422 
423   /* Otherwise, return unknown */
424   return HB_SCRIPT_UNKNOWN;
425 }
426 
427 /**
428  * hb_script_from_string:
429  * @str: (array length=len) (element-type uint8_t): a string representing an
430  *       ISO 15924 tag.
431  * @len: length of the @str, or -1 if it is %NULL-terminated.
432  *
433  * Converts a string @str representing an ISO 15924 script tag to a
434  * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
435  * hb_script_from_iso15924_tag().
436  *
437  * Return value:
438  * An #hb_script_t corresponding to the ISO 15924 tag.
439  *
440  * Since: 0.9.2
441  **/
442 hb_script_t
hb_script_from_string(const char * str,int len)443 hb_script_from_string (const char *str, int len)
444 {
445   return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
446 }
447 
448 /**
449  * hb_script_to_iso15924_tag:
450  * @script: an #hb_script_ to convert.
451  *
452  * See hb_script_from_iso15924_tag().
453  *
454  * Return value:
455  * An #hb_tag_t representing an ISO 15924 script tag.
456  *
457  * Since: 0.9.2
458  **/
459 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)460 hb_script_to_iso15924_tag (hb_script_t script)
461 {
462   return (hb_tag_t) script;
463 }
464 
465 /**
466  * hb_script_get_horizontal_direction:
467  * @script:
468  *
469  *
470  *
471  * Return value:
472  *
473  * Since: 0.9.2
474  **/
475 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)476 hb_script_get_horizontal_direction (hb_script_t script)
477 {
478   /* http://goo.gl/x9ilM */
479   switch ((hb_tag_t) script)
480   {
481     /* Unicode-1.1 additions */
482     case HB_SCRIPT_ARABIC:
483     case HB_SCRIPT_HEBREW:
484 
485     /* Unicode-3.0 additions */
486     case HB_SCRIPT_SYRIAC:
487     case HB_SCRIPT_THAANA:
488 
489     /* Unicode-4.0 additions */
490     case HB_SCRIPT_CYPRIOT:
491 
492     /* Unicode-4.1 additions */
493     case HB_SCRIPT_KHAROSHTHI:
494 
495     /* Unicode-5.0 additions */
496     case HB_SCRIPT_PHOENICIAN:
497     case HB_SCRIPT_NKO:
498 
499     /* Unicode-5.1 additions */
500     case HB_SCRIPT_LYDIAN:
501 
502     /* Unicode-5.2 additions */
503     case HB_SCRIPT_AVESTAN:
504     case HB_SCRIPT_IMPERIAL_ARAMAIC:
505     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
506     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
507     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
508     case HB_SCRIPT_OLD_TURKIC:
509     case HB_SCRIPT_SAMARITAN:
510 
511     /* Unicode-6.0 additions */
512     case HB_SCRIPT_MANDAIC:
513 
514     /* Unicode-6.1 additions */
515     case HB_SCRIPT_MEROITIC_CURSIVE:
516     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
517 
518     /* Unicode-7.0 additions */
519     case HB_SCRIPT_MANICHAEAN:
520     case HB_SCRIPT_MENDE_KIKAKUI:
521     case HB_SCRIPT_NABATAEAN:
522     case HB_SCRIPT_OLD_NORTH_ARABIAN:
523     case HB_SCRIPT_PALMYRENE:
524     case HB_SCRIPT_PSALTER_PAHLAVI:
525 
526     /* Unicode-8.0 additions */
527     case HB_SCRIPT_OLD_HUNGARIAN:
528 
529     /* Unicode-9.0 additions */
530     case HB_SCRIPT_ADLAM:
531 
532       return HB_DIRECTION_RTL;
533   }
534 
535   return HB_DIRECTION_LTR;
536 }
537 
538 
539 /* hb_user_data_array_t */
540 
541 bool
set(hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)542 hb_user_data_array_t::set (hb_user_data_key_t *key,
543 			   void *              data,
544 			   hb_destroy_func_t   destroy,
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 *
get(hb_user_data_key_t * key)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
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)584 hb_version (unsigned int *major,
585 	    unsigned int *minor,
586 	    unsigned int *micro)
587 {
588   *major = HB_VERSION_MAJOR;
589   *minor = HB_VERSION_MINOR;
590   *micro = HB_VERSION_MICRO;
591 }
592 
593 /**
594  * hb_version_string:
595  *
596  * Returns library version as a string with three components.
597  *
598  * Return value: library version string.
599  *
600  * Since: 0.9.2
601  **/
602 const char *
hb_version_string(void)603 hb_version_string (void)
604 {
605   return HB_VERSION_STRING;
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
hb_version_atleast(unsigned int major,unsigned int minor,unsigned int micro)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
parse_space(const char ** pp,const char * end)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
parse_char(const char ** pp,const char * end,char c)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
parse_uint(const char ** pp,const char * end,unsigned int * pv)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
parse_uint32(const char ** pp,const char * end,uint32_t * pv)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
free_C_locale(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
get_C_locale(void)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
parse_float(const char ** pp,const char * end,float * pv)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
parse_bool(const char ** pp,const char * end,uint32_t * pv)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", 3))
790     *pv = 0;
791   else
792     return false;
793 
794   return true;
795 }
796 
797 /* hb_feature_t */
798 
799 static bool
parse_feature_value_prefix(const char ** pp,const char * end,hb_feature_t * feature)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
parse_tag(const char ** pp,const char * end,hb_tag_t * tag)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
parse_feature_indices(const char ** pp,const char * end,hb_feature_t * feature)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
parse_feature_value_postfix(const char ** pp,const char * end,hb_feature_t * feature)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
parse_one_feature(const char ** pp,const char * end,hb_feature_t * feature)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
hb_feature_from_string(const char * str,int len,hb_feature_t * feature)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
hb_feature_to_string(hb_feature_t * feature,char * buf,unsigned int size)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
parse_variation_value(const char ** pp,const char * end,hb_variation_t * variation)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
parse_one_variation(const char ** pp,const char * end,hb_variation_t * variation)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
hb_variation_from_string(const char * str,int len,hb_variation_t * variation)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
hb_variation_to_string(hb_variation_t * variation,char * buf,unsigned int size)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 }
1050