a67fcf85e3d431a7fec01b649bd36450a762cfc6
[platform/upstream/harfbuzz.git] / src / hb-common.cc
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 fini() 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 fini (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 retry:
256   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
257   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, nullptr))
258     goto retry;
259
260   while (first_lang) {
261     hb_language_item_t *next = first_lang->next;
262     first_lang->fini ();
263     free (first_lang);
264     first_lang = next;
265   }
266 }
267 #endif
268
269 static hb_language_item_t *
270 lang_find_or_insert (const char *key)
271 {
272 retry:
273   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
274
275   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
276     if (*lang == key)
277       return lang;
278
279   /* Not found; allocate one. */
280   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
281   if (unlikely (!lang))
282     return nullptr;
283   lang->next = first_lang;
284   *lang = key;
285   if (unlikely (!lang->lang))
286   {
287     free (lang);
288     return nullptr;
289   }
290
291   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
292     lang->fini ();
293     free (lang);
294     goto retry;
295   }
296
297 #ifdef HB_USE_ATEXIT
298   if (!first_lang)
299     atexit (free_langs); /* First person registers atexit() callback. */
300 #endif
301
302   return lang;
303 }
304
305
306 /**
307  * hb_language_from_string:
308  * @str: (array length=len) (element-type uint8_t): a string representing
309  *       ISO 639 language code
310  * @len: length of the @str, or -1 if it is %NULL-terminated.
311  *
312  * Converts @str representing an ISO 639 language code to the corresponding
313  * #hb_language_t.
314  *
315  * Return value: (transfer none):
316  * The #hb_language_t corresponding to the ISO 639 language code.
317  *
318  * Since: 0.9.2
319  **/
320 hb_language_t
321 hb_language_from_string (const char *str, int len)
322 {
323   if (!str || !len || !*str)
324     return HB_LANGUAGE_INVALID;
325
326   hb_language_item_t *item = nullptr;
327   if (len >= 0)
328   {
329     /* NUL-terminate it. */
330     char strbuf[64];
331     len = MIN (len, (int) sizeof (strbuf) - 1);
332     memcpy (strbuf, str, len);
333     strbuf[len] = '\0';
334     item = lang_find_or_insert (strbuf);
335   }
336   else
337     item = lang_find_or_insert (str);
338
339   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
340 }
341
342 /**
343  * hb_language_to_string:
344  * @language: an #hb_language_t to convert.
345  *
346  * See hb_language_from_string().
347  *
348  * Return value: (transfer none):
349  * A %NULL-terminated string representing the @language. Must not be freed by
350  * the caller.
351  *
352  * Since: 0.9.2
353  **/
354 const char *
355 hb_language_to_string (hb_language_t language)
356 {
357   /* This is actually nullptr-safe! */
358   return language->s;
359 }
360
361 /**
362  * hb_language_get_default:
363  *
364  *
365  *
366  * Return value: (transfer none):
367  *
368  * Since: 0.9.2
369  **/
370 hb_language_t
371 hb_language_get_default (void)
372 {
373   static hb_language_t default_language = HB_LANGUAGE_INVALID;
374
375   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
376   if (unlikely (language == HB_LANGUAGE_INVALID)) {
377     language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
378     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
379   }
380
381   return default_language;
382 }
383
384
385 /* hb_script_t */
386
387 /**
388  * hb_script_from_iso15924_tag:
389  * @tag: an #hb_tag_t representing an ISO 15924 tag.
390  *
391  * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
392  *
393  * Return value:
394  * An #hb_script_t corresponding to the ISO 15924 tag.
395  *
396  * Since: 0.9.2
397  **/
398 hb_script_t
399 hb_script_from_iso15924_tag (hb_tag_t tag)
400 {
401   if (unlikely (tag == HB_TAG_NONE))
402     return HB_SCRIPT_INVALID;
403
404   /* Be lenient, adjust case (one capital letter followed by three small letters) */
405   tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
406
407   switch (tag) {
408
409     /* These graduated from the 'Q' private-area codes, but
410      * the old code is still aliased by Unicode, and the Qaai
411      * one in use by ICU. */
412     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
413     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
414
415     /* Script variants from https://unicode.org/iso15924/ */
416     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
417     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
418     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
419     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
420     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
421     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
422   }
423
424   /* If it looks right, just use the tag as a script */
425   if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
426     return (hb_script_t) tag;
427
428   /* Otherwise, return unknown */
429   return HB_SCRIPT_UNKNOWN;
430 }
431
432 /**
433  * hb_script_from_string:
434  * @str: (array length=len) (element-type uint8_t): a string representing an
435  *       ISO 15924 tag.
436  * @len: length of the @str, or -1 if it is %NULL-terminated.
437  *
438  * Converts a string @str representing an ISO 15924 script tag to a
439  * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
440  * hb_script_from_iso15924_tag().
441  *
442  * Return value:
443  * An #hb_script_t corresponding to the ISO 15924 tag.
444  *
445  * Since: 0.9.2
446  **/
447 hb_script_t
448 hb_script_from_string (const char *str, int len)
449 {
450   return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
451 }
452
453 /**
454  * hb_script_to_iso15924_tag:
455  * @script: an #hb_script_ to convert.
456  *
457  * See hb_script_from_iso15924_tag().
458  *
459  * Return value:
460  * An #hb_tag_t representing an ISO 15924 script tag.
461  *
462  * Since: 0.9.2
463  **/
464 hb_tag_t
465 hb_script_to_iso15924_tag (hb_script_t script)
466 {
467   return (hb_tag_t) script;
468 }
469
470 /**
471  * hb_script_get_horizontal_direction:
472  * @script:
473  *
474  *
475  *
476  * Return value:
477  *
478  * Since: 0.9.2
479  **/
480 hb_direction_t
481 hb_script_get_horizontal_direction (hb_script_t script)
482 {
483   /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
484   switch ((hb_tag_t) script)
485   {
486     /* Unicode-1.1 additions */
487     case HB_SCRIPT_ARABIC:
488     case HB_SCRIPT_HEBREW:
489
490     /* Unicode-3.0 additions */
491     case HB_SCRIPT_SYRIAC:
492     case HB_SCRIPT_THAANA:
493
494     /* Unicode-4.0 additions */
495     case HB_SCRIPT_CYPRIOT:
496
497     /* Unicode-4.1 additions */
498     case HB_SCRIPT_KHAROSHTHI:
499
500     /* Unicode-5.0 additions */
501     case HB_SCRIPT_PHOENICIAN:
502     case HB_SCRIPT_NKO:
503
504     /* Unicode-5.1 additions */
505     case HB_SCRIPT_LYDIAN:
506
507     /* Unicode-5.2 additions */
508     case HB_SCRIPT_AVESTAN:
509     case HB_SCRIPT_IMPERIAL_ARAMAIC:
510     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
511     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
512     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
513     case HB_SCRIPT_OLD_TURKIC:
514     case HB_SCRIPT_SAMARITAN:
515
516     /* Unicode-6.0 additions */
517     case HB_SCRIPT_MANDAIC:
518
519     /* Unicode-6.1 additions */
520     case HB_SCRIPT_MEROITIC_CURSIVE:
521     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
522
523     /* Unicode-7.0 additions */
524     case HB_SCRIPT_MANICHAEAN:
525     case HB_SCRIPT_MENDE_KIKAKUI:
526     case HB_SCRIPT_NABATAEAN:
527     case HB_SCRIPT_OLD_NORTH_ARABIAN:
528     case HB_SCRIPT_PALMYRENE:
529     case HB_SCRIPT_PSALTER_PAHLAVI:
530
531     /* Unicode-8.0 additions */
532     case HB_SCRIPT_HATRAN:
533     case HB_SCRIPT_OLD_HUNGARIAN:
534
535     /* Unicode-9.0 additions */
536     case HB_SCRIPT_ADLAM:
537
538     /* Unicode-11.0 additions */
539     case HB_SCRIPT_HANIFI_ROHINGYA:
540     case HB_SCRIPT_OLD_SOGDIAN:
541     case HB_SCRIPT_SOGDIAN:
542
543       return HB_DIRECTION_RTL;
544
545
546     /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
547     case HB_SCRIPT_OLD_ITALIC:
548
549       return HB_DIRECTION_INVALID;
550   }
551
552   return HB_DIRECTION_LTR;
553 }
554
555
556 /* hb_user_data_array_t */
557
558 bool
559 hb_user_data_array_t::set (hb_user_data_key_t *key,
560                            void *              data,
561                            hb_destroy_func_t   destroy,
562                            hb_bool_t           replace)
563 {
564   if (!key)
565     return false;
566
567   if (replace) {
568     if (!data && !destroy) {
569       items.remove (key, lock);
570       return true;
571     }
572   }
573   hb_user_data_item_t item = {key, data, destroy};
574   bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
575
576   return ret;
577 }
578
579 void *
580 hb_user_data_array_t::get (hb_user_data_key_t *key)
581 {
582   hb_user_data_item_t item = {nullptr, nullptr, nullptr};
583
584   return items.find (key, &item, lock) ? item.data : nullptr;
585 }
586
587
588 /* hb_version */
589
590 /**
591  * hb_version:
592  * @major: (out): Library major version component.
593  * @minor: (out): Library minor version component.
594  * @micro: (out): Library micro version component.
595  *
596  * Returns library version as three integer components.
597  *
598  * Since: 0.9.2
599  **/
600 void
601 hb_version (unsigned int *major,
602             unsigned int *minor,
603             unsigned int *micro)
604 {
605   *major = HB_VERSION_MAJOR;
606   *minor = HB_VERSION_MINOR;
607   *micro = HB_VERSION_MICRO;
608 }
609
610 /**
611  * hb_version_string:
612  *
613  * Returns library version as a string with three components.
614  *
615  * Return value: library version string.
616  *
617  * Since: 0.9.2
618  **/
619 const char *
620 hb_version_string (void)
621 {
622   return HB_VERSION_STRING;
623 }
624
625 /**
626  * hb_version_atleast:
627  * @major:
628  * @minor:
629  * @micro:
630  *
631  *
632  *
633  * Return value:
634  *
635  * Since: 0.9.30
636  **/
637 hb_bool_t
638 hb_version_atleast (unsigned int major,
639                     unsigned int minor,
640                     unsigned int micro)
641 {
642   return HB_VERSION_ATLEAST (major, minor, micro);
643 }
644
645
646
647 /* hb_feature_t and hb_variation_t */
648
649 static bool
650 parse_space (const char **pp, const char *end)
651 {
652   while (*pp < end && ISSPACE (**pp))
653     (*pp)++;
654   return true;
655 }
656
657 static bool
658 parse_char (const char **pp, const char *end, char c)
659 {
660   parse_space (pp, end);
661
662   if (*pp == end || **pp != c)
663     return false;
664
665   (*pp)++;
666   return true;
667 }
668
669 static bool
670 parse_uint (const char **pp, const char *end, unsigned int *pv)
671 {
672   char buf[32];
673   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
674   strncpy (buf, *pp, len);
675   buf[len] = '\0';
676
677   char *p = buf;
678   char *pend = p;
679   unsigned int v;
680
681   /* Intentionally use strtol instead of strtoul, such that
682    * -1 turns into "big number"... */
683   errno = 0;
684   v = strtol (p, &pend, 0);
685   if (errno || p == pend)
686     return false;
687
688   *pv = v;
689   *pp += pend - p;
690   return true;
691 }
692
693 static bool
694 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
695 {
696   char buf[32];
697   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
698   strncpy (buf, *pp, len);
699   buf[len] = '\0';
700
701   char *p = buf;
702   char *pend = p;
703   unsigned int v;
704
705   /* Intentionally use strtol instead of strtoul, such that
706    * -1 turns into "big number"... */
707   errno = 0;
708   v = strtol (p, &pend, 0);
709   if (errno || p == pend)
710     return false;
711
712   *pv = v;
713   *pp += pend - p;
714   return true;
715 }
716
717 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
718 #define USE_XLOCALE 1
719 #define HB_LOCALE_T locale_t
720 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
721 #define HB_FREE_LOCALE(loc) freelocale (loc)
722 #elif defined(_MSC_VER)
723 #define USE_XLOCALE 1
724 #define HB_LOCALE_T _locale_t
725 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
726 #define HB_FREE_LOCALE(loc) _free_locale (loc)
727 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
728 #endif
729
730 #ifdef USE_XLOCALE
731
732 static HB_LOCALE_T C_locale;
733
734 #ifdef HB_USE_ATEXIT
735 static void
736 free_C_locale (void)
737 {
738 retry:
739   HB_LOCALE_T locale = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
740
741   if (!hb_atomic_ptr_cmpexch (&C_locale, locale, nullptr))
742     goto retry;
743
744   if (locale)
745     HB_FREE_LOCALE (locale);
746 }
747 #endif
748
749 static HB_LOCALE_T
750 get_C_locale (void)
751 {
752 retry:
753   HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
754
755   if (unlikely (!C))
756   {
757     C = HB_CREATE_LOCALE ("C");
758
759     if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
760     {
761       HB_FREE_LOCALE (C_locale);
762       goto retry;
763     }
764
765 #ifdef HB_USE_ATEXIT
766     atexit (free_C_locale); /* First person registers atexit() callback. */
767 #endif
768   }
769
770   return C;
771 }
772 #endif
773
774 static bool
775 parse_float (const char **pp, const char *end, float *pv)
776 {
777   char buf[32];
778   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
779   strncpy (buf, *pp, len);
780   buf[len] = '\0';
781
782   char *p = buf;
783   char *pend = p;
784   float v;
785
786   errno = 0;
787 #ifdef USE_XLOCALE
788   v = strtod_l (p, &pend, get_C_locale ());
789 #else
790   v = strtod (p, &pend);
791 #endif
792   if (errno || p == pend)
793     return false;
794
795   *pv = v;
796   *pp += pend - p;
797   return true;
798 }
799
800 static bool
801 parse_bool (const char **pp, const char *end, uint32_t *pv)
802 {
803   parse_space (pp, end);
804
805   const char *p = *pp;
806   while (*pp < end && ISALPHA(**pp))
807     (*pp)++;
808
809   /* CSS allows on/off as aliases 1/0. */
810   if (*pp - p == 2 && 0 == strncmp (p, "on", 2))
811     *pv = 1;
812   else if (*pp - p == 3 && 0 == strncmp (p, "off", 3))
813     *pv = 0;
814   else
815     return false;
816
817   return true;
818 }
819
820 /* hb_feature_t */
821
822 static bool
823 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
824 {
825   if (parse_char (pp, end, '-'))
826     feature->value = 0;
827   else {
828     parse_char (pp, end, '+');
829     feature->value = 1;
830   }
831
832   return true;
833 }
834
835 static bool
836 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
837 {
838   parse_space (pp, end);
839
840   char quote = 0;
841
842   if (*pp < end && (**pp == '\'' || **pp == '"'))
843   {
844     quote = **pp;
845     (*pp)++;
846   }
847
848   const char *p = *pp;
849   while (*pp < end && ISALNUM(**pp))
850     (*pp)++;
851
852   if (p == *pp || *pp - p > 4)
853     return false;
854
855   *tag = hb_tag_from_string (p, *pp - p);
856
857   if (quote)
858   {
859     /* CSS expects exactly four bytes.  And we only allow quotations for
860      * CSS compatibility.  So, enforce the length. */
861      if (*pp - p != 4)
862        return false;
863     if (*pp == end || **pp != quote)
864       return false;
865     (*pp)++;
866   }
867
868   return true;
869 }
870
871 static bool
872 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
873 {
874   parse_space (pp, end);
875
876   bool has_start;
877
878   feature->start = 0;
879   feature->end = (unsigned int) -1;
880
881   if (!parse_char (pp, end, '['))
882     return true;
883
884   has_start = parse_uint (pp, end, &feature->start);
885
886   if (parse_char (pp, end, ':')) {
887     parse_uint (pp, end, &feature->end);
888   } else {
889     if (has_start)
890       feature->end = feature->start + 1;
891   }
892
893   return parse_char (pp, end, ']');
894 }
895
896 static bool
897 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
898 {
899   bool had_equal = parse_char (pp, end, '=');
900   bool had_value = parse_uint32 (pp, end, &feature->value) ||
901                    parse_bool (pp, end, &feature->value);
902   /* CSS doesn't use equal-sign between tag and value.
903    * If there was an equal-sign, then there *must* be a value.
904    * A value without an equal-sign is ok, but not required. */
905   return !had_equal || had_value;
906 }
907
908 static bool
909 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
910 {
911   return parse_feature_value_prefix (pp, end, feature) &&
912          parse_tag (pp, end, &feature->tag) &&
913          parse_feature_indices (pp, end, feature) &&
914          parse_feature_value_postfix (pp, end, feature) &&
915          parse_space (pp, end) &&
916          *pp == end;
917 }
918
919 /**
920  * hb_feature_from_string:
921  * @str: (array length=len) (element-type uint8_t): a string to parse
922  * @len: length of @str, or -1 if string is %NULL terminated
923  * @feature: (out): the #hb_feature_t to initialize with the parsed values
924  *
925  * Parses a string into a #hb_feature_t.
926  *
927  * TODO: document the syntax here.
928  *
929  * Return value:
930  * %true if @str is successfully parsed, %false otherwise.
931  *
932  * Since: 0.9.5
933  **/
934 hb_bool_t
935 hb_feature_from_string (const char *str, int len,
936                         hb_feature_t *feature)
937 {
938   hb_feature_t feat;
939
940   if (len < 0)
941     len = strlen (str);
942
943   if (likely (parse_one_feature (&str, str + len, &feat)))
944   {
945     if (feature)
946       *feature = feat;
947     return true;
948   }
949
950   if (feature)
951     memset (feature, 0, sizeof (*feature));
952   return false;
953 }
954
955 /**
956  * hb_feature_to_string:
957  * @feature: an #hb_feature_t to convert
958  * @buf: (array length=size) (out): output string
959  * @size: the allocated size of @buf
960  *
961  * Converts a #hb_feature_t into a %NULL-terminated string in the format
962  * understood by hb_feature_from_string(). The client in responsible for
963  * allocating big enough size for @buf, 128 bytes is more than enough.
964  *
965  * Since: 0.9.5
966  **/
967 void
968 hb_feature_to_string (hb_feature_t *feature,
969                       char *buf, unsigned int size)
970 {
971   if (unlikely (!size)) return;
972
973   char s[128];
974   unsigned int len = 0;
975   if (feature->value == 0)
976     s[len++] = '-';
977   hb_tag_to_string (feature->tag, s + len);
978   len += 4;
979   while (len && s[len - 1] == ' ')
980     len--;
981   if (feature->start != 0 || feature->end != (unsigned int) -1)
982   {
983     s[len++] = '[';
984     if (feature->start)
985       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
986     if (feature->end != feature->start + 1) {
987       s[len++] = ':';
988       if (feature->end != (unsigned int) -1)
989         len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
990     }
991     s[len++] = ']';
992   }
993   if (feature->value > 1)
994   {
995     s[len++] = '=';
996     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
997   }
998   assert (len < ARRAY_LENGTH (s));
999   len = MIN (len, size - 1);
1000   memcpy (buf, s, len);
1001   buf[len] = '\0';
1002 }
1003
1004 /* hb_variation_t */
1005
1006 static bool
1007 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
1008 {
1009   parse_char (pp, end, '='); /* Optional. */
1010   return parse_float (pp, end, &variation->value);
1011 }
1012
1013 static bool
1014 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
1015 {
1016   return parse_tag (pp, end, &variation->tag) &&
1017          parse_variation_value (pp, end, variation) &&
1018          parse_space (pp, end) &&
1019          *pp == end;
1020 }
1021
1022 /**
1023  * hb_variation_from_string:
1024  *
1025  * Since: 1.4.2
1026  */
1027 hb_bool_t
1028 hb_variation_from_string (const char *str, int len,
1029                           hb_variation_t *variation)
1030 {
1031   hb_variation_t var;
1032
1033   if (len < 0)
1034     len = strlen (str);
1035
1036   if (likely (parse_one_variation (&str, str + len, &var)))
1037   {
1038     if (variation)
1039       *variation = var;
1040     return true;
1041   }
1042
1043   if (variation)
1044     memset (variation, 0, sizeof (*variation));
1045   return false;
1046 }
1047
1048 /**
1049  * hb_variation_to_string:
1050  *
1051  * Since: 1.4.2
1052  */
1053 void
1054 hb_variation_to_string (hb_variation_t *variation,
1055                         char *buf, unsigned int size)
1056 {
1057   if (unlikely (!size)) return;
1058
1059   char s[128];
1060   unsigned int len = 0;
1061   hb_tag_to_string (variation->tag, s + len);
1062   len += 4;
1063   while (len && s[len - 1] == ' ')
1064     len--;
1065   s[len++] = '=';
1066   len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
1067
1068   assert (len < ARRAY_LENGTH (s));
1069   len = MIN (len, size - 1);
1070   memcpy (buf, s, len);
1071   buf[len] = '\0';
1072 }