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 * Copyright (C) 2012 - 2016 - Scilab Enterprises
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.
18 #include "scilab_sprintf.hxx"
31 #include "charEncoding.h"
32 #include "localization.h"
33 #include "os_string.h"
35 #include "sci_malloc.h"
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);
45 #define NanString L"Nan"
46 #define InfString L"Inf"
47 #define NegInfString L"-Inf"
49 wchar_t** scilab_sprintf(const std::string& funcname, const wchar_t* _pwstInput, types::typed_list& in, int* _piOutputRows, int* _piNewLine)
51 wchar_t** pwstOutput = nullptr;
52 int rhs = static_cast<int>(in.size());
53 wchar_t* pwstFirstOutput = nullptr;
58 if (funcname == "mfprintf")
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)
67 types::GenericType* gt = in[i]->getAs<types::GenericType>();
68 int col = gt->getCols();
69 for (int j = 0; j < col; ++j)
71 inPos.emplace_back(i, j);
75 std::list<std::pair<int, int>>::iterator itPos = inPos.begin();
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);
82 std::list<TokenDef*> token;
88 wchar_t* pwstStart = pwstFirstOutput;
89 bool percentpercent = false;
90 std::vector<int> argumentPos;
92 while (finish == false)
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)
100 if (token.size() && pwstStart[1] == L'%')
103 pwstEnd = wcsstr(pwstEnd + 1, L"%");
104 if (pwstEnd == nullptr)
107 end = wcslen(pwstFirstOutput);
112 end = pwstEnd - pwstFirstOutput;
117 percentpercent = true;
121 end = pwstEnd - pwstFirstOutput;
127 end = wcslen(pwstFirstOutput);
131 // parameter field placeholders; manage "%2$d" field
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)
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)
143 argumentPos.push_back(index - 1);
144 start = pwstDollar - pwstFirstOutput;
145 pwstFirstOutput[start] = L'%';
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);
157 wchar_t* pwstPercent = wcsstr(tok->pwstToken, L"%");
158 if (pwstPercent != nullptr && percentpercent == false)
162 if (*(pwstPercent + 1) == L'+' ||
163 *(pwstPercent + 1) == L' ' ||
164 *(pwstPercent + 1) == L'#' ||
165 *(pwstPercent + 1) == L'0')
171 if (*(pwstPercent + offset + 1) == L'*')
173 if (itPos == inPos.end())
175 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
180 int p = (*itPos).first;
181 //input data use to set width
182 if (in[p]->getId() != types::InternalType::IdScalarDouble)
184 Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p);
189 types::Double* dbl = in[p]->getAs<types::Double>();
190 tok->width = static_cast<int>(dbl->get()[0]);
191 tok->widthStar = true;
198 size_t previousOffset = offset;
199 wchar_t* pwstWidth = pwstPercent + offset + 1;
200 tok->width = os_wtoi(pwstWidth, &offset);
201 offset = previousOffset + offset;
204 pwstPercent += offset;
206 //looking for precision
207 if (*(pwstPercent + 1) == L'.')
211 tok->prec = os_wtoi(pwstPercent + 1, &offset);
215 pwstPercent += offset;
217 else if (*(pwstPercent + 1) == L'*')
219 if (itPos == inPos.end())
221 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
226 int p = (*itPos).first;
227 //input data use to set prec
228 if (in[p]->getId() != types::InternalType::IdScalarDouble)
230 Scierror(999, _("%s: Wrong type of input arguments #%d: A real scalar expected.\n"), funcname.data(), p + 1);
235 types::Double* dbl = in[p]->getAs<types::Double>();
236 tok->prec = static_cast<int>(dbl->get()[0]);
237 tok->precStar = true;
244 if (*(pwstPercent + 1) == L'h' ||
245 *(pwstPercent + 1) == L'l' ||
246 *(pwstPercent + 1) == L'L')
252 wchar_t wcType = *(pwstPercent + 1);
253 tok->typePos = static_cast<int>((pwstPercent + 1) - tok->pwstToken);
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())
259 FREE(pwstFirstOutput);
260 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
265 int p = positioned ? argumentPos.back() + 1 : (*itPos).first;
266 int c = (*itPos).second;
273 if (itPos == inPos.end())
275 FREE(pwstFirstOutput);
276 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
281 if (in[p]->getType() != types::InternalType::ScilabDouble)
283 FREE(pwstFirstOutput);
284 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
289 tok->outputType = types::InternalType::ScilabInt64;
296 case L'u': //unsigned
300 if (itPos == inPos.end())
302 FREE(pwstFirstOutput);
303 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
308 int p = (*itPos).first;
309 int c = (*itPos).second;
310 if (in[p]->getType() != types::InternalType::ScilabDouble)
312 FREE(pwstFirstOutput);
313 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
318 tok->outputType = types::InternalType::ScilabUInt64;
327 case L'g': //shorter between float or exp
328 case L'G': //shorter between float or EXP
330 if (itPos == inPos.end())
332 FREE(pwstFirstOutput);
333 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
338 if (in[p]->getType() != types::InternalType::ScilabDouble)
340 FREE(pwstFirstOutput);
341 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
346 tok->outputType = types::InternalType::ScilabDouble;
355 if (itPos == inPos.end())
357 FREE(pwstFirstOutput);
358 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
363 if (in[p]->getType() != types::InternalType::ScilabString)
365 FREE(pwstFirstOutput);
366 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
371 if (tok->length == false)
376 tok->outputType = types::InternalType::ScilabString;
383 FREE(pwstFirstOutput);
384 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
395 FREE(pwstFirstOutput);
396 pwstFirstOutput = nullptr;
399 if (token.size() > 1 && in.size() > first)
401 iLoop = in[first]->getAs<types::GenericType>()->getRows();
402 for (int i = first + 1; i < in.size(); ++i)
404 iLoop = std::min(iLoop, in[i]->getAs<types::GenericType>()->getRows());
408 // parameter field placeholders MUST all be specified
409 if (!argumentPos.empty())
411 std::vector<int> sortedArgumentPos = argumentPos;
412 std::sort(sortedArgumentPos.begin(), sortedArgumentPos.end());
414 for (int i = 1; i < sortedArgumentPos.size() && 0 <= previous && previous < 2; ++i)
416 previous = sortedArgumentPos[i] - sortedArgumentPos[i - 1];
418 if (previous < 0 || 2 < previous)
420 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
424 if (sortedArgumentPos.back() + 1 != token.size() - 1)
426 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
430 if (argumentPos.size() != token.size() - 1)
432 Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), funcname.data());
438 // update token's pos
439 for (auto it = token.begin(); it != token.end(); ++it)
446 tok->pos = argumentPos[tok->pos - 1] + 1;
451 // count number of output lines in function of \n and size of input args
452 (*_piOutputRows) *= iLoop;
453 (*_piOutputRows) += *_piNewLine == 1 ? 0 : 1;
455 pwstOutput = (wchar_t**)MALLOC((*_piOutputRows) * sizeof(wchar_t*));
457 std::wostringstream oFirstOutput;
458 for (int j = 0; j < iLoop; ++j)
460 wchar_t* tmpToken = NULL;
461 for (auto it = token.begin(); it != token.end(); /*no inc*/)
464 wchar_t* token = tmpToken ? tmpToken : tok->pwstToken;
466 wchar_t* lf = wcsstr(token, L"\n");
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';
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)
479 case types::InternalType::ScilabDouble:
481 wchar_t pwstTemp[bsiz];
482 double dblVal = in[tok->pos]->getAs<types::Double>()->get(j, tok->col);
484 if (std::isfinite(dblVal))
490 os_swprintf(pwstTemp, bsiz, token, tok->width, tok->prec, dblVal);
494 os_swprintf(pwstTemp, bsiz, token, tok->width, dblVal);
501 os_swprintf(pwstTemp, bsiz, token, tok->prec, dblVal);
505 os_swprintf(pwstTemp, bsiz, token, dblVal);
511 print_nan_or_inf(pwstTemp, dblVal, token, tok->typePos, tok->width);
514 oFirstOutput << pwstTemp;
517 case types::InternalType::ScilabInt64:
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;
523 if (std::isfinite(dblVal))
530 os_swprintf(pwstTemp, bsiz, token, tok->width, tok->prec, iVal);
534 os_swprintf(pwstTemp, bsiz, token, tok->width, iVal);
541 os_swprintf(pwstTemp, bsiz, token, tok->prec, iVal);
545 os_swprintf(pwstTemp, bsiz, token, iVal);
551 print_nan_or_inf(pwstTemp, dblVal, token, tok->typePos, tok->width);
554 oFirstOutput << pwstTemp;
557 case types::InternalType::ScilabUInt64:
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;
563 if (std::isfinite(dblVal))
570 os_swprintf(pwstTemp, bsiz, token, tok->width, tok->prec, iVal);
574 os_swprintf(pwstTemp, bsiz, token, tok->width, iVal);
581 os_swprintf(pwstTemp, bsiz, token, tok->prec, iVal);
585 os_swprintf(pwstTemp, bsiz, token, iVal);
591 print_nan_or_inf(pwstTemp, dblVal, token, tok->typePos, tok->width);
594 oFirstOutput << pwstTemp;
597 case types::InternalType::ScilabString:
599 wchar_t* pwstStr = nullptr;
600 std::wstring NaN = NanString;
601 std::wstring nInf = NegInfString;
602 std::wstring pInf = InfString;
604 types::InternalType* it = in[tok->pos];
605 if (it->isDouble() && std::isnan(it->getAs<types::Double>()->get(0)))
607 pwstStr = const_cast<wchar_t*>(NaN.c_str());
609 else if (it->isDouble() && std::isfinite(it->getAs<types::Double>()->get(0)) == false)
611 if (std::signbit(it->getAs<types::Double>()->get(0)))
613 pwstStr = const_cast<wchar_t*>(nInf.c_str());
617 pwstStr = const_cast<wchar_t*>(pInf.c_str());
622 pwstStr = it->getAs<types::String>()->get(j, tok->col);
625 int posC = (int)wcscspn(token, L"c");
626 int posS = (int)wcscspn(token, L"s");
628 if (posS == 0 || posC == 0)
634 bool bC = posC < posS;
640 len = std::min(std::abs(tok->prec), (int)wcslen(pwstStr));
647 len = (int)wcslen(pwstStr);
651 int tokenLen = (int)wcslen(token);
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));
662 os_swprintf(pwstTemp, len + 1, token, tok->width, pwstStr[0]);
666 os_swprintf(pwstTemp, len + 1, token, pwstStr[0]);
673 os_swprintf(pwstTemp, len + 1, token, tok->width, pwstStr);
677 os_swprintf(pwstTemp, len + 1, token, pwstStr);
681 oFirstOutput << pwstTemp;
688 oFirstOutput << token;
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"");
702 // if not at the end of token, continue with same token
703 if (static_cast<size_t>(lf - tok->pwstToken) < wcslen(tok->pwstToken))
715 if (oFirstOutput.str().length())
717 pwstOutput[outputIter++] = os_wcsdup((wchar_t*)oFirstOutput.str().c_str());
719 // fill in the remaining allocated space with default value
720 while (outputIter < *_piOutputRows)
722 pwstOutput[outputIter++] = os_wcsdup(L"");
725 for (auto& tok : token)
727 delete[] tok->pwstToken;
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)
740 size_t iInputLen = wcslen(_pwstInput);
741 wchar_t* pwstFirstOutput = (wchar_t*)MALLOC(sizeof(wchar_t) * (iInputLen + 1));
746 for (int i = 0; i < iInputLen; i++)
748 if (_pwstInput[i] == L'\\')
750 if (iInputLen == i + 1)
755 switch (_pwstInput[i + 1])
758 pwstFirstOutput[iPos++] = L'\n';
763 if (iInputLen > i + 3 && _pwstInput[i + 2] == L'\\' && _pwstInput[i + 3] == L'n')
765 pwstFirstOutput[iPos++] = L'\n';
771 pwstFirstOutput[iPos++] = L'\r';
776 pwstFirstOutput[iPos++] = L'\t';
780 pwstFirstOutput[iPos++] = L'\\';
787 else if (_pwstInput[i] == 0x0A) // ascii(10) == "\n"
789 pwstFirstOutput[iPos++] = L'\n';
794 pwstFirstOutput[iPos++] = _pwstInput[i];
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')
805 pwstFirstOutput[iPos] = 0;
806 return pwstFirstOutput;
808 /*--------------------------------------------------------------------------*/
809 static wchar_t* replace_with_ls(TokenDef* token)
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];
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';
824 /*--------------------------------------------------------------------------*/
825 static void updatel(TokenDef* token)
827 wchar_t* newToken = replace_with_ls(token);
828 delete[] token->pwstToken;
829 token->pwstToken = newToken;
831 /*--------------------------------------------------------------------------*/
832 static wchar_t* replace(const wchar_t* s, const wchar_t* r, int pos, const wchar_t* token)
834 std::wstring h(token);
836 h.replace(pos - 1, wcslen(s), r);
838 wchar_t* res = new wchar_t[h.size() + 1];
839 wcscpy(res, h.data());
842 /*--------------------------------------------------------------------------*/
843 static void replace_lu_llu(TokenDef* token)
848 wchar_t* newToken = replace(L"lu", L"llu", token->typePos, token->pwstToken);
851 delete[] token->pwstToken;
852 token->pwstToken = newToken;
857 /*--------------------------------------------------------------------------*/
858 static void replace_ld_lld(TokenDef* token)
863 wchar_t* newToken = replace(L"ld", L"lld", token->typePos, token->pwstToken);
866 delete[] token->pwstToken;
867 token->pwstToken = newToken;
872 /*--------------------------------------------------------------------------*/
873 static void print_nan_or_inf(wchar_t* pwstTemp, double dblVal, const wchar_t* token, int pos, int width)
875 int sizeTotal = (int)wcslen(token);
876 wchar_t* pwstToken = new wchar_t[sizeTotal + 2] {0};
880 os_swprintf(pwstToken, sizeTotal + 2, L"%%%dls%ls", width, token + pos + 1);
884 os_swprintf(pwstToken, sizeTotal + 2, L"%%ls%ls", token + pos + 1);
887 if (std::isnan(dblVal))
889 os_swprintf(pwstTemp, bsiz, pwstToken, NanString);
891 else if (std::signbit(dblVal))
893 os_swprintf(pwstTemp, bsiz, pwstToken, NegInfString);
897 os_swprintf(pwstTemp, bsiz, pwstToken, InfString);