fix non handled exception if error occurs in scilab.start
[scilab.git] / scilab / modules / functions / sci_gateway / cpp / sci_exec.cpp
1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2006 - INRIA - Antoine ELIAS
4 *
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution.  The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10 *
11 */
12
13 #include "functions_gw.hxx"
14
15 #include "parser.hxx"
16 #include "funcmanager.hxx"
17 #include "context.hxx"
18 #include "printvisitor.hxx"
19 #include "visitor_common.hxx"
20 #include "scilabWrite.hxx"
21 #include "configvariable.hxx"
22 #include "types_tools.hxx"
23 #include "runner.hxx"
24 #include "threadmanagement.hxx"
25 #include "macro.hxx"
26 #include "macrofile.hxx"
27 #include "filemanager.hxx"
28
29 #include <memory>
30 #include <iostream>
31 #include <fstream>
32 #include <string>
33
34 extern "C"
35 {
36 #include "os_string.h"
37 #include "expandPathVariable.h"
38 #include "prompt.h"
39 #include "Scierror.h"
40 #include "localization.h"
41 #include "os_string.h"
42 #include "mopen.h"
43 #include "mclose.h"
44 #include "fullpath.h"
45 #include "PATH_MAX.h"
46 }
47
48 void printLine(const std::string& _stPrompt, const std::string& _stLine, bool _bLF);
49 std::string printExp(std::ifstream& _File, ast::Exp* _pExp, const std::string& _stPrompt, int* _piLine /* in/out */, int* _piCol /* in/out */, std::string& _stPreviousBuffer);
50 std::string getExpression(const std::string& _stFile, ast::Exp* _pExp);
51
52 void closeFile(std::ifstream* file, int fileId, const std::wstring& wstFile, ast::Exp* pExp)
53 {
54     if (file)
55     {
56         file->close();
57         delete file;
58
59         if (pExp)
60         {
61             delete pExp;
62         }
63
64         // Check if file has not already been closed (for ex mclose('all') in function)
65         if (FileManager::isOpened(wstFile))
66         {
67             mclose(fileId);
68         }
69     }
70 }
71 /*--------------------------------------------------------------------------*/
72 types::Function::ReturnValue sci_exec(types::typed_list &in, int _iRetCount, types::typed_list &out)
73 {
74     int promptMode      = 0;//default value at startup, overthise 3 or verbose ";"
75     bool bPromptMode    = false;
76     int iErr            = 0;
77     bool bErrCatch      = false;
78     ast::Exp* pExp      = NULL;
79     int iID             = 0;
80     types::Macro* pMacro = NULL;
81     Parser parser;
82
83     wchar_t* pwstFile = NULL;
84     char* pstFile = NULL;
85
86     std::string stFile;
87     std::ifstream* file = NULL;
88     std::wstring wstFile;
89
90     if (ConfigVariable::getStartProcessing() == false)
91     {
92         if (ConfigVariable::getVerbose())
93         {
94             promptMode = 3;
95         }
96         else
97         {
98             promptMode = 0;
99         }
100     }
101
102     if (in.size() < 1 || in.size() > 3)
103     {
104         Scierror(999, _("%s: Wrong number of input arguments: %d to %d expected.\n"), "exec" , 1, 3);
105         return types::Function::Error;
106     }
107
108     // get mode and errcatch
109     if (in.size() > 1)
110     {
111         //errcatch or mode
112         if (in[1]->isString() && in[1]->getAs<types::String>()->isScalar())
113         {
114             //errcatch
115             types::String* pS = in[1]->getAs<types::String>();
116             if (os_wcsicmp(pS->get(0), L"errcatch") == 0)
117             {
118                 bErrCatch = true;
119             }
120             else
121             {
122                 Scierror(999, _("%s: Wrong value for input argument #%d: 'errcatch' expected.\n"), "exec", 2);
123                 return types::Function::Error;
124             }
125
126             if (in.size() > 2)
127             {
128
129                 if (in[2]->isDouble() == false || in[2]->getAs<types::Double>()->isScalar() == false)
130                 {
131                     //mode
132                     Scierror(999, _("%s: Wrong type for input argument #%d: A integer expected.\n"), "exec", 3);
133                     return types::Function::Error;
134                 }
135
136                 promptMode = (int)in[2]->getAs<types::Double>()->getReal()[0];
137                 bPromptMode = true;
138             }
139         }
140         else if (in[1]->isDouble() && in[1]->getAs<types::Double>()->isScalar())
141         {
142             if (in.size() > 2)
143             {
144                 Scierror(999, _("%s: Wrong value for input argument #%d: 'errcatch' expected.\n"), "exec", 2);
145                 return types::Function::Error;
146             }
147             //mode
148             promptMode = (int)in[1]->getAs<types::Double>()->getReal()[0];
149             bPromptMode = true;
150         }
151         else
152         {
153             //not managed
154             Scierror(999, _("%s: Wrong type for input argument #%d: A integer or string expected.\n"), "exec", 2);
155             return types::Function::Error;
156         }
157     }
158
159     if (in[0]->isString() && in[0]->getAs<types::String>()->isScalar())
160     {
161         //1st argument is a path, parse file and execute it
162         int iParsePathLen = 0;
163         types::String* pS = in[0]->getAs<types::String>();
164
165         pwstFile = expandPathVariableW(pS->get(0));
166         pstFile = wide_string_to_UTF8(pwstFile);
167         stFile = pstFile;
168         file = new std::ifstream(pstFile);
169
170         FREE(pstFile);
171
172         wchar_t* pwstTemp = (wchar_t*)MALLOC(sizeof(wchar_t) * (PATH_MAX * 2));
173         get_full_pathW(pwstTemp, pwstFile, PATH_MAX * 2);
174         wstFile = pwstTemp;
175
176         FREE(pwstFile);
177         /*fake call to mopen to show file within file()*/
178         if (mopen(pwstTemp, L"r", 0, &iID) != MOPEN_NO_ERROR)
179         {
180             closeFile(file, iID, wstFile, pExp);
181             FREE(pwstTemp);
182             Scierror(999, _("%s: Cannot open file %s.\n"), "exec", stFile.data());
183             return types::Function::Error;
184         }
185
186         ThreadManagement::LockParser();
187         parser.parseFile(pwstTemp, L"exec");
188         FREE(pwstTemp);
189         if (parser.getExitStatus() !=  Parser::Succeded)
190         {
191             closeFile(file, iID, wstFile, pExp);
192             if (bErrCatch)
193             {
194                 out.push_back(new types::Double(999));
195                 //to lock last error information
196                 ConfigVariable::setLastErrorCall();
197                 ConfigVariable::setLastErrorMessage(parser.getErrorMessage());
198                 ConfigVariable::setLastErrorNumber(999);
199                 delete parser.getTree();
200                 ThreadManagement::UnlockParser();
201                 return types::Function::OK;
202             }
203
204             char* pst = wide_string_to_UTF8(parser.getErrorMessage());
205             Scierror(999, "%s", pst);
206             FREE(pst);
207
208             delete parser.getTree();
209             ThreadManagement::UnlockParser();
210             return types::Function::Error;
211         }
212
213         if (ConfigVariable::getSerialize())
214         {
215             ast::Exp* temp = parser.getTree();
216             if (ConfigVariable::getTimed())
217             {
218                 pExp = callTyper(temp, L"exec");
219             }
220             else
221             {
222                 pExp = callTyper(temp);
223             }
224
225             delete temp;
226         }
227         else
228         {
229             pExp = parser.getTree();
230         }
231
232         ThreadManagement::UnlockParser();
233         // update where to set the name of the executed file.
234         ConfigVariable::setFileNameToLastWhere(wstFile.data());
235
236         ConfigVariable::setExecutedFileID(iID);
237     }
238     else if (in[0]->isMacro() || in[0]->isMacroFile())
239     {
240         if (in[0]->isMacroFile())
241         {
242             //1st argument is a macro name, parse and execute it in the current environnement
243             if (in[0]->getAs<types::MacroFile>()->parse() == false)
244             {
245                 char* pstMacro = wide_string_to_UTF8(in[0]->getAs<types::MacroFile>()->getName().c_str());
246                 Scierror(999, _("%s: Unable to parse macro '%s'"), "exec", pstMacro);
247                 FREE(pstMacro);
248                 return types::Function::Error;
249             }
250             pMacro = in[0]->getAs<types::MacroFile>()->getMacro();
251         }
252         else //1st argument is a macro name, execute it in the current environnement
253         {
254             pMacro = in[0]->getAs<types::Macro>();
255         }
256
257         // unable for macro with varargin or varargout
258         auto inputs = pMacro->getInputs();
259         auto outputs = pMacro->getOutputs();
260         if ((inputs->size() != 0 && inputs->back()->getSymbol().getName() == L"varargin") ||
261                 outputs->size() != 0 && outputs->back()->getSymbol().getName() == L"varargout")
262         {
263             Scierror(999, _("%s: Wrong type for input argument #%d: A macro without varargin and varargout expected.\n"), "exec", 1);
264             return types::Function::Error;
265         }
266
267         pExp = pMacro->getBody();
268
269         // update where to set the name of the executed macro instead of "exec"
270         ConfigVariable::WhereEntry lastWhere = ConfigVariable::getWhere().back();
271         int iLine = lastWhere.m_line;
272         int iAbsLine = lastWhere.m_absolute_line;
273         ConfigVariable::where_end();
274         ConfigVariable::where_begin(iLine, iAbsLine, pMacro);
275     }
276     else
277     {
278         Scierror(999, _("%s: Wrong type for input argument #%d: string expected.\n"), "exec", 1);
279         return types::Function::Error;
280     }
281
282     if (pMacro)
283     {
284         //store the line number where is stored this macro in file.
285         ConfigVariable::macroFirstLine_begin(pMacro->getFirstLine());
286     }
287
288     //save current prompt mode
289     int oldVal = ConfigVariable::getPromptMode();
290     ConfigVariable::setPromptMode(promptMode);
291
292     // if not exp displaying, just execute the seqexp
293     if (file == NULL || promptMode == 0 || promptMode == 2)
294     {
295         ast::SeqExp* pSeqExp = pExp->getAs<ast::SeqExp>();
296         std::unique_ptr<ast::ConstVisitor> exec(ConfigVariable::getDefaultVisitor());
297
298         try
299         {
300             symbol::Context* pCtx = symbol::Context::getInstance();
301             int scope = pCtx->getScopeLevel();
302             int level = ConfigVariable::getRecursionLevel();
303             try
304             {
305                 pSeqExp->accept(*exec);
306             }
307             catch (const ast::RecursionException& /* re */)
308             {
309                 //close opened scope during try
310                 while (pCtx->getScopeLevel() > scope)
311                 {
312                     pCtx->scope_end();
313                 }
314
315                 //decrease recursion to init value
316                 while (ConfigVariable::getRecursionLevel() > level)
317                 {
318                     ConfigVariable::where_end();
319                     ConfigVariable::decreaseRecursion();
320                 }
321
322                 //print msg about recursion limit and trigger an error
323                 wchar_t sz[1024];
324                 os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
325                 throw ast::InternalError(sz);
326             }
327         }
328         catch (const ast::InternalAbort& ia)
329         {
330             closeFile(file, iID, wstFile, pExp);
331             throw ia;
332         }
333         catch (const ast::InternalError& ie)
334         {
335             if (bErrCatch == false)
336             {
337                 closeFile(file, iID, wstFile, pExp);
338                 ConfigVariable::setPromptMode(oldVal);
339                 ConfigVariable::setExecutedFileID(0);
340                 throw ie;
341             }
342
343             ConfigVariable::resetWhereError();
344             iErr = ConfigVariable::getLastErrorNumber();
345         }
346     }
347     else
348     {
349         ast::exps_t& LExp = pExp->getAs<ast::SeqExp>()->getExps();
350
351         char pstPrompt[64];
352         //get prompt
353         GetCurrentPrompt(pstPrompt);
354         std::string stPrompt(pstPrompt);
355
356         std::string str;
357         int iCurrentLine = -1; //no data in str
358         int iCurrentCol = 0; //no data in str
359
360         for (ast::exps_t::iterator j = LExp.begin(), itEnd = LExp.end() ; j != itEnd; ++j)
361         {
362             if (ConfigVariable::getPromptMode() == 7)
363             {
364                 stPrompt = SCIPROMPT_PAUSE;
365             }
366             // printf some exp
367             ast::exps_t::iterator k = j;
368             int iLastLine = (*j)->getLocation().last_line;
369             do
370             {
371                 str = printExp(*file, *k, stPrompt, &iCurrentLine, &iCurrentCol, str);
372                 iLastLine = (*k)->getLocation().last_line;
373                 k++;
374             }
375             while (k != LExp.end() && (*k)->getLocation().first_line == iLastLine);
376
377             // In case where the line ends by spaces, iCurrentCol is not reset
378             // by printExp because we don't know if that's the end of the expression
379             // before go out of the loop. So we have to reset column count
380             // and print a new line before manage the next line.
381             if (iCurrentCol != 0)
382             {
383                 iCurrentCol = 0;
384                 printLine("", "", true);
385             }
386
387             // create a seqexp with printed exp
388             ast::exps_t* someExps = new ast::exps_t();
389             someExps->assign(j, k);
390             k--;
391             ast::SeqExp seqExp(Location((*j)->getLocation().first_line,
392                                         (*k)->getLocation().last_line,
393                                         (*j)->getLocation().first_column,
394                                         (*k)->getLocation().last_column),
395                                *someExps);
396
397             j = k;
398
399             std::unique_ptr<ast::ConstVisitor> exec(ConfigVariable::getDefaultVisitor());
400
401             try
402             {
403                 symbol::Context* pCtx = symbol::Context::getInstance();
404                 int scope = pCtx->getScopeLevel();
405                 int level = ConfigVariable::getRecursionLevel();
406                 try
407                 {
408                     // execute printed exp
409                     seqExp.accept(*exec);
410                 }
411                 catch (const ast::RecursionException& /* re */)
412                 {
413                     //close opened scope during try
414                     while (pCtx->getScopeLevel() > scope)
415                     {
416                         pCtx->scope_end();
417                     }
418
419                     //decrease recursion to init value
420                     while (ConfigVariable::getRecursionLevel() > level)
421                     {
422                         ConfigVariable::where_end();
423                         ConfigVariable::decreaseRecursion();
424                     }
425
426                     //print msg about recursion limit and trigger an error
427                     wchar_t sz[1024];
428                     os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
429                     throw ast::InternalError(sz);
430                 }
431             }
432             catch (const ast::InternalAbort& ia)
433             {
434                 closeFile(file, iID, wstFile, pExp);
435
436                 //restore previous prompt mode
437                 ConfigVariable::setPromptMode(oldVal);
438
439                 // avoid double delete on exps when "seqExp" is destryed and "LExp" too
440                 ast::exps_t& protectExp = seqExp.getExps();
441                 for (int i = 0; i < protectExp.size(); ++i)
442                 {
443                     protectExp[i] = NULL;
444                 }
445
446                 throw ia;
447             }
448             catch (const ast::InternalError& ie)
449             {
450                 ConfigVariable::setExecutedFileID(0);
451                 ConfigVariable::fillWhereError(ie.GetErrorLocation().first_line);
452                 if (ConfigVariable::getLastErrorLine() == 0)
453                 {
454                     ConfigVariable::setLastErrorLine(ie.GetErrorLocation().first_line);
455                 }
456
457                 // avoid double delete on exps when "seqExp" is destryed and "LExp" too
458                 ast::exps_t& protectExp = seqExp.getExps();
459                 for (int i = 0; i < protectExp.size(); ++i)
460                 {
461                     protectExp[i] = NULL;
462                 }
463
464                 //store message
465                 iErr = ConfigVariable::getLastErrorNumber();
466                 if (bErrCatch == false)
467                 {
468                     closeFile(file, iID, wstFile, pExp);
469
470                     //restore previous prompt mode
471                     ConfigVariable::setPromptMode(oldVal);
472                     throw ie;
473                 }
474
475                 if (pMacro)
476                 {
477                     // reset last first line of macro called
478                     ConfigVariable::macroFirstLine_end();
479                 }
480
481                 ConfigVariable::resetWhereError();
482                 break;
483             }
484
485             ConfigVariable::setExecutedFileID(0);
486
487             // avoid double delete on exps when "seqExp" is destryed and "LExp" too
488             ast::exps_t& protectExp = seqExp.getExps();
489             for (int i = 0; i < protectExp.size(); ++i)
490             {
491                 protectExp[i] = NULL;
492             }
493         }
494     }
495
496     //restore previous prompt mode
497     ConfigVariable::setPromptMode(oldVal);
498     if (bErrCatch)
499     {
500         out.push_back(new types::Double(iErr));
501         //to lock last error information
502         ConfigVariable::setLastErrorCall();
503     }
504
505     closeFile(file, iID, wstFile, pExp);
506     return types::Function::OK;
507 }
508
509 std::string getExpression(const std::string& _stFile, ast::Exp* _pExp)
510 {
511     std::string out;
512     std::string stBuffer;
513     int iLine = 0;
514     Location loc = _pExp->getLocation();
515     std::ifstream file(_stFile.c_str());
516
517     //bypass previous lines
518     for (int i = 0 ; i < loc.first_line; i++)
519     {
520         std::getline(file, stBuffer);
521     }
522
523     if (loc.first_line == loc.last_line)
524     {
525         int iStart = loc.first_column - 1;
526         int iEnd = loc.last_column - 1;
527         int iLen = iEnd - iStart;
528         out += std::string(stBuffer.c_str() + iStart, iLen);
529     }
530     else
531     {
532         //
533
534         //first line, entire or not
535         out += std::string(stBuffer.c_str() + loc.first_column - 1);
536         out += "\n";
537
538         //print other full lines
539         for (int i = loc.first_line; i < (loc.last_line - 1) ; i++)
540         {
541             std::getline(file, stBuffer);
542             out += stBuffer;
543             out += "\n";
544         }
545
546
547         //last line, entire or not
548         getline(file, stBuffer);
549         out += std::string(stBuffer.c_str(), loc.last_column - 1);
550         out += "\n";
551     }
552     return out;
553 }
554
555 std::string printExp(std::ifstream& _File, ast::Exp* _pExp, const std::string& _stPrompt, int* _piLine /* in/out */, int* _piCol /* in/out */, std::string& _stPreviousBuffer)
556 {
557     //case 1, exp is on 1 line and take the entire line
558
559     //case 2, exp is multiline
560
561     //case 3, exp is part of a line.
562     //ex : 3 exp on the same line a = 1; b = 2; c = 3;
563
564     //case 4, exp is multiline but start and/or finish in the middle of a line
565     //ex :
566     //a = 10;for i = 1 : a
567     //  a
568     //end, b = 1;
569
570     Location loc = _pExp->getLocation();
571
572     //positionning file curser at loc.first_line
573     {
574         //strange case, current position is after the wanted position
575         if (*_piLine > loc.first_line)
576         {
577             //reset line counter and restart reading at the start of the file.
578             *_piLine = -1;
579             _File.seekg( 0, std::ios_base::beg );
580         }
581
582         //bypass previous lines
583         for (int i = *_piLine ; i < loc.first_line - 1; i++)
584         {
585
586             (*_piLine)++;
587             if ((*_piLine) != (loc.first_line - 1))
588             {
589                 //empty line but not sequential lines
590                 printLine("", "", true);
591             }
592             std::getline(_File, _stPreviousBuffer);
593         }
594     }
595
596     if (loc.first_line == loc.last_line)
597     {
598         //1 line
599         int iStart = loc.first_column - 1;
600         int iEnd = loc.last_column - 1;
601         int iLen = iEnd - iStart;
602         std::string strLastLine(_stPreviousBuffer.c_str() + iStart, iLen);
603         int iExpLen = iLen;
604         int iLineLen = (int)_stPreviousBuffer.size();
605         //printLine(_pstPrompt, strLastLine, true, false);
606
607         if (iStart == 0 && iExpLen == iLineLen)
608         {
609             //entire line
610             if (*_piCol)
611             {
612                 //blank char at the end of previous line
613                 printLine("", "", true);
614             }
615             printLine(_stPrompt, strLastLine, true);
616             *_piCol = 0;
617         }
618         else
619         {
620             if (iStart == 0)
621             {
622                 //begin of line
623                 if (*_piCol)
624                 {
625                     //blank char at the end of previous line
626                     printLine("", "", true);
627                 }
628                 printLine(_stPrompt, strLastLine, false);
629                 *_piCol = loc.last_column;
630             }
631             else
632             {
633                 if (*_piCol == 0)
634                 {
635                     printLine(_stPrompt, "", false);
636                     (*_piCol)++;
637                 }
638
639                 if (*_piCol < loc.first_column)
640                 {
641                     //pickup separator between expressionsfrom file and add to output
642                     int iSize = loc.first_column - *_piCol;
643                     std::string stTemp(_stPreviousBuffer.c_str() +  (*_piCol - 1), iSize);
644                     printLine("", stTemp, false);
645                     *_piCol = loc.first_column;
646                 }
647
648                 if (iEnd == iLineLen)
649                 {
650                     printLine("", strLastLine, true);
651                     *_piCol = 0;
652                 }
653                 else
654                 {
655                     printLine("", strLastLine, false);
656                     *_piCol = loc.last_column;
657                 }
658             }
659         }
660     }
661     else
662     {
663         //multiline
664
665         if (loc.first_column == 1)
666         {
667             if (*_piCol)
668             {
669                 //blank char at the end of previous line
670                 printLine("", "", true);
671             }
672             printLine(_stPrompt, _stPreviousBuffer.c_str() + (loc.first_column - 1), false);
673         }
674         else
675         {
676             if (*_piCol < loc.first_column)
677             {
678                 //pickup separator between expressionsfrom file and add to output
679                 printLine(_stPrompt, _stPreviousBuffer.c_str(), false);
680                 *_piCol = loc.first_column;
681             }
682         }
683
684         //print other full lines
685         for (int i = loc.first_line; i < (loc.last_line - 1) ; i++)
686         {
687             (*_piLine)++;
688             std::getline(_File, _stPreviousBuffer);
689             // dont print empty line of function body
690             if (_stPreviousBuffer.size() != 0)
691             {
692                 printLine(_stPrompt, _stPreviousBuffer.c_str(), false);
693             }
694         }
695
696         //last line
697         std::getline(_File, _stPreviousBuffer);
698         (*_piLine)++;
699
700         int iSize = loc.last_column - 1;
701         std::string stLastLine(_stPreviousBuffer.c_str(), iSize);
702         int iLineLen = (int)_stPreviousBuffer.size();
703         if (iLineLen == iSize)
704         {
705             printLine(_stPrompt, stLastLine, true);
706             *_piCol = 0;
707         }
708         else
709         {
710             printLine(_stPrompt, stLastLine, false);
711             *_piCol = loc.last_column;
712         }
713     }
714
715     return _stPreviousBuffer;
716 }
717
718 void printLine(const std::string& _stPrompt, const std::string& _stLine, bool _bLF)
719 {
720     std::string st;
721
722     if (_stPrompt.size() != 0)
723     {
724         st = "\n" + _stPrompt;
725     }
726
727     st += _stLine;
728     if (_bLF && ConfigVariable::isEmptyLineShow())
729     {
730         st += "\n";
731     }
732
733     scilabWrite(st.c_str());
734 }
735 /*--------------------------------------------------------------------------*/