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 //###########################################
ShowUsage()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 //###########################################
OStringToOUString(const OString & str)75 inline OUString OStringToOUString(const OString& str)
76 { return rtl::OStringToOUString(str, osl_getThreadTextEncoding()); }
77
78 //###########################################
OUStringToOString(const OUString & str)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 '\' */
get_module_path()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 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 */
get_absolute_path(const OUString & BaseDir,const OUString & RelDir)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 //###########################################
get_absolute_file_path(const std::string & file_name)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:
StreamExceptionsEnabler(std::ios & iostrm,std::ios::iostate NewIos=std::ios::failbit|std::ios::badbit)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
~StreamExceptionsEnabler()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:
iso_lang_identifier()161 iso_lang_identifier() {};
162
iso_lang_identifier(const OString & str)163 iso_lang_identifier(const OString& str) :
164 lang_(str)
165 { init(); }
166
iso_lang_identifier(const std::string & str)167 iso_lang_identifier(const std::string& str) :
168 lang_(str.c_str())
169 { init(); }
170
language() const171 OString language() const
172 { return lang_; }
173
country() const174 OString country() const
175 { return country_; }
176
make_OString() const177 OString make_OString() const
178 { return lang_ + "-" + country_; }
179
make_std_string() const180 std::string make_std_string() const
181 {
182 OString tmp(lang_ + "-" + country_);
183 return tmp.getStr();
184 }
185
186 private:
init()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" */
make_winrc_unicode_string(const OUString & str)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 //###########################################
make_winrc_unicode_string(const std::string & str)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
begin()242 iterator begin()
243 { return iso_lang_replacement_table_.begin(); }
244
end()245 iterator end()
246 { return iso_lang_replacement_table_.end(); }
247
248 public:
249
Substitutor()250 Substitutor() {};
251
~Substitutor()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
set_language(const iso_lang_identifier & iso_lang)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
substitute(std::string & Text)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
add_substitution(const std::string & Placeholder,const std::string & Substitute)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
get_replacement_table(const std::string & iso_lang)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 //###########################################
add_group_entries(Config & aConfig,const ByteString & GroupName,Substitutor & Substitutor)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 //###########################################
read_ulf_file(const std::string & FileName,Substitutor & Substitutor)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 //###########################################
read_file(const std::string & fname,string_container_t & string_container)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 */
concatenate_files(std::ostream & os,std::istream & is)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 //###########################################
is_placeholder(const std::string & str)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 //###########################################
start_language_section(std::ostream_iterator<std::string> & ostream_iter,const iso_lang_identifier & iso_lang)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 normally not sub language dependent.
473 // Esp. for spanish we don't want to distinguish between trad.
474 // and international 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 */
inflate_rc_template_to_file(std::ostream & os,const string_container_t & rctmpl,Substitutor & substitutor)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
SAL_IMPLEMENT_MAIN_WITH_ARGS(argc,argv)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