update credits
[LibreOffice.git] / helpcompiler / source / HelpLinker.cxx
blobb50b2ce301f3a8520e5a251621dceec11070ff5d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <HelpCompiler.hxx>
21 #include <HelpLinker.hxx>
23 #include <map>
25 #include <string.h>
26 #include <limits.h>
28 #include <libxslt/xslt.h>
29 #include <libxslt/xsltutils.h>
30 #include <libxslt/functions.h>
31 #include <libxslt/extensions.h>
33 #include <sal/main.h>
34 #include <sal/types.h>
35 #include <osl/time.h>
36 #include <rtl/bootstrap.hxx>
38 #include <expat.h>
40 IndexerPreProcessor::IndexerPreProcessor
41 ( const std::string& aModuleName, const fs::path& fsIndexBaseDir,
42 const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet )
43 : m_aModuleName( aModuleName )
44 , m_fsIndexBaseDir( fsIndexBaseDir )
46 m_fsCaptionFilesDirName = fsIndexBaseDir / "caption";
47 fs::create_directory( m_fsCaptionFilesDirName );
49 m_fsContentFilesDirName = fsIndexBaseDir / "content";
50 fs::create_directory( m_fsContentFilesDirName );
52 m_xsltStylesheetPtrCaption = xsltParseStylesheetFile
53 ((const xmlChar *)idxCaptionStylesheet.native_file_string().c_str());
54 m_xsltStylesheetPtrContent = xsltParseStylesheetFile
55 ((const xmlChar *)idxContentStylesheet.native_file_string().c_str());
58 IndexerPreProcessor::~IndexerPreProcessor()
60 if( m_xsltStylesheetPtrCaption )
61 xsltFreeStylesheet( m_xsltStylesheetPtrCaption );
62 if( m_xsltStylesheetPtrContent )
63 xsltFreeStylesheet( m_xsltStylesheetPtrContent );
66 std::string getEncodedPath( const std::string& Path )
68 OString aOStr_Path( Path.c_str() );
69 OUString aOUStr_Path( OStringToOUString
70 ( aOStr_Path, fs::getThreadTextEncoding() ) );
71 OUString aPathURL;
72 osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL );
73 OString aOStr_PathURL( OUStringToOString
74 ( aPathURL, fs::getThreadTextEncoding() ) );
75 std::string aStdStr_PathURL( aOStr_PathURL.getStr() );
76 return aStdStr_PathURL;
79 void IndexerPreProcessor::processDocument
80 ( xmlDocPtr doc, const std::string &EncodedDocPath )
82 std::string aStdStr_EncodedDocPathURL = getEncodedPath( EncodedDocPath );
84 if( m_xsltStylesheetPtrCaption )
86 xmlDocPtr resCaption = xsltApplyStylesheet( m_xsltStylesheetPtrCaption, doc, NULL );
87 xmlNodePtr pResNodeCaption = resCaption->xmlChildrenNode;
88 if( pResNodeCaption )
90 fs::path fsCaptionPureTextFile_docURL = m_fsCaptionFilesDirName / aStdStr_EncodedDocPathURL;
91 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
92 FILE* pFile_docURL = _wfopen(
93 fsCaptionPureTextFile_docURL.native_file_string_w(), L"w" );
94 #else
95 FILE* pFile_docURL = fopen(
96 fsCaptionPureTextFile_docURL.native_file_string().c_str(), "w" );
97 #endif
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, NULL );
110 xmlNodePtr pResNodeContent = resContent->xmlChildrenNode;
111 if( pResNodeContent )
113 fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL;
114 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
115 FILE* pFile_docURL = _wfopen(
116 fsContentPureTextFile_docURL.native_file_string_w(), L"w" );
117 #else
118 FILE* pFile_docURL = fopen(
119 fsContentPureTextFile_docURL.native_file_string().c_str(), "w" );
120 #endif
121 if( pFile_docURL )
123 fprintf( pFile_docURL, "%s\n", pResNodeContent->content );
124 fclose( pFile_docURL );
127 xmlFreeDoc(resContent);
131 struct Data
133 std::vector<std::string> _idList;
134 typedef std::vector<std::string>::const_iterator cIter;
136 void append(const std::string &id)
138 _idList.push_back(id);
141 std::string getString() const
143 std::string ret;
144 cIter aEnd = _idList.end();
145 for (cIter aIter = _idList.begin(); aIter != aEnd; ++aIter)
146 ret += *aIter + ";";
147 return ret;
151 void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr )
153 if( pFile == NULL )
154 return;
155 char cLF = 10;
156 unsigned int nKeyLen = aKeyStr.length();
157 unsigned int nValueLen = aValueStr.length();
158 fprintf( pFile, "%x ", nKeyLen );
159 if( nKeyLen > 0 )
161 if (fwrite( aKeyStr.c_str(), 1, nKeyLen, pFile ) != nKeyLen)
162 fprintf(stderr, "fwrite to db failed\n");
164 if (fprintf( pFile, " %x ", nValueLen ) < 0)
165 fprintf(stderr, "fwrite to db failed\n");
166 if( nValueLen > 0 )
168 if (fwrite( aValueStr.c_str(), 1, nValueLen, pFile ) != nValueLen)
169 fprintf(stderr, "fwrite to db failed\n");
171 if (fprintf( pFile, "%c", cLF ) < 0)
172 fprintf(stderr, "fwrite to db failed\n");
175 class HelpKeyword
177 private:
178 typedef boost::unordered_map<std::string, Data, pref_hash> DataHashtable;
179 DataHashtable _hash;
181 public:
182 void insert(const std::string &key, const std::string &id)
184 Data &data = _hash[key];
185 data.append(id);
188 void dump_DBHelp( const fs::path& rFileName )
190 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
191 FILE* pFile = _wfopen( rFileName.native_file_string_w(), L"wb" );
192 #else
193 FILE* pFile = fopen( rFileName.native_file_string().c_str(), "wb" );
194 #endif
195 if( pFile == NULL )
196 return;
198 DataHashtable::const_iterator aEnd = _hash.end();
199 for (DataHashtable::const_iterator aIter = _hash.begin(); aIter != aEnd; ++aIter)
200 writeKeyValue_DBHelp( pFile, aIter->first, aIter->second.getString() );
202 fclose( pFile );
206 namespace URLEncoder
208 static std::string encode(const std::string &rIn)
210 const char *good = "!$&'()*+,-.=@_";
211 static const char hex[17] = "0123456789ABCDEF";
213 std::string result;
214 for (size_t i=0; i < rIn.length(); ++i)
216 unsigned char c = rIn[i];
217 if (isalnum (c) || strchr (good, c))
218 result += c;
219 else {
220 result += '%';
221 result += hex[c >> 4];
222 result += hex[c & 0xf];
225 return result;
229 void HelpLinker::addBookmark( FILE* pFile_DBHelp, std::string thishid,
230 const std::string& fileB, const std::string& anchorB,
231 const std::string& jarfileB, const std::string& titleB)
233 HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " <<
234 fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl);
236 thishid = URLEncoder::encode(thishid);
238 int fileLen = fileB.length();
239 if (!anchorB.empty())
240 fileLen += (1 + anchorB.length());
241 int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length();
243 std::vector<unsigned char> dataB(dataLen);
244 size_t i = 0;
245 dataB[i++] = static_cast<unsigned char>(fileLen);
246 for (size_t j = 0; j < fileB.length(); ++j)
247 dataB[i++] = static_cast<unsigned char>(fileB[j]);
248 if (!anchorB.empty())
250 dataB[i++] = '#';
251 for (size_t j = 0; j < anchorB.length(); ++j)
252 dataB[i++] = anchorB[j];
254 dataB[i++] = static_cast<unsigned char>(jarfileB.length());
255 for (size_t j = 0; j < jarfileB.length(); ++j)
256 dataB[i++] = jarfileB[j];
258 dataB[i++] = static_cast<unsigned char>(titleB.length());
259 for (size_t j = 0; j < titleB.length(); ++j)
260 dataB[i++] = titleB[j];
262 if( pFile_DBHelp != NULL )
264 std::string aValueStr( dataB.begin(), dataB.end() );
265 writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr );
269 void HelpLinker::initIndexerPreProcessor()
271 if( m_pIndexerPreProcessor )
272 delete m_pIndexerPreProcessor;
273 std::string mod = module;
274 std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
275 m_pIndexerPreProcessor = new IndexerPreProcessor( mod, indexDirParentName,
276 idxCaptionStylesheet, idxContentStylesheet );
282 void HelpLinker::link() throw( HelpProcessingException )
284 bool bIndexForExtension = true;
286 if( bExtensionMode )
288 indexDirParentName = extensionDestination;
290 else
292 indexDirParentName = zipdir;
293 fs::create_directory(indexDirParentName);
296 std::string mod = module;
297 std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
299 // do the work here
300 // continue with introduction of the overall process thing into the
301 // here all hzip files will be worked on
302 std::string appl = mod;
303 if (appl[0] == 's')
304 appl = appl.substr(1);
306 bool bUse_ = true;
307 if( !bExtensionMode )
308 bUse_ = false;
310 fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht")));
311 #ifdef WNT
312 //We need _wfopen to support long file paths on Windows XP
313 FILE* pFileHelpText_DBHelp = _wfopen
314 ( helpTextFileName_DBHelp.native_file_string_w(), L"wb" );
315 #else
317 FILE* pFileHelpText_DBHelp = fopen
318 ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" );
319 #endif
321 fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
322 #ifdef WNT
323 //We need _wfopen to support long file paths on Windows XP
324 FILE* pFileDbBase_DBHelp = _wfopen
325 ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" );
326 #else
327 FILE* pFileDbBase_DBHelp = fopen
328 ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" );
329 #endif
331 fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
333 HelpKeyword helpKeyword;
335 // catch HelpProcessingException to avoid locking data bases
339 // lastly, initialize the indexBuilder
340 if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
341 initIndexerPreProcessor();
343 // here we start our loop over the hzip files.
344 HashSet::iterator end = helpFiles.end();
345 for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter)
347 // process one file
348 // streamTable contains the streams in the hzip file
349 StreamTable streamTable;
350 const std::string &xhpFileName = *iter;
352 if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
354 // only work on .xhp - files
355 SAL_WARN("helpcompiler",
356 "ERROR: input list entry '"
357 << xhpFileName
358 << "' has the wrong extension (only files with extension .xhp "
359 << "are accepted)");
361 continue;
364 fs::path langsourceRoot(sourceRoot);
365 fs::path xhpFile;
367 if( bExtensionMode )
369 // langsourceRoot == sourceRoot for extensions
370 std::string xhpFileNameComplete( extensionPath );
371 xhpFileNameComplete.append( '/' + xhpFileName );
372 xhpFile = fs::path( xhpFileNameComplete );
374 else
376 langsourceRoot.append( "/" );
377 if ( m_bUseLangRoot )
378 langsourceRoot.append( lang + '/' );
379 xhpFile = fs::path(xhpFileName, fs::native);
382 HelpCompiler hc( streamTable, xhpFile, langsourceRoot, zipdir,
383 compactStylesheet, embeddStylesheet, module, lang, bExtensionMode );
385 HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl);
386 bool success = hc.compile();
387 HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl);
389 if (!success && !bExtensionMode)
391 std::stringstream aStrStream;
392 aStrStream <<
393 "\nERROR: compiling help particle '"
394 << xhpFileName
395 << "' for language '"
396 << lang
397 << "' failed!";
398 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
401 if (!m_bCreateIndex)
402 continue;
404 std::string documentPath = streamTable.document_path;
405 if (documentPath.find("/") == 0)
406 documentPath = documentPath.substr(1);
408 std::string documentJarfile = streamTable.document_module + ".jar";
410 std::string documentTitle = streamTable.document_title;
411 if (documentTitle.empty())
412 documentTitle = "<notitle>";
414 const std::string& fileB = documentPath;
415 const std::string& jarfileB = documentJarfile;
416 std::string& titleB = documentTitle;
418 // add once this as its own id.
419 addBookmark( pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB);
421 const HashSet *hidlist = streamTable.appl_hidlist;
422 if (!hidlist)
423 hidlist = streamTable.default_hidlist;
424 if (hidlist && !hidlist->empty())
426 // now iterate over all elements of the hidlist
427 HashSet::const_iterator aEnd = hidlist->end();
428 for (HashSet::const_iterator hidListIter = hidlist->begin();
429 hidListIter != aEnd; ++hidListIter)
431 std::string thishid = *hidListIter;
433 std::string anchorB;
434 size_t index = thishid.rfind('#');
435 if (index != std::string::npos)
437 anchorB = thishid.substr(1 + index);
438 thishid = thishid.substr(0, index);
440 addBookmark( pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB);
444 // now the keywords
445 const Hashtable *anchorToLL = streamTable.appl_keywords;
446 if (!anchorToLL)
447 anchorToLL = streamTable.default_keywords;
448 if (anchorToLL && !anchorToLL->empty())
450 std::string fakedHid = URLEncoder::encode(documentPath);
451 Hashtable::const_iterator aEnd = anchorToLL->end();
452 for (Hashtable::const_iterator enumer = anchorToLL->begin();
453 enumer != aEnd; ++enumer)
455 const std::string &anchor = enumer->first;
456 addBookmark(pFileDbBase_DBHelp, documentPath, fileB,
457 anchor, jarfileB, titleB);
458 std::string totalId = fakedHid + "#" + anchor;
459 // std::cerr << hzipFileName << std::endl;
460 const LinkedList& ll = enumer->second;
461 LinkedList::const_iterator aOtherEnd = ll.end();
462 for (LinkedList::const_iterator llIter = ll.begin();
463 llIter != aOtherEnd; ++llIter)
465 helpKeyword.insert(*llIter, totalId);
471 // and last the helptexts
472 const Stringtable *helpTextHash = streamTable.appl_helptexts;
473 if (!helpTextHash)
474 helpTextHash = streamTable.default_helptexts;
475 if (helpTextHash && !helpTextHash->empty())
477 Stringtable::const_iterator aEnd = helpTextHash->end();
478 for (Stringtable::const_iterator helpTextIter = helpTextHash->begin();
479 helpTextIter != aEnd; ++helpTextIter)
481 std::string helpTextId = helpTextIter->first;
482 const std::string& helpTextText = helpTextIter->second;
484 helpTextId = URLEncoder::encode(helpTextId);
486 if( pFileHelpText_DBHelp != NULL )
487 writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText );
491 //IndexerPreProcessor
492 if( !bExtensionMode || bIndexForExtension )
494 // now the indexing
495 xmlDocPtr document = streamTable.appl_doc;
496 if (!document)
497 document = streamTable.default_doc;
498 if (document)
500 std::string temp = module;
501 std::transform (temp.begin(), temp.end(), temp.begin(), tocharlower);
502 m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) );
506 } // while loop over hzip files ending
508 } // try
509 catch( const HelpProcessingException& )
511 // catch HelpProcessingException to avoid locking data bases
512 if( pFileHelpText_DBHelp != NULL )
513 fclose( pFileHelpText_DBHelp );
514 if( pFileDbBase_DBHelp != NULL )
515 fclose( pFileDbBase_DBHelp );
516 throw;
519 if( pFileHelpText_DBHelp != NULL )
520 fclose( pFileHelpText_DBHelp );
521 if( pFileDbBase_DBHelp != NULL )
522 fclose( pFileDbBase_DBHelp );
524 helpKeyword.dump_DBHelp( keyWordFileName_DBHelp);
526 if( !bExtensionMode )
528 // New index
529 Stringtable::iterator aEnd = additionalFiles.end();
530 for (Stringtable::iterator enumer = additionalFiles.begin(); enumer != aEnd;
531 ++enumer)
533 const std::string &additionalFileName = enumer->second;
534 const std::string &additionalFileKey = enumer->first;
536 fs::path fsAdditionalFileName( additionalFileName, fs::native );
537 HCDBG({
538 std::string aNativeStr = fsAdditionalFileName.native_file_string();
539 const char* pStr = aNativeStr.c_str();
540 std::cerr << pStr << std::endl;
543 fs::path fsTargetName( indexDirParentName / additionalFileKey );
545 fs::copy( fsAdditionalFileName, fsTargetName );
551 void HelpLinker::main( std::vector<std::string> &args,
552 std::string* pExtensionPath, std::string* pDestination,
553 const OUString* pOfficeHelpPath )
554 throw( HelpProcessingException )
556 bExtensionMode = false;
557 helpFiles.clear();
559 if ((!args.empty()) && args[0][0] == '@')
561 std::vector<std::string> stringList;
562 std::ifstream fileReader(args[0].substr(1).c_str());
564 while (fileReader)
566 std::string token;
567 fileReader >> token;
568 if (!token.empty())
569 stringList.push_back(token);
571 fileReader.close();
573 args = stringList;
576 size_t i = 0;
577 bool bSrcOption = false;
578 while (i < args.size())
580 if (args[i].compare("-extlangsrc") == 0)
582 ++i;
583 if (i >= args.size())
585 std::stringstream aStrStream;
586 aStrStream << "extension source missing" << std::endl;
587 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
589 extsource = args[i];
591 else if (args[i].compare("-extlangdest") == 0)
593 //If this argument is not provided then the location provided in -extsource will
594 //also be the destination
595 ++i;
596 if (i >= args.size())
598 std::stringstream aStrStream;
599 aStrStream << "extension destination missing" << std::endl;
600 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
602 extdestination = args[i];
604 else if (args[i].compare("-src") == 0)
606 ++i;
607 if (i >= args.size())
609 std::stringstream aStrStream;
610 aStrStream << "sourceroot missing" << std::endl;
611 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
613 bSrcOption = true;
614 sourceRoot = fs::path(args[i], fs::native);
616 else if (args[i].compare("-compact") == 0)
618 ++i;
619 if (i >= args.size())
621 std::stringstream aStrStream;
622 aStrStream << "compactStylesheet missing" << std::endl;
623 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
626 compactStylesheet = fs::path(args[i], fs::native);
628 else if (args[i].compare("-sty") == 0)
630 ++i;
631 if (i >= args.size())
633 std::stringstream aStrStream;
634 aStrStream << "embeddingStylesheet missing" << std::endl;
635 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
638 embeddStylesheet = fs::path(args[i], fs::native);
640 else if (args[i].compare("-zipdir") == 0)
642 ++i;
643 if (i >= args.size())
645 std::stringstream aStrStream;
646 aStrStream << "idxtemp missing" << std::endl;
647 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
650 zipdir = fs::path(args[i], fs::native);
652 else if (args[i].compare("-idxcaption") == 0)
654 ++i;
655 if (i >= args.size())
657 std::stringstream aStrStream;
658 aStrStream << "idxcaption stylesheet missing" << std::endl;
659 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
662 idxCaptionStylesheet = fs::path(args[i], fs::native);
664 else if (args[i].compare("-idxcontent") == 0)
666 ++i;
667 if (i >= args.size())
669 std::stringstream aStrStream;
670 aStrStream << "idxcontent stylesheet missing" << std::endl;
671 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
674 idxContentStylesheet = fs::path(args[i], fs::native);
676 else if (args[i].compare("-o") == 0)
678 ++i;
679 if (i >= args.size())
681 std::stringstream aStrStream;
682 aStrStream << "outputfilename missing" << std::endl;
683 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
686 outputFile = fs::path(args[i], fs::native);
688 else if (args[i].compare("-mod") == 0)
690 ++i;
691 if (i >= args.size())
693 std::stringstream aStrStream;
694 aStrStream << "module name missing" << std::endl;
695 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
698 module = args[i];
700 else if (args[i].compare("-lang") == 0)
702 ++i;
703 if (i >= args.size())
705 std::stringstream aStrStream;
706 aStrStream << "language name missing" << std::endl;
707 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
710 lang = args[i];
712 else if (args[i].compare("-hid") == 0)
714 ++i;
715 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, "obsolete -hid argument used" );
717 else if (args[i].compare("-add") == 0)
719 std::string addFile, addFileUnderPath;
720 ++i;
721 if (i >= args.size())
723 std::stringstream aStrStream;
724 aStrStream << "pathname missing" << std::endl;
725 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
728 addFileUnderPath = args[i];
729 ++i;
730 if (i >= args.size())
732 std::stringstream aStrStream;
733 aStrStream << "pathname missing" << std::endl;
734 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
736 addFile = args[i];
737 if (!addFileUnderPath.empty() && !addFile.empty())
738 additionalFiles[addFileUnderPath] = addFile;
740 else if (args[i].compare("-nolangroot") == 0)
741 m_bUseLangRoot = false;
742 else if (args[i].compare("-noindex") == 0)
743 m_bCreateIndex = false;
744 else
745 helpFiles.push_back(args[i]);
746 ++i;
749 //We can be called from the helplinker executable or the extension manager
750 //In the latter case extsource is not used.
751 if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath)
752 || !extsource.empty())
754 bExtensionMode = true;
755 if (!extsource.empty())
757 //called from helplinker.exe, pExtensionPath and pOfficeHelpPath
758 //should be NULL
759 sourceRoot = fs::path(extsource, fs::native);
760 extensionPath = sourceRoot.toUTF8();
762 if (extdestination.empty())
764 std::stringstream aStrStream;
765 aStrStream << "-extlangdest is missing" << std::endl;
766 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
768 else
770 //Convert from system path to file URL!!!
771 fs::path p(extdestination, fs::native);
772 extensionDestination = p.toUTF8();
775 else
776 { //called from extension manager
777 extensionPath = *pExtensionPath;
778 sourceRoot = fs::path(extensionPath);
779 extensionDestination = *pDestination;
781 //check if -src option was used. This option must not be used
782 //when extension help is compiled.
783 if (bSrcOption)
785 std::stringstream aStrStream;
786 aStrStream << "-src must not be used together with -extsource missing" << std::endl;
787 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
791 if (!bExtensionMode && zipdir.empty())
793 std::stringstream aStrStream;
794 aStrStream << "no index dir given" << std::endl;
795 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
798 if ( (!bExtensionMode && idxCaptionStylesheet.empty())
799 || (!extsource.empty() && idxCaptionStylesheet.empty()) )
801 //No extension mode and extension mode using commandline
802 //!extsource.empty indicates extension mode using commandline
803 // -idxcaption paramter is required
804 std::stringstream aStrStream;
805 aStrStream << "no index caption stylesheet given" << std::endl;
806 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
808 else if ( bExtensionMode && extsource.empty())
810 //This part is used when compileExtensionHelp is called from the extensions manager.
811 //If extension help is compiled using helplinker in the build process
812 OUString aIdxCaptionPathFileURL( *pOfficeHelpPath );
813 aIdxCaptionPathFileURL += OUString("/idxcaption.xsl");
815 OString aOStr_IdxCaptionPathFileURL( OUStringToOString
816 ( aIdxCaptionPathFileURL, fs::getThreadTextEncoding() ) );
817 std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() );
819 idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL );
822 if ( (!bExtensionMode && idxContentStylesheet.empty())
823 || (!extsource.empty() && idxContentStylesheet.empty()) )
825 //No extension mode and extension mode using commandline
826 //!extsource.empty indicates extension mode using commandline
827 // -idxcontent paramter is required
828 std::stringstream aStrStream;
829 aStrStream << "no index content stylesheet given" << std::endl;
830 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
832 else if ( bExtensionMode && extsource.empty())
834 //If extension help is compiled using helplinker in the build process
835 //then -idxcontent must be supplied
836 //This part is used when compileExtensionHelp is called from the extensions manager.
837 OUString aIdxContentPathFileURL( *pOfficeHelpPath );
838 aIdxContentPathFileURL += OUString("/idxcontent.xsl");
840 OString aOStr_IdxContentPathFileURL( OUStringToOString
841 ( aIdxContentPathFileURL, fs::getThreadTextEncoding() ) );
842 std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() );
844 idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL );
846 if (!bExtensionMode && embeddStylesheet.empty())
848 std::stringstream aStrStream;
849 aStrStream << "no embedding resolving file given" << std::endl;
850 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
852 if (sourceRoot.empty())
854 std::stringstream aStrStream;
855 aStrStream << "no sourceroot given" << std::endl;
856 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
858 if (!bExtensionMode && outputFile.empty())
860 std::stringstream aStrStream;
861 aStrStream << "no output file given" << std::endl;
862 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
864 if (module.empty())
866 std::stringstream aStrStream;
867 aStrStream << "module missing" << std::endl;
868 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
870 if (!bExtensionMode && lang.empty())
872 std::stringstream aStrStream;
873 aStrStream << "language missing" << std::endl;
874 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
876 link();
879 // Variable to set an exception in "C" StructuredXMLErrorFunction
880 static const HelpProcessingException* GpXMLParsingException = NULL;
882 extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
884 (void)userData;
885 (void)error;
887 std::string aErrorMsg = error->message;
888 std::string aXMLParsingFile;
889 if( error->file != NULL )
890 aXMLParsingFile = error->file;
891 int nXMLParsingLine = error->line;
892 HelpProcessingException* pException = new HelpProcessingException( aErrorMsg, aXMLParsingFile, nXMLParsingLine );
893 GpXMLParsingException = pException;
895 // Reset error handler
896 xmlSetStructuredErrorFunc( NULL, NULL );
899 HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e )
901 m_eErrorClass = e.m_eErrorClass;
902 OString tmpErrorMsg( e.m_aErrorMsg.c_str() );
903 m_aErrorMsg = OStringToOUString( tmpErrorMsg, fs::getThreadTextEncoding() );
904 OString tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() );
905 m_aXMLParsingFile = OStringToOUString( tmpXMLParsingFile, fs::getThreadTextEncoding() );
906 m_nXMLParsingLine = e.m_nXMLParsingLine;
907 return *this;
911 // Returns true in case of success, false in case of error
912 HELPLINKER_DLLPUBLIC bool compileExtensionHelp
914 const OUString& aOfficeHelpPath,
915 const OUString& aExtensionName,
916 const OUString& aExtensionLanguageRoot,
917 sal_Int32 nXhpFileCount, const OUString* pXhpFiles,
918 const OUString& aDestination,
919 HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo
922 bool bSuccess = true;
924 std::vector<std::string> args;
925 args.reserve(nXhpFileCount + 2);
926 args.push_back(std::string("-mod"));
927 OString aOExtensionName = OUStringToOString( aExtensionName, fs::getThreadTextEncoding() );
928 args.push_back(std::string(aOExtensionName.getStr()));
930 for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
932 OUString aXhpFile = pXhpFiles[iXhp];
934 OString aOXhpFile = OUStringToOString( aXhpFile, fs::getThreadTextEncoding() );
935 args.push_back(std::string(aOXhpFile.getStr()));
938 OString aOExtensionLanguageRoot = OUStringToOString( aExtensionLanguageRoot, fs::getThreadTextEncoding() );
939 const char* pExtensionPath = aOExtensionLanguageRoot.getStr();
940 std::string aStdStrExtensionPath = pExtensionPath;
941 OString aODestination = OUStringToOString(aDestination, fs::getThreadTextEncoding());
942 const char* pDestination = aODestination.getStr();
943 std::string aStdStrDestination = pDestination;
945 // Set error handler
946 xmlSetStructuredErrorFunc( NULL, (xmlStructuredErrorFunc)StructuredXMLErrorFunction );
949 HelpLinker* pHelpLinker = new HelpLinker();
950 pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
951 delete pHelpLinker;
953 catch( const HelpProcessingException& e )
955 if( GpXMLParsingException != NULL )
957 o_rHelpProcessingErrorInfo = *GpXMLParsingException;
958 delete GpXMLParsingException;
959 GpXMLParsingException = NULL;
961 else
963 o_rHelpProcessingErrorInfo = e;
965 bSuccess = false;
967 // Reset error handler
968 xmlSetStructuredErrorFunc( NULL, NULL );
970 // i83624: Tree files
971 // The following basically checks if the help.tree is well formed XML.
972 // Apparently there have been cases when translations contained
973 // non-well-formed XML in the past.
974 OUString aTreeFileURL = aExtensionLanguageRoot;
975 aTreeFileURL += OUString("/help.tree");
976 osl::DirectoryItem aTreeFileItem;
977 osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
978 osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
979 if( rcGet == osl::FileBase::E_None &&
980 aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
981 aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) )
983 sal_uInt64 ret, len = aFileStatus.getFileSize();
984 char* s = new char[ int(len) ]; // the buffer to hold the installed files
985 osl::File aFile( aTreeFileURL );
986 aFile.open( osl_File_OpenFlag_Read );
987 aFile.read( s, len, ret );
988 aFile.close();
990 XML_Parser parser = XML_ParserCreate( 0 );
991 XML_Status parsed = XML_Parse( parser, s, int( len ), true );
993 if (XML_STATUS_ERROR == parsed)
995 XML_Error nError = XML_GetErrorCode( parser );
996 o_rHelpProcessingErrorInfo.m_eErrorClass = HELPPROCESSING_XMLPARSING_ERROR;
997 o_rHelpProcessingErrorInfo.m_aErrorMsg = OUString::createFromAscii( XML_ErrorString( nError ) );;
998 o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL;
999 // CRAHSES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser );
1000 bSuccess = false;
1003 XML_ParserFree( parser );
1004 delete[] s;
1007 return bSuccess;
1010 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */