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