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 : (reinterpret_cast<const xmlChar *>(idxCaptionStylesheet.native_file_string().c_str()));
56 : m_xsltStylesheetPtrContent = xsltParseStylesheetFile
57 0 : (reinterpret_cast<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 std::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 :
287 0 : if( bExtensionMode )
288 : {
289 0 : indexDirParentName = extensionDestination;
290 : }
291 : else
292 : {
293 0 : indexDirParentName = zipdir;
294 0 : fs::create_directory(indexDirParentName);
295 : }
296 :
297 0 : std::string mod = module;
298 0 : std::transform (mod.begin(), mod.end(), mod.begin(), tocharlower);
299 :
300 : // do the work here
301 : // continue with introduction of the overall process thing into the
302 : // here all hzip files will be worked on
303 0 : std::string appl = mod;
304 0 : if (appl[0] == 's')
305 0 : appl = appl.substr(1);
306 :
307 0 : bool bUse_ = true;
308 0 : if( !bExtensionMode )
309 0 : bUse_ = false;
310 :
311 0 : fs::path helpTextFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".ht_" : ".ht")));
312 : #ifdef WNT
313 : //We need _wfopen to support long file paths on Windows XP
314 : FILE* pFileHelpText_DBHelp = _wfopen
315 : ( helpTextFileName_DBHelp.native_file_string_w(), L"wb" );
316 : #else
317 :
318 : FILE* pFileHelpText_DBHelp = fopen
319 0 : ( helpTextFileName_DBHelp.native_file_string().c_str(), "wb" );
320 : #endif
321 :
322 0 : fs::path dbBaseFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".db_" : ".db")));
323 : #ifdef WNT
324 : //We need _wfopen to support long file paths on Windows XP
325 : FILE* pFileDbBase_DBHelp = _wfopen
326 : ( dbBaseFileName_DBHelp.native_file_string_w(), L"wb" );
327 : #else
328 : FILE* pFileDbBase_DBHelp = fopen
329 0 : ( dbBaseFileName_DBHelp.native_file_string().c_str(), "wb" );
330 : #endif
331 :
332 0 : fs::path keyWordFileName_DBHelp(indexDirParentName / (mod + (bUse_ ? ".key_" : ".key")));
333 :
334 0 : HelpKeyword helpKeyword;
335 :
336 : // catch HelpProcessingException to avoid locking data bases
337 : try
338 : {
339 0 : bool bIndexForExtension = true;
340 : // lastly, initialize the indexBuilder
341 0 : if ( (!bExtensionMode || bIndexForExtension) && !helpFiles.empty())
342 0 : initIndexerPreProcessor();
343 :
344 : // here we start our loop over the hzip files.
345 0 : HashSet::iterator end = helpFiles.end();
346 0 : for (HashSet::iterator iter = helpFiles.begin(); iter != end; ++iter)
347 : {
348 : // process one file
349 : // streamTable contains the streams in the hzip file
350 0 : StreamTable streamTable;
351 0 : const std::string &xhpFileName = *iter;
352 :
353 0 : if (!bExtensionMode && xhpFileName.rfind(".xhp") != xhpFileName.length()-4)
354 : {
355 : // only work on .xhp - files
356 : SAL_WARN("helpcompiler",
357 : "ERROR: input list entry '"
358 : << xhpFileName
359 : << "' has the wrong extension (only files with extension .xhp 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 : }
507 :
508 : }
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 parameter 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 += "/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 parameter 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 += "/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 : 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, StructuredXMLErrorFunction );
947 : try
948 : {
949 0 : boost::scoped_ptr<HelpLinker> pHelpLinker(new HelpLinker());
950 0 : pHelpLinker->main( args, &aStdStrExtensionPath, &aStdStrDestination, &aOfficeHelpPath );
951 : }
952 0 : catch( const HelpProcessingException& e )
953 : {
954 0 : if( GpXMLParsingException != NULL )
955 : {
956 0 : o_rHelpProcessingErrorInfo = *GpXMLParsingException;
957 0 : delete GpXMLParsingException;
958 0 : GpXMLParsingException = NULL;
959 : }
960 : else
961 : {
962 0 : o_rHelpProcessingErrorInfo = e;
963 : }
964 0 : bSuccess = false;
965 : }
966 : // Reset error handler
967 0 : xmlSetStructuredErrorFunc( NULL, NULL );
968 :
969 : // i83624: Tree files
970 : // The following basically checks if the help.tree is well formed XML.
971 : // Apparently there have been cases when translations contained
972 : // non-well-formed XML in the past.
973 0 : OUString aTreeFileURL = aExtensionLanguageRoot + "/help.tree";
974 0 : osl::DirectoryItem aTreeFileItem;
975 0 : osl::FileBase::RC rcGet = osl::DirectoryItem::get( aTreeFileURL, aTreeFileItem );
976 0 : osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
977 0 : if( rcGet == osl::FileBase::E_None &&
978 0 : aTreeFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None &&
979 0 : aFileStatus.isValid( osl_FileStatus_Mask_FileSize ) )
980 : {
981 0 : sal_uInt64 ret, len = aFileStatus.getFileSize();
982 0 : boost::scoped_array<char> s(new char[ int(len) ]); // the buffer to hold the installed files
983 0 : osl::File aFile( aTreeFileURL );
984 0 : aFile.open( osl_File_OpenFlag_Read );
985 0 : aFile.read( s.get(), len, ret );
986 0 : aFile.close();
987 :
988 0 : XML_Parser parser = XML_ParserCreate( 0 );
989 0 : XML_Status parsed = XML_Parse( parser, s.get(), int( len ), true );
990 :
991 0 : if (XML_STATUS_ERROR == parsed)
992 : {
993 0 : XML_Error nError = XML_GetErrorCode( parser );
994 0 : o_rHelpProcessingErrorInfo.m_eErrorClass = HELPPROCESSING_XMLPARSING_ERROR;
995 0 : o_rHelpProcessingErrorInfo.m_aErrorMsg = OUString::createFromAscii( XML_ErrorString( nError ) );;
996 0 : o_rHelpProcessingErrorInfo.m_aXMLParsingFile = aTreeFileURL;
997 : // CRAHSES!!! o_rHelpProcessingErrorInfo.m_nXMLParsingLine = XML_GetCurrentLineNumber( parser );
998 0 : bSuccess = false;
999 : }
1000 :
1001 0 : XML_ParserFree( parser );
1002 : }
1003 :
1004 0 : return bSuccess;
1005 336 : }
1006 :
1007 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|