Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / helpcompiler / source / HelpLinker.cxx
blobdcf195b77d86b9159683874d74138827a1cd7f28
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <HelpCompiler.hxx>
21 #include <HelpLinker.hxx>
23 #include <map>
25 #include <string.h>
26 #include <limits.h>
28 #include <libxslt/xslt.h>
29 #include <libxslt/xsltutils.h>
30 #include <libxslt/functions.h>
31 #include <libxslt/extensions.h>
33 #include <sal/main.h>
34 #include <sal/types.h>
35 #include <osl/time.h>
36 #include <rtl/bootstrap.hxx>
38 #include <expat.h>
39 #include <memory>
41 IndexerPreProcessor::IndexerPreProcessor
42 ( const fs::path& fsIndexBaseDir,
43 const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet )
45 m_fsCaptionFilesDirName = fsIndexBaseDir / "caption";
46 fs::create_directory( m_fsCaptionFilesDirName );
48 m_fsContentFilesDirName = fsIndexBaseDir / "content";
49 fs::create_directory( m_fsContentFilesDirName );
51 m_xsltStylesheetPtrCaption = xsltParseStylesheetFile
52 (reinterpret_cast<const xmlChar *>(idxCaptionStylesheet.native_file_string().c_str()));
53 m_xsltStylesheetPtrContent = xsltParseStylesheetFile
54 (reinterpret_cast<const xmlChar *>(idxContentStylesheet.native_file_string().c_str()));
57 IndexerPreProcessor::~IndexerPreProcessor()
59 if( m_xsltStylesheetPtrCaption )
60 xsltFreeStylesheet( m_xsltStylesheetPtrCaption );
61 if( m_xsltStylesheetPtrContent )
62 xsltFreeStylesheet( m_xsltStylesheetPtrContent );
65 std::string getEncodedPath( const std::string& Path )
67 OString aOStr_Path( Path.c_str() );
68 OUString aOUStr_Path( OStringToOUString
69 ( aOStr_Path, fs::getThreadTextEncoding() ) );
70 OUString aPathURL;
71 osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL );
72 OString aOStr_PathURL( OUStringToOString
73 ( aPathURL, fs::getThreadTextEncoding() ) );
74 std::string aStdStr_PathURL( aOStr_PathURL.getStr() );
75 return aStdStr_PathURL;
78 void IndexerPreProcessor::processDocument
79 ( xmlDocPtr doc, const std::string &EncodedDocPath )
81 std::string aStdStr_EncodedDocPathURL = getEncodedPath( EncodedDocPath );
83 if( m_xsltStylesheetPtrCaption )
85 xmlDocPtr resCaption = xsltApplyStylesheet( m_xsltStylesheetPtrCaption, doc, nullptr );
86 xmlNodePtr pResNodeCaption = resCaption->xmlChildrenNode;
87 if( pResNodeCaption )
89 fs::path fsCaptionPureTextFile_docURL = m_fsCaptionFilesDirName / aStdStr_EncodedDocPathURL;
90 #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP
91 FILE* pFile_docURL = _wfopen(
92 fsCaptionPureTextFile_docURL.native_file_string_w(), L"w" );
93 #else
94 FILE* pFile_docURL = fopen(
95 fsCaptionPureTextFile_docURL.native_file_string().c_str(), "w" );
96 #endif
97 if( pFile_docURL )
99 fprintf( pFile_docURL, "%s\n", pResNodeCaption->content );
100 fclose( pFile_docURL );
103 xmlFreeDoc(resCaption);
106 if( m_xsltStylesheetPtrContent )
108 xmlDocPtr resContent = xsltApplyStylesheet( m_xsltStylesheetPtrContent, doc, nullptr );
109 xmlNodePtr pResNodeContent = resContent->xmlChildrenNode;
110 if( pResNodeContent )
112 fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL;
113 #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP
114 FILE* pFile_docURL = _wfopen(
115 fsContentPureTextFile_docURL.native_file_string_w(), L"w" );
116 #else
117 FILE* pFile_docURL = fopen(
118 fsContentPureTextFile_docURL.native_file_string().c_str(), "w" );
119 #endif
120 if( pFile_docURL )
122 fprintf( pFile_docURL, "%s\n", pResNodeContent->content );
123 fclose( pFile_docURL );
126 xmlFreeDoc(resContent);
130 struct Data
132 std::vector<std::string> _idList;
133 typedef std::vector<std::string>::const_iterator cIter;
135 void append(const std::string &id)
137 _idList.push_back(id);
140 std::string getString() const
142 std::string ret;
143 cIter aEnd = _idList.end();
144 for (cIter aIter = _idList.begin(); aIter != aEnd; ++aIter)
145 ret += *aIter + ";";
146 return ret;
150 void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr )
152 if( pFile == nullptr )
153 return;
154 char cLF = 10;
155 unsigned int nKeyLen = aKeyStr.length();
156 unsigned int nValueLen = aValueStr.length();
157 fprintf( pFile, "%x ", nKeyLen );
158 if( nKeyLen > 0 )
160 if (fwrite( aKeyStr.c_str(), 1, nKeyLen, pFile ) != nKeyLen)
161 fprintf(stderr, "fwrite to db failed\n");
163 if (fprintf( pFile, " %x ", nValueLen ) < 0)
164 fprintf(stderr, "fwrite to db failed\n");
165 if( nValueLen > 0 )
167 if (fwrite( aValueStr.c_str(), 1, nValueLen, pFile ) != nValueLen)
168 fprintf(stderr, "fwrite to db failed\n");
170 if (fprintf( pFile, "%c", cLF ) < 0)
171 fprintf(stderr, "fwrite to db failed\n");
174 class HelpKeyword
176 private:
177 typedef std::unordered_map<std::string, Data, pref_hash> DataHashtable;
178 DataHashtable _hash;
180 public:
181 void insert(const std::string &key, const std::string &id)
183 Data &data = _hash[key];
184 data.append(id);
187 void dump_DBHelp( const fs::path& rFileName )
189 #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP
190 FILE* pFile = _wfopen( rFileName.native_file_string_w(), L"wb" );
191 #else
192 FILE* pFile = fopen( rFileName.native_file_string().c_str(), "wb" );
193 #endif
194 if( pFile == nullptr )
195 return;
197 DataHashtable::const_iterator aEnd = _hash.end();
198 for (DataHashtable::const_iterator aIter = _hash.begin(); aIter != aEnd; ++aIter)
199 writeKeyValue_DBHelp( pFile, aIter->first, aIter->second.getString() );
201 fclose( pFile );
205 namespace URLEncoder
207 static std::string encode(const std::string &rIn)
209 const char *good = "!$&'()*+,-.=@_";
210 static const char hex[17] = "0123456789ABCDEF";
212 std::string result;
213 for (char c : rIn)
215 if (isalnum (c) || strchr (good, c))
216 result += c;
217 else {
218 result += '%';
219 result += hex[c >> 4];
220 result += hex[c & 0xf];
223 return result;
227 void HelpLinker::addBookmark( FILE* pFile_DBHelp, std::string thishid,
228 const std::string& fileB, const std::string& anchorB,
229 const std::string& jarfileB, const std::string& titleB)
231 HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " <<
232 fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl);
234 thishid = URLEncoder::encode(thishid);
236 int fileLen = fileB.length();
237 if (!anchorB.empty())
238 fileLen += (1 + anchorB.length());
239 int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length();
241 std::vector<unsigned char> dataB(dataLen);
242 size_t i = 0;
243 dataB[i++] = static_cast<unsigned char>(fileLen);
244 for (char j : fileB)
245 dataB[i++] = static_cast<unsigned char>(j);
246 if (!anchorB.empty())
248 dataB[i++] = '#';
249 for (char j : anchorB)
250 dataB[i++] = j;
252 dataB[i++] = static_cast<unsigned char>(jarfileB.length());
253 for (char j : jarfileB)
254 dataB[i++] = j;
256 dataB[i++] = static_cast<unsigned char>(titleB.length());
257 for (char j : titleB)
258 dataB[i++] = j;
260 if( pFile_DBHelp != nullptr )
262 std::string aValueStr( dataB.begin(), dataB.end() );
263 writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr );
267 void HelpLinker::initIndexerPreProcessor()
269 delete m_pIndexerPreProcessor;
270 m_pIndexerPreProcessor = new IndexerPreProcessor( indexDirParentName,
271 idxCaptionStylesheet, idxContentStylesheet );
277 void HelpLinker::link() throw(HelpProcessingException, BasicCodeTagger::TaggerException, std::exception)
280 if( bExtensionMode )
282 indexDirParentName = extensionDestination;
284 else
286 indexDirParentName = zipdir;
287 fs::create_directory(indexDirParentName);
290 std::string mod = module;
291 std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
293 // do the work here
294 // continue with introduction of the overall process thing into the
295 // here all hzip files will be worked on
296 std::string appl = mod;
297 if (appl[0] == 's')
298 appl = appl.substr(1);
300 bool bUse_ = true;
301 if( !bExtensionMode )
302 bUse_ = false;
304 fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht")));
305 #ifdef _WIN32
306 //We need _wfopen to support long file paths on Windows XP
307 FILE* pFileHelpText_DBHelp = _wfopen
308 ( helpTextFileName_DBHelp.native_file_string_w(), L"wb" );
309 #else
311 FILE* pFileHelpText_DBHelp = fopen
312 ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" );
313 #endif
315 fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
316 #ifdef _WIN32
317 //We need _wfopen to support long file paths on Windows XP
318 FILE* pFileDbBase_DBHelp = _wfopen
319 ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" );
320 #else
321 FILE* pFileDbBase_DBHelp = fopen
322 ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" );
323 #endif
325 fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
327 HelpKeyword helpKeyword;
329 // catch HelpProcessingException to avoid locking data bases
332 bool bIndexForExtension = true;
333 // lastly, initialize the indexBuilder
334 if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
335 initIndexerPreProcessor();
337 // here we start our loop over the hzip files.
338 HashSet::iterator end = helpFiles.end();
339 for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter)
341 // process one file
342 // streamTable contains the streams in the hzip file
343 StreamTable streamTable;
344 const std::string &xhpFileName = *iter;
346 if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
348 // only work on .xhp - files
349 SAL_WARN("helpcompiler",
350 "ERROR: input list entry '"
351 << xhpFileName
352 << "' has the wrong extension (only files with extension .xhp are accepted)");
354 continue;
357 fs::path langsourceRoot(sourceRoot);
358 fs::path xhpFile;
360 if( bExtensionMode )
362 // langsourceRoot == sourceRoot for extensions
363 std::string xhpFileNameComplete( extensionPath );
364 xhpFileNameComplete.append( '/' + xhpFileName );
365 xhpFile = fs::path( xhpFileNameComplete );
367 else
369 langsourceRoot.append( "/" );
370 if ( m_bUseLangRoot )
371 langsourceRoot.append( lang + '/' );
372 xhpFile = fs::path(xhpFileName, fs::native);
375 HelpCompiler hc( streamTable, xhpFile, langsourceRoot, zipdir,
376 compactStylesheet, embeddStylesheet, module, lang, bExtensionMode );
378 HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl);
379 bool success = hc.compile();
380 HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl);
382 if (!success && !bExtensionMode)
384 std::stringstream aStrStream;
385 aStrStream <<
386 "\nERROR: compiling help particle '"
387 << xhpFileName
388 << "' for language '"
389 << lang
390 << "' failed!";
391 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
394 if (!m_bCreateIndex)
395 continue;
397 std::string documentPath = streamTable.document_path;
398 if (documentPath.compare(0, 1, "/") == 0)
399 documentPath = documentPath.substr(1);
401 std::string documentJarfile = streamTable.document_module + ".jar";
403 std::string documentTitle = streamTable.document_title;
404 if (documentTitle.empty())
405 documentTitle = "<notitle>";
407 const std::string& fileB = documentPath;
408 const std::string& jarfileB = documentJarfile;
409 std::string& titleB = documentTitle;
411 // add once this as its own id.
412 addBookmark( pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB);
414 const HashSet *hidlist = streamTable.appl_hidlist;
415 if (!hidlist)
416 hidlist = streamTable.default_hidlist;
417 if (hidlist && !hidlist->empty())
419 // now iterate over all elements of the hidlist
420 HashSet::const_iterator aEnd = hidlist->end();
421 for (HashSet::const_iterator hidListIter = hidlist->begin();
422 hidListIter != aEnd; ++hidListIter)
424 std::string thishid = *hidListIter;
426 std::string anchorB;
427 size_t index = thishid.rfind('#');
428 if (index != std::string::npos)
430 anchorB = thishid.substr(1 + index);
431 thishid = thishid.substr(0, index);
433 addBookmark( pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB);
437 // now the keywords
438 const Hashtable *anchorToLL = streamTable.appl_keywords;
439 if (!anchorToLL)
440 anchorToLL = streamTable.default_keywords;
441 if (anchorToLL && !anchorToLL->empty())
443 std::string fakedHid = URLEncoder::encode(documentPath);
444 Hashtable::const_iterator aEnd = anchorToLL->end();
445 for (Hashtable::const_iterator enumer = anchorToLL->begin();
446 enumer != aEnd; ++enumer)
448 const std::string &anchor = enumer->first;
449 addBookmark(pFileDbBase_DBHelp, documentPath, fileB,
450 anchor, jarfileB, titleB);
451 std::string totalId = fakedHid + "#" + anchor;
452 // std::cerr << hzipFileName << std::endl;
453 const LinkedList& ll = enumer->second;
454 LinkedList::const_iterator aOtherEnd = ll.end();
455 for (LinkedList::const_iterator llIter = ll.begin();
456 llIter != aOtherEnd; ++llIter)
458 helpKeyword.insert(*llIter, totalId);
464 // and last the helptexts
465 const Stringtable *helpTextHash = streamTable.appl_helptexts;
466 if (!helpTextHash)
467 helpTextHash = streamTable.default_helptexts;
468 if (helpTextHash && !helpTextHash->empty())
470 Stringtable::const_iterator aEnd = helpTextHash->end();
471 for (Stringtable::const_iterator helpTextIter = helpTextHash->begin();
472 helpTextIter != aEnd; ++helpTextIter)
474 std::string helpTextId = helpTextIter->first;
475 const std::string& helpTextText = helpTextIter->second;
477 helpTextId = URLEncoder::encode(helpTextId);
479 if( pFileHelpText_DBHelp != nullptr )
480 writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText );
484 //IndexerPreProcessor
485 if( !bExtensionMode || bIndexForExtension )
487 // now the indexing
488 xmlDocPtr document = streamTable.appl_doc;
489 if (!document)
490 document = streamTable.default_doc;
491 if (document)
493 std::string temp = module;
494 std::transform (temp.begin(), temp.end(), temp.begin(), tocharlower);
495 m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) );
502 catch( const HelpProcessingException& )
504 // catch HelpProcessingException to avoid locking data bases
505 if( pFileHelpText_DBHelp != nullptr )
506 fclose( pFileHelpText_DBHelp );
507 if( pFileDbBase_DBHelp != nullptr )
508 fclose( pFileDbBase_DBHelp );
509 throw;
512 if( pFileHelpText_DBHelp != nullptr )
513 fclose( pFileHelpText_DBHelp );
514 if( pFileDbBase_DBHelp != nullptr )
515 fclose( pFileDbBase_DBHelp );
517 helpKeyword.dump_DBHelp( keyWordFileName_DBHelp);
519 if( !bExtensionMode )
521 // New index
522 Stringtable::iterator aEnd = additionalFiles.end();
523 for (Stringtable::iterator enumer = additionalFiles.begin(); enumer != aEnd;
524 ++enumer)
526 const std::string &additionalFileName = enumer->second;
527 const std::string &additionalFileKey = enumer->first;
529 fs::path fsAdditionalFileName( additionalFileName, fs::native );
530 HCDBG({
531 std::string aNativeStr = fsAdditionalFileName.native_file_string();
532 const char* pStr = aNativeStr.c_str();
533 std::cerr << pStr << std::endl;
536 fs::path fsTargetName( indexDirParentName / additionalFileKey );
538 fs::copy( fsAdditionalFileName, fsTargetName );
544 void HelpLinker::main( std::vector<std::string> &args,
545 std::string* pExtensionPath, std::string* pDestination,
546 const OUString* pOfficeHelpPath )
547 throw( HelpProcessingException, std::exception )
549 bExtensionMode = false;
550 helpFiles.clear();
552 if ((!args.empty()) && args[0][0] == '@')
554 std::vector<std::string> stringList;
555 std::ifstream fileReader(args[0].substr(1).c_str());
557 while (fileReader)
559 std::string token;
560 fileReader >> token;
561 if (!token.empty())
562 stringList.push_back(token);
564 fileReader.close();
566 args = stringList;
569 size_t i = 0;
570 bool bSrcOption = false;
571 while (i < args.size())
573 if (args[i].compare("-extlangsrc") == 0)
575 ++i;
576 if (i >= args.size())
578 std::stringstream aStrStream;
579 aStrStream << "extension source missing" << std::endl;
580 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
582 extsource = args[i];
584 else if (args[i].compare("-extlangdest") == 0)
586 //If this argument is not provided then the location provided in -extsource will
587 //also be the destination
588 ++i;
589 if (i >= args.size())
591 std::stringstream aStrStream;
592 aStrStream << "extension destination missing" << std::endl;
593 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
595 extdestination = args[i];
597 else if (args[i].compare("-src") == 0)
599 ++i;
600 if (i >= args.size())
602 std::stringstream aStrStream;
603 aStrStream << "sourceroot missing" << std::endl;
604 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
606 bSrcOption = true;
607 sourceRoot = fs::path(args[i], fs::native);
609 else if (args[i].compare("-compact") == 0)
611 ++i;
612 if (i >= args.size())
614 std::stringstream aStrStream;
615 aStrStream << "compactStylesheet missing" << std::endl;
616 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
619 compactStylesheet = fs::path(args[i], fs::native);
621 else if (args[i].compare("-sty") == 0)
623 ++i;
624 if (i >= args.size())
626 std::stringstream aStrStream;
627 aStrStream << "embeddingStylesheet missing" << std::endl;
628 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
631 embeddStylesheet = fs::path(args[i], fs::native);
633 else if (args[i].compare("-zipdir") == 0)
635 ++i;
636 if (i >= args.size())
638 std::stringstream aStrStream;
639 aStrStream << "idxtemp missing" << std::endl;
640 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
643 zipdir = fs::path(args[i], fs::native);
645 else if (args[i].compare("-idxcaption") == 0)
647 ++i;
648 if (i >= args.size())
650 std::stringstream aStrStream;
651 aStrStream << "idxcaption stylesheet missing" << std::endl;
652 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
655 idxCaptionStylesheet = fs::path(args[i], fs::native);
657 else if (args[i].compare("-idxcontent") == 0)
659 ++i;
660 if (i >= args.size())
662 std::stringstream aStrStream;
663 aStrStream << "idxcontent stylesheet missing" << std::endl;
664 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
667 idxContentStylesheet = fs::path(args[i], fs::native);
669 else if (args[i].compare("-o") == 0)
671 ++i;
672 if (i >= args.size())
674 std::stringstream aStrStream;
675 aStrStream << "outputfilename missing" << std::endl;
676 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
679 outputFile = fs::path(args[i], fs::native);
681 else if (args[i].compare("-mod") == 0)
683 ++i;
684 if (i >= args.size())
686 std::stringstream aStrStream;
687 aStrStream << "module name missing" << std::endl;
688 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
691 module = args[i];
693 else if (args[i].compare("-lang") == 0)
695 ++i;
696 if (i >= args.size())
698 std::stringstream aStrStream;
699 aStrStream << "language name missing" << std::endl;
700 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
703 lang = args[i];
705 else if (args[i].compare("-hid") == 0)
707 ++i;
708 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, "obsolete -hid argument used" );
710 else if (args[i].compare("-add") == 0)
712 std::string addFile, addFileUnderPath;
713 ++i;
714 if (i >= args.size())
716 std::stringstream aStrStream;
717 aStrStream << "pathname missing" << std::endl;
718 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
721 addFileUnderPath = args[i];
722 ++i;
723 if (i >= args.size())
725 std::stringstream aStrStream;
726 aStrStream << "pathname missing" << std::endl;
727 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
729 addFile = args[i];
730 if (!addFileUnderPath.empty() && !addFile.empty())
731 additionalFiles[addFileUnderPath] = addFile;
733 else if (args[i].compare("-nolangroot") == 0)
734 m_bUseLangRoot = false;
735 else if (args[i].compare("-noindex") == 0)
736 m_bCreateIndex = false;
737 else
738 helpFiles.push_back(args[i]);
739 ++i;
742 //We can be called from the helplinker executable or the extension manager
743 //In the latter case extsource is not used.
744 if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath)
745 || !extsource.empty())
747 bExtensionMode = true;
748 if (!extsource.empty())
750 //called from helplinker.exe, pExtensionPath and pOfficeHelpPath
751 //should be NULL
752 sourceRoot = fs::path(extsource, fs::native);
753 extensionPath = sourceRoot.toUTF8();
755 if (extdestination.empty())
757 std::stringstream aStrStream;
758 aStrStream << "-extlangdest is missing" << std::endl;
759 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
761 else
763 //Convert from system path to file URL!!!
764 fs::path p(extdestination, fs::native);
765 extensionDestination = p.toUTF8();
768 else
769 { //called from extension manager
770 extensionPath = *pExtensionPath;
771 sourceRoot = fs::path(extensionPath);
772 extensionDestination = *pDestination;
774 //check if -src option was used. This option must not be used
775 //when extension help is compiled.
776 if (bSrcOption)
778 std::stringstream aStrStream;
779 aStrStream << "-src must not be used together with -extsource missing" << std::endl;
780 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
784 if (!bExtensionMode && zipdir.empty())
786 std::stringstream aStrStream;
787 aStrStream << "no index dir given" << std::endl;
788 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
791 if ( (!bExtensionMode && idxCaptionStylesheet.empty())
792 || (!extsource.empty() && idxCaptionStylesheet.empty()) )
794 //No extension mode and extension mode using commandline
795 //!extsource.empty indicates extension mode using commandline
796 // -idxcaption parameter is required
797 std::stringstream aStrStream;
798 aStrStream << "no index caption stylesheet given" << std::endl;
799 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
801 else if ( bExtensionMode && extsource.empty())
803 //This part is used when compileExtensionHelp is called from the extensions manager.
804 //If extension help is compiled using helplinker in the build process
805 OUString aIdxCaptionPathFileURL( *pOfficeHelpPath );
806 aIdxCaptionPathFileURL += "/idxcaption.xsl";
808 OString aOStr_IdxCaptionPathFileURL( OUStringToOString
809 ( aIdxCaptionPathFileURL, fs::getThreadTextEncoding() ) );
810 std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() );
812 idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL );
815 if ( (!bExtensionMode && idxContentStylesheet.empty())
816 || (!extsource.empty() && idxContentStylesheet.empty()) )
818 //No extension mode and extension mode using commandline
819 //!extsource.empty indicates extension mode using commandline
820 // -idxcontent parameter is required
821 std::stringstream aStrStream;
822 aStrStream << "no index content stylesheet given" << std::endl;
823 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
825 else if ( bExtensionMode && extsource.empty())
827 //If extension help is compiled using helplinker in the build process
828 //then -idxcontent must be supplied
829 //This part is used when compileExtensionHelp is called from the extensions manager.
830 OUString aIdxContentPathFileURL( *pOfficeHelpPath );
831 aIdxContentPathFileURL += "/idxcontent.xsl";
833 OString aOStr_IdxContentPathFileURL( OUStringToOString
834 ( aIdxContentPathFileURL, fs::getThreadTextEncoding() ) );
835 std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() );
837 idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL );
839 if (!bExtensionMode && embeddStylesheet.empty())
841 std::stringstream aStrStream;
842 aStrStream << "no embedding resolving file given" << std::endl;
843 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
845 if (sourceRoot.empty())
847 std::stringstream aStrStream;
848 aStrStream << "no sourceroot given" << std::endl;
849 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
851 if (!bExtensionMode && outputFile.empty())
853 std::stringstream aStrStream;
854 aStrStream << "no output file given" << std::endl;
855 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
857 if (module.empty())
859 std::stringstream aStrStream;
860 aStrStream << "module missing" << std::endl;
861 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
863 if (!bExtensionMode && lang.empty())
865 std::stringstream aStrStream;
866 aStrStream << "language missing" << std::endl;
867 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
869 link();
872 // Variable to set an exception in "C" StructuredXMLErrorFunction
873 static const HelpProcessingException* GpXMLParsingException = nullptr;
875 extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
877 (void)userData;
878 (void)error;
880 std::string aErrorMsg = error->message;
881 std::string aXMLParsingFile;
882 if( error->file != nullptr )
883 aXMLParsingFile = error->file;
884 int nXMLParsingLine = error->line;
885 HelpProcessingException* pException = new HelpProcessingException( aErrorMsg, aXMLParsingFile, nXMLParsingLine );
886 GpXMLParsingException = pException;
888 // Reset error handler
889 xmlSetStructuredErrorFunc( nullptr, nullptr );
892 HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e )
894 m_eErrorClass = e.m_eErrorClass;
895 OString tmpErrorMsg( e.m_aErrorMsg.c_str() );
896 m_aErrorMsg = OStringToOUString( tmpErrorMsg, fs::getThreadTextEncoding() );
897 OString tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() );
898 m_aXMLParsingFile = OStringToOUString( tmpXMLParsingFile, fs::getThreadTextEncoding() );
899 m_nXMLParsingLine = e.m_nXMLParsingLine;
900 return *this;
904 // Returns true in case of success, false in case of error
905 bool compileExtensionHelp
907 const OUString& aOfficeHelpPath,
908 const OUString& aExtensionName,
909 const OUString& aExtensionLanguageRoot,
910 sal_Int32 nXhpFileCount, const OUString* pXhpFiles,
911 const OUString& aDestination,
912 HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo
915 bool bSuccess = true;
917 std::vector<std::string> args;
918 args.reserve(nXhpFileCount + 2);
919 args.push_back(std::string("-mod"));
920 OString aOExtensionName = OUStringToOString( aExtensionName, fs::getThreadTextEncoding() );
921 args.push_back(std::string(aOExtensionName.getStr()));
923 for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
925 OUString aXhpFile = pXhpFiles[iXhp];
927 OString aOXhpFile = OUStringToOString( aXhpFile, fs::getThreadTextEncoding() );
928 args.push_back(std::string(aOXhpFile.getStr()));
931 OString aOExtensionLanguageRoot = OUStringToOString( aExtensionLanguageRoot, fs::getThreadTextEncoding() );
932 const char* pExtensionPath = aOExtensionLanguageRoot.getStr();
933 std::string aStdStrExtensionPath = pExtensionPath;
934 OString aODestination = OUStringToOString(aDestination, fs::getThreadTextEncoding());
935 const char* pDestination = aODestination.getStr();
936 std::string aStdStrDestination = pDestination;
938 // Set error handler
939 xmlSetStructuredErrorFunc( nullptr, StructuredXMLErrorFunction );
942 std::unique_ptr<HelpLinker> pHelpLinker(new HelpLinker());
943 pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
945 catch( const HelpProcessingException& e )
947 if( GpXMLParsingException != nullptr )
949 o_rHelpProcessingErrorInfo = *GpXMLParsingException;
950 delete GpXMLParsingException;
951 GpXMLParsingException = nullptr;
953 else
955 o_rHelpProcessingErrorInfo = e;
957 bSuccess = false;
959 // Reset error handler
960 xmlSetStructuredErrorFunc( nullptr, nullptr );
962 // i83624: Tree files
963 // The following basically checks if the help.tree is well formed XML.
964 // Apparently there have been cases when translations contained
965 // non-well-formed XML in the past.
966 OUString aTreeFileURL = aExtensionLanguageRoot + "/help.tree";
967 osl::DirectoryItem aTreeFileItem;
968 osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
969 osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
970 if( rcGet == osl::FileBase::E_None &&
971 aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
972 aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) )
974 sal_uInt64 ret, len = aFileStatus.getFileSize();
975 std::unique_ptr<char[]> s(new char[ int(len) ]); // the buffer to hold the installed files
976 osl::File aFile( aTreeFileURL );
977 aFile.open( osl_File_OpenFlag_Read );
978 aFile.read( s.get(), len, ret );
979 aFile.close();
981 XML_Parser parser = XML_ParserCreate( nullptr );
982 XML_Status parsed = XML_Parse( parser, s.get(), int( len ), true );
984 if (XML_STATUS_ERROR == parsed)
986 XML_Error nError = XML_GetErrorCode( parser );
987 o_rHelpProcessingErrorInfo.m_eErrorClass = HELPPROCESSING_XMLPARSING_ERROR;
988 o_rHelpProcessingErrorInfo.m_aErrorMsg = OUString::createFromAscii( XML_ErrorString( nError ) );
989 o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL;
990 // CRASHES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser );
991 bSuccess = false;
994 XML_ParserFree( parser );
997 return bSuccess;
1000 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */