/* -*- 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 #include #include #include #include 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 ); } std::string getEncodedPath( const std::string& Path ) { OString aOStr_Path( Path.c_str() ); OUString aOUStr_Path( OStringToOUString ( aOStr_Path, fs::getThreadTextEncoding() ) ); OUString aPathURL; osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL ); OString aOStr_PathURL( OUStringToOString ( aPathURL, fs::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; #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP FILE* pFile_docURL = _wfopen( fsCaptionPureTextFile_docURL.native_file_string_w(), L"w" ); #else FILE* pFile_docURL = fopen( fsCaptionPureTextFile_docURL.native_file_string().c_str(), "w" ); #endif if( pFile_docURL ) { fprintf( pFile_docURL, "%s\n", pResNodeCaption->content ); fclose( pFile_docURL ); } } xmlFreeDoc(resCaption); } if( m_xsltStylesheetPtrContent ) { xmlDocPtr resContent = xsltApplyStylesheet( m_xsltStylesheetPtrContent, doc, nullptr ); xmlNodePtr pResNodeContent = resContent->xmlChildrenNode; if( pResNodeContent ) { fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL; #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP FILE* pFile_docURL = _wfopen( fsContentPureTextFile_docURL.native_file_string_w(), L"w" ); #else FILE* pFile_docURL = fopen( fsContentPureTextFile_docURL.native_file_string().c_str(), "w" ); #endif if( pFile_docURL ) { fprintf( pFile_docURL, "%s\n", pResNodeContent->content ); fclose( pFile_docURL ); } } xmlFreeDoc(resContent); } } struct Data { std::vector _idList; typedef std::vector::const_iterator cIter; void append(const std::string &id) { _idList.push_back(id); } std::string getString() const { std::string ret; cIter aEnd = _idList.end(); for (cIter aIter = _idList.begin(); aIter != aEnd; ++aIter) ret += *aIter + ";"; return ret; } }; void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr ) { if( pFile == nullptr ) return; char 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"); } 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 ) { #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP FILE* pFile = _wfopen( rFileName.native_file_string_w(), L"wb" ); #else FILE* pFile = fopen( rFileName.native_file_string().c_str(), "wb" ); #endif if( pFile == nullptr ) return; DataHashtable::const_iterator aEnd = _hash.end(); for (DataHashtable::const_iterator aIter = _hash.begin(); aIter != aEnd; ++aIter) writeKeyValue_DBHelp( pFile, aIter->first, aIter->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 std::string appl = mod; if (appl[0] == 's') appl = appl.substr(1); bool bUse_ = true; if( !bExtensionMode ) bUse_ = false; fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht"))); #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP FILE* pFileHelpText_DBHelp = _wfopen ( helpTextFileName_DBHelp.native_file_string_w(), L"wb" ); #else FILE* pFileHelpText_DBHelp = fopen ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" ); #endif fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db"))); #ifdef _WIN32 //We need _wfopen to support long file paths on Windows XP FILE* pFileDbBase_DBHelp = _wfopen ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" ); #else FILE* pFileDbBase_DBHelp = fopen ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" ); #endif 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. HashSet::iterator end = helpFiles.end(); for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter) { // process one file // streamTable contains the streams in the hzip file StreamTable streamTable; const std::string &xhpFileName = *iter; 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, xhpFile, langsourceRoot, zipdir, compactStylesheet, embeddStylesheet, module, lang, bExtensionMode ); HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl); bool success = hc.compile(); HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl); if (!success && !bExtensionMode) { std::stringstream aStrStream; aStrStream << "\nERROR: compiling help particle '" << xhpFileName << "' for language '" << lang << "' failed!"; throw HelpProcessingException( HelpProcessingErrorClass::General, aStrStream.str() ); } 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 HashSet *hidlist = streamTable.appl_hidlist; if (hidlist && !hidlist->empty()) { // now iterate over all elements of the hidlist HashSet::const_iterator aEnd = hidlist->end(); for (HashSet::const_iterator hidListIter = hidlist->begin(); hidListIter != aEnd; ++hidListIter) { std::string thishid = *hidListIter; 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; if (anchorToLL && !anchorToLL->empty()) { std::string fakedHid = URLEncoder::encode(documentPath); Hashtable::const_iterator aEnd = anchorToLL->end(); for (Hashtable::const_iterator enumer = anchorToLL->begin(); enumer != aEnd; ++enumer) { const std::string &anchor = enumer->first; addBookmark(pFileDbBase_DBHelp, documentPath, fileB, anchor, jarfileB, titleB); std::string totalId = fakedHid + "#" + anchor; // std::cerr << hzipFileName << std::endl; const LinkedList& ll = enumer->second; LinkedList::const_iterator aOtherEnd = ll.end(); for (LinkedList::const_iterator llIter = ll.begin(); llIter != aOtherEnd; ++llIter) { helpKeyword.insert(*llIter, totalId); } } } // and last the helptexts const Stringtable *helpTextHash = streamTable.appl_helptexts; if (helpTextHash && !helpTextHash->empty()) { Stringtable::const_iterator aEnd = helpTextHash->end(); for (Stringtable::const_iterator helpTextIter = helpTextHash->begin(); helpTextIter != aEnd; ++helpTextIter) { std::string helpTextId = helpTextIter->first; const std::string& helpTextText = helpTextIter->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 ) { // New index Stringtable::iterator aEnd = additionalFiles.end(); for (Stringtable::iterator enumer = additionalFiles.begin(); enumer != aEnd; ++enumer) { const std::string &additionalFileName = enumer->second; const std::string &additionalFileKey = enumer->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* pExtensionPath, std::string* 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 ); aIdxCaptionPathFileURL += "/idxcaption.xsl"; OString aOStr_IdxCaptionPathFileURL( OUStringToOString ( aIdxCaptionPathFileURL, fs::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 ); aIdxContentPathFileURL += "/idxcontent.xsl"; OString aOStr_IdxContentPathFileURL( OUStringToOString ( aIdxContentPathFileURL, fs::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" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error) { (void)userData; (void)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; OString tmpErrorMsg( e.m_aErrorMsg.c_str() ); m_aErrorMsg = OStringToOUString( tmpErrorMsg, fs::getThreadTextEncoding() ); OString tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() ); m_aXMLParsingFile = OStringToOUString( tmpXMLParsingFile, fs::getThreadTextEncoding() ); m_nXMLParsingLine = e.m_nXMLParsingLine; return *this; } // Returns true in case of success, false in case of error bool compileExtensionHelp ( const OUString& aOfficeHelpPath, const OUString& aExtensionName, const OUString& aExtensionLanguageRoot, sal_Int32 nXhpFileCount, const OUString* pXhpFiles, const OUString& aDestination, HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo ) { bool bSuccess = true; std::vector args; args.reserve(nXhpFileCount + 2); args.push_back(std::string("-mod")); OString aOExtensionName = OUStringToOString( aExtensionName, fs::getThreadTextEncoding() ); args.push_back(std::string(aOExtensionName.getStr())); for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp ) { OUString aXhpFile = pXhpFiles[iXhp]; OString aOXhpFile = OUStringToOString( aXhpFile, fs::getThreadTextEncoding() ); args.push_back(std::string(aOXhpFile.getStr())); } OString aOExtensionLanguageRoot = OUStringToOString( aExtensionLanguageRoot, fs::getThreadTextEncoding() ); const char* pExtensionPath = aOExtensionLanguageRoot.getStr(); std::string aStdStrExtensionPath = pExtensionPath; OString aODestination = OUStringToOString(aDestination, fs::getThreadTextEncoding()); const char* pDestination = aODestination.getStr(); std::string aStdStrDestination = pDestination; // Set error handler xmlSetStructuredErrorFunc( nullptr, StructuredXMLErrorFunction ); try { std::unique_ptr pHelpLinker(new HelpLinker()); pHelpLinker->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 = 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 ); 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: */