1 /******************************************************************************
2  *
3  * Derived from GDAL port/cpl_strtod.cpp
4  * Purpose:  Functions to convert ASCII string to floating point number.
5  * Author:   Andrey Kiselev, dron@ak4719.spb.edu.
6  *
7  ******************************************************************************
8  * Copyright (c) 2006, Andrey Kiselev
9  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at mines-paris dot org>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "projects.h"
31 
32 /* Windows nmake build doesn't have a proj_config.h, but HAVE_LOCALECONV */
33 /* is defined in the compilation line */
34 #ifndef HAVE_LOCALECONV
35 #include "proj_config.h"
36 #endif
37 
38 #include <stdlib.h>
39 #include <locale.h>
40 #include <errno.h>
41 
42 #define PJ_STRTOD_WORK_BUFFER_SIZE 64
43 
44 /************************************************************************/
45 /*                              pj_atof()                               */
46 /************************************************************************/
47 
48 /**
49  * Converts ASCII string to floating point number.
50  *
51  * This function converts the initial portion of the string pointed to
52  * by nptr to double floating point representation. The behaviour is the
53  * same as
54  *
55  *   pj_strtod(nptr, (char **)NULL);
56  *
57  * This function does the same as standard atof(3), but does not take
58  * locale in account. That means, the decimal delimiter is always '.'
59  * (decimal point).
60  *
61  * @param nptr Pointer to string to convert.
62  *
63  * @return Converted value.
64  */
pj_atof(const char * nptr)65 double pj_atof( const char* nptr )
66 {
67     return pj_strtod(nptr, NULL);
68 }
69 
70 
71 /************************************************************************/
72 /*                     pj_replace_point_by_locale_point()               */
73 /************************************************************************/
74 
pj_replace_point_by_locale_point(const char * pszNumber,char point,char * pszWorkBuffer)75 static char* pj_replace_point_by_locale_point(const char* pszNumber, char point,
76                                               char* pszWorkBuffer)
77 {
78 #if !defined(HAVE_LOCALECONV) || defined(_WIN32_WCE)
79 
80 #if defined(_MSC_VER)  /* Visual C++ */
81 #pragma message("localeconv not available")
82 #else
83 #warning "localeconv not available"
84 #endif
85 
86     static char byPoint = 0;
87     if (byPoint == 0)
88     {
89         char szBuf[16];
90         sprintf(szBuf, "%.1f", 1.0);
91         byPoint = szBuf[1];
92     }
93     if (point != byPoint)
94     {
95         const char* pszPoint = strchr(pszNumber, point);
96         if (pszPoint)
97         {
98             char* pszNew;
99             if( strlen(pszNumber) < PJ_STRTOD_WORK_BUFFER_SIZE )
100             {
101                 strcpy(pszWorkBuffer, pszNumber);
102                 pszNew = pszWorkBuffer;
103             }
104             else
105                 pszNew = strdup(pszNumber);
106             pszNew[pszPoint - pszNumber] = byPoint;
107             return pszNew;
108         }
109     }
110 #else
111     struct lconv *poLconv = localeconv();
112     if ( poLconv
113          && poLconv->decimal_point
114          && poLconv->decimal_point[0] != '\0' )
115     {
116         char    byPoint = poLconv->decimal_point[0];
117 
118         if (point != byPoint)
119         {
120             const char* pszLocalePoint = strchr(pszNumber, byPoint);
121             const char* pszPoint = strchr(pszNumber, point);
122             if (pszPoint || pszLocalePoint)
123             {
124                 char* pszNew;
125                 if( strlen(pszNumber) < PJ_STRTOD_WORK_BUFFER_SIZE )
126                 {
127                     strcpy(pszWorkBuffer, pszNumber);
128                     pszNew = pszWorkBuffer;
129                 }
130                 else
131                     pszNew = strdup(pszNumber);
132                 if( pszLocalePoint )
133                     pszNew[pszLocalePoint - pszNumber] = ' ';
134                 if( pszPoint )
135                     pszNew[pszPoint - pszNumber] = byPoint;
136                 return pszNew;
137             }
138         }
139     }
140 #endif
141     return (char*) pszNumber;
142 }
143 
144 /************************************************************************/
145 /*                            pj_strtod()                               */
146 /************************************************************************/
147 
148 /**
149  * Converts ASCII string to floating point number.
150  *
151  * This function converts the initial portion of the string pointed to
152  * by nptr to double floating point representation. This function does the
153  * same as standard strtod(3), but does not take locale in account and use
154  * decimal point.
155  *
156  * @param nptr Pointer to string to convert.
157  * @param endptr If is not NULL, a pointer to the character after the last
158  * character used in the conversion is stored in the location referenced
159  * by endptr.
160  * @param point Decimal delimiter.
161  *
162  * @return Converted value.
163  */
pj_strtod(const char * nptr,char ** endptr)164 double pj_strtod( const char *nptr, char **endptr )
165 {
166 /* -------------------------------------------------------------------- */
167 /*  We are implementing a simple method here: copy the input string     */
168 /*  into the temporary buffer, replace the specified decimal delimiter  */
169 /*  with the one, taken from locale settings and use standard strtod()  */
170 /*  on that buffer.                                                     */
171 /* -------------------------------------------------------------------- */
172     double      dfValue;
173     int         nError;
174     char        szWorkBuffer[PJ_STRTOD_WORK_BUFFER_SIZE];
175 
176     char*       pszNumber = pj_replace_point_by_locale_point(nptr, '.', szWorkBuffer);
177 
178     dfValue = strtod( pszNumber, endptr );
179     nError = errno;
180 
181     if ( endptr )
182         *endptr = (char *)nptr + (*endptr - pszNumber);
183 
184     if (pszNumber != (char*) nptr && pszNumber != szWorkBuffer )
185         free( pszNumber );
186 
187     errno = nError;
188     return dfValue;
189 }
190 
191