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
238 throw( HelpProcessingException );
239
HelpLinker()240 HelpLinker()
241 : init(true)
242 , m_pIndexerPreProcessor(NULL)
243 {}
~HelpLinker()244 ~HelpLinker()
245 { delete m_pIndexerPreProcessor; }
246
247 private:
248 int locCount, totCount;
249 Stringtable additionalFiles;
250 HashSet helpFiles;
251 fs::path sourceRoot;
252 fs::path embeddStylesheet;
253 fs::path idxCaptionStylesheet;
254 fs::path idxContentStylesheet;
255 fs::path zipdir;
256 fs::path outputFile;
257 std::string extsource;
258 std::string extdestination;
259 std::string module;
260 std::string lang;
261 std::string extensionPath;
262 std::string extensionDestination;
263 bool bExtensionMode;
264 fs::path indexDirName;
265 fs::path indexDirParentName;
266 bool init;
267 IndexerPreProcessor* m_pIndexerPreProcessor;
268 void initIndexerPreProcessor();
269 void link() throw( HelpProcessingException );
270 void addBookmark( FILE* pFile_DBHelp, std::string thishid,
271 const std::string& fileB, const std::string& anchorB,
272 const std::string& jarfileB, const std::string& titleB );
273 };
274
275 namespace URLEncoder
276 {
encode(const std::string & rIn)277 static std::string encode(const std::string &rIn)
278 {
279 const char *good = "!$&'()*+,-.=@_";
280 static const char hex[17] = "0123456789ABCDEF";
281
282 std::string result;
283 for (size_t i=0; i < rIn.length(); ++i)
284 {
285 unsigned char c = rIn[i];
286 if (isalnum (c) || strchr (good, c))
287 result += c;
288 else {
289 result += '%';
290 result += hex[c >> 4];
291 result += hex[c & 0xf];
292 }
293 }
294 return result;
295 }
296 }
297
addBookmark(FILE * pFile_DBHelp,std::string thishid,const std::string & fileB,const std::string & anchorB,const std::string & jarfileB,const std::string & titleB)298 void HelpLinker::addBookmark( FILE* pFile_DBHelp, std::string thishid,
299 const std::string& fileB, const std::string& anchorB,
300 const std::string& jarfileB, const std::string& titleB)
301 {
302 HCDBG(std::cerr << "HelpLinker::addBookmark " << thishid << " " <<
303 fileB << " " << anchorB << " " << jarfileB << " " << titleB << std::endl);
304
305 thishid = URLEncoder::encode(thishid);
306
307 int fileLen = fileB.length();
308 if (!anchorB.empty())
309 fileLen += (1 + anchorB.length());
310 int dataLen = 1 + fileLen + 1 + jarfileB.length() + 1 + titleB.length();
311
312 std::vector<unsigned char> dataB(dataLen);
313 size_t i = 0;
314 dataB[i++] = static_cast<unsigned char>(fileLen);
315 for (size_t j = 0; j < fileB.length(); ++j)
316 dataB[i++] = fileB[j];
317 if (!anchorB.empty())
318 {
319 dataB[i++] = '#';
320 for (size_t j = 0; j < anchorB.length(); ++j)
321 dataB[i++] = anchorB[j];
322 }
323 dataB[i++] = static_cast<unsigned char>(jarfileB.length());
324 for (size_t j = 0; j < jarfileB.length(); ++j)
325 dataB[i++] = jarfileB[j];
326
327 dataB[i++] = static_cast<unsigned char>(titleB.length());
328 for (size_t j = 0; j < titleB.length(); ++j)
329 dataB[i++] = titleB[j];
330
331 if( pFile_DBHelp != NULL )
332 {
333 std::string aValueStr( dataB.begin(), dataB.end() );
334 writeKeyValue_DBHelp( pFile_DBHelp, thishid, aValueStr );
335 }
336 }
337
initIndexerPreProcessor()338 void HelpLinker::initIndexerPreProcessor()
339 {
340 if( m_pIndexerPreProcessor )
341 delete m_pIndexerPreProcessor;
342 std::string mod = module;
343 std::transform (mod.begin(), mod.end(), mod.begin(), tolower);
344 m_pIndexerPreProcessor = new IndexerPreProcessor( mod, indexDirParentName,
345 idxCaptionStylesheet, idxContentStylesheet );
346 }
347
348 /**
349 *
350 */
link()351 void HelpLinker::link() throw( HelpProcessingException )
352 {
353 bool bIndexForExtension = true;
354
355 if( bExtensionMode )
356 {
357 //indexDirParentName = sourceRoot;
358 indexDirParentName = extensionDestination;
359 }
360 else
361 {
362 indexDirParentName = zipdir;
363 fs::create_directory(indexDirParentName);
364 }
365
366 std::string mod = module;
367 std::transform (mod.begin(), mod.end(), mod.begin(), tolower);
368
369 // do the work here
370 // continue with introduction of the overall process thing into the
371 // here all hzip files will be worked on
372 std::string appl = mod;
373 if (appl[0] == 's')
374 appl = appl.substr(1);
375
376 bool bUse_ = true;
377 if( !bExtensionMode )
378 bUse_ = false;
379
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
386
387 FILE* pFileHelpText_DBHelp = fopen
388 ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" );
389 #endif
390
391 fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
392 #ifdef WNT
393 //We need _wfopen to support long file paths on Windows XP
394 FILE* pFileDbBase_DBHelp = _wfopen
395 ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" );
396 #else
397 FILE* pFileDbBase_DBHelp = fopen
398 ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" );
399 #endif
400
401 fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
402
403 HelpKeyword helpKeyword;
404
405 // catch HelpProcessingException to avoid locking data bases
406 try
407 {
408
409 // lastly, initialize the indexBuilder
410 if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
411 initIndexerPreProcessor();
412
413 if( !bExtensionMode )
414 {
415 #ifndef OS2 // YD @TODO@ crashes libc runtime :-(
416 std::cout << "Making " << outputFile.native_file_string() <<
417 " from " << helpFiles.size() << " input files" << std::endl;
418 #endif
419 }
420
421 // here we start our loop over the hzip files.
422 HashSet::iterator end = helpFiles.end();
423 for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter)
424 {
425 if( !bExtensionMode )
426 {
427 std::cout << ".";
428 std::cout.flush();
429 }
430
431 // process one file
432 // streamTable contains the streams in the hzip file
433 StreamTable streamTable;
434 const std::string &xhpFileName = *iter;
435
436 if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
437 {
438 // only work on .xhp - files
439 std::cerr <<
440 "ERROR: input list entry '"
441 << xhpFileName
442 << "' has the wrong extension (only files with extension .xhp "
443 << "are accepted)";
444 continue;
445 }
446
447 fs::path langsourceRoot(sourceRoot);
448 fs::path xhpFile;
449
450 if( bExtensionMode )
451 {
452 // langsourceRoot == sourceRoot for extensions
453 std::string xhpFileNameComplete( extensionPath );
454 xhpFileNameComplete.append( '/' + xhpFileName );
455 xhpFile = fs::path( xhpFileNameComplete );
456 }
457 else
458 {
459 langsourceRoot.append('/' + lang + '/');
460 xhpFile = fs::path(xhpFileName, fs::native);
461 }
462
463 HelpCompiler hc( streamTable, xhpFile, langsourceRoot,
464 embeddStylesheet, module, lang, bExtensionMode );
465
466 HCDBG(std::cerr << "before compile of " << xhpFileName << std::endl);
467 bool success = hc.compile();
468 HCDBG(std::cerr << "after compile of " << xhpFileName << std::endl);
469
470 if (!success && !bExtensionMode)
471 {
472 std::stringstream aStrStream;
473 aStrStream <<
474 "\nERROR: compiling help particle '"
475 << xhpFileName
476 << "' for language '"
477 << lang
478 << "' failed!";
479 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
480 }
481
482 const std::string documentBaseId = streamTable.document_id;
483 std::string documentPath = streamTable.document_path;
484 if (documentPath.find("/") == 0)
485 documentPath = documentPath.substr(1);
486
487 std::string documentJarfile = streamTable.document_module + ".jar";
488
489 std::string documentTitle = streamTable.document_title;
490 if (documentTitle.empty())
491 documentTitle = "<notitle>";
492
493 const std::string& fileB = documentPath;
494 const std::string& jarfileB = documentJarfile;
495 std::string& titleB = documentTitle;
496
497 // add once this as its own id.
498 addBookmark(pFileDbBase_DBHelp, documentPath, fileB, std::string(), jarfileB, titleB);
499
500 const HashSet *hidlist = streamTable.appl_hidlist;
501 if (!hidlist)
502 hidlist = streamTable.default_hidlist;
503 if (hidlist && !hidlist->empty())
504 {
505 // now iterate over all elements of the hidlist
506 HashSet::const_iterator aEnd = hidlist->end();
507 for (HashSet::const_iterator hidListIter = hidlist->begin();
508 hidListIter != aEnd; ++hidListIter)
509 {
510 std::string thishid = *hidListIter;
511
512 std::string anchorB;
513 size_t index = thishid.rfind('#');
514 if (index != std::string::npos)
515 {
516 anchorB = thishid.substr(1 + index);
517 thishid = thishid.substr(0, index);
518 }
519 addBookmark(pFileDbBase_DBHelp, thishid, fileB, anchorB, jarfileB, titleB);
520 }
521 }
522
523 // now the keywords
524 const Hashtable *anchorToLL = streamTable.appl_keywords;
525 if (!anchorToLL)
526 anchorToLL = streamTable.default_keywords;
527 if (anchorToLL && !anchorToLL->empty())
528 {
529 std::string fakedHid = URLEncoder::encode(documentPath);
530 Hashtable::const_iterator aEnd = anchorToLL->end();
531 for (Hashtable::const_iterator enumer = anchorToLL->begin();
532 enumer != aEnd; ++enumer)
533 {
534 const std::string &anchor = enumer->first;
535 addBookmark(pFileDbBase_DBHelp, documentPath, fileB,
536 anchor, jarfileB, titleB);
537 std::string totalId = fakedHid + "#" + anchor;
538 // std::cerr << hzipFileName << std::endl;
539 const LinkedList& ll = enumer->second;
540 LinkedList::const_iterator aOtherEnd = ll.end();
541 for (LinkedList::const_iterator llIter = ll.begin();
542 llIter != aOtherEnd; ++llIter)
543 {
544 helpKeyword.insert(*llIter, totalId);
545 }
546 }
547
548 }
549
550 // and last the helptexts
551 const Stringtable *helpTextHash = streamTable.appl_helptexts;
552 if (!helpTextHash)
553 helpTextHash = streamTable.default_helptexts;
554 if (helpTextHash && !helpTextHash->empty())
555 {
556 Stringtable::const_iterator aEnd = helpTextHash->end();
557 for (Stringtable::const_iterator helpTextIter = helpTextHash->begin();
558 helpTextIter != aEnd; ++helpTextIter)
559 {
560 std::string helpTextId = helpTextIter->first;
561 const std::string& helpTextText = helpTextIter->second;
562
563 helpTextId = URLEncoder::encode(helpTextId);
564
565 if( pFileHelpText_DBHelp != NULL )
566 writeKeyValue_DBHelp( pFileHelpText_DBHelp, helpTextId, helpTextText );
567 }
568 }
569
570 //IndexerPreProcessor
571 if( !bExtensionMode || bIndexForExtension )
572 {
573 // now the indexing
574 xmlDocPtr document = streamTable.appl_doc;
575 if (!document)
576 document = streamTable.default_doc;
577 if (document)
578 {
579 std::string temp = module;
580 std::transform (temp.begin(), temp.end(), temp.begin(), tolower);
581 m_pIndexerPreProcessor->processDocument(document, URLEncoder::encode(documentPath) );
582 }
583 }
584
585 } // while loop over hzip files ending
586 if( !bExtensionMode )
587 std::cout << std::endl;
588
589 } // try
590 catch( const HelpProcessingException& )
591 {
592 // catch HelpProcessingException to avoid locking data bases
593 if( pFileHelpText_DBHelp != NULL )
594 fclose( pFileHelpText_DBHelp );
595 if( pFileDbBase_DBHelp != NULL )
596 fclose( pFileDbBase_DBHelp );
597 throw;
598 }
599
600 if( pFileHelpText_DBHelp != NULL )
601 fclose( pFileHelpText_DBHelp );
602 if( pFileDbBase_DBHelp != NULL )
603 fclose( pFileDbBase_DBHelp );
604
605 helpKeyword.dump_DBHelp( keyWordFileName_DBHelp);
606
607 if( !bExtensionMode )
608 {
609 // New index
610 Stringtable::iterator aEnd = additionalFiles.end();
611 for (Stringtable::iterator enumer = additionalFiles.begin(); enumer != aEnd;
612 ++enumer)
613 {
614 const std::string &additionalFileName = enumer->second;
615 const std::string &additionalFileKey = enumer->first;
616
617 fs::path fsAdditionalFileName( additionalFileName, fs::native );
618 std::string aNativeStr = fsAdditionalFileName.native_file_string();
619 const char* pStr = aNativeStr.c_str();
620 std::cerr << pStr;
621
622 fs::path fsTargetName( indexDirParentName / additionalFileKey );
623
624 fs::copy( fsAdditionalFileName, fsTargetName );
625 }
626 }
627
628 }
629
630
main(std::vector<std::string> & args,std::string * pExtensionPath,std::string * pDestination,const rtl::OUString * pOfficeHelpPath)631 void HelpLinker::main( std::vector<std::string> &args,
632 std::string* pExtensionPath, std::string* pDestination,
633 const rtl::OUString* pOfficeHelpPath )
634 throw( HelpProcessingException )
635 {
636 bExtensionMode = false;
637 helpFiles.clear();
638
639 if (args.size() > 0 && args[0][0] == '@')
640 {
641 std::vector<std::string> stringList;
642 std::string strBuf;
643 std::ifstream fileReader(args[0].substr(1).c_str());
644
645 while (fileReader)
646 {
647 std::string token;
648 fileReader >> token;
649 if (!token.empty())
650 stringList.push_back(token);
651 }
652 fileReader.close();
653
654 args = stringList;
655 }
656
657 size_t i = 0;
658 bool bSrcOption = false;
659 while (i < args.size())
660 {
661 if (args[i].compare("-extlangsrc") == 0)
662 {
663 ++i;
664 if (i >= args.size())
665 {
666 std::stringstream aStrStream;
667 aStrStream << "extension source missing" << std::endl;
668 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
669 }
670 extsource = args[i];
671 }
672 else if (args[i].compare("-extlangdest") == 0)
673 {
674 //If this argument is not provided then the location provided in -extsource will
675 //also be the destination
676 ++i;
677 if (i >= args.size())
678 {
679 std::stringstream aStrStream;
680 aStrStream << "extension destination missing" << std::endl;
681 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
682 }
683 extdestination = args[i];
684 }
685 else if (args[i].compare("-src") == 0)
686 {
687 ++i;
688 if (i >= args.size())
689 {
690 std::stringstream aStrStream;
691 aStrStream << "sourceroot missing" << std::endl;
692 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
693 }
694 bSrcOption = true;
695 sourceRoot = fs::path(args[i], fs::native);
696 }
697 else if (args[i].compare("-sty") == 0)
698 {
699 ++i;
700 if (i >= args.size())
701 {
702 std::stringstream aStrStream;
703 aStrStream << "embeddingStylesheet missing" << std::endl;
704 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
705 }
706
707 embeddStylesheet = fs::path(args[i], fs::native);
708 }
709 else if (args[i].compare("-zipdir") == 0)
710 {
711 ++i;
712 if (i >= args.size())
713 {
714 std::stringstream aStrStream;
715 aStrStream << "idxtemp missing" << std::endl;
716 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
717 }
718
719 zipdir = fs::path(args[i], fs::native);
720 }
721 else if (args[i].compare("-idxcaption") == 0)
722 {
723 ++i;
724 if (i >= args.size())
725 {
726 std::stringstream aStrStream;
727 aStrStream << "idxcaption stylesheet missing" << std::endl;
728 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
729 }
730
731 idxCaptionStylesheet = fs::path(args[i], fs::native);
732 }
733 else if (args[i].compare("-idxcontent") == 0)
734 {
735 ++i;
736 if (i >= args.size())
737 {
738 std::stringstream aStrStream;
739 aStrStream << "idxcontent stylesheet missing" << std::endl;
740 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
741 }
742
743 idxContentStylesheet = fs::path(args[i], fs::native);
744 }
745 else if (args[i].compare("-o") == 0)
746 {
747 ++i;
748 if (i >= args.size())
749 {
750 std::stringstream aStrStream;
751 aStrStream << "outputfilename missing" << std::endl;
752 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
753 }
754
755 outputFile = fs::path(args[i], fs::native);
756 }
757 else if (args[i].compare("-mod") == 0)
758 {
759 ++i;
760 if (i >= args.size())
761 {
762 std::stringstream aStrStream;
763 aStrStream << "module name missing" << std::endl;
764 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
765 }
766
767 module = args[i];
768 }
769 else if (args[i].compare("-lang") == 0)
770 {
771 ++i;
772 if (i >= args.size())
773 {
774 std::stringstream aStrStream;
775 aStrStream << "language name missing" << std::endl;
776 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
777 }
778
779 lang = args[i];
780 }
781 else if (args[i].compare("-hid") == 0)
782 {
783 ++i;
784 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, "obsolete -hid argument used" );
785 }
786 else if (args[i].compare("-add") == 0)
787 {
788 std::string addFile, addFileUnderPath;
789 ++i;
790 if (i >= args.size())
791 {
792 std::stringstream aStrStream;
793 aStrStream << "pathname missing" << std::endl;
794 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
795 }
796
797 addFileUnderPath = args[i];
798 ++i;
799 if (i >= args.size())
800 {
801 std::stringstream aStrStream;
802 aStrStream << "pathname missing" << std::endl;
803 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
804 }
805 addFile = args[i];
806 if (!addFileUnderPath.empty() && !addFile.empty())
807 additionalFiles[addFileUnderPath] = addFile;
808 }
809 else
810 helpFiles.push_back(args[i]);
811 ++i;
812 }
813
814 //We can be called from the helplinker executable or the extension manager
815 //In the latter case extsource is not used.
816 if( (pExtensionPath && pExtensionPath->length() > 0 && pOfficeHelpPath)
817 || !extsource.empty())
818 {
819 bExtensionMode = true;
820 if (!extsource.empty())
821 {
822 //called from helplinker.exe, pExtensionPath and pOfficeHelpPath
823 //should be NULL
824 sourceRoot = fs::path(extsource, fs::native);
825 extensionPath = sourceRoot.toUTF8();
826
827 if (extdestination.empty())
828 {
829 std::stringstream aStrStream;
830 aStrStream << "-extlangdest is missing" << std::endl;
831 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
832 }
833 else
834 {
835 //Convert from system path to file URL!!!
836 fs::path p(extdestination, fs::native);
837 extensionDestination = p.toUTF8();
838 }
839 }
840 else
841 { //called from extension manager
842 extensionPath = *pExtensionPath;
843 sourceRoot = fs::path(extensionPath);
844 extensionDestination = *pDestination;
845 }
846 //check if -src option was used. This option must not be used
847 //when extension help is compiled.
848 if (bSrcOption)
849 {
850 std::stringstream aStrStream;
851 aStrStream << "-src must not be used together with -extsource missing" << std::endl;
852 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
853 }
854 }
855
856 if (!bExtensionMode && zipdir.empty())
857 {
858 std::stringstream aStrStream;
859 aStrStream << "no index dir given" << std::endl;
860 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
861 }
862
863 if (!bExtensionMode && idxCaptionStylesheet.empty()
864 || !extsource.empty() && idxCaptionStylesheet.empty())
865 {
866 //No extension mode and extension mode using commandline
867 //!extsource.empty indicates extension mode using commandline
868 // -idxcaption paramter is required
869 std::stringstream aStrStream;
870 aStrStream << "no index caption stylesheet given" << std::endl;
871 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
872 }
873 else if ( bExtensionMode && extsource.empty())
874 {
875 //This part is used when compileExtensionHelp is called from the extensions manager.
876 //If extension help is compiled using helplinker in the build process
877 rtl::OUString aIdxCaptionPathFileURL( *pOfficeHelpPath );
878 aIdxCaptionPathFileURL += rtl::OUString::createFromAscii( "/idxcaption.xsl" );
879
880 rtl::OString aOStr_IdxCaptionPathFileURL( rtl::OUStringToOString
881 ( aIdxCaptionPathFileURL, fs::getThreadTextEncoding() ) );
882 std::string aStdStr_IdxCaptionPathFileURL( aOStr_IdxCaptionPathFileURL.getStr() );
883
884 idxCaptionStylesheet = fs::path( aStdStr_IdxCaptionPathFileURL );
885 }
886
887 if (!bExtensionMode && idxContentStylesheet.empty()
888 || !extsource.empty() && idxContentStylesheet.empty())
889 {
890 //No extension mode and extension mode using commandline
891 //!extsource.empty indicates extension mode using commandline
892 // -idxcontent paramter is required
893 std::stringstream aStrStream;
894 aStrStream << "no index content stylesheet given" << std::endl;
895 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
896 }
897 else if ( bExtensionMode && extsource.empty())
898 {
899 //If extension help is compiled using helplinker in the build process
900 //then -idxcontent must be supplied
901 //This part is used when compileExtensionHelp is called from the extensions manager.
902 rtl::OUString aIdxContentPathFileURL( *pOfficeHelpPath );
903 aIdxContentPathFileURL += rtl::OUString::createFromAscii( "/idxcontent.xsl" );
904
905 rtl::OString aOStr_IdxContentPathFileURL( rtl::OUStringToOString
906 ( aIdxContentPathFileURL, fs::getThreadTextEncoding() ) );
907 std::string aStdStr_IdxContentPathFileURL( aOStr_IdxContentPathFileURL.getStr() );
908
909 idxContentStylesheet = fs::path( aStdStr_IdxContentPathFileURL );
910 }
911 if (!bExtensionMode && embeddStylesheet.empty())
912 {
913 std::stringstream aStrStream;
914 aStrStream << "no embedding resolving file given" << std::endl;
915 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
916 }
917 if (sourceRoot.empty())
918 {
919 std::stringstream aStrStream;
920 aStrStream << "no sourceroot given" << std::endl;
921 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
922 }
923 if (!bExtensionMode && outputFile.empty())
924 {
925 std::stringstream aStrStream;
926 aStrStream << "no output file given" << std::endl;
927 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
928 }
929 if (module.empty())
930 {
931 std::stringstream aStrStream;
932 aStrStream << "module missing" << std::endl;
933 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
934 }
935 if (!bExtensionMode && lang.empty())
936 {
937 std::stringstream aStrStream;
938 aStrStream << "language missing" << std::endl;
939 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
940 }
941 link();
942 }
943
main(int argc,char ** argv)944 int main(int argc, char**argv)
945 {
946 sal_uInt32 starttime = osl_getGlobalTimer();
947 std::vector<std::string> args;
948 for (int i = 1; i < argc; ++i)
949 args.push_back(std::string(argv[i]));
950 try
951 {
952 HelpLinker* pHelpLinker = new HelpLinker();
953 pHelpLinker->main( args );
954 delete pHelpLinker;
955 }
956 catch( const HelpProcessingException& e )
957 {
958 std::cerr << e.m_aErrorMsg;
959 exit(1);
960 }
961 sal_uInt32 endtime = osl_getGlobalTimer();
962 #ifndef OS2 // YD @TODO@ crashes libc runtime :-(
963 std::cout << "time taken was " << (endtime-starttime)/1000.0 << " seconds" << std::endl;
964 #endif
965 return 0;
966 }
967
968 // Variable to set an exception in "C" StructuredXMLErrorFunction
969 static const HelpProcessingException* GpXMLParsingException = NULL;
970
StructuredXMLErrorFunction(void * userData,xmlErrorPtr error)971 extern "C" void StructuredXMLErrorFunction(void *userData, xmlErrorPtr error)
972 {
973 (void)userData;
974 (void)error;
975
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;
983
984 // Reset error handler
985 xmlSetStructuredErrorFunc( NULL, NULL );
986 }
987
operator =(const struct HelpProcessingException & e)988 HelpProcessingErrorInfo& HelpProcessingErrorInfo::operator=( const struct HelpProcessingException& e )
989 {
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;
997 }
998
999
1000 // 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)1001 HELPLINKER_DLLPUBLIC bool compileExtensionHelp
1002 (
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
1009 )
1010 {
1011 bool bSuccess = true;
1012
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()));
1018
1019 for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
1020 {
1021 rtl::OUString aXhpFile = pXhpFiles[iXhp];
1022
1023 rtl::OString aOXhpFile = rtl::OUStringToOString( aXhpFile, fs::getThreadTextEncoding() );
1024 args.push_back(std::string(aOXhpFile.getStr()));
1025 }
1026
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;
1033
1034 // Set error handler
1035 xmlSetStructuredErrorFunc( NULL, (xmlStructuredErrorFunc)StructuredXMLErrorFunction );
1036 try
1037 {
1038 HelpLinker* pHelpLinker = new HelpLinker();
1039 pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
1040 delete pHelpLinker;
1041 }
1042 catch( const HelpProcessingException& e )
1043 {
1044 if( GpXMLParsingException != NULL )
1045 {
1046 o_rHelpProcessingErrorInfo = *GpXMLParsingException;
1047 delete GpXMLParsingException;
1048 GpXMLParsingException = NULL;
1049 }
1050 else
1051 {
1052 o_rHelpProcessingErrorInfo = e;
1053 }
1054 bSuccess = false;
1055 }
1056 // Reset error handler
1057 xmlSetStructuredErrorFunc( NULL, NULL );
1058
1059 // i83624: Tree files
1060 ::rtl::OUString aTreeFileURL = aExtensionLanguageRoot;
1061 aTreeFileURL += rtl::OUString::createFromAscii( "/help.tree" );
1062 osl::DirectoryItem aTreeFileItem;
1063 osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
1064 osl::FileStatus aFileStatus( FileStatusMask_FileSize );
1065 if( rcGet == osl::FileBase::E_None &&
1066 aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
1067 aFileStatus.isValid( FileStatusMask_FileSize ) )
1068 {
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( OpenFlag_Read );
1073 aFile.read( s, len, ret );
1074 aFile.close();
1075
1076 XML_Parser parser = XML_ParserCreate( 0 );
1077 int parsed = XML_Parse( parser, s, int( len ), true );
1078
1079 if( parsed == 0 )
1080 {
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;
1087 }
1088
1089 XML_ParserFree( parser );
1090 delete[] s;
1091 }
1092
1093 return bSuccess;
1094 }
1095
1096