xref: /trunk/main/l10ntools/source/help/HelpLinker.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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