1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include "HelpCompiler.hxx"
25
26 #include <map>
27
28 #include <string.h>
29 #include <limits.h>
30
31 #include <libxslt/xslt.h>
32 #include <libxslt/transform.h>
33 #include <libxslt/xsltutils.h>
34 #include <libxslt/functions.h>
35 #include <libxslt/extensions.h>
36
37 #include <sal/types.h>
38 #include <osl/time.h>
39 #include <rtl/bootstrap.hxx>
40
41 #include <expat.h>
42
43 class IndexerPreProcessor
44 {
45 private:
46 std::string m_aModuleName;
47 fs::path m_fsIndexBaseDir;
48 fs::path m_fsCaptionFilesDirName;
49 fs::path m_fsContentFilesDirName;
50
51 xsltStylesheetPtr m_xsltStylesheetPtrCaption;
52 xsltStylesheetPtr m_xsltStylesheetPtrContent;
53
54 public:
55 IndexerPreProcessor( const std::string& aModuleName, const fs::path& fsIndexBaseDir,
56 const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet );
57 ~IndexerPreProcessor();
58
59 void processDocument( xmlDocPtr doc, const std::string& EncodedDocPath );
60 };
61
IndexerPreProcessor(const std::string & aModuleName,const fs::path & fsIndexBaseDir,const fs::path & idxCaptionStylesheet,const fs::path & idxContentStylesheet)62 IndexerPreProcessor::IndexerPreProcessor
63 ( const std::string& aModuleName, const fs::path& fsIndexBaseDir,
64 const fs::path& idxCaptionStylesheet, const fs::path& idxContentStylesheet )
65 : m_aModuleName( aModuleName )
66 , m_fsIndexBaseDir( fsIndexBaseDir )
67 {
68 m_fsCaptionFilesDirName = fsIndexBaseDir / "caption";
69 fs::create_directory( m_fsCaptionFilesDirName );
70
71 m_fsContentFilesDirName = fsIndexBaseDir / "content";
72 fs::create_directory( m_fsContentFilesDirName );
73
74 m_xsltStylesheetPtrCaption = xsltParseStylesheetFile
75 ((const xmlChar *)idxCaptionStylesheet.native_file_string().c_str());
76 m_xsltStylesheetPtrContent = xsltParseStylesheetFile
77 ((const xmlChar *)idxContentStylesheet.native_file_string().c_str());
78 }
79
~IndexerPreProcessor()80 IndexerPreProcessor::~IndexerPreProcessor()
81 {
82 if( m_xsltStylesheetPtrCaption )
83 xsltFreeStylesheet( m_xsltStylesheetPtrCaption );
84 if( m_xsltStylesheetPtrContent )
85 xsltFreeStylesheet( m_xsltStylesheetPtrContent );
86 }
87
88
getEncodedPath(const std::string & Path)89 std::string getEncodedPath( const std::string& Path )
90 {
91 rtl::OString aOStr_Path( Path.c_str() );
92 rtl::OUString aOUStr_Path( rtl::OStringToOUString
93 ( aOStr_Path, fs::getThreadTextEncoding() ) );
94 rtl::OUString aPathURL;
95 osl::File::getFileURLFromSystemPath( aOUStr_Path, aPathURL );
96 rtl::OString aOStr_PathURL( rtl::OUStringToOString
97 ( aPathURL, fs::getThreadTextEncoding() ) );
98 std::string aStdStr_PathURL( aOStr_PathURL.getStr() );
99 return aStdStr_PathURL;
100 }
101
processDocument(xmlDocPtr doc,const std::string & EncodedDocPath)102 void IndexerPreProcessor::processDocument
103 ( xmlDocPtr doc, const std::string &EncodedDocPath )
104 {
105 std::string aStdStr_EncodedDocPathURL = getEncodedPath( EncodedDocPath );
106
107 if( m_xsltStylesheetPtrCaption )
108 {
109 xmlDocPtr resCaption = xsltApplyStylesheet( m_xsltStylesheetPtrCaption, doc, NULL );
110 xmlNodePtr pResNodeCaption = resCaption->xmlChildrenNode;
111 if( pResNodeCaption )
112 {
113 fs::path fsCaptionPureTextFile_docURL = m_fsCaptionFilesDirName / aStdStr_EncodedDocPathURL;
114 std::string aCaptionPureTextFileStr_docURL = fsCaptionPureTextFile_docURL.native_file_string();
115 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
116 FILE* pFile_docURL = _wfopen(
117 fsCaptionPureTextFile_docURL.native_file_string_w(), L"w" );
118 #else
119 FILE* pFile_docURL = fopen(
120 fsCaptionPureTextFile_docURL.native_file_string().c_str(), "w" );
121 #endif
122 if( pFile_docURL )
123 {
124 fprintf( pFile_docURL, "%s\n", pResNodeCaption->content );
125 fclose( pFile_docURL );
126 }
127 }
128 xmlFreeDoc(resCaption);
129 }
130
131 if( m_xsltStylesheetPtrContent )
132 {
133 xmlDocPtr resContent = xsltApplyStylesheet( m_xsltStylesheetPtrContent, doc, NULL );
134 xmlNodePtr pResNodeContent = resContent->xmlChildrenNode;
135 if( pResNodeContent )
136 {
137 fs::path fsContentPureTextFile_docURL = m_fsContentFilesDirName / aStdStr_EncodedDocPathURL;
138 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
139 FILE* pFile_docURL = _wfopen(
140 fsContentPureTextFile_docURL.native_file_string_w(), L"w" );
141 #else
142 FILE* pFile_docURL = fopen(
143 fsContentPureTextFile_docURL.native_file_string().c_str(), "w" );
144 #endif
145 if( pFile_docURL )
146 {
147 fprintf( pFile_docURL, "%s\n", pResNodeContent->content );
148 fclose( pFile_docURL );
149 }
150 }
151 xmlFreeDoc(resContent);
152 }
153 }
154
155 struct Data
156 {
157 std::vector<std::string> _idList;
158 typedef std::vector<std::string>::const_iterator cIter;
159
appendData160 void append(const std::string &id)
161 {
162 _idList.push_back(id);
163 }
164
getStringData165 std::string getString() const
166 {
167 std::string ret;
168 cIter aEnd = _idList.end();
169 for (cIter aIter = _idList.begin(); aIter != aEnd; ++aIter)
170 ret += *aIter + ";";
171 return ret;
172 }
173 };
174
writeKeyValue_DBHelp(FILE * pFile,const std::string & aKeyStr,const std::string & aValueStr)175 void writeKeyValue_DBHelp( FILE* pFile, const std::string& aKeyStr, const std::string& aValueStr )
176 {
177 if( pFile == NULL )
178 return;
179 char cLF = 10;
180 unsigned int nKeyLen = aKeyStr.length();
181 unsigned int nValueLen = aValueStr.length();
182 fprintf( pFile, "%x ", nKeyLen );
183 if( nKeyLen > 0 )
184 {
185 if (fwrite( aKeyStr.c_str(), 1, nKeyLen, pFile ) != nKeyLen)
186 fprintf(stderr, "fwrite to db failed\n");
187 }
188 if (fprintf( pFile, " %x ", nValueLen ) < 0)
189 fprintf(stderr, "fwrite to db failed\n");
190 if( nValueLen > 0 )
191 {
192 if (fwrite( aValueStr.c_str(), 1, nValueLen, pFile ) != nValueLen)
193 fprintf(stderr, "fwrite to db failed\n");
194 }
195 if (fprintf( pFile, "%c", cLF ) < 0)
196 fprintf(stderr, "fwrite to db failed\n");
197 }
198
199 class HelpKeyword
200 {
201 private:
202 typedef std::hash_map<std::string, Data, pref_hash> DataHashtable;
203 DataHashtable _hash;
204
205 public:
insert(const std::string & key,const std::string & id)206 void insert(const std::string &key, const std::string &id)
207 {
208 Data &data = _hash[key];
209 data.append(id);
210 }
211
dump_DBHelp(const fs::path & rFileName)212 void dump_DBHelp( const fs::path& rFileName )
213 {
214 #ifdef WNT //We need _wfopen to support long file paths on Windows XP
215 FILE* pFile = _wfopen( rFileName.native_file_string_w(), L"wb" );
216 #else
217 FILE* pFile = fopen( rFileName.native_file_string().c_str(), "wb" );
218 #endif
219 if( pFile == NULL )
220 return;
221
222 DataHashtable::const_iterator aEnd = _hash.end();
223 for (DataHashtable::const_iterator aIter = _hash.begin(); aIter != aEnd; ++aIter)
224 writeKeyValue_DBHelp( pFile, aIter->first, aIter->second.getString() );
225
226 fclose( pFile );
227 }
228 };
229
230 class HelpLinker
231 {
232 public:
233 void main(std::vector<std::string> &args,
234 std::string* pExtensionPath = NULL,
235 std::string* pDestination = NULL,
236 const rtl::OUString* pOfficeHelpPath = NULL );
237
HelpLinker()238 HelpLinker()
239 : init(true)
240 , m_pIndexerPreProcessor(NULL)
241 {}
~HelpLinker()242 ~HelpLinker()
243 { delete m_pIndexerPreProcessor; }
244
245 private:
246 int locCount, totCount;
247 Stringtable additionalFiles;
248 HashSet helpFiles;
249 fs::path sourceRoot;
250 fs::path embeddStylesheet;
251 fs::path idxCaptionStylesheet;
252 fs::path idxContentStylesheet;
253 fs::path zipdir;
254 fs::path outputFile;
255 std::string extsource;
256 std::string extdestination;
257 std::string module;
258 std::string lang;
259 std::string extensionPath;
260 std::string extensionDestination;
261 bool bExtensionMode;
262 fs::path indexDirName;
263 fs::path indexDirParentName;
264 bool init;
265 IndexerPreProcessor* m_pIndexerPreProcessor;
266 void initIndexerPreProcessor();
267 void link();
268 void addBookmark( FILE* pFile_DBHelp, std::string thishid,
269 const std::string& fileB, const std::string& anchorB,
270 const std::string& jarfileB, const std::string& titleB );
271 };
272
273 namespace URLEncoder
274 {
encode(const std::string & rIn)275 static std::string encode(const std::string &rIn)
276 {
277 const char *good = "!$&'()*+,-.=@_";
278 static const char hex[17] = "0123456789ABCDEF";
279
280 std::string result;
281 for (size_t i=0; i < rIn.length(); ++i)
282 {
283 unsigned char c = rIn[i];
284 if (isalnum (c) || strchr (good, c))
285 result += c;
286 else {
287 result += '%';
288 result += hex[c >> 4];
289 result += hex[c & 0xf];
290 }
291 }
292 return result;
293 }
294 }
295
addBookmark(FILE * pFile_DBHelp,std::string thishid,const std::string & fileB,const std::string & anchorB,const std::string & jarfileB,const std::string & titleB)296 void HelpLinker::addBookmark( FILE* pFile_DBHelp, std::string thishid,
297 const std::string& fileB, const std::string& anchorB,
298 const std::string& jarfileB, const std::string& titleB)
299 {
300 HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " <<
301 fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl);
302
303 thishid = URLEncoder::encode(thishid);
304
305 int fileLen = fileB.length();
306 if (!anchorB.empty())
307 fileLen += (1 + anchorB.length());
308 int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length();
309
310 std::vector<unsigned char> dataB(dataLen);
311 size_t i = 0;
312 dataB[i++] = static_cast<unsigned char>(fileLen);
313 for (size_t j = 0; j < fileB.length(); ++j)
314 dataB[i++] = fileB[j];
315 if (!anchorB.empty())
316 {
317 dataB[i++] = '#';
318 for (size_t j = 0; j < anchorB.length(); ++j)
319 dataB[i++] = anchorB[j];
320 }
321 dataB[i++] = static_cast<unsigned char>(jarfileB.length());
322 for (size_t j = 0; j < jarfileB.length(); ++j)
323 dataB[i++] = jarfileB[j];
324
325 dataB[i++] = static_cast<unsigned char>(titleB.length());
326 for (size_t j = 0; j < titleB.length(); ++j)
327 dataB[i++] = titleB[j];
328
329 if( pFile_DBHelp != NULL )
330 {
331 std::string aValueStr( dataB.begin(), dataB.end() );
332 writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr );
333 }
334 }
335
initIndexerPreProcessor()336 void HelpLinker::initIndexerPreProcessor()
337 {
338 if( m_pIndexerPreProcessor )
339 delete m_pIndexerPreProcessor;
340 std::string mod = module;
341 std::transform (mod.begin(), mod.end(), mod.begin(), tolower);
342 m_pIndexerPreProcessor = new IndexerPreProcessor( mod, indexDirParentName,
343 idxCaptionStylesheet, idxContentStylesheet );
344 }
345
346 /**
347 *
348 */
link()349 void HelpLinker::link()
350 {
351 bool bIndexForExtension = true;
352
353 if( bExtensionMode )
354 {
355 //indexDirParentName = sourceRoot;
356 indexDirParentName = extensionDestination;
357 }
358 else
359 {
360 indexDirParentName = zipdir;
361 fs::create_directory(indexDirParentName);
362 }
363
364 std::string mod = module;
365 std::transform (mod.begin(), mod.end(), mod.begin(), tolower);
366
367 // do the work here
368 // continue with introduction of the overall process thing into the
369 // here all hzip files will be worked on
370 std::string appl = mod;
371 if (appl[0] == 's')
372 appl = appl.substr(1);
373
374 bool bUse_ = true;
375 if( !bExtensionMode )
376 bUse_ = false;
377
378 fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht")));
379 #ifdef WNT
380 //We need _wfopen to support long file paths on Windows XP
381 FILE* pFileHelpText_DBHelp = _wfopen
382 ( helpTextFileName_DBHelp.native_file_string_w(), L"wb" );
383 #else
384
385 FILE* pFileHelpText_DBHelp = fopen
386 ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" );
387 #endif
388
389 fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
390 #ifdef WNT
391 //We need _wfopen to support long file paths on Windows XP
392 FILE* pFileDbBase_DBHelp = _wfopen
393 ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" );
394 #else
395 FILE* pFileDbBase_DBHelp = fopen
396 ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" );
397 #endif
398
399 fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
400
401 HelpKeyword helpKeyword;
402
403 // catch HelpProcessingException to avoid locking data bases
404 try
405 {
406
407 // lastly, initialize the indexBuilder
408 if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
409 initIndexerPreProcessor();
410
411 if( !bExtensionMode )
412 {
413 #ifndef OS2 // YD @TODO@ crashes libc runtime :-(
414 std::cout << "Making " << outputFile.native_file_string() <<
415 " from " << helpFiles.size() << " input files" << std::endl;
416 #endif
417 }
418
419 // here we start our loop over the hzip files.
420 HashSet::iterator end = helpFiles.end();
421 for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter)
422 {
423 if( !bExtensionMode )
424 {
425 std::cout << ".";
426 std::cout.flush();
427 }
428
429 // process one file
430 // streamTable contains the streams in the hzip file
431 StreamTable streamTable;
432 const std::string &xhpFileName = *iter;
433
434 if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
435 {
436 // only work on .xhp - files
437 std::cerr <<
438 "ERROR: input list entry '"
439 << xhpFileName
440 << "' has the wrong extension (only files with extension .xhp "
441 << "are accepted)";
442 continue;
443 }
444
445 fs::path langsourceRoot(sourceRoot);
446 fs::path xhpFile;
447
448 if( bExtensionMode )
449 {
450 // langsourceRoot == sourceRoot for extensions
451 std::string xhpFileNameComplete( extensionPath );
452 xhpFileNameComplete.append( '/' + xhpFileName );
453 xhpFile = fs::path( xhpFileNameComplete );
454 }
455 else
456 {
457 langsourceRoot.append('/' + lang + '/');
458 xhpFile = fs::path(xhpFileName, fs::native);
459 }
460
461 HelpCompiler hc( streamTable, xhpFile, langsourceRoot,
462 embeddStylesheet, module, lang, bExtensionMode );
463
464 HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl);
465 bool success = hc.compile();
466 HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl);
467
468 if (!success && !bExtensionMode)
469 {
470 std::stringstream aStrStream;
471 aStrStream <<
472 "\nERROR: compiling help particle '"
473 << xhpFileName
474 << "' for language '"
475 << lang
476 << "' failed!";
477 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
478 }
479
480 const std::string documentBaseId = streamTable.document_id;
481 std::string documentPath = streamTable.document_path;
482 if (documentPath.find("/") == 0)
483 documentPath = documentPath.substr(1);
484
485 std::string documentJarfile = streamTable.document_module + ".jar";
486
487 std::string documentTitle = streamTable.document_title;
488 if (documentTitle.empty())
489 documentTitle = "<notitle>";
490
491 const std::string& fileB = documentPath;
492 const std::string& jarfileB = documentJarfile;
493 std::string& titleB = documentTitle;
494
495 // add once this as its own id.
496 addBookmark(pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB);
497
498 const HashSet *hidlist = streamTable.appl_hidlist;
499 if (!hidlist)
500 hidlist = streamTable.default_hidlist;
501 if (hidlist && !hidlist->empty())
502 {
503 // now iterate over all elements of the hidlist
504 HashSet::const_iterator aEnd = hidlist->end();
505 for (HashSet::const_iterator hidListIter = hidlist->begin();
506 hidListIter != aEnd; ++hidListIter)
507 {
508 std::string thishid = *hidListIter;
509
510 std::string anchorB;
511 size_t index = thishid.rfind('#');
512 if (index != std::string::npos)
513 {
514 anchorB = thishid.substr(1 + index);
515 thishid = thishid.substr(0, index);
516 }
517 addBookmark(pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB);
518 }
519 }
520
521 // now the keywords
522 const Hashtable *anchorToLL = streamTable.appl_keywords;
523 if (!anchorToLL)
524 anchorToLL = streamTable.default_keywords;
525 if (anchorToLL && !anchorToLL->empty())
526 {
527 std::string fakedHid = URLEncoder::encode(documentPath);
528 Hashtable::const_iterator aEnd = anchorToLL->end();
529 for (Hashtable::const_iterator enumer = anchorToLL->begin();
530 enumer != aEnd; ++enumer)
531 {
532 const std::string &anchor = enumer->first;
533 addBookmark(pFileDbBase_DBHelp, documentPath, fileB,
534 anchor, jarfileB, titleB);
535 std::string totalId = fakedHid + "#" + anchor;
536 // std::cerr << hzipFileName << std::endl;
537 const LinkedList& ll = enumer->second;
538 LinkedList::const_iterator aOtherEnd = ll.end();
539 for (LinkedList::const_iterator llIter = ll.begin();
540 llIter != aOtherEnd; ++llIter)
541 {
542 helpKeyword.insert(*llIter, totalId);
543 }
544 }
545
546 }
547
548 // and last the helptexts
549 const Stringtable *helpTextHash = streamTable.appl_helptexts;
550 if (!helpTextHash)
551 helpTextHash = streamTable.default_helptexts;
552 if (helpTextHash && !helpTextHash->empty())
553 {
554 Stringtable::const_iterator aEnd = helpTextHash->end();
555 for (Stringtable::const_iterator helpTextIter = helpTextHash->begin();
556 helpTextIter != aEnd; ++helpTextIter)
557 {
558 std::string helpTextId = helpTextIter->first;
559 const std::string& helpTextText = helpTextIter->second;
560
561 helpTextId = URLEncoder::encode(helpTextId);
562
563 if( pFileHelpText_DBHelp != NULL )
564 writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText );
565 }
566 }
567
568 //IndexerPreProcessor
569 if( !bExtensionMode || bIndexForExtension )
570 {
571 // now the indexing
572 xmlDocPtr document = streamTable.appl_doc;
573 if (!document)
574 document = streamTable.default_doc;
575 if (document)
576 {
577 std::string temp = module;
578 std::transform (temp.begin(), temp.end(), temp.begin(), tolower);
579 m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) );
580 }
581 }
582
583 } // while loop over hzip files ending
584 if( !bExtensionMode )
585 std::cout << std::endl;
586
587 } // try
588 catch( const HelpProcessingException& )
589 {
590 // catch HelpProcessingException to avoid locking data bases
591 if( pFileHelpText_DBHelp != NULL )
592 fclose( pFileHelpText_DBHelp );
593 if( pFileDbBase_DBHelp != NULL )
594 fclose( pFileDbBase_DBHelp );
595 throw;
596 }
597
598 if( pFileHelpText_DBHelp != NULL )
599 fclose( pFileHelpText_DBHelp );
600 if( pFileDbBase_DBHelp != NULL )
601 fclose( pFileDbBase_DBHelp );
602
603 helpKeyword.dump_DBHelp( keyWordFileName_DBHelp);
604
605 if( !bExtensionMode )
606 {
607 // New index
608 Stringtable::iterator aEnd = additionalFiles.end();
609 for (Stringtable::iterator enumer = additionalFiles.begin(); enumer != aEnd;
610 ++enumer)
611 {
612 const std::string &additionalFileName = enumer->second;
613 const std::string &additionalFileKey = enumer->first;
614
615 fs::path fsAdditionalFileName( additionalFileName, fs::native );
616 std::string aNativeStr = fsAdditionalFileName.native_file_string();
617 const char* pStr = aNativeStr.c_str();
618 std::cerr << pStr;
619
620 fs::path fsTargetName( indexDirParentName / additionalFileKey );
621
622 fs::copy( fsAdditionalFileName, fsTargetName );
623 }
624 }
625
626 }
627
628
main(std::vector<std::string> & args,std::string * pExtensionPath,std::string * pDestination,const rtl::OUString * pOfficeHelpPath)629 void HelpLinker::main( std::vector<std::string> &args,
630 std::string* pExtensionPath, std::string* pDestination,
631 const rtl::OUString* pOfficeHelpPath )
632 {
633 bExtensionMode = false;
634 helpFiles.clear();
635
636 if (args.size() > 0 && args[0][0] == '@')
637 {
638 std::vector<std::string> stringList;
639 std::string strBuf;
640 std::ifstream fileReader(args[0].substr(1).c_str());
641
642 while (fileReader)
643 {
644 std::string token;
645 fileReader >> token;
646 if (!token.empty())
647 stringList.push_back(token);
648 }
649 fileReader.close();
650
651 args = stringList;
652 }
653
654 size_t i = 0;
655 bool bSrcOption = false;
656 while (i < args.size())
657 {
658 if (args[i].compare("-extlangsrc") == 0)
659 {
660 ++i;
661 if (i >= args.size())
662 {
663 std::stringstream aStrStream;
664 aStrStream << "extension source missing" << std::endl;
665 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
666 }
667 extsource = args[i];
668 }
669 else if (args[i].compare("-extlangdest") == 0)
670 {
671 //If this argument is not provided then the location provided in -extsource will
672 //also be the destination
673 ++i;
674 if (i >= args.size())
675 {
676 std::stringstream aStrStream;
677 aStrStream << "extension destination missing" << std::endl;
678 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
679 }
680 extdestination = args[i];
681 }
682 else if (args[i].compare("-src") == 0)
683 {
684 ++i;
685 if (i >= args.size())
686 {
687 std::stringstream aStrStream;
688 aStrStream << "sourceroot missing" << std::endl;
689 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
690 }
691 bSrcOption = true;
692 sourceRoot = fs::path(args[i], fs::native);
693 }
694 else if (args[i].compare("-sty") == 0)
695 {
696 ++i;
697 if (i >= args.size())
698 {
699 std::stringstream aStrStream;
700 aStrStream << "embeddingStylesheet missing" << std::endl;
701 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
702 }
703
704 embeddStylesheet = fs::path(args[i], fs::native);
705 }
706 else if (args[i].compare("-zipdir") == 0)
707 {
708 ++i;
709 if (i >= args.size())
710 {
711 std::stringstream aStrStream;
712 aStrStream << "idxtemp missing" << std::endl;
713 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
714 }
715
716 zipdir = fs::path(args[i], fs::native);
717 }
718 else if (args[i].compare("-idxcaption") == 0)
719 {
720 ++i;
721 if (i >= args.size())
722 {
723 std::stringstream aStrStream;
724 aStrStream << "idxcaption stylesheet missing" << std::endl;
725 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
726 }
727
728 idxCaptionStylesheet = fs::path(args[i], fs::native);
729 }
730 else if (args[i].compare("-idxcontent") == 0)
731 {
732 ++i;
733 if (i >= args.size())
734 {
735 std::stringstream aStrStream;
736 aStrStream << "idxcontent stylesheet missing" << std::endl;
737 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
738 }
739
740 idxContentStylesheet = fs::path(args[i], fs::native);
741 }
742 else if (args[i].compare("-o") == 0)
743 {
744 ++i;
745 if (i >= args.size())
746 {
747 std::stringstream aStrStream;
748 aStrStream << "outputfilename missing" << std::endl;
749 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
750 }
751
752 outputFile = fs::path(args[i], fs::native);
753 }
754 else if (args[i].compare("-mod") == 0)
755 {
756 ++i;
757 if (i >= args.size())
758 {
759 std::stringstream aStrStream;
760 aStrStream << "module name missing" << std::endl;
761 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
762 }
763
764 module = args[i];
765 }
766 else if (args[i].compare("-lang") == 0)
767 {
768 ++i;
769 if (i >= args.size())
770 {
771 std::stringstream aStrStream;
772 aStrStream << "language name missing" << std::endl;
773 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
774 }
775
776 lang = args[i];
777 }
778 else if (args[i].compare("-hid") == 0)
779 {
780 ++i;
781 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, "obsolete -hid argument used" );
782 }
783 else if (args[i].compare("-add") == 0)
784 {
785 std::string addFile, addFileUnderPath;
786 ++i;
787 if (i >= args.size())
788 {
789 std::stringstream aStrStream;
790 aStrStream << "pathname missing" << std::endl;
791 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
792 }
793
794 addFileUnderPath = args[i];
795 ++i;
796 if (i >= args.size())
797 {
798 std::stringstream aStrStream;
799 aStrStream << "pathname missing" << std::endl;
800 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
801 }
802 addFile = args[i];
803 if (!addFileUnderPath.empty() && !addFile.empty())
804 additionalFiles[addFileUnderPath] = addFile;
805 }
806 else
807 helpFiles.push_back(args[i]);
808 ++i;
809 }
810
811 //We can be called from the helplinker executable or the extension manager
812 //In the latter case extsource is not used.
813 if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath)
814 || !extsource.empty())
815 {
816 bExtensionMode = true;
817 if (!extsource.empty())
818 {
819 //called from helplinker.exe, pExtensionPath and pOfficeHelpPath
820 //should be NULL
821 sourceRoot = fs::path(extsource, fs::native);
822 extensionPath = sourceRoot.toUTF8();
823
824 if (extdestination.empty())
825 {
826 std::stringstream aStrStream;
827 aStrStream << "-extlangdest is missing" << std::endl;
828 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
829 }
830 else
831 {
832 //Convert from system path to file URL!!!
833 fs::path p(extdestination, fs::native);
834 extensionDestination = p.toUTF8();
835 }
836 }
837 else
838 { //called from extension manager
839 extensionPath = *pExtensionPath;
840 sourceRoot = fs::path(extensionPath);
841 extensionDestination = *pDestination;
842 }
843 //check if -src option was used. This option must not be used
844 //when extension help is compiled.
845 if (bSrcOption)
846 {
847 std::stringstream aStrStream;
848 aStrStream << "-src must not be used together with -extsource missing" << std::endl;
849 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
850 }
851 }
852
853 if (!bExtensionMode && zipdir.empty())
854 {
855 std::stringstream aStrStream;
856 aStrStream << "no index dir given" << std::endl;
857 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
858 }
859
860 if (!bExtensionMode && idxCaptionStylesheet.empty()
861 || !extsource.empty() && idxCaptionStylesheet.empty())
862 {
863 //No extension mode and extension mode using commandline
864 //!extsource.empty indicates extension mode using commandline
865 // -idxcaption parameter is required
866 std::stringstream aStrStream;
867 aStrStream << "no index caption stylesheet given" << std::endl;
868 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
869 }
870 else if ( bExtensionMode && extsource.empty())
871 {
872 //This part is used when compileExtensionHelp is called from the extensions manager.
873 //If extension help is compiled using helplinker in the build process
874 rtl::OUString aIdxCaptionPathFileURL( *pOfficeHelpPath );
875 aIdxCaptionPathFileURL += rtl::OUString::createFromAscii( "/idxcaption.xsl" );
876
877 rtl::OString aOStr_IdxCaptionPathFileURL( rtl::OUStringToOString
878 ( aIdxCaptionPathFileURL, fs::getThreadTextEncoding() ) );
879 std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() );
880
881 idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL );
882 }
883
884 if (!bExtensionMode && idxContentStylesheet.empty()
885 || !extsource.empty() && idxContentStylesheet.empty())
886 {
887 //No extension mode and extension mode using commandline
888 //!extsource.empty indicates extension mode using commandline
889 // -idxcontent parameter is required
890 std::stringstream aStrStream;
891 aStrStream << "no index content stylesheet given" << std::endl;
892 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
893 }
894 else if ( bExtensionMode && extsource.empty())
895 {
896 //If extension help is compiled using helplinker in the build process
897 //then -idxcontent must be supplied
898 //This part is used when compileExtensionHelp is called from the extensions manager.
899 rtl::OUString aIdxContentPathFileURL( *pOfficeHelpPath );
900 aIdxContentPathFileURL += rtl::OUString::createFromAscii( "/idxcontent.xsl" );
901
902 rtl::OString aOStr_IdxContentPathFileURL( rtl::OUStringToOString
903 ( aIdxContentPathFileURL, fs::getThreadTextEncoding() ) );
904 std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() );
905
906 idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL );
907 }
908 if (!bExtensionMode && embeddStylesheet.empty())
909 {
910 std::stringstream aStrStream;
911 aStrStream << "no embedding resolving file given" << std::endl;
912 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
913 }
914 if (sourceRoot.empty())
915 {
916 std::stringstream aStrStream;
917 aStrStream << "no sourceroot given" << std::endl;
918 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
919 }
920 if (!bExtensionMode && outputFile.empty())
921 {
922 std::stringstream aStrStream;
923 aStrStream << "no output file given" << std::endl;
924 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
925 }
926 if (module.empty())
927 {
928 std::stringstream aStrStream;
929 aStrStream << "module missing" << std::endl;
930 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
931 }
932 if (!bExtensionMode && lang.empty())
933 {
934 std::stringstream aStrStream;
935 aStrStream << "language missing" << std::endl;
936 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
937 }
938 link();
939 }
940
main(int argc,char ** argv)941 int main(int argc, char**argv)
942 {
943 sal_uInt32 starttime = osl_getGlobalTimer();
944 std::vector<std::string> args;
945 for (int i = 1; i < argc; ++i)
946 args.push_back(std::string(argv[i]));
947 try
948 {
949 HelpLinker* pHelpLinker = new HelpLinker();
950 pHelpLinker->main( args );
951 delete pHelpLinker;
952 }
953 catch( const HelpProcessingException& e )
954 {
955 std::cerr << e.m_aErrorMsg;
956 exit(1);
957 }
958 sal_uInt32 endtime = osl_getGlobalTimer();
959 #ifndef OS2 // YD @TODO@ crashes libc runtime :-(
960 std::cout << "time taken was " << (endtime-starttime)/1000.0 << " seconds" << std::endl;
961 #endif
962 return 0;
963 }
964
965 // Variable to set an exception in "C" StructuredXMLErrorFunction
966 static const HelpProcessingException* GpXMLParsingException = NULL;
967
StructuredXMLErrorFunction(void * userData,xmlErrorPtr error)968 extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
969 {
970 (void)userData;
971 (void)error;
972
973 std::string aErrorMsg = error->message;
974 std::string aXMLParsingFile;
975 if( error->file != NULL )
976 aXMLParsingFile = error->file;
977 int nXMLParsingLine = error->line;
978 HelpProcessingException* pException = new HelpProcessingException( aErrorMsg, aXMLParsingFile, nXMLParsingLine );
979 GpXMLParsingException = pException;
980
981 // Reset error handler
982 xmlSetStructuredErrorFunc( NULL, NULL );
983 }
984
operator =(const struct HelpProcessingException & e)985 HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e )
986 {
987 m_eErrorClass = e.m_eErrorClass;
988 rtl::OString tmpErrorMsg( e.m_aErrorMsg.c_str() );
989 m_aErrorMsg = rtl::OStringToOUString( tmpErrorMsg, fs::getThreadTextEncoding() );
990 rtl::OString tmpXMLParsingFile( e.m_aXMLParsingFile.c_str() );
991 m_aXMLParsingFile = rtl::OStringToOUString( tmpXMLParsingFile, fs::getThreadTextEncoding() );
992 m_nXMLParsingLine = e.m_nXMLParsingLine;
993 return *this;
994 }
995
996
997 // Returns true in case of success, false in case of error
compileExtensionHelp(const rtl::OUString & aOfficeHelpPath,const rtl::OUString & aExtensionName,const rtl::OUString & aExtensionLanguageRoot,sal_Int32 nXhpFileCount,const rtl::OUString * pXhpFiles,const rtl::OUString & aDestination,HelpProcessingErrorInfo & o_rHelpProcessingErrorInfo)998 HELPLINKER_DLLPUBLIC bool compileExtensionHelp
999 (
1000 const rtl::OUString& aOfficeHelpPath,
1001 const rtl::OUString& aExtensionName,
1002 const rtl::OUString& aExtensionLanguageRoot,
1003 sal_Int32 nXhpFileCount, const rtl::OUString* pXhpFiles,
1004 const rtl::OUString& aDestination,
1005 HelpProcessingErrorInfo& o_rHelpProcessingErrorInfo
1006 )
1007 {
1008 bool bSuccess = true;
1009
1010 std::vector<std::string> args;
1011 args.reserve(nXhpFileCount + 2);
1012 args.push_back(std::string("-mod"));
1013 rtl::OString aOExtensionName = rtl::OUStringToOString( aExtensionName, fs::getThreadTextEncoding() );
1014 args.push_back(std::string(aOExtensionName.getStr()));
1015
1016 for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
1017 {
1018 rtl::OUString aXhpFile = pXhpFiles[iXhp];
1019
1020 rtl::OString aOXhpFile = rtl::OUStringToOString( aXhpFile, fs::getThreadTextEncoding() );
1021 args.push_back(std::string(aOXhpFile.getStr()));
1022 }
1023
1024 rtl::OString aOExtensionLanguageRoot = rtl::OUStringToOString( aExtensionLanguageRoot, fs::getThreadTextEncoding() );
1025 const char* pExtensionPath = aOExtensionLanguageRoot.getStr();
1026 std::string aStdStrExtensionPath = pExtensionPath;
1027 rtl::OString aODestination = rtl::OUStringToOString(aDestination, fs::getThreadTextEncoding());
1028 const char* pDestination = aODestination.getStr();
1029 std::string aStdStrDestination = pDestination;
1030
1031 // Set error handler
1032 xmlSetStructuredErrorFunc( NULL, (xmlStructuredErrorFunc)StructuredXMLErrorFunction );
1033 try
1034 {
1035 HelpLinker* pHelpLinker = new HelpLinker();
1036 pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
1037 delete pHelpLinker;
1038 }
1039 catch( const HelpProcessingException& e )
1040 {
1041 if( GpXMLParsingException != NULL )
1042 {
1043 o_rHelpProcessingErrorInfo = *GpXMLParsingException;
1044 delete GpXMLParsingException;
1045 GpXMLParsingException = NULL;
1046 }
1047 else
1048 {
1049 o_rHelpProcessingErrorInfo = e;
1050 }
1051 bSuccess = false;
1052 }
1053 // Reset error handler
1054 xmlSetStructuredErrorFunc( NULL, NULL );
1055
1056 // i83624: Tree files
1057 ::rtl::OUString aTreeFileURL = aExtensionLanguageRoot;
1058 aTreeFileURL += rtl::OUString::createFromAscii( "/help.tree" );
1059 osl::DirectoryItem aTreeFileItem;
1060 osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
1061 osl::FileStatus aFileStatus( FileStatusMask_FileSize );
1062 if( rcGet == osl::FileBase::E_None &&
1063 aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
1064 aFileStatus.isValid( FileStatusMask_FileSize ) )
1065 {
1066 sal_uInt64 ret, len = aFileStatus.getFileSize();
1067 char* s = new char[ int(len) ]; // the buffer to hold the installed files
1068 osl::File aFile( aTreeFileURL );
1069 aFile.open( OpenFlag_Read );
1070 aFile.read( s, len, ret );
1071 aFile.close();
1072
1073 XML_Parser parser = XML_ParserCreate( 0 );
1074 int parsed = XML_Parse( parser, s, int( len ), true );
1075
1076 if( parsed == 0 )
1077 {
1078 XML_Error nError = XML_GetErrorCode( parser );
1079 o_rHelpProcessingErrorInfo.m_eErrorClass = HELPPROCESSING_XMLPARSING_ERROR;
1080 o_rHelpProcessingErrorInfo.m_aErrorMsg = rtl::OUString::createFromAscii( XML_ErrorString( nError ) );
1081 o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL;
1082 // CRASHES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser );
1083 bSuccess = false;
1084 }
1085
1086 XML_ParserFree( parser );
1087 delete[] s;
1088 }
1089
1090 return bSuccess;
1091 }
1092