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