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