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