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 : #include <helpcompiler/HelpCompiler.hxx>
21 : #include <helpcompiler/HelpLinker.hxx>
22 :
23 : #include <map>
24 :
25 : #include <string.h>
26 : #include <limits.h>
27 :
28 : #include <libxslt/xslt.h>
29 : #include <libxslt/xsltutils.h>
30 : #include <libxslt/functions.h>
31 : #include <libxslt/extensions.h>
32 :
33 : #include <sal/main.h>
34 : #include <sal/types.h>
35 : #include <osl/time.h>
36 : #include <rtl/bootstrap.hxx>
37 :
38 : #include <expat.h>
39 :
40 8 : IndexerPreProcessor::IndexerPreProcessor
41 : ( const std::string& aModuleName, const fs::path& fsIndexBaseDir,
42 : const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet )
43 : : m_aModuleName( aModuleName )
44 8 : , m_fsIndexBaseDir( fsIndexBaseDir )
45 : {
46 8 : m_fsCaptionFilesDirName = fsIndexBaseDir / "caption";
47 8 : fs::create_directory( m_fsCaptionFilesDirName );
48 :
49 8 : m_fsContentFilesDirName = fsIndexBaseDir / "content";
50 8 : fs::create_directory( m_fsContentFilesDirName );
51 :
52 : m_xsltStylesheetPtrCaption = xsltParseStylesheetFile
53 8 : ((const xmlChar *)idxCaptionStylesheet.native_file_string().c_str());
54 : m_xsltStylesheetPtrContent = xsltParseStylesheetFile
55 8 : ((const xmlChar *)idxContentStylesheet.native_file_string().c_str());
56 8 : }
57 :
58 16 : IndexerPreProcessor::~IndexerPreProcessor()
59 : {
60 8 : if( m_xsltStylesheetPtrCaption )
61 8 : xsltFreeStylesheet( m_xsltStylesheetPtrCaption );
62 8 : if( m_xsltStylesheetPtrContent )
63 8 : xsltFreeStylesheet( m_xsltStylesheetPtrContent );
64 8 : }
65 :
66 8906 : std::string getEncodedPath( const std::string& Path )
67 : {
68 8906 : rtl::OString aOStr_Path( Path.c_str() );
69 : rtl::OUString aOUStr_Path( rtl::OStringToOUString
70 8906 : ( aOStr_Path, fs::getThreadTextEncoding() ) );
71 8906 : rtl::OUString aPathURL;
72 8906 : osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL );
73 : rtl::OString aOStr_PathURL( rtl::OUStringToOString
74 8906 : ( aPathURL, fs::getThreadTextEncoding() ) );
75 8906 : std::string aStdStr_PathURL( aOStr_PathURL.getStr() );
76 8906 : return aStdStr_PathURL;
77 : }
78 :
79 8906 : void IndexerPreProcessor::processDocument
80 : ( xmlDocPtr doc, const std::string &EncodedDocPath )
81 : {
82 8906 : std::string aStdStr_EncodedDocPathURL = getEncodedPath( EncodedDocPath );
83 :
84 8906 : if( m_xsltStylesheetPtrCaption )
85 : {
86 8906 : xmlDocPtr resCaption = xsltApplyStylesheet( m_xsltStylesheetPtrCaption, doc, NULL );
87 8906 : xmlNodePtr pResNodeCaption = resCaption->xmlChildrenNode;
88 8906 : if( pResNodeCaption )
89 : {
90 8906 : fs::path fsCaptionPureTextFile_docURL = m_fsCaptionFilesDirName / aStdStr_EncodedDocPathURL;
91 : #ifdef WNT //We need _wfopen to support long file paths on Windows XP
92 : FILE* pFile_docURL = _wfopen(
93 : fsCaptionPureTextFile_docURL.native_file_string_w(), L"w" );
94 : #else
95 : FILE* pFile_docURL = fopen(
96 8906 : fsCaptionPureTextFile_docURL.native_file_string().c_str(), "w" );
97 : #endif
98 8906 : if( pFile_docURL )
99 : {
100 8906 : fprintf( pFile_docURL, "%s\n", pResNodeCaption->content );
101 8906 : fclose( pFile_docURL );
102 8906 : }
103 : }
104 8906 : xmlFreeDoc(resCaption);
105 : }
106 :
107 8906 : if( m_xsltStylesheetPtrContent )
108 : {
109 8906 : xmlDocPtr resContent = xsltApplyStylesheet( m_xsltStylesheetPtrContent, doc, NULL );
110 8906 : xmlNodePtr pResNodeContent = resContent->xmlChildrenNode;
111 8906 : if( pResNodeContent )
112 : {
113 8379 : fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL;
114 : #ifdef WNT //We need _wfopen to support long file paths on Windows XP
115 : FILE* pFile_docURL = _wfopen(
116 : fsContentPureTextFile_docURL.native_file_string_w(), L"w" );
117 : #else
118 : FILE* pFile_docURL = fopen(
119 8379 : fsContentPureTextFile_docURL.native_file_string().c_str(), "w" );
120 : #endif
121 8379 : if( pFile_docURL )
122 : {
123 8379 : fprintf( pFile_docURL, "%s\n", pResNodeContent->content );
124 8379 : fclose( pFile_docURL );
125 8379 : }
126 : }
127 8906 : xmlFreeDoc(resContent);
128 8906 : }
129 8906 : }
130 :
131 37788 : struct Data
132 : {
133 : std::vector<std::string> _idList;
134 : typedef std::vector<std::string>::const_iterator cIter;
135 :
136 18894 : void append(const std::string &id)
137 : {
138 18894 : _idList.push_back(id);
139 18894 : }
140 :
141 18894 : std::string getString() const
142 : {
143 18894 : std::string ret;
144 18894 : cIter aEnd = _idList.end();
145 37788 : for (cIter aIter = _idList.begin(); aIter != aEnd; ++aIter)
146 18894 : ret += *aIter + ";";
147 18894 : return ret;
148 : }
149 : };
150 :
151 111589 : void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr )
152 : {
153 111589 : if( pFile == NULL )
154 111589 : return;
155 111589 : char cLF = 10;
156 111589 : unsigned int nKeyLen = aKeyStr.length();
157 111589 : unsigned int nValueLen = aValueStr.length();
158 111589 : fprintf( pFile, "%x ", nKeyLen );
159 111589 : if( nKeyLen > 0 )
160 : {
161 111589 : if (fwrite( aKeyStr.c_str(), 1, nKeyLen, pFile ) != nKeyLen)
162 0 : fprintf(stderr, "fwrite to db failed\n");
163 : }
164 111589 : if (fprintf( pFile, " %x ", nValueLen ) < 0)
165 0 : fprintf(stderr, "fwrite to db failed\n");
166 111589 : if( nValueLen > 0 )
167 : {
168 111405 : if (fwrite( aValueStr.c_str(), 1, nValueLen, pFile ) != nValueLen)
169 0 : fprintf(stderr, "fwrite to db failed\n");
170 : }
171 111589 : if (fprintf( pFile, "%c", cLF ) < 0)
172 0 : fprintf(stderr, "fwrite to db failed\n");
173 : }
174 :
175 18 : class HelpKeyword
176 : {
177 : private:
178 : typedef boost::unordered_map<std::string, Data, pref_hash> DataHashtable;
179 : DataHashtable _hash;
180 :
181 : public:
182 18894 : void insert(const std::string &key, const std::string &id)
183 : {
184 18894 : Data &data = _hash[key];
185 18894 : data.append(id);
186 18894 : }
187 :
188 9 : void dump_DBHelp( const fs::path& rFileName )
189 : {
190 : #ifdef WNT //We need _wfopen to support long file paths on Windows XP
191 : FILE* pFile = _wfopen( rFileName.native_file_string_w(), L"wb" );
192 : #else
193 9 : FILE* pFile = fopen( rFileName.native_file_string().c_str(), "wb" );
194 : #endif
195 9 : if( pFile == NULL )
196 9 : return;
197 :
198 9 : DataHashtable::const_iterator aEnd = _hash.end();
199 18903 : for (DataHashtable::const_iterator aIter = _hash.begin(); aIter != aEnd; ++aIter)
200 18894 : writeKeyValue_DBHelp( pFile, aIter->first, aIter->second.getString() );
201 :
202 9 : fclose( pFile );
203 : }
204 : };
205 :
206 : namespace URLEncoder
207 : {
208 105799 : static std::string encode(const std::string &rIn)
209 : {
210 105799 : const char *good = "!$&'()*+,-.=@_";
211 : static const char hex[17] = "0123456789ABCDEF";
212 :
213 105799 : std::string result;
214 3647793 : for (size_t i=0; i < rIn.length(); ++i)
215 : {
216 3541994 : unsigned char c = rIn[i];
217 3541994 : if (isalnum (c) || strchr (good, c))
218 3336670 : result += c;
219 : else {
220 205324 : result += '%';
221 205324 : result += hex[c >> 4];
222 205324 : result += hex[c & 0xf];
223 : }
224 : }
225 105799 : return result;
226 : }
227 : }
228 :
229 55346 : void HelpLinker::addBookmark( FILE* pFile_DBHelp, std::string thishid,
230 : const std::string& fileB, const std::string& anchorB,
231 : const std::string& jarfileB, const std::string& titleB)
232 : {
233 : HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " <<
234 : fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl);
235 :
236 55346 : thishid = URLEncoder::encode(thishid);
237 :
238 55346 : int fileLen = fileB.length();
239 55346 : if (!anchorB.empty())
240 46440 : fileLen += (1 + anchorB.length());
241 55346 : int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length();
242 :
243 55346 : std::vector<unsigned char> dataB(dataLen);
244 55346 : size_t i = 0;
245 55346 : dataB[i++] = static_cast<unsigned char>(fileLen);
246 1728373 : for (size_t j = 0; j < fileB.length(); ++j)
247 1673027 : dataB[i++] = static_cast<unsigned char>(fileB[j]);
248 55346 : if (!anchorB.empty())
249 : {
250 46440 : dataB[i++] = '#';
251 614915 : for (size_t j = 0; j < anchorB.length(); ++j)
252 568475 : dataB[i++] = anchorB[j];
253 : }
254 55346 : dataB[i++] = static_cast<unsigned char>(jarfileB.length());
255 612099 : for (size_t j = 0; j < jarfileB.length(); ++j)
256 556753 : dataB[i++] = jarfileB[j];
257 :
258 55346 : dataB[i++] = static_cast<unsigned char>(titleB.length());
259 882195 : for (size_t j = 0; j < titleB.length(); ++j)
260 826849 : dataB[i++] = titleB[j];
261 :
262 55346 : if( pFile_DBHelp != NULL )
263 : {
264 55346 : std::string aValueStr( dataB.begin(), dataB.end() );
265 55346 : writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr );
266 55346 : }
267 55346 : }
268 :
269 8 : void HelpLinker::initIndexerPreProcessor()
270 : {
271 8 : if( m_pIndexerPreProcessor )
272 0 : delete m_pIndexerPreProcessor;
273 8 : std::string mod = module;
274 8 : std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
275 : m_pIndexerPreProcessor = new IndexerPreProcessor( mod, indexDirParentName,
276 8 : idxCaptionStylesheet, idxContentStylesheet );
277 8 : }
278 :
279 : /**
280 : *
281 : */
282 9 : void HelpLinker::link() throw( HelpProcessingException )
283 : {
284 9 : bool bIndexForExtension = true;
285 :
286 9 : if( bExtensionMode )
287 : {
288 0 : indexDirParentName = extensionDestination;
289 : }
290 : else
291 : {
292 9 : indexDirParentName = zipdir;
293 9 : fs::create_directory(indexDirParentName);
294 : }
295 :
296 9 : std::string mod = module;
297 9 : std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
298 :
299 : // do the work here
300 : // continue with introduction of the overall process thing into the
301 : // here all hzip files will be worked on
302 9 : std::string appl = mod;
303 9 : if (appl[0] == 's')
304 9 : appl = appl.substr(1);
305 :
306 9 : bool bUse_ = true;
307 9 : if( !bExtensionMode )
308 9 : bUse_ = false;
309 :
310 9 : fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht")));
311 : #ifdef WNT
312 : //We need _wfopen to support long file paths on Windows XP
313 : FILE* pFileHelpText_DBHelp = _wfopen
314 : ( helpTextFileName_DBHelp.native_file_string_w(), L"wb" );
315 : #else
316 :
317 : FILE* pFileHelpText_DBHelp = fopen
318 9 : ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" );
319 : #endif
320 :
321 9 : fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
322 : #ifdef WNT
323 : //We need _wfopen to support long file paths on Windows XP
324 : FILE* pFileDbBase_DBHelp = _wfopen
325 : ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" );
326 : #else
327 : FILE* pFileDbBase_DBHelp = fopen
328 9 : ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" );
329 : #endif
330 :
331 9 : fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
332 :
333 9 : HelpKeyword helpKeyword;
334 :
335 : // catch HelpProcessingException to avoid locking data bases
336 : try
337 : {
338 :
339 : // lastly, initialize the indexBuilder
340 9 : if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
341 8 : initIndexerPreProcessor();
342 :
343 : // here we start our loop over the hzip files.
344 9 : HashSet::iterator end = helpFiles.end();
345 8915 : for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter)
346 : {
347 : // process one file
348 : // streamTable contains the streams in the hzip file
349 8906 : StreamTable streamTable;
350 8906 : const std::string &xhpFileName = *iter;
351 :
352 8906 : if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
353 : {
354 : // only work on .xhp - files
355 : SAL_WARN("helpcompiler",
356 : "ERROR: input list entry '"
357 : << xhpFileName
358 : << "' has the wrong extension (only files with extension .xhp "
359 : << "are accepted)");
360 :
361 0 : continue;
362 : }
363 :
364 8906 : fs::path langsourceRoot(sourceRoot);
365 8906 : fs::path xhpFile;
366 :
367 8906 : if( bExtensionMode )
368 : {
369 : // langsourceRoot == sourceRoot for extensions
370 0 : std::string xhpFileNameComplete( extensionPath );
371 0 : xhpFileNameComplete.append( '/' + xhpFileName );
372 0 : xhpFile = fs::path( xhpFileNameComplete );
373 : }
374 : else
375 : {
376 8906 : langsourceRoot.append('/' + lang + '/');
377 8906 : xhpFile = fs::path(xhpFileName, fs::native);
378 : }
379 :
380 : HelpCompiler hc( streamTable, xhpFile, langsourceRoot,
381 8906 : embeddStylesheet, module, lang, bExtensionMode );
382 :
383 : HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl);
384 8906 : bool success = hc.compile();
385 : HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl);
386 :
387 8906 : if (!success && !bExtensionMode)
388 : {
389 0 : std::stringstream aStrStream;
390 : aStrStream <<
391 0 : "\nERROR: compiling help particle '"
392 0 : << xhpFileName
393 0 : << "' for language '"
394 0 : << lang
395 0 : << "' failed!";
396 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
397 : }
398 :
399 8906 : std::string documentPath = streamTable.document_path;
400 8906 : if (documentPath.find("/") == 0)
401 8906 : documentPath = documentPath.substr(1);
402 :
403 8906 : std::string documentJarfile = streamTable.document_module + ".jar";
404 :
405 8906 : std::string documentTitle = streamTable.document_title;
406 8906 : if (documentTitle.empty())
407 0 : documentTitle = "<notitle>";
408 :
409 8906 : const std::string& fileB = documentPath;
410 8906 : const std::string& jarfileB = documentJarfile;
411 8906 : std::string& titleB = documentTitle;
412 :
413 : // add once this as its own id.
414 8906 : addBookmark( pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB);
415 :
416 8906 : const HashSet *hidlist = streamTable.appl_hidlist;
417 8906 : if (!hidlist)
418 0 : hidlist = streamTable.default_hidlist;
419 8906 : if (hidlist && !hidlist->empty())
420 : {
421 : // now iterate over all elements of the hidlist
422 6037 : HashSet::const_iterator aEnd = hidlist->end();
423 47630 : for (HashSet::const_iterator hidListIter = hidlist->begin();
424 : hidListIter != aEnd; ++hidListIter)
425 : {
426 41593 : std::string thishid = *hidListIter;
427 :
428 41593 : std::string anchorB;
429 41593 : size_t index = thishid.rfind('#');
430 41593 : if (index != std::string::npos)
431 : {
432 41593 : anchorB = thishid.substr(1 + index);
433 41593 : thishid = thishid.substr(0, index);
434 : }
435 41593 : addBookmark( pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB);
436 41593 : }
437 : }
438 :
439 : // now the keywords
440 8906 : const Hashtable *anchorToLL = streamTable.appl_keywords;
441 8906 : if (!anchorToLL)
442 0 : anchorToLL = streamTable.default_keywords;
443 8906 : if (anchorToLL && !anchorToLL->empty())
444 : {
445 4198 : std::string fakedHid = URLEncoder::encode(documentPath);
446 4198 : Hashtable::const_iterator aEnd = anchorToLL->end();
447 9045 : for (Hashtable::const_iterator enumer = anchorToLL->begin();
448 : enumer != aEnd; ++enumer)
449 : {
450 4847 : const std::string &anchor = enumer->first;
451 : addBookmark(pFileDbBase_DBHelp, documentPath, fileB,
452 4847 : anchor, jarfileB, titleB);
453 4847 : std::string totalId = fakedHid + "#" + anchor;
454 : // std::cerr << hzipFileName << std::endl;
455 4847 : const LinkedList& ll = enumer->second;
456 4847 : LinkedList::const_iterator aOtherEnd = ll.end();
457 23741 : for (LinkedList::const_iterator llIter = ll.begin();
458 : llIter != aOtherEnd; ++llIter)
459 : {
460 18894 : helpKeyword.insert(*llIter, totalId);
461 : }
462 9045 : }
463 :
464 : }
465 :
466 : // and last the helptexts
467 8906 : const Stringtable *helpTextHash = streamTable.appl_helptexts;
468 8906 : if (!helpTextHash)
469 0 : helpTextHash = streamTable.default_helptexts;
470 8906 : if (helpTextHash && !helpTextHash->empty())
471 : {
472 5910 : Stringtable::const_iterator aEnd = helpTextHash->end();
473 43259 : for (Stringtable::const_iterator helpTextIter = helpTextHash->begin();
474 : helpTextIter != aEnd; ++helpTextIter)
475 : {
476 37349 : std::string helpTextId = helpTextIter->first;
477 37349 : const std::string& helpTextText = helpTextIter->second;
478 :
479 37349 : helpTextId = URLEncoder::encode(helpTextId);
480 :
481 37349 : if( pFileHelpText_DBHelp != NULL )
482 37349 : writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText );
483 37349 : }
484 : }
485 :
486 : //IndexerPreProcessor
487 8906 : if( !bExtensionMode || bIndexForExtension )
488 : {
489 : // now the indexing
490 8906 : xmlDocPtr document = streamTable.appl_doc;
491 8906 : if (!document)
492 0 : document = streamTable.default_doc;
493 8906 : if (document)
494 : {
495 8906 : std::string temp = module;
496 8906 : std::transform (temp.begin(), temp.end(), temp.begin(), tocharlower);
497 8906 : m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) );
498 : }
499 : }
500 :
501 8906 : } // while loop over hzip files ending
502 :
503 : } // try
504 0 : catch( const HelpProcessingException& )
505 : {
506 : // catch HelpProcessingException to avoid locking data bases
507 0 : if( pFileHelpText_DBHelp != NULL )
508 0 : fclose( pFileHelpText_DBHelp );
509 0 : if( pFileDbBase_DBHelp != NULL )
510 0 : fclose( pFileDbBase_DBHelp );
511 0 : throw;
512 : }
513 :
514 9 : if( pFileHelpText_DBHelp != NULL )
515 9 : fclose( pFileHelpText_DBHelp );
516 9 : if( pFileDbBase_DBHelp != NULL )
517 9 : fclose( pFileDbBase_DBHelp );
518 :
519 9 : helpKeyword.dump_DBHelp( keyWordFileName_DBHelp);
520 :
521 9 : if( !bExtensionMode )
522 : {
523 : // New index
524 9 : Stringtable::iterator aEnd = additionalFiles.end();
525 39 : for (Stringtable::iterator enumer = additionalFiles.begin(); enumer != aEnd;
526 : ++enumer)
527 : {
528 30 : const std::string &additionalFileName = enumer->second;
529 30 : const std::string &additionalFileKey = enumer->first;
530 :
531 30 : fs::path fsAdditionalFileName( additionalFileName, fs::native );
532 : HCDBG({
533 : std::string aNativeStr = fsAdditionalFileName.native_file_string();
534 : const char* pStr = aNativeStr.c_str();
535 : std::cerr << pStr << std::endl;
536 : });
537 :
538 30 : fs::path fsTargetName( indexDirParentName / additionalFileKey );
539 :
540 30 : fs::copy( fsAdditionalFileName, fsTargetName );
541 30 : }
542 9 : }
543 9 : }
544 :
545 :
546 9 : void HelpLinker::main( std::vector<std::string> &args,
547 : std::string* pExtensionPath, std::string* pDestination,
548 : const rtl::OUString* pOfficeHelpPath )
549 : throw( HelpProcessingException )
550 : {
551 9 : bExtensionMode = false;
552 9 : helpFiles.clear();
553 :
554 9 : if ((!args.empty()) && args[0][0] == '@')
555 : {
556 9 : std::vector<std::string> stringList;
557 9 : std::ifstream fileReader(args[0].substr(1).c_str());
558 :
559 9167 : while (fileReader)
560 : {
561 9149 : std::string token;
562 9149 : fileReader >> token;
563 9149 : if (!token.empty())
564 9140 : stringList.push_back(token);
565 9149 : }
566 9 : fileReader.close();
567 :
568 9 : args = stringList;
569 : }
570 :
571 9 : size_t i = 0;
572 9 : bool bSrcOption = false;
573 9026 : while (i < args.size())
574 : {
575 9008 : if (args[i].compare("-extlangsrc") == 0)
576 : {
577 0 : ++i;
578 0 : if (i >= args.size())
579 : {
580 0 : std::stringstream aStrStream;
581 0 : aStrStream << "extension source missing" << std::endl;
582 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
583 : }
584 0 : extsource = args[i];
585 : }
586 9008 : else if (args[i].compare("-extlangdest") == 0)
587 : {
588 : //If this argument is not provided then the location provided in -extsource will
589 : //also be the destination
590 0 : ++i;
591 0 : if (i >= args.size())
592 : {
593 0 : std::stringstream aStrStream;
594 0 : aStrStream << "extension destination missing" << std::endl;
595 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
596 : }
597 0 : extdestination = args[i];
598 : }
599 9008 : else if (args[i].compare("-src") == 0)
600 : {
601 9 : ++i;
602 9 : if (i >= args.size())
603 : {
604 0 : std::stringstream aStrStream;
605 0 : aStrStream << "sourceroot missing" << std::endl;
606 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
607 : }
608 9 : bSrcOption = true;
609 9 : sourceRoot = fs::path(args[i], fs::native);
610 : }
611 8999 : else if (args[i].compare("-sty") == 0)
612 : {
613 9 : ++i;
614 9 : if (i >= args.size())
615 : {
616 0 : std::stringstream aStrStream;
617 0 : aStrStream << "embeddingStylesheet missing" << std::endl;
618 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
619 : }
620 :
621 9 : embeddStylesheet = fs::path(args[i], fs::native);
622 : }
623 8990 : else if (args[i].compare("-zipdir") == 0)
624 : {
625 9 : ++i;
626 9 : if (i >= args.size())
627 : {
628 0 : std::stringstream aStrStream;
629 0 : aStrStream << "idxtemp missing" << std::endl;
630 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
631 : }
632 :
633 9 : zipdir = fs::path(args[i], fs::native);
634 : }
635 8981 : else if (args[i].compare("-idxcaption") == 0)
636 : {
637 9 : ++i;
638 9 : if (i >= args.size())
639 : {
640 0 : std::stringstream aStrStream;
641 0 : aStrStream << "idxcaption stylesheet missing" << std::endl;
642 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
643 : }
644 :
645 9 : idxCaptionStylesheet = fs::path(args[i], fs::native);
646 : }
647 8972 : else if (args[i].compare("-idxcontent") == 0)
648 : {
649 9 : ++i;
650 9 : if (i >= args.size())
651 : {
652 0 : std::stringstream aStrStream;
653 0 : aStrStream << "idxcontent stylesheet missing" << std::endl;
654 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
655 : }
656 :
657 9 : idxContentStylesheet = fs::path(args[i], fs::native);
658 : }
659 8963 : else if (args[i].compare("-o") == 0)
660 : {
661 9 : ++i;
662 9 : if (i >= args.size())
663 : {
664 0 : std::stringstream aStrStream;
665 0 : aStrStream << "outputfilename missing" << std::endl;
666 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
667 : }
668 :
669 9 : outputFile = fs::path(args[i], fs::native);
670 : }
671 8954 : else if (args[i].compare("-mod") == 0)
672 : {
673 9 : ++i;
674 9 : if (i >= args.size())
675 : {
676 0 : std::stringstream aStrStream;
677 0 : aStrStream << "module name missing" << std::endl;
678 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
679 : }
680 :
681 9 : module = args[i];
682 : }
683 8945 : else if (args[i].compare("-lang") == 0)
684 : {
685 9 : ++i;
686 9 : if (i >= args.size())
687 : {
688 0 : std::stringstream aStrStream;
689 0 : aStrStream << "language name missing" << std::endl;
690 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
691 : }
692 :
693 9 : lang = args[i];
694 : }
695 8936 : else if (args[i].compare("-hid") == 0)
696 : {
697 0 : ++i;
698 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, "obsolete -hid argument used" );
699 : }
700 8936 : else if (args[i].compare("-add") == 0)
701 : {
702 30 : std::string addFile, addFileUnderPath;
703 30 : ++i;
704 30 : if (i >= args.size())
705 : {
706 0 : std::stringstream aStrStream;
707 0 : aStrStream << "pathname missing" << std::endl;
708 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
709 : }
710 :
711 30 : addFileUnderPath = args[i];
712 30 : ++i;
713 30 : if (i >= args.size())
714 : {
715 0 : std::stringstream aStrStream;
716 0 : aStrStream << "pathname missing" << std::endl;
717 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
718 : }
719 30 : addFile = args[i];
720 30 : if (!addFileUnderPath.empty() && !addFile.empty())
721 30 : additionalFiles[addFileUnderPath] = addFile;
722 : }
723 : else
724 8906 : helpFiles.push_back(args[i]);
725 9008 : ++i;
726 : }
727 :
728 : //We can be called from the helplinker executable or the extension manager
729 : //In the latter case extsource is not used.
730 18 : if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath)
731 9 : || !extsource.empty())
732 : {
733 0 : bExtensionMode = true;
734 0 : if (!extsource.empty())
735 : {
736 : //called from helplinker.exe, pExtensionPath and pOfficeHelpPath
737 : //should be NULL
738 0 : sourceRoot = fs::path(extsource, fs::native);
739 0 : extensionPath = sourceRoot.toUTF8();
740 :
741 0 : if (extdestination.empty())
742 : {
743 0 : std::stringstream aStrStream;
744 0 : aStrStream << "-extlangdest is missing" << std::endl;
745 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
746 : }
747 : else
748 : {
749 : //Convert from system path to file URL!!!
750 0 : fs::path p(extdestination, fs::native);
751 0 : extensionDestination = p.toUTF8();
752 : }
753 : }
754 : else
755 : { //called from extension manager
756 0 : extensionPath = *pExtensionPath;
757 0 : sourceRoot = fs::path(extensionPath);
758 0 : extensionDestination = *pDestination;
759 : }
760 : //check if -src option was used. This option must not be used
761 : //when extension help is compiled.
762 0 : if (bSrcOption)
763 : {
764 0 : std::stringstream aStrStream;
765 0 : aStrStream << "-src must not be used together with -extsource missing" << std::endl;
766 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
767 : }
768 : }
769 :
770 9 : if (!bExtensionMode && zipdir.empty())
771 : {
772 0 : std::stringstream aStrStream;
773 0 : aStrStream << "no index dir given" << std::endl;
774 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
775 : }
776 :
777 18 : if ( (!bExtensionMode && idxCaptionStylesheet.empty())
778 9 : || (!extsource.empty() && idxCaptionStylesheet.empty()) )
779 : {
780 : //No extension mode and extension mode using commandline
781 : //!extsource.empty indicates extension mode using commandline
782 : // -idxcaption paramter is required
783 0 : std::stringstream aStrStream;
784 0 : aStrStream << "no index caption stylesheet given" << std::endl;
785 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
786 : }
787 9 : else if ( bExtensionMode && extsource.empty())
788 : {
789 : //This part is used when compileExtensionHelp is called from the extensions manager.
790 : //If extension help is compiled using helplinker in the build process
791 0 : rtl::OUString aIdxCaptionPathFileURL( *pOfficeHelpPath );
792 0 : aIdxCaptionPathFileURL += rtl::OUString("/idxcaption.xsl");
793 :
794 : rtl::OString aOStr_IdxCaptionPathFileURL( rtl::OUStringToOString
795 0 : ( aIdxCaptionPathFileURL, fs::getThreadTextEncoding() ) );
796 0 : std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() );
797 :
798 0 : idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL );
799 : }
800 :
801 18 : if ( (!bExtensionMode && idxContentStylesheet.empty())
802 9 : || (!extsource.empty() && idxContentStylesheet.empty()) )
803 : {
804 : //No extension mode and extension mode using commandline
805 : //!extsource.empty indicates extension mode using commandline
806 : // -idxcontent paramter is required
807 0 : std::stringstream aStrStream;
808 0 : aStrStream << "no index content stylesheet given" << std::endl;
809 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
810 : }
811 9 : else if ( bExtensionMode && extsource.empty())
812 : {
813 : //If extension help is compiled using helplinker in the build process
814 : //then -idxcontent must be supplied
815 : //This part is used when compileExtensionHelp is called from the extensions manager.
816 0 : rtl::OUString aIdxContentPathFileURL( *pOfficeHelpPath );
817 0 : aIdxContentPathFileURL += rtl::OUString("/idxcontent.xsl");
818 :
819 : rtl::OString aOStr_IdxContentPathFileURL( rtl::OUStringToOString
820 0 : ( aIdxContentPathFileURL, fs::getThreadTextEncoding() ) );
821 0 : std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() );
822 :
823 0 : idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL );
824 : }
825 9 : if (!bExtensionMode && embeddStylesheet.empty())
826 : {
827 0 : std::stringstream aStrStream;
828 0 : aStrStream << "no embedding resolving file given" << std::endl;
829 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
830 : }
831 9 : if (sourceRoot.empty())
832 : {
833 0 : std::stringstream aStrStream;
834 0 : aStrStream << "no sourceroot given" << std::endl;
835 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
836 : }
837 9 : if (!bExtensionMode && outputFile.empty())
838 : {
839 0 : std::stringstream aStrStream;
840 0 : aStrStream << "no output file given" << std::endl;
841 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
842 : }
843 9 : if (module.empty())
844 : {
845 0 : std::stringstream aStrStream;
846 0 : aStrStream << "module missing" << std::endl;
847 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
848 : }
849 9 : if (!bExtensionMode && lang.empty())
850 : {
851 0 : std::stringstream aStrStream;
852 0 : aStrStream << "language missing" << std::endl;
853 0 : throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
854 : }
855 9 : link();
856 9 : }
857 :
858 : // Variable to set an exception in "C" StructuredXMLErrorFunction
859 : static const HelpProcessingException* GpXMLParsingException = NULL;
860 :
861 0 : extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
862 : {
863 : (void)userData;
864 : (void)error;
865 :
866 0 : std::string aErrorMsg = error->message;
867 0 : std::string aXMLParsingFile;
868 0 : if( error->file != NULL )
869 0 : aXMLParsingFile = error->file;
870 0 : int nXMLParsingLine = error->line;
871 0 : HelpProcessingException* pException = new HelpProcessingException( aErrorMsg, aXMLParsingFile, nXMLParsingLine );
872 0 : GpXMLParsingException = pException;
873 :
874 : // Reset error handler
875 0 : xmlSetStructuredErrorFunc( NULL, NULL );
876 0 : }
877 :
878 0 : HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e )
879 : {
880 0 : m_eErrorClass = e.m_eErrorClass;
881 0 : rtl::OString tmpErrorMsg( e.m_aErrorMsg.c_str() );
882 0 : m_aErrorMsg = rtl::OStringToOUString( tmpErrorMsg, fs::getThreadTextEncoding() );
883 0 : rtl::OString tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() );
884 0 : m_aXMLParsingFile = rtl::OStringToOUString( tmpXMLParsingFile, fs::getThreadTextEncoding() );
885 0 : m_nXMLParsingLine = e.m_nXMLParsingLine;
886 0 : return *this;
887 : }
888 :
889 :
890 : // Returns true in case of success, false in case of error
891 0 : HELPLINKER_DLLPUBLIC bool compileExtensionHelp
892 : (
893 : const rtl::OUString& aOfficeHelpPath,
894 : const rtl::OUString& aExtensionName,
895 : const rtl::OUString& aExtensionLanguageRoot,
896 : sal_Int32 nXhpFileCount, const rtl::OUString* pXhpFiles,
897 : const rtl::OUString& aDestination,
898 : HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo
899 : )
900 : {
901 0 : bool bSuccess = true;
902 :
903 0 : std::vector<std::string> args;
904 0 : args.reserve(nXhpFileCount + 2);
905 0 : args.push_back(std::string("-mod"));
906 0 : rtl::OString aOExtensionName = rtl::OUStringToOString( aExtensionName, fs::getThreadTextEncoding() );
907 0 : args.push_back(std::string(aOExtensionName.getStr()));
908 :
909 0 : for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
910 : {
911 0 : rtl::OUString aXhpFile = pXhpFiles[iXhp];
912 :
913 0 : rtl::OString aOXhpFile = rtl::OUStringToOString( aXhpFile, fs::getThreadTextEncoding() );
914 0 : args.push_back(std::string(aOXhpFile.getStr()));
915 0 : }
916 :
917 0 : rtl::OString aOExtensionLanguageRoot = rtl::OUStringToOString( aExtensionLanguageRoot, fs::getThreadTextEncoding() );
918 0 : const char* pExtensionPath = aOExtensionLanguageRoot.getStr();
919 0 : std::string aStdStrExtensionPath = pExtensionPath;
920 0 : rtl::OString aODestination = rtl::OUStringToOString(aDestination, fs::getThreadTextEncoding());
921 0 : const char* pDestination = aODestination.getStr();
922 0 : std::string aStdStrDestination = pDestination;
923 :
924 : // Set error handler
925 0 : xmlSetStructuredErrorFunc( NULL, (xmlStructuredErrorFunc)StructuredXMLErrorFunction );
926 : try
927 : {
928 0 : HelpLinker* pHelpLinker = new HelpLinker();
929 0 : pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
930 0 : delete pHelpLinker;
931 : }
932 0 : catch( const HelpProcessingException& e )
933 : {
934 0 : if( GpXMLParsingException != NULL )
935 : {
936 0 : o_rHelpProcessingErrorInfo = *GpXMLParsingException;
937 0 : delete GpXMLParsingException;
938 0 : GpXMLParsingException = NULL;
939 : }
940 : else
941 : {
942 0 : o_rHelpProcessingErrorInfo = e;
943 : }
944 0 : bSuccess = false;
945 : }
946 : // Reset error handler
947 0 : xmlSetStructuredErrorFunc( NULL, NULL );
948 :
949 : // i83624: Tree files
950 0 : ::rtl::OUString aTreeFileURL = aExtensionLanguageRoot;
951 0 : aTreeFileURL += rtl::OUString("/help.tree");
952 0 : osl::DirectoryItem aTreeFileItem;
953 0 : osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
954 0 : osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
955 0 : if( rcGet == osl::FileBase::E_None &&
956 0 : aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
957 0 : aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) )
958 : {
959 0 : sal_uInt64 ret, len = aFileStatus.getFileSize();
960 0 : char* s = new char[ int(len) ]; // the buffer to hold the installed files
961 0 : osl::File aFile( aTreeFileURL );
962 0 : aFile.open( osl_File_OpenFlag_Read );
963 0 : aFile.read( s, len, ret );
964 0 : aFile.close();
965 :
966 0 : XML_Parser parser = XML_ParserCreate( 0 );
967 0 : XML_Status parsed = XML_Parse( parser, s, int( len ), true );
968 :
969 0 : if (XML_STATUS_ERROR == parsed)
970 : {
971 0 : XML_Error nError = XML_GetErrorCode( parser );
972 0 : o_rHelpProcessingErrorInfo.m_eErrorClass = HELPPROCESSING_XMLPARSING_ERROR;
973 0 : o_rHelpProcessingErrorInfo.m_aErrorMsg = rtl::OUString::createFromAscii( XML_ErrorString( nError ) );;
974 0 : o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL;
975 : // CRAHSES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser );
976 0 : bSuccess = false;
977 : }
978 :
979 0 : XML_ParserFree( parser );
980 0 : delete[] s;
981 : }
982 :
983 0 : return bSuccess;
984 : }
985 :
986 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|