7ccda3926ece56b7623f70b9d003fcd3043dc53f
[scilab.git] / scilab / modules / output_stream / src / cpp / scilab_sprintf.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2010 - DIGITEO - Antoine ELIAS
4  *  Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET
5  *  Copyright (C) 2013 - Scilab Enterprises - Cedric Delamarre
6  *
7  *  This file must be used under the terms of the CeCILL.
8  *  This source file is licensed as described in the file COPYING, which
9  *  you should have received as part of this distribution.  The terms
10  *  are also available at
11  *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
12  *
13  */
14
15 #include <stdio.h>
16
17 #include "types.hxx"
18 #include "double.hxx"
19 #include "string.hxx"
20 #include "scilab_sprintf.hxx"
21
22 using namespace types;
23
24 extern "C"
25 {
26 #include "Scierror.h"
27 #include "sci_malloc.h"
28 #include "localization.h"
29 #include "charEncoding.h"
30 #include "os_wcsdup.h"
31 }
32
33 #define NanString L"Nan"
34 #define InfString L"Inf"
35 #define NegInfString L"-Inf"
36
37 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine);
38
39 wchar_t** scilab_sprintf(const char* _pstName, const wchar_t* _pwstInput, typed_list &in, ArgumentPosition* _pArgs, int _iArgsCount, int* _piOutputRows, int* _piNewLine)
40 {
41     /* Force Windows display to have two-digit exponent. */
42 #ifdef _MSC_VER
43     _set_output_format(_TWO_DIGIT_EXPONENT);
44 #endif
45     wchar_t** pwstOutput        = NULL;
46     wchar_t* pwstFirstOutput    = NULL;
47     *_piNewLine = 0;
48     size_t pos = 0;
49
50     //\n \n\r \r \t to string
51     //find number of lines
52     // replace \\n \\t... by \n \t...
53     pwstFirstOutput = replaceAndCountLines(_pwstInput, _piOutputRows, _piNewLine);
54
55     //no arg, just return _pwstInput value
56     if (_iArgsCount)
57     {
58         //store all sub parts of the input string
59         //for the input string "bla1 %s bla2 %d bla3"
60         //store  :
61         //pwstToken[0] : "bla1 "
62         //pwstToken[1] : "%s bla2 "
63         //pwstToken[2] : "%d bla3"
64
65         size_t iStart   = 0;
66         size_t iEnd     = 0;
67         int iToken      = 0;
68         int iPosArg     = 0;
69
70         TokenDef* pToken = new TokenDef[_iArgsCount + 1];
71         wchar_t* pwstStart  = pwstFirstOutput;
72
73         bool bFinish         = false;
74         bool bPercentPercent = false;
75
76         while (!bFinish)
77         {
78             wchar_t* pwstEnd = wcsstr(pwstStart + (iToken == 0 ? 0 : 1), L"%");
79             iStart = pwstStart - pwstFirstOutput;
80             bPercentPercent = false;
81             if (pwstEnd != NULL)
82             {
83                 if (iToken && pwstStart[1] == L'%')
84                 {
85                     //manage "%%"
86                     pwstEnd = wcsstr(pwstEnd + 1, L"%");
87                     if (pwstEnd == NULL)
88                     {
89                         //end of string
90                         iEnd    = wcslen(pwstFirstOutput);
91                         bFinish = true;
92                     }
93                     else
94                     {
95                         iEnd = pwstEnd - pwstFirstOutput;
96                     }
97
98                     // skip the first %
99                     iStart++;
100                     bPercentPercent = true;
101                 }
102                 else
103                 {
104                     iEnd = pwstEnd - pwstFirstOutput;
105                 }
106             }
107             else
108             {
109                 //end of string
110                 iEnd    = wcslen(pwstFirstOutput);
111                 bFinish = true;
112             }
113
114             pToken[iToken].pwstToken = new wchar_t[iEnd - iStart + 1];
115             wcsncpy(pToken[iToken].pwstToken, pwstFirstOutput + iStart, iEnd - iStart);
116             pToken[iToken].pwstToken[iEnd - iStart] = L'\0';
117
118             //identify destination type
119             //format : %[flags][width][.precision][length]specifier
120             //pToken[iToken].pwstToken
121
122             //find %
123             wchar_t* pwstPercent = wcsstr(pToken[iToken].pwstToken, L"%");
124             if (pwstPercent != NULL && bPercentPercent == false)
125             {
126                 //looking for flags
127                 if (*(pwstPercent + 1) == L'-' ||
128                         *(pwstPercent + 1) == L'+' ||
129                         *(pwstPercent + 1) == L' ' ||
130                         *(pwstPercent + 1) == L'#' ||
131                         *(pwstPercent + 1) == L'0')
132                 {
133                     pwstPercent++;
134                 }
135
136                 //looking for width
137                 if (*(pwstPercent + 1) == L'*')
138                 {
139                     pwstPercent++;
140                 }
141                 else
142                 {
143                     //number
144                     while (iswdigit(*(pwstPercent + 1)))
145                     {
146                         pwstPercent++;
147                     }
148                 }
149
150                 //looking for precision
151                 if (*(pwstPercent + 1) == L'.')
152                 {
153                     pwstPercent++;
154                     while (iswdigit(*(pwstPercent + 1)))
155                     {
156                         pwstPercent++;
157                     }
158                 }
159
160                 //looking for length
161                 if (*(pwstPercent + 1) == L'h' ||
162                         *(pwstPercent + 1) == L'l' ||
163                         *(pwstPercent + 1) == L'L')
164                 {
165                     pToken[iToken].bLengthFlag = true;
166                     pwstPercent++;
167                 }
168                 else
169                 {
170                     pToken[iToken].bLengthFlag = false;
171                 }
172
173                 wchar_t wcType = *(pwstPercent + 1);
174                 switch (wcType)
175                 {
176                     case L'i' : //integer
177                     case L'd' : //integer
178                     case L'o' : //octal
179                     case L'u' : //unsigned
180                     case L'x' : //hex
181                     case L'X' : //HEX
182                         if (_pArgs[iPosArg].type != InternalType::ScilabDouble)
183                         {
184                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
185                             *_piOutputRows = 0;
186                             return NULL;
187                         }
188
189                         if (finite(in[iPosArg + 1]->getAs<types::Double>()->get(0)) == false)
190                         {
191                             pToken[iToken].outputType = InternalType::ScilabString;
192                             *(pwstPercent + 1) = L's';
193                         }
194                         else
195                         {
196                             pToken[iToken].outputType = InternalType::ScilabInt32;
197                         }
198
199                         iPosArg++;
200                         break;
201                     case L'f' : //float
202                     case L'e' : //exp
203                     case L'E' : //EXP
204                     case L'g' : //shorter between float or exp
205                     case L'G' : //shorter between float or EXP
206                         if (_pArgs[iPosArg].type != InternalType::ScilabDouble)
207                         {
208                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
209                             *_piOutputRows = 0;
210                             return NULL;
211                         }
212
213                         if (finite(in[iPosArg + 1]->getAs<types::Double>()->get(0)) == false)
214                         {
215                             pToken[iToken].outputType = InternalType::ScilabString;
216                             *(pwstPercent + 1) = L's';
217                         }
218                         else
219                         {
220                             pToken[iToken].outputType = InternalType::ScilabDouble;
221                         }
222                         iPosArg++;
223                         break;
224                     case L's' :
225                     case L'c' :
226                         if (_pArgs[iPosArg].type != InternalType::ScilabString)
227                         {
228                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
229                             *_piOutputRows = 0;
230                             return NULL;
231                         }
232
233                         pToken[iToken].outputType = InternalType::ScilabString;
234                         iPosArg++;
235                         break;
236                     default :
237                         //houston ...
238                         break;
239                 }
240             }
241
242             pwstStart = pwstEnd;
243             iToken++;
244         }
245
246         FREE(pwstFirstOutput);
247         pwstFirstOutput = NULL;
248
249         int iLoop = 1;
250         int iFirstArg = 1;
251         if (strcmp(_pstName, "mfprintf") == 0)
252         {
253             iFirstArg = 2;
254         }
255
256         if (in.size() > 1)
257         {
258             iLoop = in[iFirstArg]->getAs<GenericType>()->getRows();
259             for (int i = iFirstArg + 1 ; i < in.size() ; i++)
260             {
261                 iLoop = std::min(iLoop, in[i]->getAs<GenericType>()->getRows());
262             }
263         }
264
265         if (*_piNewLine || (*_piOutputRows) > 1)
266         {
267             (*_piOutputRows) *= iLoop;
268         }
269
270         std::wostringstream oFirstOutput;
271         for (int j = 0 ; j < iLoop ; j++)
272         {
273             //copy the 0th token
274             oFirstOutput << pToken[0].pwstToken;
275             iPosArg = 0;
276             //start at 1, the 0th is always without %
277             for (int i = 1 ; i < _iArgsCount + 1 ; i++)
278             {
279                 void* pvVal = NULL;
280                 if (pToken[i].outputType == InternalType::ScilabDouble)
281                 {
282                     wchar_t pwstTemp[bsiz];
283                     double dblVal = in[_pArgs[iPosArg].iArg]->getAs<Double>()->get(j, _pArgs[iPosArg].iPos);
284                     swprintf(pwstTemp, bsiz, pToken[i].pwstToken, dblVal);
285                     iPosArg++;
286                     oFirstOutput << pwstTemp;
287                 }
288                 else if (pToken[i].outputType == InternalType::ScilabInt32)
289                 {
290                     wchar_t pwstTemp[bsiz];
291                     double dblVal = in[_pArgs[iPosArg].iArg]->getAs<Double>()->get(j, _pArgs[iPosArg].iPos);
292                     swprintf(pwstTemp, bsiz, pToken[i].pwstToken, (int)dblVal);
293                     iPosArg++;
294                     oFirstOutput << pwstTemp;
295                 }
296                 else if (pToken[i].outputType == InternalType::ScilabString)
297                 {
298                     wchar_t* pwstStr = NULL;
299
300                     if (in[iPosArg + 1]->isDouble() && ISNAN(in[iPosArg + 1]->getAs<types::Double>()->get(0)))
301                     {
302                         pwstStr = NanString;
303                     }
304                     else if (in[iPosArg + 1]->isDouble() && finite(in[iPosArg + 1]->getAs<types::Double>()->get(0)) == false)
305                     {
306                         if (std::signbit(in[iPosArg + 1]->getAs<types::Double>()->get(0)))
307                         {
308                             pwstStr = NegInfString;
309                         }
310                         else
311                         {
312                             pwstStr = InfString;
313                         }
314                     }
315                     else
316                     {
317                         pwstStr = in[_pArgs[iPosArg].iArg]->getAs<types::String>()->get(j, _pArgs[iPosArg].iPos);
318                     }
319
320                     int posC = (int)wcscspn(pToken[i].pwstToken, L"c");
321                     int posS = (int)wcscspn(pToken[i].pwstToken, L"s");
322
323                     if (!posS || !posC)
324                     {
325                         *_piOutputRows = 0;
326                         return NULL;
327                     }
328
329                     if (posC < posS)
330                     {
331                         oFirstOutput << pwstStr[0];
332                     }
333                     else //'s'
334                     {
335                         oFirstOutput << pwstStr;
336                     }
337
338                     iPosArg++;
339                 }
340                 else
341                 {
342                     // management of %%
343                     oFirstOutput << pToken[i].pwstToken;
344                 }
345             }
346         }
347
348         pwstFirstOutput = os_wcsdup((wchar_t*)oFirstOutput.str().c_str());
349
350     }
351
352     pwstOutput = (wchar_t**)MALLOC((*_piOutputRows) * sizeof(wchar_t*));
353
354     size_t iLen = wcslen(pwstFirstOutput);
355     int iStart = 0;
356     int j = 0;
357     for (int i = 0 ; i < iLen ; i++)
358     {
359         if (pwstFirstOutput[i] == L'\n')
360         {
361             int iSize = i - iStart;
362             pwstOutput[j] = (wchar_t*)MALLOC(sizeof(wchar_t) * (iSize + 1));
363             wcsncpy(pwstOutput[j], pwstFirstOutput + iStart, iSize);
364             pwstOutput[j][iSize] = L'\0';
365             iStart = i + 1;
366             j++;
367         }
368     }
369
370     if (j == (*_piOutputRows) - 1)
371     {
372         pwstOutput[j] = os_wcsdup(pwstFirstOutput + iStart);
373     }
374
375     FREE(pwstFirstOutput);
376     return pwstOutput;
377 }
378 /*--------------------------------------------------------------------------*/
379 // replace "\\n" "\\r" "\\t" "\\r\\n" by '\n' '\r' '\t' '\n'
380 // count number of lines
381 // indicate if one '\n' is at the end of string
382 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine)
383 {
384     size_t iInputLen = wcslen(_pwstInput);
385     wchar_t* pwstFirstOutput = (wchar_t*)MALLOC(sizeof(wchar_t) * (iInputLen + 1));
386
387     int iPos = 0;
388     *_piLines = 1;
389
390     for (int i = 0 ; i < iInputLen ; i++)
391     {
392         if (_pwstInput[i] == L'\\')
393         {
394             if (iInputLen == i + 1)
395             {
396                 continue;
397             }
398
399             switch (_pwstInput[i + 1])
400             {
401                 case L'n' :
402                     pwstFirstOutput[iPos++] = L'\n';
403                     (*_piLines)++;
404                     i++;
405                     break;
406                 case L'r' :
407                     if (iInputLen > i + 3 && _pwstInput[i + 2] == L'\\' && _pwstInput[i + 3] == L'n')
408                     {
409                         pwstFirstOutput[iPos++] = L'\n';
410                         (*_piLines)++;
411                         i += 3;
412                     }
413                     else
414                     {
415                         pwstFirstOutput[iPos++] = L'\r';
416                         i++;
417                     }
418                     break;
419                 case L't' :
420                     pwstFirstOutput[iPos++] = L'\t';
421                     i++;
422                     break;
423                 case L'\\':
424                     pwstFirstOutput[iPos++] = L'\\';
425                     i++;
426                     break;
427                 default:
428                     break;
429             }
430         }
431         else
432         {
433             pwstFirstOutput[iPos++] = _pwstInput[i];
434         }
435     }
436
437     // do not count '\n' if it's at the end of string
438     // it will be manage by piNewLine
439     if (pwstFirstOutput[iPos - 1] == '\n')
440     {
441         (*_piLines)--;
442         (*_piNewLine) = 1;
443     }
444
445     pwstFirstOutput[iPos] = 0;
446     return pwstFirstOutput;
447 }
448 /*--------------------------------------------------------------------------*/