Home | History | Annotate | Download | only in src
      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
     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
     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 
    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
    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 *
    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
    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  *       ISO639 language code
    305  * @len: length of the @str, or -1 if it is %NULL-terminated.
    306  *
    307  * Converts @str representing an ISO639 language code to the corresponding
    308  * #hb_language_t.
    309  *
    310  * Return value: (transfer none):
    311  * The #hb_language_t corresponding to the ISO639 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 ISO15924 tag.
    385  *
    386  * Converts an ISO15924 script tag to a corresponding #hb_script_t.
    387  *
    388  * Return value:
    389  * An #hb_script_t corresponding to the ISO15924 tag.
    390  *
    391  * Since: 0.9.2
    392  **/
    393 hb_script_t
    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  *       ISO15924 tag.
    431  * @len: length of the @str, or -1 if it is %NULL-terminated.
    432  *
    433  * Converts a string @str representing an ISO15924 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 ISO15924 tag.
    439  *
    440  * Since: 0.9.2
    441  **/
    442 hb_script_t
    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 ISO15924 script tag.
    456  *
    457  * Since: 0.9.2
    458  **/
    459 hb_tag_t
    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
    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
    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 *
    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 {
    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 *
    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
    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", 3))
    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 }
   1050