23b31e70693bf59d7532235d59f76850bf98baad
[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) 2013 - Scilab Enterprises - Calixte DENIZET
4  *  Copyright (C) 2013 - Scilab Enterprises - Cedric Delamarre
5  *  Copyright (C) 2015 - Scilab Enterprises - Antoine ELIAS
6  *
7  * Copyright (C) 2012 - 2016 - Scilab Enterprises
8  *
9  * This file is hereby licensed under the terms of the GNU GPL v2.0,
10  * pursuant to article 5.3.4 of the CeCILL v.2.1.
11  * This file was originally licensed under the terms of the CeCILL v2.1,
12  * and continues to be available under such terms.
13  * For more information, see the COPYING file which you should have received
14  * along with this program.
15  *
16  */
17
18 #include <stdio.h>
19 #include <cmath>
20 #include <list>
21 #include <numeric>
22 #include "types.hxx"
23 #include "double.hxx"
24 #include "string.hxx"
25 #include "scilab_sprintf.hxx"
26
27 extern "C"
28 {
29 #include "Scierror.h"
30 #include "sci_malloc.h"
31 #include "localization.h"
32 #include "charEncoding.h"
33 #include "os_string.h"
34 #include "os_wtoi.h"
35 #include "os_string.h"
36 }
37
38 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine);
39 static wchar_t* replace_with_ls(TokenDef* token);
40 static void updatel(TokenDef* token);
41 static void replace_lu_llu(TokenDef* token);
42 static void replace_ld_lld(TokenDef* token);
43 static void print_nan_or_inf(wchar_t* pwstTemp, double dblVal, const wchar_t* token, int pos, int width);
44
45 #define NanString L"Nan"
46 #define InfString L"Inf"
47 #define NegInfString L"-Inf"
48
49 wchar_t** scilab_sprintf(const std::string& funcname, const wchar_t* _pwstInput, types::typed_list &in, int* _piOutputRows, int* _piNewLine)
50 {
51     wchar_t** pwstOutput = nullptr;
52     int rhs = static_cast<int>(in.size());
53     wchar_t* pwstFirstOutput = nullptr;
54     *_piNewLine = 0;
55     int col = 0;
56
57     int first = 1;
58     if (funcname == "mfprintf")
59     {
60         first = 2;
61     }
62
63     //compute couple (index in input and col number ).
64     std::list<std::pair<int, int> > inPos;
65     for (int i = first; i < in.size(); ++i)
66     {
67         types::GenericType* gt = in[i]->getAs<types::GenericType>();
68         int col = gt->getCols();
69         for (int j = 0; j < col; ++j)
70         {
71             inPos.emplace_back(i, j);
72         }
73     }
74
75     std::list<std::pair<int, int> >::iterator itPos = inPos.begin();
76
77     //\n \n\r \r \t to string
78     //find number of lines
79     // replace \\n \\t... by \n \t...
80     pwstFirstOutput = replaceAndCountLines(_pwstInput, _piOutputRows, _piNewLine);
81
82     std::list<TokenDef*> token;
83
84     size_t start = 0;
85     size_t end = 0;
86     bool finish = false;
87
88     wchar_t* pwstStart = pwstFirstOutput;
89     bool percentpercent = false;
90     std::vector<int> argumentPos;
91
92     while (finish == false)
93     {
94         wchar_t* pwstEnd = wcsstr(pwstStart + (token.size() == 0 ? 0 : 1), L"%");
95         start = pwstStart - pwstFirstOutput;
96         percentpercent = false;
97         bool positioned = false;
98         if (pwstEnd != nullptr)
99         {
100             if (token.size() && pwstStart[1] == L'%')
101             {
102                 //manage "%%"
103                 pwstEnd = wcsstr(pwstEnd + 1, L"%");
104                 if (pwstEnd == nullptr)
105                 {
106                     //end of string
107                     end = wcslen(pwstFirstOutput);
108                     finish = true;
109                 }
110                 else
111                 {
112                     end = pwstEnd - pwstFirstOutput;
113                 }
114
115                 // skip the first %
116                 start++;
117                 percentpercent = true;
118             }
119             else
120             {
121                 end = pwstEnd - pwstFirstOutput;
122             }
123         }
124         else
125         {
126             //end of string
127             end = wcslen(pwstFirstOutput);
128             finish = true;
129         }
130
131         // parameter field placeholders; manage "%2$d" field
132         if (end > 0)
133         {
134             wchar_t* pwstDollar = wcsstr(pwstStart + (token.size() == 0 ? 0 : 1), L"$");
135             if (pwstDollar != nullptr && (pwstDollar - pwstFirstOutput) < end)
136             {
137                 int index = os_wtoi(pwstStart + 1);
138                 if (index > 0)
139                 {
140                     argumentPos.push_back(index - 1);
141                     start = pwstDollar - pwstFirstOutput;
142                     pwstFirstOutput[start] = L'%';
143                     positioned = true;
144                 }
145             }
146         }
147
148         TokenDef* tok = new TokenDef;
149         tok->pwstToken = new wchar_t[end - start + 1];
150         wcsncpy(tok->pwstToken, pwstFirstOutput + start, end - start);
151         tok->pwstToken[end - start] = L'\0';
152         token.push_back(tok);
153
154         wchar_t* pwstPercent = wcsstr(tok->pwstToken, L"%");
155         if (pwstPercent != nullptr && percentpercent == false)
156         {
157             int offset = 1;
158             //looking for flags
159             if (*(pwstPercent + 1) == L'-' ||
160                     *(pwstPercent + 1) == L'+' ||
161                     *(pwstPercent + 1) == L' ' ||
162                     *(pwstPercent + 1) == L'#' ||
163                     *(pwstPercent + 1) == L'0')
164             {
165                 offset = 2;
166             }
167
168             //looking for width
169             if (*(pwstPercent + offset) == L'*')
170             {
171                 if (itPos == inPos.end())
172                 {
173                     Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
174                     *_piOutputRows = 0;
175                     return nullptr;
176                 }
177
178                 int p = (*itPos).first;
179                 //input data use to set width
180                 if (in[p]->getId() != types::InternalType::IdScalarDouble)
181                 {
182                     Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p);
183                     *_piOutputRows = 0;
184                     return nullptr;
185                 }
186
187
188                 types::Double* dbl = in[p]->getAs<types::Double>();
189                 tok->width = static_cast<int>(dbl->get()[0]);
190                 tok->widthStar = true;
191                 ++itPos;
192                 ++pwstPercent;
193             }
194             else
195             {
196                 //number
197                 if (iswdigit(*(pwstPercent + offset)))
198                 {
199                     tok->width = os_wtoi(pwstPercent + 1);
200                     while (iswdigit(*(pwstPercent + offset)))
201                     {
202                         pwstPercent++;
203                     }
204                 }
205             }
206
207             pwstPercent += (offset - 1);
208
209             //looking for precision
210             if (*(pwstPercent + 1) == L'.')
211             {
212                 pwstPercent++;
213
214                 if (iswdigit(*(pwstPercent + 1)))
215                 {
216                     tok->prec = os_wtoi(pwstPercent + 1);
217                     while (iswdigit(*(pwstPercent + 1)))
218                     {
219                         pwstPercent++;
220                     }
221                 }
222                 else if (*(pwstPercent + 1) == L'*')
223                 {
224                     if (itPos == inPos.end())
225                     {
226                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
227                         *_piOutputRows = 0;
228                         return nullptr;
229                     }
230
231                     int p = (*itPos).first;
232                     //input data use to set prec
233                     if (in[p]->getId() != types::InternalType::IdScalarDouble)
234                     {
235                         Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p + 1);
236                         *_piOutputRows = 0;
237                         return nullptr;
238                     }
239
240                     types::Double* dbl = in[p]->getAs<types::Double>();
241                     tok->prec = static_cast<int>(dbl->get()[0]);
242                     tok->precStar = true;
243                     ++itPos;
244                     ++pwstPercent;
245                 }
246             }
247
248             //looking for length
249             if (*(pwstPercent + 1) == L'h' ||
250                     *(pwstPercent + 1) == L'l' ||
251                     *(pwstPercent + 1) == L'L')
252             {
253                 tok->length = true;
254                 pwstPercent++;
255             }
256
257             wchar_t wcType = *(pwstPercent + 1);
258             tok->typePos = static_cast<int>((pwstPercent + 1) - tok->pwstToken);
259
260             //check numer of input
261             //printf("%f") or printf("%$2", 1) for example
262             if (itPos == inPos.end() || (positioned ? argumentPos.back() + 1 : (*itPos).first) >= in.size())
263             {
264                 FREE(pwstFirstOutput);
265                 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
266                 *_piOutputRows = 0;
267                 return nullptr;
268             }
269
270             int p = positioned ? argumentPos.back() + 1 : (*itPos).first;
271             int c = (*itPos).second;
272
273             switch (wcType)
274             {
275                 case L'i': //integer
276                 case L'd': //integer
277                 {
278                     if (itPos == inPos.end())
279                     {
280                         FREE(pwstFirstOutput);
281                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
282                         *_piOutputRows = 0;
283                         return nullptr;
284                     }
285
286                     if (in[p]->getType() != types::InternalType::ScilabDouble)
287                     {
288                         FREE(pwstFirstOutput);
289                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
290                         *_piOutputRows = 0;
291                         return nullptr;
292                     }
293
294                     tok->outputType = types::InternalType::ScilabInt64;
295                     tok->pos = p;
296                     tok->col = c;
297                     ++itPos;
298                     break;
299                 }
300                 case L'o': //octal
301                 case L'u': //unsigned
302                 case L'x': //hex
303                 case L'X': //HEX
304                 {
305                     if (itPos == inPos.end())
306                     {
307                         FREE(pwstFirstOutput);
308                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
309                         *_piOutputRows = 0;
310                         return nullptr;
311                     }
312
313                     int p = (*itPos).first;
314                     int c = (*itPos).second;
315                     if (in[p]->getType() != types::InternalType::ScilabDouble)
316                     {
317                         FREE(pwstFirstOutput);
318                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
319                         *_piOutputRows = 0;
320                         return nullptr;
321                     }
322
323                     tok->outputType = types::InternalType::ScilabUInt64;
324                     tok->pos = p;
325                     tok->col = c;
326                     ++itPos;
327                     break;
328                 }
329                 case L'f': //float
330                 case L'e': //exp
331                 case L'E': //EXP
332                 case L'g': //shorter between float or exp
333                 case L'G': //shorter between float or EXP
334                 {
335                     if (itPos == inPos.end())
336                     {
337                         FREE(pwstFirstOutput);
338                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
339                         *_piOutputRows = 0;
340                         return nullptr;
341                     }
342
343                     if (in[p]->getType() != types::InternalType::ScilabDouble)
344                     {
345                         FREE(pwstFirstOutput);
346                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
347                         *_piOutputRows = 0;
348                         return nullptr;
349                     }
350
351                     tok->outputType = types::InternalType::ScilabDouble;
352                     tok->pos = p;
353                     tok->col = c;
354                     ++itPos;
355                     break;
356                 }
357                 case L's':
358                 case L'c':
359                 {
360                     if (itPos == inPos.end())
361                     {
362                         FREE(pwstFirstOutput);
363                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
364                         *_piOutputRows = 0;
365                         return nullptr;
366                     }
367
368                     if (in[p]->getType() != types::InternalType::ScilabString)
369                     {
370                         FREE(pwstFirstOutput);
371                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
372                         *_piOutputRows = 0;
373                         return nullptr;
374                     }
375
376                     if (tok->length == false)
377                     {
378                         updatel(tok);
379                     }
380
381                     tok->outputType = types::InternalType::ScilabString;
382                     tok->pos = p;
383                     tok->col = c;
384                     ++itPos;
385                     break;
386                 }
387                 default:
388                     FREE(pwstFirstOutput);
389                     Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
390                     *_piOutputRows = 0;
391                     return nullptr;
392                     break;
393             }
394         }
395
396         //continue
397         pwstStart = pwstEnd;
398     }
399
400     FREE(pwstFirstOutput);
401     pwstFirstOutput = nullptr;
402
403     int iLoop = 1;
404     if (token.size() > 1 && in.size() > first)
405     {
406         iLoop = in[first]->getAs<types::GenericType>()->getRows();
407         for (int i = first + 1; i < in.size(); ++i)
408         {
409             iLoop = std::min(iLoop, in[i]->getAs<types::GenericType>()->getRows());
410         }
411     }
412
413     // parameter field placeholders MUST all be specified
414     if (!argumentPos.empty())
415     {
416         std::vector<int> sortedArgumentPos = argumentPos;
417         std::sort(sortedArgumentPos.begin(), sortedArgumentPos.end());
418         int previous = 0;
419         for (int i = 1; i < sortedArgumentPos.size() && 0 <= previous && previous < 2; ++i)
420         {
421             previous = sortedArgumentPos[i] - sortedArgumentPos[i - 1];
422         }
423         if (previous < 0 || 2 < previous)
424         {
425             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
426             *_piOutputRows = 0;
427             return nullptr;
428         }
429         if (sortedArgumentPos.back() + 1 != token.size() - 1)
430         {
431             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
432             *_piOutputRows = 0;
433             return nullptr;
434         }
435         if (argumentPos.size() != token.size() - 1)
436         {
437             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
438             *_piOutputRows = 0;
439             return nullptr;
440         }
441
442         /*
443         // update token's pos
444         for (auto it = token.begin(); it != token.end(); ++it)
445         {
446             TokenDef* tok = *it;
447             if (tok->pos == 0)
448             {
449                 continue;
450             }
451             tok->pos = argumentPos[tok->pos - 1] + 1;
452         }
453         */
454     }
455
456     // count number of output lines in function of \n and size of input args
457     (*_piOutputRows) *= iLoop;
458     (*_piOutputRows) += *_piNewLine == 1 ? 0 : 1;
459
460     pwstOutput = (wchar_t**)MALLOC((*_piOutputRows) * sizeof(wchar_t*));
461     int outputIter = 0;
462     std::wostringstream oFirstOutput;
463     for (int j = 0; j < iLoop; ++j)
464     {
465         wchar_t* tmpToken = NULL;
466         for (auto it = token.begin(); it != token.end();/*no inc*/)
467         {
468             TokenDef* tok = *it;
469             wchar_t* token = tmpToken ? tmpToken : tok->pwstToken;
470             // find LF in token
471             wchar_t* lf = wcsstr(token, L"\n");
472             if (lf)
473             {
474                 // create tkoen as part of current token
475                 size_t sToken = lf - token;
476                 token = (wchar_t*)MALLOC((sToken + 1) * sizeof(wchar_t));
477                 wcsncpy(token, tmpToken ? tmpToken : tok->pwstToken, sToken);
478                 token[sToken] = L'\0';
479             }
480
481             // if tmpToken != NULL: we already have managed the % so manage this (part) token as a string without %
482             switch (tmpToken ? types::InternalType::ScilabNull : tok->outputType)
483             {
484                 case types::InternalType::ScilabDouble:
485                 {
486                     wchar_t pwstTemp[bsiz];
487                     double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
488
489                     if (std::isfinite(dblVal))
490                     {
491                         if (tok->widthStar)
492                         {
493                             if (tok->precStar)
494                             {
495                                 os_swprintf(pwstTemp, bsiz, token, tok->width, tok->prec, dblVal);
496                             }
497                             else
498                             {
499                                 os_swprintf(pwstTemp, bsiz, token, tok->width, dblVal);
500                             }
501                         }
502                         else
503                         {
504                             if (tok->precStar)
505                             {
506                                 os_swprintf(pwstTemp, bsiz, token, tok->prec, dblVal);
507                             }
508                             else
509                             {
510                                 os_swprintf(pwstTemp, bsiz, token, dblVal);
511                             }
512                         }
513                     }
514                     else
515                     {
516                         print_nan_or_inf(pwstTemp, dblVal, token, tok->typePos, tok->width);
517                     }
518
519                     oFirstOutput << pwstTemp;
520                     break;
521                 }
522                 case types::InternalType::ScilabInt64:
523                 {
524                     wchar_t pwstTemp[bsiz];
525                     double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
526                     long long iVal = (long long)dblVal;
527
528                     if (std::isfinite(dblVal))
529                     {
530                         replace_ld_lld(tok);
531                         if (tok->widthStar)
532                         {
533                             if (tok->precStar)
534                             {
535                                 os_swprintf(pwstTemp, bsiz, token, tok->width, tok->prec, iVal);
536                             }
537                             else
538                             {
539                                 os_swprintf(pwstTemp, bsiz, token, tok->width, iVal);
540                             }
541                         }
542                         else
543                         {
544                             if (tok->precStar)
545                             {
546                                 os_swprintf(pwstTemp, bsiz, token, tok->prec, iVal);
547                             }
548                             else
549                             {
550                                 os_swprintf(pwstTemp, bsiz, token, iVal);
551                             }
552                         }
553                     }
554                     else
555                     {
556                         print_nan_or_inf(pwstTemp, dblVal, token, tok->typePos, tok->width);
557                     }
558
559                     oFirstOutput << pwstTemp;
560                     break;
561                 }
562                 case types::InternalType::ScilabUInt64:
563                 {
564                     wchar_t pwstTemp[bsiz];
565                     double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
566                     unsigned long long iVal = (unsigned long long)dblVal;
567
568                     if (std::isfinite(dblVal))
569                     {
570                         replace_lu_llu(tok);
571                         if (tok->widthStar)
572                         {
573                             if (tok->precStar)
574                             {
575                                 os_swprintf(pwstTemp, bsiz, token, tok->width, tok->prec, iVal);
576                             }
577                             else
578                             {
579                                 os_swprintf(pwstTemp, bsiz, token, tok->width, iVal);
580                             }
581                         }
582                         else
583                         {
584                             if (tok->precStar)
585                             {
586                                 os_swprintf(pwstTemp, bsiz, token, tok->prec, iVal);
587                             }
588                             else
589                             {
590                                 os_swprintf(pwstTemp, bsiz, token, iVal);
591                             }
592                         }
593                     }
594                     else
595                     {
596                         print_nan_or_inf(pwstTemp, dblVal, token, tok->typePos, tok->width);
597                     }
598
599                     oFirstOutput << pwstTemp;
600                     break;
601                 }
602                 case types::InternalType::ScilabString:
603                 {
604                     wchar_t* pwstStr = nullptr;
605                     std::wstring NaN = NanString;
606                     std::wstring nInf = NegInfString;
607                     std::wstring pInf = InfString;
608
609                     types::InternalType* it = in[tok->pos];
610                     if (it->isDouble() && std::isnan(it->getAs<types::Double>()->get(0)))
611                     {
612                         pwstStr = const_cast<wchar_t*>(NaN.c_str());
613                     }
614                     else if (it->isDouble() && std::isfinite(it->getAs<types::Double>()->get(0)) == false)
615                     {
616                         if (std::signbit(it->getAs<types::Double>()->get(0)))
617                         {
618                             pwstStr = const_cast<wchar_t*>(nInf.c_str());
619                         }
620                         else
621                         {
622                             pwstStr = const_cast<wchar_t*>(pInf.c_str());
623                         }
624                     }
625                     else
626                     {
627                         pwstStr = it->getAs<types::String>()->get(j, tok->col);
628                     }
629
630                     int posC = (int)wcscspn(token, L"c");
631                     int posS = (int)wcscspn(token, L"s");
632
633                     if (posS == 0 || posC == 0)
634                     {
635                         *_piOutputRows = 0;
636                         return nullptr;
637                     }
638
639                     bool bC = posC < posS;
640                     int len = 1;
641                     if (tok->prec)
642                     {
643                         if (bC == false)
644                         {
645                             len = std::min(std::abs(tok->prec), (int)wcslen(pwstStr));
646                         }
647                     }
648                     else
649                     {
650                         if (bC == false)
651                         {
652                             len = (int)wcslen(pwstStr);
653                         }
654                     }
655
656                     int tokenLen = (int)wcslen(token);
657                     len += tokenLen;
658                     len = std::max(len, std::abs(tok->width));
659                     //add len of string after token like "%20s>>>" add space for ">>>"
660                     len += (tokenLen - (bC ? posC : posS));
661                     wchar_t* pwstTemp = (wchar_t*)MALLOC((len + 1) * sizeof(wchar_t));
662
663                     if (bC)
664                     {
665                         if (tok->widthStar)
666                         {
667                             os_swprintf(pwstTemp, len + 1, token, tok->width, pwstStr[0]);
668                         }
669                         else
670                         {
671                             os_swprintf(pwstTemp, len + 1, token, pwstStr[0]);
672                         }
673                     }
674                     else
675                     {
676                         if (tok->widthStar)
677                         {
678                             os_swprintf(pwstTemp, len + 1, token, tok->width, pwstStr);
679                         }
680                         else
681                         {
682                             os_swprintf(pwstTemp, len + 1, token, pwstStr);
683                         }
684                     }
685
686                     oFirstOutput << pwstTemp;
687                     FREE(pwstTemp);
688                     break;
689                 }
690                 default:
691                 {
692                     // management of %%
693                     oFirstOutput << token;
694                 }
695             }
696
697             if (lf)
698             {
699                 // write current line
700                 pwstOutput[outputIter++] = os_wcsdup((wchar_t*)oFirstOutput.str().c_str());
701                 // clear temporary stream and token
702                 oFirstOutput.clear();
703                 oFirstOutput.str(L"");
704                 FREE(token);
705                 // skip LF
706                 tmpToken = ++lf;
707                 // if not at the end of token, continue with same token
708                 if (static_cast<size_t>(lf - tok->pwstToken) < wcslen(tok->pwstToken))
709                 {
710                     continue;
711                 }
712             }
713
714             tmpToken = NULL;
715             ++it;
716         }
717     }
718
719     // write last line
720     if (oFirstOutput.str().length())
721     {
722         pwstOutput[outputIter++] = os_wcsdup((wchar_t*)oFirstOutput.str().c_str());
723     }
724     // fill in the remaining allocated space with default value
725     while (outputIter < *_piOutputRows)
726     {
727         pwstOutput[outputIter++] = os_wcsdup(L"");
728     }
729
730     for (auto & tok : token)
731     {
732         delete[] tok->pwstToken;
733         delete tok;
734     }
735
736     return pwstOutput;
737 }
738 /*--------------------------------------------------------------------------*/
739 /*--------------------------------------------------------------------------*/
740 // replace "\\n" "\\r" "\\t" "\\r\\n" by '\n' '\r' '\t' '\n'
741 // count number of lines
742 // indicate if one '\n' is at the end of string
743 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine)
744 {
745     size_t iInputLen = wcslen(_pwstInput);
746     wchar_t* pwstFirstOutput = (wchar_t*)MALLOC(sizeof(wchar_t) * (iInputLen + 1));
747
748     int iPos = 0;
749     *_piLines = 0;
750
751     for (int i = 0; i < iInputLen; i++)
752     {
753         if (_pwstInput[i] == L'\\')
754         {
755             if (iInputLen == i + 1)
756             {
757                 continue;
758             }
759
760             switch (_pwstInput[i + 1])
761             {
762                 case L'n':
763                     pwstFirstOutput[iPos++] = L'\n';
764                     (*_piLines)++;
765                     i++;
766                     break;
767                 case L'r':
768                     if (iInputLen > i + 3 && _pwstInput[i + 2] == L'\\' && _pwstInput[i + 3] == L'n')
769                     {
770                         pwstFirstOutput[iPos++] = L'\n';
771                         (*_piLines)++;
772                         i += 3;
773                     }
774                     else
775                     {
776                         pwstFirstOutput[iPos++] = L'\r';
777                         i++;
778                     }
779                     break;
780                 case L't':
781                     pwstFirstOutput[iPos++] = L'\t';
782                     i++;
783                     break;
784                 case L'\\':
785                     pwstFirstOutput[iPos++] = L'\\';
786                     i++;
787                     break;
788                 default:
789                     break;
790             }
791         }
792         else if (_pwstInput[i] == 0x0A) // ascii(10) == "\n"
793         {
794             pwstFirstOutput[iPos++] = L'\n';
795             (*_piLines)++;
796         }
797         else
798         {
799             pwstFirstOutput[iPos++] = _pwstInput[i];
800         }
801     }
802
803     // do not count '\n' if it's at the end of string
804     // it will be manage by piNewLine
805     if (iPos > 0 && pwstFirstOutput[iPos - 1] == '\n')
806     {
807         (*_piNewLine) = 1;
808     }
809
810     pwstFirstOutput[iPos] = 0;
811     return pwstFirstOutput;
812 }
813 /*--------------------------------------------------------------------------*/
814 static wchar_t* replace_with_ls(TokenDef* token)
815 {
816     //replace %s or %c by %ls or %lc to wide char compatibility
817     int iPos = token->typePos;
818     int sizeTotal = (int)wcslen(token->pwstToken);
819     wchar_t* pwstToken = new wchar_t[sizeTotal + 2];
820
821     wcsncpy(pwstToken, token->pwstToken, iPos);
822     pwstToken[iPos] = L'l';
823     pwstToken[iPos + 1] = L's';
824     wcsncpy(&pwstToken[iPos + 2], token->pwstToken + iPos + 1, sizeTotal - (iPos + 1));
825     pwstToken[sizeTotal + 1] = L'\0';
826
827     return pwstToken;
828 }
829 /*--------------------------------------------------------------------------*/
830 static void updatel(TokenDef* token)
831 {
832     wchar_t* newToken = replace_with_ls(token);
833     delete[] token->pwstToken;
834     token->pwstToken = newToken;
835 }
836 /*--------------------------------------------------------------------------*/
837 static wchar_t* replace(const wchar_t* s, const wchar_t* r, int pos, const wchar_t* token)
838 {
839     std::wstring h(token);
840     //find r
841     h.replace(pos - 1, wcslen(s), r);
842
843     wchar_t* res = new wchar_t[h.size() + 1];
844     wcscpy(res, h.data());
845     return res;
846 }
847 /*--------------------------------------------------------------------------*/
848 static void replace_lu_llu(TokenDef* token)
849 {
850 #ifdef _MSC_VER
851     if (token->length)
852     {
853         wchar_t* newToken = replace(L"lu", L"llu", token->typePos, token->pwstToken);
854         if (newToken)
855         {
856             delete[] token->pwstToken;
857             token->pwstToken = newToken;
858         }
859     }
860 #endif
861 }
862 /*--------------------------------------------------------------------------*/
863 static void replace_ld_lld(TokenDef* token)
864 {
865 #ifdef _MSC_VER
866     if (token->length)
867     {
868         wchar_t* newToken = replace(L"ld", L"lld", token->typePos, token->pwstToken);
869         if (newToken)
870         {
871             delete[] token->pwstToken;
872             token->pwstToken = newToken;
873         }
874     }
875 #endif
876 }
877 /*--------------------------------------------------------------------------*/
878 static void print_nan_or_inf(wchar_t* pwstTemp, double dblVal, const wchar_t* token, int pos, int width)
879 {
880     int sizeTotal = (int)wcslen(token);
881     wchar_t* pwstToken = new wchar_t[sizeTotal + 2] { 0 };
882
883     if (width)
884     {
885         os_swprintf(pwstToken, sizeTotal + 2, L"%%%dls%ls", width, token + pos + 1);
886     }
887     else
888     {
889         os_swprintf(pwstToken, sizeTotal + 2, L"%%ls%ls", token + pos + 1);
890     }
891
892     if (std::isnan(dblVal))
893     {
894         os_swprintf(pwstTemp, bsiz, pwstToken, NanString);
895     }
896     else if (std::signbit(dblVal))
897     {
898         os_swprintf(pwstTemp, bsiz, pwstToken, NegInfString);
899     }
900     else
901     {
902         os_swprintf(pwstTemp, bsiz, pwstToken, InfString);
903     }
904
905     delete[] pwstToken;
906
907 }