7d16abfd5fbfb2458e4e75bcac3dc4fd81a752ff
[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 *
14 */
15
16 #include "functions_gw.hxx"
17
18 #include "parser.hxx"
19 #include "funcmanager.hxx"
20 #include "context.hxx"
21 #include "printvisitor.hxx"
22 #include "visitor_common.hxx"
23 #include "scilabWrite.hxx"
24 #include "configvariable.hxx"
25 #include "types_tools.hxx"
26 #include "runner.hxx"
27 #include "threadmanagement.hxx"
28 #include "macro.hxx"
29 #include "macrofile.hxx"
30 #include "filemanager.hxx"
31
32 #include <memory>
33 #include <iostream>
34 #include <fstream>
35 #include <string>
36
37 extern "C"
38 {
39 #include "os_string.h"
40 #include "expandPathVariable.h"
41 #include "prompt.h"
42 #include "Scierror.h"
43 #include "localization.h"
44 #include "os_string.h"
45 #include "mopen.h"
46 #include "mclose.h"
47 #include "fullpath.h"
48 #include "PATH_MAX.h"
49 }
50
51 void closeFile(std::ifstream* file, int fileId, const std::wstring& wstFile, ast::Exp* pExp)
52 {
53     if (file)
54     {
55         file->close();
56         delete file;
57
58         if (pExp)
59         {
60             delete pExp;
61         }
62
63         // Check if file has not already been closed (for ex mclose('all') in function)
64         if (FileManager::isOpened(wstFile))
65         {
66             mclose(fileId);
67         }
68     }
69 }
70 /*--------------------------------------------------------------------------*/
71 types::Function::ReturnValue sci_exec(types::typed_list &in, int _iRetCount, types::typed_list &out)
72 {
73     int promptMode      = 0;//default value at startup, overthise 3 or verbose ";"
74     int iErr            = 0;
75     bool bErrCatch      = false;
76     ast::Exp* pExp      = NULL;
77     int iID             = 0;
78     types::Macro* pMacro = NULL;
79     Parser parser;
80
81     wchar_t* pwstFile = NULL;
82     char* pstFile = NULL;
83
84     std::string stFile;
85     std::ifstream* file = NULL;
86     std::wstring wstFile;
87
88     if (ConfigVariable::getStartProcessing() == false)
89     {
90         if (ConfigVariable::getVerbose())
91         {
92             promptMode = 3;
93         }
94         else
95         {
96             promptMode = 0;
97         }
98     }
99
100     if (in.size() < 1 || in.size() > 3)
101     {
102         Scierror(999, _("%s: Wrong number of input arguments: %d to %d expected.\n"), "exec" , 1, 3);
103         return types::Function::Error;
104     }
105
106     // get mode and errcatch
107     if (in.size() > 1)
108     {
109         //errcatch or mode
110         if (in[1]->isString() && in[1]->getAs<types::String>()->isScalar())
111         {
112             //errcatch
113             types::String* pS = in[1]->getAs<types::String>();
114             if (os_wcsicmp(pS->get(0), L"errcatch") == 0)
115             {
116                 bErrCatch = true;
117             }
118             else
119             {
120                 Scierror(999, _("%s: Wrong value for input argument #%d: 'errcatch' expected.\n"), "exec", 2);
121                 return types::Function::Error;
122             }
123
124             if (in.size() > 2)
125             {
126
127                 if (in[2]->isDouble() == false || in[2]->getAs<types::Double>()->isScalar() == false)
128                 {
129                     //mode
130                     Scierror(999, _("%s: Wrong type for input argument #%d: A integer expected.\n"), "exec", 3);
131                     return types::Function::Error;
132                 }
133
134                 promptMode = (int)in[2]->getAs<types::Double>()->getReal()[0];
135             }
136         }
137         else if (in[1]->isDouble() && in[1]->getAs<types::Double>()->isScalar())
138         {
139             if (in.size() > 2)
140             {
141                 Scierror(999, _("%s: Wrong value for input argument #%d: 'errcatch' expected.\n"), "exec", 2);
142                 return types::Function::Error;
143             }
144             //mode
145             promptMode = (int)in[1]->getAs<types::Double>()->getReal()[0];
146         }
147         else
148         {
149             //not managed
150             Scierror(999, _("%s: Wrong type for input argument #%d: A integer or string expected.\n"), "exec", 2);
151             return types::Function::Error;
152         }
153     }
154
155     if (in[0]->isString() && in[0]->getAs<types::String>()->isScalar())
156     {
157         //1st argument is a path, parse file and execute it
158         int iParsePathLen = 0;
159         types::String* pS = in[0]->getAs<types::String>();
160
161         pwstFile = expandPathVariableW(pS->get(0));
162         pstFile = wide_string_to_UTF8(pwstFile);
163         stFile = pstFile;
164         file = new std::ifstream(pstFile);
165
166         FREE(pstFile);
167
168         wchar_t* pwstTemp = get_full_pathW(pwstFile);
169         wstFile = pwstTemp;
170
171         FREE(pwstFile);
172         /*fake call to mopen to show file within file()*/
173         if (mopen(pwstTemp, L"r", 0, &iID) != MOPEN_NO_ERROR)
174         {
175             closeFile(file, iID, wstFile, pExp);
176             FREE(pwstTemp);
177             Scierror(999, _("%s: Cannot open file %s.\n"), "exec", stFile.data());
178             return types::Function::Error;
179         }
180
181         // update where to set the name of the executed file.
182         ConfigVariable::setFileNameToLastWhere(&wstFile);
183
184         ThreadManagement::LockParser();
185         parser.parseFile(pwstTemp, L"exec");
186         FREE(pwstTemp);
187         if (parser.getExitStatus() !=  Parser::Succeded)
188         {
189             closeFile(file, iID, wstFile, pExp);
190             if (bErrCatch)
191             {
192                 out.push_back(new types::Double(999));
193                 //to lock last error information
194                 ConfigVariable::setLastErrorCall();
195                 // when the parser can not open file the error is already set in lasterror.
196                 if (wcscmp(parser.getErrorMessage(), L""))
197                 {
198                     ConfigVariable::setLastErrorMessage(parser.getErrorMessage());
199                     ConfigVariable::setLastErrorNumber(999);
200                 }
201                 delete parser.getTree();
202                 ThreadManagement::UnlockParser();
203                 return types::Function::OK;
204             }
205
206             char* pst = wide_string_to_UTF8(parser.getErrorMessage());
207             Scierror(999, "%s", pst);
208             FREE(pst);
209
210             delete parser.getTree();
211             ThreadManagement::UnlockParser();
212             return types::Function::Error;
213         }
214
215         if (ConfigVariable::getSerialize())
216         {
217             ast::Exp* temp = parser.getTree();
218             if (ConfigVariable::getTimed())
219             {
220                 pExp = callTyper(temp, L"exec");
221             }
222             else
223             {
224                 pExp = callTyper(temp);
225             }
226
227             delete temp;
228         }
229         else
230         {
231             pExp = parser.getTree();
232         }
233
234         ThreadManagement::UnlockParser();
235
236         ConfigVariable::setExecutedFile(wstFile);
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     ast::SeqExp* pSeqExp = pExp->getAs<ast::SeqExp>();
293     pSeqExp->setExecFrom(ast::SeqExp::EXEC);
294     pSeqExp->setReturnable();
295     std::unique_ptr<ast::ConstVisitor> exec(ConfigVariable::getDefaultVisitor());
296
297     try
298     {
299         symbol::Context* pCtx = symbol::Context::getInstance();
300         int scope = pCtx->getScopeLevel();
301         int level = ConfigVariable::getRecursionLevel();
302         try
303         {
304             pSeqExp->accept(*exec);
305         }
306         catch (const ast::RecursionException& /* re */)
307         {
308             //close opened scope during try
309             while (pCtx->getScopeLevel() > scope)
310             {
311                 pCtx->scope_end();
312             }
313
314             //decrease recursion to init value
315             while (ConfigVariable::getRecursionLevel() > level)
316             {
317                 ConfigVariable::where_end();
318                 ConfigVariable::decreaseRecursion();
319             }
320
321             //print msg about recursion limit and trigger an error
322             wchar_t sz[1024];
323             os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
324             throw ast::InternalError(sz);
325         }
326     }
327     catch (const ast::InternalAbort& ia)
328     {
329         closeFile(file, iID, wstFile, pExp);
330         ConfigVariable::setPromptMode(oldVal);
331         throw ia;
332     }
333     catch (const ast::InternalError& ie)
334     {
335         if (pMacro && ConfigVariable::getLastErrorFunction() == L"")
336         {
337             ConfigVariable::setLastErrorFunction(pMacro->getName());
338         }
339
340         if (bErrCatch == false)
341         {
342             closeFile(file, iID, wstFile, pExp);
343             ConfigVariable::setPromptMode(oldVal);
344             ConfigVariable::setExecutedFile(L"");
345             throw ie;
346         }
347
348         ConfigVariable::resetWhereError();
349         iErr = ConfigVariable::getLastErrorNumber();
350     }
351
352     //restore previous prompt mode
353     ConfigVariable::setPromptMode(oldVal);
354     if (bErrCatch)
355     {
356         out.push_back(new types::Double(iErr));
357         //to lock last error information
358         ConfigVariable::setLastErrorCall();
359     }
360
361     closeFile(file, iID, wstFile, pExp);
362     return types::Function::OK;
363 }