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