Imported Upstream version 1.7.6
[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 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  **/
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  *       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
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
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_HATRAN:
528     case HB_SCRIPT_OLD_HUNGARIAN:
529
530     /* Unicode-9.0 additions */
531     case HB_SCRIPT_ADLAM:
532
533       return HB_DIRECTION_RTL;
534   }
535
536   return HB_DIRECTION_LTR;
537 }
538
539
540 /* hb_user_data_array_t */
541
542 bool
543 hb_user_data_array_t::set (hb_user_data_key_t *key,
544                            void *              data,
545                            hb_destroy_func_t   destroy,
546                            hb_bool_t           replace)
547 {
548   if (!key)
549     return false;
550
551   if (replace) {
552     if (!data && !destroy) {
553       items.remove (key, lock);
554       return true;
555     }
556   }
557   hb_user_data_item_t item = {key, data, destroy};
558   bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
559
560   return ret;
561 }
562
563 void *
564 hb_user_data_array_t::get (hb_user_data_key_t *key)
565 {
566   hb_user_data_item_t item = {nullptr, nullptr, nullptr};
567
568   return items.find (key, &item, lock) ? item.data : nullptr;
569 }
570
571
572 /* hb_version */
573
574 /**
575  * hb_version:
576  * @major: (out): Library major version component.
577  * @minor: (out): Library minor version component.
578  * @micro: (out): Library micro version component.
579  *
580  * Returns library version as three integer components.
581  *
582  * Since: 0.9.2
583  **/
584 void
585 hb_version (unsigned int *major,
586             unsigned int *minor,
587             unsigned int *micro)
588 {
589   *major = HB_VERSION_MAJOR;
590   *minor = HB_VERSION_MINOR;
591   *micro = HB_VERSION_MICRO;
592 }
593
594 /**
595  * hb_version_string:
596  *
597  * Returns library version as a string with three components.
598  *
599  * Return value: library version string.
600  *
601  * Since: 0.9.2
602  **/
603 const char *
604 hb_version_string (void)
605 {
606   return HB_VERSION_STRING;
607 }
608
609 /**
610  * hb_version_atleast:
611  * @major: 
612  * @minor: 
613  * @micro: 
614  *
615  * 
616  *
617  * Return value: 
618  *
619  * Since: 0.9.30
620  **/
621 hb_bool_t
622 hb_version_atleast (unsigned int major,
623                     unsigned int minor,
624                     unsigned int micro)
625 {
626   return HB_VERSION_ATLEAST (major, minor, micro);
627 }
628
629
630
631 /* hb_feature_t and hb_variation_t */
632
633 static bool
634 parse_space (const char **pp, const char *end)
635 {
636   while (*pp < end && ISSPACE (**pp))
637     (*pp)++;
638   return true;
639 }
640
641 static bool
642 parse_char (const char **pp, const char *end, char c)
643 {
644   parse_space (pp, end);
645
646   if (*pp == end || **pp != c)
647     return false;
648
649   (*pp)++;
650   return true;
651 }
652
653 static bool
654 parse_uint (const char **pp, const char *end, unsigned int *pv)
655 {
656   char buf[32];
657   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
658   strncpy (buf, *pp, len);
659   buf[len] = '\0';
660
661   char *p = buf;
662   char *pend = p;
663   unsigned int v;
664
665   /* Intentionally use strtol instead of strtoul, such that
666    * -1 turns into "big number"... */
667   errno = 0;
668   v = strtol (p, &pend, 0);
669   if (errno || p == pend)
670     return false;
671
672   *pv = v;
673   *pp += pend - p;
674   return true;
675 }
676
677 static bool
678 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
679 {
680   char buf[32];
681   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
682   strncpy (buf, *pp, len);
683   buf[len] = '\0';
684
685   char *p = buf;
686   char *pend = p;
687   unsigned int v;
688
689   /* Intentionally use strtol instead of strtoul, such that
690    * -1 turns into "big number"... */
691   errno = 0;
692   v = strtol (p, &pend, 0);
693   if (errno || p == pend)
694     return false;
695
696   *pv = v;
697   *pp += pend - p;
698   return true;
699 }
700
701 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
702 #define USE_XLOCALE 1
703 #define HB_LOCALE_T locale_t
704 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
705 #define HB_FREE_LOCALE(loc) freelocale (loc)
706 #elif defined(_MSC_VER)
707 #define USE_XLOCALE 1
708 #define HB_LOCALE_T _locale_t
709 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
710 #define HB_FREE_LOCALE(loc) _free_locale (loc)
711 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
712 #endif
713
714 #ifdef USE_XLOCALE
715
716 static HB_LOCALE_T C_locale;
717
718 #ifdef HB_USE_ATEXIT
719 static void
720 free_C_locale (void)
721 {
722   if (C_locale)
723     HB_FREE_LOCALE (C_locale);
724 }
725 #endif
726
727 static HB_LOCALE_T
728 get_C_locale (void)
729 {
730 retry:
731   HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
732
733   if (unlikely (!C))
734   {
735     C = HB_CREATE_LOCALE ("C");
736
737     if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
738     {
739       HB_FREE_LOCALE (C_locale);
740       goto retry;
741     }
742
743 #ifdef HB_USE_ATEXIT
744     atexit (free_C_locale); /* First person registers atexit() callback. */
745 #endif
746   }
747
748   return C;
749 }
750 #endif
751
752 static bool
753 parse_float (const char **pp, const char *end, float *pv)
754 {
755   char buf[32];
756   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
757   strncpy (buf, *pp, len);
758   buf[len] = '\0';
759
760   char *p = buf;
761   char *pend = p;
762   float v;
763
764   errno = 0;
765 #ifdef USE_XLOCALE
766   v = strtod_l (p, &pend, get_C_locale ());
767 #else
768   v = strtod (p, &pend);
769 #endif
770   if (errno || p == pend)
771     return false;
772
773   *pv = v;
774   *pp += pend - p;
775   return true;
776 }
777
778 static bool
779 parse_bool (const char **pp, const char *end, uint32_t *pv)
780 {
781   parse_space (pp, end);
782
783   const char *p = *pp;
784   while (*pp < end && ISALPHA(**pp))
785     (*pp)++;
786
787   /* CSS allows on/off as aliases 1/0. */
788   if (*pp - p == 2 && 0 == strncmp (p, "on", 2))
789     *pv = 1;
790   else if (*pp - p == 3 && 0 == strncmp (p, "off", 3))
791     *pv = 0;
792   else
793     return false;
794
795   return true;
796 }
797
798 /* hb_feature_t */
799
800 static bool
801 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
802 {
803   if (parse_char (pp, end, '-'))
804     feature->value = 0;
805   else {
806     parse_char (pp, end, '+');
807     feature->value = 1;
808   }
809
810   return true;
811 }
812
813 static bool
814 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
815 {
816   parse_space (pp, end);
817
818   char quote = 0;
819
820   if (*pp < end && (**pp == '\'' || **pp == '"'))
821   {
822     quote = **pp;
823     (*pp)++;
824   }
825
826   const char *p = *pp;
827   while (*pp < end && ISALNUM(**pp))
828     (*pp)++;
829
830   if (p == *pp || *pp - p > 4)
831     return false;
832
833   *tag = hb_tag_from_string (p, *pp - p);
834
835   if (quote)
836   {
837     /* CSS expects exactly four bytes.  And we only allow quotations for
838      * CSS compatibility.  So, enforce the length. */
839      if (*pp - p != 4)
840        return false;
841     if (*pp == end || **pp != quote)
842       return false;
843     (*pp)++;
844   }
845
846   return true;
847 }
848
849 static bool
850 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
851 {
852   parse_space (pp, end);
853
854   bool has_start;
855
856   feature->start = 0;
857   feature->end = (unsigned int) -1;
858
859   if (!parse_char (pp, end, '['))
860     return true;
861
862   has_start = parse_uint (pp, end, &feature->start);
863
864   if (parse_char (pp, end, ':')) {
865     parse_uint (pp, end, &feature->end);
866   } else {
867     if (has_start)
868       feature->end = feature->start + 1;
869   }
870
871   return parse_char (pp, end, ']');
872 }
873
874 static bool
875 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
876 {
877   bool had_equal = parse_char (pp, end, '=');
878   bool had_value = parse_uint32 (pp, end, &feature->value) ||
879                    parse_bool (pp, end, &feature->value);
880   /* CSS doesn't use equal-sign between tag and value.
881    * If there was an equal-sign, then there *must* be a value.
882    * A value without an equal-sign is ok, but not required. */
883   return !had_equal || had_value;
884 }
885
886 static bool
887 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
888 {
889   return parse_feature_value_prefix (pp, end, feature) &&
890          parse_tag (pp, end, &feature->tag) &&
891          parse_feature_indices (pp, end, feature) &&
892          parse_feature_value_postfix (pp, end, feature) &&
893          parse_space (pp, end) &&
894          *pp == end;
895 }
896
897 /**
898  * hb_feature_from_string:
899  * @str: (array length=len) (element-type uint8_t): a string to parse
900  * @len: length of @str, or -1 if string is %NULL terminated
901  * @feature: (out): the #hb_feature_t to initialize with the parsed values
902  *
903  * Parses a string into a #hb_feature_t.
904  *
905  * TODO: document the syntax here.
906  *
907  * Return value:
908  * %true if @str is successfully parsed, %false otherwise.
909  *
910  * Since: 0.9.5
911  **/
912 hb_bool_t
913 hb_feature_from_string (const char *str, int len,
914                         hb_feature_t *feature)
915 {
916   hb_feature_t feat;
917
918   if (len < 0)
919     len = strlen (str);
920
921   if (likely (parse_one_feature (&str, str + len, &feat)))
922   {
923     if (feature)
924       *feature = feat;
925     return true;
926   }
927
928   if (feature)
929     memset (feature, 0, sizeof (*feature));
930   return false;
931 }
932
933 /**
934  * hb_feature_to_string:
935  * @feature: an #hb_feature_t to convert
936  * @buf: (array length=size) (out): output string
937  * @size: the allocated size of @buf
938  *
939  * Converts a #hb_feature_t into a %NULL-terminated string in the format
940  * understood by hb_feature_from_string(). The client in responsible for
941  * allocating big enough size for @buf, 128 bytes is more than enough.
942  *
943  * Since: 0.9.5
944  **/
945 void
946 hb_feature_to_string (hb_feature_t *feature,
947                       char *buf, unsigned int size)
948 {
949   if (unlikely (!size)) return;
950
951   char s[128];
952   unsigned int len = 0;
953   if (feature->value == 0)
954     s[len++] = '-';
955   hb_tag_to_string (feature->tag, s + len);
956   len += 4;
957   while (len && s[len - 1] == ' ')
958     len--;
959   if (feature->start != 0 || feature->end != (unsigned int) -1)
960   {
961     s[len++] = '[';
962     if (feature->start)
963       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
964     if (feature->end != feature->start + 1) {
965       s[len++] = ':';
966       if (feature->end != (unsigned int) -1)
967         len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
968     }
969     s[len++] = ']';
970   }
971   if (feature->value > 1)
972   {
973     s[len++] = '=';
974     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
975   }
976   assert (len < ARRAY_LENGTH (s));
977   len = MIN (len, size - 1);
978   memcpy (buf, s, len);
979   buf[len] = '\0';
980 }
981
982 /* hb_variation_t */
983
984 static bool
985 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
986 {
987   parse_char (pp, end, '='); /* Optional. */
988   return parse_float (pp, end, &variation->value);
989 }
990
991 static bool
992 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
993 {
994   return parse_tag (pp, end, &variation->tag) &&
995          parse_variation_value (pp, end, variation) &&
996          parse_space (pp, end) &&
997          *pp == end;
998 }
999
1000 /**
1001  * hb_variation_from_string:
1002  *
1003  * Since: 1.4.2
1004  */
1005 hb_bool_t
1006 hb_variation_from_string (const char *str, int len,
1007                           hb_variation_t *variation)
1008 {
1009   hb_variation_t var;
1010
1011   if (len < 0)
1012     len = strlen (str);
1013
1014   if (likely (parse_one_variation (&str, str + len, &var)))
1015   {
1016     if (variation)
1017       *variation = var;
1018     return true;
1019   }
1020
1021   if (variation)
1022     memset (variation, 0, sizeof (*variation));
1023   return false;
1024 }
1025
1026 /**
1027  * hb_variation_to_string:
1028  *
1029  * Since: 1.4.2
1030  */
1031 void
1032 hb_variation_to_string (hb_variation_t *variation,
1033                         char *buf, unsigned int size)
1034 {
1035   if (unlikely (!size)) return;
1036
1037   char s[128];
1038   unsigned int len = 0;
1039   hb_tag_to_string (variation->tag, s + len);
1040   len += 4;
1041   while (len && s[len - 1] == ' ')
1042     len--;
1043   s[len++] = '=';
1044   len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
1045
1046   assert (len < ARRAY_LENGTH (s));
1047   len = MIN (len, size - 1);
1048   memcpy (buf, s, len);
1049   buf[len] = '\0';
1050 }