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/diagnose.h>
39 #include <osl/file.hxx>
42 #include <tools/config.hxx>
43 #include <i18nlangtag/languagetag.hxx>
50 #include <string_view>
56 namespace /* private */
62 std::cout
<< "Usage: -ulf ulf_file -rc rc_output_file -rct rc_template_file -rch rch_file -rcf rcf_file" << std::endl
;
63 std::cout
<< "-ulf Name of the ulf file" << std::endl
;
64 std::cout
<< "-rc Name of the resulting resource file" << std::endl
;
65 std::cout
<< "-rct Name of the resource template file" << std::endl
;
66 std::cout
<< "-rch Name of the resource file header" << std::endl
;
67 std::cout
<< "-rcf Name of the resource file footer" << std::endl
;
70 OUString
OStringToOUString(std::string_view str
)
71 { return rtl::OStringToOUString(str
, osl_getThreadTextEncoding()); }
73 OString
OUStringToOString(std::u16string_view str
)
74 { return rtl::OUStringToOString(str
, osl_getThreadTextEncoding()); }
76 /** Get the directory where the module
77 is located as system directory, the
78 returned directory has a trailing '\' */
79 OUString
get_module_path()
83 if (osl_Process_E_None
== osl_getProcessWorkingDir(&cwd_url
.pData
))
84 osl::FileBase::getSystemPathFromFileURL(cwd_url
, module_path
);
89 /** Make the absolute directory of a base and
90 a relative directory, if the relative
91 directory is absolute the relative
92 directory will be returned unchanged.
93 Base and relative directory should be
94 system paths the returned directory is
96 OUString
get_absolute_path(
97 const OUString
& BaseDir
, const OUString
& RelDir
)
102 osl::FileBase::getFileURLFromSystemPath(BaseDir
, base_url
);
103 osl::FileBase::getFileURLFromSystemPath(RelDir
, rel_url
);
106 (void)osl::FileBase::getAbsoluteFileURL(base_url
, rel_url
, abs_url
);
108 OUString abs_sys_path
;
109 osl::FileBase::getSystemPathFromFileURL(abs_url
, abs_sys_path
);
114 std::string
make_absolute(const std::string
& file_name
)
116 OUString fp
= get_absolute_path(
117 get_module_path(), OStringToOUString(file_name
));
118 return std::string(OUStringToOString(fp
));
121 /** A helper class, enables stream exceptions
122 on construction, restores the old exception
123 state on destruction */
124 class StreamExceptionsEnabler
127 explicit StreamExceptionsEnabler(
130 m_OldIos(m_IoStrm
.exceptions())
132 m_IoStrm
.exceptions(std::ios::failbit
| std::ios::badbit
);
135 ~StreamExceptionsEnabler()
137 m_IoStrm
.exceptions(m_OldIos
);
141 std::ios::iostate m_OldIos
;
144 class iso_lang_identifier
147 iso_lang_identifier() {};
149 explicit iso_lang_identifier(const OString
& str
) :
153 explicit iso_lang_identifier(const std::string
& str
) :
157 OUString
make_OUString() const
158 { return OStringToOUString( maBcp47
, RTL_TEXTENCODING_ASCII_US
); }
160 std::string
make_std_string() const
161 { return std::string(maBcp47
); }
167 /** Convert an OUString to the MS resource
168 file format string e.g.
169 OUString -> L"\x1A00\x2200\x3400" */
170 std::string
make_winrc_unicode_string(const OUString
& str
)
172 std::ostringstream oss
;
175 size_t length
= str
.getLength();
176 const sal_Unicode
* pchr
= str
.getStr();
178 for (size_t i
= 0; i
< length
; i
++)
179 oss
<< "\\x" << std::hex
<< static_cast<int>(*pchr
++);
185 std::string
make_winrc_unicode_string(const std::string
& str
)
187 return make_winrc_unicode_string(
188 OUString::createFromAscii(str
));
191 /** A replacement table contains pairs of
192 placeholders and the appropriate substitute */
196 typedef std::map
<std::string
, std::string
> replacement_table_t
;
197 typedef std::map
<std::string
, replacement_table_t
> iso_lang_replacement_table_t
;
200 typedef iso_lang_replacement_table_t::iterator iterator
;
201 typedef iso_lang_replacement_table_t::const_iterator const_iterator
;
204 { return iso_lang_replacement_table_
.begin(); }
207 { return iso_lang_replacement_table_
.end(); }
213 void set_language(const iso_lang_identifier
& iso_lang
)
215 active_iso_lang_
= iso_lang
;
218 // If Text is a placeholder substitute it with
219 //its substitute else leave it unchanged
220 void substitute(std::string
& Text
)
222 replacement_table_t
& prt
= get_replacement_table(active_iso_lang_
.make_std_string());
223 replacement_table_t::iterator iter
= prt
.find(Text
);
224 if (iter
!= prt
.end())
228 void add_substitution(
229 const std::string
& Placeholder
, const std::string
& Substitute
)
231 replacement_table_t
& prt
= get_replacement_table(active_iso_lang_
.make_std_string());
232 prt
.insert(std::make_pair(Placeholder
, Substitute
));
237 // Return the replacement table for the iso lang id
238 // create a new one if not already present
239 replacement_table_t
& get_replacement_table(const std::string
& iso_lang
)
241 return iso_lang_replacement_table_
[iso_lang
];
245 iso_lang_replacement_table_t iso_lang_replacement_table_
;
246 iso_lang_identifier active_iso_lang_
;
249 void add_group_entries(
251 const OString
& GroupName
,
252 Substitutor
& Substitutor
)
254 OSL_ASSERT(aConfig
.HasGroup(GroupName
));
256 aConfig
.SetGroup(GroupName
);
257 size_t key_count
= aConfig
.GetKeyCount();
258 std::map
< LanguageType
, std::string
> map
;
260 for (size_t i
= 0; i
< key_count
; i
++)
262 OString iso_lang
= aConfig
.GetKeyName(sal::static_int_cast
<sal_uInt16
>(i
));
263 OString key_value_utf8
= aConfig
.ReadKey(sal::static_int_cast
<sal_uInt16
>(i
));
264 iso_lang_identifier
myiso_lang( iso_lang
);
265 LanguageType ltype
= LanguageTag( myiso_lang
.make_OUString()).makeFallback().getLanguageType();
266 if( ( static_cast<sal_uInt16
>(ltype
) & 0x0200 ) == 0 && map
[ ltype
].empty() )
268 Substitutor
.set_language(iso_lang_identifier(iso_lang
));
270 key_value_utf8
= comphelper::string::strip(key_value_utf8
, '\"');
272 OUString key_value_utf16
=
273 OStringToOUString(key_value_utf8
, RTL_TEXTENCODING_UTF8
);
275 Substitutor
.add_substitution(
276 std::string(GroupName
), make_winrc_unicode_string(key_value_utf16
));
277 map
[ ltype
] = std::string( iso_lang
);
281 if( !map
[ ltype
].empty() )
283 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());
290 void read_ulf_file(const std::string
& FileName
, Substitutor
& Substitutor
)
292 // work-around for #i32420#
294 // as the Config class is currently not able to deal correctly with
295 // UTF8 files starting with a byte-order-mark we create a copy of the
296 // original file without the byte-order-mark
297 OUString tmpfile_url
;
298 osl_createTempFile(nullptr, nullptr, &tmpfile_url
.pData
);
300 OUString tmpfile_sys
;
301 osl::FileBase::getSystemPathFromFileURL(tmpfile_url
, tmpfile_sys
);
303 std::ifstream
in(FileName
.c_str());
304 std::ofstream
out(OUStringToOString(tmpfile_sys
).getStr());
308 StreamExceptionsEnabler
sexc_out(out
);
309 StreamExceptionsEnabler
sexc_in(in
);
311 //skip the byte-order-mark 0xEF 0xBB 0xBF, identifying UTF8 files
312 unsigned char const BOM
[3] = {0xEF, 0xBB, 0xBF};
314 in
.read(&buff
[0], 3);
316 if (memcmp(buff
, BOM
, 3) != 0)
320 while (std::getline(in
, line
))
321 out
<< line
<< std::endl
;
323 catch (const std::ios::failure
&)
330 // end work-around for #i32420#
332 Config
config(tmpfile_url
);
333 size_t grpcnt
= config
.GetGroupCount();
334 for (size_t i
= 0; i
< grpcnt
; i
++)
335 add_group_entries(config
, config
.GetGroupName(sal::static_int_cast
<sal_uInt16
>(i
)), Substitutor
);
339 const std::string
& fname
,
340 std::vector
<std::string
>& string_container
)
342 std::ifstream
file(fname
.c_str());
343 StreamExceptionsEnabler
sexc(file
);
348 while (std::getline(file
, line
))
349 string_container
.push_back(line
);
351 catch(const std::ios::failure
&)
358 /** A simple helper function that happens the
359 content of one file to another one */
360 void concatenate_files(std::ostream
& os
, std::istream
& is
)
362 StreamExceptionsEnabler
os_sexc(os
);
363 StreamExceptionsEnabler
is_sexc(is
);
368 while (std::getline(is
, line
))
369 os
<< line
<< std::endl
;
371 catch(const std::ios::failure
&)
378 bool is_placeholder(const std::string
& str
)
380 return ((str
.length() > 1) &&
382 ('%' == str
[str
.length() - 1]));
385 void start_language_section(
386 std::ostream_iterator
<std::string
>& ostream_iter
, const iso_lang_identifier
& iso_lang
)
388 ostream_iter
= std::string();
390 std::string
lang_section("LANGUAGE ");
392 LanguageType ltype
= LanguageTag( iso_lang
.make_OUString()).makeFallback().getLanguageType();
395 int primLangID
= PRIMARYLANGID(ltype
);
396 int subLangID
= SUBLANGID(ltype
);
397 // Our resources are normally not sub language dependent.
398 // Esp. for spanish we don't want to distinguish between trad.
399 // and international sorting (which leads to two different sub languages)
400 // Setting the sub language to neutral allows us to use one
401 // stringlist for all spanish variants
402 if ( ( primLangID
== LANG_SPANISH
) &&
403 ( subLangID
== SUBLANG_SPANISH
) )
404 subLangID
= SUBLANG_NEUTRAL
;
407 _itoa(primLangID
, buff
, 16);
409 sprintf(buff
, "%x", primLangID
);
411 lang_section
+= std::string("0x") + std::string(buff
);
413 lang_section
+= std::string(" , ");
416 _itoa(subLangID
, buff
, 16);
418 sprintf(buff
, "%x", subLangID
);
420 lang_section
+= std::string("0x") + std::string(buff
);
421 ostream_iter
= lang_section
;
424 /** Iterate all languages in the substitutor,
425 replace the all placeholder and append the
426 result to the output file */
427 void inflate_rc_template_to_file(
428 std::ostream
& os
, const std::vector
<std::string
>& rctmpl
, Substitutor
& substitutor
)
430 StreamExceptionsEnabler
sexc(os
);
432 Substitutor::const_iterator iter
= substitutor
.begin();
433 Substitutor::const_iterator iter_end
= substitutor
.end();
435 std::ostream_iterator
<std::string
> oi(os
, "\n");
437 for ( /**/ ;iter
!= iter_end
; ++iter
)
439 substitutor
.set_language(iso_lang_identifier(iter
->first
));
442 start_language_section(oi
, iso_lang_identifier(iter
->first
));
444 for ( auto& rct
: rctmpl
)
446 std::istringstream
iss(rct
);
453 substitutor
.substitute(token
);
455 // HACK for partially merged
456 // *.lng files where some strings have
457 // a particular language that others
458 // don't have in order to keep the
460 // coverity[tainted_data] - trusted data source
461 if (is_placeholder(token
))
462 token
= make_winrc_unicode_string(token
);
472 } // namespace /* private */
475 The file names provided via command line should be
476 absolute or relative to the directory of this module.
479 1. read the ulf file and initialize the substitutor
480 2. read the resource template file
481 3. create the output file and append the header
482 4. inflate the resource template to the output file
483 for every language using the substitutor
487 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc
, argv
)
491 CommandLine
cmdline(argc
, argv
);
493 Substitutor substitutor
;
494 read_ulf_file(make_absolute(cmdline
.get_arg("-ulf")), substitutor
);
496 std::vector
<std::string
> rc_tmpl
;
497 read_file(make_absolute(cmdline
.get_arg("-rct")), rc_tmpl
);
499 std::ofstream
rc_file(make_absolute(cmdline
.get_arg("-rc")));
500 std::ifstream
in_header(make_absolute(cmdline
.get_arg("-rch")));
501 concatenate_files(rc_file
, in_header
);
503 inflate_rc_template_to_file(rc_file
, rc_tmpl
, substitutor
);
505 std::ifstream
in_footer(make_absolute(cmdline
.get_arg("-rcf")));
506 concatenate_files(rc_file
, in_footer
);
508 catch(const std::ios::failure
& ex
)
510 std::cout
<< ex
.what() << std::endl
;
513 catch(const std::exception
& ex
)
515 std::cout
<< ex
.what() << std::endl
;
521 std::cout
<< "Unexpected error..." << std::endl
;
527 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */