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