861ff5dbd89ba5ab802a3264384d5de14f8c7019
[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  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  * === LICENSE_END ===
14 *
15 */
16
17 #include "functions_gw.hxx"
18
19 #include "parser.hxx"
20 #include "funcmanager.hxx"
21 #include "context.hxx"
22 #include "printvisitor.hxx"
23 #include "visitor_common.hxx"
24 #include "scilabWrite.hxx"
25 #include "configvariable.hxx"
26 #include "types_tools.hxx"
27 #include "runner.hxx"
28 #include "threadmanagement.hxx"
29 #include "macro.hxx"
30 #include "macrofile.hxx"
31 #include "filemanager.hxx"
32
33 #include <memory>
34 #include <iostream>
35 #include <fstream>
36 #include <string>
37
38 extern "C"
39 {
40 #include "os_string.h"
41 #include "expandPathVariable.h"
42 #include "prompt.h"
43 #include "Scierror.h"
44 #include "localization.h"
45 #include "os_string.h"
46 #include "mopen.h"
47 #include "mclose.h"
48 #include "fullpath.h"
49 #include "PATH_MAX.h"
50 }
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         // update where to set the name of the executed file.
187         ConfigVariable::setFileNameToLastWhere(wstFile.data());
188
189         ThreadManagement::LockParser();
190         parser.parseFile(pwstTemp, L"exec");
191         FREE(pwstTemp);
192         if (parser.getExitStatus() !=  Parser::Succeded)
193         {
194             closeFile(file, iID, wstFile, pExp);
195             if (bErrCatch)
196             {
197                 out.push_back(new types::Double(999));
198                 //to lock last error information
199                 ConfigVariable::setLastErrorCall();
200                 // when the parser can not open file the error is already set in lasterror.
201                 if (wcscmp(parser.getErrorMessage(), L""))
202                 {
203                     ConfigVariable::setLastErrorMessage(parser.getErrorMessage());
204                     ConfigVariable::setLastErrorNumber(999);
205                 }
206                 delete parser.getTree();
207                 ThreadManagement::UnlockParser();
208                 return types::Function::OK;
209             }
210
211             char* pst = wide_string_to_UTF8(parser.getErrorMessage());
212             Scierror(999, "%s", pst);
213             FREE(pst);
214
215             delete parser.getTree();
216             ThreadManagement::UnlockParser();
217             return types::Function::Error;
218         }
219
220         if (ConfigVariable::getSerialize())
221         {
222             ast::Exp* temp = parser.getTree();
223             if (ConfigVariable::getTimed())
224             {
225                 pExp = callTyper(temp, L"exec");
226             }
227             else
228             {
229                 pExp = callTyper(temp);
230             }
231
232             delete temp;
233         }
234         else
235         {
236             pExp = parser.getTree();
237         }
238
239         ThreadManagement::UnlockParser();
240
241         ConfigVariable::setExecutedFileID(iID);
242     }
243     else if (in[0]->isMacro() || in[0]->isMacroFile())
244     {
245         if (in[0]->isMacroFile())
246         {
247             //1st argument is a macro name, parse and execute it in the current environnement
248             if (in[0]->getAs<types::MacroFile>()->parse() == false)
249             {
250                 char* pstMacro = wide_string_to_UTF8(in[0]->getAs<types::MacroFile>()->getName().c_str());
251                 Scierror(999, _("%s: Unable to parse macro '%s'"), "exec", pstMacro);
252                 FREE(pstMacro);
253                 return types::Function::Error;
254             }
255             pMacro = in[0]->getAs<types::MacroFile>()->getMacro();
256         }
257         else //1st argument is a macro name, execute it in the current environnement
258         {
259             pMacro = in[0]->getAs<types::Macro>();
260         }
261
262         // unable for macro with varargin or varargout
263         auto inputs = pMacro->getInputs();
264         auto outputs = pMacro->getOutputs();
265         if ((inputs->size() != 0 && inputs->back()->getSymbol().getName() == L"varargin") ||
266                 outputs->size() != 0 && outputs->back()->getSymbol().getName() == L"varargout")
267         {
268             Scierror(999, _("%s: Wrong type for input argument #%d: A macro without varargin and varargout expected.\n"), "exec", 1);
269             return types::Function::Error;
270         }
271
272         pExp = pMacro->getBody();
273
274         // update where to set the name of the executed macro instead of "exec"
275         ConfigVariable::WhereEntry lastWhere = ConfigVariable::getWhere().back();
276         int iLine = lastWhere.m_line;
277         int iAbsLine = lastWhere.m_absolute_line;
278         ConfigVariable::where_end();
279         ConfigVariable::where_begin(iLine, iAbsLine, pMacro);
280     }
281     else
282     {
283         Scierror(999, _("%s: Wrong type for input argument #%d: string expected.\n"), "exec", 1);
284         return types::Function::Error;
285     }
286
287     if (pMacro)
288     {
289         //store the line number where is stored this macro in file.
290         ConfigVariable::macroFirstLine_begin(pMacro->getFirstLine());
291     }
292
293     //save current prompt mode
294     int oldVal = ConfigVariable::getPromptMode();
295     ConfigVariable::setPromptMode(promptMode);
296
297     ast::SeqExp* pSeqExp = pExp->getAs<ast::SeqExp>();
298     pSeqExp->setExecFrom(ast::SeqExp::EXEC);
299     std::unique_ptr<ast::ConstVisitor> exec(ConfigVariable::getDefaultVisitor());
300
301     try
302     {
303         symbol::Context* pCtx = symbol::Context::getInstance();
304         int scope = pCtx->getScopeLevel();
305         int level = ConfigVariable::getRecursionLevel();
306         try
307         {
308             pSeqExp->accept(*exec);
309         }
310         catch (const ast::RecursionException& /* re */)
311         {
312             //close opened scope during try
313             while (pCtx->getScopeLevel() > scope)
314             {
315                 pCtx->scope_end();
316             }
317
318             //decrease recursion to init value
319             while (ConfigVariable::getRecursionLevel() > level)
320             {
321                 ConfigVariable::where_end();
322                 ConfigVariable::decreaseRecursion();
323             }
324
325             //print msg about recursion limit and trigger an error
326             wchar_t sz[1024];
327             os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
328             throw ast::InternalError(sz);
329         }
330     }
331     catch (const ast::InternalAbort& ia)
332     {
333         closeFile(file, iID, wstFile, pExp);
334         throw ia;
335     }
336     catch (const ast::InternalError& ie)
337     {
338         if (bErrCatch == false)
339         {
340             closeFile(file, iID, wstFile, pExp);
341             ConfigVariable::setPromptMode(oldVal);
342             ConfigVariable::setExecutedFileID(0);
343             throw ie;
344         }
345
346         ConfigVariable::resetWhereError();
347         iErr = ConfigVariable::getLastErrorNumber();
348     }
349
350     //restore previous prompt mode
351     ConfigVariable::setPromptMode(oldVal);
352     if (bErrCatch)
353     {
354         out.push_back(new types::Double(iErr));
355         //to lock last error information
356         ConfigVariable::setLastErrorCall();
357     }
358
359     closeFile(file, iID, wstFile, pExp);
360     return types::Function::OK;
361 }