1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
25 typedef unsigned short WORD
;
26 #define PRIMARYLANGID(lgid) (static_cast<WORD>(lgid) & 0x3ff)
27 #define SUBLANGID(lgid) (static_cast<WORD>(lgid) >> 10)
28 #define LANG_SPANISH 0x0a
29 #define SUBLANG_NEUTRAL 0x00
30 #define SUBLANG_SPANISH 0x01
33 #include "cmdline.hxx"
35 #include <comphelper/string.hxx>
36 #include <osl/thread.h>
37 #include <osl/process.h>
38 #include <osl/file.hxx>
41 #include <tools/config.hxx>
42 #include <i18nlangtag/languagetag.hxx>
49 #include <string_view>
55 namespace /* private */
61 std::cout
<< "Usage: -ulf ulf_file -rc rc_output_file -rct rc_template_file -rch rch_file -rcf rcf_file" << std::endl
;
62 std::cout
<< "-ulf Name of the ulf file" << std::endl
;
63 std::cout
<< "-rc Name of the resulting resource file" << std::endl
;
64 std::cout
<< "-rct Name of the resource template file" << std::endl
;
65 std::cout
<< "-rch Name of the resource file header" << std::endl
;
66 std::cout
<< "-rcf Name of the resource file footer" << std::endl
;
69 OUString
OStringToOUString(std::string_view str
)
70 { return rtl::OStringToOUString(str
, osl_getThreadTextEncoding()); }
72 OString
OUStringToOString(std::u16string_view str
)
73 { return rtl::OUStringToOString(str
, osl_getThreadTextEncoding()); }
75 /** Get the directory where the module
76 is located as system directory, the
77 returned directory has a trailing '\' */
78 OUString
get_module_path()
82 if (osl_Process_E_None
== osl_getProcessWorkingDir(&cwd_url
.pData
))
83 osl::FileBase::getSystemPathFromFileURL(cwd_url
, module_path
);
88 /** Make the absolute directory of a base and
89 a relative directory, if the relative
90 directory is absolute the relative
91 directory will be returned unchanged.
92 Base and relative directory should be
93 system paths the returned directory is
95 OUString
get_absolute_path(
96 const OUString
& BaseDir
, const OUString
& RelDir
)
101 osl::FileBase::getFileURLFromSystemPath(BaseDir
, base_url
);
102 osl::FileBase::getFileURLFromSystemPath(RelDir
, rel_url
);
105 (void)osl::FileBase::getAbsoluteFileURL(base_url
, rel_url
, abs_url
);
107 OUString abs_sys_path
;
108 osl::FileBase::getSystemPathFromFileURL(abs_url
, abs_sys_path
);
113 std::string
make_absolute(const std::string
& file_name
)
115 OUString fp
= get_absolute_path(
116 get_module_path(), OStringToOUString(file_name
.c_str()));
117 return std::string(OUStringToOString(fp
));
120 /** A helper class, enables stream exceptions
121 on construction, restores the old exception
122 state on destruction */
123 class StreamExceptionsEnabler
126 explicit StreamExceptionsEnabler(
129 m_OldIos(m_IoStrm
.exceptions())
131 m_IoStrm
.exceptions(std::ios::failbit
| std::ios::badbit
);
134 ~StreamExceptionsEnabler()
136 m_IoStrm
.exceptions(m_OldIos
);
140 std::ios::iostate m_OldIos
;
143 class iso_lang_identifier
146 iso_lang_identifier() {};
148 explicit iso_lang_identifier(const OString
& str
) :
152 explicit iso_lang_identifier(const std::string
& str
) :
156 OUString
make_OUString() const
157 { return OStringToOUString( maBcp47
, RTL_TEXTENCODING_ASCII_US
); }
159 std::string
make_std_string() const
160 { return maBcp47
.getStr(); }
166 /** Convert an OUString to the MS resource
167 file format string e.g.
168 OUString -> L"\x1A00\x2200\x3400" */
169 std::string
make_winrc_unicode_string(const OUString
& str
)
171 std::ostringstream oss
;
174 size_t length
= str
.getLength();
175 const sal_Unicode
* pchr
= str
.getStr();
177 for (size_t i
= 0; i
< length
; i
++)
178 oss
<< "\\x" << std::hex
<< static_cast<int>(*pchr
++);
184 std::string
make_winrc_unicode_string(const std::string
& str
)
186 return make_winrc_unicode_string(
187 OUString::createFromAscii(str
.c_str()));
190 /** A replacement table contains pairs of
191 placeholders and the appropriate substitute */
195 typedef std::map
<std::string
, std::string
> replacement_table_t
;
196 typedef std::map
<std::string
, replacement_table_t
> iso_lang_replacement_table_t
;
199 typedef iso_lang_replacement_table_t::iterator iterator
;
200 typedef iso_lang_replacement_table_t::const_iterator const_iterator
;
203 { return iso_lang_replacement_table_
.begin(); }
206 { return iso_lang_replacement_table_
.end(); }
212 void set_language(const iso_lang_identifier
& iso_lang
)
214 active_iso_lang_
= iso_lang
;
217 // If Text is a placeholder substitute it with
218 //its substitute else leave it unchanged
219 void substitute(std::string
& Text
)
221 replacement_table_t
& prt
= get_replacement_table(active_iso_lang_
.make_std_string());
222 replacement_table_t::iterator iter
= prt
.find(Text
);
223 if (iter
!= prt
.end())
227 void add_substitution(
228 const std::string
& Placeholder
, const std::string
& Substitute
)
230 replacement_table_t
& prt
= get_replacement_table(active_iso_lang_
.make_std_string());
231 prt
.insert(std::make_pair(Placeholder
, Substitute
));
236 // Return the replacement table for the iso lang id
237 // create a new one if not already present
238 replacement_table_t
& get_replacement_table(const std::string
& iso_lang
)
240 return iso_lang_replacement_table_
[iso_lang
];
244 iso_lang_replacement_table_t iso_lang_replacement_table_
;
245 iso_lang_identifier active_iso_lang_
;
248 void add_group_entries(
250 const OString
& GroupName
,
251 Substitutor
& Substitutor
)
253 OSL_ASSERT(aConfig
.HasGroup(GroupName
));
255 aConfig
.SetGroup(GroupName
);
256 size_t key_count
= aConfig
.GetKeyCount();
257 std::map
< LanguageType
, std::string
> map
;
259 for (size_t i
= 0; i
< key_count
; i
++)
261 OString iso_lang
= aConfig
.GetKeyName(sal::static_int_cast
<sal_uInt16
>(i
));
262 OString key_value_utf8
= aConfig
.ReadKey(sal::static_int_cast
<sal_uInt16
>(i
));
263 iso_lang_identifier
myiso_lang( iso_lang
);
264 LanguageType ltype
= LanguageTag( myiso_lang
.make_OUString()).makeFallback().getLanguageType();
265 if( ( static_cast<sal_uInt16
>(ltype
) & 0x0200 ) == 0 && map
[ ltype
].empty() )
267 Substitutor
.set_language(iso_lang_identifier(iso_lang
));
269 key_value_utf8
= comphelper::string::strip(key_value_utf8
, '\"');
271 OUString key_value_utf16
=
272 OStringToOUString(key_value_utf8
, RTL_TEXTENCODING_UTF8
);
274 Substitutor
.add_substitution(
275 GroupName
.getStr(), make_winrc_unicode_string(key_value_utf16
));
276 map
[ ltype
] = std::string( iso_lang
.getStr() );
280 if( !map
[ ltype
].empty() )
282 printf("ERROR: Duplicated ms id %d found for the languages %s and %s !!!! This does not work in microsoft resources\nPlease remove one!\n", static_cast<sal_uInt16
>(ltype
) , map
[ ltype
].c_str() , iso_lang
.getStr());
289 void read_ulf_file(const std::string
& FileName
, Substitutor
& Substitutor
)
291 // work-around for #i32420#
293 // as the Config class is currently not able to deal correctly with
294 // UTF8 files starting with a byte-order-mark we create a copy of the
295 // original file without the byte-order-mark
296 OUString tmpfile_url
;
297 osl_createTempFile(nullptr, nullptr, &tmpfile_url
.pData
);
299 OUString tmpfile_sys
;
300 osl::FileBase::getSystemPathFromFileURL(tmpfile_url
, tmpfile_sys
);
302 std::ifstream
in(FileName
.c_str());
303 std::ofstream
out(OUStringToOString(tmpfile_sys
).getStr());
307 StreamExceptionsEnabler
sexc_out(out
);
308 StreamExceptionsEnabler
sexc_in(in
);
310 //skip the byte-order-mark 0xEF 0xBB 0xBF, identifying UTF8 files
311 unsigned char const BOM
[3] = {0xEF, 0xBB, 0xBF};
313 in
.read(&buff
[0], 3);
315 if (memcmp(buff
, BOM
, 3) != 0)
319 while (std::getline(in
, line
))
320 out
<< line
<< std::endl
;
322 catch (const std::ios::failure
&)
329 // end work-around for #i32420#
331 Config
config(tmpfile_url
);
332 size_t grpcnt
= config
.GetGroupCount();
333 for (size_t i
= 0; i
< grpcnt
; i
++)
334 add_group_entries(config
, config
.GetGroupName(sal::static_int_cast
<sal_uInt16
>(i
)), Substitutor
);
338 const std::string
& fname
,
339 std::vector
<std::string
>& string_container
)
341 std::ifstream
file(fname
.c_str());
342 StreamExceptionsEnabler
sexc(file
);
347 while (std::getline(file
, line
))
348 string_container
.push_back(line
);
350 catch(const std::ios::failure
&)
357 /** A simple helper function that happens the
358 content of one file to another one */
359 void concatenate_files(std::ostream
& os
, std::istream
& is
)
361 StreamExceptionsEnabler
os_sexc(os
);
362 StreamExceptionsEnabler
is_sexc(is
);
367 while (std::getline(is
, line
))
368 os
<< line
<< std::endl
;
370 catch(const std::ios::failure
&)
377 bool is_placeholder(const std::string
& str
)
379 return ((str
.length() > 1) &&
381 ('%' == str
[str
.length() - 1]));
384 void start_language_section(
385 std::ostream_iterator
<std::string
>& ostream_iter
, const iso_lang_identifier
& iso_lang
)
387 ostream_iter
= std::string();
389 std::string
lang_section("LANGUAGE ");
391 LanguageType ltype
= LanguageTag( iso_lang
.make_OUString()).makeFallback().getLanguageType();
394 int primLangID
= PRIMARYLANGID(ltype
);
395 int subLangID
= SUBLANGID(ltype
);
396 // Our resources are normally not sub language dependent.
397 // Esp. for spanish we don't want to distinguish between trad.
398 // and international sorting (which leads to two different sub languages)
399 // Setting the sub language to neutral allows us to use one
400 // stringlist for all spanish variants
401 if ( ( primLangID
== LANG_SPANISH
) &&
402 ( subLangID
== SUBLANG_SPANISH
) )
403 subLangID
= SUBLANG_NEUTRAL
;
406 _itoa(primLangID
, buff
, 16);
408 sprintf(buff
, "%x", primLangID
);
410 lang_section
+= std::string("0x") + std::string(buff
);
412 lang_section
+= std::string(" , ");
415 _itoa(subLangID
, buff
, 16);
417 sprintf(buff
, "%x", subLangID
);
419 lang_section
+= std::string("0x") + std::string(buff
);
420 ostream_iter
= lang_section
;
423 /** Iterate all languages in the substitutor,
424 replace the all placeholder and append the
425 result to the output file */
426 void inflate_rc_template_to_file(
427 std::ostream
& os
, const std::vector
<std::string
>& rctmpl
, Substitutor
& substitutor
)
429 StreamExceptionsEnabler
sexc(os
);
431 Substitutor::const_iterator iter
= substitutor
.begin();
432 Substitutor::const_iterator iter_end
= substitutor
.end();
434 std::ostream_iterator
<std::string
> oi(os
, "\n");
436 for ( /**/ ;iter
!= iter_end
; ++iter
)
438 substitutor
.set_language(iso_lang_identifier(iter
->first
));
441 start_language_section(oi
, iso_lang_identifier(iter
->first
));
443 for ( auto& rct
: rctmpl
)
445 std::istringstream
iss(rct
);
452 substitutor
.substitute(token
);
454 // HACK for partially merged
455 // *.lng files where some strings have
456 // a particular language that others
457 // don't have in order to keep the
459 // coverity[tainted_data] - trusted data source
460 if (is_placeholder(token
))
461 token
= make_winrc_unicode_string(token
);
471 } // namespace /* private */
474 The file names provided via command line should be
475 absolute or relative to the directory of this module.
478 1. read the ulf file and initialize the substitutor
479 2. read the resource template file
480 3. create the output file and append the header
481 4. inflate the resource template to the output file
482 for every language using the substitutor
486 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc
, argv
)
490 CommandLine
cmdline(argc
, argv
);
492 Substitutor substitutor
;
493 read_ulf_file(make_absolute(cmdline
.get_arg("-ulf")), substitutor
);
495 std::vector
<std::string
> rc_tmpl
;
496 read_file(make_absolute(cmdline
.get_arg("-rct")), rc_tmpl
);
498 std::ofstream
rc_file(make_absolute(cmdline
.get_arg("-rc")));
499 std::ifstream
in_header(make_absolute(cmdline
.get_arg("-rch")));
500 concatenate_files(rc_file
, in_header
);
502 inflate_rc_template_to_file(rc_file
, rc_tmpl
, substitutor
);
504 std::ifstream
in_footer(make_absolute(cmdline
.get_arg("-rcf")));
505 concatenate_files(rc_file
, in_footer
);
507 catch(const std::ios::failure
& ex
)
509 std::cout
<< ex
.what() << std::endl
;
512 catch(const std::exception
& ex
)
514 std::cout
<< ex
.what() << std::endl
;
520 std::cout
<< "Unexpected error..." << std::endl
;
526 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */