6be2a2824c8a218874535b545a3a1b8276d26ec9
[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 closeFile(std::ifstream* file, int fileId, const std::wstring& wstFile, ast::Exp* pExp)
49 {
50     if (file)
51     {
52         file->close();
53         delete file;
54
55         if (pExp)
56         {
57             delete pExp;
58         }
59
60         // Check if file has not already been closed (for ex mclose('all') in function)
61         if (FileManager::isOpened(wstFile))
62         {
63             mclose(fileId);
64         }
65     }
66 }
67 /*--------------------------------------------------------------------------*/
68 types::Function::ReturnValue sci_exec(types::typed_list &in, int _iRetCount, types::typed_list &out)
69 {
70     int promptMode      = 0;//default value at startup, overthise 3 or verbose ";"
71     bool bPromptMode    = false;
72     int iErr            = 0;
73     bool bErrCatch      = false;
74     ast::Exp* pExp      = NULL;
75     int iID             = 0;
76     types::Macro* pMacro = NULL;
77     Parser parser;
78
79     wchar_t* pwstFile = NULL;
80     char* pstFile = NULL;
81
82     std::string stFile;
83     std::ifstream* file = NULL;
84     std::wstring wstFile;
85
86     if (ConfigVariable::getStartProcessing() == false)
87     {
88         if (ConfigVariable::getVerbose())
89         {
90             promptMode = 3;
91         }
92         else
93         {
94             promptMode = 0;
95         }
96     }
97
98     if (in.size() < 1 || in.size() > 3)
99     {
100         Scierror(999, _("%s: Wrong number of input arguments: %d to %d expected.\n"), "exec" , 1, 3);
101         return types::Function::Error;
102     }
103
104     // get mode and errcatch
105     if (in.size() > 1)
106     {
107         //errcatch or mode
108         if (in[1]->isString() && in[1]->getAs<types::String>()->isScalar())
109         {
110             //errcatch
111             types::String* pS = in[1]->getAs<types::String>();
112             if (os_wcsicmp(pS->get(0), L"errcatch") == 0)
113             {
114                 bErrCatch = true;
115             }
116             else
117             {
118                 Scierror(999, _("%s: Wrong value for input argument #%d: 'errcatch' expected.\n"), "exec", 2);
119                 return types::Function::Error;
120             }
121
122             if (in.size() > 2)
123             {
124
125                 if (in[2]->isDouble() == false || in[2]->getAs<types::Double>()->isScalar() == false)
126                 {
127                     //mode
128                     Scierror(999, _("%s: Wrong type for input argument #%d: A integer expected.\n"), "exec", 3);
129                     return types::Function::Error;
130                 }
131
132                 promptMode = (int)in[2]->getAs<types::Double>()->getReal()[0];
133                 bPromptMode = true;
134             }
135         }
136         else if (in[1]->isDouble() && in[1]->getAs<types::Double>()->isScalar())
137         {
138             if (in.size() > 2)
139             {
140                 Scierror(999, _("%s: Wrong value for input argument #%d: 'errcatch' expected.\n"), "exec", 2);
141                 return types::Function::Error;
142             }
143             //mode
144             promptMode = (int)in[1]->getAs<types::Double>()->getReal()[0];
145             bPromptMode = true;
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 = (wchar_t*)MALLOC(sizeof(wchar_t) * (PATH_MAX * 2));
169         get_full_pathW(pwstTemp, pwstFile, PATH_MAX * 2);
170         wstFile = pwstTemp;
171
172         FREE(pwstFile);
173         /*fake call to mopen to show file within file()*/
174         if (mopen(pwstTemp, L"r", 0, &iID) != MOPEN_NO_ERROR)
175         {
176             closeFile(file, iID, wstFile, pExp);
177             FREE(pwstTemp);
178             Scierror(999, _("%s: Cannot open file %s.\n"), "exec", stFile.data());
179             return types::Function::Error;
180         }
181
182         // update where to set the name of the executed file.
183         ConfigVariable::setFileNameToLastWhere(wstFile.data());
184
185         ThreadManagement::LockParser();
186         parser.parseFile(pwstTemp, L"exec");
187         FREE(pwstTemp);
188         if (parser.getExitStatus() !=  Parser::Succeded)
189         {
190             closeFile(file, iID, wstFile, pExp);
191             if (bErrCatch)
192             {
193                 out.push_back(new types::Double(999));
194                 //to lock last error information
195                 ConfigVariable::setLastErrorCall();
196                 ConfigVariable::setLastErrorMessage(parser.getErrorMessage());
197                 ConfigVariable::setLastErrorNumber(999);
198                 delete parser.getTree();
199                 ThreadManagement::UnlockParser();
200                 return types::Function::OK;
201             }
202
203             char* pst = wide_string_to_UTF8(parser.getErrorMessage());
204             Scierror(999, "%s", pst);
205             FREE(pst);
206
207             delete parser.getTree();
208             ThreadManagement::UnlockParser();
209             return types::Function::Error;
210         }
211
212         if (ConfigVariable::getSerialize())
213         {
214             ast::Exp* temp = parser.getTree();
215             if (ConfigVariable::getTimed())
216             {
217                 pExp = callTyper(temp, L"exec");
218             }
219             else
220             {
221                 pExp = callTyper(temp);
222             }
223
224             delete temp;
225         }
226         else
227         {
228             pExp = parser.getTree();
229         }
230
231         ThreadManagement::UnlockParser();
232
233         ConfigVariable::setExecutedFileID(iID);
234     }
235     else if (in[0]->isMacro() || in[0]->isMacroFile())
236     {
237         if (in[0]->isMacroFile())
238         {
239             //1st argument is a macro name, parse and execute it in the current environnement
240             if (in[0]->getAs<types::MacroFile>()->parse() == false)
241             {
242                 char* pstMacro = wide_string_to_UTF8(in[0]->getAs<types::MacroFile>()->getName().c_str());
243                 Scierror(999, _("%s: Unable to parse macro '%s'"), "exec", pstMacro);
244                 FREE(pstMacro);
245                 return types::Function::Error;
246             }
247             pMacro = in[0]->getAs<types::MacroFile>()->getMacro();
248         }
249         else //1st argument is a macro name, execute it in the current environnement
250         {
251             pMacro = in[0]->getAs<types::Macro>();
252         }
253
254         // unable for macro with varargin or varargout
255         auto inputs = pMacro->getInputs();
256         auto outputs = pMacro->getOutputs();
257         if ((inputs->size() != 0 && inputs->back()->getSymbol().getName() == L"varargin") ||
258                 outputs->size() != 0 && outputs->back()->getSymbol().getName() == L"varargout")
259         {
260             Scierror(999, _("%s: Wrong type for input argument #%d: A macro without varargin and varargout expected.\n"), "exec", 1);
261             return types::Function::Error;
262         }
263
264         pExp = pMacro->getBody();
265
266         // update where to set the name of the executed macro instead of "exec"
267         ConfigVariable::WhereEntry lastWhere = ConfigVariable::getWhere().back();
268         int iLine = lastWhere.m_line;
269         int iAbsLine = lastWhere.m_absolute_line;
270         ConfigVariable::where_end();
271         ConfigVariable::where_begin(iLine, iAbsLine, pMacro);
272     }
273     else
274     {
275         Scierror(999, _("%s: Wrong type for input argument #%d: string expected.\n"), "exec", 1);
276         return types::Function::Error;
277     }
278
279     if (pMacro)
280     {
281         //store the line number where is stored this macro in file.
282         ConfigVariable::macroFirstLine_begin(pMacro->getFirstLine());
283     }
284
285     //save current prompt mode
286     int oldVal = ConfigVariable::getPromptMode();
287     ConfigVariable::setPromptMode(promptMode);
288
289     ast::SeqExp* pSeqExp = pExp->getAs<ast::SeqExp>();
290     pSeqExp->setExecFrom(ast::SeqExp::EXEC);
291     std::unique_ptr<ast::ConstVisitor> exec(ConfigVariable::getDefaultVisitor());
292
293     try
294     {
295         symbol::Context* pCtx = symbol::Context::getInstance();
296         int scope = pCtx->getScopeLevel();
297         int level = ConfigVariable::getRecursionLevel();
298         try
299         {
300             pSeqExp->accept(*exec);
301         }
302         catch (const ast::RecursionException& /* re */)
303         {
304             //close opened scope during try
305             while (pCtx->getScopeLevel() > scope)
306             {
307                 pCtx->scope_end();
308             }
309
310             //decrease recursion to init value
311             while (ConfigVariable::getRecursionLevel() > level)
312             {
313                 ConfigVariable::where_end();
314                 ConfigVariable::decreaseRecursion();
315             }
316
317             //print msg about recursion limit and trigger an error
318             wchar_t sz[1024];
319             os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
320             throw ast::InternalError(sz);
321         }
322     }
323     catch (const ast::InternalAbort& ia)
324     {
325         closeFile(file, iID, wstFile, pExp);
326         throw ia;
327     }
328     catch (const ast::InternalError& ie)
329     {
330         if (bErrCatch == false)
331         {
332             closeFile(file, iID, wstFile, pExp);
333             ConfigVariable::setPromptMode(oldVal);
334             ConfigVariable::setExecutedFileID(0);
335             throw ie;
336         }
337
338         ConfigVariable::resetWhereError();
339         iErr = ConfigVariable::getLastErrorNumber();
340     }
341
342     //restore previous prompt mode
343     ConfigVariable::setPromptMode(oldVal);
344     if (bErrCatch)
345     {
346         out.push_back(new types::Double(iErr));
347         //to lock last error information
348         ConfigVariable::setLastErrorCall();
349     }
350
351     closeFile(file, iID, wstFile, pExp);
352     return types::Function::OK;
353 }