Merge remote-tracking branch 'origin/master' into windows
[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 "types.hxx"
22 #include "double.hxx"
23 #include "string.hxx"
24 #include "scilab_sprintf.hxx"
25
26 extern "C"
27 {
28 #include "Scierror.h"
29 #include "sci_malloc.h"
30 #include "localization.h"
31 #include "charEncoding.h"
32 #include "os_string.h"
33 #include "os_wtoi.h"
34 #include "os_string.h"
35 }
36
37 static char* replaceAndCountLines(const char* _pstInput, int* _piLines, int* _piNewLine);
38 static char* replacebys(TokenDef* token);
39
40 #define NanString "Nan"
41 #define InfString "Inf"
42 #define NegInfString "-Inf"
43
44 char** scilab_sprintf(const std::string& funcname, const char* _pwstInput, types::typed_list &in, int* _piOutputRows, int* _piNewLine)
45 {
46     /* Force Windows display to have two-digit exponent. */
47 #ifdef _MSC_VER
48     _set_output_format(_TWO_DIGIT_EXPONENT);
49 #endif
50     char** pstOutput = nullptr;
51     int rhs = in.size();
52     char* pstFirstOutput = nullptr;
53     *_piNewLine = 0;
54     int col = 0;
55
56     int first = 1;
57     if (funcname == "mfprintf")
58     {
59         first = 2;
60     }
61
62     //compute couple (index in input and col number ).
63     std::list<std::pair<int, int> > inPos;
64     for (int i = first; i < in.size(); ++i)
65     {
66         types::GenericType* gt = in[i]->getAs<types::GenericType>();
67         int col = gt->getCols();
68         for (int j = 0; j < col; ++j)
69         {
70             inPos.emplace_back(i, j);
71         }
72     }
73
74     std::list<std::pair<int, int> >::iterator itPos = inPos.begin();
75
76     //\n \n\r \r \t to string
77     //find number of lines
78     // replace \\n \\t... by \n \t...
79     pstFirstOutput = replaceAndCountLines(_pwstInput, _piOutputRows, _piNewLine);
80
81     std::list<TokenDef*> token;
82
83     size_t start = 0;
84     size_t end = 0;
85     bool finish = false;
86
87     char* pstStart = pstFirstOutput;
88     bool percentpercent = false;
89
90     while (finish == false)
91     {
92         char* pstEnd = strstr(pstStart + (token.size() == 0 ? 0 : 1), "%");
93         start = pstStart - pstFirstOutput;
94         percentpercent = false;
95         if (pstEnd != nullptr)
96         {
97             if (token.size() && pstStart[1] == '%')
98             {
99                 //manage "%%"
100                 pstEnd = strstr(pstEnd + 1, "%");
101                 if (pstEnd == nullptr)
102                 {
103                     //end of string
104                     end = strlen(pstFirstOutput);
105                     finish = true;
106                 }
107                 else
108                 {
109                     end = pstEnd - pstFirstOutput;
110                 }
111
112                 // skip the first %
113                 start++;
114                 percentpercent = true;
115             }
116             else
117             {
118                 end = pstEnd - pstFirstOutput;
119             }
120         }
121         else
122         {
123             //end of string
124             end = strlen(pstFirstOutput);
125             finish = true;
126         }
127
128         TokenDef* tok = new TokenDef;
129         tok->pstToken = new char[end - start + 1];
130         strncpy(tok->pstToken, pstFirstOutput + start, end - start);
131         tok->pstToken[end - start] = L'\0';
132         token.push_back(tok);
133
134         char* pstPercent = strstr(tok->pstToken, "%");
135         if (pstPercent != nullptr && percentpercent == false)
136         {
137             //looking for flags
138             if (*(pstPercent + 1) == '-' ||
139                     *(pstPercent + 1) == '+' ||
140                     *(pstPercent + 1) == ' ' ||
141                     *(pstPercent + 1) == '#' ||
142                     *(pstPercent + 1) == '0')
143             {
144                 pstPercent++;
145             }
146
147             //looking for width
148             if (*(pstPercent + 1) == '*')
149             {
150                 if (itPos == inPos.end())
151                 {
152                     Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
153                     *_piOutputRows = 0;
154                     return nullptr;
155                 }
156
157                 int p = (*itPos).first;
158                 //input data use to set width
159                 if (in[p]->getId() != types::InternalType::IdScalarDouble)
160                 {
161                     Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p);
162                     *_piOutputRows = 0;
163                     return nullptr;
164                 }
165
166
167                 types::Double* dbl = in[p]->getAs<types::Double>();
168                 tok->width = static_cast<int>(dbl->get()[0]);
169                 tok->widthStar = true;
170                 ++itPos;
171                 ++pstPercent;
172             }
173             else
174             {
175                 //number
176                 if (isdigit(*(pstPercent + 1)))
177                 {
178                     tok->width = atoi(pstPercent + 1);
179                     while (isdigit(*(pstPercent + 1)))
180                     {
181                         pstPercent++;
182                     }
183                 }
184             }
185
186             //looking for precision
187             if (*(pstPercent + 1) == '.')
188             {
189                 pstPercent++;
190
191                 if (isdigit(*(pstPercent + 1)))
192                 {
193                     tok->prec = atoi(pstPercent + 1);
194                     while (isdigit(*(pstPercent + 1)))
195                     {
196                         pstPercent++;
197                     }
198                 }
199                 else if (*(pstPercent + 1) == '*')
200                 {
201                     if (itPos == inPos.end())
202                     {
203                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
204                         *_piOutputRows = 0;
205                         return nullptr;
206                     }
207
208                     int p = (*itPos).first;
209                     //input data use to set prec
210                     if (in[p]->getId() != types::InternalType::IdScalarDouble)
211                     {
212                         Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p + 1);
213                         *_piOutputRows = 0;
214                         return nullptr;
215                     }
216
217                     types::Double* dbl = in[p]->getAs<types::Double>();
218                     tok->prec = static_cast<int>(dbl->get()[0]);
219                     tok->precStar = true;
220                     ++itPos;
221                     ++pstPercent;
222                 }
223             }
224
225             //looking for length
226             if (*(pstPercent + 1) == 'h' ||
227                     *(pstPercent + 1) == 'l' ||
228                     *(pstPercent + 1) == 'L')
229             {
230                 tok->length = true;
231                 pstPercent++;
232             }
233
234             char type = *(pstPercent + 1);
235             tok->typePos = (pstPercent + 1) - tok->pstToken;
236
237             switch (type)
238             {
239                 case 'i': //integer
240                 case 'd': //integer
241                 {
242                     if (itPos == inPos.end())
243                     {
244                         FREE(pstFirstOutput);
245                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
246                         *_piOutputRows = 0;
247                         return nullptr;
248                     }
249
250                     int p = (*itPos).first;
251                     int c = (*itPos).second;
252                     if (in[p]->getType() != types::InternalType::ScilabDouble)
253                     {
254                         FREE(pstFirstOutput);
255                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
256                         *_piOutputRows = 0;
257                         return nullptr;
258                     }
259
260                     tok->outputType = types::InternalType::ScilabInt32;
261                     tok->pos = p;
262                     tok->col = c;
263                     ++itPos;
264                     break;
265                 }
266                 case 'o': //octal
267                 case 'u': //unsigned
268                 case 'x': //hex
269                 case 'X': //HEX
270                 {
271                     if (itPos == inPos.end())
272                     {
273                         FREE(pstFirstOutput);
274                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
275                         *_piOutputRows = 0;
276                         return nullptr;
277                     }
278
279                     int p = (*itPos).first;
280                     int c = (*itPos).second;
281                     if (in[p]->getType() != types::InternalType::ScilabDouble)
282                     {
283                         FREE(pstFirstOutput);
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::ScilabUInt32;
290                     tok->pos = p;
291                     tok->col = c;
292                     ++itPos;
293                     break;
294                 }
295                 case 'f': //float
296                 case 'e': //exp
297                 case 'E': //EXP
298                 case 'g': //shorter between float or exp
299                 case 'G': //shorter between float or EXP
300                 {
301                     if (itPos == inPos.end())
302                     {
303                         FREE(pstFirstOutput);
304                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
305                         *_piOutputRows = 0;
306                         return nullptr;
307                     }
308
309                     int p = (*itPos).first;
310                     int c = (*itPos).second;
311                     if (in[p]->getType() != types::InternalType::ScilabDouble)
312                     {
313                         FREE(pstFirstOutput);
314                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
315                         *_piOutputRows = 0;
316                         return nullptr;
317                     }
318
319                     tok->outputType = types::InternalType::ScilabDouble;
320                     tok->pos = p;
321                     tok->col = c;
322                     ++itPos;
323                     break;
324                 }
325                 case 's':
326                 case 'c':
327                 {
328                     if (itPos == inPos.end())
329                     {
330                         FREE(pstFirstOutput);
331                         Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
332                         *_piOutputRows = 0;
333                         return nullptr;
334                     }
335
336                     int p = (*itPos).first;
337                     int c = (*itPos).second;
338                     if (in[p]->getType() != types::InternalType::ScilabString)
339                     {
340                         FREE(pstFirstOutput);
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::ScilabString;
347                     tok->pos = p;
348                     tok->col = c;
349                     ++itPos;
350                     break;
351                 }
352                 default:
353                     FREE(pstFirstOutput);
354                     Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
355                     *_piOutputRows = 0;
356                     return nullptr;
357                     break;
358             }
359         }
360
361         //continue
362         pstStart = pstEnd;
363     }
364
365     FREE(pstFirstOutput);
366     pstFirstOutput = nullptr;
367
368     int iLoop = 1;
369     if (token.size() > 1)
370     {
371         std::list<TokenDef*>::iterator it = std::next(token.begin());
372         iLoop = in[(*it)->pos]->getAs<types::GenericType>()->getRows();
373         for (; it != token.end(); ++it)
374         {
375             iLoop = std::min(iLoop, in[(*it)->pos]->getAs<types::GenericType>()->getRows());
376         }
377
378         if (*_piNewLine || (*_piOutputRows) > 1)
379         {
380             (*_piOutputRows) *= iLoop;
381         }
382     }
383
384
385     //if ((token.size() - 1) != inPos.size())
386     //{
387     //    Scierror(999, _("%s: Wrong number of input arguments: at most %d expected.\n"), funcname.data(), token.size() - 1);
388     //    *_piOutputRows = 0;
389     //    return nullptr;
390     //}
391
392     std::ostringstream oFirstOutput;
393     for (int j = 0; j < iLoop; j++)
394     {
395         //copy the 0th token
396         TokenDef* f = token.front();
397         oFirstOutput << f->pstToken;
398
399         //start at 1, the 0th is always without %
400         std::list<TokenDef*>::iterator it = std::next(token.begin());
401         for (; it != token.end(); ++it)
402         {
403             TokenDef* tok = *it;
404             switch (tok->outputType)
405             {
406                 case types::InternalType::ScilabDouble:
407                 {
408                     char pstTemp[bsiz];
409                     double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
410
411                     if (std::isfinite(dblVal))
412                     {
413                         if (tok->widthStar)
414                         {
415                             if (tok->precStar)
416                             {
417                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, tok->prec, dblVal);
418                             }
419                             else
420                             {
421                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, dblVal);
422                             }
423                         }
424                         else
425                         {
426                             if (tok->precStar)
427                             {
428                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->prec, dblVal);
429                             }
430                             else
431                             {
432                                 os_sprintf(pstTemp, bsiz, tok->pstToken, dblVal);
433                             }
434                         }
435                     }
436                     else
437                     {
438                         char* newToken = replacebys(tok);
439
440                         if (std::isnan(dblVal))
441                         {
442                             os_sprintf(pstTemp, bsiz, newToken, NanString);
443                         }
444                         else if (std::signbit(dblVal))
445                         {
446                             os_sprintf(pstTemp, bsiz, newToken, NegInfString);
447                         }
448                         else
449                         {
450                             os_sprintf(pstTemp, bsiz, newToken, InfString);
451                         }
452
453                         FREE(newToken);
454                     }
455
456                     oFirstOutput << pstTemp;
457                     break;
458                 }
459                 case types::InternalType::ScilabInt32:
460                 {
461                     char pstTemp[bsiz];
462                     double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
463                     int iVal = (int)dblVal;
464                     if (std::isfinite(dblVal))
465                     {
466                         if (tok->widthStar)
467                         {
468                             if (tok->precStar)
469                             {
470                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, tok->prec, iVal);
471                             }
472                             else
473                             {
474                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, iVal);
475                             }
476                         }
477                         else
478                         {
479                             if (tok->precStar)
480                             {
481                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->prec, iVal);
482                             }
483                             else
484                             {
485                                 os_sprintf(pstTemp, bsiz, tok->pstToken, iVal);
486                             }
487                         }
488                     }
489                     else
490                     {
491                         char* newToken = replacebys(tok);
492
493                         if (std::isnan(dblVal))
494                         {
495                             os_sprintf(pstTemp, bsiz, newToken, NanString);
496                         }
497                         else
498                         {
499                             if (std::signbit(dblVal))
500                             {
501                                 os_sprintf(pstTemp, bsiz, newToken, NegInfString);
502                             }
503                             else
504                             {
505                                 os_sprintf(pstTemp, bsiz, newToken, InfString);
506                             }
507                         }
508
509                         FREE(newToken);
510                     }
511
512                     oFirstOutput << pstTemp;
513                     break;
514                 }
515                 case types::InternalType::ScilabUInt32:
516                 {
517                     char pstTemp[bsiz];
518                     double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
519                     unsigned int iVal = (unsigned int)dblVal;
520
521                     if (std::isfinite(dblVal))
522                     {
523                         if (tok->widthStar)
524                         {
525                             if (tok->precStar)
526                             {
527                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, tok->prec, iVal);
528                             }
529                             else
530                             {
531                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, iVal);
532                             }
533                         }
534                         else
535                         {
536                             if (tok->precStar)
537                             {
538                                 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->prec, iVal);
539                             }
540                             else
541                             {
542                                 os_sprintf(pstTemp, bsiz, tok->pstToken, iVal);
543                             }
544                         }
545                     }
546                     else
547                     {
548                         char* newToken = replacebys(tok);
549
550                         if (std::isnan(dblVal))
551                         {
552                             os_sprintf(pstTemp, bsiz, newToken, NanString);
553                         }
554                         else
555                         {
556                             if (std::signbit(dblVal))
557                             {
558                                 os_sprintf(pstTemp, bsiz, newToken, NegInfString);
559                             }
560                             else
561                             {
562                                 os_sprintf(pstTemp, bsiz, newToken, InfString);
563                             }
564                         }
565
566                         FREE(newToken);
567                     }
568
569                     oFirstOutput << pstTemp;
570                     break;
571                 }
572                 case types::InternalType::ScilabString:
573                 {
574                     char* pstStr = nullptr;
575                     std::string NaN = NanString;
576                     std::string nInf = NegInfString;
577                     std::string pInf = InfString;
578
579                     types::InternalType* it = in[tok->pos];
580                     if (it->isDouble() && std::isnan(it->getAs<types::Double>()->get(0)))
581                     {
582                         pstStr = const_cast<char*>(NaN.c_str());
583                     }
584                     else if (it->isDouble() && std::isfinite(it->getAs<types::Double>()->get(0)) == false)
585                     {
586                         if (std::signbit(it->getAs<types::Double>()->get(0)))
587                         {
588                             pstStr = const_cast<char*>(nInf.c_str());
589                         }
590                         else
591                         {
592                             pstStr = const_cast<char*>(pInf.c_str());
593                         }
594                     }
595                     else
596                     {
597                         pstStr = it->getAs<types::String>()->get(j, tok->col);
598                     }
599
600                     int posC = (int)strcspn(tok->pstToken, "c");
601                     int posS = (int)strcspn(tok->pstToken, "s");
602
603                     if (posS == 0 || posC == 0)
604                     {
605                         *_piOutputRows = 0;
606                         return nullptr;
607                     }
608
609                     bool bC = posC < posS;
610                     int len = 1;
611                     if (tok->prec)
612                     {
613                         if (bC == false)
614                         {
615                             len = std::min(std::abs(tok->prec), (int)strlen(pstStr));
616                         }
617                     }
618                     else
619                     {
620                         if (bC == false)
621                         {
622                             len = (int)strlen(pstStr);
623                         }
624                     }
625
626                     int tokenLen = (int)strlen(tok->pstToken);
627                     len += tokenLen;
628                     len = std::max(len, std::abs(tok->width));
629                     //add len of string after token like "%20s>>>" add space for ">>>"
630                     len += (tokenLen - (bC ? posC : posS));
631                     char* pstTemp = (char*)MALLOC((len + 1) * sizeof(char));
632
633                     if (bC)
634                     {
635                         if (tok->widthStar)
636                         {
637                             os_sprintf(pstTemp, len + 1, tok->pstToken, tok->width, pstStr[0]);
638                         }
639                         else
640                         {
641                             os_sprintf(pstTemp, len + 1, tok->pstToken, pstStr[0]);
642                         }
643                     }
644                     else
645                     {
646                         if (tok->widthStar)
647                         {
648                             os_sprintf(pstTemp, len + 1, tok->pstToken, tok->width, pstStr);
649                         }
650                         else
651                         {
652                             os_sprintf(pstTemp, len + 1, tok->pstToken, pstStr);
653                         }
654                     }
655
656                     oFirstOutput << pstTemp;
657                     FREE(pstTemp);
658                     break;
659                 }
660                 default:
661                     // management of %%
662                     oFirstOutput << tok->pstToken;
663                     break;
664             }
665         }
666     }
667
668     pstFirstOutput = os_strdup(oFirstOutput.str().c_str());
669
670     for (auto & tok : token)
671     {
672         delete[] tok->pstToken;
673         delete tok;
674     }
675
676     pstOutput = (char**)MALLOC((*_piOutputRows) * sizeof(char*));
677
678     size_t iLen = strlen(pstFirstOutput);
679     int iStart = 0;
680     int j = 0;
681     for (int i = 0; i < iLen; i++)
682     {
683         if (pstFirstOutput[i] == '\n')
684         {
685             int iSize = i - iStart;
686             pstOutput[j] = (char*)MALLOC(sizeof(char) * (iSize + 1));
687             strncpy(pstOutput[j], pstFirstOutput + iStart, iSize);
688             pstOutput[j][iSize] = '\0';
689             iStart = i + 1;
690             j++;
691         }
692     }
693
694     if (j == (*_piOutputRows) - 1)
695     {
696         pstOutput[j] = os_strdup(pstFirstOutput + iStart);
697     }
698
699     FREE(pstFirstOutput);
700     return pstOutput;
701 }
702 /*--------------------------------------------------------------------------*/
703 /*--------------------------------------------------------------------------*/
704 // replace "\\n" "\\r" "\\t" "\\r\\n" by '\n' '\r' '\t' '\n'
705 // count number of lines
706 // indicate if one '\n' is at the end of string
707 static char* replaceAndCountLines(const char* _pstInput, int* _piLines, int* _piNewLine)
708 {
709     size_t iInputLen = strlen(_pstInput);
710     char* pstFirstOutput = (char*)MALLOC(sizeof(char) * (iInputLen + 1));
711
712     int iPos = 0;
713     *_piLines = 1;
714
715     for (int i = 0; i < iInputLen; i++)
716     {
717         if (_pstInput[i] == '\\')
718         {
719             if (iInputLen == i + 1)
720             {
721                 continue;
722             }
723
724             switch (_pstInput[i + 1])
725             {
726                 case 'n':
727                     pstFirstOutput[iPos++] = '\n';
728                     (*_piLines)++;
729                     i++;
730                     break;
731                 case 'r':
732                     if (iInputLen > i + 3 && _pstInput[i + 2] == '\\' && _pstInput[i + 3] == 'n')
733                     {
734                         pstFirstOutput[iPos++] = '\n';
735                         (*_piLines)++;
736                         i += 3;
737                     }
738                     else
739                     {
740                         pstFirstOutput[iPos++] = '\r';
741                         i++;
742                     }
743                     break;
744                 case 't':
745                     pstFirstOutput[iPos++] = '\t';
746                     i++;
747                     break;
748                 case '\\':
749                     pstFirstOutput[iPos++] = '\\';
750                     i++;
751                     break;
752                 default:
753                     break;
754             }
755         }
756         else if (_pstInput[i] == 0x0A) // ascii(10) == "\n"
757         {
758             pstFirstOutput[iPos++] = '\n';
759             (*_piLines)++;
760         }
761         else
762         {
763             pstFirstOutput[iPos++] = _pstInput[i];
764         }
765     }
766
767     // do not count '\n' if it's at the end of string
768     // it will be manage by piNewLine
769     if (iPos > 0 && pstFirstOutput[iPos - 1] == '\n')
770     {
771         (*_piLines)--;
772         (*_piNewLine) = 1;
773     }
774
775     pstFirstOutput[iPos] = 0;
776     return pstFirstOutput;
777 }
778 /*--------------------------------------------------------------------------*/
779 char* replacebys(TokenDef* token)
780 {
781     int iPos = token->typePos;
782     char* pstToken = os_strdup(token->pstToken);
783     pstToken[iPos] = 's';
784     return pstToken;
785 }
786 /*--------------------------------------------------------------------------*/