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