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
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
21 #include "scilab_sprintf.hxx"
26 #include "sci_malloc.h"
27 #include "localization.h"
28 #include "charEncoding.h"
29 #include "os_string.h"
31 #include "os_string.h"
34 static char* replaceAndCountLines(const char* _pstInput, int* _piLines, int* _piNewLine);
35 static char* replacebys(TokenDef* token);
37 #define NanString "Nan"
38 #define InfString "Inf"
39 #define NegInfString "-Inf"
41 char** scilab_sprintf(const std::string& funcname, const char* _pwstInput, types::typed_list &in, int* _piOutputRows, int* _piNewLine)
43 /* Force Windows display to have two-digit exponent. */
45 _set_output_format(_TWO_DIGIT_EXPONENT);
47 char** pstOutput = nullptr;
49 char* pstFirstOutput = nullptr;
54 if (funcname == "mfprintf")
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)
63 types::GenericType* gt = in[i]->getAs<types::GenericType>();
64 int col = gt->getCols();
65 for (int j = 0; j < col; ++j)
67 inPos.emplace_back(i, j);
71 std::list<std::pair<int, int> >::iterator itPos = inPos.begin();
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);
78 std::list<TokenDef*> token;
84 char* pstStart = pstFirstOutput;
85 bool percentpercent = false;
87 while (finish == false)
89 char* pstEnd = strstr(pstStart + (token.size() == 0 ? 0 : 1), "%");
90 start = pstStart - pstFirstOutput;
91 percentpercent = false;
92 if (pstEnd != nullptr)
94 if (token.size() && pstStart[1] == '%')
97 pstEnd = strstr(pstEnd + 1, "%");
98 if (pstEnd == nullptr)
101 end = strlen(pstFirstOutput);
106 end = pstEnd - pstFirstOutput;
111 percentpercent = true;
115 end = pstEnd - pstFirstOutput;
121 end = strlen(pstFirstOutput);
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);
131 char* pstPercent = strstr(tok->pstToken, "%");
132 if (pstPercent != nullptr && percentpercent == false)
135 if (*(pstPercent + 1) == '-' ||
136 *(pstPercent + 1) == '+' ||
137 *(pstPercent + 1) == ' ' ||
138 *(pstPercent + 1) == '#' ||
139 *(pstPercent + 1) == '0')
145 if (*(pstPercent + 1) == '*')
147 if (itPos == inPos.end())
149 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
154 int p = (*itPos).first;
155 //input data use to set width
156 if (in[p]->getId() != types::InternalType::IdScalarDouble)
158 Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p);
164 types::Double* dbl = in[p]->getAs<types::Double>();
165 tok->width = static_cast<int>(dbl->get()[0]);
166 tok->widthStar = true;
173 if (isdigit(*(pstPercent + 1)))
175 tok->width = atoi(pstPercent + 1);
176 while (isdigit(*(pstPercent + 1)))
183 //looking for precision
184 if (*(pstPercent + 1) == '.')
188 if (isdigit(*(pstPercent + 1)))
190 tok->prec = atoi(pstPercent + 1);
191 while (isdigit(*(pstPercent + 1)))
196 else if (*(pstPercent + 1) == '*')
198 if (itPos == inPos.end())
200 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
205 int p = (*itPos).first;
206 //input data use to set prec
207 if (in[p]->getId() != types::InternalType::IdScalarDouble)
209 Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p + 1);
214 types::Double* dbl = in[p]->getAs<types::Double>();
215 tok->prec = static_cast<int>(dbl->get()[0]);
216 tok->precStar = true;
223 if (*(pstPercent + 1) == 'h' ||
224 *(pstPercent + 1) == 'l' ||
225 *(pstPercent + 1) == 'L')
231 char type = *(pstPercent + 1);
232 tok->typePos = (pstPercent + 1) - tok->pstToken;
239 if (itPos == inPos.end())
241 FREE(pstFirstOutput);
242 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
247 int p = (*itPos).first;
248 int c = (*itPos).second;
249 if (in[p]->getType() != types::InternalType::ScilabDouble)
251 FREE(pstFirstOutput);
252 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
257 tok->outputType = types::InternalType::ScilabInt32;
268 if (itPos == inPos.end())
270 FREE(pstFirstOutput);
271 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
276 int p = (*itPos).first;
277 int c = (*itPos).second;
278 if (in[p]->getType() != types::InternalType::ScilabDouble)
280 FREE(pstFirstOutput);
281 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
286 tok->outputType = types::InternalType::ScilabUInt32;
295 case 'g': //shorter between float or exp
296 case 'G': //shorter between float or EXP
298 if (itPos == inPos.end())
300 FREE(pstFirstOutput);
301 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
306 int p = (*itPos).first;
307 int c = (*itPos).second;
308 if (in[p]->getType() != types::InternalType::ScilabDouble)
310 FREE(pstFirstOutput);
311 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
316 tok->outputType = types::InternalType::ScilabDouble;
325 if (itPos == inPos.end())
327 FREE(pstFirstOutput);
328 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
333 int p = (*itPos).first;
334 int c = (*itPos).second;
335 if (in[p]->getType() != types::InternalType::ScilabString)
337 FREE(pstFirstOutput);
338 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
343 tok->outputType = types::InternalType::ScilabString;
350 FREE(pstFirstOutput);
351 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
362 FREE(pstFirstOutput);
363 pstFirstOutput = nullptr;
366 if (token.size() > 1)
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)
372 iLoop = std::min(iLoop, in[(*it)->pos]->getAs<types::GenericType>()->getRows());
375 if (*_piNewLine || (*_piOutputRows) > 1)
377 (*_piOutputRows) *= iLoop;
382 //if ((token.size() - 1) != inPos.size())
384 // Scierror(999, _("%s: Wrong number of input arguments: at most %d expected.\n"), funcname.data(), token.size() - 1);
385 // *_piOutputRows = 0;
389 std::ostringstream oFirstOutput;
390 for (int j = 0; j < iLoop; j++)
393 TokenDef* f = token.front();
394 oFirstOutput << f->pstToken;
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)
401 switch (tok->outputType)
403 case types::InternalType::ScilabDouble:
406 double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
408 if (std::isfinite(dblVal))
414 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, tok->prec, dblVal);
418 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, dblVal);
425 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->prec, dblVal);
429 os_sprintf(pstTemp, bsiz, tok->pstToken, dblVal);
435 char* newToken = replacebys(tok);
437 if (std::isnan(dblVal))
439 os_sprintf(pstTemp, bsiz, newToken, NanString);
441 else if (std::signbit(dblVal))
443 os_sprintf(pstTemp, bsiz, newToken, NegInfString);
447 os_sprintf(pstTemp, bsiz, newToken, InfString);
453 oFirstOutput << pstTemp;
456 case types::InternalType::ScilabInt32:
459 double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
460 int iVal = (int)dblVal;
461 if (std::isfinite(dblVal))
467 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, tok->prec, iVal);
471 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, iVal);
478 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->prec, iVal);
482 os_sprintf(pstTemp, bsiz, tok->pstToken, iVal);
488 char* newToken = replacebys(tok);
490 if (std::isnan(dblVal))
492 os_sprintf(pstTemp, bsiz, newToken, NanString);
496 if (std::signbit(dblVal))
498 os_sprintf(pstTemp, bsiz, newToken, NegInfString);
502 os_sprintf(pstTemp, bsiz, newToken, InfString);
509 oFirstOutput << pstTemp;
512 case types::InternalType::ScilabUInt32:
515 double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
516 unsigned int iVal = (unsigned int)dblVal;
518 if (std::isfinite(dblVal))
524 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, tok->prec, iVal);
528 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->width, iVal);
535 os_sprintf(pstTemp, bsiz, tok->pstToken, tok->prec, iVal);
539 os_sprintf(pstTemp, bsiz, tok->pstToken, iVal);
545 char* newToken = replacebys(tok);
547 if (std::isnan(dblVal))
549 os_sprintf(pstTemp, bsiz, newToken, NanString);
553 if (std::signbit(dblVal))
555 os_sprintf(pstTemp, bsiz, newToken, NegInfString);
559 os_sprintf(pstTemp, bsiz, newToken, InfString);
566 oFirstOutput << pstTemp;
569 case types::InternalType::ScilabString:
571 char* pstStr = nullptr;
572 std::string NaN = NanString;
573 std::string nInf = NegInfString;
574 std::string pInf = InfString;
576 types::InternalType* it = in[tok->pos];
577 if (it->isDouble() && std::isnan(it->getAs<types::Double>()->get(0)))
579 pstStr = const_cast<char*>(NaN.c_str());
581 else if (it->isDouble() && std::isfinite(it->getAs<types::Double>()->get(0)) == false)
583 if (std::signbit(it->getAs<types::Double>()->get(0)))
585 pstStr = const_cast<char*>(nInf.c_str());
589 pstStr = const_cast<char*>(pInf.c_str());
594 pstStr = it->getAs<types::String>()->get(j, tok->col);
597 int posC = (int)strcspn(tok->pstToken, "c");
598 int posS = (int)strcspn(tok->pstToken, "s");
600 if (posS == 0 || posC == 0)
606 bool bC = posC < posS;
612 len = std::min(std::abs(tok->prec), (int)strlen(pstStr));
619 len = (int)strlen(pstStr);
623 int tokenLen = (int)strlen(tok->pstToken);
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));
634 os_sprintf(pstTemp, len + 1, tok->pstToken, tok->width, pstStr[0]);
638 os_sprintf(pstTemp, len + 1, tok->pstToken, pstStr[0]);
645 os_sprintf(pstTemp, len + 1, tok->pstToken, tok->width, pstStr);
649 os_sprintf(pstTemp, len + 1, tok->pstToken, pstStr);
653 oFirstOutput << pstTemp;
659 oFirstOutput << tok->pstToken;
665 pstFirstOutput = os_strdup(oFirstOutput.str().c_str());
667 for (auto & tok : token)
669 delete[] tok->pstToken;
673 pstOutput = (char**)MALLOC((*_piOutputRows) * sizeof(char*));
675 size_t iLen = strlen(pstFirstOutput);
678 for (int i = 0; i < iLen; i++)
680 if (pstFirstOutput[i] == '\n')
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';
691 if (j == (*_piOutputRows) - 1)
693 pstOutput[j] = os_strdup(pstFirstOutput + iStart);
696 FREE(pstFirstOutput);
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)
706 size_t iInputLen = strlen(_pstInput);
707 char* pstFirstOutput = (char*)MALLOC(sizeof(char) * (iInputLen + 1));
712 for (int i = 0; i < iInputLen; i++)
714 if (_pstInput[i] == '\\')
716 if (iInputLen == i + 1)
721 switch (_pstInput[i + 1])
724 pstFirstOutput[iPos++] = '\n';
729 if (iInputLen > i + 3 && _pstInput[i + 2] == '\\' && _pstInput[i + 3] == 'n')
731 pstFirstOutput[iPos++] = '\n';
737 pstFirstOutput[iPos++] = '\r';
742 pstFirstOutput[iPos++] = '\t';
746 pstFirstOutput[iPos++] = '\\';
753 else if (_pstInput[i] == 0x0A) // ascii(10) == "\n"
755 pstFirstOutput[iPos++] = '\n';
760 pstFirstOutput[iPos++] = _pstInput[i];
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')
772 pstFirstOutput[iPos] = 0;
773 return pstFirstOutput;
775 /*--------------------------------------------------------------------------*/
776 char* replacebys(TokenDef* token)
778 int iPos = token->typePos;
779 char* pstToken = os_strdup(token->pstToken);
780 pstToken[iPos] = 's';
783 /*--------------------------------------------------------------------------*/