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