* Bug 16365 fixed: median(m,'r'|'c') was wrong after 5dc990
[scilab.git] / scilab / modules / string / sci_gateway / cpp / sci_strtod.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) Digiteo 2011 - Cedric DELAMARRE
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16
17 #include "function.hxx"
18 #include "string.hxx"
19 #include "list.hxx"
20 #include "double.hxx"
21 #include "funcmanager.hxx"
22 #include "string_gw.hxx"
23
24 extern "C"
25 {
26 #include "core_math.h"
27 #include "localization.h"
28 #include "Scierror.h"
29 #include "os_string.h"
30 #include "os_wcstod.h"
31 #include "locale.h"
32 }
33
34
35 types::Function::ReturnValue sci_strtod(types::typed_list &in, int _iRetCount, types::typed_list &out)
36 {
37     types::Double* pOutDouble = NULL;
38     types::String* pOutString = NULL;
39     types::String* pString = NULL;
40
41     wchar_t pwstKey[] = L"1234567890";
42     wchar_t pwstSymbol[] = L"-+.";
43     wchar_t wstDecimalSep = L'.';
44
45     unsigned long long ullNan = 0x7ff8000000000000;
46     double dblNan = *( double* )&ullNan;
47
48     if (in.size() < 1 || in.size() > 2)
49     {
50         Scierror(77, _("%s: Wrong number of input argument(s): %d to %d expected.\n"), "strtod", 1, 2);
51         return types::Function::Error;
52     }
53     if (_iRetCount > 2)
54     {
55         Scierror(78, _("%s: Wrong number of output argument(s): %d to %d expected.\n"), "strtod", 1, 2);
56         return types::Function::Error;
57     }
58
59     if (in[0]->isDouble() && in[0]->getAs<types::Double>()->isEmpty())
60     {
61         out.push_back(types::Double::Empty());
62         if (_iRetCount == 2)
63         {
64             out.push_back(new types::String(L""));
65         }
66
67         return types::Function::OK;
68     }
69
70     if (in[0]->isString() == false)
71     {
72         Scierror(999, _("%s: Wrong type for input argument #%d: Matrix of strings or empty matrix expected.\n"), "strtod", 1);
73         return types::Function::Error;
74     }
75
76     pString = in[0]->getAs<types::String>();
77
78     pOutDouble = new types::Double(pString->getDims(), pString->getDimsArray());
79     if (_iRetCount == 2)
80     {
81         pOutString = new types::String(pString->getDims(), pString->getDimsArray());
82     }
83
84     if (in.size() == 2)
85     {
86         if (in[1]->isString() == false)
87         {
88             Scierror(999, _("%s: Wrong type for input argument #%d: A single string expected.\n"), "strtod", 2);
89             pOutDouble->killMe();
90             if (_iRetCount == 2)
91             {
92                 pOutString->killMe();
93             }
94             return types::Function::Error;
95         }
96
97         types::String* pString2 = in[1]->getAs<types::String>();
98         std::wstring pwstr(pString2->get(0));
99
100         if (pwstr != L"." && pwstr != L",")
101         {
102             Scierror(999, _("%s: Wrong value for input argument #%d: '.' or ',' expected.\n"), "strtod", 2);
103             pOutDouble->killMe();
104             if (_iRetCount == 2)
105             {
106                 pOutString->killMe();
107             }
108             return types::Function::Error;
109         }
110
111         wstDecimalSep = *(pwstr.c_str());
112         pwstSymbol[2] = wstDecimalSep;
113
114         if (wstDecimalSep == L',')
115         {
116 #ifdef _MSC_VER
117             setlocale(LC_NUMERIC, "French_France.1252");
118 #else
119             setlocale(LC_NUMERIC, "fr_FR.UTF-8");
120 #endif
121         }
122     }
123
124     for (int i = 0 ; i < pString->getSize() ; i++)
125     {
126         //Double part
127         bool bStop = false;
128         wchar_t *pwstStop = NULL;
129         wchar_t* pstStr = pString->get(i);
130         int iSign = (int)wcscspn(pstStr, pwstSymbol);
131         int iKey = (int)wcscspn(pstStr, pwstKey);
132
133
134         //symbol can be use only if it is before key
135         if (iSign == iKey - 1)
136         {
137             //let strtod do with symbol
138             iKey -= 1;
139         }
140
141         //special case for "-.3"
142         if (iSign == iKey - 2 && pstStr[iSign + 1] == wstDecimalSep)
143         {
144             //let strtod do with symbol
145             iKey -= 2;
146         }
147
148         //Check if there is a number in the string
149         if (iKey)
150         {
151             for (int j = 0 ; j < iKey ; j++)
152             {
153                 if ((pstStr[j] != ' ') && (pstStr[j] != '\t') && (pstStr[j] != '\r'))// spaces are accepted
154                 {
155                     pOutDouble->set(i, dblNan);
156                     bStop = true;
157                     pwstStop = pstStr;
158                     break;
159                 }
160             }
161
162             //it is still a number
163             if (bStop == false)
164             {
165                 //only spaces ?
166                 if (wcslen(pstStr) == iKey) // strtod("  ")
167                 {
168                     pOutDouble->set(i, dblNan);
169                     pwstStop = pstStr;
170                 }
171                 else // strtod("  000xxx")
172                 {
173                     pOutDouble->set(i, os_wcstod(pstStr + iKey, &pwstStop));
174                 }
175             }
176         }
177         else if (wcslen(pstStr) == 0) //strtod("")
178         {
179             pOutDouble->set(i, dblNan);
180         }
181         else //all characters are digits
182         {
183             pOutDouble->set(i, os_wcstod(pstStr, &pwstStop));
184         }
185
186         if (_iRetCount == 2)
187         {
188             if (pwstStop)
189             {
190                 pOutString->set(i, pwstStop);
191             }
192             else
193             {
194                 pOutString->set(i, L"");
195             }
196         }
197     }
198
199     if (wstDecimalSep == L',')
200     {
201         setlocale(LC_NUMERIC, "C");
202     }
203
204     out.push_back(pOutDouble);
205
206     if (_iRetCount == 2)
207     {
208         out.push_back(pOutString);
209     }
210
211     return types::Function::OK;
212 }
213