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