Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / helpcompiler / source / HelpLinker.cxx
blob44444902eb71f407f3b7fe8f9741c1726fd7163f
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 <algorithm>
24 #include <fstream>
26 #include <string.h>
28 #include <libxslt/transform.h>
30 #include <sal/types.h>
31 #include <o3tl/char16_t2wchar_t.hxx>
32 #include <sal/log.hxx>
34 #include <expat.h>
35 #include <memory>
37 namespace {
38 FILE* fopen_impl(const fs::path& rPath, const char* szMode)
40 #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP
41 return _wfopen(rPath.native_file_string_w().c_str(), o3tl::toW(OUString::createFromAscii(szMode).getStr()));
42 #else
43 return fopen(rPath.native_file_string().c_str(), szMode);
44 #endif
48 IndexerPreProcessor::IndexerPreProcessor
49 ( const fs::path& fsIndexBaseDir,
50 const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet )
52 m_fsCaptionFilesDirName = fsIndexBaseDir / "caption";
53 fs::create_directory( m_fsCaptionFilesDirName );
55 m_fsContentFilesDirName = fsIndexBaseDir / "content";
56 fs::create_directory( m_fsContentFilesDirName );
58 m_xsltStylesheetPtrCaption = xsltParseStylesheetFile
59 (reinterpret_cast<const xmlChar *>(idxCaptionStylesheet.native_file_string().c_str()));
60 m_xsltStylesheetPtrContent = xsltParseStylesheetFile
61 (reinterpret_cast<const xmlChar *>(idxContentStylesheet.native_file_string().c_str()));
64 IndexerPreProcessor::~IndexerPreProcessor()
66 if( m_xsltStylesheetPtrCaption )
67 xsltFreeStylesheet( m_xsltStylesheetPtrCaption );
68 if( m_xsltStylesheetPtrContent )
69 xsltFreeStylesheet( m_xsltStylesheetPtrContent );
72 static std::string getEncodedPath( const std::string& Path )
74 OString aOStr_Path( Path.c_str() );
75 OUString aOUStr_Path( OStringToOUString
76 ( aOStr_Path, fs::getThreadTextEncoding() ) );
77 OUString aPathURL;
78 osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL );
79 OString aOStr_PathURL( OUStringToOString
80 ( aPathURL, fs::getThreadTextEncoding() ) );
81 std::string aStdStr_PathURL( aOStr_PathURL.getStr() );
82 return aStdStr_PathURL;
85 void IndexerPreProcessor::processDocument
86 ( xmlDocPtr doc, const std::string &EncodedDocPath )
88 std::string aStdStr_EncodedDocPathURL = getEncodedPath( EncodedDocPath );
90 if( m_xsltStylesheetPtrCaption )
92 xmlDocPtr resCaption = xsltApplyStylesheet( m_xsltStylesheetPtrCaption, doc, nullptr );
93 xmlNodePtr pResNodeCaption = resCaption->xmlChildrenNode;
94 if( pResNodeCaption )
96 fs::path fsCaptionPureTextFile_docURL = m_fsCaptionFilesDirName / aStdStr_EncodedDocPathURL;
97 FILE* pFile_docURL = fopen_impl( fsCaptionPureTextFile_docURL, "w" );
98 if( pFile_docURL )
100 fprintf( pFile_docURL, "%s\n", pResNodeCaption->content );
101 fclose( pFile_docURL );
104 xmlFreeDoc(resCaption);
107 if( m_xsltStylesheetPtrContent )
109 xmlDocPtr resContent = xsltApplyStylesheet( m_xsltStylesheetPtrContent, doc, nullptr );
110 xmlNodePtr pResNodeContent = resContent->xmlChildrenNode;
111 if( pResNodeContent )
113 fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL;
114 FILE* pFile_docURL = fopen_impl( fsContentPureTextFile_docURL, "w" );
115 if( pFile_docURL )
117 fprintf( pFile_docURL, "%s\n", pResNodeContent->content );
118 fclose( pFile_docURL );
121 xmlFreeDoc(resContent);
125 struct Data
127 std::vector<std::string> _idList;
129 void append(const std::string &id)
131 _idList.push_back(id);
134 std::string getString() const
136 std::string ret;
137 for (auto const& elem : _idList)
138 ret += elem + ";";
139 return ret;
143 static void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr )
145 if( pFile == nullptr )
146 return;
147 char const cLF = 10;
148 unsigned int nKeyLen = aKeyStr.length();
149 unsigned int nValueLen = aValueStr.length();
150 fprintf( pFile, "%x ", nKeyLen );
151 if( nKeyLen > 0 )
153 if (fwrite( aKeyStr.c_str(), 1, nKeyLen, pFile ) != nKeyLen)
154 fprintf(stderr, "fwrite to db failed\n");
156 if (fprintf( pFile, " %x ", nValueLen ) < 0)
157 fprintf(stderr, "fwrite to db failed\n");
158 if( nValueLen > 0 )
160 if (fwrite( aValueStr.c_str(), 1, nValueLen, pFile ) != nValueLen)
161 fprintf(stderr, "fwrite to db failed\n");
163 if (fprintf( pFile, "%c", cLF ) < 0)
164 fprintf(stderr, "fwrite to db failed\n");
167 class HelpKeyword
169 private:
170 typedef std::unordered_map<std::string, Data> DataHashtable;
171 DataHashtable _hash;
173 public:
174 void insert(const std::string &key, const std::string &id)
176 Data &data = _hash[key];
177 data.append(id);
180 void dump_DBHelp( const fs::path& rFileName )
182 FILE* pFile = fopen_impl( rFileName, "wb" );
183 if( pFile == nullptr )
184 return;
186 for (auto const& elem : _hash)
187 writeKeyValue_DBHelp( pFile, elem.first, elem.second.getString() );
189 fclose( pFile );
193 namespace URLEncoder
195 static std::string encode(const std::string &rIn)
197 const char * const good = "!$&'()*+,-.=@_";
198 static const char hex[17] = "0123456789ABCDEF";
200 std::string result;
201 for (char c : rIn)
203 if (rtl::isAsciiAlphanumeric (static_cast<unsigned char>(c))
204 || strchr (good, c))
206 result += c;
207 } else {
208 result += '%';
209 result += hex[static_cast<unsigned char>(c) >> 4];
210 result += hex[c & 0xf];
213 return result;
217 void HelpLinker::addBookmark( FILE* pFile_DBHelp, std::string thishid,
218 const std::string& fileB, const std::string& anchorB,
219 const std::string& jarfileB, const std::string& titleB)
221 HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " <<
222 fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl);
224 thishid = URLEncoder::encode(thishid);
226 int fileLen = fileB.length();
227 if (!anchorB.empty())
228 fileLen += (1 + anchorB.length());
229 int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length();
231 std::vector<unsigned char> dataB(dataLen);
232 size_t i = 0;
233 dataB[i++] = static_cast<unsigned char>(fileLen);
234 for (char j : fileB)
235 dataB[i++] = static_cast<unsigned char>(j);
236 if (!anchorB.empty())
238 dataB[i++] = '#';
239 for (char j : anchorB)
240 dataB[i++] = j;
242 dataB[i++] = static_cast<unsigned char>(jarfileB.length());
243 for (char j : jarfileB)
244 dataB[i++] = j;
246 dataB[i++] = static_cast<unsigned char>(titleB.length());
247 for (char j : titleB)
248 dataB[i++] = j;
250 if( pFile_DBHelp != nullptr )
252 std::string aValueStr( dataB.begin(), dataB.end() );
253 writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr );
257 void HelpLinker::initIndexerPreProcessor()
259 m_pIndexerPreProcessor.reset( new IndexerPreProcessor( indexDirParentName,
260 idxCaptionStylesheet, idxContentStylesheet ) );
263 void HelpLinker::link()
266 if( bExtensionMode )
268 indexDirParentName = extensionDestination;
270 else
272 indexDirParentName = zipdir;
273 fs::create_directory(indexDirParentName);
276 std::string mod = module;
277 std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
279 // do the work here
280 // continue with introduction of the overall process thing into the
281 // here all hzip files will be worked on
282 bool bUse_ = true;
283 if( !bExtensionMode )
284 bUse_ = false;
286 fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht")));
287 FILE* pFileHelpText_DBHelp = fopen_impl( helpTextFileName_DBHelp, "wb" );
289 fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
290 FILE* pFileDbBase_DBHelp = fopen_impl( dbBaseFileName_DBHelp, "wb" );
292 fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
294 HelpKeyword helpKeyword;
296 // catch HelpProcessingException to avoid locking data bases
299 bool bIndexForExtension = true;
300 // lastly, initialize the indexBuilder
301 if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
302 initIndexerPreProcessor();
304 // here we start our loop over the hzip files.
305 for (auto const& helpFile : helpFiles)
307 // process one file
308 // streamTable contains the streams in the hzip file
309 StreamTable streamTable;
310 const std::string &xhpFileName = helpFile;
312 if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
314 // only work on .xhp - files
315 SAL_WARN("helpcompiler",
316 "ERROR: input list entry '"
317 << xhpFileName
318 << "' has the wrong extension (only files with extension .xhp are accepted)");
320 continue;
323 fs::path langsourceRoot(sourceRoot);
324 fs::path xhpFile;
326 if( bExtensionMode )
328 // langsourceRoot == sourceRoot for extensions
329 std::string xhpFileNameComplete( extensionPath );
330 xhpFileNameComplete.append( '/' + xhpFileName );
331 xhpFile = fs::path( xhpFileNameComplete );
333 else
335 langsourceRoot.append( "/" );
336 if ( m_bUseLangRoot )
337 langsourceRoot.append( lang + '/' );
338 xhpFile = fs::path(xhpFileName, fs::native);
341 HelpCompiler hc( streamTable, xhpFile, langsourceRoot, zipdir,
342 compactStylesheet, embeddStylesheet, module, lang, bExtensionMode );
344 HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl);
345 hc.compile();
346 HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl);
348 if (!m_bCreateIndex)
349 continue;
351 std::string documentPath = streamTable.document_path;
352 if (documentPath.compare(0, 1, "/") == 0)
353 documentPath = documentPath.substr(1);
355 std::string documentJarfile = streamTable.document_module + ".jar";
357 std::string documentTitle = streamTable.document_title;
358 if (documentTitle.empty())
359 documentTitle = "<notitle>";
361 const std::string& fileB = documentPath;
362 const std::string& jarfileB = documentJarfile;
363 std::string& titleB = documentTitle;
365 // add once this as its own id.
366 addBookmark( pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB);
368 const std::vector<std::string> *hidlist = streamTable.appl_hidlist.get();
369 if (hidlist)
371 // now iterate over all elements of the hidlist
372 for (auto & elem : *hidlist)
374 std::string thishid = elem;
376 std::string anchorB;
377 size_t index = thishid.rfind('#');
378 if (index != std::string::npos)
380 anchorB = thishid.substr(1 + index);
381 thishid = thishid.substr(0, index);
383 addBookmark( pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB);
387 // now the keywords
388 const Hashtable *anchorToLL = streamTable.appl_keywords.get();
389 if (anchorToLL && !anchorToLL->empty())
391 std::string fakedHid = URLEncoder::encode(documentPath);
392 for (auto const& elemAnchor : *anchorToLL)
394 const std::string &anchor = elemAnchor.first;
395 addBookmark(pFileDbBase_DBHelp, documentPath, fileB,
396 anchor, jarfileB, titleB);
397 std::string totalId = fakedHid + "#" + anchor;
398 // std::cerr << hzipFileName << std::endl;
399 const LinkedList& ll = elemAnchor.second;
400 for (auto const& elem : ll)
402 helpKeyword.insert(elem, totalId);
408 // and last the helptexts
409 const Stringtable *helpTextHash = streamTable.appl_helptexts.get();
410 if (helpTextHash)
412 for (auto const& elem : *helpTextHash)
414 std::string helpTextId = elem.first;
415 const std::string& helpTextText = elem.second;
417 helpTextId = URLEncoder::encode(helpTextId);
419 if( pFileHelpText_DBHelp != nullptr )
420 writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText );
424 //IndexerPreProcessor
425 if( !bExtensionMode || bIndexForExtension )
427 // now the indexing
428 xmlDocPtr document = streamTable.appl_doc;
429 if (document)
431 std::string temp = module;
432 std::transform (temp.begin(), temp.end(), temp.begin(), tocharlower);
433 m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) );
440 catch( const HelpProcessingException& )
442 // catch HelpProcessingException to avoid locking data bases
443 if( pFileHelpText_DBHelp != nullptr )
444 fclose( pFileHelpText_DBHelp );
445 if( pFileDbBase_DBHelp != nullptr )
446 fclose( pFileDbBase_DBHelp );
447 throw;
450 if( pFileHelpText_DBHelp != nullptr )
451 fclose( pFileHelpText_DBHelp );
452 if( pFileDbBase_DBHelp != nullptr )
453 fclose( pFileDbBase_DBHelp );
455 helpKeyword.dump_DBHelp( keyWordFileName_DBHelp);
457 if( !bExtensionMode )
459 // New index
460 for (auto const& additionalFile : additionalFiles)
462 const std::string &additionalFileName = additionalFile.second;
463 const std::string &additionalFileKey = additionalFile.first;
465 fs::path fsAdditionalFileName( additionalFileName, fs::native );
466 HCDBG({
467 std::string aNativeStr = fsAdditionalFileName.native_file_string();
468 const char* pStr = aNativeStr.c_str();
469 std::cerr << pStr << std::endl;
472 fs::path fsTargetName( indexDirParentName / additionalFileKey );
474 fs::copy( fsAdditionalFileName, fsTargetName );
480 void HelpLinker::main( std::vector<std::string> &args,
481 std::string const * pExtensionPath, std::string const * pDestination,
482 const OUString* pOfficeHelpPath )
484 bExtensionMode = false;
485 helpFiles.clear();
487 if ((!args.empty()) && args[0][0] == '@')
489 std::vector<std::string> stringList;
490 std::ifstream fileReader(args[0].substr(1).c_str());
492 while (fileReader)
494 std::string token;
495 fileReader >> token;
496 if (!token.empty())
497 stringList.push_back(token);
499 fileReader.close();
501 args = stringList;
504 size_t i = 0;
505 bool bSrcOption = false;
506 while (i < args.size())
508 if (args[i].compare("-extlangsrc") == 0)
510 ++i;
511 if (i >= args.size())
513 std::stringstream aStrStream;
514 aStrStream << "extension source missing" << std::endl;
515 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
517 extsource = args[i];
519 else if (args[i].compare("-extlangdest") == 0)
521 //If this argument is not provided then the location provided in -extsource will
522 //also be the destination
523 ++i;
524 if (i >= args.size())
526 std::stringstream aStrStream;
527 aStrStream << "extension destination missing" << std::endl;
528 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
530 extdestination = args[i];
532 else if (args[i].compare("-src") == 0)
534 ++i;
535 if (i >= args.size())
537 std::stringstream aStrStream;
538 aStrStream << "sourceroot missing" << std::endl;
539 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
541 bSrcOption = true;
542 sourceRoot = fs::path(args[i], fs::native);
544 else if (args[i].compare("-compact") == 0)
546 ++i;
547 if (i >= args.size())
549 std::stringstream aStrStream;
550 aStrStream << "compactStylesheet missing" << std::endl;
551 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
554 compactStylesheet = fs::path(args[i], fs::native);
556 else if (args[i].compare("-sty") == 0)
558 ++i;
559 if (i >= args.size())
561 std::stringstream aStrStream;
562 aStrStream << "embeddingStylesheet missing" << std::endl;
563 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
566 embeddStylesheet = fs::path(args[i], fs::native);
568 else if (args[i].compare("-zipdir") == 0)
570 ++i;
571 if (i >= args.size())
573 std::stringstream aStrStream;
574 aStrStream << "idxtemp missing" << std::endl;
575 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
578 zipdir = fs::path(args[i], fs::native);
580 else if (args[i].compare("-idxcaption") == 0)
582 ++i;
583 if (i >= args.size())
585 std::stringstream aStrStream;
586 aStrStream << "idxcaption stylesheet missing" << std::endl;
587 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
590 idxCaptionStylesheet = fs::path(args[i], fs::native);
592 else if (args[i].compare("-idxcontent") == 0)
594 ++i;
595 if (i >= args.size())
597 std::stringstream aStrStream;
598 aStrStream << "idxcontent stylesheet missing" << std::endl;
599 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
602 idxContentStylesheet = fs::path(args[i], fs::native);
604 else if (args[i].compare("-o") == 0)
606 ++i;
607 if (i >= args.size())
609 std::stringstream aStrStream;
610 aStrStream << "outputfilename missing" << std::endl;
611 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
614 outputFile = fs::path(args[i], fs::native);
616 else if (args[i].compare("-mod") == 0)
618 ++i;
619 if (i >= args.size())
621 std::stringstream aStrStream;
622 aStrStream << "module name missing" << std::endl;
623 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
626 module = args[i];
628 else if (args[i].compare("-lang") == 0)
630 ++i;
631 if (i >= args.size())
633 std::stringstream aStrStream;
634 aStrStream << "language name missing" << std::endl;
635 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
638 lang = args[i];
640 else if (args[i].compare("-hid") == 0)
642 ++i;
643 throw HelpProcessingException( HelpProcessingErrorClass::General, "obsolete -hid argument used" );
645 else if (args[i].compare("-add") == 0)
647 std::string addFile, addFileUnderPath;
648 ++i;
649 if (i >= args.size())
651 std::stringstream aStrStream;
652 aStrStream << "pathname missing" << std::endl;
653 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
656 addFileUnderPath = args[i];
657 ++i;
658 if (i >= args.size())
660 std::stringstream aStrStream;
661 aStrStream << "pathname missing" << std::endl;
662 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
664 addFile = args[i];
665 if (!addFileUnderPath.empty() && !addFile.empty())
666 additionalFiles[addFileUnderPath] = addFile;
668 else if (args[i].compare("-nolangroot") == 0)
669 m_bUseLangRoot = false;
670 else if (args[i].compare("-noindex") == 0)
671 m_bCreateIndex = false;
672 else
673 helpFiles.push_back(args[i]);
674 ++i;
677 //We can be called from the helplinker executable or the extension manager
678 //In the latter case extsource is not used.
679 if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath)
680 || !extsource.empty())
682 bExtensionMode = true;
683 if (!extsource.empty())
685 //called from helplinker.exe, pExtensionPath and pOfficeHelpPath
686 //should be NULL
687 sourceRoot = fs::path(extsource, fs::native);
688 extensionPath = sourceRoot.toUTF8();
690 if (extdestination.empty())
692 std::stringstream aStrStream;
693 aStrStream << "-extlangdest is missing" << std::endl;
694 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
696 else
698 //Convert from system path to file URL!!!
699 fs::path p(extdestination, fs::native);
700 extensionDestination = p.toUTF8();
703 else
704 { //called from extension manager
705 extensionPath = *pExtensionPath;
706 sourceRoot = fs::path(extensionPath);
707 extensionDestination = *pDestination;
709 //check if -src option was used. This option must not be used
710 //when extension help is compiled.
711 if (bSrcOption)
713 std::stringstream aStrStream;
714 aStrStream << "-src must not be used together with -extsource missing" << std::endl;
715 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
719 if (!bExtensionMode && zipdir.empty())
721 std::stringstream aStrStream;
722 aStrStream << "no index dir given" << std::endl;
723 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
726 if ( (!bExtensionMode && idxCaptionStylesheet.empty())
727 || (!extsource.empty() && idxCaptionStylesheet.empty()) )
729 //No extension mode and extension mode using commandline
730 //!extsource.empty indicates extension mode using commandline
731 // -idxcaption parameter is required
732 std::stringstream aStrStream;
733 aStrStream << "no index caption stylesheet given" << std::endl;
734 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
736 else if ( bExtensionMode && extsource.empty())
738 //This part is used when compileExtensionHelp is called from the extensions manager.
739 //If extension help is compiled using helplinker in the build process
740 OUString aIdxCaptionPathFileURL = *pOfficeHelpPath + "/idxcaption.xsl";
742 OString aOStr_IdxCaptionPathFileURL( OUStringToOString
743 ( aIdxCaptionPathFileURL, fs::getThreadTextEncoding() ) );
744 std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() );
746 idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL );
749 if ( (!bExtensionMode && idxContentStylesheet.empty())
750 || (!extsource.empty() && idxContentStylesheet.empty()) )
752 //No extension mode and extension mode using commandline
753 //!extsource.empty indicates extension mode using commandline
754 // -idxcontent parameter is required
755 std::stringstream aStrStream;
756 aStrStream << "no index content stylesheet given" << std::endl;
757 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
759 else if ( bExtensionMode && extsource.empty())
761 //If extension help is compiled using helplinker in the build process
762 //then -idxcontent must be supplied
763 //This part is used when compileExtensionHelp is called from the extensions manager.
764 OUString aIdxContentPathFileURL = *pOfficeHelpPath + "/idxcontent.xsl";
766 OString aOStr_IdxContentPathFileURL( OUStringToOString
767 ( aIdxContentPathFileURL, fs::getThreadTextEncoding() ) );
768 std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() );
770 idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL );
772 if (!bExtensionMode && embeddStylesheet.empty())
774 std::stringstream aStrStream;
775 aStrStream << "no embedding resolving file given" << std::endl;
776 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
778 if (sourceRoot.empty())
780 std::stringstream aStrStream;
781 aStrStream << "no sourceroot given" << std::endl;
782 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
784 if (!bExtensionMode && outputFile.empty())
786 std::stringstream aStrStream;
787 aStrStream << "no output file given" << std::endl;
788 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
790 if (module.empty())
792 std::stringstream aStrStream;
793 aStrStream << "module missing" << std::endl;
794 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
796 if (!bExtensionMode && lang.empty())
798 std::stringstream aStrStream;
799 aStrStream << "language missing" << std::endl;
800 throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() );
802 link();
805 // Variable to set an exception in "C" StructuredXMLErrorFunction
806 static const HelpProcessingException* GpXMLParsingException = nullptr;
808 extern "C" {
810 static void StructuredXMLErrorFunction(SAL_UNUSED_PARAMETER void *, xmlErrorPtr error)
812 std::string aErrorMsg = error->message;
813 std::string aXMLParsingFile;
814 if( error->file != nullptr )
815 aXMLParsingFile = error->file;
816 int nXMLParsingLine = error->line;
817 HelpProcessingException* pException = new HelpProcessingException( aErrorMsg, aXMLParsingFile, nXMLParsingLine );
818 GpXMLParsingException = pException;
820 // Reset error handler
821 xmlSetStructuredErrorFunc( nullptr, nullptr );
826 HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e )
828 m_eErrorClass = e.m_eErrorClass;
829 OString tmpErrorMsg( e.m_aErrorMsg.c_str() );
830 m_aErrorMsg = OStringToOUString( tmpErrorMsg, fs::getThreadTextEncoding() );
831 OString tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() );
832 m_aXMLParsingFile = OStringToOUString( tmpXMLParsingFile, fs::getThreadTextEncoding() );
833 m_nXMLParsingLine = e.m_nXMLParsingLine;
834 return *this;
838 // Returns true in case of success, false in case of error
839 bool compileExtensionHelp
841 const OUString& aOfficeHelpPath,
842 const OUString& aExtensionName,
843 const OUString& aExtensionLanguageRoot,
844 sal_Int32 nXhpFileCount, const OUString* pXhpFiles,
845 const OUString& aDestination,
846 HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo
849 bool bSuccess = true;
851 std::vector<std::string> args;
852 args.reserve(nXhpFileCount + 2);
853 args.push_back(std::string("-mod"));
854 OString aOExtensionName = OUStringToOString( aExtensionName, fs::getThreadTextEncoding() );
855 args.push_back(std::string(aOExtensionName.getStr()));
857 for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
859 OUString aXhpFile = pXhpFiles[iXhp];
861 OString aOXhpFile = OUStringToOString( aXhpFile, fs::getThreadTextEncoding() );
862 args.push_back(std::string(aOXhpFile.getStr()));
865 OString aOExtensionLanguageRoot = OUStringToOString( aExtensionLanguageRoot, fs::getThreadTextEncoding() );
866 const char* pExtensionPath = aOExtensionLanguageRoot.getStr();
867 std::string aStdStrExtensionPath = pExtensionPath;
868 OString aODestination = OUStringToOString(aDestination, fs::getThreadTextEncoding());
869 const char* pDestination = aODestination.getStr();
870 std::string aStdStrDestination = pDestination;
872 // Set error handler
873 xmlSetStructuredErrorFunc( nullptr, StructuredXMLErrorFunction );
876 std::unique_ptr<HelpLinker> pHelpLinker(new HelpLinker());
877 pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
879 catch( const HelpProcessingException& e )
881 if( GpXMLParsingException != nullptr )
883 o_rHelpProcessingErrorInfo = *GpXMLParsingException;
884 delete GpXMLParsingException;
885 GpXMLParsingException = nullptr;
887 else
889 o_rHelpProcessingErrorInfo = e;
891 bSuccess = false;
893 // Reset error handler
894 xmlSetStructuredErrorFunc( nullptr, nullptr );
896 // i83624: Tree files
897 // The following basically checks if the help.tree is well formed XML.
898 // Apparently there have been cases when translations contained
899 // non-well-formed XML in the past.
900 OUString aTreeFileURL = aExtensionLanguageRoot + "/help.tree";
901 osl::DirectoryItem aTreeFileItem;
902 osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
903 osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
904 if( rcGet == osl::FileBase::E_None &&
905 aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
906 aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) )
908 sal_uInt64 ret, len = aFileStatus.getFileSize();
909 std::unique_ptr<char[]> s(new char[ int(len) ]); // the buffer to hold the installed files
910 osl::File aFile( aTreeFileURL );
911 (void)aFile.open( osl_File_OpenFlag_Read );
912 aFile.read( s.get(), len, ret );
913 aFile.close();
915 XML_Parser parser = XML_ParserCreate( nullptr );
916 XML_Status parsed = XML_Parse( parser, s.get(), int( len ), true );
918 if (XML_STATUS_ERROR == parsed)
920 XML_Error nError = XML_GetErrorCode( parser );
921 o_rHelpProcessingErrorInfo.m_eErrorClass = HelpProcessingErrorClass::XmlParsing;
922 o_rHelpProcessingErrorInfo.m_aErrorMsg = OUString::createFromAscii( XML_ErrorString( nError ) );
923 o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL;
924 // CRASHES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser );
925 bSuccess = false;
928 XML_ParserFree( parser );
931 return bSuccess;
934 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */