office-gobmx/helpcompiler/source/HelpLinker.cxx
Julien Nabet 7b11cda2aa Revert "Simplify vector initialization in helpcompiler"
since it needs 2 allocations instead of one

This reverts commit cd5ee8c7d3.

Change-Id: I4958cacf747291c923b74fdfa49679e831d0ef1b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124070
Tested-by: Jenkins
Reviewed-by: Julien Nabet <serval2412@yahoo.fr>
2021-10-22 19:30:32 +02:00

942 lines
32 KiB
C++

/* -*- 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 <HelpCompiler.hxx>
#include <HelpLinker.hxx>
#include <algorithm>
#include <fstream>
#include <string.h>
#include <libxslt/transform.h>
#include <sal/types.h>
#include <o3tl/char16_t2wchar_t.hxx>
#include <sal/log.hxx>
#include <expat.h>
#include <memory>
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<const xmlChar *>(idxCaptionStylesheet.native_file_string().c_str()));
m_xsltStylesheetPtrContent = xsltParseStylesheetFile
(reinterpret_cast<const xmlChar *>(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 )
{
OString 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<std::string> _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<std::string, Data> 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<unsigned char>(c))
|| strchr (good, c))
{
result += c;
} else {
result += '%';
result += hex[static_cast<unsigned char>(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<unsigned char> dataB(dataLen);
size_t i = 0;
dataB[i++] = static_cast<unsigned char>(fileLen);
for (char j : fileB)
dataB[i++] = static_cast<unsigned char>(j);
if (!anchorB.empty())
{
dataB[i++] = '#';
for (char j : anchorB)
dataB[i++] = j;
}
dataB[i++] = static_cast<unsigned char>(jarfileB.length());
for (char j : jarfileB)
dataB[i++] = j;
dataB[i++] = static_cast<unsigned char>(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, xhpFile, 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 = "<notitle>";
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<std::string> *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<std::string> &args,
std::string const * pExtensionPath, std::string const * pDestination,
const OUString* pOfficeHelpPath )
{
bExtensionMode = false;
helpFiles.clear();
if ((!args.empty()) && args[0][0] == '@')
{
std::vector<std::string> 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;
OString tmpErrorMsg( e.m_aErrorMsg.c_str() );
m_aErrorMsg = OStringToOUString( tmpErrorMsg, osl_getThreadTextEncoding() );
OString 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<std::string> 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<char[]> 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: */