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