1 /*
2  * Copyright © 2019  Ebrahim Byagowi
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  */
25 
26 #include "hb.hh"
27 #include "hb-machinery.hh"
28 #include "hb-number-parser.hh"
29 
30 #include <locale.h>
31 #ifdef HAVE_XLOCALE_H
32 #include <xlocale.h>
33 #endif
34 
35 template<typename T, typename Func>
36 static bool
_parse_number(const char ** pp,const char * end,T * pv,bool whole_buffer,Func f)37 _parse_number (const char **pp, const char *end, T *pv,
38 	       bool whole_buffer, Func f)
39 {
40   char buf[32];
41   unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1,
42 			     (unsigned int) (end - *pp));
43   strncpy (buf, *pp, len);
44   buf[len] = '\0';
45 
46   char *p = buf;
47   char *pend = p;
48 
49   errno = 0;
50   *pv = f (p, &pend);
51   if (unlikely (errno || p == pend ||
52 		/* Check if consumed whole buffer if is requested */
53 		(whole_buffer && pend - p != end - *pp))) return false;
54 
55   *pp += pend - p;
56   return true;
57 }
58 
59 bool
hb_parse_int(const char ** pp,const char * end,int * pv,bool whole_buffer)60 hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer)
61 {
62   return _parse_number<int> (pp, end, pv, whole_buffer,
63 			     [] (const char *p, char **end)
64 			     { return strtol (p, end, 10); });
65 }
66 
67 bool
hb_parse_uint(const char ** pp,const char * end,unsigned int * pv,bool whole_buffer,int base)68 hb_parse_uint (const char **pp, const char *end, unsigned int *pv,
69 	       bool whole_buffer, int base)
70 {
71   return _parse_number<unsigned int> (pp, end, pv, whole_buffer,
72 				      [base] (const char *p, char **end)
73 				      { return strtoul (p, end, base); });
74 }
75 
76 
77 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
78 #define USE_XLOCALE 1
79 #define HB_LOCALE_T locale_t
80 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
81 #define HB_FREE_LOCALE(loc) freelocale (loc)
82 #elif defined(_MSC_VER)
83 #define USE_XLOCALE 1
84 #define HB_LOCALE_T _locale_t
85 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
86 #define HB_FREE_LOCALE(loc) _free_locale (loc)
87 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
88 #endif
89 
90 #ifdef USE_XLOCALE
91 
92 #if HB_USE_ATEXIT
93 static void free_static_C_locale ();
94 #endif
95 
96 static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<HB_LOCALE_T>,
97 							  hb_C_locale_lazy_loader_t>
98 {
createhb_C_locale_lazy_loader_t99   static HB_LOCALE_T create ()
100   {
101     HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
102 
103 #if HB_USE_ATEXIT
104     atexit (free_static_C_locale);
105 #endif
106 
107     return C_locale;
108   }
destroyhb_C_locale_lazy_loader_t109   static void destroy (HB_LOCALE_T p)
110   {
111     HB_FREE_LOCALE (p);
112   }
get_nullhb_C_locale_lazy_loader_t113   static HB_LOCALE_T get_null ()
114   {
115     return nullptr;
116   }
117 } static_C_locale;
118 
119 #if HB_USE_ATEXIT
120 static
free_static_C_locale()121 void free_static_C_locale ()
122 {
123   static_C_locale.free_instance ();
124 }
125 #endif
126 
127 static HB_LOCALE_T
get_C_locale()128 get_C_locale ()
129 {
130   return static_C_locale.get_unconst ();
131 }
132 #endif /* USE_XLOCALE */
133 
134 bool
hb_parse_double(const char ** pp,const char * end,double * pv,bool whole_buffer)135 hb_parse_double (const char **pp, const char *end, double *pv,
136 		 bool whole_buffer)
137 {
138   return _parse_number<double> (pp, end, pv, whole_buffer,
139 				[] (const char *p, char **end)
140 				{
141 #ifdef USE_XLOCALE
142 				  return strtod_l (p, end, get_C_locale ());
143 #else
144 				  return strtod_rl (p, end);
145 #endif
146 				});
147 }
148