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