Version 3.6.0.2, tag libreoffice-3.6.0.2
[LibreOffice.git] / l10ntools / source / help / HelpLinker.cxx
bloba95c56be1b6fd17ed77f4a181181ef19ca3a7063
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include "HelpCompiler.hxx"
30 #include "l10ntools/HelpLinker.hxx"
32 #include <map>
34 #include <string.h>
35 #include <limits.h>
37 #include <libxslt/xslt.h>
38 #include <libxslt/xsltutils.h>
39 #include <libxslt/functions.h>
40 #include <libxslt/extensions.h>
42 #include <sal/main.h>
43 #include <sal/types.h>
44 #include <osl/time.h>
45 #include <rtl/bootstrap.hxx>
47 #include <expat.h>
49 namespace lucene
51 namespace document
53 class Document;
55 namespace util
57 class Reader;
61 IndexerPreProcessor::IndexerPreProcessor
62 ( const std::string& aModuleName, const fs::path& fsIndexBaseDir,
63 const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet )
64 : m_aModuleName( aModuleName )
65 , m_fsIndexBaseDir( fsIndexBaseDir )
67 m_fsCaptionFilesDirName = fsIndexBaseDir / "caption";
68 fs::create_directory( m_fsCaptionFilesDirName );
70 m_fsContentFilesDirName = fsIndexBaseDir / "content";
71 fs::create_directory( m_fsContentFilesDirName );
73 m_xsltStylesheetPtrCaption = xsltParseStylesheetFile
74 ((const xmlChar *)idxCaptionStylesheet.native_file_string().c_str());
75 m_xsltStylesheetPtrContent = xsltParseStylesheetFile
76 ((const xmlChar *)idxContentStylesheet.native_file_string().c_str());
79 IndexerPreProcessor::~IndexerPreProcessor()
81 if( m_xsltStylesheetPtrCaption )
82 xsltFreeStylesheet( m_xsltStylesheetPtrCaption );
83 if( m_xsltStylesheetPtrContent )
84 xsltFreeStylesheet( m_xsltStylesheetPtrContent );
87 std::string getEncodedPath( const std::string& Path )
89 rtl::OString aOStr_Path( Path.c_str() );
90 rtl::OUString aOUStr_Path( rtl::OStringToOUString
91 ( aOStr_Path, fs::getThreadTextEncoding() ) );
92 rtl::OUString aPathURL;
93 osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL );
94 rtl::OString aOStr_PathURL( rtl::OUStringToOString
95 ( aPathURL, fs::getThreadTextEncoding() ) );
96 std::string aStdStr_PathURL( aOStr_PathURL.getStr() );
97 return aStdStr_PathURL;
100 void IndexerPreProcessor::processDocument
101 ( xmlDocPtr doc, const std::string &EncodedDocPath )
103 std::string aStdStr_EncodedDocPathURL = getEncodedPath( EncodedDocPath );
105 if( m_xsltStylesheetPtrCaption )
107 xmlDocPtr resCaption = xsltApplyStylesheet( m_xsltStylesheetPtrCaption, doc, NULL );
108 xmlNodePtr pResNodeCaption = resCaption->xmlChildrenNode;
109 if( pResNodeCaption )
111 fs::path fsCaptionPureTextFile_docURL = m_fsCaptionFilesDirName / aStdStr_EncodedDocPathURL;
112 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
113 FILE* pFile_docURL = _wfopen(
114 fsCaptionPureTextFile_docURL.native_file_string_w(), L"w" );
115 #else
116 FILE* pFile_docURL = fopen(
117 fsCaptionPureTextFile_docURL.native_file_string().c_str(), "w" );
118 #endif
119 if( pFile_docURL )
121 fprintf( pFile_docURL, "%s\n", pResNodeCaption->content );
122 fclose( pFile_docURL );
125 xmlFreeDoc(resCaption);
128 if( m_xsltStylesheetPtrContent )
130 xmlDocPtr resContent = xsltApplyStylesheet( m_xsltStylesheetPtrContent, doc, NULL );
131 xmlNodePtr pResNodeContent = resContent->xmlChildrenNode;
132 if( pResNodeContent )
134 fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL;
135 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
136 FILE* pFile_docURL = _wfopen(
137 fsContentPureTextFile_docURL.native_file_string_w(), L"w" );
138 #else
139 FILE* pFile_docURL = fopen(
140 fsContentPureTextFile_docURL.native_file_string().c_str(), "w" );
141 #endif
142 if( pFile_docURL )
144 fprintf( pFile_docURL, "%s\n", pResNodeContent->content );
145 fclose( pFile_docURL );
148 xmlFreeDoc(resContent);
152 struct Data
154 std::vector<std::string> _idList;
155 typedef std::vector<std::string>::const_iterator cIter;
157 void append(const std::string &id)
159 _idList.push_back(id);
162 std::string getString() const
164 std::string ret;
165 cIter aEnd = _idList.end();
166 for (cIter aIter = _idList.begin(); aIter != aEnd; ++aIter)
167 ret += *aIter + ";";
168 return ret;
172 void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr )
174 if( pFile == NULL )
175 return;
176 char cLF = 10;
177 unsigned int nKeyLen = aKeyStr.length();
178 unsigned int nValueLen = aValueStr.length();
179 fprintf( pFile, "%x ", nKeyLen );
180 if( nKeyLen > 0 )
182 if (fwrite( aKeyStr.c_str(), 1, nKeyLen, pFile ) != nKeyLen)
183 fprintf(stderr, "fwrite to db failed\n");
185 if (fprintf( pFile, " %x ", nValueLen ) < 0)
186 fprintf(stderr, "fwrite to db failed\n");
187 if( nValueLen > 0 )
189 if (fwrite( aValueStr.c_str(), 1, nValueLen, pFile ) != nValueLen)
190 fprintf(stderr, "fwrite to db failed\n");
192 if (fprintf( pFile, "%c", cLF ) < 0)
193 fprintf(stderr, "fwrite to db failed\n");
196 class HelpKeyword
198 private:
199 typedef boost::unordered_map<std::string, Data, pref_hash> DataHashtable;
200 DataHashtable _hash;
202 public:
203 void insert(const std::string &key, const std::string &id)
205 Data &data = _hash[key];
206 data.append(id);
209 void dump(DB* table)
211 DataHashtable::const_iterator aEnd = _hash.end();
212 for (DataHashtable::const_iterator aIter = _hash.begin(); aIter != aEnd; ++aIter)
214 const std::string &keystr = aIter->first;
215 DBT key;
216 memset(&key, 0, sizeof(key));
217 key.data = const_cast<char*>(keystr.c_str());
218 key.size = keystr.length();
220 const Data &data = aIter->second;
221 std::string str = data.getString();
222 DBT value;
223 memset(&value, 0, sizeof(value));
224 value.data = const_cast<char*>(str.c_str());
225 value.size = str.length();
227 table->put(table, NULL, &key, &value, 0);
231 void dump_DBHelp( const fs::path& rFileName )
233 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
234 FILE* pFile = _wfopen( rFileName.native_file_string_w(), L"wb" );
235 #else
236 FILE* pFile = fopen( rFileName.native_file_string().c_str(), "wb" );
237 #endif
238 if( pFile == NULL )
239 return;
241 DataHashtable::const_iterator aEnd = _hash.end();
242 for (DataHashtable::const_iterator aIter = _hash.begin(); aIter != aEnd; ++aIter)
243 writeKeyValue_DBHelp( pFile, aIter->first, aIter->second.getString() );
245 fclose( pFile );
249 namespace URLEncoder
251 static std::string encode(const std::string &rIn)
253 const char *good = "!$&'()*+,-.=@_";
254 static const char hex[17] = "0123456789ABCDEF";
256 std::string result;
257 for (size_t i=0; i < rIn.length(); ++i)
259 unsigned char c = rIn[i];
260 if (isalnum (c) || strchr (good, c))
261 result += c;
262 else {
263 result += '%';
264 result += hex[c >> 4];
265 result += hex[c & 0xf];
268 return result;
272 void HelpLinker::addBookmark( DB* dbBase, FILE* pFile_DBHelp, std::string thishid,
273 const std::string& fileB, const std::string& anchorB,
274 const std::string& jarfileB, const std::string& titleB)
276 HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " <<
277 fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl);
279 thishid = URLEncoder::encode(thishid);
281 DBT key;
282 memset(&key, 0, sizeof(key));
283 key.data = const_cast<char*>(thishid.c_str());
284 key.size = thishid.length();
286 int fileLen = fileB.length();
287 if (!anchorB.empty())
288 fileLen += (1 + anchorB.length());
289 int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length();
291 std::vector<unsigned char> dataB(dataLen);
292 size_t i = 0;
293 dataB[i++] = static_cast<unsigned char>(fileLen);
294 for (size_t j = 0; j < fileB.length(); ++j)
295 dataB[i++] = static_cast<unsigned char>(fileB[j]);
296 if (!anchorB.empty())
298 dataB[i++] = '#';
299 for (size_t j = 0; j < anchorB.length(); ++j)
300 dataB[i++] = anchorB[j];
302 dataB[i++] = static_cast<unsigned char>(jarfileB.length());
303 for (size_t j = 0; j < jarfileB.length(); ++j)
304 dataB[i++] = jarfileB[j];
306 dataB[i++] = static_cast<unsigned char>(titleB.length());
307 for (size_t j = 0; j < titleB.length(); ++j)
308 dataB[i++] = titleB[j];
310 DBT data;
311 memset(&data, 0, sizeof(data));
312 data.data = &dataB[0];
313 data.size = dataB.size();
315 if( dbBase != NULL )
316 dbBase->put(dbBase, NULL, &key, &data, 0);
318 if( pFile_DBHelp != NULL )
320 std::string aValueStr( dataB.begin(), dataB.end() );
321 writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr );
325 void HelpLinker::initIndexerPreProcessor()
327 if( m_pIndexerPreProcessor )
328 delete m_pIndexerPreProcessor;
329 std::string mod = module;
330 std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
331 m_pIndexerPreProcessor = new IndexerPreProcessor( mod, indexDirParentName,
332 idxCaptionStylesheet, idxContentStylesheet );
338 void HelpLinker::link() throw( HelpProcessingException )
340 bool bIndexForExtension = true;
342 if( bExtensionMode )
344 indexDirParentName = extensionDestination;
346 else
348 indexDirParentName = zipdir;
349 fs::create_directory(indexDirParentName);
352 #ifdef CMC_DEBUG
353 std::cerr << "will not delete tmpdir of " << indexDirParentName.native_file_string().c_str() << std::endl;
354 #endif
356 std::string mod = module;
357 std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
359 // do the work here
360 // continue with introduction of the overall process thing into the
361 // here all hzip files will be worked on
362 std::string appl = mod;
363 if (appl[0] == 's')
364 appl = appl.substr(1);
366 bool bUse_ = true;
367 #ifdef DBHELP_ONLY
368 if( !bExtensionMode )
369 bUse_ = false;
370 #endif
372 DB* helpText(0);
373 #ifndef DBHELP_ONLY
374 fs::path helpTextFileName(indexDirParentName / (mod + ".ht"));
375 db_create(&helpText,0,0);
376 helpText->open(helpText, NULL, helpTextFileName.native_file_string().c_str(), NULL, DB_BTREE,
377 DB_CREATE | DB_TRUNCATE, 0644);
378 #endif
380 fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht")));
381 #ifdef WNT
382 //We need _wfopen to support long file paths on Windows XP
383 FILE* pFileHelpText_DBHelp = _wfopen
384 ( helpTextFileName_DBHelp.native_file_string_w(), L"wb" );
385 #else
387 FILE* pFileHelpText_DBHelp = fopen
388 ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" );
389 #endif
390 DB* dbBase(0);
391 #ifndef DBHELP_ONLY
392 fs::path dbBaseFileName(indexDirParentName / (mod + ".db"));
393 db_create(&dbBase,0,0);
394 dbBase->open(dbBase, NULL, dbBaseFileName.native_file_string().c_str(), NULL, DB_BTREE,
395 DB_CREATE | DB_TRUNCATE, 0644);
396 #endif
398 fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
399 #ifdef WNT
400 //We need _wfopen to support long file paths on Windows XP
401 FILE* pFileDbBase_DBHelp = _wfopen
402 ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" );
403 #else
404 FILE* pFileDbBase_DBHelp = fopen
405 ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" );
406 #endif
408 #ifndef DBHELP_ONLY
409 DB* keyWord(0);
410 fs::path keyWordFileName(indexDirParentName / (mod + ".key"));
411 db_create(&keyWord,0,0);
412 keyWord->open(keyWord, NULL, keyWordFileName.native_file_string().c_str(), NULL, DB_BTREE,
413 DB_CREATE | DB_TRUNCATE, 0644);
414 #endif
416 fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
418 HelpKeyword helpKeyword;
420 // catch HelpProcessingException to avoid locking data bases
424 // lastly, initialize the indexBuilder
425 if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
426 initIndexerPreProcessor();
428 // here we start our loop over the hzip files.
429 HashSet::iterator end = helpFiles.end();
430 for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter)
432 // process one file
433 // streamTable contains the streams in the hzip file
434 StreamTable streamTable;
435 const std::string &xhpFileName = *iter;
437 if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
439 // only work on .xhp - files
440 SAL_WARN("l10ntools",
441 "ERROR: input list entry '"
442 << xhpFileName
443 << "' has the wrong extension (only files with extension .xhp "
444 << "are accepted)");
446 continue;
449 fs::path langsourceRoot(sourceRoot);
450 fs::path xhpFile;
452 if( bExtensionMode )
454 // langsourceRoot == sourceRoot for extensions
455 std::string xhpFileNameComplete( extensionPath );
456 xhpFileNameComplete.append( '/' + xhpFileName );
457 xhpFile = fs::path( xhpFileNameComplete );
459 else
461 langsourceRoot.append('/' + lang + '/');
462 xhpFile = fs::path(xhpFileName, fs::native);
465 HelpCompiler hc( streamTable, xhpFile, langsourceRoot,
466 embeddStylesheet, module, lang, bExtensionMode );
468 HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl);
469 bool success = hc.compile();
470 HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl);
472 if (!success && !bExtensionMode)
474 std::stringstream aStrStream;
475 aStrStream <<
476 "\nERROR: compiling help particle '"
477 << xhpFileName
478 << "' for language '"
479 << lang
480 << "' failed!";
481 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
484 std::string documentPath = streamTable.document_path;
485 if (documentPath.find("/") == 0)
486 documentPath = documentPath.substr(1);
488 std::string documentJarfile = streamTable.document_module + ".jar";
490 std::string documentTitle = streamTable.document_title;
491 if (documentTitle.empty())
492 documentTitle = "<notitle>";
494 const std::string& fileB = documentPath;
495 const std::string& jarfileB = documentJarfile;
496 std::string& titleB = documentTitle;
498 // add once this as its own id.
499 addBookmark(dbBase, pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB);
501 // first the database *.db
502 // ByteArrayInputStream bais = null;
503 // ObjectInputStream ois = null;
505 const HashSet *hidlist = streamTable.appl_hidlist;
506 if (!hidlist)
507 hidlist = streamTable.default_hidlist;
508 if (hidlist && !hidlist->empty())
510 // now iterate over all elements of the hidlist
511 HashSet::const_iterator aEnd = hidlist->end();
512 for (HashSet::const_iterator hidListIter = hidlist->begin();
513 hidListIter != aEnd; ++hidListIter)
515 std::string thishid = *hidListIter;
517 std::string anchorB;
518 size_t index = thishid.rfind('#');
519 if (index != std::string::npos)
521 anchorB = thishid.substr(1 + index);
522 thishid = thishid.substr(0, index);
524 addBookmark(dbBase, pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB);
528 // now the keywords
529 const Hashtable *anchorToLL = streamTable.appl_keywords;
530 if (!anchorToLL)
531 anchorToLL = streamTable.default_keywords;
532 if (anchorToLL && !anchorToLL->empty())
534 std::string fakedHid = URLEncoder::encode(documentPath);
535 Hashtable::const_iterator aEnd = anchorToLL->end();
536 for (Hashtable::const_iterator enumer = anchorToLL->begin();
537 enumer != aEnd; ++enumer)
539 const std::string &anchor = enumer->first;
540 addBookmark(dbBase, pFileDbBase_DBHelp, documentPath, fileB,
541 anchor, jarfileB, titleB);
542 std::string totalId = fakedHid + "#" + anchor;
543 // std::cerr << hzipFileName << std::endl;
544 const LinkedList& ll = enumer->second;
545 LinkedList::const_iterator aOtherEnd = ll.end();
546 for (LinkedList::const_iterator llIter = ll.begin();
547 llIter != aOtherEnd; ++llIter)
549 helpKeyword.insert(*llIter, totalId);
555 // and last the helptexts
556 const Stringtable *helpTextHash = streamTable.appl_helptexts;
557 if (!helpTextHash)
558 helpTextHash = streamTable.default_helptexts;
559 if (helpTextHash && !helpTextHash->empty())
561 Stringtable::const_iterator aEnd = helpTextHash->end();
562 for (Stringtable::const_iterator helpTextIter = helpTextHash->begin();
563 helpTextIter != aEnd; ++helpTextIter)
565 std::string helpTextId = helpTextIter->first;
566 const std::string& helpTextText = helpTextIter->second;
568 helpTextId = URLEncoder::encode(helpTextId);
570 DBT keyDbt;
571 memset(&keyDbt, 0, sizeof(keyDbt));
572 keyDbt.data = const_cast<char*>(helpTextId.c_str());
573 keyDbt.size = helpTextId.length();
575 DBT textDbt;
576 memset(&textDbt, 0, sizeof(textDbt));
577 textDbt.data = const_cast<char*>(helpTextText.c_str());
578 textDbt.size = helpTextText.length();
580 if( helpText != NULL )
581 helpText->put(helpText, NULL, &keyDbt, &textDbt, 0);
583 if( pFileHelpText_DBHelp != NULL )
584 writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText );
588 //IndexerPreProcessor
589 if( !bExtensionMode || bIndexForExtension )
591 // now the indexing
592 xmlDocPtr document = streamTable.appl_doc;
593 if (!document)
594 document = streamTable.default_doc;
595 if (document)
597 std::string temp = module;
598 std::transform (temp.begin(), temp.end(), temp.begin(), tocharlower);
599 m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) );
603 } // while loop over hzip files ending
605 } // try
606 catch( const HelpProcessingException& )
608 // catch HelpProcessingException to avoid locking data bases
609 #ifndef DBHELP_ONLY
610 helpText->close(helpText, 0);
611 dbBase->close(dbBase, 0);
612 keyWord->close(keyWord, 0);
613 #endif
614 if( pFileHelpText_DBHelp != NULL )
615 fclose( pFileHelpText_DBHelp );
616 if( pFileDbBase_DBHelp != NULL )
617 fclose( pFileDbBase_DBHelp );
618 throw;
621 #ifndef DBHELP_ONLY
622 helpText->close(helpText, 0);
623 dbBase->close(dbBase, 0);
624 helpKeyword.dump(keyWord);
625 keyWord->close(keyWord, 0);
626 #endif
627 if( pFileHelpText_DBHelp != NULL )
628 fclose( pFileHelpText_DBHelp );
629 if( pFileDbBase_DBHelp != NULL )
630 fclose( pFileDbBase_DBHelp );
632 helpKeyword.dump_DBHelp( keyWordFileName_DBHelp);
634 if( !bExtensionMode )
636 // New index
637 Stringtable::iterator aEnd = additionalFiles.end();
638 for (Stringtable::iterator enumer = additionalFiles.begin(); enumer != aEnd;
639 ++enumer)
641 const std::string &additionalFileName = enumer->second;
642 const std::string &additionalFileKey = enumer->first;
644 fs::path fsAdditionalFileName( additionalFileName, fs::native );
645 std::string aNativeStr = fsAdditionalFileName.native_file_string();
646 HCDBG(const char* pStr = aNativeStr.c_str(); std::cerr << pStr << std::endl);
648 fs::path fsTargetName( indexDirParentName / additionalFileKey );
650 fs::copy( fsAdditionalFileName, fsTargetName );
656 void HelpLinker::main( std::vector<std::string> &args,
657 std::string* pExtensionPath, std::string* pDestination,
658 const rtl::OUString* pOfficeHelpPath )
659 throw( HelpProcessingException )
661 bExtensionMode = false;
662 helpFiles.clear();
664 if ((!args.empty()) && args[0][0] == '@')
666 std::vector<std::string> stringList;
667 std::ifstream fileReader(args[0].substr(1).c_str());
669 while (fileReader)
671 std::string token;
672 fileReader >> token;
673 if (!token.empty())
674 stringList.push_back(token);
676 fileReader.close();
678 args = stringList;
681 size_t i = 0;
682 bool bSrcOption = false;
683 while (i < args.size())
685 if (args[i].compare("-extlangsrc") == 0)
687 ++i;
688 if (i >= args.size())
690 std::stringstream aStrStream;
691 aStrStream << "extension source missing" << std::endl;
692 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
694 extsource = args[i];
696 else if (args[i].compare("-extlangdest") == 0)
698 //If this argument is not provided then the location provided in -extsource will
699 //also be the destination
700 ++i;
701 if (i >= args.size())
703 std::stringstream aStrStream;
704 aStrStream << "extension destination missing" << std::endl;
705 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
707 extdestination = args[i];
709 else if (args[i].compare("-src") == 0)
711 ++i;
712 if (i >= args.size())
714 std::stringstream aStrStream;
715 aStrStream << "sourceroot missing" << std::endl;
716 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
718 bSrcOption = true;
719 sourceRoot = fs::path(args[i], fs::native);
721 else if (args[i].compare("-sty") == 0)
723 ++i;
724 if (i >= args.size())
726 std::stringstream aStrStream;
727 aStrStream << "embeddingStylesheet missing" << std::endl;
728 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
731 embeddStylesheet = fs::path(args[i], fs::native);
733 else if (args[i].compare("-zipdir") == 0)
735 ++i;
736 if (i >= args.size())
738 std::stringstream aStrStream;
739 aStrStream << "idxtemp missing" << std::endl;
740 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
743 zipdir = fs::path(args[i], fs::native);
745 else if (args[i].compare("-idxcaption") == 0)
747 ++i;
748 if (i >= args.size())
750 std::stringstream aStrStream;
751 aStrStream << "idxcaption stylesheet missing" << std::endl;
752 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
755 idxCaptionStylesheet = fs::path(args[i], fs::native);
757 else if (args[i].compare("-idxcontent") == 0)
759 ++i;
760 if (i >= args.size())
762 std::stringstream aStrStream;
763 aStrStream << "idxcontent stylesheet missing" << std::endl;
764 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
767 idxContentStylesheet = fs::path(args[i], fs::native);
769 else if (args[i].compare("-o") == 0)
771 ++i;
772 if (i >= args.size())
774 std::stringstream aStrStream;
775 aStrStream << "outputfilename missing" << std::endl;
776 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
779 outputFile = fs::path(args[i], fs::native);
781 else if (args[i].compare("-mod") == 0)
783 ++i;
784 if (i >= args.size())
786 std::stringstream aStrStream;
787 aStrStream << "module name missing" << std::endl;
788 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
791 module = args[i];
793 else if (args[i].compare("-lang") == 0)
795 ++i;
796 if (i >= args.size())
798 std::stringstream aStrStream;
799 aStrStream << "language name missing" << std::endl;
800 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
803 lang = args[i];
805 else if (args[i].compare("-hid") == 0)
807 ++i;
808 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, "obsolete -hid argument used" );
810 else if (args[i].compare("-add") == 0)
812 std::string addFile, addFileUnderPath;
813 ++i;
814 if (i >= args.size())
816 std::stringstream aStrStream;
817 aStrStream << "pathname missing" << std::endl;
818 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
821 addFileUnderPath = args[i];
822 ++i;
823 if (i >= args.size())
825 std::stringstream aStrStream;
826 aStrStream << "pathname missing" << std::endl;
827 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
829 addFile = args[i];
830 if (!addFileUnderPath.empty() && !addFile.empty())
831 additionalFiles[addFileUnderPath] = addFile;
833 else
834 helpFiles.push_back(args[i]);
835 ++i;
838 //We can be called from the helplinker executable or the extension manager
839 //In the latter case extsource is not used.
840 if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath)
841 || !extsource.empty())
843 bExtensionMode = true;
844 if (!extsource.empty())
846 //called from helplinker.exe, pExtensionPath and pOfficeHelpPath
847 //should be NULL
848 sourceRoot = fs::path(extsource, fs::native);
849 extensionPath = sourceRoot.toUTF8();
851 if (extdestination.empty())
853 std::stringstream aStrStream;
854 aStrStream << "-extlangdest is missing" << std::endl;
855 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
857 else
859 //Convert from system path to file URL!!!
860 fs::path p(extdestination, fs::native);
861 extensionDestination = p.toUTF8();
864 else
865 { //called from extension manager
866 extensionPath = *pExtensionPath;
867 sourceRoot = fs::path(extensionPath);
868 extensionDestination = *pDestination;
870 //check if -src option was used. This option must not be used
871 //when extension help is compiled.
872 if (bSrcOption)
874 std::stringstream aStrStream;
875 aStrStream << "-src must not be used together with -extsource missing" << std::endl;
876 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
880 if (!bExtensionMode && zipdir.empty())
882 std::stringstream aStrStream;
883 aStrStream << "no index dir given" << std::endl;
884 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
887 if ( (!bExtensionMode && idxCaptionStylesheet.empty())
888 || (!extsource.empty() && idxCaptionStylesheet.empty()) )
890 //No extension mode and extension mode using commandline
891 //!extsource.empty indicates extension mode using commandline
892 // -idxcaption paramter is required
893 std::stringstream aStrStream;
894 aStrStream << "no index caption stylesheet given" << std::endl;
895 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
897 else if ( bExtensionMode && extsource.empty())
899 //This part is used when compileExtensionHelp is called from the extensions manager.
900 //If extension help is compiled using helplinker in the build process
901 rtl::OUString aIdxCaptionPathFileURL( *pOfficeHelpPath );
902 aIdxCaptionPathFileURL += rtl::OUString("/idxcaption.xsl");
904 rtl::OString aOStr_IdxCaptionPathFileURL( rtl::OUStringToOString
905 ( aIdxCaptionPathFileURL, fs::getThreadTextEncoding() ) );
906 std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() );
908 idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL );
911 if ( (!bExtensionMode && idxContentStylesheet.empty())
912 || (!extsource.empty() && idxContentStylesheet.empty()) )
914 //No extension mode and extension mode using commandline
915 //!extsource.empty indicates extension mode using commandline
916 // -idxcontent paramter is required
917 std::stringstream aStrStream;
918 aStrStream << "no index content stylesheet given" << std::endl;
919 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
921 else if ( bExtensionMode && extsource.empty())
923 //If extension help is compiled using helplinker in the build process
924 //then -idxcontent must be supplied
925 //This part is used when compileExtensionHelp is called from the extensions manager.
926 rtl::OUString aIdxContentPathFileURL( *pOfficeHelpPath );
927 aIdxContentPathFileURL += rtl::OUString("/idxcontent.xsl");
929 rtl::OString aOStr_IdxContentPathFileURL( rtl::OUStringToOString
930 ( aIdxContentPathFileURL, fs::getThreadTextEncoding() ) );
931 std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() );
933 idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL );
935 if (!bExtensionMode && embeddStylesheet.empty())
937 std::stringstream aStrStream;
938 aStrStream << "no embedding resolving file given" << std::endl;
939 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
941 if (sourceRoot.empty())
943 std::stringstream aStrStream;
944 aStrStream << "no sourceroot given" << std::endl;
945 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
947 if (!bExtensionMode && outputFile.empty())
949 std::stringstream aStrStream;
950 aStrStream << "no output file given" << std::endl;
951 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
953 if (module.empty())
955 std::stringstream aStrStream;
956 aStrStream << "module missing" << std::endl;
957 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
959 if (!bExtensionMode && lang.empty())
961 std::stringstream aStrStream;
962 aStrStream << "language missing" << std::endl;
963 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
965 link();
968 // Variable to set an exception in "C" StructuredXMLErrorFunction
969 static const HelpProcessingException* GpXMLParsingException = NULL;
971 extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
973 (void)userData;
974 (void)error;
976 std::string aErrorMsg = error->message;
977 std::string aXMLParsingFile;
978 if( error->file != NULL )
979 aXMLParsingFile = error->file;
980 int nXMLParsingLine = error->line;
981 HelpProcessingException* pException = new HelpProcessingException( aErrorMsg, aXMLParsingFile, nXMLParsingLine );
982 GpXMLParsingException = pException;
984 // Reset error handler
985 xmlSetStructuredErrorFunc( NULL, NULL );
988 HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e )
990 m_eErrorClass = e.m_eErrorClass;
991 rtl::OString tmpErrorMsg( e.m_aErrorMsg.c_str() );
992 m_aErrorMsg = rtl::OStringToOUString( tmpErrorMsg, fs::getThreadTextEncoding() );
993 rtl::OString tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() );
994 m_aXMLParsingFile = rtl::OStringToOUString( tmpXMLParsingFile, fs::getThreadTextEncoding() );
995 m_nXMLParsingLine = e.m_nXMLParsingLine;
996 return *this;
1000 // Returns true in case of success, false in case of error
1001 HELPLINKER_DLLPUBLIC bool compileExtensionHelp
1003 const rtl::OUString& aOfficeHelpPath,
1004 const rtl::OUString& aExtensionName,
1005 const rtl::OUString& aExtensionLanguageRoot,
1006 sal_Int32 nXhpFileCount, const rtl::OUString* pXhpFiles,
1007 const rtl::OUString& aDestination,
1008 HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo
1011 bool bSuccess = true;
1013 std::vector<std::string> args;
1014 args.reserve(nXhpFileCount + 2);
1015 args.push_back(std::string("-mod"));
1016 rtl::OString aOExtensionName = rtl::OUStringToOString( aExtensionName, fs::getThreadTextEncoding() );
1017 args.push_back(std::string(aOExtensionName.getStr()));
1019 for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
1021 rtl::OUString aXhpFile = pXhpFiles[iXhp];
1023 rtl::OString aOXhpFile = rtl::OUStringToOString( aXhpFile, fs::getThreadTextEncoding() );
1024 args.push_back(std::string(aOXhpFile.getStr()));
1027 rtl::OString aOExtensionLanguageRoot = rtl::OUStringToOString( aExtensionLanguageRoot, fs::getThreadTextEncoding() );
1028 const char* pExtensionPath = aOExtensionLanguageRoot.getStr();
1029 std::string aStdStrExtensionPath = pExtensionPath;
1030 rtl::OString aODestination = rtl::OUStringToOString(aDestination, fs::getThreadTextEncoding());
1031 const char* pDestination = aODestination.getStr();
1032 std::string aStdStrDestination = pDestination;
1034 // Set error handler
1035 xmlSetStructuredErrorFunc( NULL, (xmlStructuredErrorFunc)StructuredXMLErrorFunction );
1038 HelpLinker* pHelpLinker = new HelpLinker();
1039 pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
1040 delete pHelpLinker;
1042 catch( const HelpProcessingException& e )
1044 if( GpXMLParsingException != NULL )
1046 o_rHelpProcessingErrorInfo = *GpXMLParsingException;
1047 delete GpXMLParsingException;
1048 GpXMLParsingException = NULL;
1050 else
1052 o_rHelpProcessingErrorInfo = e;
1054 bSuccess = false;
1056 // Reset error handler
1057 xmlSetStructuredErrorFunc( NULL, NULL );
1059 // i83624: Tree files
1060 ::rtl::OUString aTreeFileURL = aExtensionLanguageRoot;
1061 aTreeFileURL += rtl::OUString("/help.tree");
1062 osl::DirectoryItem aTreeFileItem;
1063 osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
1064 osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
1065 if( rcGet == osl::FileBase::E_None &&
1066 aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
1067 aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) )
1069 sal_uInt64 ret, len = aFileStatus.getFileSize();
1070 char* s = new char[ int(len) ]; // the buffer to hold the installed files
1071 osl::File aFile( aTreeFileURL );
1072 aFile.open( osl_File_OpenFlag_Read );
1073 aFile.read( s, len, ret );
1074 aFile.close();
1076 XML_Parser parser = XML_ParserCreate( 0 );
1077 int parsed = XML_Parse( parser, s, int( len ), true );
1079 if( parsed == 0 )
1081 XML_Error nError = XML_GetErrorCode( parser );
1082 o_rHelpProcessingErrorInfo.m_eErrorClass = HELPPROCESSING_XMLPARSING_ERROR;
1083 o_rHelpProcessingErrorInfo.m_aErrorMsg = rtl::OUString::createFromAscii( XML_ErrorString( nError ) );;
1084 o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL;
1085 // CRAHSES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser );
1086 bSuccess = false;
1089 XML_ParserFree( parser );
1090 delete[] s;
1093 return bSuccess;
1096 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */