Replace Min, Max and Abs by std::min, std::max and std::abs
[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) 2010 - DIGITEO - Antoine ELIAS
4  *  Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET
5  *  Copyright (C) 2013 - Scilab Enterprises - Cedric Delamarre
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
17 #include "types.hxx"
18 #include "double.hxx"
19 #include "string.hxx"
20 #include "scilab_sprintf.hxx"
21
22 using namespace types;
23
24 extern "C"
25 {
26 #include "Scierror.h"
27 #include "sci_malloc.h"
28 #include "localization.h"
29 #include "charEncoding.h"
30 #include "os_wcsdup.h"
31 }
32
33 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine);
34
35 wchar_t** scilab_sprintf(const char* _pstName, const wchar_t* _pwstInput, typed_list &in, ArgumentPosition* _pArgs, int _iArgsCount, int* _piOutputRows, int* _piNewLine)
36 {
37     /* Force Windows display to have two-digit exponent. */
38 #ifdef _MSC_VER
39     _set_output_format(_TWO_DIGIT_EXPONENT);
40 #endif
41     wchar_t** pwstOutput        = NULL;
42     wchar_t* pwstFirstOutput    = NULL;
43     *_piNewLine = 0;
44     size_t pos = 0;
45
46     //\n \n\r \r \t to string
47     //find number of lines
48     // replace \\n \\t... by \n \t...
49     pwstFirstOutput = replaceAndCountLines(_pwstInput, _piOutputRows, _piNewLine);
50
51     //no arg, just return _pwstInput value
52     if (_iArgsCount)
53     {
54         //store all sub parts of the input string
55         //for the input string "bla1 %s bla2 %d bla3"
56         //store  :
57         //pwstToken[0] : "bla1 "
58         //pwstToken[1] : "%s bla2 "
59         //pwstToken[2] : "%d bla3"
60
61         size_t iStart   = 0;
62         size_t iEnd     = 0;
63         int iToken      = 0;
64         int iPosArg     = 0;
65
66         TokenDef* pToken = new TokenDef[_iArgsCount + 1];
67         wchar_t* pwstStart  = pwstFirstOutput;
68
69         bool bFinish         = false;
70         bool bPercentPercent = false;
71
72         while (!bFinish)
73         {
74             wchar_t* pwstEnd = wcsstr(pwstStart + (iToken == 0 ? 0 : 1), L"%");
75             iStart = pwstStart - pwstFirstOutput;
76             bPercentPercent = false;
77             if (pwstEnd != NULL)
78             {
79                 if (iToken && pwstStart[1] == L'%')
80                 {
81                     //manage "%%"
82                     pwstEnd = wcsstr(pwstEnd + 1, L"%");
83                     if (pwstEnd == NULL)
84                     {
85                         //end of string
86                         iEnd    = wcslen(pwstFirstOutput);
87                         bFinish = true;
88                     }
89                     else
90                     {
91                         iEnd = pwstEnd - pwstFirstOutput;
92                     }
93
94                     // skip the first %
95                     iStart++;
96                     bPercentPercent = true;
97                 }
98                 else
99                 {
100                     iEnd = pwstEnd - pwstFirstOutput;
101                 }
102             }
103             else
104             {
105                 //end of string
106                 iEnd    = wcslen(pwstFirstOutput);
107                 bFinish = true;
108             }
109
110             pToken[iToken].pwstToken = new wchar_t[iEnd - iStart + 1];
111             wcsncpy(pToken[iToken].pwstToken, pwstFirstOutput + iStart, iEnd - iStart);
112             pToken[iToken].pwstToken[iEnd - iStart] = L'\0';
113
114             //identify destination type
115             //format : %[flags][width][.precision][length]specifier
116             //pToken[iToken].pwstToken
117
118             //find %
119             wchar_t* pwstPercent = wcsstr(pToken[iToken].pwstToken, L"%");
120             if (pwstPercent != NULL && bPercentPercent == false)
121             {
122                 //looking for flags
123                 if (*(pwstPercent + 1) == L'-' ||
124                         *(pwstPercent + 1) == L'+' ||
125                         *(pwstPercent + 1) == L' ' ||
126                         *(pwstPercent + 1) == L'#' ||
127                         *(pwstPercent + 1) == L'0')
128                 {
129                     pwstPercent++;
130                 }
131
132                 //looking for width
133                 if (*(pwstPercent + 1) == L'*')
134                 {
135                     pwstPercent++;
136                 }
137                 else
138                 {
139                     //number
140                     while (iswdigit(*(pwstPercent + 1)))
141                     {
142                         pwstPercent++;
143                     }
144                 }
145
146                 //looking for precision
147                 if (*(pwstPercent + 1) == L'.')
148                 {
149                     pwstPercent++;
150                     while (iswdigit(*(pwstPercent + 1)))
151                     {
152                         pwstPercent++;
153                     }
154                 }
155
156                 //looking for length
157                 if (*(pwstPercent + 1) == L'h' ||
158                         *(pwstPercent + 1) == L'l' ||
159                         *(pwstPercent + 1) == L'L')
160                 {
161                     pToken[iToken].bLengthFlag = true;
162                     pwstPercent++;
163                 }
164                 else
165                 {
166                     pToken[iToken].bLengthFlag = false;
167                 }
168
169                 wchar_t wcType = *(pwstPercent + 1);
170                 switch (wcType)
171                 {
172                     case L'i' : //integer
173                     case L'd' : //integer
174                     case L'o' : //octal
175                     case L'u' : //unsigned
176                     case L'x' : //hex
177                     case L'X' : //HEX
178                         if (_pArgs[iPosArg].type != InternalType::ScilabDouble)
179                         {
180                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
181                             *_piOutputRows = 0;
182                             return NULL;
183                         }
184                         pToken[iToken].outputType = InternalType::ScilabInt32;
185                         iPosArg++;
186                         break;
187                     case L'f' : //float
188                     case L'e' : //exp
189                     case L'E' : //EXP
190                     case L'g' : //shorter between float or exp
191                     case L'G' : //shorter between float or EXP
192                         if (_pArgs[iPosArg].type != InternalType::ScilabDouble)
193                         {
194                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
195                             *_piOutputRows = 0;
196                             return NULL;
197                         }
198                         pToken[iToken].outputType = InternalType::ScilabDouble;
199                         iPosArg++;
200                         break;
201                     case L's' :
202                     case L'c' :
203                         if (_pArgs[iPosArg].type != InternalType::ScilabString)
204                         {
205                             Scierror(999, _("%s: Wrong number of input arguments: data doesn't fit with format.\n"), _pstName);
206                             *_piOutputRows = 0;
207                             return NULL;
208                         }
209                         pToken[iToken].outputType = InternalType::ScilabString;
210                         iPosArg++;
211                         break;
212                     default :
213                         //houston ...
214                         break;
215                 }
216             }
217
218             pwstStart = pwstEnd;
219             iToken++;
220         }
221
222         FREE(pwstFirstOutput);
223         pwstFirstOutput = NULL;
224
225         int iLoop = 1;
226         int iFirstArg = 1;
227         if (strcmp(_pstName, "mfprintf") == 0)
228         {
229             iFirstArg = 2;
230         }
231
232         if (in.size() > 1)
233         {
234             iLoop = in[iFirstArg]->getAs<GenericType>()->getRows();
235             for (int i = iFirstArg + 1 ; i < in.size() ; i++)
236             {
237                 iLoop = std::min(iLoop, in[i]->getAs<GenericType>()->getRows());
238             }
239         }
240
241         if (*_piNewLine || (*_piOutputRows) > 1)
242         {
243             (*_piOutputRows) *= iLoop;
244         }
245
246         pwstFirstOutput = (wchar_t*)MALLOC(sizeof(wchar_t*) * iLoop * bsiz);
247         memset(pwstFirstOutput, 0x00, sizeof(wchar_t*) * iLoop * bsiz);
248         for (int j = 0 ; j < iLoop ; j++)
249         {
250             //copy the 0th token
251             wcscat(pwstFirstOutput, pToken[0].pwstToken);
252             iPosArg = 0;
253             //start at 1, the 0th is always without %
254             for (int i = 1 ; i < _iArgsCount + 1 ; i++)
255             {
256                 wchar_t pwstTemp[bsiz];
257                 void* pvVal = NULL;
258                 if (pToken[i].outputType == InternalType::ScilabDouble)
259                 {
260                     double dblVal = in[_pArgs[iPosArg].iArg]->getAs<Double>()->get(j, _pArgs[iPosArg].iPos);
261                     swprintf(pwstTemp, bsiz, pToken[i].pwstToken, dblVal);
262                     iPosArg++;
263                 }
264                 else if (pToken[i].outputType == InternalType::ScilabInt32)
265                 {
266                     double dblVal = in[_pArgs[iPosArg].iArg]->getAs<Double>()->get(j, _pArgs[iPosArg].iPos);
267                     swprintf(pwstTemp, bsiz, pToken[i].pwstToken, (int)dblVal);
268                     iPosArg++;
269                 }
270                 else if (pToken[i].outputType == InternalType::ScilabString)
271                 {
272                     wchar_t* pwstStr = in[_pArgs[iPosArg].iArg]->getAs<types::String>()->get(j, _pArgs[iPosArg].iPos);
273                     int posC = (int)wcscspn(pToken[i].pwstToken, L"c");
274                     int posS = (int)wcscspn(pToken[i].pwstToken, L"s");
275                     if (!posS || !posC)
276                     {
277                         *_piOutputRows = 0;
278                         return NULL;
279                     }
280                     if (posC < posS)
281                     {
282                         if (pToken[i].bLengthFlag == false)
283                         {
284                             //replace %c by %lc to wide char compatibility
285                             int sizeTotal = (int)wcslen(pToken[i].pwstToken);
286                             wchar_t* pwstToken = (wchar_t*)MALLOC(sizeof(wchar_t) * (sizeTotal + 2));
287                             wcsncpy(pwstToken, pToken[i].pwstToken, posC);
288                             pwstToken[posC] = L'l';
289                             wcsncpy(&pwstToken[posC + 1], &pToken[i].pwstToken[posC], sizeTotal - posC);
290                             pwstToken[sizeTotal + 1]  = L'\0';
291                             swprintf(pwstTemp, bsiz, pwstToken, pwstStr[0]);
292                             FREE(pwstToken);
293                         }
294                         else
295                         {
296                             swprintf(pwstTemp, bsiz, pToken[i].pwstToken, pwstStr[0]);
297                         }
298                     }
299                     else //'s'
300                     {
301                         if (pToken[i].bLengthFlag == false)
302                         {
303                             //replace %s by %ls to wide char compatibility
304                             int sizeTotal = (int)wcslen(pToken[i].pwstToken);
305                             wchar_t* pwstToken = (wchar_t*)MALLOC(sizeof(wchar_t) * (sizeTotal + 2));
306                             wcsncpy(pwstToken, pToken[i].pwstToken, posS);
307                             pwstToken[posS] = L'l';
308                             wcsncpy(&pwstToken[posS + 1], &pToken[i].pwstToken[posS], sizeTotal - posS);
309                             pwstToken[sizeTotal + 1]  = L'\0';
310                             swprintf(pwstTemp, bsiz, pwstToken, pwstStr);
311                             FREE(pwstToken);
312                         }
313                         else
314                         {
315                             swprintf(pwstTemp, bsiz, pToken[i].pwstToken, pwstStr);
316                         }
317                     }
318                     iPosArg++;
319                 }
320                 else
321                 {
322                     // management of %%
323                     wcscpy(pwstTemp, pToken[i].pwstToken);
324                 }
325
326                 wcscat(pwstFirstOutput, pwstTemp);
327             }
328         }
329     }
330
331
332     pwstOutput = (wchar_t**)MALLOC((*_piOutputRows) * sizeof(wchar_t*));
333
334     size_t iLen = wcslen(pwstFirstOutput);
335
336     int iStart = 0;
337     int j = 0;
338     for (int i = 0 ; i < iLen ; i++)
339     {
340         if (pwstFirstOutput[i] == L'\n')
341         {
342             int iSize = i - iStart;
343             pwstOutput[j] = (wchar_t*)MALLOC(sizeof(wchar_t) * (iSize + 1));
344             wcsncpy(pwstOutput[j], pwstFirstOutput + iStart, iSize);
345             pwstOutput[j][iSize] = L'\0';
346             iStart = i + 1;
347             j++;
348         }
349     }
350
351     if (j == (*_piOutputRows) - 1)
352     {
353         pwstOutput[j] = os_wcsdup(pwstFirstOutput + iStart);
354     }
355
356     FREE(pwstFirstOutput);
357     return pwstOutput;
358 }
359 /*--------------------------------------------------------------------------*/
360 // replace "\\n" "\\r" "\\t" "\\r\\n" by '\n' '\r' '\t' '\n'
361 // count number of lines
362 // indicate if one '\n' is at the end of string
363 static wchar_t* replaceAndCountLines(const wchar_t* _pwstInput, int* _piLines, int* _piNewLine)
364 {
365     size_t iInputLen = wcslen(_pwstInput);
366     wchar_t* pwstFirstOutput = (wchar_t*)MALLOC(sizeof(wchar_t) * (iInputLen + 1));
367
368     int iPos = 0;
369     *_piLines = 1;
370
371     for (int i = 0 ; i < iInputLen ; i++)
372     {
373         if (_pwstInput[i] == L'\\')
374         {
375             if (iInputLen == i + 1)
376             {
377                 pwstFirstOutput[iPos++] = L'\\';
378                 continue;
379             }
380
381             switch (_pwstInput[i + 1])
382             {
383                 case L'n' :
384                     pwstFirstOutput[iPos++] = L'\n';
385                     (*_piLines)++;
386                     i++;
387                     break;
388                 case L'r' :
389                     if (iInputLen > i + 3 && _pwstInput[i + 2] == L'\\' && _pwstInput[i + 3] == L'n')
390                     {
391                         pwstFirstOutput[iPos++] = L'\n';
392                         (*_piLines)++;
393                         i += 3;
394                     }
395                     else
396                     {
397                         pwstFirstOutput[iPos++] = L'\r';
398                         i++;
399                     }
400                     break;
401                 case L't' :
402                     pwstFirstOutput[iPos++] = L'\t';
403                     i++;
404                     break;
405                 default:
406                     pwstFirstOutput[iPos++] = L'\\';
407                     i++;
408                     break;
409             }
410         }
411         else
412         {
413             pwstFirstOutput[iPos++] = _pwstInput[i];
414         }
415     }
416
417     // do not count '\n' if it's at the end of string
418     // it will be manage by piNewLine
419     if (pwstFirstOutput[iPos - 1] == '\n')
420     {
421         (*_piLines)--;
422         (*_piNewLine) = 1;
423     }
424
425     pwstFirstOutput[iPos] = 0;
426     return pwstFirstOutput;
427 }
428 /*--------------------------------------------------------------------------*/