/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include namespace { FILE* fopen_impl(const fs::path& rPath, const char* szMode) { #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP return _wfopen(rPath.native_file_string_w().c_str(), o3tl::toW(OUString::createFromAscii(szMode).getStr())); #else return fopen(rPath.native_file_string().c_str(), szMode); #endif } } IndexerPreProcessor::IndexerPreProcessor ( const fs::path& fsIndexBaseDir, const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet ) { m_fsCaptionFilesDirName = fsIndexBaseDir / "caption"; fs::create_directory( m_fsCaptionFilesDirName ); m_fsContentFilesDirName = fsIndexBaseDir / "content"; fs::create_directory( m_fsContentFilesDirName ); m_xsltStylesheetPtrCaption = xsltParseStylesheetFile (reinterpret_cast(idxCaptionStylesheet.native_file_string().c_str())); m_xsltStylesheetPtrContent = xsltParseStylesheetFile (reinterpret_cast(idxContentStylesheet.native_file_string().c_str())); } IndexerPreProcessor::~IndexerPreProcessor() { if( m_xsltStylesheetPtrCaption ) xsltFreeStylesheet( m_xsltStylesheetPtrCaption ); if( m_xsltStylesheetPtrContent ) xsltFreeStylesheet( m_xsltStylesheetPtrContent ); } static std::string getEncodedPath( const std::string& Path ) { std::string_view aOStr_Path( Path.c_str() ); OUString aOUStr_Path( OStringToOUString ( aOStr_Path, osl_getThreadTextEncoding() ) ); OUString aPathURL; osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL ); OString aOStr_PathURL( OUStringToOString ( aPathURL, osl_getThreadTextEncoding() ) ); std::string aStdStr_PathURL( aOStr_PathURL.getStr() ); return aStdStr_PathURL; } void IndexerPreProcessor::processDocument ( xmlDocPtr doc, const std::string &EncodedDocPath ) { std::string aStdStr_EncodedDocPathURL = getEncodedPath( EncodedDocPath ); if( m_xsltStylesheetPtrCaption ) { xmlDocPtr resCaption = xsltApplyStylesheet( m_xsltStylesheetPtrCaption, doc, nullptr ); xmlNodePtr pResNodeCaption = resCaption->xmlChildrenNode; if( pResNodeCaption ) { fs::path fsCaptionPureTextFile_docURL = m_fsCaptionFilesDirName / aStdStr_EncodedDocPathURL; FILE* pFile_docURL = fopen_impl( fsCaptionPureTextFile_docURL, "w" ); if( pFile_docURL ) { fprintf( pFile_docURL, "%s\n", pResNodeCaption->content ); fclose( pFile_docURL ); } } xmlFreeDoc(resCaption); } if( !m_xsltStylesheetPtrContent ) return; xmlDocPtr resContent = xsltApplyStylesheet( m_xsltStylesheetPtrContent, doc, nullptr ); xmlNodePtr pResNodeContent = resContent->xmlChildrenNode; if( pResNodeContent ) { fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL; FILE* pFile_docURL = fopen_impl( fsContentPureTextFile_docURL, "w" ); if( pFile_docURL ) { fprintf( pFile_docURL, "%s\n", pResNodeContent->content ); fclose( pFile_docURL ); } } xmlFreeDoc(resContent); } namespace { struct Data { std::vector _idList; void append(const std::string &id) { _idList.push_back(id); } std::string getString() const { std::string ret; for (auto const& elem : _idList) ret += elem + ";"; return ret; } }; } static void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr ) { if( pFile == nullptr ) return; char const cLF = 10; unsigned int nKeyLen = aKeyStr.length(); unsigned int nValueLen = aValueStr.length(); fprintf( pFile, "%x ", nKeyLen ); if( nKeyLen > 0 ) { if (fwrite( aKeyStr.c_str(), 1, nKeyLen, pFile ) != nKeyLen) fprintf(stderr, "fwrite to db failed\n"); } if (fprintf( pFile, " %x ", nValueLen ) < 0) fprintf(stderr, "fwrite to db failed\n"); if( nValueLen > 0 ) { if (fwrite( aValueStr.c_str(), 1, nValueLen, pFile ) != nValueLen) fprintf(stderr, "fwrite to db failed\n"); } if (fprintf( pFile, "%c", cLF ) < 0) fprintf(stderr, "fwrite to db failed\n"); } namespace { class HelpKeyword { private: typedef std::unordered_map DataHashtable; DataHashtable _hash; public: void insert(const std::string &key, const std::string &id) { Data &data = _hash[key]; data.append(id); } void dump_DBHelp( const fs::path& rFileName ) { FILE* pFile = fopen_impl( rFileName, "wb" ); if( pFile == nullptr ) return; for (auto const& elem : _hash) writeKeyValue_DBHelp( pFile, elem.first, elem.second.getString() ); fclose( pFile ); } }; } namespace URLEncoder { static std::string encode(const std::string &rIn) { const char * const good = "!$&'()*+,-.=@_"; static const char hex[17] = "0123456789ABCDEF"; std::string result; for (char c : rIn) { if (rtl::isAsciiAlphanumeric (static_cast(c)) || strchr (good, c)) { result += c; } else { result += '%'; result += hex[static_cast(c) >> 4]; result += hex[c & 0xf]; } } return result; } } void HelpLinker::addBookmark( FILE* pFile_DBHelp, std::string thishid, const std::string& fileB, const std::string& anchorB, const std::string& jarfileB, const std::string& titleB) { HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " << fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl); thishid = URLEncoder::encode(thishid); int fileLen = fileB.length(); if (!anchorB.empty()) fileLen += (1 + anchorB.length()); int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length(); std::vector dataB(dataLen); size_t i = 0; dataB[i++] = static_cast(fileLen); for (char j : fileB) dataB[i++] = static_cast(j); if (!anchorB.empty()) { dataB[i++] = '#'; for (char j : anchorB) dataB[i++] = j; } dataB[i++] = static_cast(jarfileB.length()); for (char j : jarfileB) dataB[i++] = j; dataB[i++] = static_cast(titleB.length()); for (char j : titleB) dataB[i++] = j; if( pFile_DBHelp != nullptr ) { std::string aValueStr( dataB.begin(), dataB.end() ); writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr ); } } void HelpLinker::initIndexerPreProcessor() { m_pIndexerPreProcessor.reset( new IndexerPreProcessor( indexDirParentName, idxCaptionStylesheet, idxContentStylesheet ) ); } void HelpLinker::link() { if( bExtensionMode ) { indexDirParentName = extensionDestination; } else { indexDirParentName = zipdir; fs::create_directory(indexDirParentName); } std::string mod = module; std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower); // do the work here // continue with introduction of the overall process thing into the // here all hzip files will be worked on bool bUse_ = true; if( !bExtensionMode ) bUse_ = false; fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht"))); FILE* pFileHelpText_DBHelp = fopen_impl( helpTextFileName_DBHelp, "wb" ); fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db"))); FILE* pFileDbBase_DBHelp = fopen_impl( dbBaseFileName_DBHelp, "wb" ); fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key"))); HelpKeyword helpKeyword; // catch HelpProcessingException to avoid locking data bases try { bool bIndexForExtension = true; // lastly, initialize the indexBuilder if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty()) initIndexerPreProcessor(); // here we start our loop over the hzip files. for (auto const& helpFile : helpFiles) { // process one file // streamTable contains the streams in the hzip file StreamTable streamTable; const std::string &xhpFileName = helpFile; if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4) { // only work on .xhp - files SAL_WARN("helpcompiler", "ERROR: input list entry '" << xhpFileName << "' has the wrong extension (only files with extension .xhp are accepted)"); continue; } fs::path langsourceRoot(sourceRoot); fs::path xhpFile; if( bExtensionMode ) { // langsourceRoot == sourceRoot for extensions std::string xhpFileNameComplete( extensionPath ); xhpFileNameComplete.append( '/' + xhpFileName ); xhpFile = fs::path( xhpFileNameComplete ); } else { langsourceRoot.append( "/" ); if ( m_bUseLangRoot ) langsourceRoot.append( lang + '/' ); xhpFile = fs::path(xhpFileName, fs::native); } HelpCompiler hc( streamTable, std::move(xhpFile), std::move(langsourceRoot), zipdir, compactStylesheet, embeddStylesheet, module, lang, bExtensionMode ); HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl); hc.compile(); HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl); if (!m_bCreateIndex) continue; std::string documentPath = streamTable.document_path; if (documentPath.compare(0, 1, "/") == 0) documentPath = documentPath.substr(1); std::string documentJarfile = streamTable.document_module + ".jar"; std::string documentTitle = streamTable.document_title; if (documentTitle.empty()) documentTitle = ""; const std::string& fileB = documentPath; const std::string& jarfileB = documentJarfile; std::string& titleB = documentTitle; // add once this as its own id. addBookmark( pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB); const std::vector *hidlist = streamTable.appl_hidlist.get(); if (hidlist) { // now iterate over all elements of the hidlist for (auto & elem : *hidlist) { std::string thishid = elem; std::string anchorB; size_t index = thishid.rfind('#'); if (index != std::string::npos) { anchorB = thishid.substr(1 + index); thishid = thishid.substr(0, index); } addBookmark( pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB); } } // now the keywords const Hashtable *anchorToLL = streamTable.appl_keywords.get(); if (anchorToLL && !anchorToLL->empty()) { std::string fakedHid = URLEncoder::encode(documentPath); for (auto const& elemAnchor : *anchorToLL) { const std::string &anchor = elemAnchor.first; addBookmark(pFileDbBase_DBHelp, documentPath, fileB, anchor, jarfileB, titleB); std::string totalId = fakedHid + "#" + anchor; // std::cerr << hzipFileName << std::endl; const LinkedList& ll = elemAnchor.second; for (auto const& elem : ll) { helpKeyword.insert(elem, totalId); } } } // and last the helptexts const Stringtable *helpTextHash = streamTable.appl_helptexts.get(); if (helpTextHash) { for (auto const& elem : *helpTextHash) { std::string helpTextId = elem.first; const std::string& helpTextText = elem.second; helpTextId = URLEncoder::encode(helpTextId); if( pFileHelpText_DBHelp != nullptr ) writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText ); } } //IndexerPreProcessor if( !bExtensionMode || bIndexForExtension ) { // now the indexing xmlDocPtr document = streamTable.appl_doc; if (document) { std::string temp = module; std::transform (temp.begin(), temp.end(), temp.begin(), tocharlower); m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) ); } } } } catch( const HelpProcessingException& ) { // catch HelpProcessingException to avoid locking data bases if( pFileHelpText_DBHelp != nullptr ) fclose( pFileHelpText_DBHelp ); if( pFileDbBase_DBHelp != nullptr ) fclose( pFileDbBase_DBHelp ); throw; } if( pFileHelpText_DBHelp != nullptr ) fclose( pFileHelpText_DBHelp ); if( pFileDbBase_DBHelp != nullptr ) fclose( pFileDbBase_DBHelp ); helpKeyword.dump_DBHelp( keyWordFileName_DBHelp); if( bExtensionMode ) return; // New index for (auto const& additionalFile : additionalFiles) { const std::string &additionalFileName = additionalFile.second; const std::string &additionalFileKey = additionalFile.first; fs::path fsAdditionalFileName( additionalFileName, fs::native ); HCDBG({ std::string aNativeStr = fsAdditionalFileName.native_file_string(); const char* pStr = aNativeStr.c_str(); std::cerr << pStr << std::endl; }); fs::path fsTargetName( indexDirParentName / additionalFileKey ); fs::copy( fsAdditionalFileName, fsTargetName ); } } void HelpLinker::main( std::vector &args, std::string const * pExtensionPath, std::string const * pDestination, const OUString* pOfficeHelpPath ) { bExtensionMode = false; helpFiles.clear(); if ((!args.empty()) && args[0][0] == '@') { std::vector stringList; std::ifstream fileReader(args[0].substr(1).c_str()); while (fileReader) { std::string token; fileReader >> token; if (!token.empty()) stringList.push_back(token); } fileReader.close(); args = stringList; } size_t i = 0; bool bSrcOption = false; while (i < args.size()) { if (args[i].compare("-extlangsrc") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "extension source missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } extsource = args[i]; } else if (args[i].compare("-extlangdest") == 0) { //If this argument is not provided then the location provided in -extsource will //also be the destination ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "extension destination missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } extdestination = args[i]; } else if (args[i].compare("-src") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "sourceroot missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } bSrcOption = true; sourceRoot = fs::path(args[i], fs::native); } else if (args[i].compare("-compact") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "compactStylesheet missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } compactStylesheet = fs::path(args[i], fs::native); } else if (args[i].compare("-sty") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "embeddingStylesheet missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } embeddStylesheet = fs::path(args[i], fs::native); } else if (args[i].compare("-zipdir") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "idxtemp missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } zipdir = fs::path(args[i], fs::native); } else if (args[i].compare("-idxcaption") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "idxcaption stylesheet missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } idxCaptionStylesheet = fs::path(args[i], fs::native); } else if (args[i].compare("-idxcontent") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "idxcontent stylesheet missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } idxContentStylesheet = fs::path(args[i], fs::native); } else if (args[i].compare("-o") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "outputfilename missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } outputFile = fs::path(args[i], fs::native); } else if (args[i].compare("-mod") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "module name missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } module = args[i]; } else if (args[i].compare("-lang") == 0) { ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "language name missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } lang = args[i]; } else if (args[i].compare("-hid") == 0) { ++i; throw HelpProcessingException( HelpProcessingErrorClass::General, "obsolete -hid argument used" ); } else if (args[i].compare("-add") == 0) { std::string addFile, addFileUnderPath; ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "pathname missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } addFileUnderPath = args[i]; ++i; if (i >= args.size()) { std::stringstream aStrStream; aStrStream << "pathname missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } addFile = args[i]; if (!addFileUnderPath.empty() && !addFile.empty()) additionalFiles[addFileUnderPath] = addFile; } else if (args[i].compare("-nolangroot") == 0) m_bUseLangRoot = false; else if (args[i].compare("-noindex") == 0) m_bCreateIndex = false; else helpFiles.push_back(args[i]); ++i; } //We can be called from the helplinker executable or the extension manager //In the latter case extsource is not used. if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath) || !extsource.empty()) { bExtensionMode = true; if (!extsource.empty()) { //called from helplinker.exe, pExtensionPath and pOfficeHelpPath //should be NULL sourceRoot = fs::path(extsource, fs::native); extensionPath = sourceRoot.toUTF8(); if (extdestination.empty()) { std::stringstream aStrStream; aStrStream << "-extlangdest is missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } else { //Convert from system path to file URL!!! fs::path p(extdestination, fs::native); extensionDestination = p.toUTF8(); } } else { //called from extension manager extensionPath = *pExtensionPath; sourceRoot = fs::path(extensionPath); extensionDestination = *pDestination; } //check if -src option was used. This option must not be used //when extension help is compiled. if (bSrcOption) { std::stringstream aStrStream; aStrStream << "-src must not be used together with -extsource missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } } if (!bExtensionMode && zipdir.empty()) { std::stringstream aStrStream; aStrStream << "no index dir given" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } if ( (!bExtensionMode && idxCaptionStylesheet.empty()) || (!extsource.empty() && idxCaptionStylesheet.empty()) ) { //No extension mode and extension mode using commandline //!extsource.empty indicates extension mode using commandline // -idxcaption parameter is required std::stringstream aStrStream; aStrStream << "no index caption stylesheet given" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } else if ( bExtensionMode && extsource.empty()) { //This part is used when compileExtensionHelp is called from the extensions manager. //If extension help is compiled using helplinker in the build process OUString aIdxCaptionPathFileURL = *pOfficeHelpPath + "/idxcaption.xsl"; OString aOStr_IdxCaptionPathFileURL( OUStringToOString ( aIdxCaptionPathFileURL, osl_getThreadTextEncoding() ) ); std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() ); idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL ); } if ( (!bExtensionMode && idxContentStylesheet.empty()) || (!extsource.empty() && idxContentStylesheet.empty()) ) { //No extension mode and extension mode using commandline //!extsource.empty indicates extension mode using commandline // -idxcontent parameter is required std::stringstream aStrStream; aStrStream << "no index content stylesheet given" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } else if ( bExtensionMode && extsource.empty()) { //If extension help is compiled using helplinker in the build process //then -idxcontent must be supplied //This part is used when compileExtensionHelp is called from the extensions manager. OUString aIdxContentPathFileURL = *pOfficeHelpPath + "/idxcontent.xsl"; OString aOStr_IdxContentPathFileURL( OUStringToOString ( aIdxContentPathFileURL, osl_getThreadTextEncoding() ) ); std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() ); idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL ); } if (!bExtensionMode && embeddStylesheet.empty()) { std::stringstream aStrStream; aStrStream << "no embedding resolving file given" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } if (sourceRoot.empty()) { std::stringstream aStrStream; aStrStream << "no sourceroot given" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } if (!bExtensionMode && outputFile.empty()) { std::stringstream aStrStream; aStrStream << "no output file given" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } if (module.empty()) { std::stringstream aStrStream; aStrStream << "module missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } if (!bExtensionMode && lang.empty()) { std::stringstream aStrStream; aStrStream << "language missing" << std::endl; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } link(); } // Variable to set an exception in "C" StructuredXMLErrorFunction static const HelpProcessingException* GpXMLParsingException = nullptr; extern "C" { static void StructuredXMLErrorFunction(SAL_UNUSED_PARAMETER void *, xmlErrorPtr error) { std::string aErrorMsg = error->message; std::string aXMLParsingFile; if( error->file != nullptr ) aXMLParsingFile = error->file; int nXMLParsingLine = error->line; HelpProcessingException* pException = new HelpProcessingException( aErrorMsg, aXMLParsingFile, nXMLParsingLine ); GpXMLParsingException = pException; // Reset error handler xmlSetStructuredErrorFunc( nullptr, nullptr ); } } HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e ) { m_eErrorClass = e.m_eErrorClass; std::string_view tmpErrorMsg( e.m_aErrorMsg.c_str() ); m_aErrorMsg = OStringToOUString( tmpErrorMsg, osl_getThreadTextEncoding() ); std::string_view tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() ); m_aXMLParsingFile = OStringToOUString( tmpXMLParsingFile, osl_getThreadTextEncoding() ); m_nXMLParsingLine = e.m_nXMLParsingLine; return *this; } // Returns true in case of success, false in case of error bool compileExtensionHelp ( const OUString& aOfficeHelpPath, std::u16string_view aExtensionName, std::u16string_view aExtensionLanguageRoot, sal_Int32 nXhpFileCount, const OUString* pXhpFiles, std::u16string_view aDestination, HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo ) { bool bSuccess = true; std::vector args; args.reserve(nXhpFileCount + 2); args.push_back(std::string("-mod")); OString aOExtensionName = OUStringToOString( aExtensionName, osl_getThreadTextEncoding() ); args.push_back(std::string(aOExtensionName.getStr())); for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp ) { OUString aXhpFile = pXhpFiles[iXhp]; OString aOXhpFile = OUStringToOString( aXhpFile, osl_getThreadTextEncoding() ); args.push_back(std::string(aOXhpFile.getStr())); } OString aOExtensionLanguageRoot = OUStringToOString( aExtensionLanguageRoot, osl_getThreadTextEncoding() ); const char* pExtensionPath = aOExtensionLanguageRoot.getStr(); std::string aStdStrExtensionPath = pExtensionPath; OString aODestination = OUStringToOString(aDestination, osl_getThreadTextEncoding()); const char* pDestination = aODestination.getStr(); std::string aStdStrDestination = pDestination; // Set error handler xmlSetStructuredErrorFunc( nullptr, StructuredXMLErrorFunction ); try { HelpLinker aHelpLinker; aHelpLinker.main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath ); } catch( const HelpProcessingException& e ) { if( GpXMLParsingException != nullptr ) { o_rHelpProcessingErrorInfo = *GpXMLParsingException; delete GpXMLParsingException; GpXMLParsingException = nullptr; } else { o_rHelpProcessingErrorInfo = e; } bSuccess = false; } // Reset error handler xmlSetStructuredErrorFunc( nullptr, nullptr ); // i83624: Tree files // The following basically checks if the help.tree is well formed XML. // Apparently there have been cases when translations contained // non-well-formed XML in the past. OUString aTreeFileURL = OUString::Concat(aExtensionLanguageRoot) + "/help.tree"; osl::DirectoryItem aTreeFileItem; osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem ); osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize ); if( rcGet == osl::FileBase::E_None && aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None && aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) ) { sal_uInt64 ret, len = aFileStatus.getFileSize(); std::unique_ptr s(new char[ int(len) ]); // the buffer to hold the installed files osl::File aFile( aTreeFileURL ); (void)aFile.open( osl_File_OpenFlag_Read ); aFile.read( s.get(), len, ret ); aFile.close(); XML_Parser parser = XML_ParserCreate( nullptr ); XML_Status parsed = XML_Parse( parser, s.get(), int( len ), true ); if (XML_STATUS_ERROR == parsed) { XML_Error nError = XML_GetErrorCode( parser ); o_rHelpProcessingErrorInfo.m_eErrorClass = HelpProcessingErrorClass::XmlParsing; o_rHelpProcessingErrorInfo.m_aErrorMsg = OUString::createFromAscii( XML_ErrorString( nError ) ); o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL; // CRASHES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser ); bSuccess = false; } XML_ParserFree( parser ); } return bSuccess; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */