9c85ee34d265393a31da05b3e0c02f30a3e5ae33
[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 #include <cmath>
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_string.h"
31 #include "os_wtoi.h"
32 #include "os_string.h"
33 }
34
35 #define NanString L"Nan"
36 #define InfString L"Inf"
37 #define NegInfString L"-Inf"
38
39 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine);
40 static wchar_t* addl(TokenDef* token);
41 static void updatel(TokenDef* token);
42 wchar_t** scilab_sprintf(const char* _pstName, const wchar_t* _pwstInput, typed_list &in, ArgumentPosition* _pArgs, int _iArgsCount, int* _piOutputRows, int* _piNewLine)
43 {
44     /* Force Windows display to have two-digit exponent. */
45 #ifdef _MSC_VER
46     _set_output_format(_TWO_DIGIT_EXPONENT);
47 #endif
48     wchar_t** pwstOutput = NULL;
49     wchar_t* pwstFirstOutput = NULL;
50     *_piNewLine = 0;
51     size_t pos = 0;
52
53     //\n \n\r \r \t to string
54     //find number of lines
55     // replace \\n \\t... by \n \t...
56     pwstFirstOutput = replaceAndCountLines(_pwstInput, _piOutputRows, _piNewLine);
57
58     //no arg, just return _pwstInput value
59     if (_iArgsCount)
60     {
61         //store all sub parts of the input string
62         //for the input string "bla1 %s bla2 %d bla3"
63         //store  :
64         //pwstToken[0] : "bla1 "
65         //pwstToken[1] : "%s bla2 "
66         //pwstToken[2] : "%d bla3"
67
68         size_t iStart = 0;
69         size_t iEnd = 0;
70         int iToken = 0;
71         int iPosArg = 0;
72
73         TokenDef* pToken = new TokenDef[_iArgsCount + 1];
74         wchar_t* pwstStart = pwstFirstOutput;
75
76         bool bFinish = false;
77         bool bPercentPercent = false;
78
79         while (!bFinish)
80         {
81             wchar_t* pwstEnd = wcsstr(pwstStart + (iToken == 0 ? 0 : 1), L"%");
82             iStart = pwstStart - pwstFirstOutput;
83             bPercentPercent = false;
84             if (pwstEnd != NULL)
85             {
86                 if (iToken && pwstStart[1] == L'%')
87                 {
88                     //manage "%%"
89                     pwstEnd = wcsstr(pwstEnd + 1, L"%");
90                     if (pwstEnd == NULL)
91                     {
92                         //end of string
93                         iEnd = wcslen(pwstFirstOutput);
94                         bFinish = true;
95                     }
96                     else
97                     {
98                         iEnd = pwstEnd - pwstFirstOutput;
99                     }
100
101                     // skip the first %
102                     iStart++;
103                     bPercentPercent = true;
104                 }
105                 else
106                 {
107                     iEnd = pwstEnd - pwstFirstOutput;
108                 }
109             }
110             else
111             {
112                 //end of string
113                 iEnd = wcslen(pwstFirstOutput);
114                 bFinish = true;
115             }
116
117             pToken[iToken].pwstToken = new wchar_t[iEnd - iStart + 1];
118             wcsncpy(pToken[iToken].pwstToken, pwstFirstOutput + iStart, iEnd - iStart);
119             pToken[iToken].pwstToken[iEnd - iStart] = L'\0';
120             pToken[iToken].outputType = InternalType::ScilabNull;
121
122             //identify destination type
123             //format : %[flags][width][.precision][length]specifier
124             //pToken[iToken].pwstToken
125
126             //find %
127             wchar_t* pwstPercent = wcsstr(pToken[iToken].pwstToken, L"%");
128             if (pwstPercent != NULL && bPercentPercent == false)
129             {
130                 //looking for flags
131                 if (*(pwstPercent + 1) == L'-' ||
132                         *(pwstPercent + 1) == L'+' ||
133                         *(pwstPercent + 1) == L' ' ||
134                         *(pwstPercent + 1) == L'#' ||
135                         *(pwstPercent + 1) == L'0')
136                 {
137                     pwstPercent++;
138                 }
139
140                 //looking for width
141                 pToken[iToken].width = -1;
142                 if (*(pwstPercent + 1) == L'*')
143                 {
144                     pwstPercent++;
145                 }
146                 else
147                 {
148                     //number
149                     if (iswdigit(*(pwstPercent + 1)))
150                     {
151                         pToken[iToken].width = os_wtoi(pwstPercent + 1);
152                         while (iswdigit(*(pwstPercent + 1)))
153                         {
154                             pwstPercent++;
155                         }
156                     }
157                 }
158
159                 //looking for precision
160                 pToken[iToken].prec = -1;
161                 if (*(pwstPercent + 1) == L'.')
162                 {
163                     pwstPercent++;
164
165                     if (iswdigit(*(pwstPercent + 1)))
166                     {
167                         pToken[iToken].prec = os_wtoi(pwstPercent + 1);
168                         while (iswdigit(*(pwstPercent + 1)))
169                         {
170                             pwstPercent++;
171                         }
172                     }
173                 }
174
175                 //looking for length
176                 if (*(pwstPercent + 1) == L'h' ||
177                         *(pwstPercent + 1) == L'l' ||
178                         *(pwstPercent + 1) == L'L')
179                 {
180                     pToken[iToken].bLengthFlag = true;
181                     pwstPercent++;
182                 }
183                 else
184                 {
185                     pToken[iToken].bLengthFlag = false;
186                 }
187
188                 wchar_t wcType = *(pwstPercent + 1);
189                 pToken[iToken].typePos = (pwstPercent + 1) - pToken[iToken].pwstToken;
190                 switch (wcType)
191                 {
192                     case L'i' : //integer
193                     case L'd' : //integer
194                         if (_pArgs[iPosArg].type != InternalType::ScilabDouble)
195                         {
196                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
197                             *_piOutputRows = 0;
198                             return NULL;
199                         }
200
201                         pToken[iToken].outputType = InternalType::ScilabInt32;
202
203                         iPosArg++;
204                         break;
205
206                     case L'o' : //octal
207                     case L'u' : //unsigned
208                     case L'x' : //hex
209                     case L'X' : //HEX
210                         if (_pArgs[iPosArg].type != InternalType::ScilabDouble)
211                         {
212                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
213                             *_piOutputRows = 0;
214                             return NULL;
215                         }
216
217                         pToken[iToken].outputType = InternalType::ScilabUInt32;
218
219                         iPosArg++;
220                         break;
221                     case L'f' : //float
222                     case L'e' : //exp
223                     case L'E' : //EXP
224                     case L'g' : //shorter between float or exp
225                     case L'G' : //shorter between float or EXP
226                         if (_pArgs[iPosArg].type != InternalType::ScilabDouble)
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::ScilabDouble;
234                         iPosArg++;
235                         break;
236                     case L's' :
237                     case L'c' :
238                         if (_pArgs[iPosArg].type != InternalType::ScilabString)
239                         {
240                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
241                             *_piOutputRows = 0;
242                             return NULL;
243                         }
244
245                         if (pToken[iToken].bLengthFlag == false)
246                         {
247                             updatel(&(pToken[iToken]));
248                         }
249
250                         pToken[iToken].outputType = InternalType::ScilabString;
251                         iPosArg++;
252                         break;
253                     default :
254                         //houston ...
255                         break;
256                 }
257             }
258
259             pwstStart = pwstEnd;
260             iToken++;
261         }
262
263         FREE(pwstFirstOutput);
264         pwstFirstOutput = NULL;
265
266         int iLoop = 1;
267         int iFirstArg = 1;
268         if (strcmp(_pstName, "mfprintf") == 0)
269         {
270             iFirstArg = 2;
271         }
272
273         if (in.size() > 1)
274         {
275             iLoop = in[iFirstArg]->getAs<GenericType>()->getRows();
276             for (int i = iFirstArg + 1 ; i < in.size() ; i++)
277             {
278                 iLoop = std::min(iLoop, in[i]->getAs<GenericType>()->getRows());
279             }
280         }
281
282         if (*_piNewLine || (*_piOutputRows) > 1)
283         {
284             (*_piOutputRows) *= iLoop;
285         }
286
287         std::wostringstream oFirstOutput;
288         for (int j = 0 ; j < iLoop ; j++)
289         {
290             //copy the 0th token
291             oFirstOutput << pToken[0].pwstToken;
292             iPosArg = 0;
293             //start at 1, the 0th is always without %
294             for (int i = 1 ; i < _iArgsCount + 1 ; i++)
295             {
296                 if (pToken[i].outputType == InternalType::ScilabDouble)
297                 {
298                     wchar_t pwstTemp[bsiz];
299                     double dblVal = in[_pArgs[iPosArg].iArg]->getAs<Double>()->get(j, _pArgs[iPosArg].iPos);
300
301                     if (finite(dblVal))
302                     {
303                         os_swprintf(pwstTemp, bsiz, pToken[i].pwstToken, dblVal);
304                     }
305                     else
306                     {
307                         wchar_t* newToken = addl(&pToken[i]);
308
309                         if (ISNAN(dblVal))
310                         {
311                             os_swprintf(pwstTemp, bsiz, newToken, NanString);
312                         }
313                         else if (std::signbit(dblVal))
314                         {
315                             os_swprintf(pwstTemp, bsiz, newToken, NegInfString);
316                         }
317                         else
318                         {
319                             os_swprintf(pwstTemp, bsiz, newToken, InfString);
320                         }
321
322                         delete[] newToken;
323                     }
324                     iPosArg++;
325                     oFirstOutput << pwstTemp;
326                 }
327                 else if (pToken[i].outputType == InternalType::ScilabInt32)
328                 {
329
330                     wchar_t pwstTemp[bsiz];
331                     double dblVal = in[_pArgs[iPosArg].iArg]->getAs<Double>()->get(j, _pArgs[iPosArg].iPos);
332
333                     if (finite(dblVal))
334                     {
335                         os_swprintf(pwstTemp, bsiz, pToken[i].pwstToken, (int)dblVal);
336                     }
337                     else
338                     {
339                         wchar_t* newToken = addl(&pToken[i]);
340
341                         if (ISNAN(dblVal))
342                         {
343                             os_swprintf(pwstTemp, bsiz, newToken, NanString);
344                         }
345                         else
346                         {
347                             if (std::signbit(dblVal))
348                             {
349                                 os_swprintf(pwstTemp, bsiz, newToken, NegInfString);
350                             }
351                             else
352                             {
353                                 os_swprintf(pwstTemp, bsiz, newToken, InfString);
354                             }
355                         }
356
357                         delete[] newToken;
358                     }
359                     iPosArg++;
360                     oFirstOutput << pwstTemp;
361                 }
362                 else if (pToken[i].outputType == InternalType::ScilabUInt32)
363                 {
364
365                     wchar_t pwstTemp[bsiz];
366                     double dblVal = in[_pArgs[iPosArg].iArg]->getAs<Double>()->get(j, _pArgs[iPosArg].iPos);
367
368                     if (finite(dblVal))
369                     {
370                         os_swprintf(pwstTemp, bsiz, pToken[i].pwstToken, (unsigned int)dblVal);
371                     }
372                     else
373                     {
374                         wchar_t* newToken = addl(&pToken[i]);
375
376                         if (ISNAN(dblVal))
377                         {
378                             os_swprintf(pwstTemp, bsiz, newToken, NanString);
379                         }
380                         else
381                         {
382                             if (std::signbit(dblVal))
383                             {
384                                 os_swprintf(pwstTemp, bsiz, newToken, NegInfString);
385                             }
386                             else
387                             {
388                                 os_swprintf(pwstTemp, bsiz, newToken, InfString);
389                             }
390                         }
391
392                         delete[] newToken;
393                     }
394                     iPosArg++;
395                     oFirstOutput << pwstTemp;
396                 }
397                 else if (pToken[i].outputType == InternalType::ScilabString)
398                 {
399                     wchar_t* pwstStr = NULL;
400
401                     if (in[iPosArg + 1]->isDouble() && ISNAN(in[iPosArg + 1]->getAs<types::Double>()->get(0)))
402                     {
403                         pwstStr = NanString;
404                     }
405                     else if (in[iPosArg + 1]->isDouble() && finite(in[iPosArg + 1]->getAs<types::Double>()->get(0)) == false)
406                     {
407                         if (std::signbit(in[iPosArg + 1]->getAs<types::Double>()->get(0)))
408                         {
409                             pwstStr = NegInfString;
410                         }
411                         else
412                         {
413                             pwstStr = InfString;
414                         }
415                     }
416                     else
417                     {
418                         pwstStr = in[_pArgs[iPosArg].iArg]->getAs<types::String>()->get(j, _pArgs[iPosArg].iPos);
419                     }
420
421                     int posC = (int)wcscspn(pToken[i].pwstToken, L"c");
422                     int posS = (int)wcscspn(pToken[i].pwstToken, L"s");
423
424                     if (posS == 0 || posC == 0)
425                     {
426                         *_piOutputRows = 0;
427                         return NULL;
428                     }
429
430                     bool bC = posC < posS;
431                     int len = 1;
432                     if (pToken[i].prec == -1)
433                     {
434                         if (bC == false)
435                         {
436                             len = (int)wcslen(pwstStr);
437                         }
438                     }
439                     else
440                     {
441                         if (bC == false)
442                         {
443                             len = std::min(pToken[i].prec, (int)wcslen(pwstStr));
444                         }
445                     }
446
447                     int tokenLen = (int)wcslen(pToken[i].pwstToken);
448                     len += tokenLen;
449                     len = std::max(len, pToken[i].width);
450                     //add len of string after token like "%20s>>>" add space for ">>>"
451                     len += (tokenLen - (bC ? posC : posS));
452                     wchar_t* pwstTemp = (wchar_t*)MALLOC((len + 1) * sizeof(wchar_t));
453
454                     if (bC)
455                     {
456                         os_swprintf(pwstTemp, len + 1, pToken[i].pwstToken, pwstStr[0]);
457                     }
458                     else
459                     {
460                         os_swprintf(pwstTemp, len + 1, pToken[i].pwstToken, pwstStr);
461                     }
462
463                     oFirstOutput << pwstTemp;
464                     FREE(pwstTemp);
465                     iPosArg++;
466                 }
467                 else
468                 {
469                     // management of %%
470                     oFirstOutput << pToken[i].pwstToken;
471                 }
472             }
473         }
474
475         pwstFirstOutput = os_wcsdup((wchar_t*)oFirstOutput.str().c_str());
476
477         for (int j = 0; j < _iArgsCount + 1; ++j)
478         {
479             delete[] pToken[j].pwstToken;
480         }
481         delete[] pToken;
482
483     }
484
485     pwstOutput = (wchar_t**)MALLOC((*_piOutputRows) * sizeof(wchar_t*));
486
487     size_t iLen = wcslen(pwstFirstOutput);
488     int iStart = 0;
489     int j = 0;
490     for (int i = 0 ; i < iLen ; i++)
491     {
492         if (pwstFirstOutput[i] == L'\n')
493         {
494             int iSize = i - iStart;
495             pwstOutput[j] = (wchar_t*)MALLOC(sizeof(wchar_t) * (iSize + 1));
496             wcsncpy(pwstOutput[j], pwstFirstOutput + iStart, iSize);
497             pwstOutput[j][iSize] = L'\0';
498             iStart = i + 1;
499             j++;
500         }
501     }
502
503     if (j == (*_piOutputRows) - 1)
504     {
505         pwstOutput[j] = os_wcsdup(pwstFirstOutput + iStart);
506     }
507
508     FREE(pwstFirstOutput);
509     return pwstOutput;
510 }
511 /*--------------------------------------------------------------------------*/
512 // replace "\\n" "\\r" "\\t" "\\r\\n" by '\n' '\r' '\t' '\n'
513 // count number of lines
514 // indicate if one '\n' is at the end of string
515 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine)
516 {
517     size_t iInputLen = wcslen(_pwstInput);
518     wchar_t* pwstFirstOutput = (wchar_t*)MALLOC(sizeof(wchar_t) * (iInputLen + 1));
519
520     int iPos = 0;
521     *_piLines = 1;
522
523     for (int i = 0 ; i < iInputLen ; i++)
524     {
525         if (_pwstInput[i] == L'\\')
526         {
527             if (iInputLen == i + 1)
528             {
529                 continue;
530             }
531
532             switch (_pwstInput[i + 1])
533             {
534                 case L'n' :
535                     pwstFirstOutput[iPos++] = L'\n';
536                     (*_piLines)++;
537                     i++;
538                     break;
539                 case L'r' :
540                     if (iInputLen > i + 3 && _pwstInput[i + 2] == L'\\' && _pwstInput[i + 3] == L'n')
541                     {
542                         pwstFirstOutput[iPos++] = L'\n';
543                         (*_piLines)++;
544                         i += 3;
545                     }
546                     else
547                     {
548                         pwstFirstOutput[iPos++] = L'\r';
549                         i++;
550                     }
551                     break;
552                 case L't' :
553                     pwstFirstOutput[iPos++] = L'\t';
554                     i++;
555                     break;
556                 case L'\\':
557                     pwstFirstOutput[iPos++] = L'\\';
558                     i++;
559                     break;
560                 default:
561                     break;
562             }
563         }
564         else
565         {
566             pwstFirstOutput[iPos++] = _pwstInput[i];
567         }
568     }
569
570     // do not count '\n' if it's at the end of string
571     // it will be manage by piNewLine
572     if (pwstFirstOutput[iPos - 1] == '\n')
573     {
574         (*_piLines)--;
575         (*_piNewLine) = 1;
576     }
577
578     pwstFirstOutput[iPos] = 0;
579     return pwstFirstOutput;
580 }
581 /*--------------------------------------------------------------------------*/
582 wchar_t* addl(TokenDef* token)
583 {
584     //replace %s or %c by %ls or %lc to wide char compatibility
585     int iPos = token->typePos;
586     int sizeTotal = (int)wcslen(token->pwstToken);
587     wchar_t* pwstToken = new wchar_t[sizeTotal + 2];
588
589     wcsncpy(pwstToken, token->pwstToken, iPos);
590     pwstToken[iPos] = L'l';
591     pwstToken[iPos + 1] = L's';
592     wcsncpy(&pwstToken[iPos + 2], token->pwstToken + iPos + 1, sizeTotal - (iPos + 1));
593     pwstToken[sizeTotal + 1]  = L'\0';
594
595     return pwstToken;
596 }
597 /*--------------------------------------------------------------------------*/
598 void updatel(TokenDef* token)
599 {
600     wchar_t* newToken = addl(token);
601     delete[] token->pwstToken;
602     token->pwstToken = newToken;
603 }