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