Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <helpcompiler/HelpCompiler.hxx>
22 : #include <limits.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #include <libxslt/xslt.h>
26 : #include <libxslt/xsltInternals.h>
27 : #include <libxslt/transform.h>
28 : #include <libxslt/xsltutils.h>
29 : #include <osl/thread.hxx>
30 :
31 0 : static void impl_sleep( sal_uInt32 nSec )
32 : {
33 : TimeValue aTime;
34 0 : aTime.Seconds = nSec;
35 0 : aTime.Nanosec = 0;
36 :
37 0 : osl::Thread::wait( aTime );
38 0 : }
39 :
40 8906 : HelpCompiler::HelpCompiler(StreamTable &in_streamTable, const fs::path &in_inputFile,
41 : const fs::path &in_src, const fs::path &in_resEmbStylesheet,
42 : const std::string &in_module, const std::string &in_lang, bool in_bExtensionMode)
43 : : streamTable(in_streamTable), inputFile(in_inputFile),
44 : src(in_src), module(in_module), lang(in_lang), resEmbStylesheet(in_resEmbStylesheet),
45 8906 : bExtensionMode( in_bExtensionMode )
46 : {
47 8906 : xmlKeepBlanksDefaultValue = 0;
48 8906 : char* guitmp = getenv("GUI");
49 8906 : if (guitmp)
50 : {
51 8906 : gui = (strcmp(guitmp, "UNX") ? gui : "UNIX");
52 8906 : gui = (strcmp(guitmp, "MAC") ? gui : "MAC");
53 8906 : gui = (strcmp(guitmp, "WNT") ? gui : "WIN");
54 : }
55 8906 : }
56 :
57 8906 : xmlDocPtr HelpCompiler::getSourceDocument(const fs::path &filePath)
58 : {
59 : static const char *params[4 + 1];
60 : static xsltStylesheetPtr cur = NULL;
61 :
62 : xmlDocPtr res;
63 8906 : if( bExtensionMode )
64 : {
65 0 : res = xmlParseFile(filePath.native_file_string().c_str());
66 0 : if( !res ){
67 0 : impl_sleep( 3 );
68 0 : res = xmlParseFile(filePath.native_file_string().c_str());
69 : }
70 : }
71 : else
72 : {
73 8906 : if (!cur)
74 : {
75 8 : static std::string fsroot('\'' + src.toUTF8() + '\'');
76 8 : static std::string esclang('\'' + lang + '\'');
77 :
78 8 : xmlSubstituteEntitiesDefault(1);
79 8 : xmlLoadExtDtdDefaultValue = 1;
80 8 : cur = xsltParseStylesheetFile((const xmlChar *)resEmbStylesheet.native_file_string().c_str());
81 :
82 8 : int nbparams = 0;
83 8 : params[nbparams++] = "Language";
84 8 : params[nbparams++] = esclang.c_str();
85 8 : params[nbparams++] = "fsroot";
86 8 : params[nbparams++] = fsroot.c_str();
87 8 : params[nbparams] = NULL;
88 : }
89 8906 : xmlDocPtr doc = xmlParseFile(filePath.native_file_string().c_str());
90 8906 : if( !doc )
91 : {
92 0 : impl_sleep( 3 );
93 0 : doc = xmlParseFile(filePath.native_file_string().c_str());
94 : }
95 :
96 : //???res = xmlParseFile(filePath.native_file_string().c_str());
97 :
98 8906 : res = xsltApplyStylesheet(cur, doc, params);
99 8906 : xmlFreeDoc(doc);
100 : }
101 8906 : return res;
102 : }
103 :
104 : // returns a node representing the whole stuff compiled for the current
105 : // application.
106 939275 : xmlNodePtr HelpCompiler::clone(xmlNodePtr node, const std::string& appl)
107 : {
108 939275 : xmlNodePtr root = xmlCopyNode(node, 2);
109 939275 : if (node->xmlChildrenNode)
110 : {
111 532819 : xmlNodePtr list = node->xmlChildrenNode;
112 2000443 : while (list)
113 : {
114 934805 : if (strcmp((const char*)list->name, "switchinline") == 0 || strcmp((const char*)list->name, "switch") == 0)
115 : {
116 11434 : std::string tmp="";
117 11434 : if (strcmp((const char*)xmlGetProp(list, (xmlChar*)"select"), "sys"))
118 : {
119 5584 : tmp = gui;
120 : }
121 11434 : if (strcmp((const char*)xmlGetProp(list, (xmlChar*)"select"), "appl"))
122 : {
123 5850 : tmp = appl;
124 : }
125 11434 : if (tmp.compare("") != 0)
126 : {
127 11434 : bool isCase=false;
128 11434 : xmlNodePtr caseList=list->xmlChildrenNode;
129 42031 : while (caseList)
130 : {
131 19163 : xmlChar *select = xmlGetProp(caseList, (xmlChar*)"select");
132 19163 : if (select)
133 : {
134 12810 : if (!strcmp((const char*)select, tmp.c_str()) && !isCase)
135 : {
136 0 : isCase=true;
137 0 : xmlNodePtr clp = caseList->xmlChildrenNode;
138 0 : while (clp)
139 : {
140 0 : xmlAddChild(root, clone(clp, appl));
141 0 : clp = clp->next;
142 : }
143 : }
144 12810 : xmlFree(select);
145 : }
146 : else
147 : {
148 6353 : if ((strcmp((const char*)caseList->name, "defaultinline") != 0) && (strcmp((const char*)caseList->name, "default") != 0))
149 : {
150 0 : xmlAddChild(root, clone(caseList, appl));
151 : }
152 : else
153 : {
154 6353 : if (!isCase)
155 : {
156 6353 : xmlNodePtr clp = caseList->xmlChildrenNode;
157 19704 : while (clp)
158 : {
159 6998 : xmlAddChild(root, clone(clp, appl));
160 6998 : clp = clp->next;
161 : }
162 : }
163 : }
164 : }
165 19163 : caseList = caseList->next;
166 : }
167 11434 : }
168 : }
169 : else
170 : {
171 923371 : xmlAddChild(root, clone(list, appl));
172 : }
173 934805 : list = list->next;
174 : }
175 : }
176 939275 : return root;
177 : }
178 :
179 8906 : class myparser
180 : {
181 : public:
182 : std::string documentId;
183 : std::string fileName;
184 : std::string title;
185 : HashSet *hidlist;
186 : Hashtable *keywords;
187 : Stringtable *helptexts;
188 : private:
189 : HashSet extendedHelpText;
190 : public:
191 8906 : myparser(const std::string &indocumentId, const std::string &infileName,
192 : const std::string &intitle) : documentId(indocumentId), fileName(infileName),
193 8906 : title(intitle)
194 : {
195 8906 : hidlist = new HashSet;
196 8906 : keywords = new Hashtable;
197 8906 : helptexts = new Stringtable;
198 8906 : }
199 : void traverse( xmlNodePtr parentNode );
200 : private:
201 : std::string module;
202 : std::string dump(xmlNodePtr node);
203 : };
204 :
205 140861 : std::string myparser::dump(xmlNodePtr node)
206 : {
207 140861 : std::string app;
208 140861 : if (node->xmlChildrenNode)
209 : {
210 67497 : xmlNodePtr list = node->xmlChildrenNode;
211 214200 : while (list)
212 : {
213 79206 : app += dump(list);
214 79206 : list = list->next;
215 : }
216 : }
217 140861 : if (xmlNodeIsText(node))
218 : {
219 73176 : xmlChar *pContent = xmlNodeGetContent(node);
220 73176 : app += std::string((const char*)pContent);
221 73176 : xmlFree(pContent);
222 : }
223 140861 : return app;
224 : }
225 :
226 61337 : void trim(std::string& str)
227 : {
228 61337 : std::string::size_type pos = str.find_last_not_of(' ');
229 61337 : if(pos != std::string::npos)
230 : {
231 61161 : str.erase(pos + 1);
232 61161 : pos = str.find_first_not_of(' ');
233 61161 : if(pos != std::string::npos)
234 61161 : str.erase(0, pos);
235 : }
236 : else
237 176 : str.erase(str.begin(), str.end());
238 61337 : }
239 :
240 932098 : void myparser::traverse( xmlNodePtr parentNode )
241 : {
242 : // traverse all nodes that belong to the parent
243 : xmlNodePtr test ;
244 1855290 : for (test = parentNode->xmlChildrenNode; test; test = test->next)
245 : {
246 923192 : if (fileName.empty() && !strcmp((const char*)test->name, "filename"))
247 : {
248 8906 : xmlNodePtr node = test->xmlChildrenNode;
249 8906 : if (xmlNodeIsText(node))
250 : {
251 8906 : xmlChar *pContent = xmlNodeGetContent(node);
252 8906 : fileName = std::string((const char*)pContent);
253 8906 : xmlFree(pContent);
254 : }
255 : }
256 914286 : else if (title.empty() && !strcmp((const char*)test->name, "title"))
257 : {
258 8906 : title = dump(test);
259 8906 : if (title.empty())
260 8 : title = "<notitle>";
261 : }
262 905380 : else if (!strcmp((const char*)test->name, "bookmark"))
263 : {
264 46442 : xmlChar *branchxml = xmlGetProp(test, (const xmlChar*)"branch");
265 46442 : xmlChar *idxml = xmlGetProp(test, (const xmlChar*)"id");
266 46442 : std::string branch((const char*)branchxml);
267 46442 : std::string anchor((const char*)idxml);
268 46442 : xmlFree (branchxml);
269 46442 : xmlFree (idxml);
270 :
271 46442 : std::string hid;
272 :
273 46442 : if (branch.find("hid") == 0)
274 : {
275 41593 : size_t index = branch.find('/');
276 41593 : if (index != std::string::npos)
277 : {
278 41593 : hid = branch.substr(1 + index);
279 : // one shall serve as a documentId
280 41593 : if (documentId.empty())
281 6037 : documentId = hid;
282 41593 : extendedHelpText.push_back(hid);
283 41593 : std::string foo = anchor.empty() ? hid : hid + "#" + anchor;
284 : HCDBG(std::cerr << "hid pushback" << foo << std::endl);
285 41593 : hidlist->push_back( anchor.empty() ? hid : hid + "#" + anchor);
286 : }
287 : else
288 0 : continue;
289 : }
290 4849 : else if (branch.compare("index") == 0)
291 : {
292 4847 : LinkedList ll;
293 :
294 23741 : for (xmlNodePtr nd = test->xmlChildrenNode; nd; nd = nd->next)
295 : {
296 18894 : if (strcmp((const char*)nd->name, "bookmark_value"))
297 0 : continue;
298 :
299 18894 : std::string embedded;
300 18894 : xmlChar *embeddedxml = xmlGetProp(nd, (const xmlChar*)"embedded");
301 18894 : if (embeddedxml)
302 : {
303 0 : embedded = std::string((const char*)embeddedxml);
304 0 : xmlFree (embeddedxml);
305 : std::transform (embedded.begin(), embedded.end(),
306 0 : embedded.begin(), tocharlower);
307 : }
308 :
309 18894 : bool isEmbedded = !embedded.empty() && embedded.compare("true") == 0;
310 18894 : if (isEmbedded)
311 0 : continue;
312 :
313 18894 : std::string keyword = dump(nd);
314 18894 : size_t keywordSem = keyword.find(';');
315 18894 : if (keywordSem != std::string::npos)
316 : {
317 : std::string tmppre =
318 13741 : keyword.substr(0,keywordSem);
319 13741 : trim(tmppre);
320 : std::string tmppos =
321 13741 : keyword.substr(1+keywordSem);
322 13741 : trim(tmppos);
323 13741 : keyword = tmppre + ";" + tmppos;
324 : }
325 18894 : ll.push_back(keyword);
326 18894 : }
327 4847 : if (!ll.empty())
328 4847 : (*keywords)[anchor] = ll;
329 : }
330 2 : else if (branch.compare("contents") == 0)
331 : {
332 : // currently not used
333 46442 : }
334 : }
335 858938 : else if (!strcmp((const char*)test->name, "ahelp"))
336 : {
337 33855 : std::string text = dump(test);
338 33855 : trim(text);
339 33855 : std::string name;
340 :
341 33855 : HashSet::const_iterator aEnd = extendedHelpText.end();
342 75090 : for (HashSet::const_iterator iter = extendedHelpText.begin(); iter != aEnd; ++iter)
343 : {
344 41235 : name = *iter;
345 41235 : (*helptexts)[name] = text;
346 : }
347 33855 : extendedHelpText.clear();
348 : }
349 : // traverse children
350 923192 : traverse(test);
351 : }
352 932098 : }
353 :
354 8906 : bool HelpCompiler::compile( void ) throw( HelpProcessingException )
355 : {
356 : // we now have the jaroutputstream, which will contain the document.
357 : // now determine the document as a dom tree in variable docResolved
358 :
359 8906 : xmlDocPtr docResolvedOrg = getSourceDocument(inputFile);
360 :
361 : // now add path to the document
362 : // resolve the dom
363 :
364 8906 : if (!docResolvedOrg)
365 : {
366 0 : impl_sleep( 3 );
367 0 : docResolvedOrg = getSourceDocument(inputFile);
368 0 : if( !docResolvedOrg )
369 : {
370 0 : std::stringstream aStrStream;
371 0 : aStrStream << "ERROR: file not existing: " << inputFile.native_file_string().c_str() << std::endl;
372 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
373 : }
374 : }
375 :
376 8906 : std::string documentId;
377 8906 : std::string fileName;
378 8906 : std::string title;
379 : // returns a clone of the document with switch-cases resolved
380 8906 : std::string appl = module.substr(1);
381 56382 : for (unsigned int i = 0; i < appl.length(); ++i)
382 : {
383 47476 : appl[i]=toupper(appl[i]);
384 : }
385 8906 : xmlNodePtr docResolved = clone(xmlDocGetRootElement(docResolvedOrg), appl);
386 8906 : myparser aparser(documentId, fileName, title);
387 8906 : aparser.traverse(docResolved);
388 8906 : documentId = aparser.documentId;
389 8906 : fileName = aparser.fileName;
390 8906 : title = aparser.title;
391 :
392 : HCDBG(std::cerr << documentId << " : " << fileName << " : " << title << std::endl);
393 :
394 8906 : xmlDocPtr docResolvedDoc = xmlCopyDoc(docResolvedOrg, false);
395 8906 : xmlDocSetRootElement(docResolvedDoc, docResolved);
396 :
397 8906 : streamTable.dropappl();
398 8906 : streamTable.appl_doc = docResolvedDoc;
399 8906 : streamTable.appl_hidlist = aparser.hidlist;
400 8906 : streamTable.appl_helptexts = aparser.helptexts;
401 8906 : streamTable.appl_keywords = aparser.keywords;
402 :
403 8906 : streamTable.document_id = documentId;
404 8906 : streamTable.document_path = fileName;
405 8906 : streamTable.document_title = title;
406 8906 : std::string actMod = module;
407 :
408 8906 : if ( !bExtensionMode && !fileName.empty())
409 : {
410 8906 : if (fileName.find("/text/") == 0)
411 : {
412 8906 : int len = strlen("/text/");
413 8906 : actMod = fileName.substr(len);
414 8906 : actMod = actMod.substr(0, actMod.find('/'));
415 : }
416 : }
417 8906 : streamTable.document_module = actMod;
418 8906 : xmlFreeDoc(docResolvedOrg);
419 8906 : return true;
420 : }
421 :
422 : namespace fs
423 : {
424 79308 : rtl_TextEncoding getThreadTextEncoding( void )
425 : {
426 : static bool bNeedsInit = true;
427 : static rtl_TextEncoding nThreadTextEncoding;
428 79308 : if( bNeedsInit )
429 : {
430 9 : bNeedsInit = false;
431 9 : nThreadTextEncoding = osl_getThreadTextEncoding();
432 : }
433 79308 : return nThreadTextEncoding;
434 : }
435 :
436 25 : void create_directory(const fs::path indexDirName)
437 : {
438 : HCDBG(
439 : std::cerr << "creating " <<
440 : rtl::OUStringToOString(indexDirName.data, RTL_TEXTENCODING_UTF8).getStr()
441 : << std::endl
442 : );
443 25 : osl::Directory::createPath(indexDirName.data);
444 25 : }
445 :
446 30 : void copy(const fs::path &src, const fs::path &dest)
447 : {
448 30 : osl::File::copy(src.data, dest.data);
449 30 : }
450 : }
451 :
452 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|