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 : maBcp47(str)
166 0 : { }
167 :
168 0 : iso_lang_identifier(const std::string& str) :
169 0 : maBcp47(str.c_str())
170 0 : { }
171 :
172 0 : OUString make_OUString() const
173 0 : { return OStringToOUString( maBcp47, RTL_TEXTENCODING_ASCII_US); }
174 :
175 0 : std::string make_std_string() const
176 0 : { return maBcp47.getStr(); }
177 :
178 : private:
179 : OString maBcp47;
180 : };
181 :
182 : /** Convert a OUString to the MS resource
183 : file format string e.g.
184 : OUString -> L"\x1A00\x2200\x3400" */
185 0 : std::string make_winrc_unicode_string(const OUString& str)
186 : {
187 0 : std::ostringstream oss;
188 0 : oss << "L\"";
189 :
190 0 : size_t length = str.getLength();
191 0 : const sal_Unicode* pchr = str.getStr();
192 :
193 0 : for (size_t i = 0; i < length; i++)
194 0 : oss << "\\x" << std::hex << (int)*pchr++;
195 :
196 0 : oss << "\"";
197 0 : return oss.str();
198 : }
199 :
200 0 : std::string make_winrc_unicode_string(const std::string& str)
201 : {
202 : return make_winrc_unicode_string(
203 0 : OUString::createFromAscii(str.c_str()));
204 : }
205 :
206 : /** A replacement table contains pairs of
207 : placeholders and the appropriate substitute */
208 : class Substitutor
209 : {
210 : private:
211 : typedef std::map<std::string, std::string> replacement_table_t;
212 : typedef std::map<std::string, replacement_table_t*> iso_lang_replacement_table_t;
213 :
214 : public:
215 : typedef iso_lang_replacement_table_t::iterator iterator;
216 : typedef iso_lang_replacement_table_t::const_iterator const_iterator;
217 :
218 0 : iterator begin()
219 0 : { return iso_lang_replacement_table_.begin(); }
220 :
221 0 : iterator end()
222 0 : { return iso_lang_replacement_table_.end(); }
223 :
224 : public:
225 :
226 0 : Substitutor() {};
227 :
228 0 : ~Substitutor()
229 0 : {
230 0 : iso_lang_replacement_table_t::iterator iter_end = iso_lang_replacement_table_.end();
231 0 : iso_lang_replacement_table_t::iterator iter = iso_lang_replacement_table_.begin();
232 :
233 0 : for( /* no init */; iter != iter_end; ++iter)
234 0 : delete iter->second;
235 :
236 0 : iso_lang_replacement_table_.clear();
237 0 : }
238 :
239 0 : void set_language(const iso_lang_identifier& iso_lang)
240 : {
241 0 : active_iso_lang_ = iso_lang;
242 0 : }
243 :
244 : // If Text is a placeholder substitute it with
245 : //its substitute else leave it unchanged
246 0 : void substitute(std::string& Text)
247 : {
248 0 : replacement_table_t* prt = get_replacement_table(active_iso_lang_.make_std_string());
249 : OSL_ASSERT(prt);
250 0 : replacement_table_t::iterator iter = prt->find(Text);
251 0 : if (iter != prt->end())
252 0 : Text = iter->second;
253 0 : }
254 :
255 0 : void add_substitution(
256 : const std::string& Placeholder, const std::string& Substitute)
257 : {
258 0 : replacement_table_t* prt = get_replacement_table(active_iso_lang_.make_std_string());
259 : OSL_ASSERT(prt);
260 0 : prt->insert(std::make_pair(Placeholder, Substitute));
261 0 : }
262 :
263 :
264 : private:
265 : // Return the replacement table for the iso lang id
266 : // create a new one if not already present
267 0 : replacement_table_t* get_replacement_table(const std::string& iso_lang)
268 : {
269 : iso_lang_replacement_table_t::iterator iter =
270 0 : iso_lang_replacement_table_.find(iso_lang);
271 :
272 0 : replacement_table_t* prt = NULL;
273 :
274 0 : if (iso_lang_replacement_table_.end() == iter)
275 : {
276 0 : prt = new replacement_table_t();
277 0 : iso_lang_replacement_table_.insert(std::make_pair(iso_lang, prt));
278 : }
279 : else
280 : {
281 0 : prt = iter->second;
282 : }
283 0 : return prt;
284 : }
285 :
286 : private:
287 : iso_lang_replacement_table_t iso_lang_replacement_table_;
288 : iso_lang_identifier active_iso_lang_;
289 : };
290 :
291 : typedef std::map< unsigned short , std::string , std::less< unsigned short > > shortmap;
292 :
293 0 : void add_group_entries(
294 : Config& aConfig,
295 : const OString& GroupName,
296 : Substitutor& Substitutor)
297 : {
298 : OSL_ASSERT(aConfig.HasGroup(GroupName));
299 :
300 0 : aConfig.SetGroup(GroupName);
301 0 : size_t key_count = aConfig.GetKeyCount();
302 0 : shortmap map;
303 :
304 0 : for (size_t i = 0; i < key_count; i++)
305 : {
306 0 : OString iso_lang = aConfig.GetKeyName(sal::static_int_cast<sal_uInt16>(i));
307 0 : OString key_value_utf8 = aConfig.ReadKey(sal::static_int_cast<sal_uInt16>(i));
308 0 : iso_lang_identifier myiso_lang( iso_lang );
309 0 : LanguageType ltype = LanguageTag( myiso_lang.make_OUString()).makeFallback().getLanguageType();
310 0 : if( ( ltype & 0x0200 ) == 0 && map[ ltype ].empty() )
311 : {
312 0 : Substitutor.set_language(iso_lang_identifier(iso_lang));
313 :
314 0 : key_value_utf8 = comphelper::string::strip(key_value_utf8, '\"');
315 :
316 : OUString key_value_utf16 =
317 0 : OStringToOUString(key_value_utf8, RTL_TEXTENCODING_UTF8);
318 :
319 : Substitutor.add_substitution(
320 0 : GroupName.getStr(), make_winrc_unicode_string(key_value_utf16));
321 0 : map[ static_cast<unsigned short>(ltype) ] = std::string( iso_lang.getStr() );
322 : }
323 : else
324 : {
325 0 : if( !map[ ltype ].empty() )
326 : {
327 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());
328 0 : exit( -1 );
329 : }
330 : }
331 0 : }
332 0 : }
333 :
334 0 : void read_ulf_file(const std::string& FileName, Substitutor& Substitutor)
335 : {
336 : // work-around for #i32420#
337 :
338 : // as the Config class is currently not able to deal correctly with
339 : // UTF8 files starting with a byte-order-mark we create a copy of the
340 : // original file without the byte-order-mark
341 0 : OUString tmpfile_url;
342 0 : osl_createTempFile(NULL, NULL, &tmpfile_url.pData);
343 :
344 0 : OUString tmpfile_sys;
345 0 : osl::FileBase::getSystemPathFromFileURL(tmpfile_url, tmpfile_sys);
346 :
347 0 : std::ifstream in(FileName.c_str());
348 0 : std::ofstream out(OUStringToOString(tmpfile_sys).getStr());
349 :
350 : try
351 : {
352 0 : StreamExceptionsEnabler sexc_out(out);
353 0 : StreamExceptionsEnabler sexc_in(in);
354 :
355 : //skip the byte-order-mark 0xEF 0xBB 0xBF, identifying UTF8 files
356 0 : unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
357 : char buff[3];
358 0 : in.read(&buff[0], 3);
359 :
360 0 : if (memcmp(buff, BOM, 3) != 0)
361 0 : in.seekg(0);
362 :
363 0 : std::string line;
364 0 : while (std::getline(in, line))
365 0 : out << line << std::endl;
366 : }
367 0 : catch (const std::ios::failure&)
368 : {
369 0 : if (!in.eof())
370 0 : throw;
371 : }
372 :
373 :
374 : // end work-around for #i32420#
375 :
376 0 : Config config(tmpfile_url.getStr());
377 0 : size_t grpcnt = config.GetGroupCount();
378 0 : for (size_t i = 0; i < grpcnt; i++)
379 0 : add_group_entries(config, config.GetGroupName(sal::static_int_cast<sal_uInt16>(i)), Substitutor);
380 0 : }
381 :
382 0 : void read_file(
383 : const std::string& fname,
384 : string_container_t& string_container)
385 : {
386 0 : std::ifstream file(fname.c_str());
387 0 : StreamExceptionsEnabler sexc(file);
388 :
389 : try
390 : {
391 0 : std::string line;
392 0 : while (std::getline(file, line))
393 0 : string_container.push_back(line);
394 : }
395 0 : catch(const std::ios::failure&)
396 : {
397 0 : if (!file.eof())
398 0 : throw;
399 0 : }
400 0 : }
401 :
402 : /** A simple helper function that appens the
403 : content of one file to another one */
404 0 : void concatenate_files(std::ostream& os, std::istream& is)
405 : {
406 0 : StreamExceptionsEnabler os_sexc(os);
407 0 : StreamExceptionsEnabler is_sexc(is);
408 :
409 : try
410 : {
411 0 : std::string line;
412 0 : while (std::getline(is, line))
413 0 : os << line << std::endl;
414 : }
415 0 : catch(const std::ios::failure&)
416 : {
417 0 : if (!is.eof())
418 0 : throw;
419 0 : }
420 0 : }
421 :
422 0 : bool is_placeholder(const std::string& str)
423 : {
424 0 : return ((str.length() > 1) &&
425 0 : ('%' == str[0]) &&
426 0 : ('%' == str[str.length() - 1]));
427 : }
428 :
429 0 : void start_language_section(
430 : std::ostream_iterator<std::string>& ostream_iter, const iso_lang_identifier& iso_lang)
431 : {
432 0 : ostream_iter = std::string();
433 :
434 0 : std::string lang_section("LANGUAGE ");
435 :
436 0 : LanguageType ltype = LanguageTag( iso_lang.make_OUString()).makeFallback().getLanguageType();
437 :
438 : char buff[10];
439 0 : int primLangID = PRIMARYLANGID(ltype);
440 0 : int subLangID = SUBLANGID(ltype);
441 : // Our resources are normaly not sub language dependent.
442 : // Esp. for spanish we don't want to distinguish between trad.
443 : // and internatinal sorting ( which leads to two different sub languages )
444 : // Setting the sub language to neutral allows us to use one
445 : // stringlist for all spanish variants
446 0 : if ( ( primLangID == LANG_SPANISH ) &&
447 : ( subLangID == SUBLANG_SPANISH ) )
448 0 : subLangID = SUBLANG_NEUTRAL;
449 :
450 : #ifdef WNT
451 : _itoa(primLangID, buff, 16);
452 : #else
453 0 : sprintf(buff, "%x", primLangID);
454 : #endif
455 0 : lang_section += std::string("0x") + std::string(buff);
456 :
457 0 : lang_section += std::string(" , ");
458 :
459 : #ifdef WNT
460 : _itoa(subLangID, buff, 16);
461 : #else
462 0 : sprintf(buff, "%x", subLangID);
463 : #endif
464 0 : lang_section += std::string("0x") + std::string(buff);
465 0 : ostream_iter = lang_section;
466 0 : }
467 :
468 : /** Iterate all languages in the substitutor,
469 : replace the all placeholder and append the
470 : result to the output file */
471 0 : void inflate_rc_template_to_file(
472 : std::ostream& os, const string_container_t& rctmpl, Substitutor& substitutor)
473 : {
474 0 : StreamExceptionsEnabler sexc(os);
475 :
476 0 : Substitutor::const_iterator iter = substitutor.begin();
477 0 : Substitutor::const_iterator iter_end = substitutor.end();
478 :
479 0 : std::ostream_iterator<std::string> oi(os, "\n");
480 :
481 0 : for ( /**/ ;iter != iter_end; ++iter)
482 : {
483 0 : substitutor.set_language(iso_lang_identifier(iter->first));
484 :
485 0 : string_container_t::const_iterator rct_iter = rctmpl.begin();
486 0 : string_container_t::const_iterator rct_iter_end = rctmpl.end();
487 :
488 0 : if (!rctmpl.empty())
489 0 : start_language_section(oi, iter->first);
490 :
491 0 : for ( /**/ ;rct_iter != rct_iter_end; ++rct_iter)
492 : {
493 0 : std::istringstream iss(*rct_iter);
494 0 : std::string line;
495 :
496 0 : while (iss)
497 : {
498 0 : std::string token;
499 0 : iss >> token;
500 0 : substitutor.substitute(token);
501 :
502 : // HACK for partially merged
503 : // *.lng files where some strings have
504 : // a particular language that others
505 : // don't have in order to keep the
506 : // build
507 0 : if (is_placeholder(token))
508 0 : token = make_winrc_unicode_string(token);
509 :
510 0 : line += token;
511 0 : line += " ";
512 0 : }
513 0 : oi = line;
514 0 : }
515 0 : }
516 0 : }
517 :
518 : } // namespace /* private */
519 :
520 : /* MAIN
521 : The file names provided via command line should be
522 : absolute or relative to the directory of this module.
523 :
524 : Algo:
525 : 1. read the ulf file and initialize the substitutor
526 : 2. read the resource template file
527 : 3. create the output file and append the header
528 : 4. inflate the resource template to the output file
529 : for every language using the substitutor
530 : 5. append the footer
531 : */
532 : #define MAKE_ABSOLUTE(s) (get_absolute_file_path((s)).getStr())
533 : #define ULF_FILE(c) MAKE_ABSOLUTE((c).get_arg("-ulf"))
534 : #define RC_TEMPLATE(c) MAKE_ABSOLUTE((c).get_arg("-rct"))
535 : #define RC_FILE(c) MAKE_ABSOLUTE((c).get_arg("-rc"))
536 : #define RC_HEADER(c) MAKE_ABSOLUTE((c).get_arg("-rch"))
537 : #define RC_FOOTER(c) MAKE_ABSOLUTE((c).get_arg("-rcf"))
538 :
539 0 : SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
540 : {
541 : try
542 : {
543 0 : CommandLine cmdline(argc, argv);
544 :
545 0 : Substitutor substitutor;
546 0 : read_ulf_file(ULF_FILE(cmdline), substitutor);
547 :
548 0 : string_container_t rc_tmpl;
549 0 : read_file(RC_TEMPLATE(cmdline), rc_tmpl);
550 :
551 0 : std::ofstream rc_file(RC_FILE(cmdline));
552 0 : std::ifstream in_header(RC_HEADER(cmdline));
553 0 : concatenate_files(rc_file, in_header);
554 :
555 0 : inflate_rc_template_to_file(rc_file, rc_tmpl, substitutor);
556 :
557 0 : std::ifstream in_footer(RC_FOOTER(cmdline));
558 0 : concatenate_files(rc_file, in_footer);
559 : }
560 0 : catch(const std::ios::failure& ex)
561 : {
562 0 : std::cout << ex.what() << std::endl;
563 0 : return 1;
564 : }
565 0 : catch(const std::exception& ex)
566 : {
567 0 : std::cout << ex.what() << std::endl;
568 0 : ShowUsage();
569 0 : return 1;
570 : }
571 0 : catch(...)
572 : {
573 0 : std::cout << "Unexpected error..." << std::endl;
574 0 : return 1;
575 : }
576 0 : return 0;
577 0 : }
578 :
579 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|