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