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