1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_shell.hxx" 30 31 #include <tools/presys.h> 32 #if defined _MSC_VER 33 #pragma warning(push, 1) 34 #endif 35 #include <windows.h> 36 #if defined _MSC_VER 37 #pragma warning(pop) 38 #endif 39 #include <tools/postsys.h> 40 41 #define VCL_NEED_BASETSD 42 43 #include "cmdline.hxx" 44 45 #include "osl/thread.h" 46 #include "osl/process.h" 47 #include "osl/file.hxx" 48 #include "sal/main.h" 49 50 #include "tools/config.hxx" 51 #include "i18npool/mslangid.hxx" 52 53 #include <iostream> 54 #include <fstream> 55 #include <map> 56 #include <sstream> 57 #include <iterator> 58 #include <algorithm> 59 #include <string> 60 61 namespace /* private */ 62 { 63 64 using rtl::OUString; 65 using rtl::OString; 66 67 //########################################### 68 void ShowUsage() 69 { 70 std::cout << "Usage: -ulf ulf_file -rc rc_output_file -rct rc_template_file -rch rch_file -rcf rcf_file" << std::endl; 71 std::cout << "-ulf Name of the ulf file" << std::endl; 72 std::cout << "-rc Name of the resulting resource file" << std::endl; 73 std::cout << "-rct Name of the resource template file" << std::endl; 74 std::cout << "-rch Name of the resource file header" << std::endl; 75 std::cout << "-rcf Name of the resource file footer" << std::endl; 76 } 77 78 //########################################### 79 inline OUString OStringToOUString(const OString& str) 80 { return rtl::OStringToOUString(str, osl_getThreadTextEncoding()); } 81 82 //########################################### 83 inline OString OUStringToOString(const OUString& str) 84 { return rtl::OUStringToOString(str, osl_getThreadTextEncoding()); } 85 86 //########################################### 87 /** Get the directory where the module 88 is located as system directory, the 89 returned directory has a trailing '\' */ 90 OUString get_module_path() 91 { 92 OUString cwd_url; 93 OUString module_path; 94 if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData)) 95 osl::FileBase::getSystemPathFromFileURL(cwd_url, module_path); 96 97 return module_path; 98 } 99 100 //########################################### 101 /** Make the absolute directory of a base and 102 a relative directory, if the relative 103 directory is absolute the the relative 104 directory will be returned unchanged. 105 Base and relative directory should be 106 system paths the returned directory is 107 a system path too */ 108 OUString get_absolute_path( 109 const OUString& BaseDir, const OUString& RelDir) 110 { 111 OUString base_url; 112 OUString rel_url; 113 114 osl::FileBase::getFileURLFromSystemPath(BaseDir, base_url); 115 osl::FileBase::getFileURLFromSystemPath(RelDir, rel_url); 116 117 OUString abs_url; 118 osl::FileBase::getAbsoluteFileURL(base_url, rel_url, abs_url); 119 120 OUString abs_sys_path; 121 osl::FileBase::getSystemPathFromFileURL(abs_url, abs_sys_path); 122 123 return abs_sys_path; 124 } 125 126 //########################################### 127 OString get_absolute_file_path(const std::string& file_name) 128 { 129 OUString fp = get_absolute_path( 130 get_module_path(), OStringToOUString(file_name.c_str())); 131 return OUStringToOString(fp); 132 } 133 134 //########################################### 135 /** A helper class, enables stream exceptions 136 on construction, restors the old exception 137 state on destruction */ 138 class StreamExceptionsEnabler 139 { 140 public: 141 explicit StreamExceptionsEnabler( 142 std::ios& iostrm, 143 std::ios::iostate NewIos = std::ios::failbit | std::ios::badbit) : 144 m_IoStrm(iostrm), 145 m_OldIos(m_IoStrm.exceptions()) 146 { 147 m_IoStrm.exceptions(NewIos); 148 } 149 150 ~StreamExceptionsEnabler() 151 { 152 m_IoStrm.exceptions(m_OldIos); 153 } 154 private: 155 std::ios& m_IoStrm; 156 std::ios::iostate m_OldIos; 157 }; 158 159 typedef std::vector<std::string> string_container_t; 160 161 //########################################### 162 class iso_lang_identifier 163 { 164 public: 165 iso_lang_identifier() {}; 166 167 iso_lang_identifier(const OString& str) : 168 lang_(str) 169 { init(); } 170 171 iso_lang_identifier(const std::string& str) : 172 lang_(str.c_str()) 173 { init(); } 174 175 OString language() const 176 { return lang_; } 177 178 OString country() const 179 { return country_; } 180 181 OString make_OString() const 182 { return lang_ + "-" + country_; } 183 184 std::string make_std_string() const 185 { 186 OString tmp(lang_ + "-" + country_); 187 return tmp.getStr(); 188 } 189 190 private: 191 void init() 192 { 193 sal_Int32 idx = lang_.indexOf("-"); 194 195 if (idx > -1) 196 { 197 country_ = lang_.copy(idx + 1); 198 lang_ = lang_.copy(0, idx); 199 } 200 } 201 202 private: 203 OString lang_; 204 OString country_; 205 }; 206 207 //########################################### 208 /** Convert a OUString to the MS resource 209 file format string e.g. 210 OUString -> L"\x1A00\x2200\x3400" */ 211 std::string make_winrc_unicode_string(const OUString& str) 212 { 213 std::ostringstream oss; 214 oss << "L\""; 215 216 size_t length = str.getLength(); 217 const sal_Unicode* pchr = str.getStr(); 218 219 for (size_t i = 0; i < length; i++) 220 oss << "\\x" << std::hex << (int)*pchr++; 221 222 oss << "\""; 223 return oss.str(); 224 } 225 226 //########################################### 227 std::string make_winrc_unicode_string(const std::string& str) 228 { 229 return make_winrc_unicode_string( 230 OUString::createFromAscii(str.c_str())); 231 } 232 233 //################################################ 234 /** A replacement table contains pairs of 235 placeholders and the appropriate substitute */ 236 class Substitutor 237 { 238 private: 239 typedef std::map<std::string, std::string> replacement_table_t; 240 typedef std::map<std::string, replacement_table_t*> iso_lang_replacement_table_t; 241 242 public: 243 typedef iso_lang_replacement_table_t::iterator iterator; 244 typedef iso_lang_replacement_table_t::const_iterator const_iterator; 245 246 iterator begin() 247 { return iso_lang_replacement_table_.begin(); } 248 249 iterator end() 250 { return iso_lang_replacement_table_.end(); } 251 252 public: 253 254 Substitutor() {}; 255 256 ~Substitutor() 257 { 258 iso_lang_replacement_table_t::iterator iter_end = iso_lang_replacement_table_.end(); 259 iso_lang_replacement_table_t::iterator iter = iso_lang_replacement_table_.begin(); 260 261 for( /* no init */; iter != iter_end; ++iter) 262 delete iter->second; 263 264 iso_lang_replacement_table_.clear(); 265 } 266 267 void set_language(const iso_lang_identifier& iso_lang) 268 { 269 active_iso_lang_ = iso_lang; 270 } 271 272 // If Text is a placeholder substitute it with 273 //its substitute else leave it unchanged 274 void substitute(std::string& Text) 275 { 276 replacement_table_t* prt = get_replacement_table(active_iso_lang_.make_std_string()); 277 OSL_ASSERT(prt); 278 replacement_table_t::iterator iter = prt->find(Text); 279 if (iter != prt->end()) 280 Text = iter->second; 281 } 282 283 void add_substitution( 284 const std::string& Placeholder, const std::string& Substitute) 285 { 286 replacement_table_t* prt = get_replacement_table(active_iso_lang_.make_std_string()); 287 OSL_ASSERT(prt); 288 prt->insert(std::make_pair(Placeholder, Substitute)); 289 } 290 291 292 private: 293 // Return the replacement table for the iso lang id 294 // create a new one if not already present 295 replacement_table_t* get_replacement_table(const std::string& iso_lang) 296 { 297 iso_lang_replacement_table_t::iterator iter = 298 iso_lang_replacement_table_.find(iso_lang); 299 300 replacement_table_t* prt = NULL; 301 302 if (iso_lang_replacement_table_.end() == iter) 303 { 304 prt = new replacement_table_t(); 305 iso_lang_replacement_table_.insert(std::make_pair(iso_lang, prt)); 306 } 307 else 308 { 309 prt = iter->second; 310 } 311 return prt; 312 } 313 314 private: 315 iso_lang_replacement_table_t iso_lang_replacement_table_; 316 iso_lang_identifier active_iso_lang_; 317 }; 318 319 typedef std::map< unsigned short , std::string , std::less< unsigned short > > shortmap; 320 321 //########################################### 322 void add_group_entries( 323 Config& aConfig, 324 const ByteString& GroupName, 325 Substitutor& Substitutor) 326 { 327 OSL_ASSERT(aConfig.HasGroup(GroupName)); 328 329 aConfig.SetGroup(GroupName); 330 size_t key_count = aConfig.GetKeyCount(); 331 shortmap map; 332 333 for (size_t i = 0; i < key_count; i++) 334 { 335 ByteString iso_lang = aConfig.GetKeyName(sal::static_int_cast<USHORT>(i)); 336 ByteString key_value_utf8 = aConfig.ReadKey(sal::static_int_cast<USHORT>(i)); 337 iso_lang_identifier myiso_lang( iso_lang ); 338 LanguageType ltype = MsLangId::convertIsoNamesToLanguage(myiso_lang.language(), myiso_lang.country()); 339 if( ( ltype & 0x0200 ) == 0 && map[ ltype ].empty() ) 340 { 341 Substitutor.set_language(iso_lang_identifier(iso_lang)); 342 343 key_value_utf8.EraseLeadingAndTrailingChars('\"'); 344 345 OUString key_value_utf16 = 346 rtl::OStringToOUString(key_value_utf8, RTL_TEXTENCODING_UTF8); 347 348 Substitutor.add_substitution( 349 GroupName.GetBuffer(), make_winrc_unicode_string(key_value_utf16)); 350 map[ static_cast<unsigned short>(ltype) ] = std::string( iso_lang.GetBuffer() ); 351 } 352 else 353 { 354 if( !map[ ltype ].empty() ) 355 { 356 printf("ERROR: Duplicated ms id %d found for the languages %s and %s !!!! This does not work in microsoft resources\nPlease remove one!\n", ltype , map[ ltype ].c_str() , iso_lang.GetBuffer()); 357 exit( -1 ); 358 } 359 } 360 } 361 } 362 363 //########################################### 364 void read_ulf_file(const std::string& FileName, Substitutor& Substitutor) 365 { 366 // work-around for #i32420# 367 368 // as the Config class is currently not able to deal correctly with 369 // UTF8 files starting with a byte-order-mark we create a copy of the 370 // original file without the byte-order-mark 371 rtl::OUString tmpfile_url; 372 osl_createTempFile(NULL, NULL, &tmpfile_url.pData); 373 374 rtl::OUString tmpfile_sys; 375 osl::FileBase::getSystemPathFromFileURL(tmpfile_url, tmpfile_sys); 376 377 std::ifstream in(FileName.c_str()); 378 std::ofstream out(OUStringToOString(tmpfile_sys).getStr()); 379 380 try 381 { 382 StreamExceptionsEnabler sexc_out(out); 383 StreamExceptionsEnabler sexc_in(in); 384 385 //skip the byte-order-mark 0xEF 0xBB 0xBF, identifying UTF8 files 386 unsigned char BOM[3] = {0xEF, 0xBB, 0xBF}; 387 char buff[3]; 388 in.read(&buff[0], 3); 389 390 if (memcmp(buff, BOM, 3) != 0) 391 in.seekg(0); 392 393 std::string line; 394 while (std::getline(in, line)) 395 out << line << std::endl; 396 } 397 catch (const std::ios::failure&) 398 { 399 if (!in.eof()) 400 throw; 401 } 402 403 //Config config(OStringToOUString(FileName.c_str()).getStr()); 404 405 // end work-around for #i32420# 406 407 Config config(tmpfile_url.getStr()); 408 size_t grpcnt = config.GetGroupCount(); 409 for (size_t i = 0; i < grpcnt; i++) 410 add_group_entries(config, config.GetGroupName(sal::static_int_cast<USHORT>(i)), Substitutor); 411 } 412 413 //########################################### 414 void read_file( 415 const std::string& fname, 416 string_container_t& string_container) 417 { 418 std::ifstream file(fname.c_str()); 419 StreamExceptionsEnabler sexc(file); 420 421 try 422 { 423 std::string line; 424 while (std::getline(file, line)) 425 string_container.push_back(line); 426 } 427 catch(const std::ios::failure&) 428 { 429 if (!file.eof()) 430 throw; 431 } 432 } 433 434 //########################################### 435 /** A simple helper function that appens the 436 content of one file to another one */ 437 void concatenate_files(std::ostream& os, std::istream& is) 438 { 439 StreamExceptionsEnabler os_sexc(os); 440 StreamExceptionsEnabler is_sexc(is); 441 442 try 443 { 444 std::string line; 445 while (std::getline(is, line)) 446 os << line << std::endl; 447 } 448 catch(const std::ios::failure&) 449 { 450 if (!is.eof()) 451 throw; 452 } 453 } 454 455 //########################################### 456 bool is_placeholder(const std::string& str) 457 { 458 return ((str.length() > 1) && 459 ('%' == str[0]) && 460 ('%' == str[str.length() - 1])); 461 } 462 463 //########################################### 464 void start_language_section( 465 std::ostream_iterator<std::string>& ostream_iter, const iso_lang_identifier& iso_lang) 466 { 467 ostream_iter = std::string(); 468 469 std::string lang_section("LANGUAGE "); 470 471 LanguageType ltype = MsLangId::convertIsoNamesToLanguage(iso_lang.language(), iso_lang.country()); 472 473 char buff[10]; 474 int primLangID = PRIMARYLANGID(ltype); 475 int subLangID = SUBLANGID(ltype); 476 // Our resources are normaly not sub language dependant. 477 // Esp. for spanish we don't want to distinguish between trad. 478 // and internatinal sorting ( which leads to two different sub languages ) 479 // Setting the sub language to neutral allows us to use one 480 // stringlist for all spanish variants ( see #123126# ) 481 if ( ( primLangID == LANG_SPANISH ) && 482 ( subLangID == SUBLANG_SPANISH ) ) 483 subLangID = SUBLANG_NEUTRAL; 484 485 _itoa(primLangID, buff, 16); 486 lang_section += std::string("0x") + std::string(buff); 487 488 lang_section += std::string(" , "); 489 490 _itoa(subLangID, buff, 16); 491 492 lang_section += std::string("0x") + std::string(buff); 493 ostream_iter = lang_section; 494 } 495 496 //########################################### 497 /** Iterate all languages in the substitutor, 498 replace the all placeholder and append the 499 result to the output file */ 500 void inflate_rc_template_to_file( 501 std::ostream& os, const string_container_t& rctmpl, Substitutor& substitutor) 502 { 503 StreamExceptionsEnabler sexc(os); 504 505 Substitutor::const_iterator iter = substitutor.begin(); 506 Substitutor::const_iterator iter_end = substitutor.end(); 507 508 std::ostream_iterator<std::string> oi(os, "\n"); 509 510 for ( /**/ ;iter != iter_end; ++iter) 511 { 512 substitutor.set_language(iso_lang_identifier(iter->first)); 513 514 string_container_t::const_iterator rct_iter = rctmpl.begin(); 515 string_container_t::const_iterator rct_iter_end = rctmpl.end(); 516 517 if (!rctmpl.empty()) 518 start_language_section(oi, iter->first); 519 520 for ( /**/ ;rct_iter != rct_iter_end; ++rct_iter) 521 { 522 std::istringstream iss(*rct_iter); 523 std::string line; 524 525 while (iss) 526 { 527 std::string token; 528 iss >> token; 529 substitutor.substitute(token); 530 531 // #110274# HACK for partially merged 532 // *.lng files where some strings have 533 // a particular language that others 534 // don't have in order to keep the 535 // build 536 if (is_placeholder(token)) 537 token = make_winrc_unicode_string(token); 538 539 line += token; 540 line += " "; 541 } 542 oi = line; 543 } 544 } 545 } 546 547 } // namespace /* private */ 548 549 //#################################################### 550 /* MAIN 551 The file names provided via command line should be 552 absolute or relative to the directory of this module. 553 554 Algo: 555 1. read the ulf file and initialize the substitutor 556 2. read the resource template file 557 3. create the output file and append the header 558 4. inflate the resource template to the output file 559 for every language using the substitutor 560 5. append the footer 561 */ 562 #define MAKE_ABSOLUTE(s) (get_absolute_file_path((s)).getStr()) 563 #define ULF_FILE(c) MAKE_ABSOLUTE((c).get_arg("-ulf")) 564 #define RC_TEMPLATE(c) MAKE_ABSOLUTE((c).get_arg("-rct")) 565 #define RC_FILE(c) MAKE_ABSOLUTE((c).get_arg("-rc")) 566 #define RC_HEADER(c) MAKE_ABSOLUTE((c).get_arg("-rch")) 567 #define RC_FOOTER(c) MAKE_ABSOLUTE((c).get_arg("-rcf")) 568 569 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) 570 { 571 try 572 { 573 CommandLine cmdline(argc, argv); 574 575 Substitutor substitutor; 576 read_ulf_file(ULF_FILE(cmdline), substitutor); 577 578 string_container_t rc_tmpl; 579 read_file(RC_TEMPLATE(cmdline), rc_tmpl); 580 581 std::ofstream rc_file(RC_FILE(cmdline)); 582 std::ifstream in_header(RC_HEADER(cmdline)); 583 concatenate_files(rc_file, in_header); 584 585 inflate_rc_template_to_file(rc_file, rc_tmpl, substitutor); 586 587 std::ifstream in_footer(RC_FOOTER(cmdline)); 588 concatenate_files(rc_file, in_footer); 589 } 590 catch(const std::ios::failure& ex) 591 { 592 std::cout << ex.what() << std::endl; 593 } 594 catch(std::exception& ex) 595 { 596 std::cout << ex.what() << std::endl; 597 ShowUsage(); 598 } 599 catch(...) 600 { 601 std::cout << "Unexpected error..." << std::endl; 602 } 603 return 0; 604 } 605 606