* Bug 15920 fixed: now genlib always regenerates missing .bin
[scilab.git] / scilab / modules / io / sci_gateway / cpp / sci_genlib.cpp
1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2006 - INRIA - Allan CORNET
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 #ifdef _MSC_VER
17 #pragma warning(disable : 4996) //It's not beautifull but that works !
18 #endif
19
20 #define DEFAULT_ENCODING "UTF-8"
21
22 #ifdef _MSC_VER
23 #define FILE_SEPARATOR L"\\"
24 #else
25 #define FILE_SEPARATOR L"/"
26 #endif
27
28 //XML API
29 #include <libxml/xpath.h>
30 #include <libxml/xmlwriter.h>
31
32 #include <cstring>
33 #include "string.hxx"
34 #include "parser.hxx"
35 #include "context.hxx"
36 #include "io_gw.hxx"
37 #include "scilabWrite.hxx"
38 #include "configvariable.hxx"
39 #include "library.hxx"
40 #include "macrofile.hxx"
41 #include "serializervisitor.hxx"
42 #include "loadlib.hxx"
43
44 extern "C"
45 {
46 #include "setenvvar.h"
47 #include "sci_malloc.h"
48 #include "localization.h"
49 #include "PATH_MAX.h"
50 #include "findfiles.h"
51 #include "FileExist.h"
52 #include "deleteafile.h"
53 #include "os_string.h"
54 #include "splitpath.h"
55 #include "os_wfopen.h"
56 #include "sciprint.h"
57 #include "freeArrayOfString.h"
58 #include "Scierror.h"
59 #include "scicurdir.h"
60 #include "md5.h"
61 #include "pathconvert.h"
62
63 #include <wchar.h> // for wcscmp
64 #include <stdlib.h> // for qsort
65 }
66
67
68 xmlTextWriterPtr openXMLFile(const wchar_t *_pstFilename, const wchar_t* _pstLibName);
69 void closeXMLFile(xmlTextWriterPtr _pWriter);
70 bool AddMacroToXML(xmlTextWriterPtr _pWriter, const std::wstring& name, const std::wstring& file, const std::wstring& md5);
71 static int cmp(const void* p1, const void* p2);
72
73
74 /*--------------------------------------------------------------------------*/
75 types::Function::ReturnValue sci_genlib(types::typed_list &in, int _iRetCount, types::typed_list &out)
76 {
77     int succes = 1;
78     std::vector<std::wstring> failed_files;
79     std::vector<std::wstring> success_files;
80     std::vector<std::wstring> funcs;
81
82     wchar_t pstParseFile[PATH_MAX + FILENAME_MAX];
83     wchar_t pstVerbose[65535];
84
85     int iNbFile = 0;
86     wchar_t *pstParsePath = NULL;
87     int iParsePathLen = 0;
88     wchar_t* pstLibName = NULL;
89     bool bVerbose = false;
90     bool bForce = false;
91
92     if (in.size() < 1 || in.size() > 4)
93     {
94         Scierror(78, _("%s: Wrong number of input argument(s): %d to %d expected.\n"), "genlib", 1, 4);
95         return types::Function::Error;
96     }
97
98     //param 1, library name
99     types::InternalType* pIT = in[0];
100     if (pIT->isString() == false)
101     {
102         Scierror(999, _("%s: Wrong type for input argument #%d: string expected.\n"), "genlib", 1);
103         return types::Function::Error;
104     }
105
106     types::String *pS = pIT->getAs<types::String>();
107     if (pS->getSize() != 1)
108     {
109         Scierror(999, _("%s: Wrong size for input argument #%d: string expected.\n"), "genlib", 1);
110         return types::Function::Error;
111     }
112     pstLibName = pS->get(0);
113
114     //param 2, library path
115     if (in.size() > 1)
116     {
117         pIT = in[1];
118         if (pIT->isString() == false)
119         {
120             Scierror(999, _("%s: Wrong type for input argument #%d: string expected.\n"), "genlib", 2);
121             return types::Function::Error;
122         }
123     }
124     else
125     {
126         int ierr = 0;
127         char* pstr = scigetcwd(&ierr);
128         pIT = new types::String(pstr);
129         FREE(pstr);
130     }
131
132     pS = pIT->getAs<types::String>();
133     if (pS->isScalar() == false)
134     {
135         Scierror(999, _("%s: Wrong size for input argument #%d: string expected.\n"), "genlib", 2);
136         return types::Function::Error;
137     }
138
139     //param 3, force flag
140     if (in.size() > 2)
141     {
142         pIT = in[2];
143         if (pIT->isBool() == false)
144         {
145             Scierror(999, _("%s: Wrong type for input argument #%d: A scalar boolean expected.\n"), "genlib", 3);
146             return types::Function::Error;
147         }
148
149         types::Bool* p = pIT->getAs<types::Bool>();
150         if (p->isScalar() == false)
151         {
152             Scierror(999, _("%s: Wrong type for input argument #%d: A scalar boolean expected.\n"), "genlib", 3);
153             return types::Function::Error;
154         }
155
156         bForce = p->get()[0] == 1;
157     }
158
159     if (in.size() > 3)
160     {
161         //verbose flag
162         pIT = in[3];
163         if (pIT->isBool() == false)
164         {
165             Scierror(999, _("%s: Wrong type for input argument #%d: A scalar boolean expected.\n"), "genlib", 3);
166             return types::Function::Error;
167         }
168
169         types::Bool* p = pIT->getAs<types::Bool>();
170         if (p->isScalar() == false)
171         {
172             Scierror(999, _("%s: Wrong type for input argument #%d: A scalar boolean expected.\n"), "genlib", 3);
173             return types::Function::Error;
174         }
175
176         bVerbose = p->get()[0] == 1;
177     }
178
179     wchar_t* pstFile = pS->get(0);
180     pstParsePath = pathconvertW(pstFile, TRUE, TRUE, AUTO_STYLE);
181
182     if (in.size() == 1)
183     {
184         delete pS;
185     }
186
187     os_swprintf(pstParseFile, PATH_MAX + FILENAME_MAX, L"%lslib", pstParsePath);
188
189     if (bVerbose)
190     {
191         os_swprintf(pstVerbose, 65535, _W("-- Creation of [%ls] (Macros) --\n").c_str(), pstLibName);
192
193         //save current prompt mode
194         int oldVal = ConfigVariable::getPromptMode();
195         //set mode silent for errors
196         ConfigVariable::setPromptMode(0);
197         scilabWriteW(pstVerbose);
198         //restore previous prompt mode
199         ConfigVariable::setPromptMode(oldVal);
200     }
201
202     MacroInfoList lstOld;
203     if (FileExistW(pstParseFile))
204     {
205         //read it to get previous information like md5
206         std::wstring libname;
207         parseLibFile(pstParseFile, lstOld, libname);
208         deleteafileW(pstParseFile);
209     }
210
211     xmlTextWriterPtr pWriter = openXMLFile(pstParseFile, pstLibName);
212
213     if (pWriter == NULL)
214     {
215         os_swprintf(pstVerbose, 65535, _W("%ls: Cannot open file ''%ls''.\n").c_str(), L"genlib", pstParseFile);
216         scilabWriteW(pstVerbose);
217
218         out.push_back(new types::Bool(0));
219         FREE(pstParsePath);
220         return types::Function::OK;
221     }
222
223
224     wchar_t **pstPath = findfilesW(pstParsePath, L"*.sci", &iNbFile, FALSE);
225
226     if (pstPath)
227     {
228         // sort by name using C-style functions
229         qsort(pstPath, iNbFile, sizeof(wchar_t*), cmp);
230
231         types::Library* pLib = new types::Library(pstParsePath);
232         for (int k = 0 ; k < iNbFile ; k++)
233         {
234             //version with direct parsing
235             //parse the file to find all functions
236             std::wstring stFullPath = std::wstring(pstParsePath) + std::wstring(pstPath[k]);
237             std::wstring stFullPathBin(stFullPath);
238             stFullPathBin.replace(stFullPathBin.end() - 3, stFullPathBin.end(), L"bin");
239             std::wstring pstPathBin(pstPath[k]);
240             pstPathBin.replace(pstPathBin.end() - 3, pstPathBin.end(), L"bin");
241
242             //compute file md5
243             FILE* fmdf5 = os_wfopen(stFullPath.data(), L"rb");
244             if (fmdf5 == NULL)
245             {
246                 char* pstr = wide_string_to_UTF8(stFullPath.data());
247                 Scierror(999, _("%s: Cannot open file ''%s''.\n"), "genlib", pstr);
248                 FREE(pstr);
249                 FREE(pstParsePath);
250                 freeArrayOfWideString(pstPath, iNbFile);
251                 pLib->killMe();
252                 return types::Function::Error;
253             }
254
255             char* md5 = md5_file(fmdf5);
256             fclose(fmdf5);
257
258             wchar_t* wmd5 = to_wide_string(md5);
259             FREE(md5);
260             std::wstring wide_md5(wmd5);
261             FREE(wmd5);
262
263             if (bForce == false)
264             {
265                 //check if is exist in old file
266                 MacroInfoList::iterator it = lstOld.find(pstPathBin);
267                 if (it != lstOld.end() && FileExistW(stFullPathBin.data()))
268                 {
269                     if (wide_md5 == (*it).second.md5)
270                     {
271                         //file not change, we can skip it
272                         AddMacroToXML(pWriter, (*it).second.name, pstPathBin, wide_md5);
273                         pLib->add((*it).second.name, new types::MacroFile((*it).second.name, stFullPathBin, pstLibName));
274                         success_files.push_back(stFullPath);
275                         funcs.push_back((*it).second.name);
276                         continue;
277                     }
278                 }
279             }
280
281             if (bVerbose)
282             {
283                 sciprint(_("%ls: Processing file: %ls\n"), L"genlib", pstPath[k]);
284             }
285
286             Parser parser;
287             parser.parseFile(stFullPath, ConfigVariable::getSCIPath());
288             if (parser.getExitStatus() !=  Parser::Succeded)
289             {
290                 if (_iRetCount != 4)
291                 {
292                     std::wstring wstrErr = parser.getErrorMessage();
293
294                     wchar_t errmsg[256];
295                     os_swprintf(errmsg, 256, _W("%ls: Error in file %ls.\n").c_str(), L"genlib", stFullPath.data());
296                     wstrErr += errmsg;
297
298                     char* str = wide_string_to_UTF8(wstrErr.c_str());
299                     Scierror(999, str);
300                     FREE(str);
301
302                     FREE(pstParsePath);
303                     freeArrayOfWideString(pstPath, iNbFile);
304                     closeXMLFile(pWriter);
305                     delete pLib;
306                     return types::Function::Error;
307                 }
308
309                 failed_files.push_back(stFullPath);
310                 succes = 0;
311                 continue;
312             }
313
314             //serialize ast
315             ast::SerializeVisitor* s = new ast::SerializeVisitor(parser.getTree());
316
317             unsigned char* serialAst = s->serialize();
318             // Header is : buffer size (4 bytes) + scilab version (4 bytes)
319             unsigned int size = *((unsigned int*)serialAst);
320
321             FILE* f = os_wfopen(stFullPathBin.c_str(), L"wb");
322             fwrite(serialAst, 1, size, f);
323             fclose(f);
324
325             ast::exps_t LExp = parser.getTree()->getAs<ast::SeqExp>()->getExps();
326             for (ast::exps_t::iterator j = LExp.begin(), itEnd = LExp.end() ; j != itEnd ; ++j)
327             {
328                 if ((*j)->isFunctionDec())
329                 {
330                     ast::FunctionDec* pFD = (*j)->getAs<ast::FunctionDec>();
331                     const std::wstring& name = pFD->getSymbol().getName();
332                     if (name + L".sci" == pstPath[k])
333                     {
334                         if (AddMacroToXML(pWriter, name, pstPathBin, wide_md5) == false)
335                         {
336                             os_swprintf(pstVerbose, 65535, _W("%ls: Warning: %ls information cannot be added to file %ls. File ignored\n").c_str(), L"genlib", pFD->getSymbol().getName().c_str(), pstPath[k]);
337                             scilabWriteW(pstVerbose);
338                         }
339
340                         pLib->add(name, new types::MacroFile(name, stFullPathBin, pstLibName));
341                         success_files.push_back(stFullPath);
342                         funcs.push_back(name);
343                         break;
344                     }
345                 }
346             }
347
348             delete s;
349             delete parser.getTree();
350         }
351
352         symbol::Context* ctx = symbol::Context::getInstance();
353         symbol::Symbol sym = symbol::Symbol(pstLibName);
354         if (ctx->isprotected(sym) == false)
355         {
356             ctx->put(symbol::Symbol(pstLibName), pLib);
357         }
358         else
359         {
360             Scierror(999, _("Redefining permanent variable.\n"));
361
362             freeArrayOfWideString(pstPath, iNbFile);
363             FREE(pstParsePath);
364             closeXMLFile(pWriter);
365             delete pLib;
366             return types::Function::Error;
367         }
368     }
369
370     freeArrayOfWideString(pstPath, iNbFile);
371
372     out.push_back(new types::Bool(succes));
373
374     if (_iRetCount > 1)
375     {
376         int size = static_cast<int>(funcs.size());
377         if (size == 0)
378         {
379             out.push_back(types::Double::Empty());
380         }
381         else
382         {
383             types::String* s = new types::String(size, 1);
384
385             for (int i = 0; i < size; ++i)
386             {
387                 s->set(i, funcs[i].data());
388             }
389
390             out.push_back(s);
391         }
392     }
393
394     if (_iRetCount > 2)
395     {
396         int size = static_cast<int>(success_files.size());
397         if (size == 0)
398         {
399             out.push_back(types::Double::Empty());
400         }
401         else
402         {
403             types::String* s = new types::String(size, 1);
404
405             for (int i = 0; i < size; ++i)
406             {
407                 s->set(i, success_files[i].data());
408             }
409
410             out.push_back(s);
411         }
412     }
413
414     if (_iRetCount > 3)
415     {
416         int size = static_cast<int>(failed_files.size());
417         if (size == 0)
418         {
419             out.push_back(types::Double::Empty());
420         }
421         else
422         {
423             types::String* s = new types::String(size, 1);
424
425             for (int i = 0; i < size; ++i)
426             {
427                 s->set(i, failed_files[i].data());
428             }
429
430             out.push_back(s);
431         }
432     }
433
434     FREE(pstParsePath);
435     closeXMLFile(pWriter);
436     return types::Function::OK;
437 }
438
439 void closeXMLFile(xmlTextWriterPtr _pWriter)
440 {
441     int iLen;
442
443     //close opened nodes
444     iLen = xmlTextWriterEndElement(_pWriter);
445     if (iLen < 0)
446     {
447         return;
448     }
449
450     //close document
451     iLen = xmlTextWriterEndDocument(_pWriter);
452     if (iLen < 0)
453     {
454         return;
455     }
456
457     //close xml writer
458     xmlFreeTextWriter(_pWriter);
459 }
460
461 xmlTextWriterPtr openXMLFile(const wchar_t *_pstFilename, const wchar_t* _pstLibName)
462 {
463     int iLen;
464     xmlTextWriterPtr pWriter = NULL;;
465     char *pstFilename = wide_string_to_UTF8(_pstFilename);
466     char *pstLibName = wide_string_to_UTF8(_pstLibName);
467
468
469     //create a writer
470     pWriter = xmlNewTextWriterFilename(pstFilename, 0);
471     if (pWriter == NULL)
472     {
473         FREE(pstFilename);
474         FREE(pstLibName);
475         return NULL;
476     }
477
478     //setup indentation
479     xmlTextWriterSetIndent (pWriter, 1);
480     xmlTextWriterSetIndentString (pWriter, (xmlChar*)"  ");
481
482     //create a new document
483     iLen = xmlTextWriterStartDocument(pWriter, NULL, DEFAULT_ENCODING, "no");
484     if (iLen < 0)
485     {
486         closeXMLFile(pWriter);
487         FREE(pstFilename);
488         FREE(pstLibName);
489         return NULL;
490     }
491
492     //add a node "scilablib"
493     iLen = xmlTextWriterStartElement(pWriter, (xmlChar*)"scilablib");
494     if (iLen < 0)
495     {
496         closeXMLFile(pWriter);
497         FREE(pstFilename);
498         FREE(pstLibName);
499         return NULL;
500     }
501
502     //Add attribute "name"
503     iLen = xmlTextWriterWriteAttribute(pWriter, (xmlChar*)"name", (xmlChar*)pstLibName);
504     if (iLen < 0)
505     {
506         closeXMLFile(pWriter);
507         FREE(pstFilename);
508         FREE(pstLibName);
509         return NULL;
510     }
511
512     FREE(pstFilename);
513     FREE(pstLibName);
514
515     return pWriter;
516 }
517
518 bool AddMacroToXML(xmlTextWriterPtr _pWriter, const std::wstring& name, const std::wstring& file, const std::wstring& md5)
519 {
520     int iLen;
521
522     if (_pWriter == NULL)
523     {
524         return false;
525     }
526
527     //create node "macro"
528     iLen = xmlTextWriterStartElement(_pWriter, (xmlChar*)"macro");
529     if (iLen < 0)
530     {
531         return false;
532     }
533
534     //Add attribute "name"
535     char* pst1 = wide_string_to_UTF8(name.data());
536     iLen = xmlTextWriterWriteAttribute(_pWriter, (xmlChar*)"name", (xmlChar*)pst1);
537     if (iLen < 0)
538     {
539         return false;
540     }
541     FREE(pst1);
542
543     //Add attribute "file"
544     char* pst2 = wide_string_to_UTF8(file.data());
545     iLen = xmlTextWriterWriteAttribute(_pWriter, (xmlChar*)"file", (xmlChar*)pst2);
546     if (iLen < 0)
547     {
548         return false;
549     }
550     FREE(pst2);
551
552     //Add attribute "md5"
553     char* pst3 = wide_string_to_UTF8(md5.data());
554     iLen = xmlTextWriterWriteAttribute(_pWriter, (xmlChar*)"md5", (xmlChar*)pst3);
555     if (iLen < 0)
556     {
557         return false;
558     }
559     FREE(pst3);
560
561     //close "macro" node
562     iLen = xmlTextWriterEndElement(_pWriter);
563     if (iLen < 0)
564     {
565         return false;
566     }
567
568     return true;
569 }
570
571
572 static int cmp(const void* p1, const void* p2)
573 {
574     return wcscmp(* (wchar_t * const *) p1, * (wchar_t * const *) p2);
575 }
576