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