1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24
25 #include "HelpCompiler.hxx"
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <libxslt/xslt.h>
30 #include <libxslt/xsltInternals.h>
31 #include <libxslt/transform.h>
32 #include <libxslt/xsltutils.h>
33 #ifdef __MINGW32__
34 #include <tools/prewin.h>
35 #include <tools/postwin.h>
36 #endif
37 #include <osl/thread.hxx>
38
impl_sleep(sal_uInt32 nSec)39 static void impl_sleep( sal_uInt32 nSec )
40 {
41 TimeValue aTime;
42 aTime.Seconds = nSec;
43 aTime.Nanosec = 0;
44
45 osl::Thread::wait( aTime );
46 }
47
HelpCompiler(StreamTable & in_streamTable,const fs::path & in_inputFile,const fs::path & in_src,const fs::path & in_resEmbStylesheet,const std::string & in_module,const std::string & in_lang,bool in_bExtensionMode)48 HelpCompiler::HelpCompiler(StreamTable &in_streamTable, const fs::path &in_inputFile,
49 const fs::path &in_src, const fs::path &in_resEmbStylesheet,
50 const std::string &in_module, const std::string &in_lang, bool in_bExtensionMode)
51 : streamTable(in_streamTable), inputFile(in_inputFile),
52 src(in_src), module(in_module), lang(in_lang), resEmbStylesheet(in_resEmbStylesheet),
53 bExtensionMode( in_bExtensionMode )
54 {
55 xmlKeepBlanksDefaultValue = 0;
56 }
57
getSourceDocument(const fs::path & filePath)58 xmlDocPtr HelpCompiler::getSourceDocument(const fs::path &filePath)
59 {
60 static const char *params[4 + 1];
61 static xsltStylesheetPtr cur = NULL;
62
63 xmlDocPtr res;
64 if( bExtensionMode )
65 {
66 res = xmlParseFile(filePath.native_file_string().c_str());
67 if( !res ){
68 impl_sleep( 3 );
69 res = xmlParseFile(filePath.native_file_string().c_str());
70 }
71 }
72 else
73 {
74 if (!cur)
75 {
76 static std::string fsroot('\'' + src.toUTF8() + '\'');
77 static std::string esclang('\'' + lang + '\'');
78
79 xmlSubstituteEntitiesDefault(1);
80 xmlLoadExtDtdDefaultValue = 1;
81 cur = xsltParseStylesheetFile((const xmlChar *)resEmbStylesheet.native_file_string().c_str());
82
83 int nbparams = 0;
84 params[nbparams++] = "Language";
85 params[nbparams++] = esclang.c_str();
86 params[nbparams++] = "fsroot";
87 params[nbparams++] = fsroot.c_str();
88 params[nbparams] = NULL;
89 }
90 xmlDocPtr doc = xmlParseFile(filePath.native_file_string().c_str());
91 if( !doc )
92 {
93 impl_sleep( 3 );
94 doc = xmlParseFile(filePath.native_file_string().c_str());
95 }
96
97 //???res = xmlParseFile(filePath.native_file_string().c_str());
98
99 res = xsltApplyStylesheet(cur, doc, params);
100 xmlFreeDoc(doc);
101 }
102 return res;
103 }
104
switchFind(xmlDocPtr doc)105 HashSet HelpCompiler::switchFind(xmlDocPtr doc)
106 {
107 HashSet hs;
108 xmlChar *xpath = (xmlChar*)"//switchinline";
109
110 xmlXPathContextPtr context = xmlXPathNewContext(doc);
111 xmlXPathObjectPtr result = xmlXPathEvalExpression(xpath, context);
112 xmlXPathFreeContext(context);
113 if (result)
114 {
115 xmlNodeSetPtr nodeset = result->nodesetval;
116 for (int i = 0; i < nodeset->nodeNr; i++)
117 {
118 xmlNodePtr el = nodeset->nodeTab[i];
119 xmlChar *select = xmlGetProp(el, (xmlChar*)"select");
120 if (select)
121 {
122 if (!strcmp((const char*)select, "appl"))
123 {
124 xmlNodePtr n1 = el->xmlChildrenNode;
125 while (n1)
126 {
127 if ((!xmlStrcmp(n1->name, (const xmlChar*)"caseinline")))
128 {
129 xmlChar *appl = xmlGetProp(n1, (xmlChar*)"select");
130 hs.push_back(std::string((const char*)appl));
131 xmlFree(appl);
132 }
133 else if ((!xmlStrcmp(n1->name, (const xmlChar*)"defaultinline")))
134 hs.push_back(std::string("DEFAULT"));
135 n1 = n1->next;
136 }
137 }
138 xmlFree(select);
139 }
140 }
141 xmlXPathFreeObject(result);
142 }
143 hs.push_back(std::string("DEFAULT"));
144 return hs;
145 }
146
147 // returns a node representing the whole stuff compiled for the current
148 // application.
clone(xmlNodePtr node,const std::string & appl)149 xmlNodePtr HelpCompiler::clone(xmlNodePtr node, const std::string& appl)
150 {
151 xmlNodePtr parent = xmlCopyNode(node, 2);
152 xmlNodePtr n = node->xmlChildrenNode;
153 while (n != NULL)
154 {
155 bool isappl = false;
156 if ( (!strcmp((const char*)n->name, "switchinline")) ||
157 (!strcmp((const char*)n->name, "switch")) )
158 {
159 xmlChar *select = xmlGetProp(n, (xmlChar*)"select");
160 if (select)
161 {
162 if (!strcmp((const char*)select, "appl"))
163 isappl = true;
164 xmlFree(select);
165 }
166 }
167 if (isappl)
168 {
169 xmlNodePtr caseNode = n->xmlChildrenNode;
170 if (appl == "DEFAULT")
171 {
172 while (caseNode)
173 {
174 if (!strcmp((const char*)caseNode->name, "defaultinline"))
175 {
176 xmlNodePtr cnl = caseNode->xmlChildrenNode;
177 while (cnl)
178 {
179 xmlAddChild(parent, clone(cnl, appl));
180 cnl = cnl->next;
181 }
182 break;
183 }
184 caseNode = caseNode->next;
185 }
186 }
187 else
188 {
189 while (caseNode)
190 {
191 isappl=false;
192 if (!strcmp((const char*)caseNode->name, "caseinline"))
193 {
194 xmlChar *select = xmlGetProp(n, (xmlChar*)"select");
195 if (select)
196 {
197 if (!strcmp((const char*)select, appl.c_str()))
198 isappl = true;
199 xmlFree(select);
200 }
201 if (isappl)
202 {
203 xmlNodePtr cnl = caseNode->xmlChildrenNode;
204 while (cnl)
205 {
206 xmlAddChild(parent, clone(cnl, appl));
207 cnl = cnl->next;
208 }
209 break;
210 }
211
212 }
213 caseNode = caseNode->next;
214 }
215 }
216
217 }
218 else
219 xmlAddChild(parent, clone(n, appl));
220
221 n = n->next;
222 }
223 return parent;
224 }
225
226 class myparser
227 {
228 public:
229 std::string documentId;
230 std::string fileName;
231 std::string title;
232 HashSet *hidlist;
233 Hashtable *keywords;
234 Stringtable *helptexts;
235 private:
236 HashSet extendedHelpText;
237 public:
myparser(const std::string & indocumentId,const std::string & infileName,const std::string & intitle)238 myparser(const std::string &indocumentId, const std::string &infileName,
239 const std::string &intitle) : documentId(indocumentId), fileName(infileName),
240 title(intitle)
241 {
242 hidlist = new HashSet;
243 keywords = new Hashtable;
244 helptexts = new Stringtable;
245 }
246 void traverse( xmlNodePtr parentNode );
247 private:
248 std::string dump(xmlNodePtr node);
249 };
250
dump(xmlNodePtr node)251 std::string myparser::dump(xmlNodePtr node)
252 {
253 std::string app;
254 if (node->xmlChildrenNode)
255 {
256 xmlNodePtr list = node->xmlChildrenNode;
257 while (list)
258 {
259 app += dump(list);
260 list = list->next;
261 }
262 }
263 if (xmlNodeIsText(node))
264 {
265 xmlChar *pContent = xmlNodeGetContent(node);
266 app += std::string((const char*)pContent);
267 xmlFree(pContent);
268 // std::cout << app << std::endl;
269 }
270 return app;
271 }
272
trim(std::string & str)273 void trim(std::string& str)
274 {
275 std::string::size_type pos = str.find_last_not_of(' ');
276 if(pos != std::string::npos)
277 {
278 str.erase(pos + 1);
279 pos = str.find_first_not_of(' ');
280 if(pos != std::string::npos)
281 str.erase(0, pos);
282 }
283 else
284 str.erase(str.begin(), str.end());
285 }
286
traverse(xmlNodePtr parentNode)287 void myparser::traverse( xmlNodePtr parentNode )
288 {
289 // traverse all nodes that belong to the parent
290 xmlNodePtr test ;
291 for (test = parentNode->xmlChildrenNode; test; test = test->next)
292 {
293 if (fileName.empty() && !strcmp((const char*)test->name, "filename"))
294 {
295 xmlNodePtr node = test->xmlChildrenNode;
296 if (xmlNodeIsText(node))
297 {
298 xmlChar *pContent = xmlNodeGetContent(node);
299 fileName = std::string((const char*)pContent);
300 xmlFree(pContent);
301 }
302 }
303 else if (title.empty() && !strcmp((const char*)test->name, "title"))
304 {
305 title = dump(test);
306 if (title.empty())
307 title = "<notitle>";
308 }
309 else if (!strcmp((const char*)test->name, "bookmark"))
310 {
311 xmlChar *branchxml = xmlGetProp(test, (const xmlChar*)"branch");
312 xmlChar *idxml = xmlGetProp(test, (const xmlChar*)"id");
313 std::string branch((const char*)branchxml);
314 std::string anchor((const char*)idxml);
315 xmlFree (branchxml);
316 xmlFree (idxml);
317
318 std::string hid;
319
320 if (branch.find("hid") == 0)
321 {
322 size_t index = branch.find('/');
323 if (index != std::string::npos)
324 {
325 hid = branch.substr(1 + index);
326 // one shall serve as a documentId
327 if (documentId.empty())
328 documentId = hid;
329 extendedHelpText.push_back(hid);
330 std::string foo = anchor.empty() ? hid : hid + "#" + anchor;
331 HCDBG(std::cerr << "hid pushback" << foo << std::endl);
332 hidlist->push_back( anchor.empty() ? hid : hid + "#" + anchor);
333 }
334 else
335 continue;
336 }
337 else if (branch.compare("index") == 0)
338 {
339 LinkedList ll;
340
341 for (xmlNodePtr nd = test->xmlChildrenNode; nd; nd = nd->next)
342 {
343 if (strcmp((const char*)nd->name, "bookmark_value"))
344 continue;
345
346 std::string embedded;
347 xmlChar *embeddedxml = xmlGetProp(nd, (const xmlChar*)"embedded");
348 if (embeddedxml)
349 {
350 embedded = std::string((const char*)embeddedxml);
351 xmlFree (embeddedxml);
352 std::transform (embedded.begin(), embedded.end(),
353 embedded.begin(), tolower);
354 }
355
356 bool isEmbedded = !embedded.empty() && embedded.compare("true") == 0;
357 if (isEmbedded)
358 continue;
359
360 std::string keyword = dump(nd);
361 size_t keywordSem = keyword.find(';');
362 if (keywordSem != std::string::npos)
363 {
364 std::string tmppre =
365 keyword.substr(0,keywordSem);
366 trim(tmppre);
367 std::string tmppos =
368 keyword.substr(1+keywordSem);
369 trim(tmppos);
370 keyword = tmppre + ";" + tmppos;
371 }
372 ll.push_back(keyword);
373 }
374 if (!ll.empty())
375 (*keywords)[anchor] = ll;
376 }
377 else if (branch.compare("contents") == 0)
378 {
379 // currently not used
380 }
381 }
382 else if (!strcmp((const char*)test->name, "ahelp"))
383 {
384 std::string text = dump(test);
385 trim(text);
386 std::string name;
387
388 HashSet::const_iterator aEnd = extendedHelpText.end();
389 for (HashSet::const_iterator iter = extendedHelpText.begin(); iter != aEnd;
390 ++iter)
391 {
392 name = *iter;
393 (*helptexts)[name] = text;
394 }
395 extendedHelpText.clear();
396 }
397
398 // traverse children
399 traverse(test);
400 }
401 }
402
compile(void)403 bool HelpCompiler::compile( void ) throw( HelpProcessingException )
404 {
405 // we now have the jaroutputstream, which will contain the document.
406 // now determine the document as a dom tree in variable docResolved
407
408 xmlDocPtr docResolvedOrg = getSourceDocument(inputFile);
409
410 // now add path to the document
411 // resolve the dom
412 if (!docResolvedOrg)
413 {
414 impl_sleep( 3 );
415 docResolvedOrg = getSourceDocument(inputFile);
416 if( !docResolvedOrg )
417 {
418 std::stringstream aStrStream;
419 aStrStream << "ERROR: file not existing: " << inputFile.native_file_string().c_str() << std::endl;
420 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
421 }
422 }
423
424 // now find all applications for which one has to compile
425 std::string documentId;
426 std::string fileName;
427 std::string title;
428 // returns all applications for which one has to compile
429 HashSet applications = switchFind(docResolvedOrg);
430
431 HashSet::const_iterator aEnd = applications.end();
432 for (HashSet::const_iterator aI = applications.begin(); aI != aEnd; ++aI)
433 {
434 std::string appl = *aI;
435 std::string modulename = appl;
436 if (modulename[0] == 'S')
437 {
438 modulename = modulename.substr(1);
439 std::transform(modulename.begin(), modulename.end(), modulename.begin(), tolower);
440 }
441 if (modulename != "DEFAULT" && modulename != module)
442 continue;
443
444 // returns a clone of the document with swich-cases resolved
445 xmlNodePtr docResolved = clone(xmlDocGetRootElement(docResolvedOrg), appl);
446 myparser aparser(documentId, fileName, title);
447 aparser.traverse(docResolved);
448
449 documentId = aparser.documentId;
450 fileName = aparser.fileName;
451 title = aparser.title;
452
453 HCDBG(std::cerr << documentId << " : " << fileName << " : " << title << std::endl);
454
455 xmlDocPtr docResolvedDoc = xmlCopyDoc(docResolvedOrg, false);
456 xmlDocSetRootElement(docResolvedDoc, docResolved);
457
458 if (modulename == "DEFAULT")
459 {
460 streamTable.dropdefault();
461 streamTable.default_doc = docResolvedDoc;
462 streamTable.default_hidlist = aparser.hidlist;
463 streamTable.default_helptexts = aparser.helptexts;
464 streamTable.default_keywords = aparser.keywords;
465 }
466 else if (modulename == module)
467 {
468 streamTable.dropappl();
469 streamTable.appl_doc = docResolvedDoc;
470 streamTable.appl_hidlist = aparser.hidlist;
471 streamTable.appl_helptexts = aparser.helptexts;
472 streamTable.appl_keywords = aparser.keywords;
473 }
474 else
475 {
476 std::stringstream aStrStream;
477 aStrStream << "ERROR: Found unexpected module name \"" << modulename
478 << "\" in file" << src.native_file_string().c_str() << std::endl;
479 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
480 }
481
482 } // end iteration over all applications
483
484 streamTable.document_id = documentId;
485 streamTable.document_path = fileName;
486 streamTable.document_title = title;
487 std::string actMod = module;
488 if ( !bExtensionMode && !fileName.empty())
489 {
490 if (fileName.find("/text/") == 0)
491 {
492 int len = strlen("/text/");
493 actMod = fileName.substr(len);
494 actMod = actMod.substr(0, actMod.find('/'));
495 }
496 }
497 streamTable.document_module = actMod;
498
499 xmlFreeDoc(docResolvedOrg);
500 return true;
501 }
502
503 namespace fs
504 {
getThreadTextEncoding(void)505 rtl_TextEncoding getThreadTextEncoding( void )
506 {
507 static bool bNeedsInit = true;
508 static rtl_TextEncoding nThreadTextEncoding;
509 if( bNeedsInit )
510 {
511 bNeedsInit = false;
512 nThreadTextEncoding = osl_getThreadTextEncoding();
513 }
514 return nThreadTextEncoding;
515 }
516
create_directory(const fs::path indexDirName)517 void create_directory(const fs::path indexDirName)
518 {
519 HCDBG(
520 std::cerr << "creating " <<
521 rtl::OUStringToOString(indexDirName.data, RTL_TEXTENCODING_UTF8).getStr()
522 << std::endl
523 );
524 osl::Directory::createPath(indexDirName.data);
525 }
526
rename(const fs::path & src,const fs::path & dest)527 void rename(const fs::path &src, const fs::path &dest)
528 {
529 osl::File::move(src.data, dest.data);
530 }
531
copy(const fs::path & src,const fs::path & dest)532 void copy(const fs::path &src, const fs::path &dest)
533 {
534 osl::File::copy(src.data, dest.data);
535 }
536
exists(const fs::path & in)537 bool exists(const fs::path &in)
538 {
539 osl::File tmp(in.data);
540 return (tmp.open(osl_File_OpenFlag_Read) == osl::FileBase::E_None);
541 }
542
remove(const fs::path & in)543 void remove(const fs::path &in)
544 {
545 osl::File::remove(in.data);
546 }
547
removeRecursive(rtl::OUString const & _suDirURL)548 void removeRecursive(rtl::OUString const& _suDirURL)
549 {
550 {
551 osl::Directory aDir(_suDirURL);
552 aDir.open();
553 if (aDir.isOpen())
554 {
555 osl::DirectoryItem aItem;
556 osl::FileStatus aStatus(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes);
557 while (aDir.getNextItem(aItem) == ::osl::FileBase::E_None)
558 {
559 if (osl::FileBase::E_None == aItem.getFileStatus(aStatus) &&
560 aStatus.isValid(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes))
561 {
562 rtl::OUString suFilename = aStatus.getFileName();
563 rtl::OUString suFullFileURL;
564 suFullFileURL += _suDirURL;
565 suFullFileURL += rtl::OUString::createFromAscii("/");
566 suFullFileURL += suFilename;
567
568 if (aStatus.getFileType() == osl::FileStatus::Directory)
569 removeRecursive(suFullFileURL);
570 else
571 osl::File::remove(suFullFileURL);
572 }
573 }
574 aDir.close();
575 }
576 }
577 osl::Directory::remove(_suDirURL);
578 }
579
remove_all(const fs::path & in)580 void remove_all(const fs::path &in)
581 {
582 removeRecursive(in.data);
583 }
584 }
585
586 /* vi:set tabstop=4 shiftwidth=4 expandtab: */
587